diff options
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ast.rs | 6 | ||||
| -rw-r--r-- | src/libsyntax/attr.rs | 183 | ||||
| -rw-r--r-- | src/libsyntax/diagnostic_list.rs | 15 | ||||
| -rw-r--r-- | src/libsyntax/ext/base.rs | 21 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 76 | ||||
| -rw-r--r-- | src/libsyntax/ext/tt/macro_parser.rs | 8 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 156 | ||||
| -rw-r--r-- | src/libsyntax/json.rs | 26 | ||||
| -rw-r--r-- | src/libsyntax/lib.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/parse/attr.rs | 7 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 8 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 8 | ||||
| -rw-r--r-- | src/libsyntax/parse/token.rs | 3 | ||||
| -rw-r--r-- | src/libsyntax/print/pp.rs | 30 | ||||
| -rw-r--r-- | src/libsyntax/print/pprust.rs | 39 |
15 files changed, 382 insertions, 205 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index cbfcad12f1e..f8cd6103bdf 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -474,10 +474,10 @@ pub enum NestedMetaItemKind { /// A spanned compile-time attribute item. /// -/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]` +/// E.g. `#[test]`, `#[derive(..)]`, `#[rustfmt::skip]` or `#[feature = "foo"]` #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct MetaItem { - pub ident: Ident, + pub ident: Path, pub node: MetaItemKind, pub span: Span, } @@ -821,7 +821,7 @@ impl Stmt { pub fn is_item(&self) -> bool { match self.node { - StmtKind::Local(_) => true, + StmtKind::Item(_) => true, _ => false, } } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index c68a743303a..ace9904e0c0 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -15,10 +15,10 @@ pub use self::ReprAttr::*; pub use self::IntType::*; use ast; -use ast::{AttrId, Attribute, Name, Ident}; +use ast::{AttrId, Attribute, Name, Ident, Path, PathSegment}; use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind}; use ast::{Lit, LitKind, Expr, ExprKind, Item, Local, Stmt, StmtKind}; -use codemap::{Spanned, respan, dummy_spanned}; +use codemap::{BytePos, Spanned, respan, dummy_spanned}; use syntax_pos::Span; use errors::Handler; use feature_gate::{Features, GatedCfg}; @@ -107,6 +107,14 @@ pub fn is_known(attr: &Attribute) -> bool { }) } +const RUST_KNOWN_TOOL: &[&str] = &["clippy", "rustfmt"]; + +pub fn is_known_tool(attr: &Attribute) -> bool { + let tool_name = + attr.path.segments.iter().next().expect("empty path in attribute").ident.name; + RUST_KNOWN_TOOL.contains(&tool_name.as_str().as_ref()) +} + impl NestedMetaItem { /// Returns the MetaItem if self is a NestedMetaItemKind::MetaItem. pub fn meta_item(&self) -> Option<&MetaItem> { @@ -137,7 +145,7 @@ impl NestedMetaItem { /// Returns the name of the meta item, e.g. `foo` in `#[foo]`, /// `#[foo="bar"]` and `#[foo(bar)]`, if self is a MetaItem pub fn name(&self) -> Option<Name> { - self.meta_item().and_then(|meta_item| Some(meta_item.ident.name)) + self.meta_item().and_then(|meta_item| Some(meta_item.name())) } /// Gets the string value if self is a MetaItem and the MetaItem is a @@ -154,7 +162,7 @@ impl NestedMetaItem { if meta_item_list.len() == 1 { let nested_item = &meta_item_list[0]; if nested_item.is_literal() { - Some((meta_item.ident.name, nested_item.literal().unwrap())) + Some((meta_item.name(), nested_item.literal().unwrap())) } else { None } @@ -204,6 +212,10 @@ impl NestedMetaItem { } } +fn name_from_path(path: &Path) -> Name { + path.segments.last().expect("empty path in attribute").ident.name +} + impl Attribute { pub fn check_name(&self, name: &str) -> bool { let matches = self.path == name; @@ -213,11 +225,10 @@ impl Attribute { matches } - pub fn name(&self) -> Option<Name> { - match self.path.segments.len() { - 1 => Some(self.path.segments[0].ident.name), - _ => None, - } + /// Returns the **last** segment of the name of this attribute. + /// E.g. `foo` for `#[foo]`, `skip` for `#[rustfmt::skip]`. + pub fn name(&self) -> Name { + name_from_path(&self.path) } pub fn value_str(&self) -> Option<Symbol> { @@ -247,9 +258,17 @@ impl Attribute { pub fn is_value_str(&self) -> bool { self.value_str().is_some() } + + pub fn is_scoped(&self) -> bool { + self.path.segments.len() > 1 + } } impl MetaItem { + pub fn name(&self) -> Name { + name_from_path(&self.ident) + } + pub fn value_str(&self) -> Option<Symbol> { match self.node { MetaItemKind::NameValue(ref v) => { @@ -279,7 +298,7 @@ impl MetaItem { pub fn span(&self) -> Span { self.span } pub fn check_name(&self, name: &str) -> bool { - self.ident.name == name + self.name() == name } pub fn is_value_str(&self) -> bool { @@ -296,10 +315,7 @@ impl Attribute { pub fn meta(&self) -> Option<MetaItem> { let mut tokens = self.tokens.trees().peekable(); Some(MetaItem { - ident: match self.path.segments.len() { - 1 => self.path.segments[0].ident, - _ => return None, - }, + ident: self.path.clone(), node: if let Some(node) = MetaItemKind::from_tokens(&mut tokens) { if tokens.peek().is_some() { return None; @@ -344,12 +360,8 @@ impl Attribute { } pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> { - if self.path.segments.len() > 1 { - sess.span_diagnostic.span_err(self.path.span, "expected ident, found path"); - } - Ok(MetaItem { - ident: self.path.segments.last().unwrap().ident, + ident: self.path.clone(), node: self.parse(sess, |parser| parser.parse_meta_item_kind())?, span: self.span, }) @@ -387,16 +399,17 @@ pub fn mk_name_value_item_str(ident: Ident, value: Spanned<Symbol>) -> MetaItem } pub fn mk_name_value_item(span: Span, ident: Ident, value: ast::Lit) -> MetaItem { - MetaItem { ident, span, node: MetaItemKind::NameValue(value) } + MetaItem { ident: Path::from_ident(ident), span, node: MetaItemKind::NameValue(value) } } pub fn mk_list_item(span: Span, ident: Ident, items: Vec<NestedMetaItem>) -> MetaItem { - MetaItem { ident, span, node: MetaItemKind::List(items) } + MetaItem { ident: Path::from_ident(ident), span, node: MetaItemKind::List(items) } } pub fn mk_word_item(ident: Ident) -> MetaItem { - MetaItem { ident, span: ident.span, node: MetaItemKind::Word } + MetaItem { ident: Path::from_ident(ident), span: ident.span, node: MetaItemKind::Word } } + pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem { respan(ident.span, NestedMetaItemKind::MetaItem(mk_word_item(ident))) } @@ -422,7 +435,7 @@ pub fn mk_spanned_attr_inner(sp: Span, id: AttrId, item: MetaItem) -> Attribute Attribute { id, style: ast::AttrStyle::Inner, - path: ast::Path::from_ident(item.ident), + path: item.ident, tokens: item.node.tokens(item.span), is_sugared_doc: false, span: sp, @@ -440,7 +453,7 @@ pub fn mk_spanned_attr_outer(sp: Span, id: AttrId, item: MetaItem) -> Attribute Attribute { id, style: ast::AttrStyle::Outer, - path: ast::Path::from_ident(item.ident), + path: item.ident, tokens: item.node.tokens(item.span), is_sugared_doc: false, span: sp, @@ -453,7 +466,7 @@ pub fn mk_sugared_doc_attr(id: AttrId, text: Symbol, span: Span) -> Attribute { Attribute { id, style, - path: ast::Path::from_ident(Ident::from_str("doc").with_span_pos(span)), + path: Path::from_ident(Ident::from_str("doc").with_span_pos(span)), tokens: MetaItemKind::NameValue(lit).tokens(span), is_sugared_doc: true, span, @@ -489,7 +502,7 @@ pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool { item.check_name("feature") && item.meta_item_list().map(|list| { list.iter().any(|mi| { - mi.word().map(|w| w.ident.name == feature_name) + mi.word().map(|w| w.name() == feature_name) .unwrap_or(false) }) }).unwrap_or(false) @@ -562,7 +575,7 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) { gated_cfg.check_and_emit(sess, feats); } - sess.config.contains(&(cfg.ident.name, cfg.value_str())) + sess.config.contains(&(cfg.name(), cfg.value_str())) }) } @@ -583,7 +596,7 @@ pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F) // The unwraps below may look dangerous, but we've already asserted // that they won't fail with the loop above. - match &*cfg.ident.name.as_str() { + match &*cfg.name().as_str() { "any" => mis.iter().any(|mi| { eval_condition(mi.meta_item().unwrap(), sess, eval) }), @@ -676,7 +689,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler, let meta = meta.as_ref().unwrap(); let get = |meta: &MetaItem, item: &mut Option<Symbol>| { if item.is_some() { - handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.ident.name)); + handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.name())); return false } if let Some(v) = meta.value_str() { @@ -695,14 +708,14 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler, )+ for meta in metas { if let Some(mi) = meta.meta_item() { - match &*mi.ident.name.as_str() { + match &*mi.name().as_str() { $( stringify!($name) => if !get(mi, &mut $name) { continue 'outer }, )+ _ => { handle_errors(diagnostic, mi.span, - AttrError::UnknownMetaItem(mi.ident.name)); + AttrError::UnknownMetaItem(mi.name())); continue 'outer } } @@ -714,7 +727,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler, } } - match &*meta.ident.name.as_str() { + match &*meta.name().as_str() { "rustc_deprecated" => { if rustc_depr.is_some() { span_err!(diagnostic, item_sp, E0540, @@ -769,13 +782,13 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler, let mut issue = None; for meta in metas { if let Some(mi) = meta.meta_item() { - match &*mi.ident.name.as_str() { + match &*mi.name().as_str() { "feature" => if !get(mi, &mut feature) { continue 'outer }, "reason" => if !get(mi, &mut reason) { continue 'outer }, "issue" => if !get(mi, &mut issue) { continue 'outer }, _ => { handle_errors(diagnostic, meta.span, - AttrError::UnknownMetaItem(mi.ident.name)); + AttrError::UnknownMetaItem(mi.name())); continue 'outer } } @@ -825,12 +838,12 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler, let mut since = None; for meta in metas { if let NestedMetaItemKind::MetaItem(ref mi) = meta.node { - match &*mi.ident.name.as_str() { + match &*mi.name().as_str() { "feature" => if !get(mi, &mut feature) { continue 'outer }, "since" => if !get(mi, &mut since) { continue 'outer }, _ => { handle_errors(diagnostic, meta.span, - AttrError::UnknownMetaItem(mi.ident.name)); + AttrError::UnknownMetaItem(mi.name())); continue 'outer } } @@ -917,7 +930,7 @@ fn find_deprecation_generic<'a, I>(diagnostic: &Handler, depr = if let Some(metas) = attr.meta_item_list() { let get = |meta: &MetaItem, item: &mut Option<Symbol>| { if item.is_some() { - handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.ident.name)); + handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.name())); return false } if let Some(v) = meta.value_str() { @@ -933,12 +946,12 @@ fn find_deprecation_generic<'a, I>(diagnostic: &Handler, let mut note = None; for meta in metas { if let NestedMetaItemKind::MetaItem(ref mi) = meta.node { - match &*mi.ident.name.as_str() { + match &*mi.name().as_str() { "since" => if !get(mi, &mut since) { continue 'outer }, "note" => if !get(mi, &mut note) { continue 'outer }, _ => { handle_errors(diagnostic, meta.span, - AttrError::UnknownMetaItem(mi.ident.name)); + AttrError::UnknownMetaItem(mi.name())); continue 'outer } } @@ -990,7 +1003,7 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr> let mut recognised = false; if let Some(mi) = item.word() { - let word = &*mi.ident.name.as_str(); + let word = &*mi.name().as_str(); let hint = match word { "C" => Some(ReprC), "packed" => Some(ReprPacked(1)), @@ -1012,11 +1025,11 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr> let parse_alignment = |node: &ast::LitKind| -> Result<u32, &'static str> { if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { if literal.is_power_of_two() { - // rustc::ty::layout::Align restricts align to <= 2147483647 - if *literal <= 2147483647 { + // rustc::ty::layout::Align restricts align to <= 2^29 + if *literal <= 1 << 29 { Ok(*literal as u32) } else { - Err("larger than 2147483647") + Err("larger than 2^29") } } else { Err("not a power of two") @@ -1045,6 +1058,30 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr> span_err!(diagnostic, item.span, E0589, "invalid `repr(align)` attribute: {}", literal_error); } + } else { + if let Some(meta_item) = item.meta_item() { + if meta_item.name() == "align" { + if let MetaItemKind::NameValue(ref value) = meta_item.node { + recognised = true; + let mut err = struct_span_err!(diagnostic, item.span, E0693, + "incorrect `repr(align)` attribute format"); + match value.node { + ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => { + err.span_suggestion(item.span, + "use parentheses instead", + format!("align({})", int)); + } + ast::LitKind::Str(s, _) => { + err.span_suggestion(item.span, + "use parentheses instead", + format!("align({})", s)); + } + _ => {} + } + err.emit(); + } + } + } } if !recognised { // Not a word we recognize @@ -1103,18 +1140,56 @@ impl IntType { impl MetaItem { fn tokens(&self) -> TokenStream { - let ident = TokenTree::Token(self.span, Token::from_ast_ident(self.ident)); - TokenStream::concat(vec![ident.into(), self.node.tokens(self.span)]) + let mut idents = vec![]; + let mut last_pos = BytePos(0 as u32); + for (i, segment) in self.ident.segments.iter().enumerate() { + let is_first = i == 0; + if !is_first { + let mod_sep_span = Span::new(last_pos, + segment.ident.span.lo(), + segment.ident.span.ctxt()); + idents.push(TokenTree::Token(mod_sep_span, Token::ModSep).into()); + } + idents.push(TokenTree::Token(segment.ident.span, + Token::from_ast_ident(segment.ident)).into()); + last_pos = segment.ident.span.hi(); + } + idents.push(self.node.tokens(self.span)); + TokenStream::concat(idents) } fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem> where I: Iterator<Item = TokenTree>, { - let (span, ident) = match tokens.next() { - Some(TokenTree::Token(span, Token::Ident(ident, _))) => (span, ident), + // FIXME: Share code with `parse_path`. + let ident = match tokens.next() { + Some(TokenTree::Token(span, Token::Ident(ident, _))) => { + if let Some(TokenTree::Token(_, Token::ModSep)) = tokens.peek() { + let mut segments = vec![PathSegment::from_ident(ident.with_span_pos(span))]; + tokens.next(); + loop { + if let Some(TokenTree::Token(span, + Token::Ident(ident, _))) = tokens.next() { + segments.push(PathSegment::from_ident(ident.with_span_pos(span))); + } else { + return None; + } + if let Some(TokenTree::Token(_, Token::ModSep)) = tokens.peek() { + tokens.next(); + } else { + break; + } + } + let span = span.with_hi(segments.last().unwrap().ident.span.hi()); + Path { span, segments } + } else { + Path::from_ident(ident.with_span_pos(span)) + } + } Some(TokenTree::Token(_, Token::Interpolated(ref nt))) => match nt.0 { - token::Nonterminal::NtIdent(ident, _) => (ident.span, ident), + token::Nonterminal::NtIdent(ident, _) => Path::from_ident(ident), token::Nonterminal::NtMeta(ref meta) => return Some(meta.clone()), + token::Nonterminal::NtPath(ref path) => path.clone(), _ => return None, }, _ => return None, @@ -1123,10 +1198,11 @@ impl MetaItem { let node = MetaItemKind::from_tokens(tokens)?; let hi = match node { MetaItemKind::NameValue(ref lit) => lit.span.hi(), - MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(span.hi()), - _ => span.hi(), + MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(ident.span.hi()), + _ => ident.span.hi(), }; - Some(MetaItem { ident, node, span: span.with_hi(hi) }) + let span = ident.span.with_hi(hi); + Some(MetaItem { ident, node, span }) } } @@ -1228,10 +1304,7 @@ impl LitKind { match *self { LitKind::Str(string, ast::StrStyle::Cooked) => { - let mut escaped = String::new(); - for ch in string.as_str().chars() { - escaped.extend(ch.escape_unicode()); - } + let escaped = string.as_str().escape_default(); Token::Literal(token::Lit::Str_(Symbol::intern(&escaped)), None) } LitKind::Str(string, ast::StrStyle::Raw(n)) => { diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs index bb7988e64bc..5f940437ab3 100644 --- a/src/libsyntax/diagnostic_list.rs +++ b/src/libsyntax/diagnostic_list.rs @@ -244,6 +244,18 @@ fn main() { ``` "##, +E0589: r##" +The value of `N` that was specified for `repr(align(N))` was not a power +of two, or was greater than 2^29. + +```compile_fail,E0589 +#[repr(align(15))] // error: invalid `repr(align)` attribute: not a power of two +enum Foo { + Bar(u64), +} +``` +"##, + E0658: r##" An unstable feature was used. @@ -321,7 +333,8 @@ register_diagnostics! { E0555, // malformed feature attribute, expected #![feature(...)] E0556, // malformed feature, expected just one word E0584, // file for module `..` found at both .. and .. - E0589, // invalid `repr(align)` attribute E0629, // missing 'feature' (rustc_const_unstable) E0630, // rustc_const_unstable attribute must be paired with stable/unstable attribute + E0693, // incorrect `repr(align)` attribute format + E0694, // an unknown tool name found in scoped attributes } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 0c313ab1489..3b76084f2fb 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -118,6 +118,20 @@ impl Annotatable { } } + pub fn expect_stmt(self) -> ast::Stmt { + match self { + Annotatable::Stmt(stmt) => stmt.into_inner(), + _ => panic!("expected statement"), + } + } + + pub fn expect_expr(self) -> P<ast::Expr> { + match self { + Annotatable::Expr(expr) => expr, + _ => panic!("expected expression"), + } + } + pub fn derive_allowed(&self) -> bool { match *self { Annotatable::Item(ref item) => match item.node { @@ -661,7 +675,9 @@ pub trait Resolver { fn resolve_imports(&mut self); // Resolves attribute and derive legacy macros from `#![plugin(..)]`. - fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>; + fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec<Attribute>, allow_derive: bool) + -> Option<Attribute>; + fn resolve_invoc(&mut self, invoc: &mut Invocation, scope: Mark, force: bool) -> Result<Option<Lrc<SyntaxExtension>>, Determinacy>; fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind, force: bool) @@ -687,7 +703,8 @@ impl Resolver for DummyResolver { fn add_builtin(&mut self, _ident: ast::Ident, _ext: Lrc<SyntaxExtension>) {} fn resolve_imports(&mut self) {} - fn find_legacy_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>) -> Option<Attribute> { None } + fn find_legacy_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>, _allow_derive: bool) + -> Option<Attribute> { None } fn resolve_invoc(&mut self, _invoc: &mut Invocation, _scope: Mark, _force: bool) -> Result<Option<Lrc<SyntaxExtension>>, Determinacy> { Err(Determinacy::Determined) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 1434e5fddea..584b9455a93 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -143,7 +143,7 @@ impl ExpansionKind { } fn expect_from_annotatables<I: IntoIterator<Item = Annotatable>>(self, items: I) -> Expansion { - let items = items.into_iter(); + let mut items = items.into_iter(); match self { ExpansionKind::Items => Expansion::Items(items.map(Annotatable::expect_item).collect()), @@ -153,7 +153,14 @@ impl ExpansionKind { Expansion::TraitItems(items.map(Annotatable::expect_trait_item).collect()), ExpansionKind::ForeignItems => Expansion::ForeignItems(items.map(Annotatable::expect_foreign_item).collect()), - _ => unreachable!(), + ExpansionKind::Stmts => Expansion::Stmts(items.map(Annotatable::expect_stmt).collect()), + ExpansionKind::Expr => Expansion::Expr( + items.next().expect("expected exactly one expression").expect_expr() + ), + ExpansionKind::OptExpr => + Expansion::OptExpr(items.next().map(Annotatable::expect_expr)), + ExpansionKind::Pat | ExpansionKind::Ty => + panic!("patterns and types aren't annotatable"), } } } @@ -803,7 +810,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { invoc.expansion_data.mark.set_expn_info(expn_info); let span = span.with_ctxt(self.cx.backtrace()); let dummy = ast::MetaItem { // FIXME(jseyfried) avoid this - ident: keywords::Invalid.ident(), + ident: Path::from_ident(keywords::Invalid.ident()), span: DUMMY_SP, node: ast::MetaItemKind::Word, }; @@ -956,14 +963,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.collect(kind, InvocationKind::Attr { attr, traits, item }) } - // If `item` is an attr invocation, remove and return the macro attribute. + /// If `item` is an attr invocation, remove and return the macro attribute and derive traits. fn classify_item<T>(&mut self, mut item: T) -> (Option<ast::Attribute>, Vec<Path>, T) where T: HasAttrs, { let (mut attr, mut traits) = (None, Vec::new()); item = item.map_attrs(|mut attrs| { - if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs) { + if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs, + true) { attr = Some(legacy_attr_invoc); return attrs; } @@ -978,6 +986,28 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { (attr, traits, item) } + /// Alternative of `classify_item()` that ignores `#[derive]` so invocations fallthrough + /// to the unused-attributes lint (making it an error on statements and expressions + /// is a breaking change) + fn classify_nonitem<T: HasAttrs>(&mut self, mut item: T) -> (Option<ast::Attribute>, T) { + let mut attr = None; + + item = item.map_attrs(|mut attrs| { + if let Some(legacy_attr_invoc) = self.cx.resolver.find_legacy_attr_invoc(&mut attrs, + false) { + attr = Some(legacy_attr_invoc); + return attrs; + } + + if self.cx.ecfg.proc_macro_enabled() { + attr = find_attr_invoc(&mut attrs); + } + attrs + }); + + (attr, item) + } + fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> { self.cfg.configure(node) } @@ -987,12 +1017,23 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn check_attributes(&mut self, attrs: &[ast::Attribute]) { let features = self.cx.ecfg.features.unwrap(); for attr in attrs.iter() { - feature_gate::check_attribute(attr, self.cx.parse_sess, features); + self.check_attribute_inner(attr, features); + + // macros are expanded before any lint passes so this warning has to be hardcoded + if attr.path == "derive" { + self.cx.struct_span_warn(attr.span, "`#[derive]` does nothing on macro invocations") + .note("this may become a hard error in a future release") + .emit(); + } } } fn check_attribute(&mut self, at: &ast::Attribute) { let features = self.cx.ecfg.features.unwrap(); + self.check_attribute_inner(at, features); + } + + fn check_attribute_inner(&mut self, at: &ast::Attribute, features: &Features) { feature_gate::check_attribute(at, self.cx.parse_sess, features); } } @@ -1008,15 +1049,16 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let mut expr = self.cfg.configure_expr(expr).into_inner(); expr.node = self.cfg.configure_expr_kind(expr.node); - let (attr, derives, expr) = self.classify_item(expr); + // ignore derives so they remain unused + let (attr, expr) = self.classify_nonitem(expr); - if attr.is_some() || !derives.is_empty() { + if attr.is_some() { // collect the invoc regardless of whether or not attributes are permitted here // expansion will eat the attribute so it won't error later attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); // ExpansionKind::Expr requires the macro to emit an expression - return self.collect_attr(attr, derives, Annotatable::Expr(P(expr)), ExpansionKind::Expr) + return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), ExpansionKind::Expr) .make_expr(); } @@ -1032,12 +1074,13 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let mut expr = configure!(self, expr).into_inner(); expr.node = self.cfg.configure_expr_kind(expr.node); - let (attr, derives, expr) = self.classify_item(expr); + // ignore derives so they remain unused + let (attr, expr) = self.classify_nonitem(expr); - if attr.is_some() || !derives.is_empty() { + if attr.is_some() { attr.as_ref().map(|a| self.cfg.maybe_emit_expr_attr_err(a)); - return self.collect_attr(attr, derives, Annotatable::Expr(P(expr)), + return self.collect_attr(attr, vec![], Annotatable::Expr(P(expr)), ExpansionKind::OptExpr) .make_opt_expr(); } @@ -1071,7 +1114,14 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { // we'll expand attributes on expressions separately if !stmt.is_expr() { - let (attr, derives, stmt_) = self.classify_item(stmt); + let (attr, derives, stmt_) = if stmt.is_item() { + self.classify_item(stmt) + } else { + // ignore derives on non-item statements so it falls through + // to the unused-attributes lint + let (attr, stmt) = self.classify_nonitem(stmt); + (attr, vec![], stmt) + }; if attr.is_some() || !derives.is_empty() { return self.collect_attr(attr, derives, diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index d9c3deb30da..71634ada894 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -835,7 +835,13 @@ fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal { "path" => token::NtPath(panictry!(p.parse_path_common(PathStyle::Type, false))), "meta" => token::NtMeta(panictry!(p.parse_meta_item())), "vis" => token::NtVis(panictry!(p.parse_visibility(true))), - "lifetime" => token::NtLifetime(p.expect_lifetime().ident), + "lifetime" => if p.check_lifetime() { + token::NtLifetime(p.expect_lifetime().ident) + } else { + let token_str = pprust::token_to_string(&p.token); + p.fatal(&format!("expected a lifetime, found `{}`", &token_str)).emit(); + FatalError.raise(); + } // this is not supposed to happen, since it has been checked // when compiling the macro. _ => p.span_bug(sp, "invalid fragment specifier"), diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 0331e90164f..d8db76a95ff 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -90,24 +90,24 @@ macro_rules! declare_features { } }; - ($((removed, $feature: ident, $ver: expr, $issue: expr, None),)+) => { + ($((removed, $feature: ident, $ver: expr, $issue: expr, None, $reason: expr),)+) => { /// Represents unstable features which have since been removed (it was once Active) - const REMOVED_FEATURES: &'static [(&'static str, &'static str, Option<u32>)] = &[ - $((stringify!($feature), $ver, $issue)),+ + const REMOVED_FEATURES: &[(&str, &str, Option<u32>, Option<&str>)] = &[ + $((stringify!($feature), $ver, $issue, $reason)),+ ]; }; ($((stable_removed, $feature: ident, $ver: expr, $issue: expr, None),)+) => { /// Represents stable features which have since been removed (it was once Accepted) - const STABLE_REMOVED_FEATURES: &'static [(&'static str, &'static str, Option<u32>)] = &[ - $((stringify!($feature), $ver, $issue)),+ + const STABLE_REMOVED_FEATURES: &[(&str, &str, Option<u32>, Option<&str>)] = &[ + $((stringify!($feature), $ver, $issue, None)),+ ]; }; ($((accepted, $feature: ident, $ver: expr, $issue: expr, None),)+) => { /// Those language feature has since been Accepted (it was once Active) - const ACCEPTED_FEATURES: &'static [(&'static str, &'static str, Option<u32>)] = &[ - $((stringify!($feature), $ver, $issue)),+ + const ACCEPTED_FEATURES: &[(&str, &str, Option<u32>, Option<&str>)] = &[ + $((stringify!($feature), $ver, $issue, None)),+ ]; } } @@ -162,9 +162,6 @@ declare_features! ( // OIBIT specific features (active, optin_builtin_traits, "1.0.0", Some(13231), None), - // macro re-export needs more discussion and stabilization - (active, macro_reexport, "1.0.0", Some(29638), None), - // Allows use of #[staged_api] // rustc internal (active, staged_api, "1.0.0", None, None), @@ -369,15 +366,9 @@ declare_features! ( // #[doc(include="some-file")] (active, external_doc, "1.22.0", Some(44732), None), - // allow `#[must_use]` on functions and comparison operators (RFC 1940) - (active, fn_must_use, "1.21.0", Some(43302), None), - // Future-proofing enums/structs with #[non_exhaustive] attribute (RFC 2008) (active, non_exhaustive, "1.22.0", Some(44109), None), - // Trait object syntax with `dyn` prefix - (active, dyn_trait, "1.22.0", Some(44662), Some(Edition::Edition2018)), - // `crate` as visibility modifier, synonymous to `pub(crate)` (active, crate_visibility_modifier, "1.23.0", Some(45388), Some(Edition::Edition2018)), @@ -397,7 +388,7 @@ declare_features! ( (active, generic_associated_types, "1.23.0", Some(44265), None), // Resolve absolute paths as paths from other crates - (active, extern_absolute_paths, "1.24.0", Some(44660), None), + (active, extern_absolute_paths, "1.24.0", Some(44660), Some(Edition::Edition2018)), // `foo.rs` as an alternative to `foo/mod.rs` (active, non_modrs_mods, "1.24.0", Some(44660), Some(Edition::Edition2018)), @@ -466,30 +457,38 @@ declare_features! ( // #[doc(alias = "...")] (active, doc_alias, "1.27.0", Some(50146), None), + + // Access to crate names passed via `--extern` through prelude + (active, extern_prelude, "1.27.0", Some(44660), Some(Edition::Edition2018)), + + // Scoped attributes + (active, tool_attributes, "1.25.0", Some(44690), None), ); declare_features! ( - (removed, import_shadowing, "1.0.0", None, None), - (removed, managed_boxes, "1.0.0", None, None), + (removed, import_shadowing, "1.0.0", None, None, None), + (removed, managed_boxes, "1.0.0", None, None, None), // Allows use of unary negate on unsigned integers, e.g. -e for e: u8 - (removed, negate_unsigned, "1.0.0", Some(29645), None), - (removed, reflect, "1.0.0", Some(27749), None), + (removed, negate_unsigned, "1.0.0", Some(29645), None, None), + (removed, reflect, "1.0.0", Some(27749), None, None), // A way to temporarily opt out of opt in copy. This will *never* be accepted. - (removed, opt_out_copy, "1.0.0", None, None), - (removed, quad_precision_float, "1.0.0", None, None), - (removed, struct_inherit, "1.0.0", None, None), - (removed, test_removed_feature, "1.0.0", None, None), - (removed, visible_private_types, "1.0.0", None, None), - (removed, unsafe_no_drop_flag, "1.0.0", None, None), + (removed, opt_out_copy, "1.0.0", None, None, None), + (removed, quad_precision_float, "1.0.0", None, None, None), + (removed, struct_inherit, "1.0.0", None, None, None), + (removed, test_removed_feature, "1.0.0", None, None, None), + (removed, visible_private_types, "1.0.0", None, None, None), + (removed, unsafe_no_drop_flag, "1.0.0", None, None, None), // Allows using items which are missing stability attributes // rustc internal - (removed, unmarked_api, "1.0.0", None, None), - (removed, pushpop_unsafe, "1.2.0", None, None), - (removed, allocator, "1.0.0", None, None), - // Allows the `#[simd]` attribute -- removed in favor of `#[repr(simd)]` - (removed, simd, "1.0.0", Some(27731), None), - // Merged into `slice_patterns` - (removed, advanced_slice_patterns, "1.0.0", Some(23121), None), + (removed, unmarked_api, "1.0.0", None, None, None), + (removed, pushpop_unsafe, "1.2.0", None, None, None), + (removed, allocator, "1.0.0", None, None, None), + (removed, simd, "1.0.0", Some(27731), None, + Some("removed in favor of `#[repr(simd)]`")), + (removed, advanced_slice_patterns, "1.0.0", Some(23121), None, + Some("merged into `#![feature(slice_patterns)]`")), + (removed, macro_reexport, "1.0.0", Some(29638), None, + Some("subsumed by `#![feature(use_extern_macros)]` and `pub use`")), ); declare_features! ( @@ -592,6 +591,10 @@ declare_features! ( (accepted, cfg_target_feature, "1.27.0", Some(29717), None), // Allows #[target_feature(...)] (accepted, target_feature, "1.27.0", None, None), + // Trait object syntax with `dyn` prefix + (accepted, dyn_trait, "1.27.0", Some(44662), None), + // allow `#[must_use]` on functions; and, must-use operators (RFC 1940) + (accepted, fn_must_use, "1.27.0", Some(43302), None), ); // If you change this, please modify src/doc/unstable-book as well. You must @@ -675,7 +678,6 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG ("forbid", Normal, Ungated), ("deny", Normal, Ungated), - ("macro_reexport", Normal, Ungated), ("macro_use", Normal, Ungated), ("macro_export", Normal, Ungated), ("plugin_registrar", Normal, Ungated), @@ -1080,7 +1082,7 @@ pub struct GatedCfg { impl GatedCfg { pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> { - let name = cfg.ident.name.as_str(); + let name = cfg.name().as_str(); GATED_CFGS.iter() .position(|info| info.0 == name) .map(|idx| { @@ -1133,7 +1135,7 @@ macro_rules! gate_feature { impl<'a> Context<'a> { fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) { debug!("check_attribute(attr = {:?})", attr); - let name = unwrap_or!(attr.name(), return).as_str(); + let name = attr.name().as_str(); for &(n, ty, ref gateage) in BUILTIN_ATTRIBUTES { if name == n { if let Gated(_, name, desc, ref has_feature) = *gateage { @@ -1173,12 +1175,28 @@ impl<'a> Context<'a> { // before the plugin attributes are registered // so we skip this then if !is_macro { - gate_feature!(self, custom_attribute, attr.span, - &format!("The attribute `{}` is currently \ - unknown to the compiler and \ - may have meaning \ - added to it in the future", - attr.path)); + if attr.is_scoped() { + gate_feature!(self, tool_attributes, attr.span, + &format!("scoped attribute `{}` is experimental", attr.path)); + if attr::is_known_tool(attr) { + attr::mark_used(attr); + } else { + span_err!( + self.parse_sess.span_diagnostic, + attr.span, + E0694, + "an unknown tool name found in scoped attribute: `{}`.", + attr.path + ); + } + } else { + gate_feature!(self, custom_attribute, attr.span, + &format!("The attribute `{}` is currently \ + unknown to the compiler and \ + may have meaning \ + added to it in the future", + attr.path)); + } } } } @@ -1204,7 +1222,7 @@ fn find_lang_feature_issue(feature: &str) -> Option<u32> { let found = ACCEPTED_FEATURES.iter().chain(REMOVED_FEATURES).chain(STABLE_REMOVED_FEATURES) .find(|t| t.0 == feature); match found { - Some(&(_, _, issue)) => issue, + Some(&(_, _, issue, _)) => issue, None => panic!("Feature `{}` is not declared anywhere", feature), } } @@ -1518,11 +1536,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, underscore_imports, i.span, "renaming extern crates with `_` is unstable"); } - if let Some(attr) = attr::find_by_name(&i.attrs[..], "macro_reexport") { - gate_feature_post!(&self, macro_reexport, attr.span, - "macros re-exports are experimental \ - and possibly buggy"); - } } ast::ItemKind::ForeignMod(ref foreign_module) => { @@ -1546,11 +1559,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { function may change over time, for now \ a top-level `fn main()` is required"); } - if let Some(attr) = attr::find_by_name(&i.attrs[..], "must_use") { - gate_feature_post!(&self, fn_must_use, attr.span, - "`#[must_use]` on functions is experimental", - GateStrength::Soft); - } } ast::ItemKind::Struct(..) => { @@ -1582,7 +1590,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { "trait aliases are not yet fully implemented"); } - ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, ref impl_items) => { + ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, _) => { if polarity == ast::ImplPolarity::Negative { gate_feature_post!(&self, optin_builtin_traits, i.span, @@ -1595,16 +1603,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { i.span, "specialization is unstable"); } - - for impl_item in impl_items { - if let ast::ImplItemKind::Method(..) = impl_item.node { - if let Some(attr) = attr::find_by_name(&impl_item.attrs[..], "must_use") { - gate_feature_post!(&self, fn_must_use, attr.span, - "`#[must_use]` on methods is experimental", - GateStrength::Soft); - } - } - } } ast::ItemKind::Trait(ast::IsAuto::Yes, ..) => { @@ -1657,10 +1655,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, never_type, ty.span, "The `!` type is experimental"); } - ast::TyKind::TraitObject(_, ast::TraitObjectSyntax::Dyn) => { - gate_feature_post!(&self, dyn_trait, ty.span, - "`dyn Trait` syntax is unstable"); - } _ => {} } visit::walk_ty(self, ty) @@ -1842,8 +1836,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute], crate_edition: Edition) -> Features { - fn feature_removed(span_handler: &Handler, span: Span) { - span_err!(span_handler, span, E0557, "feature has been removed"); + fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) { + let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed"); + if let Some(reason) = reason { + err.span_note(span, reason); + } + err.emit(); } let mut features = Features::new(); @@ -1864,7 +1862,7 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute], for mi in list { let name = if let Some(word) = mi.word() { - word.ident.name + word.name() } else { span_err!(span_handler, mi.span, E0556, "malformed feature, expected just one word"); @@ -1876,19 +1874,19 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute], set(&mut features, mi.span); feature_checker.collect(&features, mi.span); } - else if let Some(&(_, _, _)) = REMOVED_FEATURES.iter() - .find(|& &(n, _, _)| name == n) + else if let Some(&(.., reason)) = REMOVED_FEATURES.iter() + .find(|& &(n, ..)| name == n) .or_else(|| STABLE_REMOVED_FEATURES.iter() - .find(|& &(n, _, _)| name == n)) { - feature_removed(span_handler, mi.span); + .find(|& &(n, ..)| name == n)) { + feature_removed(span_handler, mi.span, reason); } - else if let Some(&(_, _, _)) = ACCEPTED_FEATURES.iter() - .find(|& &(n, _, _)| name == n) { + else if let Some(&(..)) = ACCEPTED_FEATURES.iter() + .find(|& &(n, ..)| name == n) { features.declared_stable_lang_features.push((name, mi.span)); } else if let Some(&edition) = ALL_EDITIONS.iter() .find(|e| name == e.feature_name()) { if edition <= crate_edition { - feature_removed(span_handler, mi.span); + feature_removed(span_handler, mi.span, None); } else { for &(.., f_edition, set) in ACTIVE_FEATURES.iter() { if let Some(f_edition) = f_edition { diff --git a/src/libsyntax/json.rs b/src/libsyntax/json.rs index b4f34fb12e3..2f15e75093b 100644 --- a/src/libsyntax/json.rs +++ b/src/libsyntax/json.rs @@ -23,7 +23,7 @@ use codemap::{CodeMap, FilePathMapping}; use syntax_pos::{self, MacroBacktrace, Span, SpanLabel, MultiSpan}; use errors::registry::Registry; use errors::{DiagnosticBuilder, SubDiagnostic, CodeSuggestion, CodeMapper}; -use errors::DiagnosticId; +use errors::{DiagnosticId, Applicability}; use errors::emitter::{Emitter, EmitterWriter}; use rustc_data_structures::sync::{self, Lrc}; @@ -39,7 +39,7 @@ pub struct JsonEmitter { cm: Lrc<CodeMapper + sync::Send + sync::Sync>, pretty: bool, /// Whether "approximate suggestions" are enabled in the config - approximate_suggestions: bool, + suggestion_applicability: bool, ui_testing: bool, } @@ -47,13 +47,13 @@ impl JsonEmitter { pub fn stderr(registry: Option<Registry>, code_map: Lrc<CodeMap>, pretty: bool, - approximate_suggestions: bool) -> JsonEmitter { + suggestion_applicability: bool) -> JsonEmitter { JsonEmitter { dst: Box::new(io::stderr()), registry, cm: code_map, pretty, - approximate_suggestions, + suggestion_applicability, ui_testing: false, } } @@ -68,13 +68,13 @@ impl JsonEmitter { registry: Option<Registry>, code_map: Lrc<CodeMap>, pretty: bool, - approximate_suggestions: bool) -> JsonEmitter { + suggestion_applicability: bool) -> JsonEmitter { JsonEmitter { dst, registry, cm: code_map, pretty, - approximate_suggestions, + suggestion_applicability, ui_testing: false, } } @@ -138,7 +138,7 @@ struct DiagnosticSpan { suggested_replacement: Option<String>, /// If the suggestion is approximate #[rustc_serialize_exclude_null] - suggestion_approximate: Option<bool>, + suggestion_applicability: Option<Applicability>, /// Macro invocations that created the code at this span, if any. expansion: Option<Box<DiagnosticSpanMacroExpansion>>, } @@ -239,7 +239,7 @@ impl Diagnostic { impl DiagnosticSpan { fn from_span_label(span: SpanLabel, - suggestion: Option<(&String, bool)>, + suggestion: Option<(&String, Applicability)>, je: &JsonEmitter) -> DiagnosticSpan { Self::from_span_etc(span.span, @@ -252,7 +252,7 @@ impl DiagnosticSpan { fn from_span_etc(span: Span, is_primary: bool, label: Option<String>, - suggestion: Option<(&String, bool)>, + suggestion: Option<(&String, Applicability)>, je: &JsonEmitter) -> DiagnosticSpan { // obtain the full backtrace from the `macro_backtrace` @@ -272,7 +272,7 @@ impl DiagnosticSpan { fn from_span_full(span: Span, is_primary: bool, label: Option<String>, - suggestion: Option<(&String, bool)>, + suggestion: Option<(&String, Applicability)>, mut backtrace: vec::IntoIter<MacroBacktrace>, je: &JsonEmitter) -> DiagnosticSpan { @@ -301,7 +301,7 @@ impl DiagnosticSpan { }) }); - let suggestion_approximate = if je.approximate_suggestions { + let suggestion_applicability = if je.suggestion_applicability { suggestion.map(|x| x.1) } else { None @@ -318,7 +318,7 @@ impl DiagnosticSpan { is_primary, text: DiagnosticSpanLine::from_span(span, je), suggested_replacement: suggestion.map(|x| x.0.clone()), - suggestion_approximate, + suggestion_applicability, expansion: backtrace_step, label, } @@ -344,7 +344,7 @@ impl DiagnosticSpan { }; DiagnosticSpan::from_span_label(span_label, Some((&suggestion_inner.snippet, - suggestion.approximate)), + suggestion.applicability)), je) }) }) diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 870ce1926ad..f148aaf7267 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -25,6 +25,7 @@ #![feature(non_exhaustive)] #![feature(const_atomic_usize_new)] #![feature(rustc_attrs)] +#![feature(str_escape)] #![recursion_limit="256"] diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 90f08ab1468..cceed589212 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -149,7 +149,7 @@ impl<'a> Parser<'a> { }; Ok(if let Some(meta) = meta { self.bump(); - (ast::Path::from_ident(meta.ident), meta.node.tokens(meta.span)) + (meta.ident, meta.node.tokens(meta.span)) } else { (self.parse_path(PathStyle::Mod)?, self.parse_tokens()) }) @@ -225,9 +225,10 @@ impl<'a> Parser<'a> { } let lo = self.span; - let ident = self.parse_ident()?; + let ident = self.parse_path(PathStyle::Mod)?; let node = self.parse_meta_item_kind()?; - Ok(ast::MetaItem { ident, node: node, span: lo.to(self.prev_span) }) + let span = lo.to(self.prev_span); + Ok(ast::MetaItem { ident, node, span }) } pub fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> { diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index ff09c6aa2f0..f252020bc31 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -298,14 +298,10 @@ pub fn char_lit(lit: &str, diag: Option<(Span, &Handler)>) -> (char, isize) { } } -pub fn escape_default(s: &str) -> String { - s.chars().map(char::escape_default).flat_map(|x| x).collect() -} - /// Parse a string representing a string literal into its final form. Does /// unescaping. pub fn str_lit(lit: &str, diag: Option<(Span, &Handler)>) -> String { - debug!("parse_str_lit: given {}", escape_default(lit)); + debug!("str_lit: given {}", lit.escape_default()); let mut res = String::with_capacity(lit.len()); let error = |i| format!("lexer should have rejected {} at {}", lit, i); @@ -374,7 +370,7 @@ pub fn str_lit(lit: &str, diag: Option<(Span, &Handler)>) -> String { /// Parse a string representing a raw string literal into its final form. The /// only operation this does is convert embedded CRLF into a single LF. pub fn raw_str_lit(lit: &str) -> String { - debug!("raw_str_lit: given {}", escape_default(lit)); + debug!("raw_str_lit: given {}", lit.escape_default()); let mut res = String::with_capacity(lit.len()); let mut chars = lit.chars().peekable(); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 324cadc84e8..bf4a68679df 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1958,16 +1958,16 @@ impl<'a> Parser<'a> { let meta_ident = match self.token { token::Interpolated(ref nt) => match nt.0 { token::NtMeta(ref meta) => match meta.node { - ast::MetaItemKind::Word => Some(meta.ident), + ast::MetaItemKind::Word => Some(meta.ident.clone()), _ => None, }, _ => None, }, _ => None, }; - if let Some(ident) = meta_ident { + if let Some(path) = meta_ident { self.bump(); - return Ok(ast::Path::from_ident(ident)); + return Ok(path); } self.parse_path(style) } @@ -2042,7 +2042,7 @@ impl<'a> Parser<'a> { }) } - fn check_lifetime(&mut self) -> bool { + pub fn check_lifetime(&mut self) -> bool { self.expected_tokens.push(TokenType::Lifetime); self.token.is_lifetime() } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 44394384c7a..938711ca1d4 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -277,9 +277,10 @@ impl Token { DotDot | DotDotDot | DotDotEq | // range notation Lt | BinOp(Shl) | // associated path ModSep | // global path + Lifetime(..) | // labeled loop Pound => true, // expression attributes Interpolated(ref nt) => match nt.0 { - NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) => true, + NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) | NtLifetime(..) => true, _ => false, }, _ => false, diff --git a/src/libsyntax/print/pp.rs b/src/libsyntax/print/pp.rs index 5c4bf47a6db..e7bd369053c 100644 --- a/src/libsyntax/print/pp.rs +++ b/src/libsyntax/print/pp.rs @@ -247,12 +247,14 @@ pub fn mk_printer<'a>(out: Box<io::Write+'a>, linewidth: usize) -> Printer<'a> { debug!("mk_printer {}", linewidth); Printer { out, - buf_len: n, + buf_max_len: n, margin: linewidth as isize, space: linewidth as isize, left: 0, right: 0, - buf: vec![BufEntry { token: Token::Eof, size: 0 }; n], + // Initialize a single entry; advance_right() will extend it on demand + // up to `buf_max_len` elements. + buf: vec![BufEntry::default()], left_total: 0, right_total: 0, scan_stack: VecDeque::new(), @@ -263,7 +265,7 @@ pub fn mk_printer<'a>(out: Box<io::Write+'a>, linewidth: usize) -> Printer<'a> { pub struct Printer<'a> { out: Box<io::Write+'a>, - buf_len: usize, + buf_max_len: usize, /// Width of lines we're constrained to margin: isize, /// Number of spaces left on line @@ -297,6 +299,12 @@ struct BufEntry { size: isize, } +impl Default for BufEntry { + fn default() -> Self { + BufEntry { token: Token::Eof, size: 0 } + } +} + impl<'a> Printer<'a> { pub fn last_token(&mut self) -> Token { self.buf[self.right].token.clone() @@ -322,7 +330,9 @@ impl<'a> Printer<'a> { self.right_total = 1; self.left = 0; self.right = 0; - } else { self.advance_right(); } + } else { + self.advance_right(); + } debug!("pp Begin({})/buffer Vec<{},{}>", b.offset, self.left, self.right); self.buf[self.right] = BufEntry { token: token, size: -self.right_total }; @@ -349,7 +359,9 @@ impl<'a> Printer<'a> { self.right_total = 1; self.left = 0; self.right = 0; - } else { self.advance_right(); } + } else { + self.advance_right(); + } debug!("pp Break({})/buffer Vec<{},{}>", b.offset, self.left, self.right); self.check_stack(0); @@ -408,7 +420,11 @@ impl<'a> Printer<'a> { } pub fn advance_right(&mut self) { self.right += 1; - self.right %= self.buf_len; + self.right %= self.buf_max_len; + // Extend the buf if necessary. + if self.right == self.buf.len() { + self.buf.push(BufEntry::default()); + } assert_ne!(self.right, self.left); } pub fn advance_left(&mut self) -> io::Result<()> { @@ -438,7 +454,7 @@ impl<'a> Printer<'a> { } self.left += 1; - self.left %= self.buf_len; + self.left %= self.buf_max_len; left_size = self.buf[self.left].size; } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 88860df10e2..d8228e2b28b 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -656,7 +656,7 @@ pub trait PrintState<'a> { style: ast::StrStyle) -> io::Result<()> { let st = match style { ast::StrStyle::Cooked => { - (format!("\"{}\"", parse::escape_default(st))) + (format!("\"{}\"", st.escape_default())) } ast::StrStyle::Raw(n) => { (format!("r{delim}\"{string}\"{delim}", @@ -714,6 +714,22 @@ pub trait PrintState<'a> { Ok(()) } + fn print_attribute_path(&mut self, path: &ast::Path) -> io::Result<()> { + for (i, segment) in path.segments.iter().enumerate() { + if i > 0 { + self.writer().word("::")? + } + if segment.ident.name != keywords::CrateRoot.name() && + segment.ident.name != keywords::DollarCrate.name() + { + self.writer().word(&segment.ident.name.as_str())?; + } else if segment.ident.name == keywords::DollarCrate.name() { + self.print_dollar_crate(segment.ident.span.ctxt())?; + } + } + Ok(()) + } + fn print_attribute(&mut self, attr: &ast::Attribute) -> io::Result<()> { self.print_attribute_inline(attr, false) } @@ -735,17 +751,7 @@ pub trait PrintState<'a> { if let Some(mi) = attr.meta() { self.print_meta_item(&mi)? } else { - for (i, segment) in attr.path.segments.iter().enumerate() { - if i > 0 { - self.writer().word("::")? - } - if segment.ident.name != keywords::CrateRoot.name() && - segment.ident.name != keywords::DollarCrate.name() { - self.writer().word(&segment.ident.name.as_str())?; - } else if segment.ident.name == keywords::DollarCrate.name() { - self.print_dollar_crate(segment.ident.span.ctxt())?; - } - } + self.print_attribute_path(&attr.path)?; self.writer().space()?; self.print_tts(attr.tokens.clone())?; } @@ -767,16 +773,15 @@ pub trait PrintState<'a> { fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> { self.ibox(INDENT_UNIT)?; match item.node { - ast::MetaItemKind::Word => { - self.writer().word(&item.ident.name.as_str())?; - } + ast::MetaItemKind::Word => self.print_attribute_path(&item.ident)?, ast::MetaItemKind::NameValue(ref value) => { - self.word_space(&item.ident.name.as_str())?; + self.print_attribute_path(&item.ident)?; + self.writer().space()?; self.word_space("=")?; self.print_literal(value)?; } ast::MetaItemKind::List(ref items) => { - self.writer().word(&item.ident.name.as_str())?; + self.print_attribute_path(&item.ident)?; self.popen()?; self.commasep(Consistent, &items[..], |
