]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/utils/attrs.rs
Rollup merge of #73236 - GuillaumeGomez:cleanup-e0666, r=Dylan-DPC
[rust.git] / src / tools / clippy / clippy_lints / src / utils / attrs.rs
1 use rustc_ast::ast;
2 use rustc_ast::expand::is_proc_macro_attr;
3 use rustc_errors::Applicability;
4 use rustc_session::Session;
5 use std::str::FromStr;
6
7 /// Deprecation status of attributes known by Clippy.
8 #[allow(dead_code)]
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 pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
18     ("author", DeprecationStatus::None),
19     ("cognitive_complexity", DeprecationStatus::None),
20     (
21         "cyclomatic_complexity",
22         DeprecationStatus::Replaced("cognitive_complexity"),
23     ),
24     ("dump", DeprecationStatus::None),
25 ];
26
27 pub struct LimitStack {
28     stack: Vec<u64>,
29 }
30
31 impl Drop for LimitStack {
32     fn drop(&mut self) {
33         assert_eq!(self.stack.len(), 1);
34     }
35 }
36
37 impl LimitStack {
38     #[must_use]
39     pub fn new(limit: u64) -> Self {
40         Self { stack: vec![limit] }
41     }
42     pub fn limit(&self) -> u64 {
43         *self.stack.last().expect("there should always be a value in the stack")
44     }
45     pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
46         let stack = &mut self.stack;
47         parse_attrs(sess, attrs, name, |val| stack.push(val));
48     }
49     pub fn pop_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
50         let stack = &mut self.stack;
51         parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val)));
52     }
53 }
54
55 pub fn get_attr<'a>(
56     sess: &'a Session,
57     attrs: &'a [ast::Attribute],
58     name: &'static str,
59 ) -> impl Iterator<Item = &'a ast::Attribute> {
60     attrs.iter().filter(move |attr| {
61         let attr = if let ast::AttrKind::Normal(ref attr) = attr.kind {
62             attr
63         } else {
64             return false;
65         };
66         let attr_segments = &attr.path.segments;
67         if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" {
68             if let Some(deprecation_status) =
69                 BUILTIN_ATTRIBUTES
70                     .iter()
71                     .find_map(|(builtin_name, deprecation_status)| {
72                         if *builtin_name == attr_segments[1].ident.to_string() {
73                             Some(deprecation_status)
74                         } else {
75                             None
76                         }
77                     })
78             {
79                 let mut diag = sess.struct_span_err(attr_segments[1].ident.span, "Usage of deprecated attribute");
80                 match *deprecation_status {
81                     DeprecationStatus::Deprecated => {
82                         diag.emit();
83                         false
84                     },
85                     DeprecationStatus::Replaced(new_name) => {
86                         diag.span_suggestion(
87                             attr_segments[1].ident.span,
88                             "consider using",
89                             new_name.to_string(),
90                             Applicability::MachineApplicable,
91                         );
92                         diag.emit();
93                         false
94                     },
95                     DeprecationStatus::None => {
96                         diag.cancel();
97                         attr_segments[1].ident.to_string() == name
98                     },
99                 }
100             } else {
101                 sess.span_err(attr_segments[1].ident.span, "Usage of unknown attribute");
102                 false
103             }
104         } else {
105             false
106         }
107     })
108 }
109
110 fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) {
111     for attr in get_attr(sess, attrs, name) {
112         if let Some(ref value) = attr.value_str() {
113             if let Ok(value) = FromStr::from_str(&value.as_str()) {
114                 f(value)
115             } else {
116                 sess.span_err(attr.span, "not a number");
117             }
118         } else {
119             sess.span_err(attr.span, "bad clippy attribute");
120         }
121     }
122 }
123
124 /// Return true if the attributes contain any of `proc_macro`,
125 /// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
126 pub fn is_proc_macro(attrs: &[ast::Attribute]) -> bool {
127     attrs.iter().any(is_proc_macro_attr)
128 }