diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 90aaea90d..702931c05 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -1,6 +1,7 @@ use crate::syntax::ast::*; -use crate::rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass}; +use crate::rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass}; use crate::rustc::{declare_tool_lint, lint_array}; +use crate::utils::span_lint; /// **What it does:** Checks for unnecessary double parentheses. /// @@ -35,20 +36,20 @@ impl EarlyLintPass for DoubleParens { match expr.node { ExprKind::Paren(ref in_paren) => match in_paren.node { ExprKind::Paren(_) | ExprKind::Tup(_) => { - cx.span_lint(DOUBLE_PARENS, expr.span, "Consider removing unnecessary double parentheses"); + span_lint(cx, DOUBLE_PARENS, expr.span, "Consider removing unnecessary double parentheses"); }, _ => {}, }, ExprKind::Call(_, ref params) => if params.len() == 1 { let param = ¶ms[0]; if let ExprKind::Paren(_) = param.node { - cx.span_lint(DOUBLE_PARENS, param.span, "Consider removing unnecessary double parentheses"); + span_lint(cx, DOUBLE_PARENS, param.span, "Consider removing unnecessary double parentheses"); } }, ExprKind::MethodCall(_, ref params) => if params.len() == 2 { let param = ¶ms[1]; if let ExprKind::Paren(_) = param.node { - cx.span_lint(DOUBLE_PARENS, param.span, "Consider removing unnecessary double parentheses"); + span_lint(cx, DOUBLE_PARENS, param.span, "Consider removing unnecessary double parentheses"); } }, _ => {}, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3937e5cb9..ec00a13c0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -293,8 +293,9 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { reg.register_late_lint_pass(box serde_api::Serde); reg.register_early_lint_pass(box utils::internal_lints::Clippy); - reg.register_late_lint_pass(box utils::internal_lints::LintWithoutLintPass::default()); + reg.register_late_lint_pass(box utils::internal_lints::CompilerLintFunctions::new()); reg.register_early_lint_pass(box utils::internal_lints::DefaultHashTypes::default()); + reg.register_late_lint_pass(box utils::internal_lints::LintWithoutLintPass::default()); reg.register_late_lint_pass(box utils::inspector::Pass); reg.register_late_lint_pass(box utils::author::Pass); reg.register_late_lint_pass(box types::TypePass); @@ -494,8 +495,9 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { reg.register_lint_group("clippy::internal", Some("clippy_internal"), vec![ utils::internal_lints::CLIPPY_LINTS_INTERNAL, - utils::internal_lints::LINT_WITHOUT_LINT_PASS, + utils::internal_lints::COMPILER_LINT_FUNCTIONS, utils::internal_lints::DEFAULT_HASH_TYPES, + utils::internal_lints::LINT_WITHOUT_LINT_PASS, ]); reg.register_lint_group("clippy::all", Some("clippy"), vec![ diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 97a6922d3..058f7ee2f 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,14 +1,16 @@ -use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass, EarlyContext, EarlyLintPass}; -use crate::rustc::{declare_tool_lint, lint_array}; -use crate::rustc::hir::*; +use crate::utils::{ + match_qpath, match_type, paths, span_help_and_lint, span_lint, span_lint_and_sugg, walk_ptrs_ty, +}; +use if_chain::if_chain; use crate::rustc::hir; use crate::rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; +use crate::rustc::hir::*; +use crate::rustc::lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintArray, LintPass}; +use crate::rustc::{declare_tool_lint, lint_array}; use crate::rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use crate::utils::{match_qpath, paths, span_lint, span_lint_and_sugg}; -use crate::syntax::symbol::LocalInternedString; use crate::syntax::ast::{Crate as AstCrate, Ident, ItemKind, Name}; use crate::syntax::source_map::Span; - +use crate::syntax::symbol::LocalInternedString; /// **What it does:** Checks for various things we like to keep tidy in clippy. /// @@ -23,7 +25,6 @@ declare_clippy_lint! { "various things that will negatively affect your clippy experience" } - /// **What it does:** Ensures every lint is associated to a `LintPass`. /// /// **Why is this bad?** The compiler only knows lints via a `LintPass`. Without @@ -53,7 +54,6 @@ declare_clippy_lint! { "declaring a lint without associating it in a LintPass" } - /// **What it does:** Checks for the presence of the default hash types "HashMap" or "HashSet" /// and recommends the FxHash* variants. /// @@ -65,6 +65,29 @@ declare_clippy_lint! { "forbid HashMap and HashSet and suggest the FxHash* variants" } +/// **What it does:** Checks for calls to `cx.span_lint*` and suggests to use the `utils::*` +/// variant of the function. +/// +/// **Why is this bad?** The `utils::*` variants also add a link to the Clippy documentation to the +/// warning/error messages. +/// +/// **Known problems:** None. +/// +/// **Example:** +/// Bad: +/// ```rust +/// cx.span_lint(LINT_NAME, "message"); +/// ``` +/// +/// Good: +/// ```rust +/// utils::span_lint(cx, LINT_NAME, "message"); +/// ``` +declare_clippy_lint! { + pub COMPILER_LINT_FUNCTIONS, + internal, + "usage of the lint functions of the compiler instead of the utils::* variant" +} #[derive(Copy, Clone)] pub struct Clippy; @@ -119,7 +142,6 @@ pub struct LintWithoutLintPass { registered_lints: FxHashSet, } - impl LintPass for LintWithoutLintPass { fn get_lints(&self) -> LintArray { lint_array!(LINT_WITHOUT_LINT_PASS) @@ -171,7 +193,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass { } } - fn is_lint_ref_type(ty: &Ty) -> bool { if let TyKind::Rptr( _, @@ -188,7 +209,6 @@ fn is_lint_ref_type(ty: &Ty) -> bool { false } - fn is_lint_array_type(ty: &Ty) -> bool { if let TyKind::Path(ref path) = ty.node { match_qpath(path, &paths::LINT_ARRAY) @@ -224,8 +244,8 @@ pub struct DefaultHashTypes { impl DefaultHashTypes { pub fn default() -> Self { let mut map = FxHashMap::default(); - map.insert("HashMap".to_owned(), "FxHashMap".to_owned()); - map.insert("HashSet".to_owned(), "FxHashSet".to_owned()); + map.insert("HashMap".to_string(), "FxHashMap".to_string()); + map.insert("HashSet".to_string(), "FxHashSet".to_string()); Self { map } } } @@ -240,8 +260,62 @@ impl EarlyLintPass for DefaultHashTypes { fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { let ident_string = ident.to_string(); if let Some(replace) = self.map.get(&ident_string) { - let msg = format!("Prefer {} over {}, it has better performance and we don't need any collision prevention in clippy", replace, ident_string); - span_lint_and_sugg(cx, DEFAULT_HASH_TYPES, ident.span, &msg, "use", replace.to_owned()); + let msg = format!("Prefer {} over {}, it has better performance \ + and we don't need any collision prevention in clippy", + replace, ident_string); + span_lint_and_sugg( + cx, + DEFAULT_HASH_TYPES, + ident.span, + &msg, + "use", + replace.to_string(), + ); + } + } +} + +#[derive(Clone, Default)] +pub struct CompilerLintFunctions { + map: FxHashMap, +} + +impl CompilerLintFunctions { + pub fn new() -> Self { + let mut map = FxHashMap::default(); + map.insert("span_lint".to_string(), "utils::span_lint".to_string()); + map.insert("struct_span_lint".to_string(), "utils::span_lint".to_string()); + map.insert("lint".to_string(), "utils::span_lint".to_string()); + map.insert("span_lint_note".to_string(), "utils::span_note_and_lint".to_string()); + map.insert("span_lint_help".to_string(), "utils::span_help_and_lint".to_string()); + Self { map } + } +} + +impl LintPass for CompilerLintFunctions { + fn get_lints(&self) -> LintArray { + lint_array!(COMPILER_LINT_FUNCTIONS) + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CompilerLintFunctions { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { + if_chain! { + if let ExprKind::MethodCall(ref path, _, ref args) = expr.node; + let fn_name = path.ident.as_str().to_string(); + if let Some(sugg) = self.map.get(&fn_name); + let ty = walk_ptrs_ty(cx.tables.expr_ty(&args[0])); + if match_type(cx, ty, &paths::EARLY_CONTEXT) + || match_type(cx, ty, &paths::LATE_CONTEXT); + then { + span_help_and_lint( + cx, + COMPILER_LINT_FUNCTIONS, + path.ident.span, + "usage of a compiler lint function", + &format!("Please use the Clippy variant of this function: `{}`", sugg), + ); + } } } } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index ff67d381a..30475da70 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -26,6 +26,7 @@ pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"]; pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; pub const DROP: [&str; 3] = ["core", "mem", "drop"]; pub const DURATION: [&str; 3] = ["core", "time", "Duration"]; +pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"]; pub const FMT_ARGUMENTS_NEWV1FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; @@ -41,6 +42,7 @@ pub const INTO_ITERATOR: [&str; 4] = ["core", "iter", "traits", "IntoIterator"]; pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const ITERATOR: [&str; 4] = ["core", "iter", "iterator", "Iterator"]; +pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"]; pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"]; pub const LINT: [&str; 3] = ["rustc", "lint", "Lint"]; pub const LINT_ARRAY: [&str; 3] = ["rustc", "lint", "LintArray"];