| use crate::internal_paths; |
| use clippy_utils::diagnostics::span_lint_and_sugg; |
| use clippy_utils::{is_lint_allowed, method_calls, sym}; |
| use rustc_errors::Applicability; |
| use rustc_hir as hir; |
| use rustc_lint::{LateContext, LateLintPass}; |
| use rustc_lint_defs::declare_tool_lint; |
| use rustc_session::declare_lint_pass; |
| |
| declare_tool_lint! { |
| /// ### What it does |
| /// Checks for calls to `cx.outer().expn_data()` and suggests to use |
| /// the `cx.outer_expn_data()` |
| /// |
| /// ### Why is this bad? |
| /// `cx.outer_expn_data()` is faster and more concise. |
| /// |
| /// ### Example |
| /// ```rust,ignore |
| /// expr.span.ctxt().outer().expn_data() |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```rust,ignore |
| /// expr.span.ctxt().outer_expn_data() |
| /// ``` |
| pub clippy::OUTER_EXPN_EXPN_DATA, |
| Warn, |
| "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`", |
| report_in_external_macro: true |
| } |
| |
| declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]); |
| |
| impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass { |
| fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { |
| if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) { |
| return; |
| } |
| |
| let (method_names, arg_lists, spans) = method_calls(expr, 2); |
| if let [sym::expn_data, sym::outer_expn] = method_names.as_slice() |
| && let (self_arg, args) = arg_lists[1] |
| && args.is_empty() |
| && let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs() |
| && internal_paths::SYNTAX_CONTEXT.matches_ty(cx, self_ty) |
| { |
| span_lint_and_sugg( |
| cx, |
| OUTER_EXPN_EXPN_DATA, |
| spans[1].with_hi(expr.span.hi()), |
| "usage of `outer_expn().expn_data()`", |
| "try", |
| "outer_expn_data()".to_string(), |
| Applicability::MachineApplicable, |
| ); |
| } |
| } |
| } |