]>
Commit | Line | Data |
---|---|---|
04454e1e | 1 | use rustc_ast::ast; |
923072b8 | 2 | use rustc_ast::attr; |
f20569fa XL |
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. | |
f20569fa XL |
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 | ||
a2a8927a | 17 | #[rustfmt::skip] |
f20569fa | 18 | pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[ |
a2a8927a XL |
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), | |
04454e1e | 25 | ("has_significant_drop", DeprecationStatus::None), |
f20569fa XL |
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 attr, _) = attr.kind { | |
63 | attr | |
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", | |
923072b8 | 95 | new_name, |
f20569fa XL |
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() { | |
a2a8927a | 117 | if let Ok(value) = FromStr::from_str(value.as_str()) { |
17df50a5 | 118 | f(value); |
f20569fa XL |
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!("`{}` is defined multiple times", name)) | |
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!("`{}` cannot be an outer attribute", name)); | |
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 | } | |
cdc7bbd5 XL |
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 | } |