| use std::path::Path; | |
| use cargo_util_terminal::report::Group; | |
| use cargo_util_terminal::report::Level; | |
| use cargo_util_terminal::report::Origin; | |
| use cargo_util_terminal::report::Patch; | |
| use cargo_util_terminal::report::Snippet; | |
| use tracing::instrument; | |
| use super::SUSPICIOUS; | |
| use crate::CargoResult; | |
| use crate::GlobalContext; | |
| use crate::diagnostics::Lint; | |
| use crate::diagnostics::LintLevelProduct; | |
| use crate::diagnostics::ScopedDiagnosticStats; | |
| use crate::diagnostics::workspace_rel_path; | |
| use crate::workspace::Package; | |
| use crate::workspace::Workspace; | |
| pub static LINT: &Lint = &Lint { | |
| name: "missing_lints_inheritance", | |
| primary_group: &SUSPICIOUS, | |
| msrv: Some(super::CARGO_LINTS_MSRV), | |
| feature_gate: None, | |
| docs: Some( | |
| r#" | |
| ### What it does | |
| Checks for packages without a `lints` table while `workspace.lints` is present. | |
| ### Why is this bad? | |
| Many people mistakenly think that `workspace.lints` is implicitly inherited when it is not. | |
| ### Drawbacks | |
| ### Example | |
| ```toml | |
| [workspace.lints.cargo] | |
| ``` | |
| Should be written as: | |
| ```toml | |
| [workspace.lints.cargo] | |
| [lints] | |
| workspace = true | |
| ``` | |
| or make it explicit that you don't intend to inherit by adding an empty `[lints]` table: | |
| ```toml | |
| [workspace.lints.cargo] | |
| [lints] | |
| ``` | |
| "#, | |
| ), | |
| }; | |
| #[instrument(skip_all)] | |
| pub(crate) fn lint_package( | |
| ws: &Workspace<'_>, | |
| pkg: &Package, | |
| manifest_path: &Path, | |
| level: LintLevelProduct, | |
| pkg_stats: &mut ScopedDiagnosticStats<'_>, | |
| gctx: &GlobalContext, | |
| ) -> CargoResult<()> { | |
| let LintLevelProduct { | |
| level: lint_level, | |
| source, | |
| } = level; | |
| let root = ws.root_maybe(); | |
| // `normalized_toml` normally isn't guaranteed to include inheritance information except | |
| // `workspace.lints` is used outside of inheritance for workspace-level lints. | |
| let ws_lints = root | |
| .normalized_toml() | |
| .workspace | |
| .as_ref() | |
| .map(|ws| ws.lints.is_some()) | |
| .unwrap_or(false); | |
| if !ws_lints { | |
| return Ok(()); | |
| } | |
| if pkg.manifest().normalized_toml().lints.is_some() { | |
| return Ok(()); | |
| } | |
| let manifest = pkg.manifest(); | |
| let contents = manifest.contents(); | |
| let level = lint_level.to_diagnostic_level(); | |
| let emitted_source = LINT.emitted_source(lint_level, source, gctx); | |
| let manifest_path = workspace_rel_path(ws, manifest_path); | |
| let mut primary = | |
| Group::with_title(level.primary_title("missing `[lints]` to inherit `[workspace.lints]`")); | |
| primary = primary.element(Origin::path(&manifest_path)); | |
| primary = primary.element(Level::NOTE.message(emitted_source)); | |
| let mut report = vec![primary]; | |
| if let Some(contents) = contents { | |
| let span = contents.len()..contents.len(); | |
| let mut help = | |
| Group::with_title(Level::HELP.secondary_title("to inherit `workspace.lints, add:")); | |
| help = help.element( | |
| Snippet::source(contents) | |
| .path(&manifest_path) | |
| .patch(Patch::new(span.clone(), "\n[lints]\nworkspace = true")), | |
| ); | |
| report.push(help); | |
| let mut help = Group::with_title( | |
| Level::HELP.secondary_title("to clarify your intent to not inherit, add:"), | |
| ); | |
| help = help.element( | |
| Snippet::source(contents) | |
| .path(&manifest_path) | |
| .patch(Patch::new(span, "\n[lints]")), | |
| ); | |
| report.push(help); | |
| } | |
| pkg_stats.record_lint(lint_level); | |
| gctx.shell().print_report(&report, lint_level.force())?; | |
| Ok(()) | |
| } |