syntax: Do not accidentally treat multi-segment meta-items as single-segment

This commit is contained in:
Vadim Petrochenkov 2019-02-28 09:17:24 +03:00
parent e2009ea5ff
commit 8371caf5ee
29 changed files with 236 additions and 213 deletions

View File

@ -166,7 +166,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
// ```
let hints: Vec<_> = item.attrs
.iter()
.filter(|attr| attr.name() == "repr")
.filter(|attr| attr.check_name("repr"))
.filter_map(|attr| attr.meta_item_list())
.flatten()
.collect();
@ -177,7 +177,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
let mut is_transparent = false;
for hint in &hints {
let name = if let Some(name) = hint.name() {
let name = if let Some(name) = hint.ident_str() {
name
} else {
// Invalid repr hint like repr(42). We don't check for unrecognized hints here
@ -185,7 +185,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
continue;
};
let (article, allowed_targets) = match &*name.as_str() {
let (article, allowed_targets) = match name {
"C" | "align" => {
is_c |= name == "C";
if target != Target::Struct &&
@ -313,7 +313,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
fn check_used(&self, item: &hir::Item, target: Target) {
for attr in &item.attrs {
if attr.name() == "used" && target != Target::Static {
if attr.check_name("used") && target != Target::Static {
self.tcx.sess
.span_err(attr.span, "attribute must be applied to a `static` variable");
}

View File

@ -197,7 +197,8 @@ impl<'a> HashStable<StableHashingContext<'a>> for [ast::Attribute] {
let filtered: SmallVec<[&ast::Attribute; 8]> = self
.iter()
.filter(|attr| {
!attr.is_sugared_doc && !hcx.is_ignored_attr(attr.name())
!attr.is_sugared_doc &&
!attr.ident().map_or(false, |ident| hcx.is_ignored_attr(ident.name))
})
.collect();
@ -224,7 +225,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for ast::Attribute {
hcx: &mut StableHashingContext<'a>,
hasher: &mut StableHasher<W>) {
// Make sure that these have been filtered out.
debug_assert!(!hcx.is_ignored_attr(self.name()));
debug_assert!(!self.ident().map_or(false, |ident| hcx.is_ignored_attr(ident.name)));
debug_assert!(!self.is_sugared_doc);
let ast::Attribute {

View File

@ -194,7 +194,7 @@ impl<'a> LintLevelsBuilder<'a> {
struct_span_err!(sess, span, E0452, "malformed lint attribute")
};
for attr in attrs {
let level = match Level::from_str(&attr.name().as_str()) {
let level = match attr.ident_str().and_then(|name| Level::from_str(name)) {
None => continue,
Some(lvl) => lvl,
};
@ -255,9 +255,9 @@ impl<'a> LintLevelsBuilder<'a> {
}
for li in metas {
let word = match li.word() {
Some(word) => word,
None => {
let meta_item = match li.meta_item() {
Some(meta_item) if meta_item.is_word() => meta_item,
_ => {
let mut err = bad_attr(li.span);
if let Some(item) = li.meta_item() {
if let ast::MetaItemKind::NameValue(_) = item.node {
@ -270,23 +270,24 @@ impl<'a> LintLevelsBuilder<'a> {
continue;
}
};
let tool_name = if let Some(lint_tool) = word.is_scoped() {
if !attr::is_known_lint_tool(lint_tool) {
let tool_name = if meta_item.ident.segments.len() > 1 {
let tool_ident = meta_item.ident.segments[0].ident;
if !attr::is_known_lint_tool(tool_ident) {
span_err!(
sess,
lint_tool.span,
tool_ident.span,
E0710,
"an unknown tool name found in scoped lint: `{}`",
word.ident
meta_item.ident
);
continue;
}
Some(lint_tool.as_str())
Some(tool_ident.as_str())
} else {
None
};
let name = word.name();
let name = meta_item.ident.segments.last().expect("empty lint name").ident.name;
match store.check_lint_name(&name.as_str(), tool_name) {
CheckLintNameResult::Ok(ids) => {
let src = LintSource::Node(name, li.span, reason);

View File

@ -65,9 +65,9 @@ impl<'a, 'tcx> LibFeatureCollector<'a, 'tcx> {
for meta in metas {
if let Some(mi) = meta.meta_item() {
// Find the `feature = ".."` meta-item.
match (&*mi.name().as_str(), mi.value_str()) {
("feature", val) => feature = val,
("since", val) => since = val,
match (mi.ident_str(), mi.value_str()) {
(Some("feature"), val) => feature = val,
(Some("since"), val) => since = val,
_ => {}
}
}

View File

@ -194,11 +194,12 @@ impl<'a, 'tcx: 'a> Annotator<'a, 'tcx> {
} else {
// Emit errors for non-staged-api crates.
for attr in attrs {
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 \
outside of the standard library");
if let Some(tag) = attr.ident_str() {
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 \
outside of the standard library");
}
}
}

View File

@ -1850,7 +1850,8 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
error!("argument value must be a string");
}
MetaItemKind::NameValue(..) | MetaItemKind::Word => {
return (meta_item.name(), meta_item.value_str());
let ident = meta_item.ident().expect("multi-segment cfg key");
return (ident.name, meta_item.value_str());
}
}
}

View File

@ -177,10 +177,12 @@ impl<'a, 'gcx, 'tcx> OnUnimplementedDirective {
for command in self.subcommands.iter().chain(Some(self)).rev() {
if let Some(ref condition) = command.condition {
if !attr::eval_condition(condition, &tcx.sess.parse_sess, &mut |c| {
options.contains(&(
c.name().as_str().to_string(),
c.value_str().map(|s| s.as_str().to_string())
))
c.ident_str().map_or(false, |name| {
options.contains(&(
name.to_string(),
c.value_str().map(|s| s.as_str().to_string())
))
})
}) {
debug!("evaluate: skipping {:?} due to condition", command);
continue

View File

@ -99,9 +99,9 @@ impl<'a, 'tcx> IfThisChanged<'a, 'tcx> {
fn argument(&self, attr: &ast::Attribute) -> Option<ast::Name> {
let mut value = None;
for list_item in attr.meta_item_list().unwrap_or_default() {
match list_item.word() {
Some(word) if value.is_none() =>
value = Some(word.name()),
match list_item.ident() {
Some(ident) if list_item.is_word() && value.is_none() =>
value = Some(ident.name),
_ =>
// FIXME better-encapsulate meta_item (don't directly access `node`)
span_bug!(list_item.span(), "unexpected meta-item {:?}", list_item.node),

View File

@ -576,7 +576,7 @@ fn expect_associated_value(tcx: TyCtxt<'_, '_, '_>, item: &NestedMetaItem) -> as
if let Some(value) = item.value_str() {
value
} else {
let msg = if let Some(name) = item.name() {
let msg = if let Some(name) = item.ident_str() {
format!("associated value expected for `{}`", name)
} else {
"expected an associated value".to_string()

View File

@ -760,7 +760,7 @@ impl LintPass for DeprecatedAttr {
impl EarlyLintPass for DeprecatedAttr {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
for &&(n, _, _, ref g) in &self.depr_attrs {
if attr.name() == n {
if attr.ident_str() == Some(n) {
if let &AttributeGate::Gated(Stability::Deprecated(link, suggestion),
ref name,
ref reason,

View File

@ -267,19 +267,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
}
}
let name = attr.name();
let name = attr.ident_str();
if !attr::is_used(attr) {
debug!("Emitting warning for: {:?}", attr);
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
// Is it a builtin attribute that must be used at the crate level?
let known_crate = BUILTIN_ATTRIBUTES.iter()
.find(|&&(builtin, ty, ..)| name == builtin && ty == AttributeType::CrateLevel)
.find(|&&(builtin, ty, ..)| {
name == Some(builtin) && ty == AttributeType::CrateLevel
})
.is_some();
// Has a plugin registered this attribute as one that must be used at
// the crate level?
let plugin_crate = plugin_attributes.iter()
.find(|&&(ref x, t)| name == &**x && AttributeType::CrateLevel == t)
.find(|&&(ref x, t)| name == Some(x) && AttributeType::CrateLevel == t)
.is_some();
if known_crate || plugin_crate {
let msg = match attr.style {

View File

@ -53,9 +53,7 @@ impl<'a, 'tcx> VarianceTest<'a, 'tcx> {
// The `..` are the names of fields to dump.
let meta_items = attr.meta_item_list().unwrap_or_default();
for meta_item in meta_items {
let name = meta_item.word().map(|mi| mi.name().as_str());
let name = name.as_ref().map(|s| &s[..]).unwrap_or("");
let name = meta_item.ident_str().unwrap_or("");
match name {
"abi" => {
self.tcx

View File

@ -56,10 +56,10 @@ pub fn load_plugins(sess: &Session,
for plugin in plugins {
// plugins must have a name and can't be key = value
match plugin.name() {
match plugin.ident_str() {
Some(name) if !plugin.is_value_str() => {
let args = plugin.meta_item_list().map(ToOwned::to_owned);
loader.load_plugin(plugin.span, &name.as_str(), args.unwrap_or_default());
loader.load_plugin(plugin.span, name, args.unwrap_or_default());
},
_ => call_malformed_plugin_attribute(sess, attr.span),
}

View File

@ -463,10 +463,9 @@ impl<'a> Resolver<'a> {
if let Some(attr) = attr::find_by_name(&item.attrs, "proc_macro_derive") {
if let Some(trait_attr) =
attr.meta_item_list().and_then(|list| list.get(0).cloned()) {
if let Some(ident) = trait_attr.name().map(Ident::with_empty_ctxt) {
let sp = trait_attr.span;
if let Some(ident) = trait_attr.ident() {
let def = Def::Macro(def.def_id(), MacroKind::ProcMacroStub);
self.define(parent, ident, MacroNS, (def, vis, sp, expansion));
self.define(parent, ident, MacroNS, (def, vis, ident.span, expansion));
}
}
}
@ -812,9 +811,9 @@ impl<'a> Resolver<'a> {
break;
}
MetaItemKind::List(nested_metas) => for nested_meta in nested_metas {
match nested_meta.word() {
Some(word) => single_imports.push((word.name(), word.span)),
None => ill_formed(nested_meta.span),
match nested_meta.ident() {
Some(ident) if nested_meta.is_word() => single_imports.push(ident),
_ => ill_formed(nested_meta.span),
}
}
MetaItemKind::NameValue(..) => ill_formed(meta.span),
@ -850,23 +849,23 @@ impl<'a> Resolver<'a> {
self.legacy_import_macro(ident.name, imported_binding, span, allow_shadowing);
});
} else {
for (name, span) in single_imports.iter().cloned() {
let ident = Ident::with_empty_ctxt(name);
for ident in single_imports.iter().cloned() {
let result = self.resolve_ident_in_module(
ModuleOrUniformRoot::Module(module),
ident,
MacroNS,
None,
false,
span,
ident.span,
);
if let Ok(binding) = result {
let directive = macro_use_directive(span);
let directive = macro_use_directive(ident.span);
self.potentially_unused_imports.push(directive);
let imported_binding = self.import(binding, directive);
self.legacy_import_macro(name, imported_binding, span, allow_shadowing);
self.legacy_import_macro(ident.name, imported_binding,
ident.span, allow_shadowing);
} else {
span_err!(self.session, span, E0469, "imported macro not found");
span_err!(self.session, ident.span, E0469, "imported macro not found");
}
}
}

View File

@ -58,7 +58,13 @@ impl Cfg {
/// If the content is not properly formatted, it will return an error indicating what and where
/// the error is.
pub fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
let name = cfg.name();
let name = match cfg.ident() {
Some(ident) => ident.name,
None => return Err(InvalidCfgError {
msg: "expected a single identifier",
span: cfg.span
}),
};
match cfg.node {
MetaItemKind::Word => Ok(Cfg::Cfg(name, None)),
MetaItemKind::NameValue(ref lit) => match lit.node {

View File

@ -492,7 +492,7 @@ impl Item {
pub fn is_non_exhaustive(&self) -> bool {
self.attrs.other_attrs.iter()
.any(|a| a.name().as_str() == "non_exhaustive")
.any(|a| a.check_name("non_exhaustive"))
}
/// Returns a documentation-level item type from the item.
@ -3683,7 +3683,7 @@ impl Clean<Vec<Item>> for doctree::ExternCrate {
fn clean(&self, cx: &DocContext<'_>) -> Vec<Item> {
let please_inline = self.vis.node.is_pub() && self.attrs.iter().any(|a| {
a.name() == "doc" && match a.meta_item_list() {
a.check_name("doc") && match a.meta_item_list() {
Some(l) => attr::list_contains_name(&l, "inline"),
None => false,
}
@ -3722,7 +3722,7 @@ impl Clean<Vec<Item>> 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 mut denied = !self.vis.node.is_pub() || self.attrs.iter().any(|a| {
a.name() == "doc" && match a.meta_item_list() {
a.check_name("doc") && match a.meta_item_list() {
Some(l) => attr::list_contains_name(&l, "no_inline") ||
attr::list_contains_name(&l, "hidden"),
None => false,

View File

@ -521,8 +521,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
for attr in krate.module.as_ref().unwrap().attrs.lists("doc") {
let diag = ctxt.sess().diagnostic();
let name = attr.name().map(|s| s.as_str());
let name = name.as_ref().map(|s| &s[..]);
let name = attr.ident_str();
if attr.is_word() {
if name == Some("no_default_passes") {
report_deprecated_attr("no_default_passes", diag);

View File

@ -562,8 +562,7 @@ pub fn run(mut krate: clean::Crate,
// going to emit HTML
if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) {
for attr in attrs.lists("doc") {
let name = attr.name().map(|s| s.as_str());
match (name.as_ref().map(|s| &s[..]), attr.value_str()) {
match (attr.ident_str(), attr.value_str()) {
(Some("html_favicon_url"), Some(s)) => {
scx.layout.favicon = s.to_string();
}
@ -3714,19 +3713,19 @@ fn item_enum(w: &mut fmt::Formatter<'_>, cx: &Context, it: &clean::Item,
}
fn render_attribute(attr: &ast::MetaItem) -> Option<String> {
let name = attr.name();
let path = attr.ident.to_string();
if attr.is_word() {
Some(name.to_string())
Some(path)
} else if let Some(v) = attr.value_str() {
Some(format!("{} = {:?}", name, v.as_str()))
Some(format!("{} = {:?}", path, v.as_str()))
} else if let Some(values) = attr.meta_item_list() {
let display: Vec<_> = values.iter().filter_map(|attr| {
attr.meta_item().and_then(|mi| render_attribute(mi))
}).collect();
if display.len() > 0 {
Some(format!("{}({})", name, display.join(", ")))
Some(format!("{}({})", path, display.join(", ")))
} else {
None
}
@ -3750,8 +3749,7 @@ fn render_attributes(w: &mut fmt::Formatter<'_>, it: &clean::Item) -> fmt::Resul
let mut attrs = String::new();
for attr in &it.attrs.other_attrs {
let name = attr.name();
if !ATTRIBUTE_WHITELIST.contains(&&*name.as_str()) {
if !attr.ident_str().map_or(false, |name| ATTRIBUTE_WHITELIST.contains(&name)) {
continue;
}
if let Some(s) = render_attribute(&attr.meta().unwrap()) {

View File

@ -178,9 +178,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
Some(kind) => {
let name = if kind == MacroKind::Derive {
item.attrs.lists("proc_macro_derive")
.filter_map(|mi| mi.name())
.filter_map(|mi| mi.ident())
.next()
.expect("proc-macro derives require a name")
.name
} else {
name
};
@ -193,8 +194,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
if let Some(list) = mi.meta_item_list() {
for inner_mi in list {
if let Some(name) = inner_mi.name() {
helpers.push(name);
if let Some(ident) = inner_mi.ident() {
helpers.push(ident.name);
}
}
}

View File

@ -1,6 +1,6 @@
//! Parsing and validation of builtin attributes
use crate::ast::{self, Attribute, MetaItem, Name, NestedMetaItemKind};
use crate::ast::{self, Attribute, MetaItem, NestedMetaItemKind};
use crate::feature_gate::{Features, GatedCfg};
use crate::parse::ParseSess;
@ -10,8 +10,8 @@ use syntax_pos::{symbol::Symbol, Span};
use super::{mark_used, MetaItemKind};
enum AttrError {
MultipleItem(Name),
UnknownMetaItem(Name, &'static [&'static str]),
MultipleItem(String),
UnknownMetaItem(String, &'static [&'static str]),
MissingSince,
MissingFeature,
MultipleStabilityLevels,
@ -155,10 +155,7 @@ pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool {
attrs.iter().any(|item| {
item.check_name("feature") &&
item.meta_item_list().map(|list| {
list.iter().any(|mi| {
mi.word().map(|w| w.name() == feature_name)
.unwrap_or(false)
})
list.iter().any(|mi| mi.is_word() && mi.check_name(feature_name))
}).unwrap_or(false)
})
}
@ -206,7 +203,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
let meta = meta.as_ref().unwrap();
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
if item.is_some() {
handle_errors(sess, meta.span, AttrError::MultipleItem(meta.name()));
handle_errors(sess, meta.span, AttrError::MultipleItem(meta.ident.to_string()));
return false
}
if let Some(v) = meta.value_str() {
@ -225,9 +222,9 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
)+
for meta in metas {
if let Some(mi) = meta.meta_item() {
match &*mi.name().as_str() {
match mi.ident_str() {
$(
stringify!($name)
Some(stringify!($name))
=> if !get(mi, &mut $name) { continue 'outer },
)+
_ => {
@ -235,7 +232,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
handle_errors(
sess,
mi.span,
AttrError::UnknownMetaItem(mi.name(), expected),
AttrError::UnknownMetaItem(mi.ident.to_string(), expected),
);
continue 'outer
}
@ -255,7 +252,7 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
}
}
match &*meta.name().as_str() {
match meta.ident_str().expect("not a stability level") {
"rustc_deprecated" => {
if rustc_depr.is_some() {
span_err!(diagnostic, item_sp, E0540,
@ -309,16 +306,16 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
let mut issue = None;
for meta in metas {
if let Some(mi) = meta.meta_item() {
match &*mi.name().as_str() {
"feature" => if !get(mi, &mut feature) { continue 'outer },
"reason" => if !get(mi, &mut reason) { continue 'outer },
"issue" => if !get(mi, &mut issue) { continue 'outer },
match mi.ident_str() {
Some("feature") => if !get(mi, &mut feature) { continue 'outer },
Some("reason") => if !get(mi, &mut reason) { continue 'outer },
Some("issue") => if !get(mi, &mut issue) { continue 'outer },
_ => {
handle_errors(
sess,
meta.span,
AttrError::UnknownMetaItem(
mi.name(),
mi.ident.to_string(),
&["feature", "reason", "issue"]
),
);
@ -380,15 +377,17 @@ fn find_stability_generic<'a, I>(sess: &ParseSess,
for meta in metas {
match &meta.node {
NestedMetaItemKind::MetaItem(mi) => {
match &*mi.name().as_str() {
"feature" => if !get(mi, &mut feature) { continue 'outer },
"since" => if !get(mi, &mut since) { continue 'outer },
match mi.ident_str() {
Some("feature") =>
if !get(mi, &mut feature) { continue 'outer },
Some("since") =>
if !get(mi, &mut since) { continue 'outer },
_ => {
handle_errors(
sess,
meta.span,
AttrError::UnknownMetaItem(
mi.name(), &["since", "note"],
mi.ident.to_string(), &["since", "note"],
),
);
continue 'outer
@ -502,7 +501,8 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
true
}
MetaItemKind::NameValue(..) | MetaItemKind::Word => {
sess.config.contains(&(cfg.name(), cfg.value_str()))
let ident = cfg.ident().expect("multi-segment cfg predicate");
sess.config.contains(&(ident.name, cfg.value_str()))
}
}
})
@ -532,14 +532,14 @@ pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F)
// The unwraps below may look dangerous, but we've already asserted
// that they won't fail with the loop above.
match &*cfg.name().as_str() {
"any" => mis.iter().any(|mi| {
match cfg.ident_str() {
Some("any") => mis.iter().any(|mi| {
eval_condition(mi.meta_item().unwrap(), sess, eval)
}),
"all" => mis.iter().all(|mi| {
Some("all") => mis.iter().all(|mi| {
eval_condition(mi.meta_item().unwrap(), sess, eval)
}),
"not" => {
Some("not") => {
if mis.len() != 1 {
span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern");
return false;
@ -547,8 +547,9 @@ pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F)
!eval_condition(mis[0].meta_item().unwrap(), sess, eval)
},
p => {
span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", p);
_ => {
span_err!(sess.span_diagnostic, cfg.span, E0537,
"invalid predicate `{}`", cfg.ident);
false
}
}
@ -602,7 +603,9 @@ fn find_deprecation_generic<'a, I>(sess: &ParseSess,
MetaItemKind::List(list) => {
let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
if item.is_some() {
handle_errors(sess, meta.span, AttrError::MultipleItem(meta.name()));
handle_errors(
sess, meta.span, AttrError::MultipleItem(meta.ident.to_string())
);
return false
}
if let Some(v) = meta.value_str() {
@ -632,14 +635,15 @@ fn find_deprecation_generic<'a, I>(sess: &ParseSess,
for meta in list {
match &meta.node {
NestedMetaItemKind::MetaItem(mi) => {
match &*mi.name().as_str() {
"since" => if !get(mi, &mut since) { continue 'outer },
"note" => if !get(mi, &mut note) { continue 'outer },
match mi.ident_str() {
Some("since") => if !get(mi, &mut since) { continue 'outer },
Some("note") => if !get(mi, &mut note) { continue 'outer },
_ => {
handle_errors(
sess,
meta.span,
AttrError::UnknownMetaItem(mi.name(), &["since", "note"]),
meta.span(),
AttrError::UnknownMetaItem(mi.ident.to_string(),
&["since", "note"]),
);
continue 'outer
}
@ -724,19 +728,13 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
}
let mut recognised = false;
if let Some(mi) = item.word() {
let word = &*mi.name().as_str();
let hint = match word {
"C" => Some(ReprC),
"packed" => Some(ReprPacked(1)),
"simd" => Some(ReprSimd),
"transparent" => Some(ReprTransparent),
_ => match int_type_of_word(word) {
Some(ity) => Some(ReprInt(ity)),
None => {
None
}
}
if item.is_word() {
let hint = match item.ident_str() {
Some("C") => Some(ReprC),
Some("packed") => Some(ReprPacked(1)),
Some("simd") => Some(ReprSimd),
Some("transparent") => Some(ReprTransparent),
name => name.and_then(|name| int_type_of_word(name)).map(ReprInt),
};
if let Some(h) = hint {
@ -782,7 +780,7 @@ pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
}
} else {
if let Some(meta_item) = item.meta_item() {
if meta_item.name() == "align" {
if meta_item.check_name("align") {
if let MetaItemKind::NameValue(ref value) = meta_item.node {
recognised = true;
let mut err = struct_span_err!(diagnostic, item.span, E0693,

View File

@ -90,10 +90,12 @@ impl NestedMetaItem {
self.meta_item().map_or(false, |meta_item| meta_item.check_name(name))
}
/// Returns the name of the meta item, e.g., `foo` in `#[foo]`,
/// `#[foo="bar"]` and `#[foo(bar)]`, if self is a MetaItem
pub fn name(&self) -> Option<Name> {
self.meta_item().and_then(|meta_item| Some(meta_item.name()))
/// For a single-segment meta-item returns its name, otherwise returns `None`.
pub fn ident(&self) -> Option<Ident> {
self.meta_item().and_then(|meta_item| meta_item.ident())
}
pub fn ident_str(&self) -> Option<&str> {
self.ident().map(|name| name.as_str().get())
}
/// Gets the string value if self is a MetaItem and the MetaItem is a
@ -108,25 +110,14 @@ impl NestedMetaItem {
|meta_item| meta_item.meta_item_list().and_then(
|meta_item_list| {
if meta_item_list.len() == 1 {
let nested_item = &meta_item_list[0];
if nested_item.is_literal() {
Some((meta_item.name(), nested_item.literal().unwrap()))
} else {
None
if let Some(ident) = meta_item.ident() {
if let Some(lit) = meta_item_list[0].literal() {
return Some((ident.name, lit));
}
}
}
else {
None
}}))
}
/// Returns a MetaItem if self is a MetaItem with Kind Word.
pub fn word(&self) -> Option<&MetaItem> {
self.meta_item().and_then(|meta_item| if meta_item.is_word() {
Some(meta_item)
} else {
None
})
None
}))
}
/// Gets a list of inner meta items from a list MetaItem type.
@ -146,7 +137,7 @@ impl NestedMetaItem {
/// Returns `true` if self is a MetaItem and the meta item is a word.
pub fn is_word(&self) -> bool {
self.word().is_some()
self.meta_item().map_or(false, |meta_item| meta_item.is_word())
}
/// Returns `true` if self is a MetaItem and the meta item is a ValueString.
@ -160,10 +151,6 @@ impl NestedMetaItem {
}
}
fn name_from_path(path: &Path) -> Name {
path.segments.last().expect("empty path in attribute").ident.name
}
impl Attribute {
/// Returns `true` if the attribute's path matches the argument. If it matches, then the
/// attribute is marked as used.
@ -177,10 +164,16 @@ impl Attribute {
matches
}
/// 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)
/// For a single-segment attribute returns its name, otherwise returns `None`.
pub fn ident(&self) -> Option<Ident> {
if self.path.segments.len() == 1 {
Some(self.path.segments[0].ident)
} else {
None
}
}
pub fn ident_str(&self) -> Option<&str> {
self.ident().map(|name| name.as_str().get())
}
pub fn value_str(&self) -> Option<Symbol> {
@ -195,7 +188,7 @@ impl Attribute {
}
pub fn is_word(&self) -> bool {
self.path.segments.len() == 1 && self.tokens.is_empty()
self.tokens.is_empty()
}
pub fn span(&self) -> Span {
@ -213,8 +206,16 @@ impl Attribute {
}
impl MetaItem {
pub fn name(&self) -> Name {
name_from_path(&self.ident)
/// For a single-segment meta-item returns its name, otherwise returns `None`.
pub fn ident(&self) -> Option<Ident> {
if self.ident.segments.len() == 1 {
Some(self.ident.segments[0].ident)
} else {
None
}
}
pub fn ident_str(&self) -> Option<&str> {
self.ident().map(|name| name.as_str().get())
}
// #[attribute(name = "value")]
@ -255,7 +256,7 @@ impl MetaItem {
pub fn span(&self) -> Span { self.span }
pub fn check_name(&self, name: &str) -> bool {
self.name() == name
self.ident == name
}
pub fn is_value_str(&self) -> bool {
@ -265,14 +266,6 @@ impl MetaItem {
pub fn is_meta_item_list(&self) -> bool {
self.meta_item_list().is_some()
}
pub fn is_scoped(&self) -> Option<Ident> {
if self.ident.segments.len() > 1 {
Some(self.ident.segments[0].ident)
} else {
None
}
}
}
impl Attribute {

View File

@ -380,9 +380,14 @@ pub fn compile(
.map(|attr| attr
.meta_item_list()
.map(|list| list.iter()
.map(|it| it.name().unwrap_or_else(|| sess.span_diagnostic.span_bug(
it.span, "allow internal unstable expects feature names",
)))
.filter_map(|it| {
let name = it.ident().map(|ident| ident.name);
if name.is_none() {
sess.span_diagnostic.span_err(it.span(),
"allow internal unstable expects feature names")
}
name
})
.collect::<Vec<Symbol>>().into()
)
.unwrap_or_else(|| {

View File

@ -1289,9 +1289,8 @@ pub struct GatedCfg {
impl GatedCfg {
pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
let name = cfg.name().as_str();
GATED_CFGS.iter()
.position(|info| info.0 == name)
.position(|info| cfg.check_name(info.0))
.map(|idx| {
GatedCfg {
span: cfg.span,
@ -1342,16 +1341,16 @@ 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 = attr.name().as_str();
let name = attr.ident_str();
for &(n, ty, _template, ref gateage) in BUILTIN_ATTRIBUTES {
if name == n {
if name == Some(n) {
if let Gated(_, name, desc, ref has_feature) = *gateage {
if !attr.span.allows_unstable(name) {
gate_feature_fn!(
self, has_feature, attr.span, name, desc, GateStrength::Hard
);
}
} else if name == "doc" {
} else if n == "doc" {
if let Some(content) = attr.meta_item_list() {
if content.iter().any(|c| c.check_name("include")) {
gate_feature!(self, external_doc, attr.span,
@ -1374,7 +1373,7 @@ impl<'a> Context<'a> {
}
}
if !attr::is_known(attr) {
if name.starts_with("rustc_") {
if name.map_or(false, |name| name.starts_with("rustc_")) {
let msg = "unless otherwise specified, attributes with the prefix `rustc_` \
are reserved for internal compiler diagnostics";
gate_feature!(self, rustc_attrs, attr.span, msg);
@ -2055,13 +2054,12 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
};
for mi in list {
let name = if let Some(word) = mi.word() {
word.name()
} else {
continue
let name = match mi.ident_str() {
Some(name) if mi.is_word() => name,
_ => continue,
};
if incomplete_features.iter().any(|f| *f == name.as_str()) {
if incomplete_features.iter().any(|f| *f == name) {
span_handler.struct_span_warn(
mi.span,
&format!(
@ -2101,12 +2099,13 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute],
};
for mi in list {
let name = if let Some(word) = mi.word() {
word.name()
} else {
span_err!(span_handler, mi.span, E0556,
"malformed feature, expected just one word");
continue
let name = match mi.ident() {
Some(ident) if mi.is_word() => ident.name,
_ => {
span_err!(span_handler, mi.span, E0556,
"malformed feature, expected just one word");
continue
}
};
if let Some(edition) = edition_enabled_features.get(&name) {

View File

@ -438,6 +438,9 @@ fn get_test_runner(sd: &errors::Handler, krate: &ast::Crate) -> Option<ast::Path
sd.span_fatal(test_attr.span(),
"#![test_runner(..)] accepts exactly 1 argument").raise()
}
meta_list[0].word().as_ref().unwrap().ident.clone()
match meta_list[0].meta_item() {
Some(meta_item) if meta_item.is_word() => meta_item.ident.clone(),
_ => sd.span_fatal(test_attr.span, "`test_runner` argument must be a path").raise()
}
})
}

View File

@ -17,9 +17,11 @@ struct MarkAttrs<'a>(&'a [ast::Name]);
impl<'a> Visitor<'a> for MarkAttrs<'a> {
fn visit_attribute(&mut self, attr: &Attribute) {
if self.0.contains(&attr.name()) {
mark_used(attr);
mark_known(attr);
if let Some(ident) = attr.ident() {
if self.0.contains(&ident.name) {
mark_used(attr);
mark_known(attr);
}
}
}

View File

@ -463,12 +463,9 @@ impl<'a> TraitDef<'a> {
let mut attrs = newitem.attrs.clone();
attrs.extend(item.attrs
.iter()
.filter(|a| {
match &*a.name().as_str() {
"allow" | "warn" | "deny" | "forbid" | "stable" | "unstable" => true,
_ => false,
}
})
.filter(|a| a.ident_str().map_or(false, |name| {
["allow", "warn", "deny", "forbid", "stable", "unstable"].contains(&name)
}))
.cloned());
push(Annotatable::Item(P(ast::Item { attrs: attrs, ..(*newitem).clone() })))
}

View File

@ -113,24 +113,27 @@ impl<'a> CollectProcMacros<'a> {
"attribute must have either one or two arguments");
return
}
let trait_attr = &list[0];
let attributes_attr = list.get(1);
let trait_name = match trait_attr.name() {
Some(name) => name,
let trait_attr = match list[0].meta_item() {
Some(meta_item) => meta_item,
_ => {
self.handler.span_err(trait_attr.span(), "not a meta item");
self.handler.span_err(list[0].span(), "not a meta item");
return
}
};
let trait_ident = match trait_attr.ident() {
Some(trait_ident) if trait_attr.is_word() => trait_ident,
_ => {
self.handler.span_err(trait_attr.span, "must only be one word");
return
}
};
if !trait_attr.is_word() {
self.handler.span_err(trait_attr.span(), "must only be one word");
}
if deriving::is_builtin_trait(trait_name) {
self.handler.span_err(trait_attr.span(),
if deriving::is_builtin_trait(trait_ident.name) {
self.handler.span_err(trait_attr.span,
"cannot override a built-in #[derive] mode");
}
let attributes_attr = list.get(1);
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
if !attr.check_name("attributes") {
self.handler.span_err(attr.span(), "second argument must be `attributes`")
@ -141,20 +144,22 @@ impl<'a> CollectProcMacros<'a> {
`attributes(foo, bar)`");
&[]
}).into_iter().filter_map(|attr| {
let name = match attr.name() {
Some(name) => name,
let attr = match attr.meta_item() {
Some(meta_item) => meta_item,
_ => {
self.handler.span_err(attr.span(), "not a meta item");
return None;
},
};
if !attr.is_word() {
self.handler.span_err(attr.span(), "must only be one word");
return None;
}
Some(name)
let ident = match attr.ident() {
Some(ident) if attr.is_word() => ident,
_ => {
self.handler.span_err(attr.span, "must only be one word");
return None;
}
};
Some(ident.name)
}).collect()
} else {
Vec::new()
@ -163,7 +168,7 @@ impl<'a> CollectProcMacros<'a> {
if self.in_root && item.vis.node.is_pub() {
self.derives.push(ProcMacroDerive {
span: item.span,
trait_name,
trait_name: trait_ident.name,
function_name: item.ident,
attrs: proc_attrs,
});

View File

@ -31,7 +31,7 @@ pub fn foo5(input: TokenStream) -> TokenStream { input }
pub fn foo6(input: TokenStream) -> TokenStream { input }
#[proc_macro_derive(m::d7)]
//FIXME ERROR: must only be one word
//~^ ERROR: must only be one word
pub fn foo7(input: TokenStream) -> TokenStream { input }
#[proc_macro_derive(d8(a))]
@ -64,7 +64,7 @@ pub fn foo13(input: TokenStream) -> TokenStream { input }
pub fn foo14(input: TokenStream) -> TokenStream { input }
#[proc_macro_derive(d15, attributes(m::a))]
//FIXME ERROR: must only be one word
//~^ ERROR: must only be one word
pub fn foo15(input: TokenStream) -> TokenStream { input }
#[proc_macro_derive(d16, attributes(a(b)))]

View File

@ -22,6 +22,12 @@ error: must only be one word
LL | #[proc_macro_derive(d6 = "")]
| ^^^^^^^
error: must only be one word
--> $DIR/attribute.rs:33:21
|
LL | #[proc_macro_derive(m::d7)]
| ^^^^^
error: must only be one word
--> $DIR/attribute.rs:37:21
|
@ -64,6 +70,12 @@ error: must only be one word
LL | #[proc_macro_derive(d14, attributes(a = ""))]
| ^^^^^^
error: must only be one word
--> $DIR/attribute.rs:66:37
|
LL | #[proc_macro_derive(d15, attributes(m::a))]
| ^^^^
error: must only be one word
--> $DIR/attribute.rs:70:37
|
@ -82,5 +94,5 @@ error: attribute must be of the form `#[proc_macro_derive(TraitName, /*opt*/ att
LL | #[proc_macro_derive = ""]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 14 previous errors
error: aborting due to 16 previous errors