1212
1313use crate :: reexport:: * ;
1414use 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,
1717} ;
18- use crate :: rustc:: hir:: * ;
19- use crate :: rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
20- use crate :: rustc:: { declare_tool_lint, lint_array} ;
2118use if_chain:: if_chain;
19+ use crate :: rustc:: hir:: * ;
20+ use crate :: rustc:: lint:: {
21+ CheckLintNameResult , LateContext , LateLintPass , LintArray , LintContext , LintPass ,
22+ } ;
2223use crate :: rustc:: ty:: { self , TyCtxt } ;
24+ use crate :: rustc:: { declare_tool_lint, lint_array} ;
2325use 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+ } ;
2529use crate :: syntax:: source_map:: Span ;
2630use crate :: rustc_errors:: Applicability ;
2731
@@ -138,6 +142,33 @@ declare_clippy_lint! {
138142 "empty line after outer attribute"
139143}
140144
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+
141172#[ derive( Copy , Clone ) ]
142173pub struct AttrPass ;
143174
@@ -147,14 +178,21 @@ impl LintPass for AttrPass {
147178 INLINE_ALWAYS ,
148179 DEPRECATED_SEMVER ,
149180 USELESS_ATTRIBUTE ,
150- EMPTY_LINE_AFTER_OUTER_ATTR
181+ EMPTY_LINE_AFTER_OUTER_ATTR ,
182+ UNKNOWN_CLIPPY_LINTS ,
151183 )
152184 }
153185}
154186
155187impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for AttrPass {
156188 fn check_attribute ( & mut self , cx : & LateContext < ' a , ' tcx > , attr : & ' tcx Attribute ) {
157189 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+ }
158196 if items. is_empty ( ) || attr. name ( ) != "deprecated" {
159197 return ;
160198 }
@@ -247,6 +285,47 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
247285 }
248286}
249287
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+
250329fn is_relevant_item ( tcx : TyCtxt < ' _ , ' _ , ' _ > , item : & Item ) -> bool {
251330 if let ItemKind :: Fn ( _, _, _, eid) = item. node {
252331 is_relevant_expr ( tcx, tcx. body_tables ( eid) , & tcx. hir . body ( eid) . value )
0 commit comments