| use rustc_ast::ast::Safety; | |
| use rustc_span::Symbol; | |
| use crate::ParsedDescription; | |
| /// A template to suggest the correct syntax of an attribute. | |
| /// | |
| /// This is not used to *check* attributes. The attribute's parser is responsible for that. | |
| /// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now. | |
| #[derive(Clone, Copy, Default)] | |
| pub struct AttributeTemplate { | |
| /// If `true`, the attribute is allowed to be a bare word like `#[test]`. | |
| pub word: bool, | |
| /// If `Some`, the attribute is allowed to take a list of items like `#[allow(..)]`. | |
| pub list: Option<&'static [&'static str]>, | |
| /// If non-empty, the attribute is allowed to take a list containing exactly | |
| /// one of the listed words, like `#[coverage(off)]`. | |
| pub one_of: &'static [Symbol], | |
| /// If `Some`, the attribute is allowed to be a name/value pair where the | |
| /// value is a string, like `#[must_use = "reason"]`. | |
| pub name_value_str: Option<&'static [&'static str]>, | |
| /// A link to the document for this attribute. | |
| pub docs: Option<&'static str>, | |
| } | |
| impl AttributeTemplate { | |
| pub fn suggestions( | |
| &self, | |
| description: ParsedDescription, | |
| safety: Safety, | |
| name: impl std::fmt::Display, | |
| ) -> Vec<String> { | |
| let macro_call = match description { | |
| ParsedDescription::Macro => "!", | |
| ParsedDescription::Attribute => "", | |
| }; | |
| let mut suggestions = vec![]; | |
| let (safety_start, safety_end) = match safety { | |
| Safety::Unsafe(_) => ("unsafe(", ")"), | |
| _ => ("", ""), | |
| }; | |
| if self.word { | |
| debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); | |
| suggestions.push(format!("{safety_start}{name}{safety_end}")); | |
| } | |
| if let Some(descr) = self.list { | |
| for descr in descr { | |
| suggestions.push(format!("{safety_start}{name}{macro_call}({descr}){safety_end}")); | |
| } | |
| } | |
| suggestions.extend( | |
| self.one_of.iter().map(|&word| format!("{safety_start}{name}({word}){safety_end}")), | |
| ); | |
| if let Some(descr) = self.name_value_str { | |
| debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); | |
| for descr in descr { | |
| suggestions.push(format!("{safety_start}{name} = \"{descr}\"{safety_end}")); | |
| } | |
| } | |
| suggestions.sort(); | |
| suggestions | |
| } | |
| } | |
| /// A convenience macro for constructing attribute templates. | |
| /// E.g., `template!(Word, List: "description")` means that the attribute | |
| /// supports forms `#[attr]` and `#[attr(description)]`. | |
| #[macro_export] | |
| macro_rules! template { | |
| (Word) => { $crate::template!(@ true, None, &[], None, None) }; | |
| (Word, $link: literal) => { $crate::template!(@ true, None, &[], None, Some($link)) }; | |
| (List: $descr: expr) => { $crate::template!(@ false, Some($descr), &[], None, None) }; | |
| (List: $descr: expr, $link: literal) => { $crate::template!(@ false, Some($descr), &[], None, Some($link)) }; | |
| (OneOf: $one_of: expr) => { $crate::template!(@ false, None, $one_of, None, None) }; | |
| (NameValueStr: [$($descr: literal),* $(,)?]) => { $crate::template!(@ false, None, &[], Some(&[$($descr,)*]), None) }; | |
| (NameValueStr: [$($descr: literal),* $(,)?], $link: literal) => { $crate::template!(@ false, None, &[], Some(&[$($descr,)*]), Some($link)) }; | |
| (NameValueStr: $descr: literal) => { $crate::template!(@ false, None, &[], Some(&[$descr]), None) }; | |
| (NameValueStr: $descr: literal, $link: literal) => { $crate::template!(@ false, None, &[], Some(&[$descr]), Some($link)) }; | |
| (Word, List: $descr: expr) => { $crate::template!(@ true, Some($descr), &[], None, None) }; | |
| (Word, List: $descr: expr, $link: literal) => { $crate::template!(@ true, Some($descr), &[], None, Some($link)) }; | |
| (Word, NameValueStr: $descr: expr) => { $crate::template!(@ true, None, &[], Some(&[$descr]), None) }; | |
| (Word, NameValueStr: $descr: expr, $link: literal) => { $crate::template!(@ true, None, &[], Some(&[$descr]), Some($link)) }; | |
| (List: $descr1: expr, NameValueStr: $descr2: expr) => { | |
| $crate::template!(@ false, Some($descr1), &[], Some(&[$descr2]), None) | |
| }; | |
| (List: $descr1: expr, NameValueStr: $descr2: expr, $link: literal) => { | |
| $crate::template!(@ false, Some($descr1), &[], Some(&[$descr2]), Some($link)) | |
| }; | |
| (Word, List: $descr1: expr, NameValueStr: $descr2: expr) => { | |
| $crate::template!(@ true, Some($descr1), &[], Some(&[$descr2]), None) | |
| }; | |
| (Word, List: $descr1: expr, NameValueStr: $descr2: expr, $link: literal) => { | |
| $crate::template!(@ true, Some($descr1), &[], Some(&[$descr2]), Some($link)) | |
| }; | |
| (@ $word: expr, $list: expr, $one_of: expr, $name_value_str: expr, $link: expr) => { $crate::AttributeTemplate { | |
| word: $word, list: $list, one_of: $one_of, name_value_str: $name_value_str, docs: $link, | |
| } }; | |
| } |