]> git.lizzy.rs Git - rust.git/blob - clippy_utils/src/attrs.rs
Improve heuristics for determining whether eager of lazy evaluation is preferred
[rust.git] / clippy_utils / src / attrs.rs
1 use rustc_ast::{ast, attr};
2 use rustc_errors::Applicability;
3 use rustc_session::Session;
4 use rustc_span::sym;
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 #[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 ];
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.name == sym::clippy {
68             BUILTIN_ATTRIBUTES
69                 .iter()
70                 .find_map(|&(builtin_name, ref deprecation_status)| {
71                     if attr_segments[1].ident.name.as_str() == builtin_name {
72                         Some(deprecation_status)
73                     } else {
74                         None
75                     }
76                 })
77                 .map_or_else(
78                     || {
79                         sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute");
80                         false
81                     },
82                     |deprecation_status| {
83                         let mut diag =
84                             sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute");
85                         match *deprecation_status {
86                             DeprecationStatus::Deprecated => {
87                                 diag.emit();
88                                 false
89                             },
90                             DeprecationStatus::Replaced(new_name) => {
91                                 diag.span_suggestion(
92                                     attr_segments[1].ident.span,
93                                     "consider using",
94                                     new_name.to_string(),
95                                     Applicability::MachineApplicable,
96                                 );
97                                 diag.emit();
98                                 false
99                             },
100                             DeprecationStatus::None => {
101                                 diag.cancel();
102                                 attr_segments[1].ident.name.as_str() == name
103                             },
104                         }
105                     },
106                 )
107         } else {
108             false
109         }
110     })
111 }
112
113 fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) {
114     for attr in get_attr(sess, attrs, name) {
115         if let Some(ref value) = attr.value_str() {
116             if let Ok(value) = FromStr::from_str(&value.as_str()) {
117                 f(value);
118             } else {
119                 sess.span_err(attr.span, "not a number");
120             }
121         } else {
122             sess.span_err(attr.span, "bad clippy attribute");
123         }
124     }
125 }
126
127 pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
128     let mut unique_attr = None;
129     for attr in get_attr(sess, attrs, name) {
130         match attr.style {
131             ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
132             ast::AttrStyle::Inner => {
133                 sess.struct_span_err(attr.span, &format!("`{}` is defined multiple times", name))
134                     .span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
135                     .emit();
136             },
137             ast::AttrStyle::Outer => {
138                 sess.span_err(attr.span, &format!("`{}` cannot be an outer attribute", name));
139             },
140         }
141     }
142     unique_attr
143 }
144
145 /// Return true if the attributes contain any of `proc_macro`,
146 /// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
147 pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
148     attrs.iter().any(|attr| sess.is_proc_macro_attr(attr))
149 }
150
151 /// Return true if the attributes contain `#[doc(hidden)]`
152 pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
153     attrs
154         .iter()
155         .filter(|attr| attr.has_name(sym::doc))
156         .filter_map(ast::Attribute::meta_item_list)
157         .any(|l| attr::list_contains_name(&l, sym::hidden))
158 }
159
160 /// Return true if the attributes contain `#[unstable]`
161 pub fn is_unstable(attrs: &[ast::Attribute]) -> bool {
162     attrs.iter().any(|attr| attr.has_name(sym::unstable))
163 }