rust/compiler/rustc_lint/src/noop_method_call.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

104 lines
4.0 KiB
Rust
Raw Normal View History

2021-01-05 10:07:50 +01:00
use crate::context::LintContext;
2022-06-17 13:15:00 +01:00
use crate::rustc_middle::ty::TypeVisitable;
2021-01-05 10:07:50 +01:00
use crate::LateContext;
use crate::LateLintPass;
use rustc_errors::fluent;
2021-01-05 10:07:50 +01:00
use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind};
use rustc_middle::ty;
use rustc_span::symbol::sym;
declare_lint! {
/// The `noop_method_call` lint detects specific calls to noop methods
/// such as a calling `<&T as Clone>::clone` where `T: !Clone`.
///
/// ### Example
///
/// ```rust
/// # #![allow(unused)]
2021-02-17 10:06:23 +01:00
/// #![warn(noop_method_call)]
2021-01-05 10:07:50 +01:00
/// struct Foo;
/// let foo = &Foo;
/// let clone: &Foo = foo.clone();
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Some method calls are noops meaning that they do nothing. Usually such methods
/// are the result of blanket implementations that happen to create some method invocations
/// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but
/// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything
/// as references are copy. This lint detects these calls and warns the user about them.
pub NOOP_METHOD_CALL,
2021-02-16 22:39:05 +01:00
Allow,
2021-01-05 10:07:50 +01:00
"detects the use of well-known noop methods"
}
declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
2021-01-13 10:39:25 -08:00
// We only care about method calls.
let ExprKind::MethodCall(call, elements, _) = &expr.kind else {
return
2021-01-13 10:39:25 -08:00
};
// We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
// traits and ignore any other method call.
let (trait_id, did) = match cx.typeck_results().type_dependent_def(expr.hir_id) {
// Verify we are dealing with a method/associated function.
Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) {
// Check that we're dealing with a trait method for one of the traits we care about.
Some(trait_id)
2021-10-04 16:11:22 -05:00
if matches!(
cx.tcx.get_diagnostic_name(trait_id),
Some(sym::Borrow | sym::Clone | sym::Deref)
) =>
2021-01-13 10:39:25 -08:00
{
(trait_id, did)
2021-01-05 10:07:50 +01:00
}
2021-01-13 10:39:25 -08:00
_ => return,
},
_ => return,
};
let substs = cx.typeck_results().node_substs(expr.hir_id);
2022-01-12 03:19:52 +00:00
if substs.needs_subst() {
2021-01-13 10:39:25 -08:00
// We can't resolve on types that require monomorphization, so we don't handle them if
// we need to perform substitution.
2021-01-13 10:39:25 -08:00
return;
}
let param_env = cx.tcx.param_env(trait_id);
// Resolve the trait method instance.
let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) else {
return
2021-01-13 10:39:25 -08:00
};
// (Re)check that it implements the noop diagnostic.
2021-11-16 13:12:38 -06:00
let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return };
if !matches!(
name,
sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref
) {
return;
2021-01-05 10:07:50 +01:00
}
2021-11-16 13:12:38 -06:00
let receiver = &elements[0];
let receiver_ty = cx.typeck_results().expr_ty(receiver);
let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
if receiver_ty != expr_ty {
// This lint will only trigger if the receiver type and resulting expression \
// type are the same, implying that the method call is unnecessary.
return;
}
let expr_span = expr.span;
let span = expr_span.with_lo(receiver.span.hi());
cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
lint.build(fluent::lint::noop_method_call)
.set_arg("method", call.ident.name)
.set_arg("receiver_ty", receiver_ty)
.span_label(span, fluent::lint::label)
.note(fluent::lint::note)
.emit();
2021-11-16 13:12:38 -06:00
});
2021-01-05 10:07:50 +01:00
}
}