Custom lints for my crates

I would like to create some custom lints for crates I'm planning on writing, how/where would you recommend creating them?

Clippy might not be the best place since the lints don't apply to general Rust code, but just to code using my crates. Rustc plugins are apparently deprecated and shortly going to be removed. rust-analyzer is apparently not a reliable place to lint for things that require knowing types and not just syntax.

I don't think this is possible, although I would be very happy to be proven wrong.

Besides basically rewriting clippy that is.

I believe it's technically possible via compiler plugins as you noted (I haven't heard that they'll be removed). Aside from that, I'm not aware of anything. Any approach would take a very large amount of effort, regardless.

You could use a procedural macro:
In a crate my_lint:

// Cargo.toml
[lib]
proc-macro = true

[dependencies]
quote = "1.0"
syn = { version = "1.0", features = ["visit", "full"] }
proc-macro-error = "1.0"
// lib.rs
use proc_macro::TokenStream;
use proc_macro_error::{abort, proc_macro_error};
use quote::quote;
use syn::{
    parse_macro_input,
    visit::{self, Visit},
    ItemFn, Local,
};

/// Abort compilation if a `let` binding of the form
/// `let x: Type;` is found
#[proc_macro_error]
#[proc_macro_attribute]
pub fn abort_not_assigned(_metadata: TokenStream, input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as ItemFn);
    let mut linter = Linter;
    linter.visit_item_fn(&input);
    TokenStream::from(quote!(#input))
}

struct Linter;

impl<'ast> Visit<'ast> for Linter {
    fn visit_local(&mut self, local: &'ast Local) {
        if local.init.is_none() {
            abort!(local, "not assigned !")
        }
        visit::visit_local(self, local);
    }
}

And in mycrate:

// Cargo.toml
[dependencies]
my_lint = { path = "path/to/my_lint" }
// lib.rs
use my_lint::abort_not_assigned;

#[abort_not_assigned]
pub fn will_abort() -> i32 {
    let x: i32;
    x = 5;
    x
}

#[abort_not_assigned]
pub fn will_not_abort() -> i32 {
    let x = 5;
    x
}

There are some drawbacks however:

  • From what I gather there is currently no other option than emitting a hard error, so this is a pretty hardcore linter
  • All functions that you want linted need to be annotated with #[abort_not_assigned]
  • The linting is only based on syntax, not semantics, so this approach is very limited :frowning:
  • This might greatly increase compilation times

Sidenote : there is a #[must_use] attribute one could use for this specific case.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.