diff --git a/src/advisories/cfg.rs b/src/advisories/cfg.rs index f7ff4390..23fe47c1 100644 --- a/src/advisories/cfg.rs +++ b/src/advisories/cfg.rs @@ -1,7 +1,7 @@ use crate::{ cfg::{PackageSpecOrExtended, Reason, ValidationContext}, diag::{Diagnostic, FileId, Label}, - LintLevel, PathBuf, Spanned, + LintLevel, PathBuf, Span, Spanned, }; use rustsec::advisory; use time::Duration; @@ -44,6 +44,7 @@ pub struct Config { /// use the '.' separator instead of ',' which is used by some locales and /// supported in the RFC3339 format, but not by this implementation pub maximum_db_staleness: Spanned, + deprecated: Vec, } impl Default for Config { @@ -62,6 +63,7 @@ impl Default for Config { git_fetch_with_cli: None, disable_yank_checking: false, maximum_db_staleness: Spanned::new(Duration::seconds_f64(NINETY_DAYS)), + deprecated: Vec::new(), } } } @@ -97,13 +99,20 @@ impl<'de> toml_span::Deserialize<'de> for Config { } else { Vec::new() }; - let vulnerability = th.optional("vulnerability").unwrap_or(LintLevel::Deny); - let unmaintained = th.optional("unmaintained").unwrap_or(LintLevel::Warn); - let unsound = th.optional("unsound").unwrap_or(LintLevel::Warn); + + use crate::cfg::deprecated; + + let mut fdeps = Vec::new(); + + let vulnerability = + deprecated(&mut th, "vulnerability", &mut fdeps).unwrap_or(LintLevel::Deny); + let unmaintained = + deprecated(&mut th, "unmaintained", &mut fdeps).unwrap_or(LintLevel::Warn); + let unsound = deprecated(&mut th, "unsound", &mut fdeps).unwrap_or(LintLevel::Warn); let yanked = th .optional_s("yanked") .unwrap_or(Spanned::new(LintLevel::Warn)); - let notice = th.optional("notice").unwrap_or(LintLevel::Warn); + let notice = deprecated(&mut th, "notice", &mut fdeps).unwrap_or(LintLevel::Warn); let (ignore, ignore_yanked) = if let Some((_, mut ignore)) = th.take("ignore") { let mut u = Vec::new(); let mut y = Vec::new(); @@ -152,7 +161,38 @@ impl<'de> toml_span::Deserialize<'de> for Config { } else { (Vec::new(), Vec::new()) }; - let severity_threshold = th.parse_opt("severity-threshold"); + let st = |th: &mut TableHelper<'_>, fdeps: &mut Vec| { + let (k, mut v) = th.take("severity-threshold")?; + + fdeps.push(k.span); + let s = match v.take_string(Some( + "https://docs.rs/rustsec/latest/rustsec/advisory/enum.Severity.html", + )) { + Ok(s) => s, + Err(err) => { + th.errors.push(err); + return None; + } + }; + + match s.parse() { + Ok(st) => Some(st), + Err(err) => { + th.errors.push( + ( + toml_span::ErrorKind::Custom( + format!("failed to parse rustsec::Severity: {err}").into(), + ), + v.span, + ) + .into(), + ); + None + } + } + }; + + let severity_threshold = st(&mut th, &mut fdeps); let git_fetch_with_cli = th.optional("git-fetch-with-cli"); let disable_yank_checking = th.optional("disable-yank-checking").unwrap_or_default(); let maximum_db_staleness = if let Some((_, mut val)) = th.take("maximum-db-staleness") { @@ -199,6 +239,7 @@ impl<'de> toml_span::Deserialize<'de> for Config { git_fetch_with_cli, disable_yank_checking, maximum_db_staleness, + deprecated: fdeps, }) } } @@ -226,6 +267,23 @@ impl crate::cfg::UnvalidatedConfig for Config { } } + use crate::diag::general::{Deprecated, DeprecationReason}; + + // Output any deprecations, we'll remove the fields at the same time we + // remove all the logic they drive + for dep in self.deprecated { + ctx.push( + Deprecated { + reason: DeprecationReason::WillBeRemoved(Some( + "https://github.com/EmbarkStudios/cargo-deny/pull/606", + )), + key: dep, + file_id: ctx.cfg_id, + } + .into(), + ); + } + ValidConfig { file_id: ctx.cfg_id, db_path: self.db_path, @@ -450,7 +508,13 @@ mod test { #[test] fn deserializes_advisories_cfg() { let cd = ConfigData::::load("tests/cfg/advisories.toml"); - let validated = cd.validate(|a| a.advisories); + let validated = cd.validate_with_diags( + |a| a.advisories, + |files, diags| { + let diags = write_diagnostics(files, diags.into_iter()); + insta::assert_snapshot!(diags); + }, + ); insta::assert_json_snapshot!(validated); } diff --git a/src/advisories/snapshots/cargo_deny__advisories__cfg__test__deserializes_advisories_cfg-2.snap b/src/advisories/snapshots/cargo_deny__advisories__cfg__test__deserializes_advisories_cfg-2.snap new file mode 100644 index 00000000..6039f389 --- /dev/null +++ b/src/advisories/snapshots/cargo_deny__advisories__cfg__test__deserializes_advisories_cfg-2.snap @@ -0,0 +1,44 @@ +--- +source: src/advisories/cfg.rs +expression: validated +--- +{ + "file_id": 1, + "db_path": "~/.cargo/advisory-dbs", + "db_urls": [ + "https://github.com/RustSec/advisory-db" + ], + "ignore": [ + "RUSTSEC-0000-0000" + ], + "ignore_yanked": [ + { + "spec": { + "name": "crate", + "version-req": "=0.1" + }, + "reason": null, + "use-instead": null + }, + { + "spec": { + "name": "yanked", + "version-req": null + }, + "reason": "a new version has not been released", + "use-instead": null + } + ], + "vulnerability": "deny", + "unmaintained": "warn", + "unsound": "warn", + "yanked": "warn", + "notice": "warn", + "severity_threshold": "medium", + "git_fetch_with_cli": false, + "disable_yank_checking": false, + "maximum_db_staleness": [ + 466560000, + 0 + ] +} diff --git a/src/advisories/snapshots/cargo_deny__advisories__cfg__test__deserializes_advisories_cfg.snap b/src/advisories/snapshots/cargo_deny__advisories__cfg__test__deserializes_advisories_cfg.snap index 6039f389..5dbe202d 100644 --- a/src/advisories/snapshots/cargo_deny__advisories__cfg__test__deserializes_advisories_cfg.snap +++ b/src/advisories/snapshots/cargo_deny__advisories__cfg__test__deserializes_advisories_cfg.snap @@ -1,44 +1,35 @@ --- source: src/advisories/cfg.rs -expression: validated +expression: diags --- -{ - "file_id": 1, - "db_path": "~/.cargo/advisory-dbs", - "db_urls": [ - "https://github.com/RustSec/advisory-db" - ], - "ignore": [ - "RUSTSEC-0000-0000" - ], - "ignore_yanked": [ - { - "spec": { - "name": "crate", - "version-req": "=0.1" - }, - "reason": null, - "use-instead": null - }, - { - "spec": { - "name": "yanked", - "version-req": null - }, - "reason": "a new version has not been released", - "use-instead": null - } - ], - "vulnerability": "deny", - "unmaintained": "warn", - "unsound": "warn", - "yanked": "warn", - "notice": "warn", - "severity_threshold": "medium", - "git_fetch_with_cli": false, - "disable_yank_checking": false, - "maximum_db_staleness": [ - 466560000, - 0 - ] -} +warning[deprecated]: this key will be removed in a future update, see https://github.com/EmbarkStudios/cargo-deny/pull/606 for details + ┌─ tests/cfg/advisories.toml:4:1 + │ +4 │ vulnerability = "deny" + │ ^^^^^^^^^^^^^ + +warning[deprecated]: this key will be removed in a future update, see https://github.com/EmbarkStudios/cargo-deny/pull/606 for details + ┌─ tests/cfg/advisories.toml:5:1 + │ +5 │ unmaintained = "warn" + │ ^^^^^^^^^^^^ + +warning[deprecated]: this key will be removed in a future update, see https://github.com/EmbarkStudios/cargo-deny/pull/606 for details + ┌─ tests/cfg/advisories.toml:6:1 + │ +6 │ unsound = "warn" + │ ^^^^^^^ + +warning[deprecated]: this key will be removed in a future update, see https://github.com/EmbarkStudios/cargo-deny/pull/606 for details + ┌─ tests/cfg/advisories.toml:8:1 + │ +8 │ notice = "warn" + │ ^^^^^^ + +warning[deprecated]: this key will be removed in a future update, see https://github.com/EmbarkStudios/cargo-deny/pull/606 for details + ┌─ tests/cfg/advisories.toml:14:1 + │ +14 │ severity-threshold = "medium" + │ ^^^^^^^^^^^^^^^^^^ + + diff --git a/src/cfg.rs b/src/cfg.rs index 72811d09..0e726af5 100644 --- a/src/cfg.rs +++ b/src/cfg.rs @@ -72,3 +72,27 @@ impl<'de> toml_span::Deserialize<'de> for Reason { Ok(Self(r)) } } + +/// Deserialize a field from the table if it exists, but append the key's span +/// so it can be marked as deprecated +pub fn deprecated<'de, T>( + th: &mut toml_span::de_helpers::TableHelper<'de>, + field: &'static str, + spans: &mut Vec, +) -> Option +where + T: toml_span::Deserialize<'de>, +{ + let Some((k, mut v)) = th.take(field) else { + return None; + }; + spans.push(k.span); + + match T::deserialize(&mut v) { + Ok(v) => Some(v), + Err(mut err) => { + th.errors.append(&mut err.errors); + None + } + } +} diff --git a/src/diag/general.rs b/src/diag/general.rs index efbad40c..fcd08d1e 100644 --- a/src/diag/general.rs +++ b/src/diag/general.rs @@ -29,23 +29,39 @@ impl From for String { } pub enum DeprecationReason { - WillBeRemoved, + WillBeRemoved(Option<&'static str>), Moved(&'static str), Renamed(&'static str), MovedAndRenamed { table: &'static str, key: &'static str, }, + Removed(&'static str), } impl fmt::Display for DeprecationReason { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::WillBeRemoved => f.write_str("the key will be removed in a future update"), - Self::Moved(tab) => write!(f, "the key has been moved to [{tab}]"), - Self::Renamed(nname) => write!(f, "the key has been renamed to '{nname}'"), + Self::WillBeRemoved(url) => { + if let Some(url) = url { + write!( + f, + "this key will be removed in a future update, see {url} for details" + ) + } else { + f.write_str("this key will be removed in a future update") + } + } + Self::Moved(tab) => write!(f, "this key has been moved to [{tab}]"), + Self::Renamed(nname) => write!(f, "this key has been renamed to '{nname}'"), Self::MovedAndRenamed { table, key } => { - write!(f, "the key been moved to [{table}] and renamed to '{key}'") + write!(f, "this key been moved to [{table}] and renamed to '{key}'") + } + Self::Removed(url) => { + write!( + f, + "this key has been removed, see {url} for migration information" + ) } } } diff --git a/src/licenses/cfg.rs b/src/licenses/cfg.rs index 57aa138b..62e0e0df 100644 --- a/src/licenses/cfg.rs +++ b/src/licenses/cfg.rs @@ -22,9 +22,9 @@ //! ``` use crate::{ - cfg::{PackageSpec, ValidationContext}, + cfg::{deprecated, PackageSpec, ValidationContext}, diag::{Diagnostic, FileId, Label}, - LintLevel, PathBuf, Spanned, + LintLevel, PathBuf, Span, Spanned, }; use toml_span::{de_helpers::TableHelper, value::Value, DeserError, Deserialize}; @@ -248,6 +248,7 @@ pub struct Config { /// If true, performs license checks for dev-dependencies for workspace /// crates as well pub include_dev: bool, + deprecated: Vec, } impl Default for Config { @@ -265,6 +266,7 @@ impl Default for Config { clarify: Vec::new(), exceptions: Vec::new(), include_dev: false, + deprecated: Vec::new(), } } } @@ -273,15 +275,18 @@ impl<'de> Deserialize<'de> for Config { fn deserialize(value: &mut Value<'de>) -> Result { let mut th = TableHelper::new(value)?; + let mut fdeps = Vec::new(); + let private = th.optional("private").unwrap_or_default(); - let unlicensed = th.optional("unlicensed").unwrap_or(LintLevel::Deny); - let allow_osi_fsf_free = th.optional("allow-osi-fsf-free").unwrap_or_default(); - let copyleft = th.optional("copyleft").unwrap_or(LintLevel::Warn); - let default = th.optional("default").unwrap_or(LintLevel::Deny); + let unlicensed = deprecated(&mut th, "unlicensed", &mut fdeps).unwrap_or(LintLevel::Deny); + let allow_osi_fsf_free = + deprecated(&mut th, "allow-osi-fsf-free", &mut fdeps).unwrap_or_default(); + let copyleft = deprecated(&mut th, "copyleft", &mut fdeps).unwrap_or(LintLevel::Warn); + let default = deprecated(&mut th, "default", &mut fdeps).unwrap_or(LintLevel::Deny); let confidence_threshold = th .optional("confidence-threshold") .unwrap_or(DEFAULT_CONFIDENCE_THRESHOLD); - let deny = th.optional("deny").unwrap_or_default(); + let deny = deprecated(&mut th, "deny", &mut fdeps).unwrap_or_default(); let allow = th.optional("allow").unwrap_or_default(); let unused_allowed_license = th .optional("unused-allowed-license") @@ -305,6 +310,7 @@ impl<'de> Deserialize<'de> for Config { clarify, exceptions, include_dev, + deprecated: fdeps, }) } } @@ -398,6 +404,23 @@ impl crate::cfg::UnvalidatedConfig for Config { }); } + use crate::diag::general::{Deprecated, DeprecationReason}; + + // Output any deprecations, we'll remove the fields at the same time we + // remove all the logic they drive + for dep in self.deprecated { + ctx.push( + Deprecated { + reason: DeprecationReason::WillBeRemoved(Some( + "https://github.com/EmbarkStudios/cargo-deny/pull/606", + )), + key: dep, + file_id: ctx.cfg_id, + } + .into(), + ); + } + ValidConfig { file_id: ctx.cfg_id, private: self.private, @@ -537,7 +560,13 @@ mod test { #[test] fn deserializes_licenses_cfg() { let cd = ConfigData::::load("tests/cfg/licenses.toml"); - let validated = cd.validate(|l| l.licenses); + let validated = cd.validate_with_diags( + |l| l.licenses, + |files, diags| { + let diags = write_diagnostics(files, diags.into_iter()); + insta::assert_snapshot!(diags); + }, + ); insta::assert_json_snapshot!(validated); } diff --git a/src/licenses/snapshots/cargo_deny__licenses__cfg__test__correct_duplicate_license_spans.snap b/src/licenses/snapshots/cargo_deny__licenses__cfg__test__correct_duplicate_license_spans.snap index 26b04612..d45669c4 100644 --- a/src/licenses/snapshots/cargo_deny__licenses__cfg__test__correct_duplicate_license_spans.snap +++ b/src/licenses/snapshots/cargo_deny__licenses__cfg__test__correct_duplicate_license_spans.snap @@ -11,4 +11,10 @@ error: a license id was specified in both `allow` and `deny` 11 │ "MIT", │ --- deny +warning[deprecated]: this key will be removed in a future update, see https://github.com/EmbarkStudios/cargo-deny/pull/606 for details + ┌─ license-in-allow-and-deny:10:1 + │ +10 │ deny = [ + │ ^^^^ + diff --git a/src/licenses/snapshots/cargo_deny__licenses__cfg__test__deserializes_licenses_cfg-2.snap b/src/licenses/snapshots/cargo_deny__licenses__cfg__test__deserializes_licenses_cfg-2.snap new file mode 100644 index 00000000..b59d7789 --- /dev/null +++ b/src/licenses/snapshots/cargo_deny__licenses__cfg__test__deserializes_licenses_cfg-2.snap @@ -0,0 +1,57 @@ +--- +source: src/licenses/cfg.rs +expression: validated +--- +{ + "file_id": 1, + "private": { + "ignore": true, + "ignore_sources": [], + "registries": [ + "sekrets" + ] + }, + "unlicensed": "warn", + "copyleft": "deny", + "unused_allowed_license": "warn", + "allow_osi_fsf_free": "Both", + "default": "warn", + "confidence_threshold": 0.95, + "denied": [ + "BSD-2-Clause", + "Nokia" + ], + "allowed": [ + "Apache-2.0 WITH LLVM-exception", + "EUPL-1.2" + ], + "clarifications": [ + { + "spec": { + "name": "ring", + "version-req": null + }, + "expression": "MIT AND ISC AND OpenSSL", + "license-files": [ + { + "path": "LICENSE", + "hash": 3171872035 + } + ] + } + ], + "exceptions": [ + { + "spec": { + "name": "adler32", + "version-req": "^0.1.1" + }, + "allowed": [ + "Zlib" + ], + "file_id": 1 + } + ], + "ignore_sources": [], + "include_dev": false +} diff --git a/src/licenses/snapshots/cargo_deny__licenses__cfg__test__deserializes_licenses_cfg.snap b/src/licenses/snapshots/cargo_deny__licenses__cfg__test__deserializes_licenses_cfg.snap index b59d7789..bd710320 100644 --- a/src/licenses/snapshots/cargo_deny__licenses__cfg__test__deserializes_licenses_cfg.snap +++ b/src/licenses/snapshots/cargo_deny__licenses__cfg__test__deserializes_licenses_cfg.snap @@ -1,57 +1,35 @@ --- source: src/licenses/cfg.rs -expression: validated +expression: diags --- -{ - "file_id": 1, - "private": { - "ignore": true, - "ignore_sources": [], - "registries": [ - "sekrets" - ] - }, - "unlicensed": "warn", - "copyleft": "deny", - "unused_allowed_license": "warn", - "allow_osi_fsf_free": "Both", - "default": "warn", - "confidence_threshold": 0.95, - "denied": [ - "BSD-2-Clause", - "Nokia" - ], - "allowed": [ - "Apache-2.0 WITH LLVM-exception", - "EUPL-1.2" - ], - "clarifications": [ - { - "spec": { - "name": "ring", - "version-req": null - }, - "expression": "MIT AND ISC AND OpenSSL", - "license-files": [ - { - "path": "LICENSE", - "hash": 3171872035 - } - ] - } - ], - "exceptions": [ - { - "spec": { - "name": "adler32", - "version-req": "^0.1.1" - }, - "allowed": [ - "Zlib" - ], - "file_id": 1 - } - ], - "ignore_sources": [], - "include_dev": false -} +warning[deprecated]: this key will be removed in a future update, see https://github.com/EmbarkStudios/cargo-deny/pull/606 for details + ┌─ tests/cfg/licenses.toml:2:1 + │ +2 │ unlicensed = "warn" + │ ^^^^^^^^^^ + +warning[deprecated]: this key will be removed in a future update, see https://github.com/EmbarkStudios/cargo-deny/pull/606 for details + ┌─ tests/cfg/licenses.toml:3:1 + │ +3 │ allow-osi-fsf-free = "both" + │ ^^^^^^^^^^^^^^^^^^ + +warning[deprecated]: this key will be removed in a future update, see https://github.com/EmbarkStudios/cargo-deny/pull/606 for details + ┌─ tests/cfg/licenses.toml:4:1 + │ +4 │ copyleft = "deny" + │ ^^^^^^^^ + +warning[deprecated]: this key will be removed in a future update, see https://github.com/EmbarkStudios/cargo-deny/pull/606 for details + ┌─ tests/cfg/licenses.toml:5:1 + │ +5 │ default = "warn" + │ ^^^^^^^ + +warning[deprecated]: this key will be removed in a future update, see https://github.com/EmbarkStudios/cargo-deny/pull/606 for details + ┌─ tests/cfg/licenses.toml:8:1 + │ +8 │ deny = [ + │ ^^^^ + +