cargo/lints/rules/
missing_lints_inheritance.rs1use 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 edition_lint_opts: None,
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
56pub fn missing_lints_inheritance(
57 ws: &Workspace<'_>,
58 pkg: &Package,
59 manifest_path: &Path,
60 cargo_lints: &TomlToolLints,
61 error_count: &mut usize,
62 gctx: &GlobalContext,
63) -> CargoResult<()> {
64 let (lint_level, reason) = LINT.level(
65 cargo_lints,
66 pkg.rust_version(),
67 pkg.manifest().edition(),
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 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, reason);
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}