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