1212//! code was written, and check if the span contains that text. Note this will only work correctly
1313//! if the span is not from a `macro_rules` based macro.
1414
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+ } ;
1620use rustc_hir:: {
1721 intravisit:: FnKind , Block , BlockCheckMode , Body , Closure , Destination , Expr , ExprKind , FieldDef , FnHeader , HirId ,
1822 Impl , ImplItem , ImplItemKind , IsAuto , Item , ItemKind , LoopSource , MatchSource , Node , QPath , TraitItem ,
@@ -25,12 +29,16 @@ use rustc_span::{Span, Symbol};
2529use rustc_target:: spec:: abi:: Abi ;
2630
2731/// The search pattern to look for. Used by `span_matches_pat`
28- #[ derive( Clone , Copy ) ]
32+ #[ derive( Clone ) ]
2933pub enum Pat {
3034 /// A single string.
3135 Str ( & ' static str ) ,
36+ /// A single string.
37+ OwnedStr ( String ) ,
3238 /// Any of the given strings.
3339 MultiStr ( & ' static [ & ' static str ] ) ,
40+ /// Any of the given strings.
41+ OwnedMultiStr ( Vec < String > ) ,
3442 /// The string representation of the symbol.
3543 Sym ( Symbol ) ,
3644 /// 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) ->
5159 let end_str = s. trim_end_matches ( |c : char | c. is_whitespace ( ) || c == ')' || c == ',' ) ;
5260 ( match start_pat {
5361 Pat :: Str ( text) => start_str. starts_with ( text) ,
62+ Pat :: OwnedStr ( text) => start_str. starts_with ( & text) ,
5463 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) ) ,
5565 Pat :: Sym ( sym) => start_str. starts_with ( sym. as_str ( ) ) ,
5666 Pat :: Num => start_str. as_bytes ( ) . first ( ) . map_or ( false , u8:: is_ascii_digit) ,
5767 } && match end_pat {
5868 Pat :: Str ( text) => end_str. ends_with ( text) ,
69+ Pat :: OwnedStr ( text) => end_str. starts_with ( & text) ,
5970 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) ) ,
6072 Pat :: Sym ( sym) => end_str. ends_with ( sym. as_str ( ) ) ,
6173 Pat :: Num => end_str. as_bytes ( ) . last ( ) . map_or ( false , u8:: is_ascii_hexdigit) ,
6274 } )
@@ -271,6 +283,42 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI
271283 ( start_pat, end_pat)
272284}
273285
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+
274322pub trait WithSearchPat {
275323 type Context : LintContext ;
276324 fn search_pat ( & self , cx : & Self :: Context ) -> ( Pat , Pat ) ;
@@ -310,6 +358,19 @@ impl<'cx> WithSearchPat for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
310358 }
311359}
312360
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+
313374/// Checks if the item likely came from a proc-macro.
314375///
315376/// This should be called after `in_external_macro` and the initial pattern matching of the ast as
0 commit comments