From 77ced43a53e3c5cd22afdcb7f471786f180db8e8 Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Tue, 30 Jan 2018 13:12:37 +0900 Subject: [PATCH 1/7] Add tests for a new feature 'tool_attributes' --- .../feature-gate-tool_attributes.rs | 15 ++++++++++++ src/test/compile-fail/unknown-tool-name.rs | 16 +++++++++++++ .../compile-fail/unknown_tool_attributes-1.rs | 18 +++++++++++++++ src/test/run-pass/tool_attributes.rs | 23 +++++++++++++++++++ src/test/ui/feature-gate-tool_attributes.rs | 15 ++++++++++++ .../ui/feature-gate-tool_attributes.stderr | 10 ++++++++ 6 files changed, 97 insertions(+) create mode 100644 src/test/compile-fail/feature-gate-tool_attributes.rs create mode 100644 src/test/compile-fail/unknown-tool-name.rs create mode 100644 src/test/compile-fail/unknown_tool_attributes-1.rs create mode 100644 src/test/run-pass/tool_attributes.rs create mode 100644 src/test/ui/feature-gate-tool_attributes.rs create mode 100644 src/test/ui/feature-gate-tool_attributes.stderr diff --git a/src/test/compile-fail/feature-gate-tool_attributes.rs b/src/test/compile-fail/feature-gate-tool_attributes.rs new file mode 100644 index 0000000000000..5a7536ca3304b --- /dev/null +++ b/src/test/compile-fail/feature-gate-tool_attributes.rs @@ -0,0 +1,15 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + #[rustfmt::skip] //~ ERROR scoped attribute `rustfmt::skip` is experimental + let x = + 3; +} diff --git a/src/test/compile-fail/unknown-tool-name.rs b/src/test/compile-fail/unknown-tool-name.rs new file mode 100644 index 0000000000000..1ee837b05d8df --- /dev/null +++ b/src/test/compile-fail/unknown-tool-name.rs @@ -0,0 +1,16 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(tool_attributes)] + +#![foo::bar] //~ ERROR An unkown tool name found in scoped attributes: `foo::bar`. [E0693] + +#[foo::bar] //~ ERROR An unkown tool name found in scoped attributes: `foo::bar`. [E0693] +fn main() {} diff --git a/src/test/compile-fail/unknown_tool_attributes-1.rs b/src/test/compile-fail/unknown_tool_attributes-1.rs new file mode 100644 index 0000000000000..08a7bfed2784f --- /dev/null +++ b/src/test/compile-fail/unknown_tool_attributes-1.rs @@ -0,0 +1,18 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Make sure that 'custom_attributes' feature does not allow scoped attributes. + +#![feature(custom_attributes)] + +#[foo::bar] +//~^ ERROR scoped attribute `foo::bar` is experimental (see issue #44690) [E0658] +//~^^ ERROR An unkown tool name found in scoped attributes: `foo::bar`. [E0693] +fn main() {} diff --git a/src/test/run-pass/tool_attributes.rs b/src/test/run-pass/tool_attributes.rs new file mode 100644 index 0000000000000..eb13930de403f --- /dev/null +++ b/src/test/run-pass/tool_attributes.rs @@ -0,0 +1,23 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Scoped attributes should not trigger an unused attributes lint. + +#![feature(tool_attributes)] +#![deny(unused_attributes)] + +fn main() { + #[rustfmt::skip] + foo (); +} + +fn foo() { + assert!(true); +} diff --git a/src/test/ui/feature-gate-tool_attributes.rs b/src/test/ui/feature-gate-tool_attributes.rs new file mode 100644 index 0000000000000..2b7cf56d93812 --- /dev/null +++ b/src/test/ui/feature-gate-tool_attributes.rs @@ -0,0 +1,15 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + #[rustfmt::skip] //~ ERROR scoped attribute `rustfmt::skip` is experimental + let x = 3 + ; +} diff --git a/src/test/ui/feature-gate-tool_attributes.stderr b/src/test/ui/feature-gate-tool_attributes.stderr new file mode 100644 index 0000000000000..13307bd713309 --- /dev/null +++ b/src/test/ui/feature-gate-tool_attributes.stderr @@ -0,0 +1,10 @@ +error[E0658]: scoped attribute `rustfmt::skip` is experimental (see issue #44690) + --> $DIR/feature-gate-tool_attributes.rs:12:5 + | +12 | #[rustfmt::skip] //~ ERROR scoped attribute `rustfmt::skip` is experimental + | ^^^^^^^^^^^^^^^^ + | + = help: add #![feature(tool_attributes)] to the crate attributes to enable + +error: aborting due to previous error + From b93af0132da5ab2dc6387de48a911f7e68b16730 Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Tue, 30 Jan 2018 14:30:39 +0900 Subject: [PATCH 2/7] Allow Path for name of MetaItem --- src/librustc/ich/impls_syntax.rs | 16 ++++-- src/librustc_driver/lib.rs | 2 +- src/librustc_resolve/macros.rs | 2 +- src/libsyntax/ast.rs | 4 +- src/libsyntax/attr.rs | 94 +++++++++++++++++++++++--------- src/libsyntax/ext/derive.rs | 3 +- src/libsyntax/ext/expand.rs | 2 +- src/libsyntax/parse/attr.rs | 8 +-- src/libsyntax/parse/parser.rs | 20 ------- src/libsyntax/print/pprust.rs | 36 ++++++------ 10 files changed, 110 insertions(+), 77 deletions(-) diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index c31a5c9d86d77..520e599c746ba 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -190,6 +190,17 @@ impl<'gcx> HashStable> for [ast::Attribute] { } } +impl<'gcx> HashStable> for ast::Path { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + self.segments.len().hash_stable(hcx, hasher); + for segment in &self.segments { + segment.identifier.name.hash_stable(hcx, hasher); + } + } +} + impl<'gcx> HashStable> for ast::Attribute { fn hash_stable(&self, hcx: &mut StableHashingContext<'gcx>, @@ -208,10 +219,7 @@ impl<'gcx> HashStable> for ast::Attribute { } = *self; style.hash_stable(hcx, hasher); - path.segments.len().hash_stable(hcx, hasher); - for segment in &path.segments { - segment.identifier.name.hash_stable(hcx, hasher); - } + path.hash_stable(hcx, hasher); for tt in tokens.trees() { tt.hash_stable(hcx, hasher); } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 05dcaf731352a..95bf5b39cc095 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -1012,7 +1012,7 @@ impl RustcDefaultCalls { let mut cfgs = Vec::new(); for &(name, ref value) in sess.parse_sess.config.iter() { let gated_cfg = GatedCfg::gate(&ast::MetaItem { - name, + name: ast::Path::from_ident(DUMMY_SP, name.to_ident()), node: ast::MetaItemKind::Word, span: DUMMY_SP, }); diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 080ef3252a633..6ccf1b053e463 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -234,7 +234,7 @@ impl<'a> base::Resolver for Resolver<'a> { if name == "derive" { let result = attrs[i].parse_list(&self.session.parse_sess, |parser| { - parser.parse_path_allowing_meta(PathStyle::Mod) + parser.parse_path(PathStyle::Mod) }); let mut traits = match result { diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index c7ab6158256ba..ccdec5954c66c 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -468,10 +468,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 name: Name, + pub name: Path, pub node: MetaItemKind, pub span: Span, } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index d18d6f5e6bd0d..e25a68de4e399 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -18,7 +18,7 @@ use ast; use ast::{AttrId, Attribute, Name, Ident}; 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, DUMMY_SP}; use errors::Handler; use feature_gate::{Features, GatedCfg}; @@ -205,6 +205,10 @@ impl NestedMetaItem { } } +fn name_from_path(path: &ast::Path) -> Name { + path.segments.iter().next().unwrap().identifier.name +} + impl Attribute { pub fn check_name(&self, name: &str) -> bool { let matches = self.path == name; @@ -252,7 +256,7 @@ impl Attribute { impl MetaItem { pub fn name(&self) -> Name { - self.name + name_from_path(&self.name) } pub fn value_str(&self) -> Option { @@ -301,10 +305,7 @@ impl Attribute { pub fn meta(&self) -> Option { let mut tokens = self.tokens.trees().peekable(); Some(MetaItem { - name: match self.path.segments.len() { - 1 => self.path.segments[0].identifier.name, - _ => return None, - }, + name: self.path.clone(), node: if let Some(node) = MetaItemKind::from_tokens(&mut tokens) { if tokens.peek().is_some() { return None; @@ -349,12 +350,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 { - name: self.path.segments.last().unwrap().identifier.name, + name: self.path.clone(), node: self.parse(sess, |parser| parser.parse_meta_item_kind())?, span: self.span, }) @@ -407,16 +404,26 @@ pub fn mk_word_item(name: Name) -> MetaItem { mk_spanned_word_item(DUMMY_SP, name) } +macro_rules! mk_spanned_meta_item { + ($sp:ident, $name:ident, $node:expr) => { + MetaItem { + span: $sp, + name: ast::Path::from_ident($sp, ast::Ident::with_empty_ctxt($name)), + node: $node, + } + } +} + pub fn mk_spanned_name_value_item(sp: Span, name: Name, value: ast::Lit) -> MetaItem { - MetaItem { span: sp, name: name, node: MetaItemKind::NameValue(value) } + mk_spanned_meta_item!(sp, name, MetaItemKind::NameValue(value)) } pub fn mk_spanned_list_item(sp: Span, name: Name, items: Vec) -> MetaItem { - MetaItem { span: sp, name: name, node: MetaItemKind::List(items) } + mk_spanned_meta_item!(sp, name, MetaItemKind::List(items)) } pub fn mk_spanned_word_item(sp: Span, name: Name) -> MetaItem { - MetaItem { span: sp, name: name, node: MetaItemKind::Word } + mk_spanned_meta_item!(sp, name, MetaItemKind::Word) } pub fn mk_attr_id() -> AttrId { @@ -440,7 +447,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.span, ast::Ident::with_empty_ctxt(item.name)), + path: item.name, tokens: item.node.tokens(item.span), is_sugared_doc: false, span: sp, @@ -458,7 +465,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.span, ast::Ident::with_empty_ctxt(item.name)), + path: item.name, tokens: item.node.tokens(item.span), is_sugared_doc: false, span: sp, @@ -600,7 +607,7 @@ pub fn eval_condition(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.name.as_str() { + match &*cfg.name().as_str() { "any" => mis.iter().any(|mi| { eval_condition(mi.meta_item().unwrap(), sess, eval) }), @@ -731,7 +738,7 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler, } } - match &*meta.name.as_str() { + match &*meta.name().as_str() { "rustc_deprecated" => { if rustc_depr.is_some() { span_err!(diagnostic, item_sp, E0540, @@ -1106,18 +1113,52 @@ impl IntType { impl MetaItem { fn tokens(&self) -> TokenStream { - let ident = TokenTree::Token(self.span, Token::Ident(Ident::with_empty_ctxt(self.name))); - 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.name.segments.iter().enumerate() { + let is_first = i == 0; + if !is_first { + let mod_sep_span = Span::new(last_pos, segment.span.lo(), segment.span.ctxt()); + idents.push(TokenTree::Token(mod_sep_span, Token::ModSep).into()); + } + idents.push(TokenTree::Token(segment.span, Token::Ident(segment.identifier)).into()); + last_pos = segment.span.hi(); + } + idents.push(self.node.tokens(self.span)); + TokenStream::concat(idents) } fn from_tokens(tokens: &mut iter::Peekable) -> Option where I: Iterator, { - let (span, name) = match tokens.next() { - Some(TokenTree::Token(span, Token::Ident(ident))) => (span, ident.name), + let name = match tokens.next() { + Some(TokenTree::Token(span, Token::Ident(ident))) => { + if let Some(TokenTree::Token(_, Token::ModSep)) = tokens.peek() { + tokens.next(); + let mut segments = vec![]; + loop { + if let Some(TokenTree::Token(span, Token::Ident(ident))) = tokens.next() { + segments.push(ast::PathSegment::from_ident(ident, span)); + } else { + return None; + } + if let Some(TokenTree::Token(_, Token::ModSep)) = tokens.peek() { + tokens.next(); + } else { + break; + } + } + ast::Path { span, segments } + } else { + ast::Path::from_ident(span, ident) + } + } Some(TokenTree::Token(_, Token::Interpolated(ref nt))) => match nt.0 { - token::Nonterminal::NtIdent(ident) => (ident.span, ident.node.name), + token::Nonterminal::NtIdent(ident) => { + ast::Path::from_ident(ident.span, ident.node) + } token::Nonterminal::NtMeta(ref meta) => return Some(meta.clone()), + token::Nonterminal::NtPath(ref path) => path.clone(), _ => return None, }, _ => return None, @@ -1126,10 +1167,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(name.span.hi()), + _ => name.span.hi(), }; - Some(MetaItem { name, node, span: span.with_hi(hi) }) + let span = name.span.with_hi(hi); + Some(MetaItem { name, node, span }) } } diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index c7fa0331c1bd5..25d5c6505def1 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -26,8 +26,7 @@ pub fn collect_derives(cx: &mut ExtCtxt, attrs: &mut Vec) -> Vec return true; } - match attr.parse_list(cx.parse_sess, - |parser| parser.parse_path_allowing_meta(PathStyle::Mod)) { + match attr.parse_list(cx.parse_sess, |parser| parser.parse_path(PathStyle::Mod)) { Ok(ref traits) if traits.is_empty() => { cx.span_warn(attr.span, "empty trait list in `derive`"); false diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 44a073545a730..14875f0e91c0b 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -689,7 +689,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 - name: keywords::Invalid.name(), + name: Path::from_ident(DUMMY_SP, keywords::Invalid.ident()), span: DUMMY_SP, node: ast::MetaItemKind::Word, }; diff --git a/src/libsyntax/parse/attr.rs b/src/libsyntax/parse/attr.rs index 053746b579dcb..3cfba83ac7e4d 100644 --- a/src/libsyntax/parse/attr.rs +++ b/src/libsyntax/parse/attr.rs @@ -158,8 +158,7 @@ impl<'a> Parser<'a> { }; Ok(if let Some(meta) = meta { self.bump(); - (ast::Path::from_ident(meta.span, ast::Ident::with_empty_ctxt(meta.name)), - meta.node.tokens(meta.span)) + (meta.name, meta.node.tokens(meta.span)) } else { (self.parse_path(PathStyle::Mod)?, self.parse_tokens()) }) @@ -235,9 +234,10 @@ impl<'a> Parser<'a> { } let lo = self.span; - let ident = self.parse_ident()?; + let name = self.parse_path(PathStyle::Mod)?; let node = self.parse_meta_item_kind()?; - Ok(ast::MetaItem { name: ident.name, node: node, span: lo.to(self.prev_span) }) + let span = lo.to(self.prev_span); + Ok(ast::MetaItem { name, node, span }) } pub fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index dc3745fc4a3ee..f55e5415821ff 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1988,26 +1988,6 @@ impl<'a> Parser<'a> { Ok(ast::Path { segments, span: lo.to(self.prev_span) }) } - /// Like `parse_path`, but also supports parsing `Word` meta items into paths for back-compat. - /// This is used when parsing derive macro paths in `#[derive]` attributes. - pub fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, ast::Path> { - let meta_ident = match self.token { - token::Interpolated(ref nt) => match nt.0 { - token::NtMeta(ref meta) => match meta.node { - ast::MetaItemKind::Word => Some(ast::Ident::with_empty_ctxt(meta.name)), - _ => None, - }, - _ => None, - }, - _ => None, - }; - if let Some(ident) = meta_ident { - self.bump(); - return Ok(ast::Path::from_ident(self.prev_span, ident)); - } - self.parse_path(style) - } - fn parse_path_segments(&mut self, segments: &mut Vec, style: PathStyle, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ae459c668aae4..60628018e703a 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -708,6 +708,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.identifier.name != keywords::CrateRoot.name() && + segment.identifier.name != keywords::DollarCrate.name() + { + self.writer().word(&segment.identifier.name.as_str())?; + } else if segment.identifier.name == keywords::DollarCrate.name() { + self.print_dollar_crate(segment.identifier.ctxt)?; + } + } + Ok(()) + } + fn print_attribute(&mut self, attr: &ast::Attribute) -> io::Result<()> { self.print_attribute_inline(attr, false) } @@ -729,17 +745,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.identifier.name != keywords::CrateRoot.name() && - segment.identifier.name != keywords::DollarCrate.name() { - self.writer().word(&segment.identifier.name.as_str())?; - } else if segment.identifier.name == keywords::DollarCrate.name() { - self.print_dollar_crate(segment.identifier.ctxt)?; - } - } + self.print_attribute_path(&attr.path)?; self.writer().space()?; self.print_tts(attr.tokens.clone())?; } @@ -761,16 +767,14 @@ 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.name.as_str())?; - } + ast::MetaItemKind::Word => self.print_attribute_path(&item.name)?, ast::MetaItemKind::NameValue(ref value) => { - self.word_space(&item.name.as_str())?; + self.print_attribute_path(&item.name)?; self.word_space("=")?; self.print_literal(value)?; } ast::MetaItemKind::List(ref items) => { - self.writer().word(&item.name.as_str())?; + self.print_attribute_path(&item.name)?; self.popen()?; self.commasep(Consistent, &items[..], From f2b4234e0e3446d36eceb840fc6d709af9a03ce7 Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Tue, 30 Jan 2018 14:53:01 +0900 Subject: [PATCH 3/7] Remove Option from the return type of Attribute::name() --- src/librustc/hir/check_attr.rs | 11 +++-------- src/librustc/ich/impls_syntax.rs | 5 ++--- src/librustc/lint/levels.rs | 2 +- src/librustc/middle/stability.rs | 2 +- src/librustc_lint/builtin.rs | 3 +-- src/librustc_lint/lib.rs | 1 - src/librustc_lint/unused.rs | 3 +-- src/librustc_resolve/macros.rs | 6 +++--- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/html/render.rs | 2 +- src/libsyntax/attr.rs | 9 ++++----- src/libsyntax/ext/derive.rs | 3 ++- src/libsyntax/feature_gate.rs | 2 +- src/libsyntax/parse/parser.rs | 20 ++++++++++++++++++++ src/libsyntax/print/pprust.rs | 1 + src/libsyntax_ext/deriving/custom.rs | 8 +++----- src/libsyntax_ext/deriving/generic/mod.rs | 2 +- 17 files changed, 46 insertions(+), 36 deletions(-) diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs index a073910fdc83f..41f3fb3665a1f 100644 --- a/src/librustc/hir/check_attr.rs +++ b/src/librustc/hir/check_attr.rs @@ -50,10 +50,8 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> { self.tcx.target_features_enabled(self.tcx.hir.local_def_id(item.id)); for attr in &item.attrs { - if let Some(name) = attr.name() { - if name == "inline" { - self.check_inline(attr, item, target) - } + if attr.name() == "inline" { + self.check_inline(attr, item, target) } } @@ -81,10 +79,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> { // ``` let hints: Vec<_> = item.attrs .iter() - .filter(|attr| match attr.name() { - Some(name) => name == "repr", - None => false, - }) + .filter(|attr| attr.name() == "repr") .filter_map(|attr| attr.meta_item_list()) .flat_map(|hints| hints) .collect(); diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index 520e599c746ba..ae39a8ca42537 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -178,8 +178,7 @@ impl<'gcx> HashStable> for [ast::Attribute] { let filtered: AccumulateVec<[&ast::Attribute; 8]> = self .iter() .filter(|attr| { - !attr.is_sugared_doc && - attr.name().map(|name| !hcx.is_ignored_attr(name)).unwrap_or(true) + !attr.is_sugared_doc && !hcx.is_ignored_attr(attr.name()) }) .collect(); @@ -206,7 +205,7 @@ impl<'gcx> HashStable> for ast::Attribute { hcx: &mut StableHashingContext<'gcx>, hasher: &mut StableHasher) { // Make sure that these have been filtered out. - debug_assert!(self.name().map(|name| !hcx.is_ignored_attr(name)).unwrap_or(true)); + debug_assert!(!hcx.is_ignored_attr(self.name())); debug_assert!(!self.is_sugared_doc); let ast::Attribute { diff --git a/src/librustc/lint/levels.rs b/src/librustc/lint/levels.rs index 4bc37747f2a76..42e265aac912c 100644 --- a/src/librustc/lint/levels.rs +++ b/src/librustc/lint/levels.rs @@ -197,7 +197,7 @@ impl<'a> LintLevelsBuilder<'a> { "malformed lint attribute"); }; for attr in attrs { - let level = match attr.name().and_then(|name| Level::from_str(&name.as_str())) { + let level = match Level::from_str(&attr.name().as_str()) { None => continue, Some(lvl) => lvl, }; diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index e80ea16f565ab..a342ceca53cbd 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -205,7 +205,7 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> { } else { // Emit errors for non-staged-api crates. for attr in attrs { - let tag = unwrap_or!(attr.name(), continue); + let tag = attr.name(); if tag == "unstable" || tag == "stable" || tag == "rustc_deprecated" { attr::mark_used(attr); self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \ diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index de55710bdf3d0..781f9d0d7d09c 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -663,9 +663,8 @@ impl LintPass for DeprecatedAttr { impl EarlyLintPass for DeprecatedAttr { fn check_attribute(&mut self, cx: &EarlyContext, attr: &ast::Attribute) { - let name = unwrap_or!(attr.name(), return); for &&(n, _, ref g) in &self.depr_attrs { - if name == n { + if attr.name() == n { if let &AttributeGate::Gated(Stability::Deprecated(link), ref name, ref reason, diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 699765dde03ff..48a7272a42631 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -33,7 +33,6 @@ #![feature(rustc_diagnostic_macros)] #![feature(slice_patterns)] -#[macro_use] extern crate syntax; #[macro_use] extern crate rustc; diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 439533fae49d9..16169f0bc1d49 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -174,8 +174,6 @@ impl LintPass for UnusedAttributes { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes { fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) { debug!("checking attribute: {:?}", attr); - let name = unwrap_or!(attr.name(), return); - // Note that check_name() marks the attribute as used if it matches. for &(ref name, ty, _) in BUILTIN_ATTRIBUTES { match ty { @@ -195,6 +193,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes { } } + let name = attr.name(); if !attr::is_used(attr) { debug!("Emitting warning for: {:?}", attr); cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute"); diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 6ccf1b053e463..223918e1952fa 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -210,7 +210,7 @@ impl<'a> base::Resolver for Resolver<'a> { fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec) -> Option { for i in 0..attrs.len() { - let name = unwrap_or!(attrs[i].name(), continue); + let name = attrs[i].name(); if self.session.plugin_attributes.borrow().iter() .any(|&(ref attr_nm, _)| name == &**attr_nm) { @@ -230,11 +230,11 @@ impl<'a> base::Resolver for Resolver<'a> { // Check for legacy derives for i in 0..attrs.len() { - let name = unwrap_or!(attrs[i].name(), continue); + let name = attrs[i].name(); if name == "derive" { let result = attrs[i].parse_list(&self.session.parse_sess, |parser| { - parser.parse_path(PathStyle::Mod) + parser.parse_path_allowing_meta(PathStyle::Mod) }); let mut traits = match result { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 66b5f3b5ea366..6bbb3e8991825 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -3359,7 +3359,7 @@ impl Clean> for doctree::Import { // #[doc(no_inline)] attribute is present. // Don't inline doc(hidden) imports so they can be stripped at a later stage. let denied = self.vis != hir::Public || self.attrs.iter().any(|a| { - a.name().unwrap() == "doc" && match a.meta_item_list() { + a.name() == "doc" && match a.meta_item_list() { Some(l) => attr::list_contains_name(&l, "no_inline") || attr::list_contains_name(&l, "hidden"), None => false, diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 1fb8f106cac03..0106b5ac2c94f 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -3077,7 +3077,7 @@ fn render_attributes(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result { let mut attrs = String::new(); for attr in &it.attrs.other_attrs { - let name = attr.name().unwrap(); + let name = attr.name(); if !ATTRIBUTE_WHITELIST.contains(&&*name.as_str()) { continue; } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index e25a68de4e399..672a7f6b188f4 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -218,11 +218,10 @@ impl Attribute { matches } - pub fn name(&self) -> Option { - match self.path.segments.len() { - 1 => Some(self.path.segments[0].identifier.name), - _ => None, - } + /// Returns the first segment of the name of this attribute. + /// E.g. `foo` for `#[foo]`, `rustfmt` for `#[rustfmt::skip]`. + pub fn name(&self) -> Name { + name_from_path(&self.path) } pub fn value_str(&self) -> Option { diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index 25d5c6505def1..c7fa0331c1bd5 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -26,7 +26,8 @@ pub fn collect_derives(cx: &mut ExtCtxt, attrs: &mut Vec) -> Vec return true; } - match attr.parse_list(cx.parse_sess, |parser| parser.parse_path(PathStyle::Mod)) { + match attr.parse_list(cx.parse_sess, + |parser| parser.parse_path_allowing_meta(PathStyle::Mod)) { Ok(ref traits) if traits.is_empty() => { cx.span_warn(attr.span, "empty trait list in `derive`"); false diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ea916d5168c33..ebcefa55a686c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -1074,7 +1074,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 { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f55e5415821ff..f8fde18691374 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1988,6 +1988,26 @@ impl<'a> Parser<'a> { Ok(ast::Path { segments, span: lo.to(self.prev_span) }) } + /// Like `parse_path`, but also supports parsing `Word` meta items into paths for back-compat. + /// This is used when parsing derive macro paths in `#[derive]` attributes. + pub fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, ast::Path> { + let meta_name = match self.token { + token::Interpolated(ref nt) => match nt.0 { + token::NtMeta(ref meta) => match meta.node { + ast::MetaItemKind::Word => Some(meta.name.clone()), + _ => None, + }, + _ => None, + }, + _ => None, + }; + if let Some(path) = meta_name { + self.bump(); + return Ok(path); + } + self.parse_path(style) + } + fn parse_path_segments(&mut self, segments: &mut Vec, style: PathStyle, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 60628018e703a..68177e45838a1 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -770,6 +770,7 @@ pub trait PrintState<'a> { ast::MetaItemKind::Word => self.print_attribute_path(&item.name)?, ast::MetaItemKind::NameValue(ref value) => { self.print_attribute_path(&item.name)?; + self.writer().space()?; self.word_space("=")?; self.print_literal(value)?; } diff --git a/src/libsyntax_ext/deriving/custom.rs b/src/libsyntax_ext/deriving/custom.rs index 22e78e9b426b2..f918298abda8b 100644 --- a/src/libsyntax_ext/deriving/custom.rs +++ b/src/libsyntax_ext/deriving/custom.rs @@ -22,11 +22,9 @@ struct MarkAttrs<'a>(&'a [ast::Name]); impl<'a> Visitor<'a> for MarkAttrs<'a> { fn visit_attribute(&mut self, attr: &Attribute) { - if let Some(name) = attr.name() { - if self.0.contains(&name) { - mark_used(attr); - mark_known(attr); - } + if self.0.contains(&attr.name()) { + mark_used(attr); + mark_known(attr); } } diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 0dfe9cb970efb..a2136b0560d8a 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -467,7 +467,7 @@ impl<'a> TraitDef<'a> { attrs.extend(item.attrs .iter() .filter(|a| { - a.name().is_some() && match &*a.name().unwrap().as_str() { + match &*a.name().as_str() { "allow" | "warn" | "deny" | "forbid" | "stable" | "unstable" => true, _ => false, } From 5146c0c1059ce26c61ddc05d404e7236db401f8a Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Tue, 30 Jan 2018 19:28:55 +0900 Subject: [PATCH 4/7] Gate tool_attributes feature --- src/libsyntax/attr.rs | 10 ++++++++++ src/libsyntax/diagnostic_list.rs | 1 + src/libsyntax/ext/expand.rs | 6 +++++- src/libsyntax/feature_gate.rs | 31 +++++++++++++++++++++++++------ 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 672a7f6b188f4..f5411df05d597 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -108,6 +108,12 @@ pub fn is_known(attr: &Attribute) -> bool { }) } +const RUST_KNOWN_TOOL: &[&str] = &["clippy", "rustfmt"]; + +pub fn is_known_tool(attr: &Attribute) -> bool { + RUST_KNOWN_TOOL.contains(&attr.name().as_str().as_ref()) +} + impl NestedMetaItem { /// Returns the MetaItem if self is a NestedMetaItemKind::MetaItem. pub fn meta_item(&self) -> Option<&MetaItem> { @@ -251,6 +257,10 @@ 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 { diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs index d841281e48580..085e2ae50c3a2 100644 --- a/src/libsyntax/diagnostic_list.rs +++ b/src/libsyntax/diagnostic_list.rs @@ -366,4 +366,5 @@ register_diagnostics! { E0589, // invalid `repr(align)` attribute E0629, // missing 'feature' (rustc_const_unstable) E0630, // rustc_const_unstable attribute must be paired with stable/unstable attribute + E0693, // an unknown tool name found in scoped attributes } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 14875f0e91c0b..5cac33ae07652 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -857,12 +857,16 @@ 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); } } 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); } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ebcefa55a686c..67e443c8e1371 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -449,6 +449,9 @@ declare_features! ( // Use `?` as the Kleene "at most one" operator (active, macro_at_most_once_rep, "1.25.0", Some(48075)), + + // Scoped attributes + (active, tool_attributes, "1.25.0", Some(44690)), ); declare_features! ( @@ -1114,12 +1117,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, + E0693, + "An unkown tool name found in scoped attributes: `{}`.", + 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)); + } } } } From 9dcb692c1e1c295ab712a98ae2ecbe2866eaab69 Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Tue, 30 Jan 2018 19:29:11 +0900 Subject: [PATCH 5/7] Add tool_attributes feature to unstable book --- .../src/language-features/tool-attributes.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/doc/unstable-book/src/language-features/tool-attributes.md diff --git a/src/doc/unstable-book/src/language-features/tool-attributes.md b/src/doc/unstable-book/src/language-features/tool-attributes.md new file mode 100644 index 0000000000000..15fc84a3e2a3d --- /dev/null +++ b/src/doc/unstable-book/src/language-features/tool-attributes.md @@ -0,0 +1,26 @@ +# `tool_attributes` + +The tracking issue for this feature is: [#44690] + +[#44690]: https://github.com/rust-lang/rust/issues/44690 + +------------------------ + +Tool attributes let you use scoped attributes to control the behavior +of certain tools. + +Currently tool names which can be appear in scoped attributes are restricted to +`clippy` and `rustfmt`. + +## An example + +```rust +#![feature(tool_attributes)] + +#[rustfmt::skip] +fn foo() { println!("hello, world"); } + +fn main() { + foo(); +} +``` From bbc0e62ca0884de6a0f28fe7966c7ce1c3f06543 Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Sun, 4 Feb 2018 15:36:26 +0900 Subject: [PATCH 6/7] Change Attribute::name to return the last segment And fix some typos --- src/libsyntax/attr.rs | 11 +++++++---- src/libsyntax/feature_gate.rs | 2 +- src/test/compile-fail/unknown-tool-name.rs | 4 ++-- src/test/compile-fail/unknown_tool_attributes-1.rs | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index f5411df05d597..bcc2cbb4ef635 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -111,7 +111,9 @@ pub fn is_known(attr: &Attribute) -> bool { const RUST_KNOWN_TOOL: &[&str] = &["clippy", "rustfmt"]; pub fn is_known_tool(attr: &Attribute) -> bool { - RUST_KNOWN_TOOL.contains(&attr.name().as_str().as_ref()) + let tool_name = + attr.path.segments.iter().next().expect("empty path in attribute").identifier.name; + RUST_KNOWN_TOOL.contains(&tool_name.as_str().as_ref()) } impl NestedMetaItem { @@ -212,7 +214,7 @@ impl NestedMetaItem { } fn name_from_path(path: &ast::Path) -> Name { - path.segments.iter().next().unwrap().identifier.name + path.segments.last().expect("empty path in attribute").identifier.name } impl Attribute { @@ -224,8 +226,8 @@ impl Attribute { matches } - /// Returns the first segment of the name of this attribute. - /// E.g. `foo` for `#[foo]`, `rustfmt` for `#[rustfmt::skip]`. + /// 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) } @@ -1124,6 +1126,7 @@ impl MetaItem { fn tokens(&self) -> TokenStream { let mut idents = vec![]; let mut last_pos = BytePos(0 as u32); + // FIXME: Share code with `parse_path`. for (i, segment) in self.name.segments.iter().enumerate() { let is_first = i == 0; if !is_first { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 67e443c8e1371..266802f846ecf 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -1127,7 +1127,7 @@ impl<'a> Context<'a> { self.parse_sess.span_diagnostic, attr.span, E0693, - "An unkown tool name found in scoped attributes: `{}`.", + "An unknown tool name found in scoped attributes: `{}`.", attr.path ); } diff --git a/src/test/compile-fail/unknown-tool-name.rs b/src/test/compile-fail/unknown-tool-name.rs index 1ee837b05d8df..0cb897917b445 100644 --- a/src/test/compile-fail/unknown-tool-name.rs +++ b/src/test/compile-fail/unknown-tool-name.rs @@ -10,7 +10,7 @@ #![feature(tool_attributes)] -#![foo::bar] //~ ERROR An unkown tool name found in scoped attributes: `foo::bar`. [E0693] +#![foo::bar] //~ ERROR An unknown tool name found in scoped attributes: `foo::bar`. [E0693] -#[foo::bar] //~ ERROR An unkown tool name found in scoped attributes: `foo::bar`. [E0693] +#[foo::bar] //~ ERROR An unknown tool name found in scoped attributes: `foo::bar`. [E0693] fn main() {} diff --git a/src/test/compile-fail/unknown_tool_attributes-1.rs b/src/test/compile-fail/unknown_tool_attributes-1.rs index 08a7bfed2784f..fbc4621946195 100644 --- a/src/test/compile-fail/unknown_tool_attributes-1.rs +++ b/src/test/compile-fail/unknown_tool_attributes-1.rs @@ -14,5 +14,5 @@ #[foo::bar] //~^ ERROR scoped attribute `foo::bar` is experimental (see issue #44690) [E0658] -//~^^ ERROR An unkown tool name found in scoped attributes: `foo::bar`. [E0693] +//~^^ ERROR An unknown tool name found in scoped attributes: `foo::bar`. [E0693] fn main() {} From 12b22875ab043c45566b67ee830f13ffa821ce53 Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Sun, 4 Feb 2018 16:00:24 +0900 Subject: [PATCH 7/7] Fix failures in rustdoc --- src/librustdoc/clean/cfg.rs | 240 ++++++++++-------------------------- 1 file changed, 62 insertions(+), 178 deletions(-) diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 5eb3e38d5b371..1c599f6df7f55 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -407,6 +407,42 @@ mod test { Cfg::Cfg(Symbol::intern(name), Some(Symbol::intern(value))) } + fn dummy_meta_item_word(name: &str) -> MetaItem { + MetaItem { + name: Path::from_ident(DUMMY_SP, Ident::from_str(name)), + node: MetaItemKind::Word, + span: DUMMY_SP, + } + } + + macro_rules! dummy_meta_item_list { + ($name:ident, [$($list:ident),* $(,)*]) => { + MetaItem { + name: Path::from_ident(DUMMY_SP, Ident::from_str(stringify!($name))), + node: MetaItemKind::List(vec![ + $( + dummy_spanned(NestedMetaItemKind::MetaItem( + dummy_meta_item_word(stringify!($list)), + )), + )* + ]), + span: DUMMY_SP, + } + }; + + ($name:ident, [$($list:expr),* $(,)*]) => { + MetaItem { + name: Path::from_ident(DUMMY_SP, Ident::from_str(stringify!($name))), + node: MetaItemKind::List(vec![ + $( + dummy_spanned(NestedMetaItemKind::MetaItem($list)), + )* + ]), + span: DUMMY_SP, + } + }; + } + #[test] fn test_cfg_not() { assert_eq!(!Cfg::False, Cfg::True); @@ -525,15 +561,11 @@ mod test { #[test] fn test_parse_ok() { - let mi = MetaItem { - name: Symbol::intern("all"), - node: MetaItemKind::Word, - span: DUMMY_SP, - }; + let mi = dummy_meta_item_word("all"); assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all"))); let mi = MetaItem { - name: Symbol::intern("all"), + name: Path::from_ident(DUMMY_SP, Ident::from_str("all")), node: MetaItemKind::NameValue(dummy_spanned(LitKind::Str( Symbol::intern("done"), StrStyle::Cooked, @@ -542,208 +574,60 @@ mod test { }; assert_eq!(Cfg::parse(&mi), Ok(name_value_cfg("all", "done"))); - let mi = MetaItem { - name: Symbol::intern("all"), - node: MetaItemKind::List(vec![ - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("a"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("b"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - ]), - span: DUMMY_SP, - }; + let mi = dummy_meta_item_list!(all, [a, b]); assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b"))); - let mi = MetaItem { - name: Symbol::intern("any"), - node: MetaItemKind::List(vec![ - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("a"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("b"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - ]), - span: DUMMY_SP, - }; + let mi = dummy_meta_item_list!(any, [a, b]); assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") | word_cfg("b"))); - let mi = MetaItem { - name: Symbol::intern("not"), - node: MetaItemKind::List(vec![ - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("a"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - ]), - span: DUMMY_SP, - }; + let mi = dummy_meta_item_list!(not, [a]); assert_eq!(Cfg::parse(&mi), Ok(!word_cfg("a"))); - let mi = MetaItem { - name: Symbol::intern("not"), - node: MetaItemKind::List(vec![ - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("any"), - node: MetaItemKind::List(vec![ - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("a"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("all"), - node: MetaItemKind::List(vec![ - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("b"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("c"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - ]), - span: DUMMY_SP, - })), - ]), - span: DUMMY_SP, - })), + let mi = dummy_meta_item_list!(not, [ + dummy_meta_item_list!(any, [ + dummy_meta_item_word("a"), + dummy_meta_item_list!(all, [b, c]), ]), - span: DUMMY_SP, - }; + ]); assert_eq!(Cfg::parse(&mi), Ok(!(word_cfg("a") | (word_cfg("b") & word_cfg("c"))))); - let mi = MetaItem { - name: Symbol::intern("all"), - node: MetaItemKind::List(vec![ - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("a"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("b"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("c"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - ]), - span: DUMMY_SP, - }; + let mi = dummy_meta_item_list!(all, [a, b, c]); assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b") & word_cfg("c"))); } #[test] fn test_parse_err() { let mi = MetaItem { - name: Symbol::intern("foo"), + name: Path::from_ident(DUMMY_SP, Ident::from_str("foo")), node: MetaItemKind::NameValue(dummy_spanned(LitKind::Bool(false))), span: DUMMY_SP, }; assert!(Cfg::parse(&mi).is_err()); - let mi = MetaItem { - name: Symbol::intern("not"), - node: MetaItemKind::List(vec![ - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("a"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("b"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - ]), - span: DUMMY_SP, - }; + let mi = dummy_meta_item_list!(not, [a, b]); assert!(Cfg::parse(&mi).is_err()); - let mi = MetaItem { - name: Symbol::intern("not"), - node: MetaItemKind::List(vec![]), - span: DUMMY_SP, - }; + let mi = dummy_meta_item_list!(not, []); assert!(Cfg::parse(&mi).is_err()); - let mi = MetaItem { - name: Symbol::intern("foo"), - node: MetaItemKind::List(vec![ - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("a"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - ]), - span: DUMMY_SP, - }; + let mi = dummy_meta_item_list!(foo, []); assert!(Cfg::parse(&mi).is_err()); - let mi = MetaItem { - name: Symbol::intern("all"), - node: MetaItemKind::List(vec![ - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("foo"), - node: MetaItemKind::List(vec![]), - span: DUMMY_SP, - })), - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("b"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - ]), - span: DUMMY_SP, - }; + let mi = dummy_meta_item_list!(all, [ + dummy_meta_item_list!(foo, []), + dummy_meta_item_word("b"), + ]); assert!(Cfg::parse(&mi).is_err()); - let mi = MetaItem { - name: Symbol::intern("any"), - node: MetaItemKind::List(vec![ - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("a"), - node: MetaItemKind::Word, - span: DUMMY_SP, - })), - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("foo"), - node: MetaItemKind::List(vec![]), - span: DUMMY_SP, - })), - ]), - span: DUMMY_SP, - }; + let mi = dummy_meta_item_list!(any, [ + dummy_meta_item_word("a"), + dummy_meta_item_list!(foo, []), + ]); assert!(Cfg::parse(&mi).is_err()); - let mi = MetaItem { - name: Symbol::intern("not"), - node: MetaItemKind::List(vec![ - dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem { - name: Symbol::intern("foo"), - node: MetaItemKind::List(vec![]), - span: DUMMY_SP, - })), - ]), - span: DUMMY_SP, - }; + let mi = dummy_meta_item_list!(not, [ + dummy_meta_item_list!(foo, []), + ]); assert!(Cfg::parse(&mi).is_err()); }