12
12
//! code was written, and check if the span contains that text. Note this will only work correctly
13
13
//! if the span is not from a `macro_rules` based macro.
14
14
15
- use rustc_ast:: ast:: { IntTy , LitIntType , LitKind , StrStyle , UintTy } ;
15
+ use rustc_ast:: {
16
+ ast:: { AttrKind , Attribute , IntTy , LitIntType , LitKind , StrStyle , UintTy } ,
17
+ token:: CommentKind ,
18
+ AttrStyle ,
19
+ } ;
16
20
use rustc_hir:: {
17
21
intravisit:: FnKind , Block , BlockCheckMode , Body , Closure , Destination , Expr , ExprKind , FieldDef , FnHeader , HirId ,
18
22
Impl , ImplItem , ImplItemKind , IsAuto , Item , ItemKind , LoopSource , MatchSource , Node , QPath , TraitItem ,
@@ -25,12 +29,16 @@ use rustc_span::{Span, Symbol};
25
29
use rustc_target:: spec:: abi:: Abi ;
26
30
27
31
/// The search pattern to look for. Used by `span_matches_pat`
28
- #[ derive( Clone , Copy ) ]
32
+ #[ derive( Clone ) ]
29
33
pub enum Pat {
30
34
/// A single string.
31
35
Str ( & ' static str ) ,
36
+ /// A single string.
37
+ OwnedStr ( String ) ,
32
38
/// Any of the given strings.
33
39
MultiStr ( & ' static [ & ' static str ] ) ,
40
+ /// Any of the given strings.
41
+ OwnedMultiStr ( Vec < String > ) ,
34
42
/// The string representation of the symbol.
35
43
Sym ( Symbol ) ,
36
44
/// Any decimal or hexadecimal digit depending on the location.
@@ -51,12 +59,16 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
51
59
let end_str = s. trim_end_matches ( |c : char | c. is_whitespace ( ) || c == ')' || c == ',' ) ;
52
60
( match start_pat {
53
61
Pat :: Str ( text) => start_str. starts_with ( text) ,
62
+ Pat :: OwnedStr ( text) => start_str. starts_with ( & text) ,
54
63
Pat :: MultiStr ( texts) => texts. iter ( ) . any ( |s| start_str. starts_with ( s) ) ,
64
+ Pat :: OwnedMultiStr ( texts) => texts. iter ( ) . any ( |s| start_str. starts_with ( s) ) ,
55
65
Pat :: Sym ( sym) => start_str. starts_with ( sym. as_str ( ) ) ,
56
66
Pat :: Num => start_str. as_bytes ( ) . first ( ) . map_or ( false , u8:: is_ascii_digit) ,
57
67
} && match end_pat {
58
68
Pat :: Str ( text) => end_str. ends_with ( text) ,
69
+ Pat :: OwnedStr ( text) => end_str. starts_with ( & text) ,
59
70
Pat :: MultiStr ( texts) => texts. iter ( ) . any ( |s| start_str. ends_with ( s) ) ,
71
+ Pat :: OwnedMultiStr ( texts) => texts. iter ( ) . any ( |s| start_str. starts_with ( s) ) ,
60
72
Pat :: Sym ( sym) => end_str. ends_with ( sym. as_str ( ) ) ,
61
73
Pat :: Num => end_str. as_bytes ( ) . last ( ) . map_or ( false , u8:: is_ascii_hexdigit) ,
62
74
} )
@@ -271,6 +283,42 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI
271
283
( start_pat, end_pat)
272
284
}
273
285
286
+ fn attr_search_pat ( attr : & Attribute ) -> ( Pat , Pat ) {
287
+ match attr. kind {
288
+ AttrKind :: Normal ( ..) => {
289
+ let mut pat = if matches ! ( attr. style, AttrStyle :: Outer ) {
290
+ ( Pat :: Str ( "#[" ) , Pat :: Str ( "]" ) )
291
+ } else {
292
+ ( Pat :: Str ( "#![" ) , Pat :: Str ( "]" ) )
293
+ } ;
294
+
295
+ if let Some ( ident) = attr. ident ( ) && let Pat :: Str ( old_pat) = pat. 0 {
296
+ // TODO: I feel like it's likely we can use `Cow` instead but this will require quite a bit of
297
+ // refactoring
298
+ // NOTE: This will likely have false positives, like `allow = 1`
299
+ pat. 0 = Pat :: OwnedMultiStr ( vec ! [ ident. to_string( ) , old_pat. to_owned( ) ] ) ;
300
+ pat. 1 = Pat :: Str ( "" ) ;
301
+ }
302
+
303
+ pat
304
+ } ,
305
+ AttrKind :: DocComment ( _kind @ CommentKind :: Line , ..) => {
306
+ if matches ! ( attr. style, AttrStyle :: Outer ) {
307
+ ( Pat :: Str ( "///" ) , Pat :: Str ( "" ) )
308
+ } else {
309
+ ( Pat :: Str ( "//!" ) , Pat :: Str ( "" ) )
310
+ }
311
+ } ,
312
+ AttrKind :: DocComment ( _kind @ CommentKind :: Block , ..) => {
313
+ if matches ! ( attr. style, AttrStyle :: Outer ) {
314
+ ( Pat :: Str ( "/**" ) , Pat :: Str ( "*/" ) )
315
+ } else {
316
+ ( Pat :: Str ( "/*!" ) , Pat :: Str ( "*/" ) )
317
+ }
318
+ } ,
319
+ }
320
+ }
321
+
274
322
pub trait WithSearchPat {
275
323
type Context : LintContext ;
276
324
fn search_pat ( & self , cx : & Self :: Context ) -> ( Pat , Pat ) ;
@@ -310,6 +358,19 @@ impl<'cx> WithSearchPat for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
310
358
}
311
359
}
312
360
361
+ // `Attribute` does not have the `hir` associated lifetime, so we cannot use the macro
362
+ impl < ' cx > WithSearchPat for & ' cx Attribute {
363
+ type Context = LateContext < ' cx > ;
364
+
365
+ fn search_pat ( & self , _cx : & Self :: Context ) -> ( Pat , Pat ) {
366
+ attr_search_pat ( self )
367
+ }
368
+
369
+ fn span ( & self ) -> Span {
370
+ self . span
371
+ }
372
+ }
373
+
313
374
/// Checks if the item likely came from a proc-macro.
314
375
///
315
376
/// This should be called after `in_external_macro` and the initial pattern matching of the ast as
0 commit comments