diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d9d470925fd..0d5d10c7ae5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -866,6 +866,7 @@ All notable changes to this project will be documented in this file. [`unimplemented`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unimplemented [`unit_arg`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unit_arg [`unit_cmp`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unit_cmp +[`unknown_clippy_lints`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unknown_clippy_lints [`unnecessary_cast`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unnecessary_cast [`unnecessary_filter_map`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_fold`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#unnecessary_fold diff --git a/README.md b/README.md index b4091cdab6c6..3c056171f02e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ We are currently in the process of discussing Clippy 1.0 via the RFC process in A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 283 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html) +[There are 284 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index f463ce5fa35d..89d676f3f4ce 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -12,16 +12,20 @@ use crate::reexport::*; use crate::utils::{ - in_macro, last_line_of_span, match_def_path, opt_def_id, paths, snippet_opt, span_lint, span_lint_and_then, - without_block_comments, + in_macro, last_line_of_span, match_def_path, opt_def_id, paths, snippet_opt, span_lint, + span_lint_and_then, without_block_comments, }; -use crate::rustc::hir::*; -use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; -use crate::rustc::{declare_tool_lint, lint_array}; use if_chain::if_chain; +use crate::rustc::hir::*; +use crate::rustc::lint::{ + CheckLintNameResult, LateContext, LateLintPass, LintArray, LintContext, LintPass, +}; use crate::rustc::ty::{self, TyCtxt}; +use crate::rustc::{declare_tool_lint, lint_array}; use semver::Version; -use crate::syntax::ast::{AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem, NestedMetaItemKind}; +use crate::syntax::ast::{ + AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem, NestedMetaItemKind, +}; use crate::syntax::source_map::Span; use crate::rustc_errors::Applicability; @@ -138,6 +142,33 @@ declare_clippy_lint! { "empty line after outer attribute" } +/// **What it does:** Checks for `allow`/`warn`/`deny`/`forbid` attributes with scoped clippy +/// lints and if those lints exist in clippy. If there is a uppercase letter in the lint name +/// (not the tool name) and a lowercase version of this lint exists, it will suggest to lowercase +/// the lint name. +/// +/// **Why is this bad?** A lint attribute with a mistyped lint name won't have an effect. +/// +/// **Known problems:** None. +/// +/// **Example:** +/// Bad: +/// ```rust +/// #![warn(if_not_els)] +/// #![deny(clippy::All)] +/// ``` +/// +/// Good: +/// ```rust +/// #![warn(if_not_else)] +/// #![deny(clippy::all)] +/// ``` +declare_clippy_lint! { + pub UNKNOWN_CLIPPY_LINTS, + style, + "unknown_lints for scoped Clippy lints" +} + #[derive(Copy, Clone)] pub struct AttrPass; @@ -147,7 +178,8 @@ impl LintPass for AttrPass { INLINE_ALWAYS, DEPRECATED_SEMVER, USELESS_ATTRIBUTE, - EMPTY_LINE_AFTER_OUTER_ATTR + EMPTY_LINE_AFTER_OUTER_ATTR, + UNKNOWN_CLIPPY_LINTS, ) } } @@ -155,6 +187,12 @@ impl LintPass for AttrPass { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass { fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) { if let Some(ref items) = attr.meta_item_list() { + match &*attr.name().as_str() { + "allow" | "warn" | "deny" | "forbid" => { + check_clippy_lint_names(cx, items); + } + _ => {} + } if items.is_empty() || attr.name() != "deprecated" { return; } @@ -247,6 +285,47 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass { } } +#[allow(clippy::single_match_else)] +fn check_clippy_lint_names(cx: &LateContext<'_, '_>, items: &[NestedMetaItem]) { + let lint_store = cx.lints(); + for lint in items { + if_chain! { + if let Some(word) = lint.word(); + if let Some(tool_name) = word.is_scoped(); + if tool_name.as_str() == "clippy"; + let name = word.name(); + if let CheckLintNameResult::Tool(Err((None, _))) = lint_store.check_lint_name( + &name.as_str(), + Some(tool_name.as_str()), + ); + then { + span_lint_and_then( + cx, + UNKNOWN_CLIPPY_LINTS, + lint.span, + &format!("unknown clippy lint: clippy::{}", name), + |db| { + if name.as_str().chars().any(|c| c.is_uppercase()) { + let name_lower = name.as_str().to_lowercase().to_string(); + match lint_store.check_lint_name( + &name_lower, + Some(tool_name.as_str()) + ) { + CheckLintNameResult::NoLint => (), + _ => { + db.span_suggestion(lint.span, + "lowercase the lint name", + name_lower); + } + } + } + } + ); + } + }; + } +} + fn is_relevant_item(tcx: TyCtxt<'_, '_, '_>, item: &Item) -> bool { if let ItemKind::Fn(_, _, _, eid) = item.node { is_relevant_expr(tcx, tcx.body_tables(eid), &tcx.hir.body(eid).value) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 86ecba38d590..56cd2ca3e926 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -533,6 +533,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { assign_ops::ASSIGN_OP_PATTERN, assign_ops::MISREFACTORED_ASSIGN_OP, attrs::DEPRECATED_SEMVER, + attrs::UNKNOWN_CLIPPY_LINTS, attrs::USELESS_ATTRIBUTE, bit_mask::BAD_BIT_MASK, bit_mask::INEFFECTIVE_BIT_MASK, @@ -749,6 +750,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { reg.register_lint_group("clippy::style", Some("clippy_style"), vec![ assign_ops::ASSIGN_OP_PATTERN, + attrs::UNKNOWN_CLIPPY_LINTS, bit_mask::VERBOSE_BIT_MASK, blacklisted_name::BLACKLISTED_NAME, block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR, diff --git a/tests/ui/unknown_clippy_lints.rs b/tests/ui/unknown_clippy_lints.rs new file mode 100644 index 000000000000..d0b4ae9f5329 --- /dev/null +++ b/tests/ui/unknown_clippy_lints.rs @@ -0,0 +1,16 @@ +// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(clippy::All)] +#![warn(clippy::pedantic)] + +#[warn(clippy::if_not_els)] +fn main() { + +} diff --git a/tests/ui/unknown_clippy_lints.stderr b/tests/ui/unknown_clippy_lints.stderr new file mode 100644 index 000000000000..83ee0e9dd31a --- /dev/null +++ b/tests/ui/unknown_clippy_lints.stderr @@ -0,0 +1,16 @@ +error: unknown clippy lint: clippy::if_not_els + --> $DIR/unknown_clippy_lints.rs:13:8 + | +13 | #[warn(clippy::if_not_els)] + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unknown-clippy-lints` implied by `-D warnings` + +error: unknown clippy lint: clippy::All + --> $DIR/unknown_clippy_lints.rs:10:10 + | +10 | #![allow(clippy::All)] + | ^^^^^^^^^^^ help: lowercase the lint name: `all` + +error: aborting due to 2 previous errors +