12
12
13
13
use crate :: reexport:: * ;
14
14
use crate :: utils:: {
15
- in_macro, last_line_of_span, match_def_path, opt_def_id, paths, snippet_opt, span_lint, span_lint_and_then ,
16
- without_block_comments,
15
+ in_macro, last_line_of_span, match_def_path, opt_def_id, paths, snippet_opt, span_lint,
16
+ span_lint_and_then , without_block_comments,
17
17
} ;
18
- use crate :: rustc:: hir:: * ;
19
- use crate :: rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
20
- use crate :: rustc:: { declare_tool_lint, lint_array} ;
21
18
use if_chain:: if_chain;
19
+ use crate :: rustc:: hir:: * ;
20
+ use crate :: rustc:: lint:: {
21
+ CheckLintNameResult , LateContext , LateLintPass , LintArray , LintContext , LintPass ,
22
+ } ;
22
23
use crate :: rustc:: ty:: { self , TyCtxt } ;
24
+ use crate :: rustc:: { declare_tool_lint, lint_array} ;
23
25
use semver:: Version ;
24
- use crate :: syntax:: ast:: { AttrStyle , Attribute , Lit , LitKind , MetaItemKind , NestedMetaItem , NestedMetaItemKind } ;
26
+ use crate :: syntax:: ast:: {
27
+ AttrStyle , Attribute , Lit , LitKind , MetaItemKind , NestedMetaItem , NestedMetaItemKind ,
28
+ } ;
25
29
use crate :: syntax:: source_map:: Span ;
26
30
use crate :: rustc_errors:: Applicability ;
27
31
@@ -138,6 +142,33 @@ declare_clippy_lint! {
138
142
"empty line after outer attribute"
139
143
}
140
144
145
+ /// **What it does:** Checks for `allow`/`warn`/`deny`/`forbid` attributes with scoped clippy
146
+ /// lints and if those lints exist in clippy. If there is a uppercase letter in the lint name
147
+ /// (not the tool name) and a lowercase version of this lint exists, it will suggest to lowercase
148
+ /// the lint name.
149
+ ///
150
+ /// **Why is this bad?** A lint attribute with a mistyped lint name won't have an effect.
151
+ ///
152
+ /// **Known problems:** None.
153
+ ///
154
+ /// **Example:**
155
+ /// Bad:
156
+ /// ```rust
157
+ /// #![warn(if_not_els)]
158
+ /// #![deny(clippy::All)]
159
+ /// ```
160
+ ///
161
+ /// Good:
162
+ /// ```rust
163
+ /// #![warn(if_not_else)]
164
+ /// #![deny(clippy::all)]
165
+ /// ```
166
+ declare_clippy_lint ! {
167
+ pub UNKNOWN_CLIPPY_LINTS ,
168
+ style,
169
+ "unknown_lints for scoped Clippy lints"
170
+ }
171
+
141
172
#[ derive( Copy , Clone ) ]
142
173
pub struct AttrPass ;
143
174
@@ -147,14 +178,21 @@ impl LintPass for AttrPass {
147
178
INLINE_ALWAYS ,
148
179
DEPRECATED_SEMVER ,
149
180
USELESS_ATTRIBUTE ,
150
- EMPTY_LINE_AFTER_OUTER_ATTR
181
+ EMPTY_LINE_AFTER_OUTER_ATTR ,
182
+ UNKNOWN_CLIPPY_LINTS ,
151
183
)
152
184
}
153
185
}
154
186
155
187
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for AttrPass {
156
188
fn check_attribute ( & mut self , cx : & LateContext < ' a , ' tcx > , attr : & ' tcx Attribute ) {
157
189
if let Some ( ref items) = attr. meta_item_list ( ) {
190
+ match & * attr. name ( ) . as_str ( ) {
191
+ "allow" | "warn" | "deny" | "forbid" => {
192
+ check_clippy_lint_names ( cx, items) ;
193
+ }
194
+ _ => { }
195
+ }
158
196
if items. is_empty ( ) || attr. name ( ) != "deprecated" {
159
197
return ;
160
198
}
@@ -247,6 +285,47 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
247
285
}
248
286
}
249
287
288
+ #[ allow( clippy:: single_match_else) ]
289
+ fn check_clippy_lint_names ( cx : & LateContext < ' _ , ' _ > , items : & [ NestedMetaItem ] ) {
290
+ let lint_store = cx. lints ( ) ;
291
+ for lint in items {
292
+ if_chain ! {
293
+ if let Some ( word) = lint. word( ) ;
294
+ if let Some ( tool_name) = word. is_scoped( ) ;
295
+ if tool_name. as_str( ) == "clippy" ;
296
+ let name = word. name( ) ;
297
+ if let CheckLintNameResult :: Tool ( Err ( ( None , _) ) ) = lint_store. check_lint_name(
298
+ & name. as_str( ) ,
299
+ Some ( tool_name. as_str( ) ) ,
300
+ ) ;
301
+ then {
302
+ span_lint_and_then(
303
+ cx,
304
+ UNKNOWN_CLIPPY_LINTS ,
305
+ lint. span,
306
+ & format!( "unknown clippy lint: clippy::{}" , name) ,
307
+ |db| {
308
+ if name. as_str( ) . chars( ) . any( |c| c. is_uppercase( ) ) {
309
+ let name_lower = name. as_str( ) . to_lowercase( ) . to_string( ) ;
310
+ match lint_store. check_lint_name(
311
+ & name_lower,
312
+ Some ( tool_name. as_str( ) )
313
+ ) {
314
+ CheckLintNameResult :: NoLint => ( ) ,
315
+ _ => {
316
+ db. span_suggestion( lint. span,
317
+ "lowercase the lint name" ,
318
+ name_lower) ;
319
+ }
320
+ }
321
+ }
322
+ }
323
+ ) ;
324
+ }
325
+ } ;
326
+ }
327
+ }
328
+
250
329
fn is_relevant_item ( tcx : TyCtxt < ' _ , ' _ , ' _ > , item : & Item ) -> bool {
251
330
if let ItemKind :: Fn ( _, _, _, eid) = item. node {
252
331
is_relevant_expr ( tcx, tcx. body_tables ( eid) , & tcx. hir . body ( eid) . value )
0 commit comments