blob: 5cdc66ae905362dc35a6b13d966a8360006f6eff [file] [log] [blame] [edit]
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,
);
}
}
}