3 stable releases

3.0.1 Apr 10, 2024
3.0.0 Apr 8, 2024
2.6.1 Mar 18, 2024

#1482 in Parser implementations

MIT/Apache

84KB
1.5K SLoC

dylint_linting

docs.rs documentation

This crate provides macros for creating Dylint libraries, and utilities for creating configurable libraries.

Contents

dylint_library!

The dylint_library! macro expands to the following:

#[allow(unused_extern_crates)]
extern crate rustc_driver;

#[no_mangle]
pub extern "C" fn dylint_version() -> *mut std::os::raw::c_char {
    std::ffi::CString::new($crate::DYLINT_VERSION)
        .unwrap()
        .into_raw()
}

If your library uses the dylint_library! macro and the dylint-link tool, then all you should have to do is implement the register_lints function. See the examples in this repository.

declare_late_lint!, etc.

If your library contains just one lint, using declare_late_lint!, etc. can make your code more concise. Each of these macros requires the same arguments as declare_lint!, and wraps the following:

  • a call to dylint_library!
  • an implementation of the register_lints function
  • a call to declare_lint!
  • a call to declare_lint_pass!

For example, declare_late_lint!(vis NAME, Level, "description") expands to the following:

dylint_linting::dylint_library!();

extern crate rustc_lint;
extern crate rustc_session;

#[no_mangle]
pub fn register_lints(sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) {
    dylint_linting::init_config(sess);
    lint_store.register_lints(&[NAME]);
    lint_store.register_late_pass(|_| Box::new(Name));
}

rustc_session::declare_lint!(vis NAME, Level, "description");

rustc_session::declare_lint_pass!(Name => [NAME]);

declare_early_lint! and declare_pre_expansion_lint! are defined similarly.

impl_late_lint!, etc.

impl_late_lint!, etc. are like declare_late_lint!, etc. except:

  • each calls impl_lint_pass! instead of declare_lint_pass!;
  • each requires an additional argument to specify the value of the lint's LintPass structure.

That is, impl_late_lint!'s additional argument is what goes here:

    lint_store.register_late_pass(|_| Box::new(...));
                                               ^^^

An example use of impl_pre_expansion_lint! can be found in env_cargo_path in this repository.

constituent feature

Enabling the package-level constituent feature changes the way the above macros work. Specifically, it causes them to exclude:

  • the call to dylint_library!
  • the use of #[no_mangle] just prior to the declaration of register_lints

Such changes facilitate inclusion of a lint declared with one of the above macros into a larger library. That is:

  • With the feature turned off, the lint can be built as a library by itself.
  • With the feature turned on, the lint can be built as part of a larger library, alongside other lints.

The general-purpose and supplementary lints in this repository employ this technique. That is, each general-purpose lint can be built as a library by itself, or as part of the general library. An analogous statement applies to the supplementary lints and the supplementary library. The constituent feature is the underlying mechanism that makes this work.

Configurable libraries

Libraries can be configured by including a dylint.toml file in the target workspace's root directory. This crate provides the following functions for reading and parsing dylint.toml files:

A configurable library containing just one lint will typically have a lib.rs file of the following form:

dylint_linting::impl_late_lint! {
    ...,
    LintName::new()
}

// Lint configuration
#[derive(Default, serde::Deserialize)]
struct Config {
    boolean: bool,
    strings: Vec<String>,
}

// Keep a copy of the configuration in the `LintPass` structure.
struct LintName {
    config: Config,
}

// Read the configuration from the `dylint.toml` file, or use the default configuration if
// none is present.
impl LintName {
    pub fn new() -> Self {
        Self {
            config: dylint_linting::config_or_default(env!("CARGO_PKG_NAME")),
        }
    }
}

For a concrete example of a lib.rs file with this form, see the non_local_effect_before_error_return library in this repository.

A library containing more than one lint must implement the register_lints function without relying on the above macros. If the library is configurable, then its register_lints function should include a call to dylint_linting::init_config, as in the following example:

#[no_mangle]
pub fn register_lints(sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) {
    // `init_config` or `try_init_config` must be called before `config_or_default`, `config`,
    // or `config_toml` is called.
    dylint_linting::init_config(sess);

    lint_store.register_lints(&[FIRST_LINT_NAME, SECOND_LINT_NAME]);

    lint_store.register_late_pass(|_| Box::new(LintPassName::new()));
}

Additional documentation on config_or_default, etc. can be found on docs.rs.

Dependencies

~1.3–2.2MB
~45K SLoC