方法检查

在某些情况下,我们可能希望在开发 lint 时检查方法。我们可能对两种问题感到好奇

  • 调用:表达式是否调用了特定的方法?
  • 定义:impl 是否定义了一个方法?

检查 expr 是否调用特定方法

假设我们有一个 expr,我们可以通过对 ExprKind 进行模式匹配来检查它是否调用了特定方法,例如 our_fancy_method,我们可以通过 expr.kind 访问 ExprKind

#![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.as_str() == "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`");
        }
    }
}
}

仔细查看 ExprKind 枚举变体 MethodCall,以获取有关模式匹配的更多信息。正如在定义 Lint 中提到的,如果读者希望探索更多,methods lint 类型充满了与 MethodCall 的模式匹配。

检查 impl 块是否实现了一个方法

有时我们想检查是否调用了某个方法,而其他时候我们想知道我们的 Ty 是否定义了一个方法。

要检查我们的 impl 块是否定义了一个方法 our_fancy_method,我们将利用 check_impl_item 方法,该方法在我们喜爱的 LateLintPass 中可用(有关更多信息,请参阅 Clippy 书中的 “Lint Passes” 章节)。此方法为我们提供了一个 ImplItem 结构,它表示 impl 块中的任何内容。

让我们看看我们如何检查类型上 our_fancy_method 的实现

#![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.as_str() == "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!");
        }
    }
}
}