1+ //! Utility functions for attributes, including Clippy's built-in ones
2+
13use crate :: source:: SpanRangeExt ;
24use crate :: { sym, tokenize_with_text} ;
35use rustc_ast:: attr;
@@ -12,131 +14,59 @@ use rustc_session::Session;
1214use rustc_span:: { Span , Symbol } ;
1315use std:: str:: FromStr ;
1416
15- /// Deprecation status of attributes known by Clippy.
16- pub enum DeprecationStatus {
17- /// Attribute is deprecated
18- Deprecated ,
19- /// Attribute is deprecated and was replaced by the named attribute
20- Replaced ( & ' static str ) ,
21- None ,
22- }
23-
24- #[ rustfmt:: skip]
25- pub const BUILTIN_ATTRIBUTES : & [ ( Symbol , DeprecationStatus ) ] = & [
26- ( sym:: author, DeprecationStatus :: None ) ,
27- ( sym:: version, DeprecationStatus :: None ) ,
28- ( sym:: cognitive_complexity, DeprecationStatus :: None ) ,
29- ( sym:: cyclomatic_complexity, DeprecationStatus :: Replaced ( "cognitive_complexity" ) ) ,
30- ( sym:: dump, DeprecationStatus :: None ) ,
31- ( sym:: msrv, DeprecationStatus :: None ) ,
32- // The following attributes are for the 3rd party crate authors.
33- // See book/src/attribs.md
34- ( sym:: has_significant_drop, DeprecationStatus :: None ) ,
35- ( sym:: format_args, DeprecationStatus :: None ) ,
36- ] ;
37-
38- pub struct LimitStack {
39- stack : Vec < u64 > ,
40- }
41-
42- impl Drop for LimitStack {
43- fn drop ( & mut self ) {
44- assert_eq ! ( self . stack. len( ) , 1 ) ;
45- }
46- }
47-
48- impl LimitStack {
49- #[ must_use]
50- pub fn new ( limit : u64 ) -> Self {
51- Self { stack : vec ! [ limit] }
52- }
53- pub fn limit ( & self ) -> u64 {
54- * self . stack . last ( ) . expect ( "there should always be a value in the stack" )
55- }
56- pub fn push_attrs ( & mut self , sess : & Session , attrs : & [ impl AttributeExt ] , name : Symbol ) {
57- let stack = & mut self . stack ;
58- parse_attrs ( sess, attrs, name, |val| stack. push ( val) ) ;
59- }
60- pub fn pop_attrs ( & mut self , sess : & Session , attrs : & [ impl AttributeExt ] , name : Symbol ) {
61- let stack = & mut self . stack ;
62- parse_attrs ( sess, attrs, name, |val| assert_eq ! ( stack. pop( ) , Some ( val) ) ) ;
63- }
64- }
65-
66- pub fn get_attr < ' a , A : AttributeExt + ' a > (
17+ /// Given `attrs`, extract all the instances of a built-in Clippy attribute called `name`
18+ pub fn get_builtin_attr < ' a , A : AttributeExt + ' a > (
6719 sess : & ' a Session ,
6820 attrs : & ' a [ A ] ,
6921 name : Symbol ,
7022) -> impl Iterator < Item = & ' a A > {
7123 attrs. iter ( ) . filter ( move |attr| {
72- let Some ( attr_segments) = attr. ident_path ( ) else {
73- return false ;
74- } ;
24+ if let Some ( [ clippy, segment2] ) = attr. ident_path ( ) . as_deref ( )
25+ && clippy. name == sym:: clippy
26+ {
27+ let new_name = match segment2. name {
28+ sym:: cyclomatic_complexity => Some ( "cognitive_complexity" ) ,
29+ sym:: author
30+ | sym:: version
31+ | sym:: cognitive_complexity
32+ | sym:: dump
33+ | sym:: msrv
34+ // The following attributes are for the 3rd party crate authors.
35+ // See book/src/attribs.md
36+ | sym:: has_significant_drop
37+ | sym:: format_args => None ,
38+ _ => {
39+ sess. dcx ( ) . span_err ( segment2. span , "usage of unknown attribute" ) ;
40+ return false ;
41+ } ,
42+ } ;
7543
76- if attr_segments. len ( ) == 2 && attr_segments[ 0 ] . name == sym:: clippy {
77- BUILTIN_ATTRIBUTES
78- . iter ( )
79- . find_map ( |( builtin_name, deprecation_status) | {
80- if attr_segments[ 1 ] . name == * builtin_name {
81- Some ( deprecation_status)
82- } else {
83- None
84- }
85- } )
86- . map_or_else (
87- || {
88- sess. dcx ( ) . span_err ( attr_segments[ 1 ] . span , "usage of unknown attribute" ) ;
89- false
90- } ,
91- |deprecation_status| {
92- let mut diag = sess
93- . dcx ( )
94- . struct_span_err ( attr_segments[ 1 ] . span , "usage of deprecated attribute" ) ;
95- match * deprecation_status {
96- DeprecationStatus :: Deprecated => {
97- diag. emit ( ) ;
98- false
99- } ,
100- DeprecationStatus :: Replaced ( new_name) => {
101- diag. span_suggestion (
102- attr_segments[ 1 ] . span ,
103- "consider using" ,
104- new_name,
105- Applicability :: MachineApplicable ,
106- ) ;
107- diag. emit ( ) ;
108- false
109- } ,
110- DeprecationStatus :: None => {
111- diag. cancel ( ) ;
112- attr_segments[ 1 ] . name == name
113- } ,
114- }
115- } ,
116- )
44+ match new_name {
45+ Some ( new_name) => {
46+ sess. dcx ( )
47+ . struct_span_err ( segment2. span , "usage of deprecated attribute" )
48+ . with_span_suggestion (
49+ segment2. span ,
50+ "consider using" ,
51+ new_name,
52+ Applicability :: MachineApplicable ,
53+ )
54+ . emit ( ) ;
55+ false
56+ } ,
57+ None => segment2. name == name,
58+ }
11759 } else {
11860 false
11961 }
12062 } )
12163}
12264
123- fn parse_attrs < F : FnMut ( u64 ) > ( sess : & Session , attrs : & [ impl AttributeExt ] , name : Symbol , mut f : F ) {
124- for attr in get_attr ( sess, attrs, name) {
125- if let Some ( value) = attr. value_str ( ) {
126- if let Ok ( value) = FromStr :: from_str ( value. as_str ( ) ) {
127- f ( value) ;
128- } else {
129- sess. dcx ( ) . span_err ( attr. span ( ) , "not a number" ) ;
130- }
131- } else {
132- sess. dcx ( ) . span_err ( attr. span ( ) , "bad clippy attribute" ) ;
133- }
134- }
135- }
136-
137- pub fn get_unique_attr < ' a , A : AttributeExt > ( sess : & ' a Session , attrs : & ' a [ A ] , name : Symbol ) -> Option < & ' a A > {
65+ /// If `attrs` contain exactly one instance of a built-in Clippy attribute called `name`,
66+ /// returns that attribute, and `None` otherwise
67+ pub fn get_unique_builtin_attr < ' a , A : AttributeExt > ( sess : & ' a Session , attrs : & ' a [ A ] , name : Symbol ) -> Option < & ' a A > {
13868 let mut unique_attr: Option < & A > = None ;
139- for attr in get_attr ( sess, attrs, name) {
69+ for attr in get_builtin_attr ( sess, attrs, name) {
14070 if let Some ( duplicate) = unique_attr {
14171 sess. dcx ( )
14272 . struct_span_err ( attr. span ( ) , format ! ( "`{name}` is defined multiple times" ) )
@@ -149,13 +79,13 @@ pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], n
14979 unique_attr
15080}
15181
152- /// Returns true if the attributes contain any of `proc_macro`,
153- /// `proc_macro_derive` or ` proc_macro_attribute`, false otherwise
82+ /// Checks whether `attrs` contain any of `proc_macro`, `proc_macro_derive` or
83+ /// `proc_macro_attribute`
15484pub fn is_proc_macro ( attrs : & [ impl AttributeExt ] ) -> bool {
15585 attrs. iter ( ) . any ( AttributeExt :: is_proc_macro_attr)
15686}
15787
158- /// Returns true if the attributes contain `#[doc(hidden)]`
88+ /// Checks whether `attrs` contain `#[doc(hidden)]`
15989pub fn is_doc_hidden ( attrs : & [ impl AttributeExt ] ) -> bool {
16090 attrs
16191 . iter ( )
@@ -164,6 +94,7 @@ pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool {
16494 . any ( |l| attr:: list_contains_name ( & l, sym:: hidden) )
16595}
16696
97+ /// Checks whether the given ADT, or any of its fields/variants, are marked as `#[non_exhaustive]`
16798pub fn has_non_exhaustive_attr ( tcx : TyCtxt < ' _ > , adt : AdtDef < ' _ > ) -> bool {
16899 adt. is_variant_list_non_exhaustive ( )
169100 || find_attr ! ( tcx. get_all_attrs( adt. did( ) ) , AttributeKind :: NonExhaustive ( ..) )
@@ -176,7 +107,7 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool {
176107 . any ( |field_def| find_attr ! ( tcx. get_all_attrs( field_def. did) , AttributeKind :: NonExhaustive ( ..) ) )
177108}
178109
179- /// Checks if the given span contains a `#[cfg(..)]` attribute
110+ /// Checks whether the given span contains a `#[cfg(..)]` attribute
180111pub fn span_contains_cfg ( cx : & LateContext < ' _ > , s : Span ) -> bool {
181112 s. check_source_text ( cx, |src| {
182113 let mut iter = tokenize_with_text ( src) ;
@@ -198,3 +129,52 @@ pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
198129 false
199130 } )
200131}
132+
133+ /// Currently used to keep track of the current value of `#[clippy::cognitive_complexity(N)]`
134+ pub struct LimitStack {
135+ default : u64 ,
136+ stack : Vec < u64 > ,
137+ }
138+
139+ impl Drop for LimitStack {
140+ fn drop ( & mut self ) {
141+ debug_assert_eq ! ( self . stack, Vec :: <u64 >:: new( ) ) ; // avoid `.is_empty()`, for a nicer error message
142+ }
143+ }
144+
145+ #[ expect( missing_docs, reason = "they're all trivial..." ) ]
146+ impl LimitStack {
147+ #[ must_use]
148+ /// Initialize the stack starting with a default value, which usually comes from configuration
149+ pub fn new ( limit : u64 ) -> Self {
150+ Self {
151+ default : limit,
152+ stack : vec ! [ ] ,
153+ }
154+ }
155+ pub fn limit ( & self ) -> u64 {
156+ self . stack . last ( ) . copied ( ) . unwrap_or ( self . default )
157+ }
158+ pub fn push_attrs ( & mut self , sess : & Session , attrs : & [ impl AttributeExt ] , name : Symbol ) {
159+ let stack = & mut self . stack ;
160+ parse_attrs ( sess, attrs, name, |val| stack. push ( val) ) ;
161+ }
162+ pub fn pop_attrs ( & mut self , sess : & Session , attrs : & [ impl AttributeExt ] , name : Symbol ) {
163+ let stack = & mut self . stack ;
164+ parse_attrs ( sess, attrs, name, |val| debug_assert_eq ! ( stack. pop( ) , Some ( val) ) ) ;
165+ }
166+ }
167+
168+ fn parse_attrs < F : FnMut ( u64 ) > ( sess : & Session , attrs : & [ impl AttributeExt ] , name : Symbol , mut f : F ) {
169+ for attr in get_builtin_attr ( sess, attrs, name) {
170+ let Some ( value) = attr. value_str ( ) else {
171+ sess. dcx ( ) . span_err ( attr. span ( ) , "bad clippy attribute" ) ;
172+ continue ;
173+ } ;
174+ let Ok ( value) = u64:: from_str ( value. as_str ( ) ) else {
175+ sess. dcx ( ) . span_err ( attr. span ( ) , "not a number" ) ;
176+ continue ;
177+ } ;
178+ f ( value) ;
179+ }
180+ }
0 commit comments