Skip to main content

cargo/diagnostics/rules/
missing_lints_inheritance.rs

1use std::path::Path;
2
3use cargo_util_schemas::manifest::TomlToolLints;
4use cargo_util_terminal::report::Group;
5use cargo_util_terminal::report::Level;
6use cargo_util_terminal::report::Origin;
7use cargo_util_terminal::report::Patch;
8use cargo_util_terminal::report::Snippet;
9use tracing::instrument;
10
11use super::SUSPICIOUS;
12use crate::CargoResult;
13use crate::GlobalContext;
14use crate::core::Package;
15use crate::core::Workspace;
16use crate::diagnostics::Lint;
17use crate::diagnostics::LintLevel;
18use crate::diagnostics::rel_cwd_manifest_path;
19
20pub static LINT: &Lint = &Lint {
21    name: "missing_lints_inheritance",
22    desc: "missing `[lints]` to inherit `[workspace.lints]`",
23    primary_group: &SUSPICIOUS,
24    msrv: Some(super::CARGO_LINTS_MSRV),
25    feature_gate: None,
26    docs: Some(
27        r#"
28### What it does
29
30Checks for packages without a `lints` table while `workspace.lints` is present.
31
32### Why it is bad
33
34Many people mistakenly think that `workspace.lints` is implicitly inherited when it is not.
35
36### Drawbacks
37
38### Example
39
40```toml
41[workspace.lints.cargo]
42```
43
44Should be written as:
45
46```toml
47[workspace.lints.cargo]
48
49[lints]
50workspace = true
51```
52"#,
53    ),
54};
55
56#[instrument(skip_all)]
57pub fn missing_lints_inheritance(
58    ws: &Workspace<'_>,
59    pkg: &Package,
60    manifest_path: &Path,
61    cargo_lints: &TomlToolLints,
62    error_count: &mut usize,
63    gctx: &GlobalContext,
64) -> CargoResult<()> {
65    let (lint_level, source) = LINT.level(
66        cargo_lints,
67        pkg.rust_version(),
68        pkg.manifest().unstable_features(),
69    );
70
71    if lint_level == LintLevel::Allow {
72        return Ok(());
73    }
74
75    let root = ws.root_maybe();
76    // `normalized_toml` normally isn't guaranteed to include inheritance information except
77    // `workspace.lints` is used outside of inheritance for workspace-level lints.
78    let ws_lints = root
79        .normalized_toml()
80        .workspace
81        .as_ref()
82        .map(|ws| ws.lints.is_some())
83        .unwrap_or(false);
84    if !ws_lints {
85        return Ok(());
86    }
87    if pkg.manifest().normalized_toml().lints.is_some() {
88        return Ok(());
89    }
90
91    let manifest = pkg.manifest();
92    let contents = manifest.contents();
93    let level = lint_level.to_diagnostic_level();
94    let emitted_source = LINT.emitted_source(lint_level, source);
95    let manifest_path = rel_cwd_manifest_path(manifest_path, gctx);
96
97    let mut primary = Group::with_title(level.primary_title(LINT.desc));
98    primary = primary.element(Origin::path(&manifest_path));
99    primary = primary.element(Level::NOTE.message(emitted_source));
100    let mut report = vec![primary];
101    if let Some(contents) = contents {
102        let span = contents.len()..contents.len();
103        let mut help =
104            Group::with_title(Level::HELP.secondary_title("to inherit `workspace.lints, add:"));
105        help = help.element(
106            Snippet::source(contents)
107                .path(&manifest_path)
108                .patch(Patch::new(span.clone(), "\n[lints]\nworkspace = true")),
109        );
110        report.push(help);
111        let mut help = Group::with_title(
112            Level::HELP.secondary_title("to clarify your intent to not inherit, add:"),
113        );
114        help = help.element(
115            Snippet::source(contents)
116                .path(&manifest_path)
117                .patch(Patch::new(span, "\n[lints]")),
118        );
119        report.push(help);
120    }
121
122    if lint_level.is_error() {
123        *error_count += 1;
124    }
125    gctx.shell().print_report(&report, lint_level.force())?;
126
127    Ok(())
128}