Auto merge of #50030 - flip1995:rfc2103, r=petrochenkov
Implement tool_attributes feature (RFC 2103) cc #44690 This is currently just a rebased and compiling (hopefully) version of #47773. Let's see if travis likes this. I will add the implementation for `tool_lints` this week.
This commit is contained in:
commit
d68b0eceaa
35 changed files with 391 additions and 302 deletions
|
@ -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();
|
||||||
|
}
|
||||||
|
```
|
|
@ -153,10 +153,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
|
||||||
// ```
|
// ```
|
||||||
let hints: Vec<_> = item.attrs
|
let hints: Vec<_> = item.attrs
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|attr| match attr.name() {
|
.filter(|attr| attr.name() == "repr")
|
||||||
Some(name) => name == "repr",
|
|
||||||
None => false,
|
|
||||||
})
|
|
||||||
.filter_map(|attr| attr.meta_item_list())
|
.filter_map(|attr| attr.meta_item_list())
|
||||||
.flat_map(|hints| hints)
|
.flat_map(|hints| hints)
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -311,7 +308,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
|
||||||
|
|
||||||
fn check_used(&self, item: &hir::Item, target: Target) {
|
fn check_used(&self, item: &hir::Item, target: Target) {
|
||||||
for attr in &item.attrs {
|
for attr in &item.attrs {
|
||||||
if attr.name().map(|name| name == "used").unwrap_or(false) && target != Target::Static {
|
if attr.name() == "used" && target != Target::Static {
|
||||||
self.tcx.sess
|
self.tcx.sess
|
||||||
.span_err(attr.span, "attribute must be applied to a `static` variable");
|
.span_err(attr.span, "attribute must be applied to a `static` variable");
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,8 +199,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] {
|
||||||
let filtered: AccumulateVec<[&ast::Attribute; 8]> = self
|
let filtered: AccumulateVec<[&ast::Attribute; 8]> = self
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|attr| {
|
.filter(|attr| {
|
||||||
!attr.is_sugared_doc &&
|
!attr.is_sugared_doc && !hcx.is_ignored_attr(attr.name())
|
||||||
attr.name().map(|name| !hcx.is_ignored_attr(name)).unwrap_or(true)
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -211,12 +210,23 @@ impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> HashStable<StableHashingContext<'a>> for ast::Path {
|
||||||
|
fn hash_stable<W: StableHasherResult>(&self,
|
||||||
|
hcx: &mut StableHashingContext<'a>,
|
||||||
|
hasher: &mut StableHasher<W>) {
|
||||||
|
self.segments.len().hash_stable(hcx, hasher);
|
||||||
|
for segment in &self.segments {
|
||||||
|
segment.ident.name.hash_stable(hcx, hasher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> HashStable<StableHashingContext<'a>> for ast::Attribute {
|
impl<'a> HashStable<StableHashingContext<'a>> for ast::Attribute {
|
||||||
fn hash_stable<W: StableHasherResult>(&self,
|
fn hash_stable<W: StableHasherResult>(&self,
|
||||||
hcx: &mut StableHashingContext<'a>,
|
hcx: &mut StableHashingContext<'a>,
|
||||||
hasher: &mut StableHasher<W>) {
|
hasher: &mut StableHasher<W>) {
|
||||||
// Make sure that these have been filtered out.
|
// 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);
|
debug_assert!(!self.is_sugared_doc);
|
||||||
|
|
||||||
let ast::Attribute {
|
let ast::Attribute {
|
||||||
|
@ -229,10 +239,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ast::Attribute {
|
||||||
} = *self;
|
} = *self;
|
||||||
|
|
||||||
style.hash_stable(hcx, hasher);
|
style.hash_stable(hcx, hasher);
|
||||||
path.segments.len().hash_stable(hcx, hasher);
|
path.hash_stable(hcx, hasher);
|
||||||
for segment in &path.segments {
|
|
||||||
segment.ident.name.hash_stable(hcx, hasher);
|
|
||||||
}
|
|
||||||
for tt in tokens.trees() {
|
for tt in tokens.trees() {
|
||||||
tt.hash_stable(hcx, hasher);
|
tt.hash_stable(hcx, hasher);
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,7 +198,7 @@ impl<'a> LintLevelsBuilder<'a> {
|
||||||
"malformed lint attribute");
|
"malformed lint attribute");
|
||||||
};
|
};
|
||||||
for attr in attrs {
|
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,
|
None => continue,
|
||||||
Some(lvl) => lvl,
|
Some(lvl) => lvl,
|
||||||
};
|
};
|
||||||
|
@ -221,7 +221,7 @@ impl<'a> LintLevelsBuilder<'a> {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let name = word.ident.name;
|
let name = word.name();
|
||||||
match store.check_lint_name(&name.as_str()) {
|
match store.check_lint_name(&name.as_str()) {
|
||||||
CheckLintNameResult::Ok(ids) => {
|
CheckLintNameResult::Ok(ids) => {
|
||||||
let src = LintSource::Node(name, li.span);
|
let src = LintSource::Node(name, li.span);
|
||||||
|
@ -260,7 +260,7 @@ impl<'a> LintLevelsBuilder<'a> {
|
||||||
Some(li.span.into()),
|
Some(li.span.into()),
|
||||||
&msg);
|
&msg);
|
||||||
if name.as_str().chars().any(|c| c.is_uppercase()) {
|
if name.as_str().chars().any(|c| c.is_uppercase()) {
|
||||||
let name_lower = name.as_str().to_lowercase();
|
let name_lower = name.as_str().to_lowercase().to_string();
|
||||||
if let CheckLintNameResult::NoLint =
|
if let CheckLintNameResult::NoLint =
|
||||||
store.check_lint_name(&name_lower) {
|
store.check_lint_name(&name_lower) {
|
||||||
db.emit();
|
db.emit();
|
||||||
|
|
|
@ -205,7 +205,7 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
|
||||||
} else {
|
} else {
|
||||||
// Emit errors for non-staged-api crates.
|
// Emit errors for non-staged-api crates.
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
let tag = unwrap_or!(attr.name(), continue);
|
let tag = attr.name();
|
||||||
if tag == "unstable" || tag == "stable" || tag == "rustc_deprecated" {
|
if tag == "unstable" || tag == "stable" || tag == "rustc_deprecated" {
|
||||||
attr::mark_used(attr);
|
attr::mark_used(attr);
|
||||||
self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \
|
self.tcx.sess.span_err(attr.span(), "stability attributes may not be used \
|
||||||
|
|
|
@ -1683,7 +1683,7 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> ast::CrateConfig {
|
||||||
early_error(ErrorOutputType::default(), &msg)
|
early_error(ErrorOutputType::default(), &msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
(meta_item.ident.name, meta_item.value_str())
|
(meta_item.name(), meta_item.value_str())
|
||||||
})
|
})
|
||||||
.collect::<ast::CrateConfig>()
|
.collect::<ast::CrateConfig>()
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,7 +190,7 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedDirective {
|
||||||
for command in self.subcommands.iter().chain(Some(self)).rev() {
|
for command in self.subcommands.iter().chain(Some(self)).rev() {
|
||||||
if let Some(ref condition) = command.condition {
|
if let Some(ref condition) = command.condition {
|
||||||
if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
|
if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
|
||||||
options.contains(&(c.ident.name.as_str().to_string(),
|
options.contains(&(c.name().as_str().to_string(),
|
||||||
match c.value_str().map(|s| s.as_str().to_string()) {
|
match c.value_str().map(|s| s.as_str().to_string()) {
|
||||||
Some(s) => Some(s),
|
Some(s) => Some(s),
|
||||||
None => None
|
None => None
|
||||||
|
|
|
@ -1060,7 +1060,7 @@ impl RustcDefaultCalls {
|
||||||
let mut cfgs = Vec::new();
|
let mut cfgs = Vec::new();
|
||||||
for &(name, ref value) in sess.parse_sess.config.iter() {
|
for &(name, ref value) in sess.parse_sess.config.iter() {
|
||||||
let gated_cfg = GatedCfg::gate(&ast::MetaItem {
|
let gated_cfg = GatedCfg::gate(&ast::MetaItem {
|
||||||
ident: ast::Ident::with_empty_ctxt(name),
|
ident: ast::Path::from_ident(name.to_ident()),
|
||||||
node: ast::MetaItemKind::Word,
|
node: ast::MetaItemKind::Word,
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
});
|
});
|
||||||
|
|
|
@ -110,7 +110,7 @@ impl<'a, 'tcx> IfThisChanged<'a, 'tcx> {
|
||||||
for list_item in attr.meta_item_list().unwrap_or_default() {
|
for list_item in attr.meta_item_list().unwrap_or_default() {
|
||||||
match list_item.word() {
|
match list_item.word() {
|
||||||
Some(word) if value.is_none() =>
|
Some(word) if value.is_none() =>
|
||||||
value = Some(word.ident.name),
|
value = Some(word.name()),
|
||||||
_ =>
|
_ =>
|
||||||
// FIXME better-encapsulate meta_item (don't directly access `node`)
|
// FIXME better-encapsulate meta_item (don't directly access `node`)
|
||||||
span_bug!(list_item.span(), "unexpected meta-item {:?}", list_item.node),
|
span_bug!(list_item.span(), "unexpected meta-item {:?}", list_item.node),
|
||||||
|
|
|
@ -675,9 +675,8 @@ impl LintPass for DeprecatedAttr {
|
||||||
|
|
||||||
impl EarlyLintPass for DeprecatedAttr {
|
impl EarlyLintPass for DeprecatedAttr {
|
||||||
fn check_attribute(&mut self, cx: &EarlyContext, attr: &ast::Attribute) {
|
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 {
|
for &&(n, _, ref g) in &self.depr_attrs {
|
||||||
if name == n {
|
if attr.name() == n {
|
||||||
if let &AttributeGate::Gated(Stability::Deprecated(link),
|
if let &AttributeGate::Gated(Stability::Deprecated(link),
|
||||||
ref name,
|
ref name,
|
||||||
ref reason,
|
ref reason,
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
#![feature(quote)]
|
#![feature(quote)]
|
||||||
#![feature(rustc_diagnostic_macros)]
|
#![feature(rustc_diagnostic_macros)]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate syntax;
|
extern crate syntax;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rustc;
|
extern crate rustc;
|
||||||
|
|
|
@ -192,8 +192,6 @@ impl LintPass for UnusedAttributes {
|
||||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
|
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
|
||||||
fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) {
|
fn check_attribute(&mut self, cx: &LateContext, attr: &ast::Attribute) {
|
||||||
debug!("checking attribute: {:?}", attr);
|
debug!("checking attribute: {:?}", attr);
|
||||||
let name = unwrap_or!(attr.name(), return);
|
|
||||||
|
|
||||||
// Note that check_name() marks the attribute as used if it matches.
|
// Note that check_name() marks the attribute as used if it matches.
|
||||||
for &(ref name, ty, _) in BUILTIN_ATTRIBUTES {
|
for &(ref name, ty, _) in BUILTIN_ATTRIBUTES {
|
||||||
match ty {
|
match ty {
|
||||||
|
@ -213,6 +211,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let name = attr.name();
|
||||||
if !attr::is_used(attr) {
|
if !attr::is_used(attr) {
|
||||||
debug!("Emitting warning for: {:?}", attr);
|
debug!("Emitting warning for: {:?}", attr);
|
||||||
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
|
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
|
||||||
|
|
|
@ -706,7 +706,7 @@ impl<'a> Resolver<'a> {
|
||||||
match attr.meta_item_list() {
|
match attr.meta_item_list() {
|
||||||
Some(names) => for attr in names {
|
Some(names) => for attr in names {
|
||||||
if let Some(word) = attr.word() {
|
if let Some(word) = attr.word() {
|
||||||
imports.imports.push((word.ident.name, attr.span()));
|
imports.imports.push((word.name(), attr.span()));
|
||||||
} else {
|
} else {
|
||||||
span_err!(self.session, attr.span(), E0466, "bad macro import");
|
span_err!(self.session, attr.span(), E0466, "bad macro import");
|
||||||
}
|
}
|
||||||
|
|
|
@ -209,7 +209,7 @@ impl<'a> base::Resolver for Resolver<'a> {
|
||||||
fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>, allow_derive: bool)
|
fn find_legacy_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>, allow_derive: bool)
|
||||||
-> Option<ast::Attribute> {
|
-> Option<ast::Attribute> {
|
||||||
for i in 0..attrs.len() {
|
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()
|
if self.session.plugin_attributes.borrow().iter()
|
||||||
.any(|&(ref attr_nm, _)| name == &**attr_nm) {
|
.any(|&(ref attr_nm, _)| name == &**attr_nm) {
|
||||||
|
@ -231,7 +231,7 @@ impl<'a> base::Resolver for Resolver<'a> {
|
||||||
|
|
||||||
// Check for legacy derives
|
// Check for legacy derives
|
||||||
for i in 0..attrs.len() {
|
for i in 0..attrs.len() {
|
||||||
let name = unwrap_or!(attrs[i].name(), continue);
|
let name = attrs[i].name();
|
||||||
|
|
||||||
if name == "derive" {
|
if name == "derive" {
|
||||||
let result = attrs[i].parse_list(&self.session.parse_sess, |parser| {
|
let result = attrs[i].parse_list(&self.session.parse_sess, |parser| {
|
||||||
|
|
|
@ -67,7 +67,7 @@ impl Cfg {
|
||||||
/// If the content is not properly formatted, it will return an error indicating what and where
|
/// If the content is not properly formatted, it will return an error indicating what and where
|
||||||
/// the error is.
|
/// the error is.
|
||||||
pub fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
|
pub fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
|
||||||
let name = cfg.ident.name;
|
let name = cfg.name();
|
||||||
match cfg.node {
|
match cfg.node {
|
||||||
MetaItemKind::Word => Ok(Cfg::Cfg(name, None)),
|
MetaItemKind::Word => Ok(Cfg::Cfg(name, None)),
|
||||||
MetaItemKind::NameValue(ref lit) => match lit.node {
|
MetaItemKind::NameValue(ref lit) => match lit.node {
|
||||||
|
@ -436,6 +436,42 @@ mod test {
|
||||||
Cfg::Cfg(Symbol::intern(name), Some(Symbol::intern(value)))
|
Cfg::Cfg(Symbol::intern(name), Some(Symbol::intern(value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dummy_meta_item_word(name: &str) -> MetaItem {
|
||||||
|
MetaItem {
|
||||||
|
ident: Path::from_ident(Ident::from_str(name)),
|
||||||
|
node: MetaItemKind::Word,
|
||||||
|
span: DUMMY_SP,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! dummy_meta_item_list {
|
||||||
|
($name:ident, [$($list:ident),* $(,)*]) => {
|
||||||
|
MetaItem {
|
||||||
|
ident: Path::from_ident(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 {
|
||||||
|
ident: Path::from_ident(Ident::from_str(stringify!($name))),
|
||||||
|
node: MetaItemKind::List(vec![
|
||||||
|
$(
|
||||||
|
dummy_spanned(NestedMetaItemKind::MetaItem($list)),
|
||||||
|
)*
|
||||||
|
]),
|
||||||
|
span: DUMMY_SP,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cfg_not() {
|
fn test_cfg_not() {
|
||||||
with_globals(|| {
|
with_globals(|| {
|
||||||
|
@ -561,15 +597,11 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_ok() {
|
fn test_parse_ok() {
|
||||||
with_globals(|| {
|
with_globals(|| {
|
||||||
let mi = MetaItem {
|
let mi = dummy_meta_item_word("all");
|
||||||
ident: Ident::from_str("all"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
};
|
|
||||||
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all")));
|
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all")));
|
||||||
|
|
||||||
let mi = MetaItem {
|
let mi = MetaItem {
|
||||||
ident: Ident::from_str("all"),
|
ident: Path::from_ident(Ident::from_str("all")),
|
||||||
node: MetaItemKind::NameValue(dummy_spanned(LitKind::Str(
|
node: MetaItemKind::NameValue(dummy_spanned(LitKind::Str(
|
||||||
Symbol::intern("done"),
|
Symbol::intern("done"),
|
||||||
StrStyle::Cooked,
|
StrStyle::Cooked,
|
||||||
|
@ -578,111 +610,24 @@ mod test {
|
||||||
};
|
};
|
||||||
assert_eq!(Cfg::parse(&mi), Ok(name_value_cfg("all", "done")));
|
assert_eq!(Cfg::parse(&mi), Ok(name_value_cfg("all", "done")));
|
||||||
|
|
||||||
let mi = MetaItem {
|
let mi = dummy_meta_item_list!(all, [a, b]);
|
||||||
ident: Ident::from_str("all"),
|
|
||||||
node: MetaItemKind::List(vec![
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("a"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("b"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
};
|
|
||||||
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b")));
|
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b")));
|
||||||
|
|
||||||
let mi = MetaItem {
|
let mi = dummy_meta_item_list!(any, [a, b]);
|
||||||
ident: Ident::from_str("any"),
|
|
||||||
node: MetaItemKind::List(vec![
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("a"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("b"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
};
|
|
||||||
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") | word_cfg("b")));
|
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") | word_cfg("b")));
|
||||||
|
|
||||||
let mi = MetaItem {
|
let mi = dummy_meta_item_list!(not, [a]);
|
||||||
ident: Ident::from_str("not"),
|
|
||||||
node: MetaItemKind::List(vec![
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("a"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
};
|
|
||||||
assert_eq!(Cfg::parse(&mi), Ok(!word_cfg("a")));
|
assert_eq!(Cfg::parse(&mi), Ok(!word_cfg("a")));
|
||||||
|
|
||||||
let mi = MetaItem {
|
let mi = dummy_meta_item_list!(not, [
|
||||||
ident: Ident::from_str("not"),
|
dummy_meta_item_list!(any, [
|
||||||
node: MetaItemKind::List(vec![
|
dummy_meta_item_word("a"),
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
dummy_meta_item_list!(all, [b, c]),
|
||||||
ident: Ident::from_str("any"),
|
|
||||||
node: MetaItemKind::List(vec![
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("a"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("all"),
|
|
||||||
node: MetaItemKind::List(vec![
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("b"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("c"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
]),
|
]),
|
||||||
span: DUMMY_SP,
|
]);
|
||||||
};
|
|
||||||
assert_eq!(Cfg::parse(&mi), Ok(!(word_cfg("a") | (word_cfg("b") & word_cfg("c")))));
|
assert_eq!(Cfg::parse(&mi), Ok(!(word_cfg("a") | (word_cfg("b") & word_cfg("c")))));
|
||||||
|
|
||||||
let mi = MetaItem {
|
let mi = dummy_meta_item_list!(all, [a, b, c]);
|
||||||
ident: Ident::from_str("all"),
|
|
||||||
node: MetaItemKind::List(vec![
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("a"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("b"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("c"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
};
|
|
||||||
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b") & word_cfg("c")));
|
assert_eq!(Cfg::parse(&mi), Ok(word_cfg("a") & word_cfg("b") & word_cfg("c")));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -691,97 +636,36 @@ mod test {
|
||||||
fn test_parse_err() {
|
fn test_parse_err() {
|
||||||
with_globals(|| {
|
with_globals(|| {
|
||||||
let mi = MetaItem {
|
let mi = MetaItem {
|
||||||
ident: Ident::from_str("foo"),
|
ident: Path::from_ident(Ident::from_str("foo")),
|
||||||
node: MetaItemKind::NameValue(dummy_spanned(LitKind::Bool(false))),
|
node: MetaItemKind::NameValue(dummy_spanned(LitKind::Bool(false))),
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
};
|
};
|
||||||
assert!(Cfg::parse(&mi).is_err());
|
assert!(Cfg::parse(&mi).is_err());
|
||||||
|
|
||||||
let mi = MetaItem {
|
let mi = dummy_meta_item_list!(not, [a, b]);
|
||||||
ident: Ident::from_str("not"),
|
|
||||||
node: MetaItemKind::List(vec![
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("a"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("b"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
};
|
|
||||||
assert!(Cfg::parse(&mi).is_err());
|
assert!(Cfg::parse(&mi).is_err());
|
||||||
|
|
||||||
let mi = MetaItem {
|
let mi = dummy_meta_item_list!(not, []);
|
||||||
ident: Ident::from_str("not"),
|
|
||||||
node: MetaItemKind::List(vec![]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
};
|
|
||||||
assert!(Cfg::parse(&mi).is_err());
|
assert!(Cfg::parse(&mi).is_err());
|
||||||
|
|
||||||
let mi = MetaItem {
|
let mi = dummy_meta_item_list!(foo, []);
|
||||||
ident: Ident::from_str("foo"),
|
|
||||||
node: MetaItemKind::List(vec![
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("a"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
};
|
|
||||||
assert!(Cfg::parse(&mi).is_err());
|
assert!(Cfg::parse(&mi).is_err());
|
||||||
|
|
||||||
let mi = MetaItem {
|
let mi = dummy_meta_item_list!(all, [
|
||||||
ident: Ident::from_str("all"),
|
dummy_meta_item_list!(foo, []),
|
||||||
node: MetaItemKind::List(vec![
|
dummy_meta_item_word("b"),
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
]);
|
||||||
ident: Ident::from_str("foo"),
|
|
||||||
node: MetaItemKind::List(vec![]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("b"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
};
|
|
||||||
assert!(Cfg::parse(&mi).is_err());
|
assert!(Cfg::parse(&mi).is_err());
|
||||||
|
|
||||||
let mi = MetaItem {
|
let mi = dummy_meta_item_list!(any, [
|
||||||
ident: Ident::from_str("any"),
|
dummy_meta_item_word("a"),
|
||||||
node: MetaItemKind::List(vec![
|
dummy_meta_item_list!(foo, []),
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
]);
|
||||||
ident: Ident::from_str("a"),
|
|
||||||
node: MetaItemKind::Word,
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("foo"),
|
|
||||||
node: MetaItemKind::List(vec![]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
};
|
|
||||||
assert!(Cfg::parse(&mi).is_err());
|
assert!(Cfg::parse(&mi).is_err());
|
||||||
|
|
||||||
let mi = MetaItem {
|
let mi = dummy_meta_item_list!(not, [
|
||||||
ident: Ident::from_str("not"),
|
dummy_meta_item_list!(foo, []),
|
||||||
node: MetaItemKind::List(vec![
|
]);
|
||||||
dummy_spanned(NestedMetaItemKind::MetaItem(MetaItem {
|
|
||||||
ident: Ident::from_str("foo"),
|
|
||||||
node: MetaItemKind::List(vec![]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
})),
|
|
||||||
]),
|
|
||||||
span: DUMMY_SP,
|
|
||||||
};
|
|
||||||
assert!(Cfg::parse(&mi).is_err());
|
assert!(Cfg::parse(&mi).is_err());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3661,7 +3661,7 @@ impl Clean<Vec<Item>> for doctree::Import {
|
||||||
// #[doc(no_inline)] attribute is present.
|
// #[doc(no_inline)] attribute is present.
|
||||||
// Don't inline doc(hidden) imports so they can be stripped at a later stage.
|
// 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| {
|
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") ||
|
Some(l) => attr::list_contains_name(&l, "no_inline") ||
|
||||||
attr::list_contains_name(&l, "hidden"),
|
attr::list_contains_name(&l, "hidden"),
|
||||||
None => false,
|
None => false,
|
||||||
|
|
|
@ -3284,7 +3284,7 @@ fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_attribute(attr: &ast::MetaItem) -> Option<String> {
|
fn render_attribute(attr: &ast::MetaItem) -> Option<String> {
|
||||||
let name = attr.ident.name;
|
let name = attr.name();
|
||||||
|
|
||||||
if attr.is_word() {
|
if attr.is_word() {
|
||||||
Some(format!("{}", name))
|
Some(format!("{}", name))
|
||||||
|
@ -3319,7 +3319,7 @@ fn render_attributes(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
|
||||||
let mut attrs = String::new();
|
let mut attrs = String::new();
|
||||||
|
|
||||||
for attr in &it.attrs.other_attrs {
|
for attr in &it.attrs.other_attrs {
|
||||||
let name = attr.name().unwrap();
|
let name = attr.name();
|
||||||
if !ATTRIBUTE_WHITELIST.contains(&&*name.as_str()) {
|
if !ATTRIBUTE_WHITELIST.contains(&&*name.as_str()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -474,10 +474,10 @@ pub enum NestedMetaItemKind {
|
||||||
|
|
||||||
/// A spanned compile-time attribute item.
|
/// 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)]
|
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
|
||||||
pub struct MetaItem {
|
pub struct MetaItem {
|
||||||
pub ident: Ident,
|
pub ident: Path,
|
||||||
pub node: MetaItemKind,
|
pub node: MetaItemKind,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,10 @@ pub use self::ReprAttr::*;
|
||||||
pub use self::IntType::*;
|
pub use self::IntType::*;
|
||||||
|
|
||||||
use ast;
|
use ast;
|
||||||
use ast::{AttrId, Attribute, Name, Ident};
|
use ast::{AttrId, Attribute, Name, Ident, Path, PathSegment};
|
||||||
use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
|
use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
|
||||||
use ast::{Lit, LitKind, Expr, ExprKind, Item, Local, Stmt, StmtKind};
|
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 syntax_pos::Span;
|
||||||
use errors::Handler;
|
use errors::Handler;
|
||||||
use feature_gate::{Features, GatedCfg};
|
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 {
|
impl NestedMetaItem {
|
||||||
/// Returns the MetaItem if self is a NestedMetaItemKind::MetaItem.
|
/// Returns the MetaItem if self is a NestedMetaItemKind::MetaItem.
|
||||||
pub fn meta_item(&self) -> Option<&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]`,
|
/// Returns the name of the meta item, e.g. `foo` in `#[foo]`,
|
||||||
/// `#[foo="bar"]` and `#[foo(bar)]`, if self is a MetaItem
|
/// `#[foo="bar"]` and `#[foo(bar)]`, if self is a MetaItem
|
||||||
pub fn name(&self) -> Option<Name> {
|
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
|
/// 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 {
|
if meta_item_list.len() == 1 {
|
||||||
let nested_item = &meta_item_list[0];
|
let nested_item = &meta_item_list[0];
|
||||||
if nested_item.is_literal() {
|
if nested_item.is_literal() {
|
||||||
Some((meta_item.ident.name, nested_item.literal().unwrap()))
|
Some((meta_item.name(), nested_item.literal().unwrap()))
|
||||||
} else {
|
} else {
|
||||||
None
|
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 {
|
impl Attribute {
|
||||||
pub fn check_name(&self, name: &str) -> bool {
|
pub fn check_name(&self, name: &str) -> bool {
|
||||||
let matches = self.path == name;
|
let matches = self.path == name;
|
||||||
|
@ -213,11 +225,10 @@ impl Attribute {
|
||||||
matches
|
matches
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> Option<Name> {
|
/// Returns the **last** segment of the name of this attribute.
|
||||||
match self.path.segments.len() {
|
/// E.g. `foo` for `#[foo]`, `skip` for `#[rustfmt::skip]`.
|
||||||
1 => Some(self.path.segments[0].ident.name),
|
pub fn name(&self) -> Name {
|
||||||
_ => None,
|
name_from_path(&self.path)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn value_str(&self) -> Option<Symbol> {
|
pub fn value_str(&self) -> Option<Symbol> {
|
||||||
|
@ -247,9 +258,17 @@ impl Attribute {
|
||||||
pub fn is_value_str(&self) -> bool {
|
pub fn is_value_str(&self) -> bool {
|
||||||
self.value_str().is_some()
|
self.value_str().is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_scoped(&self) -> bool {
|
||||||
|
self.path.segments.len() > 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetaItem {
|
impl MetaItem {
|
||||||
|
pub fn name(&self) -> Name {
|
||||||
|
name_from_path(&self.ident)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn value_str(&self) -> Option<Symbol> {
|
pub fn value_str(&self) -> Option<Symbol> {
|
||||||
match self.node {
|
match self.node {
|
||||||
MetaItemKind::NameValue(ref v) => {
|
MetaItemKind::NameValue(ref v) => {
|
||||||
|
@ -279,7 +298,7 @@ impl MetaItem {
|
||||||
pub fn span(&self) -> Span { self.span }
|
pub fn span(&self) -> Span { self.span }
|
||||||
|
|
||||||
pub fn check_name(&self, name: &str) -> bool {
|
pub fn check_name(&self, name: &str) -> bool {
|
||||||
self.ident.name == name
|
self.name() == name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_value_str(&self) -> bool {
|
pub fn is_value_str(&self) -> bool {
|
||||||
|
@ -296,10 +315,7 @@ impl Attribute {
|
||||||
pub fn meta(&self) -> Option<MetaItem> {
|
pub fn meta(&self) -> Option<MetaItem> {
|
||||||
let mut tokens = self.tokens.trees().peekable();
|
let mut tokens = self.tokens.trees().peekable();
|
||||||
Some(MetaItem {
|
Some(MetaItem {
|
||||||
ident: match self.path.segments.len() {
|
ident: self.path.clone(),
|
||||||
1 => self.path.segments[0].ident,
|
|
||||||
_ => return None,
|
|
||||||
},
|
|
||||||
node: if let Some(node) = MetaItemKind::from_tokens(&mut tokens) {
|
node: if let Some(node) = MetaItemKind::from_tokens(&mut tokens) {
|
||||||
if tokens.peek().is_some() {
|
if tokens.peek().is_some() {
|
||||||
return None;
|
return None;
|
||||||
|
@ -344,12 +360,8 @@ impl Attribute {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> {
|
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 {
|
Ok(MetaItem {
|
||||||
ident: self.path.segments.last().unwrap().ident,
|
ident: self.path.clone(),
|
||||||
node: self.parse(sess, |parser| parser.parse_meta_item_kind())?,
|
node: self.parse(sess, |parser| parser.parse_meta_item_kind())?,
|
||||||
span: self.span,
|
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 {
|
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 {
|
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 {
|
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 {
|
pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem {
|
||||||
respan(ident.span, NestedMetaItemKind::MetaItem(mk_word_item(ident)))
|
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 {
|
Attribute {
|
||||||
id,
|
id,
|
||||||
style: ast::AttrStyle::Inner,
|
style: ast::AttrStyle::Inner,
|
||||||
path: ast::Path::from_ident(item.ident),
|
path: item.ident,
|
||||||
tokens: item.node.tokens(item.span),
|
tokens: item.node.tokens(item.span),
|
||||||
is_sugared_doc: false,
|
is_sugared_doc: false,
|
||||||
span: sp,
|
span: sp,
|
||||||
|
@ -440,7 +453,7 @@ pub fn mk_spanned_attr_outer(sp: Span, id: AttrId, item: MetaItem) -> Attribute
|
||||||
Attribute {
|
Attribute {
|
||||||
id,
|
id,
|
||||||
style: ast::AttrStyle::Outer,
|
style: ast::AttrStyle::Outer,
|
||||||
path: ast::Path::from_ident(item.ident),
|
path: item.ident,
|
||||||
tokens: item.node.tokens(item.span),
|
tokens: item.node.tokens(item.span),
|
||||||
is_sugared_doc: false,
|
is_sugared_doc: false,
|
||||||
span: sp,
|
span: sp,
|
||||||
|
@ -453,7 +466,7 @@ pub fn mk_sugared_doc_attr(id: AttrId, text: Symbol, span: Span) -> Attribute {
|
||||||
Attribute {
|
Attribute {
|
||||||
id,
|
id,
|
||||||
style,
|
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),
|
tokens: MetaItemKind::NameValue(lit).tokens(span),
|
||||||
is_sugared_doc: true,
|
is_sugared_doc: true,
|
||||||
span,
|
span,
|
||||||
|
@ -489,7 +502,7 @@ pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool {
|
||||||
item.check_name("feature") &&
|
item.check_name("feature") &&
|
||||||
item.meta_item_list().map(|list| {
|
item.meta_item_list().map(|list| {
|
||||||
list.iter().any(|mi| {
|
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)
|
||||||
})
|
})
|
||||||
}).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)) {
|
if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
|
||||||
gated_cfg.check_and_emit(sess, feats);
|
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
|
// The unwraps below may look dangerous, but we've already asserted
|
||||||
// that they won't fail with the loop above.
|
// 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| {
|
"any" => mis.iter().any(|mi| {
|
||||||
eval_condition(mi.meta_item().unwrap(), sess, eval)
|
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 meta = meta.as_ref().unwrap();
|
||||||
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
|
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
|
||||||
if item.is_some() {
|
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
|
return false
|
||||||
}
|
}
|
||||||
if let Some(v) = meta.value_str() {
|
if let Some(v) = meta.value_str() {
|
||||||
|
@ -695,14 +708,14 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
|
||||||
)+
|
)+
|
||||||
for meta in metas {
|
for meta in metas {
|
||||||
if let Some(mi) = meta.meta_item() {
|
if let Some(mi) = meta.meta_item() {
|
||||||
match &*mi.ident.name.as_str() {
|
match &*mi.name().as_str() {
|
||||||
$(
|
$(
|
||||||
stringify!($name)
|
stringify!($name)
|
||||||
=> if !get(mi, &mut $name) { continue 'outer },
|
=> if !get(mi, &mut $name) { continue 'outer },
|
||||||
)+
|
)+
|
||||||
_ => {
|
_ => {
|
||||||
handle_errors(diagnostic, mi.span,
|
handle_errors(diagnostic, mi.span,
|
||||||
AttrError::UnknownMetaItem(mi.ident.name));
|
AttrError::UnknownMetaItem(mi.name()));
|
||||||
continue 'outer
|
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" => {
|
"rustc_deprecated" => {
|
||||||
if rustc_depr.is_some() {
|
if rustc_depr.is_some() {
|
||||||
span_err!(diagnostic, item_sp, E0540,
|
span_err!(diagnostic, item_sp, E0540,
|
||||||
|
@ -769,13 +782,13 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
|
||||||
let mut issue = None;
|
let mut issue = None;
|
||||||
for meta in metas {
|
for meta in metas {
|
||||||
if let Some(mi) = meta.meta_item() {
|
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 },
|
"feature" => if !get(mi, &mut feature) { continue 'outer },
|
||||||
"reason" => if !get(mi, &mut reason) { continue 'outer },
|
"reason" => if !get(mi, &mut reason) { continue 'outer },
|
||||||
"issue" => if !get(mi, &mut issue) { continue 'outer },
|
"issue" => if !get(mi, &mut issue) { continue 'outer },
|
||||||
_ => {
|
_ => {
|
||||||
handle_errors(diagnostic, meta.span,
|
handle_errors(diagnostic, meta.span,
|
||||||
AttrError::UnknownMetaItem(mi.ident.name));
|
AttrError::UnknownMetaItem(mi.name()));
|
||||||
continue 'outer
|
continue 'outer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -825,12 +838,12 @@ fn find_stability_generic<'a, I>(diagnostic: &Handler,
|
||||||
let mut since = None;
|
let mut since = None;
|
||||||
for meta in metas {
|
for meta in metas {
|
||||||
if let NestedMetaItemKind::MetaItem(ref mi) = meta.node {
|
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 },
|
"feature" => if !get(mi, &mut feature) { continue 'outer },
|
||||||
"since" => if !get(mi, &mut since) { continue 'outer },
|
"since" => if !get(mi, &mut since) { continue 'outer },
|
||||||
_ => {
|
_ => {
|
||||||
handle_errors(diagnostic, meta.span,
|
handle_errors(diagnostic, meta.span,
|
||||||
AttrError::UnknownMetaItem(mi.ident.name));
|
AttrError::UnknownMetaItem(mi.name()));
|
||||||
continue 'outer
|
continue 'outer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -917,7 +930,7 @@ fn find_deprecation_generic<'a, I>(diagnostic: &Handler,
|
||||||
depr = if let Some(metas) = attr.meta_item_list() {
|
depr = if let Some(metas) = attr.meta_item_list() {
|
||||||
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
|
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
|
||||||
if item.is_some() {
|
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
|
return false
|
||||||
}
|
}
|
||||||
if let Some(v) = meta.value_str() {
|
if let Some(v) = meta.value_str() {
|
||||||
|
@ -933,12 +946,12 @@ fn find_deprecation_generic<'a, I>(diagnostic: &Handler,
|
||||||
let mut note = None;
|
let mut note = None;
|
||||||
for meta in metas {
|
for meta in metas {
|
||||||
if let NestedMetaItemKind::MetaItem(ref mi) = meta.node {
|
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 },
|
"since" => if !get(mi, &mut since) { continue 'outer },
|
||||||
"note" => if !get(mi, &mut note) { continue 'outer },
|
"note" => if !get(mi, &mut note) { continue 'outer },
|
||||||
_ => {
|
_ => {
|
||||||
handle_errors(diagnostic, meta.span,
|
handle_errors(diagnostic, meta.span,
|
||||||
AttrError::UnknownMetaItem(mi.ident.name));
|
AttrError::UnknownMetaItem(mi.name()));
|
||||||
continue 'outer
|
continue 'outer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -990,7 +1003,7 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr>
|
||||||
|
|
||||||
let mut recognised = false;
|
let mut recognised = false;
|
||||||
if let Some(mi) = item.word() {
|
if let Some(mi) = item.word() {
|
||||||
let word = &*mi.ident.name.as_str();
|
let word = &*mi.name().as_str();
|
||||||
let hint = match word {
|
let hint = match word {
|
||||||
"C" => Some(ReprC),
|
"C" => Some(ReprC),
|
||||||
"packed" => Some(ReprPacked(1)),
|
"packed" => Some(ReprPacked(1)),
|
||||||
|
@ -1047,7 +1060,7 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr>
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Some(meta_item) = item.meta_item() {
|
if let Some(meta_item) = item.meta_item() {
|
||||||
if meta_item.ident.name == "align" {
|
if meta_item.name() == "align" {
|
||||||
if let MetaItemKind::NameValue(ref value) = meta_item.node {
|
if let MetaItemKind::NameValue(ref value) = meta_item.node {
|
||||||
recognised = true;
|
recognised = true;
|
||||||
let mut err = struct_span_err!(diagnostic, item.span, E0693,
|
let mut err = struct_span_err!(diagnostic, item.span, E0693,
|
||||||
|
@ -1127,18 +1140,56 @@ impl IntType {
|
||||||
|
|
||||||
impl MetaItem {
|
impl MetaItem {
|
||||||
fn tokens(&self) -> TokenStream {
|
fn tokens(&self) -> TokenStream {
|
||||||
let ident = TokenTree::Token(self.span, Token::from_ast_ident(self.ident));
|
let mut idents = vec![];
|
||||||
TokenStream::concat(vec![ident.into(), self.node.tokens(self.span)])
|
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>
|
fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
|
||||||
where I: Iterator<Item = TokenTree>,
|
where I: Iterator<Item = TokenTree>,
|
||||||
{
|
{
|
||||||
let (span, ident) = match tokens.next() {
|
// FIXME: Share code with `parse_path`.
|
||||||
Some(TokenTree::Token(span, Token::Ident(ident, _))) => (span, ident),
|
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 {
|
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::NtMeta(ref meta) => return Some(meta.clone()),
|
||||||
|
token::Nonterminal::NtPath(ref path) => path.clone(),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
},
|
},
|
||||||
_ => return None,
|
_ => return None,
|
||||||
|
@ -1147,10 +1198,11 @@ impl MetaItem {
|
||||||
let node = MetaItemKind::from_tokens(tokens)?;
|
let node = MetaItemKind::from_tokens(tokens)?;
|
||||||
let hi = match node {
|
let hi = match node {
|
||||||
MetaItemKind::NameValue(ref lit) => lit.span.hi(),
|
MetaItemKind::NameValue(ref lit) => lit.span.hi(),
|
||||||
MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(span.hi()),
|
MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(ident.span.hi()),
|
||||||
_ => 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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -336,4 +336,5 @@ register_diagnostics! {
|
||||||
E0629, // missing 'feature' (rustc_const_unstable)
|
E0629, // missing 'feature' (rustc_const_unstable)
|
||||||
E0630, // rustc_const_unstable attribute must be paired with stable/unstable attribute
|
E0630, // rustc_const_unstable attribute must be paired with stable/unstable attribute
|
||||||
E0693, // incorrect `repr(align)` attribute format
|
E0693, // incorrect `repr(align)` attribute format
|
||||||
|
E0694, // an unknown tool name found in scoped attributes
|
||||||
}
|
}
|
||||||
|
|
|
@ -810,7 +810,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
invoc.expansion_data.mark.set_expn_info(expn_info);
|
invoc.expansion_data.mark.set_expn_info(expn_info);
|
||||||
let span = span.with_ctxt(self.cx.backtrace());
|
let span = span.with_ctxt(self.cx.backtrace());
|
||||||
let dummy = ast::MetaItem { // FIXME(jseyfried) avoid this
|
let dummy = ast::MetaItem { // FIXME(jseyfried) avoid this
|
||||||
ident: keywords::Invalid.ident(),
|
ident: Path::from_ident(keywords::Invalid.ident()),
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
||||||
node: ast::MetaItemKind::Word,
|
node: ast::MetaItemKind::Word,
|
||||||
};
|
};
|
||||||
|
@ -1017,7 +1017,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
||||||
fn check_attributes(&mut self, attrs: &[ast::Attribute]) {
|
fn check_attributes(&mut self, attrs: &[ast::Attribute]) {
|
||||||
let features = self.cx.ecfg.features.unwrap();
|
let features = self.cx.ecfg.features.unwrap();
|
||||||
for attr in attrs.iter() {
|
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
|
// macros are expanded before any lint passes so this warning has to be hardcoded
|
||||||
if attr.path == "derive" {
|
if attr.path == "derive" {
|
||||||
|
@ -1030,6 +1030,10 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
||||||
|
|
||||||
fn check_attribute(&mut self, at: &ast::Attribute) {
|
fn check_attribute(&mut self, at: &ast::Attribute) {
|
||||||
let features = self.cx.ecfg.features.unwrap();
|
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);
|
feature_gate::check_attribute(at, self.cx.parse_sess, features);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -460,6 +460,9 @@ declare_features! (
|
||||||
|
|
||||||
// Access to crate names passed via `--extern` through prelude
|
// Access to crate names passed via `--extern` through prelude
|
||||||
(active, extern_prelude, "1.27.0", Some(44660), Some(Edition::Edition2018)),
|
(active, extern_prelude, "1.27.0", Some(44660), Some(Edition::Edition2018)),
|
||||||
|
|
||||||
|
// Scoped attributes
|
||||||
|
(active, tool_attributes, "1.25.0", Some(44690), None),
|
||||||
);
|
);
|
||||||
|
|
||||||
declare_features! (
|
declare_features! (
|
||||||
|
@ -1079,7 +1082,7 @@ pub struct GatedCfg {
|
||||||
|
|
||||||
impl GatedCfg {
|
impl GatedCfg {
|
||||||
pub fn gate(cfg: &ast::MetaItem) -> Option<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()
|
GATED_CFGS.iter()
|
||||||
.position(|info| info.0 == name)
|
.position(|info| info.0 == name)
|
||||||
.map(|idx| {
|
.map(|idx| {
|
||||||
|
@ -1132,7 +1135,7 @@ macro_rules! gate_feature {
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
|
fn check_attribute(&self, attr: &ast::Attribute, is_macro: bool) {
|
||||||
debug!("check_attribute(attr = {:?})", attr);
|
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 {
|
for &(n, ty, ref gateage) in BUILTIN_ATTRIBUTES {
|
||||||
if name == n {
|
if name == n {
|
||||||
if let Gated(_, name, desc, ref has_feature) = *gateage {
|
if let Gated(_, name, desc, ref has_feature) = *gateage {
|
||||||
|
@ -1172,12 +1175,28 @@ impl<'a> Context<'a> {
|
||||||
// before the plugin attributes are registered
|
// before the plugin attributes are registered
|
||||||
// so we skip this then
|
// so we skip this then
|
||||||
if !is_macro {
|
if !is_macro {
|
||||||
gate_feature!(self, custom_attribute, attr.span,
|
if attr.is_scoped() {
|
||||||
&format!("The attribute `{}` is currently \
|
gate_feature!(self, tool_attributes, attr.span,
|
||||||
unknown to the compiler and \
|
&format!("scoped attribute `{}` is experimental", attr.path));
|
||||||
may have meaning \
|
if attr::is_known_tool(attr) {
|
||||||
added to it in the future",
|
attr::mark_used(attr);
|
||||||
attr.path));
|
} 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1843,7 +1862,7 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
|
||||||
for mi in list {
|
for mi in list {
|
||||||
|
|
||||||
let name = if let Some(word) = mi.word() {
|
let name = if let Some(word) = mi.word() {
|
||||||
word.ident.name
|
word.name()
|
||||||
} else {
|
} else {
|
||||||
span_err!(span_handler, mi.span, E0556,
|
span_err!(span_handler, mi.span, E0556,
|
||||||
"malformed feature, expected just one word");
|
"malformed feature, expected just one word");
|
||||||
|
|
|
@ -149,7 +149,7 @@ impl<'a> Parser<'a> {
|
||||||
};
|
};
|
||||||
Ok(if let Some(meta) = meta {
|
Ok(if let Some(meta) = meta {
|
||||||
self.bump();
|
self.bump();
|
||||||
(ast::Path::from_ident(meta.ident), meta.node.tokens(meta.span))
|
(meta.ident, meta.node.tokens(meta.span))
|
||||||
} else {
|
} else {
|
||||||
(self.parse_path(PathStyle::Mod)?, self.parse_tokens())
|
(self.parse_path(PathStyle::Mod)?, self.parse_tokens())
|
||||||
})
|
})
|
||||||
|
@ -225,9 +225,10 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let lo = self.span;
|
let lo = self.span;
|
||||||
let ident = self.parse_ident()?;
|
let ident = self.parse_path(PathStyle::Mod)?;
|
||||||
let node = self.parse_meta_item_kind()?;
|
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> {
|
pub fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
|
||||||
|
|
|
@ -1958,16 +1958,16 @@ impl<'a> Parser<'a> {
|
||||||
let meta_ident = match self.token {
|
let meta_ident = match self.token {
|
||||||
token::Interpolated(ref nt) => match nt.0 {
|
token::Interpolated(ref nt) => match nt.0 {
|
||||||
token::NtMeta(ref meta) => match meta.node {
|
token::NtMeta(ref meta) => match meta.node {
|
||||||
ast::MetaItemKind::Word => Some(meta.ident),
|
ast::MetaItemKind::Word => Some(meta.ident.clone()),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(ident) = meta_ident {
|
if let Some(path) = meta_ident {
|
||||||
self.bump();
|
self.bump();
|
||||||
return Ok(ast::Path::from_ident(ident));
|
return Ok(path);
|
||||||
}
|
}
|
||||||
self.parse_path(style)
|
self.parse_path(style)
|
||||||
}
|
}
|
||||||
|
|
|
@ -714,6 +714,22 @@ pub trait PrintState<'a> {
|
||||||
Ok(())
|
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<()> {
|
fn print_attribute(&mut self, attr: &ast::Attribute) -> io::Result<()> {
|
||||||
self.print_attribute_inline(attr, false)
|
self.print_attribute_inline(attr, false)
|
||||||
}
|
}
|
||||||
|
@ -735,17 +751,7 @@ pub trait PrintState<'a> {
|
||||||
if let Some(mi) = attr.meta() {
|
if let Some(mi) = attr.meta() {
|
||||||
self.print_meta_item(&mi)?
|
self.print_meta_item(&mi)?
|
||||||
} else {
|
} else {
|
||||||
for (i, segment) in attr.path.segments.iter().enumerate() {
|
self.print_attribute_path(&attr.path)?;
|
||||||
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.writer().space()?;
|
self.writer().space()?;
|
||||||
self.print_tts(attr.tokens.clone())?;
|
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<()> {
|
fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> {
|
||||||
self.ibox(INDENT_UNIT)?;
|
self.ibox(INDENT_UNIT)?;
|
||||||
match item.node {
|
match item.node {
|
||||||
ast::MetaItemKind::Word => {
|
ast::MetaItemKind::Word => self.print_attribute_path(&item.ident)?,
|
||||||
self.writer().word(&item.ident.name.as_str())?;
|
|
||||||
}
|
|
||||||
ast::MetaItemKind::NameValue(ref value) => {
|
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.word_space("=")?;
|
||||||
self.print_literal(value)?;
|
self.print_literal(value)?;
|
||||||
}
|
}
|
||||||
ast::MetaItemKind::List(ref items) => {
|
ast::MetaItemKind::List(ref items) => {
|
||||||
self.writer().word(&item.ident.name.as_str())?;
|
self.print_attribute_path(&item.ident)?;
|
||||||
self.popen()?;
|
self.popen()?;
|
||||||
self.commasep(Consistent,
|
self.commasep(Consistent,
|
||||||
&items[..],
|
&items[..],
|
||||||
|
|
|
@ -22,11 +22,9 @@ struct MarkAttrs<'a>(&'a [ast::Name]);
|
||||||
|
|
||||||
impl<'a> Visitor<'a> for MarkAttrs<'a> {
|
impl<'a> Visitor<'a> for MarkAttrs<'a> {
|
||||||
fn visit_attribute(&mut self, attr: &Attribute) {
|
fn visit_attribute(&mut self, attr: &Attribute) {
|
||||||
if let Some(name) = attr.name() {
|
if self.0.contains(&attr.name()) {
|
||||||
if self.0.contains(&name) {
|
mark_used(attr);
|
||||||
mark_used(attr);
|
mark_known(attr);
|
||||||
mark_known(attr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -472,7 +472,7 @@ impl<'a> TraitDef<'a> {
|
||||||
attrs.extend(item.attrs
|
attrs.extend(item.attrs
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|a| {
|
.filter(|a| {
|
||||||
a.name().is_some() && match &*a.name().unwrap().as_str() {
|
match &*a.name().as_str() {
|
||||||
"allow" | "warn" | "deny" | "forbid" | "stable" | "unstable" => true,
|
"allow" | "warn" | "deny" | "forbid" | "stable" | "unstable" => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ fn expand_duplicate(cx: &mut ExtCtxt,
|
||||||
let copy_name = match mi.node {
|
let copy_name = match mi.node {
|
||||||
ast::MetaItemKind::List(ref xs) => {
|
ast::MetaItemKind::List(ref xs) => {
|
||||||
if let Some(word) = xs[0].word() {
|
if let Some(word) = xs[0].word() {
|
||||||
word.ident
|
word.ident.segments.last().unwrap().ident
|
||||||
} else {
|
} else {
|
||||||
cx.span_err(mi.span, "Expected word");
|
cx.span_err(mi.span, "Expected word");
|
||||||
return;
|
return;
|
||||||
|
|
15
src/test/compile-fail/feature-gate-tool_attributes.rs
Normal file
15
src/test/compile-fail/feature-gate-tool_attributes.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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;
|
||||||
|
}
|
16
src/test/compile-fail/unknown-tool-name.rs
Normal file
16
src/test/compile-fail/unknown-tool-name.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![feature(tool_attributes)]
|
||||||
|
|
||||||
|
#![foo::bar] //~ ERROR an unknown tool name found in scoped attribute: `foo::bar`. [E0694]
|
||||||
|
|
||||||
|
#[foo::bar] //~ ERROR an unknown tool name found in scoped attribute: `foo::bar`. [E0694]
|
||||||
|
fn main() {}
|
18
src/test/compile-fail/unknown_tool_attributes-1.rs
Normal file
18
src/test/compile-fail/unknown_tool_attributes-1.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 unknown tool name found in scoped attribute: `foo::bar`. [E0694]
|
||||||
|
fn main() {}
|
|
@ -112,7 +112,7 @@ fn expand_duplicate(cx: &mut ExtCtxt,
|
||||||
let copy_name = match mi.node {
|
let copy_name = match mi.node {
|
||||||
ast::MetaItemKind::List(ref xs) => {
|
ast::MetaItemKind::List(ref xs) => {
|
||||||
if let Some(word) = xs[0].word() {
|
if let Some(word) = xs[0].word() {
|
||||||
word.ident
|
word.ident.segments.last().unwrap().ident
|
||||||
} else {
|
} else {
|
||||||
cx.span_err(mi.span, "Expected word");
|
cx.span_err(mi.span, "Expected word");
|
||||||
return;
|
return;
|
||||||
|
|
23
src/test/run-pass/tool_attributes.rs
Normal file
23
src/test/run-pass/tool_attributes.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
|
||||||
|
}
|
15
src/test/ui/feature-gate-tool_attributes.rs
Normal file
15
src/test/ui/feature-gate-tool_attributes.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
|
||||||
|
;
|
||||||
|
}
|
11
src/test/ui/feature-gate-tool_attributes.stderr
Normal file
11
src/test/ui/feature-gate-tool_attributes.stderr
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
error[E0658]: scoped attribute `rustfmt::skip` is experimental (see issue #44690)
|
||||||
|
--> $DIR/feature-gate-tool_attributes.rs:12:5
|
||||||
|
|
|
||||||
|
LL | #[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
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
Loading…
Add table
Add a link
Reference in a new issue