diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 53143800f..eaf8f9b57 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -429,6 +429,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { reg.register_early_lint_pass(box utils::internal_lints::ClippyLintsInternal); reg.register_late_lint_pass(box utils::internal_lints::CompilerLintFunctions::new()); reg.register_late_lint_pass(box utils::internal_lints::LintWithoutLintPass::default()); + reg.register_late_lint_pass(box utils::internal_lints::OuterExpnInfoPass); reg.register_late_lint_pass(box utils::inspector::DeepCodeInspector); reg.register_late_lint_pass(box utils::author::Author); reg.register_late_lint_pass(box types::Types); @@ -659,6 +660,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { utils::internal_lints::CLIPPY_LINTS_INTERNAL, utils::internal_lints::COMPILER_LINT_FUNCTIONS, utils::internal_lints::LINT_WITHOUT_LINT_PASS, + utils::internal_lints::OUTER_EXPN_INFO, ]); 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 6b91ac8cc..44cdba8ce 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,4 +1,6 @@ -use crate::utils::{match_def_path, match_type, paths, span_help_and_lint, span_lint, walk_ptrs_ty}; +use crate::utils::{ + match_def_path, match_type, method_calls, paths, span_help_and_lint, span_lint, span_lint_and_sugg, walk_ptrs_ty, +}; use if_chain::if_chain; use rustc::hir; use rustc::hir::def::{DefKind, Res}; @@ -7,6 +9,7 @@ use rustc::hir::*; use rustc::lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintArray, LintPass}; use rustc::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::Applicability; use syntax::ast::{Crate as AstCrate, ItemKind, Name}; use syntax::source_map::Span; use syntax_pos::symbol::LocalInternedString; @@ -72,6 +75,29 @@ declare_clippy_lint! { "usage of the lint functions of the compiler instead of the utils::* variant" } +declare_clippy_lint! { + /// **What it does:** Checks for calls to `cx.outer().expn_info()` and suggests to use + /// the `cx.outer_expn_info()` + /// + /// **Why is this bad?** `cx.outer_expn_info()` is faster and more concise. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// Bad: + /// ```rust + /// expr.span.ctxt().outer().expn_info() + /// ``` + /// + /// Good: + /// ```rust + /// expr.span.ctxt().outer_expn_info() + /// ``` + pub OUTER_EXPN_INFO, + internal, + "using `cx.outer().expn_info()` instead of `cx.outer_expn_info()`" +} + declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]); impl EarlyLintPass for ClippyLintsInternal { @@ -251,3 +277,34 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CompilerLintFunctions { } } } + +pub struct OuterExpnInfoPass; + +impl_lint_pass!(OuterExpnInfoPass => [OUTER_EXPN_INFO]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnInfoPass { + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) { + let (method_names, arg_lists) = method_calls(expr, 2); + let method_names: Vec = method_names.iter().map(|s| s.as_str()).collect(); + let method_names: Vec<&str> = method_names.iter().map(std::convert::AsRef::as_ref).collect(); + if_chain! { + if let ["expn_info", "outer"] = method_names.as_slice(); + let args = arg_lists[1]; + if args.len() == 1; + let self_arg = &args[0]; + let self_ty = walk_ptrs_ty(cx.tables.expr_ty(self_arg)); + if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT); + then { + span_lint_and_sugg( + cx, + OUTER_EXPN_INFO, + expr.span.trim_start(self_arg.span).unwrap_or(expr.span), + "usage of `outer().expn_info()`", + "try", + ".outer_expn_info()".to_string(), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index e0f4d4080..1960618eb 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -101,6 +101,7 @@ pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"]; pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"]; pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"]; pub const STRING: [&str; 3] = ["alloc", "string", "String"]; +pub const SYNTAX_CONTEXT: [&str; 3] = ["syntax_pos", "hygiene", "SyntaxContext"]; pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"]; diff --git a/tests/ui/outer_expn_info.rs b/tests/ui/outer_expn_info.rs new file mode 100644 index 000000000..1bc6f6888 --- /dev/null +++ b/tests/ui/outer_expn_info.rs @@ -0,0 +1,23 @@ +#![deny(clippy::internal)] +#![feature(rustc_private)] + +#[macro_use] +extern crate rustc; +use rustc::hir::Expr; +use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; + +declare_lint! { + pub TEST_LINT, + Warn, + "" +} + +declare_lint_pass!(Pass => [TEST_LINT]); + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { + fn check_expr(&mut self, _cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { + let _ = expr.span.ctxt().outer().expn_info(); + } +} + +fn main() {} diff --git a/tests/ui/outer_expn_info.stderr b/tests/ui/outer_expn_info.stderr new file mode 100644 index 000000000..2bd3a9835 --- /dev/null +++ b/tests/ui/outer_expn_info.stderr @@ -0,0 +1,15 @@ +error: usage of `outer().expn_info()` + --> $DIR/outer_expn_info.rs:19:33 + | +LL | let _ = expr.span.ctxt().outer().expn_info(); + | ^^^^^^^^^^^^^^^^^^^^ help: try: `.outer_expn_info()` + | +note: lint level defined here + --> $DIR/outer_expn_info.rs:1:9 + | +LL | #![deny(clippy::internal)] + | ^^^^^^^^^^^^^^^^ + = note: #[deny(clippy::outer_expn_info)] implied by #[deny(clippy::internal)] + +error: aborting due to previous error +