mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 13:04:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			165 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! Meta-syntax validation logic of attributes for post-expansion.
 | |
| 
 | |
| use crate::parse_in;
 | |
| 
 | |
| use rustc_ast::tokenstream::{DelimSpan, TokenTree};
 | |
| use rustc_ast::{self as ast, Attribute, MacArgs, MacDelimiter, MetaItem, MetaItemKind};
 | |
| use rustc_errors::{Applicability, PResult};
 | |
| use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP};
 | |
| use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
 | |
| use rustc_session::parse::ParseSess;
 | |
| use rustc_span::{sym, Symbol};
 | |
| 
 | |
| pub fn check_meta(sess: &ParseSess, attr: &Attribute) {
 | |
|     if attr.is_doc_comment() {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     let attr_info =
 | |
|         attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a);
 | |
| 
 | |
|     // Check input tokens for built-in and key-value attributes.
 | |
|     match attr_info {
 | |
|         // `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
 | |
|         Some((name, _, template, _)) if name != sym::rustc_dummy => {
 | |
|             check_builtin_attribute(sess, attr, name, template)
 | |
|         }
 | |
|         _ => {
 | |
|             if let MacArgs::Eq(..) = attr.get_normal_item().args {
 | |
|                 // All key-value attributes are restricted to meta-item syntax.
 | |
|                 parse_meta(sess, attr)
 | |
|                     .map_err(|mut err| {
 | |
|                         err.emit();
 | |
|                     })
 | |
|                     .ok();
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> {
 | |
|     let item = attr.get_normal_item();
 | |
|     Ok(MetaItem {
 | |
|         span: attr.span,
 | |
|         path: item.path.clone(),
 | |
|         kind: match &item.args {
 | |
|             MacArgs::Empty => MetaItemKind::Word,
 | |
|             MacArgs::Eq(_, t) => {
 | |
|                 let t = TokenTree::Token(t.clone()).into();
 | |
|                 let v = parse_in(sess, t, "name value", |p| p.parse_unsuffixed_lit())?;
 | |
|                 MetaItemKind::NameValue(v)
 | |
|             }
 | |
|             MacArgs::Delimited(dspan, delim, t) => {
 | |
|                 check_meta_bad_delim(sess, *dspan, *delim, "wrong meta list delimiters");
 | |
|                 let nmis = parse_in(sess, t.clone(), "meta list", |p| p.parse_meta_seq_top())?;
 | |
|                 MetaItemKind::List(nmis)
 | |
|             }
 | |
|         },
 | |
|     })
 | |
| }
 | |
| 
 | |
| pub fn check_meta_bad_delim(sess: &ParseSess, span: DelimSpan, delim: MacDelimiter, msg: &str) {
 | |
|     if let ast::MacDelimiter::Parenthesis = delim {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     sess.span_diagnostic
 | |
|         .struct_span_err(span.entire(), msg)
 | |
|         .multipart_suggestion(
 | |
|             "the delimiters should be `(` and `)`",
 | |
|             vec![(span.open, "(".to_string()), (span.close, ")".to_string())],
 | |
|             Applicability::MachineApplicable,
 | |
|         )
 | |
|         .emit();
 | |
| }
 | |
| 
 | |
| /// Checks that the given meta-item is compatible with this `AttributeTemplate`.
 | |
| fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
 | |
|     match meta {
 | |
|         MetaItemKind::Word => template.word,
 | |
|         MetaItemKind::List(..) => template.list.is_some(),
 | |
|         MetaItemKind::NameValue(lit) if lit.kind.is_str() => template.name_value_str.is_some(),
 | |
|         MetaItemKind::NameValue(..) => false,
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn check_builtin_attribute(
 | |
|     sess: &ParseSess,
 | |
|     attr: &Attribute,
 | |
|     name: Symbol,
 | |
|     template: AttributeTemplate,
 | |
| ) {
 | |
|     // Some special attributes like `cfg` must be checked
 | |
|     // before the generic check, so we skip them here.
 | |
|     let should_skip = |name| name == sym::cfg;
 | |
|     // Some of previously accepted forms were used in practice,
 | |
|     // report them as warnings for now.
 | |
|     let should_warn = |name| {
 | |
|         name == sym::doc
 | |
|             || name == sym::ignore
 | |
|             || name == sym::inline
 | |
|             || name == sym::link
 | |
|             || name == sym::test
 | |
|             || name == sym::bench
 | |
|     };
 | |
| 
 | |
|     match parse_meta(sess, attr) {
 | |
|         Ok(meta) => {
 | |
|             if !should_skip(name) && !is_attr_template_compatible(&template, &meta.kind) {
 | |
|                 let error_msg = format!("malformed `{}` attribute input", name);
 | |
|                 let mut msg = "attribute must be of the form ".to_owned();
 | |
|                 let mut suggestions = vec![];
 | |
|                 let mut first = true;
 | |
|                 if template.word {
 | |
|                     first = false;
 | |
|                     let code = format!("#[{}]", name);
 | |
|                     msg.push_str(&format!("`{}`", &code));
 | |
|                     suggestions.push(code);
 | |
|                 }
 | |
|                 if let Some(descr) = template.list {
 | |
|                     if !first {
 | |
|                         msg.push_str(" or ");
 | |
|                     }
 | |
|                     first = false;
 | |
|                     let code = format!("#[{}({})]", name, descr);
 | |
|                     msg.push_str(&format!("`{}`", &code));
 | |
|                     suggestions.push(code);
 | |
|                 }
 | |
|                 if let Some(descr) = template.name_value_str {
 | |
|                     if !first {
 | |
|                         msg.push_str(" or ");
 | |
|                     }
 | |
|                     let code = format!("#[{} = \"{}\"]", name, descr);
 | |
|                     msg.push_str(&format!("`{}`", &code));
 | |
|                     suggestions.push(code);
 | |
|                 }
 | |
|                 if should_warn(name) {
 | |
|                     sess.buffer_lint(
 | |
|                         &ILL_FORMED_ATTRIBUTE_INPUT,
 | |
|                         meta.span,
 | |
|                         ast::CRATE_NODE_ID,
 | |
|                         &msg,
 | |
|                     );
 | |
|                 } else {
 | |
|                     sess.span_diagnostic
 | |
|                         .struct_span_err(meta.span, &error_msg)
 | |
|                         .span_suggestions(
 | |
|                             meta.span,
 | |
|                             if suggestions.len() == 1 {
 | |
|                                 "must be of the form"
 | |
|                             } else {
 | |
|                                 "the following are the possible correct uses"
 | |
|                             },
 | |
|                             suggestions.into_iter(),
 | |
|                             Applicability::HasPlaceholders,
 | |
|                         )
 | |
|                         .emit();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         Err(mut err) => {
 | |
|             err.emit();
 | |
|         }
 | |
|     }
 | |
| }
 | 
