Rust lint工具库dylint_internal的使用,动态代码分析与自定义lint规则开发

Rust lint工具库dylint_internal的使用,动态代码分析与自定义lint规则开发

dylint_internal是Dylint的内部工具包,普通用户通常不需要直接使用这个包。

安装

全局安装二进制工具

cargo install dylint_internal

运行上述命令将全局安装preinstall-toolchains二进制工具。

作为库安装

在项目目录中运行以下Cargo命令:

cargo add dylint_internal

或者将以下行添加到您的Cargo.toml中:

dylint_internal = "4.1.0"

元数据

  • 版本: 4.1.0
  • 发布时间: 4个月前
  • Rust版本: 2021 edition
  • 许可证: MIT OR Apache-2.0
  • 大小: 25.1 KiB

所有者

  • Samuel Moelius (smoelius)

完整示例代码

以下是一个使用dylint_internal开发自定义lint规则的完整示例:

// 首先,添加dylint_internal作为依赖
// Cargo.toml:
// [dependencies]
// dylint_internal = "4.1.0"

use dylint_internal::{Driver, LintPass, LateContext, LateLintPass, LintContext};
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_lint::{LateLintPass, LintArray};
use rustc_session::declare_lint_pass;

// 声明我们的自定义lint
declare_lint! {
    pub MY_CUSTOM_LINT,
    Warn,
    "检测到可能的问题 - 这是我们的自定义lint"
}

// 实现LintPass
declare_lint_pass!(MyCustomLint => [MY_CUSTOM_LINT]);

// 实现LateLintPass来检查具体的语法节点
impl<'tcx> LateLintPass<'tcx> for MyCustomLint {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx ast::Expr) {
        // 这里可以添加具体的检查逻辑
        // 例如,检查是否使用了println!宏
        if let ast::ExprKind::MacCall(mac) = &expr.kind {
            if mac.path.segments[0].ident.name == sym!(println) {
                cx.span_lint(
                    MY_CUSTOM_LINT,
                    expr.span,
                    "避免在生产代码中使用println!宏",
                );
            }
        }
    }
}

// 注册我们的lint
#[no_mangle]
pub fn register_lints(_sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) {
    lint_store.register_lints(&[&MY_CUSTOM_LINT]);
    lint_store.register_late_pass(|_| Box::new(MyCustomLint));
}

// 主函数
fn main() {
    // 使用dylint_internal的Driver运行我们的lint
    let args: Vec<String> = std::env::args().collect();
    dylint_internal::driver().run(&args).unwrap();
}

这个示例展示了如何:

  1. 定义一个自定义lint
  2. 实现LateLintPass来检查特定的语法节点
  3. 注册lint到编译器
  4. 使用dylint_internal的Driver来运行lint

要使用这个lint,您需要将其编译为动态库,然后通过Dylint加载它。


1 回复

Rust lint工具库dylint_internal的使用:动态代码分析与自定义lint规则开发

介绍

dylint_internal 是一个用于开发自定义Rust lint规则的库,它允许开发者创建动态加载的lint工具。与传统的静态链接lint工具不同,dylint_internal提供了更灵活的lint规则加载方式,特别适合需要频繁更新lint规则或针对特定项目定制lint的场景。

主要特性

  • 动态加载lint规则,无需重新编译工具链
  • 支持编写自定义lint规则
  • 与Rust编译器基础设施集成
  • 提供丰富的辅助工具和宏来简化lint开发

完整示例代码

以下是基于内容中提供的示例代码扩展的完整demo:

// src/lib.rs
use dylint_internal::{declare_lint, lint_array};
use rustc_ast::ast::*;
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintArray, LintPass};
use rustc_session::declare_lint_pass;
use rustc_span::sym;
use rustc_span::Span;

// 示例1: 检查println!宏使用
declare_lint! {
    pub PRINTLN_USED,
    Warn,
    "检测到使用了`println!`宏"
}

declare_lint_pass!(NoPrintln => [PRINTLN_USED]);

impl EarlyLintPass for NoPrintln {
    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
        if let Some(ident) = mac.path.get_ident() {
            if ident.name == sym!(println) {
                cx.span_lint(
                    PRINTLN_USED,
                    mac.span,
                    "避免使用`println!`,考虑使用日志框架代替",
                );
            }
        }
    }
}

// 示例2: 检查Result类型函数是否添加了must_use属性
declare_lint! {
    pub MUST_USE_RESULT,
    Warn,
    "检测到返回Result类型的函数缺少`#[must_use]`属性"
}

declare_lint_pass!(MustUseResult => [MUST_USE_RESULT]);

impl LateLintPass<'_> for MustUseResult {
    fn check_fn(
        &mut self,
        cx: &LateContext<'_>,
        kind: FnKind<'_>,
        decl: &FnDecl<'_>,
        _: &Body<'_>,
        span: Span,
    ) {
        if let FnKind::Fn(_, _, header, _, _) = kind {
            if is_result_type(decl.output) && !header.attrs.iter().any(|a| a.has_name(sym!(must_use))) {
                cx.span_lint(
                    MUST_USE_RESULT,
                    span,
                    "返回`Result`类型的函数应该添加`#[must_use]`属性",
                );
            }
        }
    }
}

// 判断是否为Result类型的简化实现
fn is_result_type(ty: &FnRetTy) -> bool {
    match ty {
        FnRetTy::Ty(ty) => {
            let ty_str = ty.to_string();
            ty_str.contains("Result") || ty_str.contains("std::result::Result")
        }
        FnRetTy::DefaultReturn(_) => false,
    }
}

// 导出lint规则
#[no_mangle]
pub fn dylint_lints() -> &'static LintArray {
    lint_array!(PRINTLN_USED, MUST_USE_RESULT)
}

配套配置文件示例

# dylint.toml
[no_println]
allowed_files = ["src/main.rs", "tests/*.rs"]

[must_use_result]
exclude_modules = ["test_utils"]

构建和使用说明

  1. 构建lint库:
cargo build --release
  1. 使用dylint运行自定义lint:
dylint -l target/release/libmy_lints.so path/to/your/project
  1. 查看特定lint的详细输出:
RUST_LOG=dylint=debug dylint -l target/release/libmy_lints.so path/to/your/project

最佳实践补充

  1. 错误信息:为每个lint提供详细的错误信息和修复建议
  2. 测试覆盖:为自定义lint编写测试用例
  3. 版本兼容:确保lint规则与不同版本的Rust编译器兼容
  4. 配置灵活性:提供丰富的配置选项以适应不同项目需求

调试技巧补充

  1. 使用cargo rustc -- -Zunpretty=expanded查看宏展开
  2. 在lint规则中添加调试日志:
log::debug!("Checking macro: {:?}", mac);
  1. 使用rustc_ast::print::pprust::item_to_string打印AST节点详细信息
回到顶部