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