]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_utils/src/attrs.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_utils / src / attrs.rs
CommitLineData
04454e1e 1use rustc_ast::ast;
923072b8 2use rustc_ast::attr;
f20569fa
XL
3use rustc_errors::Applicability;
4use rustc_session::Session;
5use rustc_span::sym;
6use std::str::FromStr;
7
8/// Deprecation status of attributes known by Clippy.
f20569fa
XL
9pub 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 18pub 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
28pub struct LimitStack {
29 stack: Vec<u64>,
30}
31
32impl Drop for LimitStack {
33 fn drop(&mut self) {
34 assert_eq!(self.stack.len(), 1);
35 }
36}
37
38impl 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
56pub 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
114fn 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
128pub 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
148pub 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)]`
153pub 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}