diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 576a4a8cd7c..c1fe51fbf8a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1090,6 +1090,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS), LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA), LintId::of(&utils::internal_lints::PRODUCE_ICE), + LintId::of(&utils::internal_lints::DEFAULT_LINT), ]); store.register_group(true, "clippy::all", Some("clippy"), vec![ diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 825e796382b..dcedaedc27f 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -13,10 +13,10 @@ use rustc_hir::*; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::declare_tool_lint; use rustc_session::{declare_lint_pass, impl_lint_pass}; -use rustc_span::source_map::Span; +use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::SymbolStr; use syntax::ast; -use syntax::ast::{Crate as AstCrate, ItemKind, Name}; +use syntax::ast::{Crate as AstCrate, ItemKind, LitKind, Name}; use syntax::visit::FnKind; declare_clippy_lint! { @@ -121,6 +121,29 @@ declare_clippy_lint! { "this message should not appear anywhere as we ICE before and don't emit the lint" } +declare_clippy_lint! { + /// **What it does:** Checks for cases of an auto-generated lint without an updated description, + /// i.e. `default lint description`. + /// + /// **Why is this bad?** Indicates that the lint is not finished. + /// + /// **Known problems:** None + /// + /// **Example:** + /// Bad: + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } + /// ``` + /// + /// Good: + /// ```rust,ignore + /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } + /// ``` + pub DEFAULT_LINT, + internal, + "found 'default lint description' in a lint declaration" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -163,12 +186,34 @@ pub struct LintWithoutLintPass { registered_lints: FxHashSet, } -impl_lint_pass!(LintWithoutLintPass => [LINT_WITHOUT_LINT_PASS]); +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]); impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass { fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item<'_>) { - if let hir::ItemKind::Static(ref ty, Mutability::Not, _) = item.kind { + if let hir::ItemKind::Static(ref ty, Mutability::Not, body_id) = item.kind { if is_lint_ref_type(cx, ty) { + let expr = &cx.tcx.hir().body(body_id).value; + if_chain! { + if let ExprKind::AddrOf(_, _, ref inner_exp) = expr.kind; + if let ExprKind::Struct(_, ref fields, _) = inner_exp.kind; + let field = fields.iter() + .find(|f| f.ident.as_str() == "desc") + .expect("lints must have a description field"); + if let ExprKind::Lit(Spanned { + node: LitKind::Str(ref sym, _), + .. + }) = field.expr.kind; + if sym.as_str() == "default lint description"; + + then { + span_lint( + cx, + DEFAULT_LINT, + item.span, + &format!("the lint `{}` has the default lint description", item.ident.name), + ); + } + } self.declared_lints.insert(item.ident.name, item.span); } } else if is_expn_of(item.span, "impl_lint_pass").is_some() diff --git a/tests/ui/default_lint.rs b/tests/ui/default_lint.rs new file mode 100644 index 00000000000..988a7b866de --- /dev/null +++ b/tests/ui/default_lint.rs @@ -0,0 +1,28 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +#[macro_use] +extern crate rustc; +#[macro_use] +extern crate rustc_session; +extern crate rustc_lint; +use rustc_lint::{LintArray, LintPass}; + +declare_tool_lint! { + pub clippy::TEST_LINT, + Warn, + "", + report_in_external_macro: true +} + +declare_tool_lint! { + pub clippy::TEST_LINT_DEFAULT, + Warn, + "default lint description", + report_in_external_macro: true +} + +declare_lint_pass!(Pass => [TEST_LINT]); +declare_lint_pass!(Pass2 => [TEST_LINT_DEFAULT]); + +fn main() {} diff --git a/tests/ui/default_lint.stderr b/tests/ui/default_lint.stderr new file mode 100644 index 00000000000..21fd86ec6aa --- /dev/null +++ b/tests/ui/default_lint.stderr @@ -0,0 +1,21 @@ +error: the lint `TEST_LINT_DEFAULT` has the default lint description + --> $DIR/default_lint.rs:18:1 + | +LL | / declare_tool_lint! { +LL | | pub clippy::TEST_LINT_DEFAULT, +LL | | Warn, +LL | | "default lint description", +LL | | report_in_external_macro: true +LL | | } + | |_^ + | +note: lint level defined here + --> $DIR/default_lint.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: `#[deny(clippy::default_lint)]` implied by `#[deny(clippy::internal)]` + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error: aborting due to previous error +