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