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?** An lint attribute with a misstyped 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,46 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
247
285
}
248
286
}
249
287
288
+ fn check_clippy_lint_names ( cx : & LateContext < ' _ , ' _ > , items : & Vec < NestedMetaItem > ) {
289
+ let lint_store = cx. lints ( ) ;
290
+ for lint in items {
291
+ if_chain ! {
292
+ if let Some ( word) = lint. word( ) ;
293
+ if let Some ( tool_name) = word. is_scoped( ) ;
294
+ if tool_name. as_str( ) == "clippy" ;
295
+ let name = word. name( ) ;
296
+ if let CheckLintNameResult :: Tool ( Err ( ( None , _) ) ) = lint_store. check_lint_name(
297
+ & name. as_str( ) ,
298
+ Some ( tool_name. as_str( ) ) ,
299
+ ) ;
300
+ then {
301
+ span_lint_and_then(
302
+ cx,
303
+ UNKNOWN_CLIPPY_LINTS ,
304
+ lint. span,
305
+ & format!( "unknwon clippy lint: clippy::{}" , name) ,
306
+ |db| {
307
+ if name. as_str( ) . chars( ) . any( |c| c. is_uppercase( ) ) {
308
+ let name_lower = name. as_str( ) . to_lowercase( ) . to_string( ) ;
309
+ match lint_store. check_lint_name(
310
+ & name_lower,
311
+ Some ( tool_name. as_str( ) )
312
+ ) {
313
+ CheckLintNameResult :: NoLint => { }
314
+ _ => {
315
+ db. span_suggestion( lint. span,
316
+ "lowercase the lint name" ,
317
+ name_lower) ;
318
+ }
319
+ }
320
+ }
321
+ }
322
+ ) ;
323
+ }
324
+ } ;
325
+ }
326
+ }
327
+
250
328
fn is_relevant_item ( tcx : TyCtxt < ' _ , ' _ , ' _ > , item : & Item ) -> bool {
251
329
if let ItemKind :: Fn ( _, _, _, eid) = item. node {
252
330
is_relevant_expr ( tcx, tcx. body_tables ( eid) , & tcx. hir . body ( eid) . value )
0 commit comments