#
cargo/lints/rules/
unknown_lints.rs

    1use annotate_snippets::AnnotationKind;
    2use annotate_snippets::Group;
    3use annotate_snippets::Level;
    4use annotate_snippets::Origin;
    5use annotate_snippets::Snippet;
    6use cargo_util_schemas::manifest::TomlToolLints;
    7
    8use crate::CargoResult;
    9use crate::GlobalContext;
    10use crate::lints::LINT_GROUPS;
    11use crate::lints::LINTS;
    12use crate::lints::Lint;
    13use crate::lints::LintLevel;
    14use crate::lints::ManifestFor;
    15use crate::lints::SUSPICIOUS;
    16use crate::lints::get_key_value_span;
    17
    18pub const LINT: Lint = Lint {
    19    name: "unknown_lints",
    20    desc: "unknown lint",
    21    primary_group: &SUSPICIOUS,
    22    edition_lint_opts: None,
    23    feature_gate: None,
    24    docs: Some(
    25        r#"
    26### What it does
    27Checks for unknown lints in the `[lints.cargo]` table
    28
    29### Why it is bad
    30- The lint name could be misspelled, leading to confusion as to why it is
    31  not working as expected
    32- The unknown lint could end up causing an error if `cargo` decides to make
    33  a lint with the same name in the future
    34
    35### Example
    36```toml
    37[lints.cargo]
    38this-lint-does-not-exist = "warn"
    39```
    40"#,
    41    ),
    42};
    43
    44pub fn output_unknown_lints(
    45    unknown_lints: Vec<&String>,
    46    manifest: &ManifestFor<'_>,
    47    manifest_path: &str,
    48    cargo_lints: &TomlToolLints,
    49    error_count: &mut usize,
    50    gctx: &GlobalContext,
    51) -> CargoResult<()> {
    52    let (lint_level, reason) = manifest.lint_level(cargo_lints, LINT);
    53    if lint_level == LintLevel::Allow {
    54        return Ok(());
    55    }
    56
    57    let level = lint_level.to_diagnostic_level();
    58    let mut emitted_source = None;
    59    for lint_name in unknown_lints {
    60        if lint_level.is_error() {
    61            *error_count += 1;
    62        }
    63        let title = format!("{}: `{lint_name}`", LINT.desc);
    64        let underscore_lint_name = lint_name.replace("-", "_");
    65        let matching = if let Some(lint) = LINTS.iter().find(|l| l.name == underscore_lint_name) {
    66            Some((lint.name, "lint"))
    67        } else if let Some(group) = LINT_GROUPS.iter().find(|g| g.name == underscore_lint_name) {
    68            Some((group.name, "group"))
    69        } else {
    70            None
    71        };
    72        let help =
    73            matching.map(|(name, kind)| format!("there is a {kind} with a similar name: `{name}`"));
    74
    75        let key_path = match manifest {
    76            ManifestFor::Package(_) => &["lints", "cargo", lint_name][..],
    77            ManifestFor::Workspace(_) => &["workspace", "lints", "cargo", lint_name][..],
    78        };
    79
    80        let mut report = Vec::new();
    81        let mut group = Group::with_title(level.clone().primary_title(title));
    82
    83        if let Some(document) = manifest.document()
    84            && let Some(contents) = manifest.contents()
    85        {
    86            let Some(span) = get_key_value_span(document, key_path) else {
    87                // This lint is handled by either package or workspace lint.
    88                return Ok(());
    89            };
    90            group = group.element(
    91                Snippet::source(contents)
    92                    .path(manifest_path)
    93                    .annotation(AnnotationKind::Primary.span(span.key)),
    94            );
    95        } else {
    96            group = group.element(Origin::path(manifest_path));
    97        }
    98
    99        if emitted_source.is_none() {
    100            emitted_source = Some(LINT.emitted_source(lint_level, reason));
    101            group = group.element(Level::NOTE.message(emitted_source.as_ref().unwrap()));
    102        }
    103        if let Some(help) = help.as_ref() {
    104            group = group.element(Level::HELP.message(help));
    105        }
    106        report.push(group);
    107
    108        gctx.shell().print_report(&report, lint_level.force())?;
    109    }
    110
    111    Ok(())
    112}