Method Checking

In some scenarios we might want to check for methods when developing a lint. There are two kinds of questions that we might be curious about:

  • Invocation: Does an expression call a specific method?
  • Definition: Does an impl define a method?

Checking if an expr is calling a specific method

Suppose we have an expr, we can check whether it calls a specific method, e.g. our_fancy_method, by performing a pattern match on the ExprKind that we can access from expr.kind:

#![allow(unused)]
fn main() {
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_span::sym;
use clippy_utils::is_trait_method;

impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
        // Check our expr is calling a method with pattern matching
        if let hir::ExprKind::MethodCall(path, _, [self_arg, ..]) = &expr.kind
            // Check if the name of this method is `our_fancy_method`
            && path.ident.name == sym!(our_fancy_method)
            // We can check the type of the self argument whenever necessary.
            // (It's necessary if we want to check that method is specifically belonging to a specific trait,
            // for example, a `map` method could belong to user-defined trait instead of to `Iterator`)
            // See the next section for more information.
            && is_trait_method(cx, self_arg, sym::OurFancyTrait)
        {
            println!("`expr` is a method call for `our_fancy_method`");
        }
    }
}
}

Take a closer look at the ExprKind enum variant MethodCall for more information on the pattern matching. As mentioned in Define Lints, the methods lint type is full of pattern matching with MethodCall in case the reader wishes to explore more.

Additionally, we use the clippy_utils::sym! macro to conveniently convert an input our_fancy_method into a Symbol and compare that symbol to the Ident's name in the PathSegment in the MethodCall.

Checking if a impl block implements a method

While sometimes we want to check whether a method is being called or not, other times we want to know if our Ty defines a method.

To check if our impl block defines a method our_fancy_method, we will utilize the check_impl_item method that is available in our beloved LateLintPass (for more information, refer to the "Lint Passes" chapter in the Clippy book). This method provides us with an ImplItem struct, which represents anything within an impl block.

Let us take a look at how we might check for the implementation of our_fancy_method on a type:

#![allow(unused)]
fn main() {
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::return_ty;
use rustc_hir::{ImplItem, ImplItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_span::symbol::sym;

impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
        // Check if item is a method/function
        if let ImplItemKind::Fn(ref signature, _) = impl_item.kind
            // Check the method is named `our_fancy_method`
            && impl_item.ident.name == sym!(our_fancy_method)
            // We can also check it has a parameter `self`
            && signature.decl.implicit_self.has_implicit_self()
            // We can go even further and even check if its return type is `String`
            && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym::String)
        {
            println!("`our_fancy_method` is implemented!");
        }
    }
}
}