]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_utils/src/attrs.rs
Merge commit 'ac0e10aa68325235069a842f47499852b2dee79e' into clippyup
[rust.git] / src / tools / clippy / clippy_utils / src / attrs.rs
1 use rustc_ast::ast;
2 use rustc_ast::attr;
3 use rustc_errors::Applicability;
4 use rustc_session::Session;
5 use rustc_span::sym;
6 use std::str::FromStr;
7
8 /// Deprecation status of attributes known by Clippy.
9 pub enum DeprecationStatus {
10     /// Attribute is deprecated
11     Deprecated,
12     /// Attribute is deprecated and was replaced by the named attribute
13     Replaced(&'static str),
14     None,
15 }
16
17 #[rustfmt::skip]
18 pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
19     ("author",                DeprecationStatus::None),
20     ("version",               DeprecationStatus::None),
21     ("cognitive_complexity",  DeprecationStatus::None),
22     ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")),
23     ("dump",                  DeprecationStatus::None),
24     ("msrv",                  DeprecationStatus::None),
25     ("has_significant_drop",  DeprecationStatus::None),
26 ];
27
28 pub struct LimitStack {
29     stack: Vec<u64>,
30 }
31
32 impl Drop for LimitStack {
33     fn drop(&mut self) {
34         assert_eq!(self.stack.len(), 1);
35     }
36 }
37
38 impl LimitStack {
39     #[must_use]
40     pub fn new(limit: u64) -> Self {
41         Self { stack: vec![limit] }
42     }
43     pub fn limit(&self) -> u64 {
44         *self.stack.last().expect("there should always be a value in the stack")
45     }
46     pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
47         let stack = &mut self.stack;
48         parse_attrs(sess, attrs, name, |val| stack.push(val));
49     }
50     pub fn pop_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
51         let stack = &mut self.stack;
52         parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val)));
53     }
54 }
55
56 pub fn get_attr<'a>(
57     sess: &'a Session,
58     attrs: &'a [ast::Attribute],
59     name: &'static str,
60 ) -> impl Iterator<Item = &'a ast::Attribute> {
61     attrs.iter().filter(move |attr| {
62         let attr = if let ast::AttrKind::Normal(ref normal) = attr.kind {
63             &normal.item
64         } else {
65             return false;
66         };
67         let attr_segments = &attr.path.segments;
68         if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy {
69             BUILTIN_ATTRIBUTES
70                 .iter()
71                 .find_map(|&(builtin_name, ref deprecation_status)| {
72                     if attr_segments[1].ident.name.as_str() == builtin_name {
73                         Some(deprecation_status)
74                     } else {
75                         None
76                     }
77                 })
78                 .map_or_else(
79                     || {
80                         sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute");
81                         false
82                     },
83                     |deprecation_status| {
84                         let mut diag =
85                             sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute");
86                         match *deprecation_status {
87                             DeprecationStatus::Deprecated => {
88                                 diag.emit();
89                                 false
90                             },
91                             DeprecationStatus::Replaced(new_name) => {
92                                 diag.span_suggestion(
93                                     attr_segments[1].ident.span,
94                                     "consider using",
95                                     new_name,
96                                     Applicability::MachineApplicable,
97                                 );
98                                 diag.emit();
99                                 false
100                             },
101                             DeprecationStatus::None => {
102                                 diag.cancel();
103                                 attr_segments[1].ident.name.as_str() == name
104                             },
105                         }
106                     },
107                 )
108         } else {
109             false
110         }
111     })
112 }
113
114 fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) {
115     for attr in get_attr(sess, attrs, name) {
116         if let Some(ref value) = attr.value_str() {
117             if let Ok(value) = FromStr::from_str(value.as_str()) {
118                 f(value);
119             } else {
120                 sess.span_err(attr.span, "not a number");
121             }
122         } else {
123             sess.span_err(attr.span, "bad clippy attribute");
124         }
125     }
126 }
127
128 pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
129     let mut unique_attr = None;
130     for attr in get_attr(sess, attrs, name) {
131         match attr.style {
132             ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
133             ast::AttrStyle::Inner => {
134                 sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
135                     .span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
136                     .emit();
137             },
138             ast::AttrStyle::Outer => {
139                 sess.span_err(attr.span, &format!("`{name}` cannot be an outer attribute"));
140             },
141         }
142     }
143     unique_attr
144 }
145
146 /// Return true if the attributes contain any of `proc_macro`,
147 /// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
148 pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
149     attrs.iter().any(|attr| sess.is_proc_macro_attr(attr))
150 }
151
152 /// Return true if the attributes contain `#[doc(hidden)]`
153 pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
154     attrs
155         .iter()
156         .filter(|attr| attr.has_name(sym::doc))
157         .filter_map(ast::Attribute::meta_item_list)
158         .any(|l| attr::list_contains_name(&l, sym::hidden))
159 }