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 Some([clippy, segment2]) = attr.ident_path().as_deref()
24 && clippy.name == sym::clippy
25 {
26 let new_name = match segment2.name {
27 sym::cyclomatic_complexity => Some("cognitive_complexity"),
28 sym::author
29 | sym::version
30 | sym::cognitive_complexity
31 | sym::dump
32 | sym::msrv
33 | sym::has_significant_drop
36 | sym::format_args => None,
37 _ => {
38 sess.dcx().span_err(segment2.span, "usage of unknown attribute");
39 return false;
40 },
41 };
42
43 match new_name {
44 Some(new_name) => {
45 sess.dcx()
46 .struct_span_err(segment2.span, "usage of deprecated attribute")
47 .with_span_suggestion(
48 segment2.span,
49 "consider using",
50 new_name,
51 Applicability::MachineApplicable,
52 )
53 .emit();
54 false
55 },
56 None => segment2.name == name,
57 }
58 } else {
59 false
60 }
61 })
62}
63
64pub fn get_unique_builtin_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: Symbol) -> Option<&'a A> {
67 let mut unique_attr: Option<&A> = None;
68 for attr in get_builtin_attr(sess, attrs, name) {
69 if let Some(duplicate) = unique_attr {
70 sess.dcx()
71 .struct_span_err(attr.span(), format!("`{name}` is defined multiple times"))
72 .with_span_note(duplicate.span(), "first definition found here")
73 .emit();
74 } else {
75 unique_attr = Some(attr);
76 }
77 }
78 unique_attr
79}
80
81pub fn is_proc_macro(attrs: &[impl AttributeExt]) -> bool {
84 attrs.iter().any(AttributeExt::is_proc_macro_attr)
85}
86
87pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool {
89 attrs.iter().any(|attr| attr.is_doc_hidden())
90}
91
92pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool {
94 adt.is_variant_list_non_exhaustive()
95 || find_attr!(tcx.get_all_attrs(adt.did()), AttributeKind::NonExhaustive(..))
96 || adt.variants().iter().any(|variant_def| {
97 variant_def.is_field_list_non_exhaustive()
98 || find_attr!(tcx.get_all_attrs(variant_def.def_id), AttributeKind::NonExhaustive(..))
99 })
100 || adt
101 .all_fields()
102 .any(|field_def| find_attr!(tcx.get_all_attrs(field_def.did), AttributeKind::NonExhaustive(..)))
103}
104
105pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
107 s.check_source_text(cx, |src| {
108 let mut iter = tokenize_with_text(src);
109
110 while iter.any(|(t, ..)| matches!(t, TokenKind::Pound)) {
112 let mut iter = iter.by_ref().skip_while(|(t, ..)| {
113 matches!(
114 t,
115 TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
116 )
117 });
118 if matches!(iter.next(), Some((TokenKind::OpenBracket, ..)))
119 && matches!(iter.next(), Some((TokenKind::Ident, "cfg", _)))
120 {
121 return true;
122 }
123 }
124 false
125 })
126}
127
128pub struct LimitStack {
130 default: u64,
131 stack: Vec<u64>,
132}
133
134impl Drop for LimitStack {
135 fn drop(&mut self) {
136 debug_assert_eq!(self.stack, Vec::<u64>::new()); }
138}
139
140#[expect(missing_docs, reason = "they're all trivial...")]
141impl LimitStack {
142 #[must_use]
143 pub fn new(limit: u64) -> Self {
145 Self {
146 default: limit,
147 stack: vec![],
148 }
149 }
150 pub fn limit(&self) -> u64 {
151 self.stack.last().copied().unwrap_or(self.default)
152 }
153 pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
154 let stack = &mut self.stack;
155 parse_attrs(sess, attrs, name, |val| stack.push(val));
156 }
157 pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
158 let stack = &mut self.stack;
159 parse_attrs(sess, attrs, name, |val| debug_assert_eq!(stack.pop(), Some(val)));
160 }
161}
162
163fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[impl AttributeExt], name: Symbol, mut f: F) {
164 for attr in get_builtin_attr(sess, attrs, name) {
165 let Some(value) = attr.value_str() else {
166 sess.dcx().span_err(attr.span(), "bad clippy attribute");
167 continue;
168 };
169 let Ok(value) = u64::from_str(value.as_str()) else {
170 sess.dcx().span_err(attr.span(), "not a number");
171 continue;
172 };
173 f(value);
174 }
175}