cargo/diagnostics/rules/
missing_lints_inheritance.rs1use std::path::Path;
2
3use cargo_util_terminal::report::Group;
4use cargo_util_terminal::report::Level;
5use cargo_util_terminal::report::Origin;
6use cargo_util_terminal::report::Patch;
7use cargo_util_terminal::report::Snippet;
8use tracing::instrument;
9
10use super::SUSPICIOUS;
11use crate::CargoResult;
12use crate::GlobalContext;
13use crate::core::Package;
14use crate::core::Workspace;
15use crate::diagnostics::Lint;
16use crate::diagnostics::LintLevelProduct;
17use crate::diagnostics::ScopedDiagnosticStats;
18use crate::diagnostics::workspace_rel_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
53or make it explicit that you don't intend to inherit by adding an empty `[lints]` table:
54
55```toml
56[workspace.lints.cargo]
57
58[lints]
59```
60"#,
61 ),
62};
63
64#[instrument(skip_all)]
65pub(crate) fn lint_package(
66 ws: &Workspace<'_>,
67 pkg: &Package,
68 manifest_path: &Path,
69 level: LintLevelProduct,
70 pkg_stats: &mut ScopedDiagnosticStats<'_>,
71 gctx: &GlobalContext,
72) -> CargoResult<()> {
73 let LintLevelProduct {
74 level: lint_level,
75 source,
76 } = level;
77
78 let root = ws.root_maybe();
79 let ws_lints = root
82 .normalized_toml()
83 .workspace
84 .as_ref()
85 .map(|ws| ws.lints.is_some())
86 .unwrap_or(false);
87 if !ws_lints {
88 return Ok(());
89 }
90 if pkg.manifest().normalized_toml().lints.is_some() {
91 return Ok(());
92 }
93
94 let manifest = pkg.manifest();
95 let contents = manifest.contents();
96 let level = lint_level.to_diagnostic_level();
97 let emitted_source = LINT.emitted_source(lint_level, source);
98 let manifest_path = workspace_rel_path(ws, manifest_path);
99
100 let mut primary = Group::with_title(level.primary_title(LINT.desc));
101 primary = primary.element(Origin::path(&manifest_path));
102 primary = primary.element(Level::NOTE.message(emitted_source));
103 let mut report = vec![primary];
104 if let Some(contents) = contents {
105 let span = contents.len()..contents.len();
106 let mut help =
107 Group::with_title(Level::HELP.secondary_title("to inherit `workspace.lints, add:"));
108 help = help.element(
109 Snippet::source(contents)
110 .path(&manifest_path)
111 .patch(Patch::new(span.clone(), "\n[lints]\nworkspace = true")),
112 );
113 report.push(help);
114 let mut help = Group::with_title(
115 Level::HELP.secondary_title("to clarify your intent to not inherit, add:"),
116 );
117 help = help.element(
118 Snippet::source(contents)
119 .path(&manifest_path)
120 .patch(Patch::new(span, "\n[lints]")),
121 );
122 report.push(help);
123 }
124
125 pkg_stats.record_lint(lint_level);
126 gctx.shell().print_report(&report, lint_level.force())?;
127
128 Ok(())
129}