Skip to main content

cargo/lints/rules/
missing_lints_inheritance.rs

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