1use crate::source::SpanRangeExt;
4use crate::{sym, tokenize_with_text};
5use rustc_ast::attr::AttributeExt;
6use rustc_errors::Applicability;
7use rustc_hir::find_attr;
8use rustc_lexer::TokenKind;
9use rustc_lint::LateContext;
10use rustc_middle::ty::{AdtDef, TyCtxt};
11use rustc_session::Session;
12use rustc_span::{Span, Symbol};
13use std::str::FromStr;
14
15pub fn get_builtin_attr<'a, A: AttributeExt + 'a>(
17 sess: &'a Session,
18 attrs: &'a [A],
19 name: Symbol,
20) -> impl Iterator<Item = &'a A> {
21 attrs.iter().filter(move |attr| {
22 if let [clippy, segment2] = &*attr.path()
23 && *clippy == sym::clippy
24 {
25 let path_span = attr
26 .path_span()
27 .expect("Clippy attributes are unparsed and have a span");
28 let new_name = match *segment2 {
29 sym::cyclomatic_complexity => Some("cognitive_complexity"),
30 sym::author
31 | sym::version
32 | sym::cognitive_complexity
33 | sym::dump
34 | sym::msrv
35 | sym::has_significant_drop
38 | sym::format_args => None,
39 _ => {
40 sess.dcx().span_err(path_span, "usage of unknown attribute");
41 return false;
42 },
43 };
44
45 match new_name {
46 Some(new_name) => {
47 sess.dcx()
48 .struct_span_err(path_span, "usage of deprecated attribute")
49 .with_span_suggestion(
50 path_span,
51 "consider using",
52 format!("clippy::{new_name}"),
53 Applicability::MachineApplicable,
54 )
55 .emit();
56 false
57 },
58 None => *segment2 == name,
59 }
60 } else {
61 false
62 }
63 })
64}
65
66pub fn get_unique_builtin_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: Symbol) -> Option<&'a A> {
69 let mut unique_attr: Option<&A> = None;
70 for attr in get_builtin_attr(sess, attrs, name) {
71 if let Some(duplicate) = unique_attr {
72 sess.dcx()
73 .struct_span_err(attr.span(), format!("`{name}` is defined multiple times"))
74 .with_span_note(duplicate.span(), "first definition found here")
75 .emit();
76 } else {
77 unique_attr = Some(attr);
78 }
79 }
80 unique_attr
81}
82
83pub fn is_proc_macro(attrs: &[impl AttributeExt]) -> bool {
86 attrs.iter().any(AttributeExt::is_proc_macro_attr)
87}
88
89pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool {
91 attrs.iter().any(AttributeExt::is_doc_hidden)
92}
93
94pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool {
96 adt.is_variant_list_non_exhaustive()
97 || find_attr!(tcx, adt.did(), NonExhaustive(..))
98 || adt.variants().iter().any(|variant_def| {
99 variant_def.is_field_list_non_exhaustive() || find_attr!(tcx, variant_def.def_id, NonExhaustive(..))
100 })
101 || adt
102 .all_fields()
103 .any(|field_def| find_attr!(tcx, field_def.did, NonExhaustive(..)))
104}
105
106pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
108 s.check_source_text(cx, |src| {
109 let mut iter = tokenize_with_text(src);
110
111 while iter.any(|(t, ..)| matches!(t, TokenKind::Pound)) {
113 let mut iter = iter.by_ref().skip_while(|(t, ..)| {
114 matches!(
115 t,
116 TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
117 )
118 });
119 if matches!(iter.next(), Some((TokenKind::OpenBracket, ..)))
120 && matches!(iter.next(), Some((TokenKind::Ident, "cfg", _)))
121 {
122 return true;
123 }
124 }
125 false
126 })
127}
128
129pub struct LimitStack {
131 default: u64,
132 stack: Vec<u64>,
133}
134
135impl Drop for LimitStack {
136 fn drop(&mut self) {
137 debug_assert_eq!(self.stack, Vec::<u64>::new()); }
139}
140
141#[expect(missing_docs, reason = "they're all trivial...")]
142impl LimitStack {
143 #[must_use]
144 pub fn new(limit: u64) -> Self {
146 Self {
147 default: limit,
148 stack: vec![],
149 }
150 }
151 pub fn limit(&self) -> u64 {
152 self.stack.last().copied().unwrap_or(self.default)
153 }
154 pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
155 let stack = &mut self.stack;
156 parse_attrs(sess, attrs, name, |val| stack.push(val));
157 }
158 pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
159 let stack = &mut self.stack;
160 parse_attrs(sess, attrs, name, |val| {
161 let popped = stack.pop();
162 debug_assert_eq!(popped, Some(val));
163 });
164 }
165}
166
167fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[impl AttributeExt], name: Symbol, mut f: F) {
168 for attr in get_builtin_attr(sess, attrs, name) {
169 let Some(value) = attr.value_str() else {
170 sess.dcx().span_err(attr.span(), "bad clippy attribute");
171 continue;
172 };
173 let Ok(value) = u64::from_str(value.as_str()) else {
174 sess.dcx().span_err(attr.span(), "not a number");
175 continue;
176 };
177 f(value);
178 }
179}