From 545b72f70e98ffeb141d57f4d2b1e00d50ba18b5 Mon Sep 17 00:00:00 2001 From: m Date: Tue, 28 May 2024 21:44:48 -0700 Subject: [PATCH 1/3] add initial lint level config --- src/check_release.rs | 295 ++++++++++-------- src/config.rs | 38 ++- src/lib.rs | 2 +- src/lints/auto_trait_impl_removed.ron | 1 + src/lints/constructible_struct_adds_field.ron | 1 + ...onstructible_struct_adds_private_field.ron | 1 + .../constructible_struct_changed_type.ron | 1 + src/lints/derive_trait_impl_removed.ron | 1 + src/lints/enum_marked_non_exhaustive.ron | 1 + src/lints/enum_missing.ron | 1 + src/lints/enum_must_use_added.ron | 1 + src/lints/enum_now_doc_hidden.ron | 1 + src/lints/enum_repr_int_changed.ron | 1 + src/lints/enum_repr_int_removed.ron | 1 + src/lints/enum_repr_transparent_removed.ron | 1 + src/lints/enum_struct_variant_field_added.ron | 1 + .../enum_struct_variant_field_missing.ron | 1 + ...um_struct_variant_field_now_doc_hidden.ron | 1 + src/lints/enum_tuple_variant_field_added.ron | 1 + .../enum_tuple_variant_field_missing.ron | 1 + ...num_tuple_variant_field_now_doc_hidden.ron | 1 + src/lints/enum_variant_added.ron | 1 + src/lints/enum_variant_missing.ron | 1 + src/lints/exported_function_changed_abi.ron | 1 + src/lints/function_abi_no_longer_unwind.ron | 1 + src/lints/function_changed_abi.ron | 1 + src/lints/function_const_removed.ron | 1 + src/lints/function_export_name_changed.ron | 1 + src/lints/function_missing.ron | 1 + src/lints/function_must_use_added.ron | 1 + src/lints/function_now_doc_hidden.ron | 1 + .../function_parameter_count_changed.ron | 1 + src/lints/function_unsafe_added.ron | 1 + .../inherent_associated_pub_const_missing.ron | 1 + src/lints/inherent_method_const_removed.ron | 1 + src/lints/inherent_method_missing.ron | 1 + src/lints/inherent_method_must_use_added.ron | 1 + src/lints/inherent_method_unsafe_added.ron | 1 + src/lints/method_parameter_count_changed.ron | 1 + src/lints/module_missing.ron | 1 + src/lints/pub_module_level_const_missing.ron | 1 + .../pub_module_level_const_now_doc_hidden.ron | 1 + src/lints/pub_static_missing.ron | 1 + src/lints/pub_static_mut_now_immutable.ron | 1 + src/lints/pub_static_now_doc_hidden.ron | 1 + src/lints/repr_c_removed.ron | 1 + src/lints/repr_packed_added.ron | 1 + src/lints/repr_packed_removed.ron | 1 + src/lints/sized_impl_removed.ron | 1 + src/lints/struct_marked_non_exhaustive.ron | 1 + src/lints/struct_missing.ron | 1 + src/lints/struct_must_use_added.ron | 1 + src/lints/struct_now_doc_hidden.ron | 1 + src/lints/struct_pub_field_missing.ron | 1 + src/lints/struct_pub_field_now_doc_hidden.ron | 1 + src/lints/struct_repr_transparent_removed.ron | 1 + .../struct_with_pub_fields_changed_type.ron | 1 + .../trait_associated_const_now_doc_hidden.ron | 1 + .../trait_associated_type_now_doc_hidden.ron | 1 + src/lints/trait_method_missing.ron | 1 + src/lints/trait_method_now_doc_hidden.ron | 1 + src/lints/trait_method_unsafe_added.ron | 1 + src/lints/trait_method_unsafe_removed.ron | 1 + src/lints/trait_missing.ron | 1 + src/lints/trait_must_use_added.ron | 1 + src/lints/trait_no_longer_object_safe.ron | 1 + src/lints/trait_now_doc_hidden.ron | 1 + .../trait_removed_associated_constant.ron | 1 + src/lints/trait_removed_associated_type.ron | 1 + src/lints/trait_removed_supertrait.ron | 1 + src/lints/trait_unsafe_added.ron | 1 + src/lints/trait_unsafe_removed.ron | 1 + src/lints/tuple_struct_to_plain_struct.ron | 1 + src/lints/type_marked_deprecated.ron | 1 + src/lints/union_field_missing.ron | 5 +- src/lints/union_missing.ron | 1 + src/lints/union_now_doc_hidden.ron | 1 + src/lints/unit_struct_changed_kind.ron | 1 + src/lints/variant_marked_non_exhaustive.ron | 1 + src/main.rs | 11 +- src/query.rs | 46 ++- 81 files changed, 328 insertions(+), 144 deletions(-) diff --git a/src/check_release.rs b/src/check_release.rs index 1d6ddccf..e92dbe18 100644 --- a/src/check_release.rs +++ b/src/check_release.rs @@ -7,11 +7,11 @@ use anyhow::Context; use clap::crate_version; use itertools::Itertools; use rayon::prelude::*; -use trustfall::TransparentValue; +use trustfall::{FieldValue, TransparentValue}; use trustfall_rustdoc::{VersionedCrate, VersionedIndexedCrate, VersionedRustdocAdapter}; use crate::{ - query::{ActualSemverUpdate, RequiredSemverUpdate, SemverQuery}, + query::{ActualSemverUpdate, LintLevel, RequiredSemverUpdate, SemverQuery}, CrateReport, GlobalConfig, ReleaseType, }; @@ -64,6 +64,96 @@ fn classify_semver_version_change( } } +fn print_lint_failure( + config: &mut GlobalConfig, + semver_query: &SemverQuery, + results: Vec, FieldValue>>, +) -> anyhow::Result<()> { + if let Some(ref_link) = semver_query.reference_link.as_deref() { + config.log_info(|config| { + writeln!(config.stdout(), "{}Description:{}\n{}\n{:>12} {}\n{:>12} https://github.com/obi1kenobi/cargo-semver-checks/tree/v{}/src/lints/{}.ron\n", + Style::new().bold(), Reset, + &semver_query.error_message, + "ref:", + ref_link, + "impl:", + crate_version!(), + semver_query.id, + )?; + Ok(()) + })?; + } else { + config.log_info(|config| { + writeln!( + config.stdout(), + "{}Description:{}\n{}\n{:>12} https://github.com/obi1kenobi/cargo-semver-checks/tree/v{}/src/lints/{}.ron", + Style::new().bold(), + Reset, + &semver_query.error_message, + "impl:", + crate_version!(), + semver_query.id, + )?; + Ok(()) + })?; + } + + config.log_info(|config| { + writeln!( + config.stdout(), + "{}Failed in:{}", + Style::new().bold(), + Reset + )?; + Ok(()) + })?; + + for semver_violation_result in results { + let pretty_result: BTreeMap, TransparentValue> = semver_violation_result + .into_iter() + .map(|(k, v)| (k, v.into())) + .collect(); + + if let Some(template) = semver_query.per_result_error_template.as_deref() { + let message = config + .handlebars() + .render_template(template, &pretty_result) + .context("Error instantiating semver query template.") + .expect("could not materialize template"); + config.log_info(|config| { + writeln!(config.stdout(), " {}", message)?; + Ok(()) + })?; + + config.log_extra_verbose(|config| { + let serde_pretty = + serde_json::to_string_pretty(&pretty_result).expect("serde failed"); + let indented_serde = serde_pretty + .split('\n') + .map(|line| format!(" {line}")) + .join("\n"); + writeln!( + config.stdout(), + "\tlint rule output values:\n{}", + indented_serde + )?; + Ok(()) + })?; + } else { + config.log_info(|config| { + writeln!( + config.stdout(), + "{}\n", + serde_json::to_string_pretty(&pretty_result)? + )?; + Ok(()) + })?; + } + } + + Ok(()) +} + pub(super) fn run_check_release( config: &mut GlobalConfig, crate_name: &str, @@ -71,6 +161,8 @@ pub(super) fn run_check_release( baseline_crate: VersionedCrate, release_type: Option, ) -> anyhow::Result { + let queries = config.all_queries()?; + let current_version = current_crate.crate_version(); let baseline_version = baseline_crate.crate_version(); @@ -100,9 +192,10 @@ pub(super) fn run_check_release( let previous = VersionedIndexedCrate::new(&baseline_crate); let adapter = VersionedRustdocAdapter::new(¤t, Some(&previous))?; - let (queries_to_run, queries_to_skip): (Vec<_>, _) = SemverQuery::all_queries() - .into_values() - .partition(|query| !version_change.supports_requirement(query.required_update)); + let (queries_to_run, queries_to_skip): (Vec<_>, _) = queries.values().partition(|query| { + !version_change.supports_requirement(query.required_update) + && query.lint_level >= LintLevel::Warn + }); let skipped_queries = queries_to_skip.len(); config.shell_status( @@ -155,7 +248,8 @@ pub(super) fn run_check_release( }) .collect::>>()?; - let mut results_with_errors = vec![]; + let mut error_results = vec![]; + let mut warning_results = vec![]; for (semver_query, time_to_decide, results) in all_results { config .log_verbose(|config| { @@ -194,11 +288,16 @@ pub(super) fn run_check_release( }) .expect("print failed"); if !results.is_empty() { - results_with_errors.push((semver_query, results)); + if semver_query.lint_level == LintLevel::Deny { + error_results.push((semver_query, results)); + } else { + warning_results.push((semver_query, results)); + } } } - if !results_with_errors.is_empty() { + let failed_checks = error_results.len() + warning_results.len(); + if failed_checks > 0 { config .shell_print( "Checked", @@ -206,8 +305,8 @@ pub(super) fn run_check_release( "[{:>8.3}s] {} checks; {} passed, {} failed, {} unnecessary", queries_start_instant.elapsed().as_secs_f32(), queries_to_run.len(), - queries_to_run.len() - results_with_errors.len(), - results_with_errors.len(), + queries_to_run.len() - failed_checks, + failed_checks, skipped_queries, ), Color::Ansi(AnsiColor::Red), @@ -215,145 +314,73 @@ pub(super) fn run_check_release( ) .expect("print failed"); - let mut required_versions = vec![]; - - for (semver_query, results) in results_with_errors { - required_versions.push(semver_query.required_update); - config - .log_info(|config| { - writeln!( - config.stdout(), - "\n--- failure {}: {} ---\n", - &semver_query.id, - &semver_query.human_readable_name - )?; - Ok(()) - }) - .expect("print failed"); - - if let Some(ref_link) = semver_query.reference_link.as_deref() { - config.log_info(|config| { - writeln!(config.stdout(), "{}Description:{}\n{}\n{:>12} {}\n{:>12} https://github.com/obi1kenobi/cargo-semver-checks/tree/v{}/src/lints/{}.ron\n", - Style::new().bold(), Reset, - &semver_query.error_message, - "ref:", - ref_link, - "impl:", - crate_version!(), - semver_query.id, - )?; - Ok(()) - }) - .expect("print failed"); - } else { - config.log_info(|config| { - writeln!( - config.stdout(), - "{}Description:{}\n{}\n{:>12} https://github.com/obi1kenobi/cargo-semver-checks/tree/v{}/src/lints/{}.ron", - Style::new().bold(), - Reset, - &semver_query.error_message, - "impl:", - crate_version!(), - semver_query.id, - )?; - Ok(()) - }) - .expect("print failed"); - } + let mut warnings = BTreeMap::new(); + let mut errors = BTreeMap::new(); + // print errors before warnings like clippy does + for (semver_query, results) in warning_results { + warnings + .entry(semver_query.required_update) + .and_modify(|e| *e += 1) + .or_insert(1); + + config.shell_warn(format!( + "{}: {}", + &semver_query.id, &semver_query.human_readable_name + ))?; + print_lint_failure(config, semver_query, results)?; + } - config - .log_info(|config| { - writeln!( - config.stdout(), - "{}Failed in:{}", - Style::new().bold(), - Reset - )?; - Ok(()) - }) - .expect("print failed"); + for (semver_query, results) in error_results { + errors + .entry(semver_query.required_update) + .and_modify(|e| *e += 1) + .or_insert(1); + + config.shell_error(format!( + "{}: {}", + &semver_query.id, &semver_query.human_readable_name + ))?; + print_lint_failure(config, semver_query, results)?; + } - for semver_violation_result in results { - let pretty_result: BTreeMap, TransparentValue> = semver_violation_result - .into_iter() - .map(|(k, v)| (k, v.into())) - .collect(); - - if let Some(template) = semver_query.per_result_error_template.as_deref() { - let message = config - .handlebars() - .render_template(template, &pretty_result) - .context("Error instantiating semver query template.") - .expect("could not materialize template"); - config - .log_info(|config| { - writeln!(config.stdout(), " {}", message)?; - Ok(()) - }) - .expect("print failed"); - - config - .log_extra_verbose(|config| { - let serde_pretty = - serde_json::to_string_pretty(&pretty_result).expect("serde failed"); - let indented_serde = serde_pretty - .split('\n') - .map(|line| format!(" {line}")) - .join("\n"); - writeln!( - config.stdout(), - "\tlint rule output values:\n{}", - indented_serde - )?; - Ok(()) - }) - .expect("print failed"); - } else { - config - .log_info(|config| { - writeln!( - config.stdout(), - "{}\n", - serde_json::to_string_pretty(&pretty_result)? - )?; - Ok(()) - }) - .expect("print failed"); + let required_bump = errors.last_key_value().map(|x| *x.0); + let suggested_bump = warnings.last_key_value().map(|x| *x.0); + + if let Some(suggested_bump) = suggested_bump { + config.shell_warn(format!( + "generated {} major- and {} minor-version warnings", + warnings.get(&RequiredSemverUpdate::Major).unwrap_or(&0), + warnings.get(&RequiredSemverUpdate::Minor).unwrap_or(&0), + ))?; + match required_bump { + // no need to warn about warnings' version bump if it is the + // same or less extreme than the required version bump + Some(x) if x >= suggested_bump => {} + _ => { + config.shell_warn(format!( + "warnings suggest a new {} version", + suggested_bump.as_str() + ))?; } } } - let required_bump = if required_versions.contains(&RequiredSemverUpdate::Major) { - RequiredSemverUpdate::Major - } else if required_versions.contains(&RequiredSemverUpdate::Minor) { - RequiredSemverUpdate::Minor - } else { - unreachable!("{:?}", required_versions) - }; - - config - .shell_print( + if let Some(required_bump) = required_bump { + config.shell_print( "Summary", format_args!( "semver requires new {} version: {} major and {} minor checks failed", required_bump.as_str(), - required_versions - .iter() - .filter(|x| *x == &RequiredSemverUpdate::Major) - .count(), - required_versions - .iter() - .filter(|x| *x == &RequiredSemverUpdate::Minor) - .count(), + errors.get(&RequiredSemverUpdate::Major).unwrap_or(&0), + errors.get(&RequiredSemverUpdate::Minor).unwrap_or(&0), ), Color::Ansi(AnsiColor::Red), true, - ) - .expect("print failed"); + )?; + } Ok(CrateReport { - required_bump: Some(required_bump.into()), + required_bump: required_bump.map(Into::into), detected_bump: version_change, }) } else { diff --git a/src/config.rs b/src/config.rs index 3ee52795..73084ab1 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,8 @@ use anstream::{AutoStream, ColorChoice}; use anstyle::{AnsiColor, Color, Reset, Style}; -use std::io::Write; +use std::{collections::BTreeMap, io::Write}; -use crate::templating::make_handlebars_registry; +use crate::{query::QueryOverride, templating::make_handlebars_registry, SemverQuery}; #[allow(dead_code)] pub struct GlobalConfig { @@ -14,6 +14,9 @@ pub struct GlobalConfig { minimum_rustc_version: semver::Version, stdout: AutoStream>, stderr: AutoStream>, + /// A mapping of lint names to values to override that lint's defaults, + /// such as its lint level and required semver bump. + query_overrides: BTreeMap, } impl Default for GlobalConfig { @@ -38,6 +41,7 @@ impl GlobalConfig { minimum_rustc_version: semver::Version::new(1, 74, 0), stdout: AutoStream::new(Box::new(std::io::stdout()), stdout_choice), stderr: AutoStream::new(Box::new(std::io::stderr()), stderr_choice), + query_overrides: BTreeMap::new(), } } @@ -149,6 +153,10 @@ impl GlobalConfig { self.shell_print("warning", message, Color::Ansi(AnsiColor::Yellow), false) } + pub fn shell_error(&mut self, message: impl std::fmt::Display) -> anyhow::Result<()> { + self.shell_print("error", message, Color::Ansi(AnsiColor::Red), false) + } + /// Gets the color-supporting `stdout` that the crate will use. /// /// See [`GlobalConfig::set_stdout`] and [`GlobalConfig::set_out_color_choice`] to @@ -280,6 +288,32 @@ impl GlobalConfig { ColorChoice::Never | ColorChoice::Auto => false, } } + + pub fn set_query_overrides( + &mut self, + query_overrides: BTreeMap, + ) -> &mut Self { + self.query_overrides = query_overrides; + self + } + + #[must_use] + pub fn query_overrides(&self) -> &BTreeMap { + &self.query_overrides + } + + #[must_use] + pub fn all_queries(&self) -> anyhow::Result> { + let mut queries = SemverQuery::all_queries(); + for (name, overrides) in &self.query_overrides { + if let Some(query) = queries.get_mut(name) { + query.apply_override(overrides); + } else { + anyhow::bail!("Can't configure lint with unknown name `{name}`."); + } + } + Ok(queries) + } } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index 2ce2523f..6d3e5a21 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ use std::path::{Path, PathBuf}; use std::time::Instant; pub use config::GlobalConfig; -pub use query::{ActualSemverUpdate, RequiredSemverUpdate, SemverQuery}; +pub use query::{ActualSemverUpdate, LintLevel, QueryOverride, RequiredSemverUpdate, SemverQuery}; /// Test a release for semver violations. #[non_exhaustive] diff --git a/src/lints/auto_trait_impl_removed.ron b/src/lints/auto_trait_impl_removed.ron index 685ad293..426d18a5 100644 --- a/src/lints/auto_trait_impl_removed.ron +++ b/src/lints/auto_trait_impl_removed.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "auto trait no longer implemented", description: "A type has stopped implementing one or more auto traits.", required_update: Major, + lint_level: Deny, // TODO: Add a better reference link once the cargo semver reference has a section on auto traits. reference_link: Some("https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits"), query: r#" diff --git a/src/lints/constructible_struct_adds_field.ron b/src/lints/constructible_struct_adds_field.ron index 4e49607d..59ca17a6 100644 --- a/src/lints/constructible_struct_adds_field.ron +++ b/src/lints/constructible_struct_adds_field.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "externally-constructible struct adds field", description: "A struct constructible with a struct literal added a new pub field.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/reference/expressions/struct-expr.html"), query: r#" { diff --git a/src/lints/constructible_struct_adds_private_field.ron b/src/lints/constructible_struct_adds_private_field.ron index 6ab9b454..615f64e4 100644 --- a/src/lints/constructible_struct_adds_private_field.ron +++ b/src/lints/constructible_struct_adds_private_field.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "struct no longer constructible due to new private field", description: "A struct is no longer constructible with a struct literal due to a new private field.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/reference/expressions/struct-expr.html"), query: r#" { diff --git a/src/lints/constructible_struct_changed_type.ron b/src/lints/constructible_struct_changed_type.ron index 49d98045..4d36104b 100644 --- a/src/lints/constructible_struct_changed_type.ron +++ b/src/lints/constructible_struct_changed_type.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "struct constructible with literal became an enum or union", description: "A struct was converted into an enum or union, breaking struct literals.", required_update: Major, + lint_level: Deny, reference_link: Some("https://github.com/obi1kenobi/cargo-semver-checks/issues/297#issuecomment-1399099659"), reference: Some( r#"\ diff --git a/src/lints/derive_trait_impl_removed.ron b/src/lints/derive_trait_impl_removed.ron index 034aed92..678bbc00 100644 --- a/src/lints/derive_trait_impl_removed.ron +++ b/src/lints/derive_trait_impl_removed.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "built-in derived trait no longer implemented", description: "A type has stopped implementing a built-in trait that used to be derived.", required_update: Major, + lint_level: Deny, // TODO: Find a better reference than the definition of #[derive(...)]. // The cargo semver reference doesn't say that no longer deriving a pub trait is breaking. reference_link: Some("https://doc.rust-lang.org/reference/attributes/derive.html#derive"), diff --git a/src/lints/enum_marked_non_exhaustive.ron b/src/lints/enum_marked_non_exhaustive.ron index fa1d7087..41d7e161 100644 --- a/src/lints/enum_marked_non_exhaustive.ron +++ b/src/lints/enum_marked_non_exhaustive.ron @@ -4,6 +4,7 @@ SemverQuery( description: "An exhaustive enum has been marked #[non_exhaustive].", reference: Some("An exhaustive enum has been marked #[non_exhaustive]. Pattern-matching on it outside of its crate must now include a wildcard pattern like `_`, or it will fail to compile."), required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#attr-adding-non-exhaustive"), query: r#" { diff --git a/src/lints/enum_missing.ron b/src/lints/enum_missing.ron index 042b9f6b..ad0ac327 100644 --- a/src/lints/enum_missing.ron +++ b/src/lints/enum_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub enum removed or renamed", description: "An enum can no longer be imported by its prior path.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/enum_must_use_added.ron b/src/lints/enum_must_use_added.ron index e4ebcf90..9581d3d3 100644 --- a/src/lints/enum_must_use_added.ron +++ b/src/lints/enum_must_use_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "enum #[must_use] added", description: "An enum has been marked with #[must_use].", required_update: Minor, + lint_level: Deny, // TODO: Change the reference link to point to the cargo semver reference // once it has a section on attribute #[must_use]. diff --git a/src/lints/enum_now_doc_hidden.ron b/src/lints/enum_now_doc_hidden.ron index 3abb7dc2..561cce65 100644 --- a/src/lints/enum_now_doc_hidden.ron +++ b/src/lints/enum_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub enum is now #[doc(hidden)]", description: "A pub enum is now marked #[doc(hidden)] and is thus no longer part of the public API.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/enum_repr_int_changed.ron b/src/lints/enum_repr_int_changed.ron index 653f30bc..431c687f 100644 --- a/src/lints/enum_repr_int_changed.ron +++ b/src/lints/enum_repr_int_changed.ron @@ -4,6 +4,7 @@ SemverQuery( description: "An enum's repr attribute changed integer types.", reference: Some("The repr(u*) or repr(i*) attribute on an enum was changed to another integer type. This can cause its memory representation to change, breaking FFI use cases."), required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#repr-int-enum-change"), query: r#" diff --git a/src/lints/enum_repr_int_removed.ron b/src/lints/enum_repr_int_removed.ron index 8244efc0..9f68f3d6 100644 --- a/src/lints/enum_repr_int_removed.ron +++ b/src/lints/enum_repr_int_removed.ron @@ -4,6 +4,7 @@ SemverQuery( description: "An enum's repr attribute was removed.", reference: Some("The repr(u*) or repr(i*) attribute was removed from an enum. This can cause its memory representation to change, breaking FFI use cases."), required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#repr-int-enum-remove"), query: r#" diff --git a/src/lints/enum_repr_transparent_removed.ron b/src/lints/enum_repr_transparent_removed.ron index 1a00ebe9..3cc45b70 100644 --- a/src/lints/enum_repr_transparent_removed.ron +++ b/src/lints/enum_repr_transparent_removed.ron @@ -23,6 +23,7 @@ To avoid false-positives, this query is restricted to checking only enums that i - which has at most one non-PhantomData field "#), required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#repr-transparent-remove"), query: r#" diff --git a/src/lints/enum_struct_variant_field_added.ron b/src/lints/enum_struct_variant_field_added.ron index d8069b63..3d42194a 100644 --- a/src/lints/enum_struct_variant_field_added.ron +++ b/src/lints/enum_struct_variant_field_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub enum struct variant field added", description: "An enum's exhaustive struct variant has a new field.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute"), query: r#" { diff --git a/src/lints/enum_struct_variant_field_missing.ron b/src/lints/enum_struct_variant_field_missing.ron index af106239..5e4c3f0f 100644 --- a/src/lints/enum_struct_variant_field_missing.ron +++ b/src/lints/enum_struct_variant_field_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub enum struct variant's field removed or renamed" , description: "An enum's struct variant has a field that is no longer available under its prior name.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/enum_struct_variant_field_now_doc_hidden.ron b/src/lints/enum_struct_variant_field_now_doc_hidden.ron index 5713c002..544579af 100644 --- a/src/lints/enum_struct_variant_field_now_doc_hidden.ron +++ b/src/lints/enum_struct_variant_field_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub enum struct variant's field is now #[doc(hidden)]", description: "An enum's struct variant has a field that is now marked #[doc(hidden)] and is thus no longer part of the public API.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/enum_tuple_variant_field_added.ron b/src/lints/enum_tuple_variant_field_added.ron index 53eb0bd3..a9317d64 100644 --- a/src/lints/enum_tuple_variant_field_added.ron +++ b/src/lints/enum_tuple_variant_field_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub enum tuple variant field added", description: "An enum's exhaustive tuple variant has a new field.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute"), query: r#" { diff --git a/src/lints/enum_tuple_variant_field_missing.ron b/src/lints/enum_tuple_variant_field_missing.ron index 4721ec3b..f00db828 100644 --- a/src/lints/enum_tuple_variant_field_missing.ron +++ b/src/lints/enum_tuple_variant_field_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub enum tuple variant's field removed", description: "A field has been removed from an enum's tuple variant.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/enum_tuple_variant_field_now_doc_hidden.ron b/src/lints/enum_tuple_variant_field_now_doc_hidden.ron index f3ff2c93..f4dac49b 100644 --- a/src/lints/enum_tuple_variant_field_now_doc_hidden.ron +++ b/src/lints/enum_tuple_variant_field_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub enum tuple variant field is now #[doc(hidden)]", description: "A pub enum tuple variant field is now marked #[doc(hidden)] and is thus no longer part of the public API.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/enum_variant_added.ron b/src/lints/enum_variant_added.ron index a40e73a7..df332d49 100644 --- a/src/lints/enum_variant_added.ron +++ b/src/lints/enum_variant_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "enum variant added on exhaustive enum", description: "An exhaustive enum has a new variant.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#enum-variant-new"), query: r#" { diff --git a/src/lints/enum_variant_missing.ron b/src/lints/enum_variant_missing.ron index 72e6e298..258fc630 100644 --- a/src/lints/enum_variant_missing.ron +++ b/src/lints/enum_variant_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub enum variant removed or renamed", description: "An enum variant is no longer available under its prior name.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/exported_function_changed_abi.ron b/src/lints/exported_function_changed_abi.ron index ff561f4f..227ba812 100644 --- a/src/lints/exported_function_changed_abi.ron +++ b/src/lints/exported_function_changed_abi.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "exported function changed ABI", description: "A function marked `#[no_mangle]` or assigned an explicit `#[export_name]` changed its external ABI.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/reference/items/external-blocks.html#abi"), query: r#"{ CrateDiff { diff --git a/src/lints/function_abi_no_longer_unwind.ron b/src/lints/function_abi_no_longer_unwind.ron index 5b6ea93b..7c35d395 100644 --- a/src/lints/function_abi_no_longer_unwind.ron +++ b/src/lints/function_abi_no_longer_unwind.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "function abi no longer unwind", description: "A pub fn changed from an unwind-capable ABI to the same-named ABI without unwind. If that function causes an unwind (e.g. by panicking), its behavior is now undefined.", required_update: Major, + lint_level: Deny, reference_link: Some("https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html"), query: r#" { diff --git a/src/lints/function_changed_abi.ron b/src/lints/function_changed_abi.ron index 57efe147..b3f86bde 100644 --- a/src/lints/function_changed_abi.ron +++ b/src/lints/function_changed_abi.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub function changed ABI", description: "A public function changed its external ABI.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/reference/items/external-blocks.html#abi"), query: r#" { diff --git a/src/lints/function_const_removed.ron b/src/lints/function_const_removed.ron index 7b8c9c09..cd9a1d02 100644 --- a/src/lints/function_const_removed.ron +++ b/src/lints/function_const_removed.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub fn is no longer const", description: "A function can no longer be called in a const context.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/reference/const_eval.html"), query: r#" { diff --git a/src/lints/function_export_name_changed.ron b/src/lints/function_export_name_changed.ron index cae43cb9..35753aeb 100644 --- a/src/lints/function_export_name_changed.ron +++ b/src/lints/function_export_name_changed.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "function's export name has changed or been removed", description: "A function's ABI name with #[no_mangle] or #[export_name = \"name\"] has changed or been removed", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute"), query: r#" { diff --git a/src/lints/function_missing.ron b/src/lints/function_missing.ron index 2f19116e..53ca3b10 100644 --- a/src/lints/function_missing.ron +++ b/src/lints/function_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub fn removed or renamed", description: "A function can no longer be imported by its prior path.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/function_must_use_added.ron b/src/lints/function_must_use_added.ron index a0fa50f0..d2624340 100644 --- a/src/lints/function_must_use_added.ron +++ b/src/lints/function_must_use_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "function #[must_use] added", description: "A function has been marked with #[must_use].", required_update: Minor, + lint_level: Deny, // TODO: Change the reference link to point to the cargo semver reference // once it has a section on attribute #[must_use]. diff --git a/src/lints/function_now_doc_hidden.ron b/src/lints/function_now_doc_hidden.ron index d08f4579..021f2bbd 100644 --- a/src/lints/function_now_doc_hidden.ron +++ b/src/lints/function_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub function is now #[doc(hidden)]", description: "A pub function is now marked #[doc(hidden)] and is thus no longer part of the public API.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/function_parameter_count_changed.ron b/src/lints/function_parameter_count_changed.ron index 9259f27c..ab1c4e24 100644 --- a/src/lints/function_parameter_count_changed.ron +++ b/src/lints/function_parameter_count_changed.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub fn parameter count changed", description: "Parameter count of a function has changed.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#fn-change-arity"), query: r#" { diff --git a/src/lints/function_unsafe_added.ron b/src/lints/function_unsafe_added.ron index c70809b7..f594d7bf 100644 --- a/src/lints/function_unsafe_added.ron +++ b/src/lints/function_unsafe_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub fn became unsafe", description: "A function became unsafe to call.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#calling-an-unsafe-function-or-method"), query: r#" { diff --git a/src/lints/inherent_associated_pub_const_missing.ron b/src/lints/inherent_associated_pub_const_missing.ron index b72ffb8f..ad9dc89e 100644 --- a/src/lints/inherent_associated_pub_const_missing.ron +++ b/src/lints/inherent_associated_pub_const_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "inherent impl's associated pub const removed", description: "An inherent impl's associated public const removed or renamed", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/inherent_method_const_removed.ron b/src/lints/inherent_method_const_removed.ron index aea41110..3b8c6604 100644 --- a/src/lints/inherent_method_const_removed.ron +++ b/src/lints/inherent_method_const_removed.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub method is no longer const", description: "A method or associated fn can no longer be called in a const context.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/reference/const_eval.html"), query: r#" { diff --git a/src/lints/inherent_method_missing.ron b/src/lints/inherent_method_missing.ron index 7eca6b8b..a143900e 100644 --- a/src/lints/inherent_method_missing.ron +++ b/src/lints/inherent_method_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub method removed or renamed", description: "A method or associated fn is no longer available under its prior name.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/inherent_method_must_use_added.ron b/src/lints/inherent_method_must_use_added.ron index 04362511..46a850a3 100644 --- a/src/lints/inherent_method_must_use_added.ron +++ b/src/lints/inherent_method_must_use_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "inherent method #[must_use] added", description: "An inherent method or associated fn has been marked #[must_use].", required_update: Minor, + lint_level: Deny, // TODO: Change the reference link to point to the cargo semver reference // once it has a section on attribute #[must_use]. diff --git a/src/lints/inherent_method_unsafe_added.ron b/src/lints/inherent_method_unsafe_added.ron index c8baee92..6a6b0859 100644 --- a/src/lints/inherent_method_unsafe_added.ron +++ b/src/lints/inherent_method_unsafe_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub method became unsafe", description: "A method or associated fn became unsafe to call.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#calling-an-unsafe-function-or-method"), query: r#" { diff --git a/src/lints/method_parameter_count_changed.ron b/src/lints/method_parameter_count_changed.ron index abe5fc13..a6df57e9 100644 --- a/src/lints/method_parameter_count_changed.ron +++ b/src/lints/method_parameter_count_changed.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub method parameter count changed", description: "Parameter count of a method has changed.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#fn-change-arity"), query: r#" { diff --git a/src/lints/module_missing.ron b/src/lints/module_missing.ron index d5c1e112..8122341f 100644 --- a/src/lints/module_missing.ron +++ b/src/lints/module_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub module removed or renamed", description: "A module can no longer be imported by its prior path", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/pub_module_level_const_missing.ron b/src/lints/pub_module_level_const_missing.ron index 13c245d5..d3cb9045 100644 --- a/src/lints/pub_module_level_const_missing.ron +++ b/src/lints/pub_module_level_const_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub module-level const is missing", description: "A pub const is missing, renamed, or changed to static.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/pub_module_level_const_now_doc_hidden.ron b/src/lints/pub_module_level_const_now_doc_hidden.ron index 6727cc6d..b38e2b57 100644 --- a/src/lints/pub_module_level_const_now_doc_hidden.ron +++ b/src/lints/pub_module_level_const_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub module-level const is now #[doc(hidden)]", description: "A pub const is now marked #[doc(hidden)] and is thus no longer part of the public API.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/pub_static_missing.ron b/src/lints/pub_static_missing.ron index a1bf856e..345e36cb 100644 --- a/src/lints/pub_static_missing.ron +++ b/src/lints/pub_static_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub static is missing", description: "A pub static is missing, renamed, or made private", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/pub_static_mut_now_immutable.ron b/src/lints/pub_static_mut_now_immutable.ron index c0676175..e6df0fe7 100644 --- a/src/lints/pub_static_mut_now_immutable.ron +++ b/src/lints/pub_static_mut_now_immutable.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub static mut is now immutable", description: "A mutable static became immutable and thus can no longer be assigned to", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/reference/items/static-items.html"), query: r#" { diff --git a/src/lints/pub_static_now_doc_hidden.ron b/src/lints/pub_static_now_doc_hidden.ron index 7e6fc07b..c63f1e5e 100644 --- a/src/lints/pub_static_now_doc_hidden.ron +++ b/src/lints/pub_static_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub static is now #[doc(hidden)]", description: "A pub static is now marked #[doc(hidden)] and is thus no longer part of the public API.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/repr_c_removed.ron b/src/lints/repr_c_removed.ron index 4db42a3f..76ee4c6c 100644 --- a/src/lints/repr_c_removed.ron +++ b/src/lints/repr_c_removed.ron @@ -4,6 +4,7 @@ SemverQuery( description: "A type that used to be repr(C) is no longer repr(C).", reference: Some("A type that used to be repr(C) is no longer repr(C). This can cause its memory layout to change, breaking FFI use cases."), required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#repr-c-remove"), query: r#" diff --git a/src/lints/repr_packed_added.ron b/src/lints/repr_packed_added.ron index 0177a29f..2ff7f7f2 100644 --- a/src/lints/repr_packed_added.ron +++ b/src/lints/repr_packed_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "repr(packed) added", description: "A struct or union has been marked with #[repr(packed)].", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#repr-packed-add"), query: r#" { diff --git a/src/lints/repr_packed_removed.ron b/src/lints/repr_packed_removed.ron index 978bbeb2..c2889f97 100644 --- a/src/lints/repr_packed_removed.ron +++ b/src/lints/repr_packed_removed.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "repr(packed) removed", description: "A struct or union that used to be #[repr(packed)] is no longer #[repr(packed)].", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#repr-packed-remove"), query: r#" { diff --git a/src/lints/sized_impl_removed.ron b/src/lints/sized_impl_removed.ron index f1a0136f..daa3b1c6 100644 --- a/src/lints/sized_impl_removed.ron +++ b/src/lints/sized_impl_removed.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "Sized no longer implemented", description: "A type is no longer `Sized`.", required_update: Major, + lint_level: Deny, // TODO: Add a better reference link once the cargo semver reference has a section on Sized. reference_link: Some("https://doc.rust-lang.org/reference/special-types-and-traits.html#sized"), query: r#" diff --git a/src/lints/struct_marked_non_exhaustive.ron b/src/lints/struct_marked_non_exhaustive.ron index 88603abc..f4e3666b 100644 --- a/src/lints/struct_marked_non_exhaustive.ron +++ b/src/lints/struct_marked_non_exhaustive.ron @@ -4,6 +4,7 @@ SemverQuery( description: "An exhaustive struct has been marked #[non_exhaustive].", reference: Some("An exhaustive struct has been marked #[non_exhaustive] making it no longer constructible using a struct literal outside its crate."), required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#attr-adding-non-exhaustive"), query: r#" { diff --git a/src/lints/struct_missing.ron b/src/lints/struct_missing.ron index efcf718e..18e05850 100644 --- a/src/lints/struct_missing.ron +++ b/src/lints/struct_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub struct removed or renamed", description: "A struct can no longer be imported by its prior path.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/struct_must_use_added.ron b/src/lints/struct_must_use_added.ron index 8ad70af2..76306f4b 100644 --- a/src/lints/struct_must_use_added.ron +++ b/src/lints/struct_must_use_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "struct #[must_use] added", description: "A struct has been marked with #[must_use].", required_update: Minor, + lint_level: Deny, // TODO: Change the reference link to point to the cargo semver reference // once it has a section on attribute #[must_use]. diff --git a/src/lints/struct_now_doc_hidden.ron b/src/lints/struct_now_doc_hidden.ron index 088cf369..cdf94454 100644 --- a/src/lints/struct_now_doc_hidden.ron +++ b/src/lints/struct_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub struct is now #[doc(hidden)]", description: "A pub struct is now marked #[doc(hidden)] and is thus no longer part of the public API.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/struct_pub_field_missing.ron b/src/lints/struct_pub_field_missing.ron index 90422d9f..5c48f2b6 100644 --- a/src/lints/struct_pub_field_missing.ron +++ b/src/lints/struct_pub_field_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub struct's pub field removed or renamed", description: "A struct field is no longer available under its prior name.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/struct_pub_field_now_doc_hidden.ron b/src/lints/struct_pub_field_now_doc_hidden.ron index 77d4bfea..f02ebcd9 100644 --- a/src/lints/struct_pub_field_now_doc_hidden.ron +++ b/src/lints/struct_pub_field_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub struct field is now #[doc(hidden)]", description: "A pub struct field is now marked #[doc(hidden)] and is no longer part of the public API.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/struct_repr_transparent_removed.ron b/src/lints/struct_repr_transparent_removed.ron index a2e77182..850ce091 100644 --- a/src/lints/struct_repr_transparent_removed.ron +++ b/src/lints/struct_repr_transparent_removed.ron @@ -26,6 +26,7 @@ To avoid false-positives, this query is restricted to checking only structs that - that one field is public. "#), required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#repr-transparent-remove"), query: r#" diff --git a/src/lints/struct_with_pub_fields_changed_type.ron b/src/lints/struct_with_pub_fields_changed_type.ron index 5d3a44a2..5f8e9134 100644 --- a/src/lints/struct_with_pub_fields_changed_type.ron +++ b/src/lints/struct_with_pub_fields_changed_type.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "struct with pub fields became an enum or union", description: "A struct was converted into an enum or union, breaking accesses to its fields.", required_update: Major, + lint_level: Deny, reference_link: Some("https://github.com/obi1kenobi/cargo-semver-checks/issues/297#issuecomment-1399099659"), reference: Some( r#"\ diff --git a/src/lints/trait_associated_const_now_doc_hidden.ron b/src/lints/trait_associated_const_now_doc_hidden.ron index f2fa1a83..7d2dd627 100644 --- a/src/lints/trait_associated_const_now_doc_hidden.ron +++ b/src/lints/trait_associated_const_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "trait associated const is now #[doc(hidden)]", description: "A public trait associated const is now marked as #[doc(hidden)] and has thus been removed from the public API", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/trait_associated_type_now_doc_hidden.ron b/src/lints/trait_associated_type_now_doc_hidden.ron index ba890489..fab20e1b 100644 --- a/src/lints/trait_associated_type_now_doc_hidden.ron +++ b/src/lints/trait_associated_type_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "trait associated type is now #[doc(hidden)]", description: "A public trait associated type is now marked as #[doc(hidden)] and has thus been removed from the public API", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/trait_method_missing.ron b/src/lints/trait_method_missing.ron index 52d4d3fb..fbec293b 100644 --- a/src/lints/trait_method_missing.ron +++ b/src/lints/trait_method_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub trait method removed or renamed", description: "A trait method can no longer be called by its prior path.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#major-any-change-to-trait-item-signatures"), query: r#" { diff --git a/src/lints/trait_method_now_doc_hidden.ron b/src/lints/trait_method_now_doc_hidden.ron index a3245cb4..d35f877d 100644 --- a/src/lints/trait_method_now_doc_hidden.ron +++ b/src/lints/trait_method_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub trait method is now #[doc(hidden)]", description: "A public trait method is now marked as #[doc(hidden)] and has thus been removed from the public API", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/trait_method_unsafe_added.ron b/src/lints/trait_method_unsafe_added.ron index d7ffcc55..40ac26e8 100644 --- a/src/lints/trait_method_unsafe_added.ron +++ b/src/lints/trait_method_unsafe_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub trait method became unsafe", description: "A method in a public trait became unsafe", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#calling-an-unsafe-function-or-method"), query: r#" { diff --git a/src/lints/trait_method_unsafe_removed.ron b/src/lints/trait_method_unsafe_removed.ron index a5fbcb04..7265aed1 100644 --- a/src/lints/trait_method_unsafe_removed.ron +++ b/src/lints/trait_method_unsafe_removed.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub trait method became safe", description: "A method in a public trait became safe", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/stable/reference/unsafe-keyword.html#unsafe-functions-unsafe-fn"), query: r#" { diff --git a/src/lints/trait_missing.ron b/src/lints/trait_missing.ron index 10c9fa14..a9cdf1b0 100644 --- a/src/lints/trait_missing.ron +++ b/src/lints/trait_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub trait removed or renamed", description: "A trait can no longer be imported by its prior path.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/trait_must_use_added.ron b/src/lints/trait_must_use_added.ron index ff7bff89..023c534c 100644 --- a/src/lints/trait_must_use_added.ron +++ b/src/lints/trait_must_use_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "trait #[must_use] added", description: "A trait has been marked with #[must_use].", required_update: Minor, + lint_level: Deny, // TODO: Change the reference link to point to the cargo semver reference // once it has a section on attribute #[must_use]. diff --git a/src/lints/trait_no_longer_object_safe.ron b/src/lints/trait_no_longer_object_safe.ron index dcf696f2..417404f5 100644 --- a/src/lints/trait_no_longer_object_safe.ron +++ b/src/lints/trait_no_longer_object_safe.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "trait no longer object safe", description: "A trait is no longer object safe, meaning it can no longer be used as `dyn Trait`.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/stable/reference/items/traits.html#object-safety"), query: r#" { diff --git a/src/lints/trait_now_doc_hidden.ron b/src/lints/trait_now_doc_hidden.ron index 8a146477..28f1b915 100644 --- a/src/lints/trait_now_doc_hidden.ron +++ b/src/lints/trait_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub trait is now #[doc(hidden)]", description: "A pub trait is now marked #[doc(hidden)] and is thus no longer part of the public API.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/trait_removed_associated_constant.ron b/src/lints/trait_removed_associated_constant.ron index 7aaac134..fb830b63 100644 --- a/src/lints/trait_removed_associated_constant.ron +++ b/src/lints/trait_removed_associated_constant.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "trait's associated constant was removed", description: "A trait's associated constant was removed or renamed", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/trait_removed_associated_type.ron b/src/lints/trait_removed_associated_type.ron index db536141..880e1e50 100644 --- a/src/lints/trait_removed_associated_type.ron +++ b/src/lints/trait_removed_associated_type.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "trait's associated type was removed", description: "A trait's associated type was removed or renamed.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/trait_removed_supertrait.ron b/src/lints/trait_removed_supertrait.ron index 00c943d0..6a26c9aa 100644 --- a/src/lints/trait_removed_supertrait.ron +++ b/src/lints/trait_removed_supertrait.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "supertrait removed or renamed", description: "Removing a supertrait bound is a breaking change, since users of the trait can no longer assume it can also be used like its supertrait.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/reference/items/traits.html#supertraits"), query: r#" { diff --git a/src/lints/trait_unsafe_added.ron b/src/lints/trait_unsafe_added.ron index d33aede9..c2ddf240 100644 --- a/src/lints/trait_unsafe_added.ron +++ b/src/lints/trait_unsafe_added.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub trait became unsafe", description: "A public trait became unsafe.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#implementing-an-unsafe-trait"), query: r#" { diff --git a/src/lints/trait_unsafe_removed.ron b/src/lints/trait_unsafe_removed.ron index 4e4c5c0b..75aab024 100644 --- a/src/lints/trait_unsafe_removed.ron +++ b/src/lints/trait_unsafe_removed.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub unsafe trait became safe", description: "A public unsafe trait became safe.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#implementing-an-unsafe-trait"), query: r#" { diff --git a/src/lints/tuple_struct_to_plain_struct.ron b/src/lints/tuple_struct_to_plain_struct.ron index 12c162d0..f736891a 100644 --- a/src/lints/tuple_struct_to_plain_struct.ron +++ b/src/lints/tuple_struct_to_plain_struct.ron @@ -7,6 +7,7 @@ This is breaking even if the fields have the same names, because tuple struct pa Source: Rust for Rustaceans, Chapter 3, "Type Modifications", page 51 "#), required_update: Major, + lint_level: Deny, reference_link: None, query: r#" { diff --git a/src/lints/type_marked_deprecated.ron b/src/lints/type_marked_deprecated.ron index 1f30bab8..c05af8d4 100644 --- a/src/lints/type_marked_deprecated.ron +++ b/src/lints/type_marked_deprecated.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "#[deprecated] added on type", description: "A type has been newly marked with #[deprecated].", required_update: Minor, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute"), query: r#" { diff --git a/src/lints/union_field_missing.ron b/src/lints/union_field_missing.ron index 1f0eec90..804757c3 100644 --- a/src/lints/union_field_missing.ron +++ b/src/lints/union_field_missing.ron @@ -2,7 +2,8 @@ SemverQuery( id: "union_field_missing", human_readable_name: "pub union pub field is removed or renamed", description: "pub union pub field is removed or renamed. No longer present under it's previous name, by whatever cause.", - required_update: Major, + required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { @@ -44,7 +45,7 @@ SemverQuery( field @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { visibility_limit @filter(op: "=", value: ["$public"]) name @filter(op: "=", value: ["%field_name"]) - + } } } diff --git a/src/lints/union_missing.ron b/src/lints/union_missing.ron index 85cd04d1..76be3480 100644 --- a/src/lints/union_missing.ron +++ b/src/lints/union_missing.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub union removed or renamed", description: "A union can no longer be imported by its prior path.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#item-remove"), query: r#" { diff --git a/src/lints/union_now_doc_hidden.ron b/src/lints/union_now_doc_hidden.ron index 577297c1..f3ebf549 100644 --- a/src/lints/union_now_doc_hidden.ron +++ b/src/lints/union_now_doc_hidden.ron @@ -3,6 +3,7 @@ SemverQuery( human_readable_name: "pub union is now #[doc(hidden)]", description: "A pub union is now marked #[doc(hidden)] and is thus no longer part of the public API.", required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden"), query: r#" { diff --git a/src/lints/unit_struct_changed_kind.ron b/src/lints/unit_struct_changed_kind.ron index 6ddc6096..00b0ab07 100644 --- a/src/lints/unit_struct_changed_kind.ron +++ b/src/lints/unit_struct_changed_kind.ron @@ -4,6 +4,7 @@ SemverQuery( description: "A struct changed from a unit struct to a plain struct.", reference: Some("A public struct that was previously a unit struct is now a plain struct. The unit struct was not marked #[non_exhaustive], so it could be constructed outside of the defining crate. Plain structs cannot be constructed using the syntax allowed for unit structs, so this is a major breaking change for code that depends on it."), required_update: Major, + lint_level: Deny, // TODO: Change the reference link once this cargo docs PR merges: // https://github.com/rust-lang/cargo/pull/10871 diff --git a/src/lints/variant_marked_non_exhaustive.ron b/src/lints/variant_marked_non_exhaustive.ron index 2cf1e8b4..fb354dcd 100644 --- a/src/lints/variant_marked_non_exhaustive.ron +++ b/src/lints/variant_marked_non_exhaustive.ron @@ -4,6 +4,7 @@ SemverQuery( description: "An exhaustive enum variant has been marked #[non_exhaustive].", reference: Some("An exhaustive enum variant has been marked #[non_exhaustive], preventing it from being constructed using a literal from outside its own crate."), required_update: Major, + lint_level: Deny, reference_link: Some("https://doc.rust-lang.org/cargo/reference/semver.html#attr-adding-non-exhaustive"), query: r#" { diff --git a/src/main.rs b/src/main.rs index fd588fa8..4f1322e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,8 @@ use std::{env, path::PathBuf}; use cargo_semver_checks::{ - GlobalConfig, PackageSelection, ReleaseType, Rustdoc, ScopeSelection, SemverQuery, + GlobalConfig, PackageSelection, QueryOverride, ReleaseType, Rustdoc, ScopeSelection, + SemverQuery, }; use clap::{Args, Parser, Subcommand}; @@ -29,12 +30,16 @@ fn main() { let mut config = GlobalConfig::new(); config.set_log_level(args.check_release.verbosity.log_level()); - let queries = SemverQuery::all_queries(); - let mut rows = vec![["id", "type", "description"], ["==", "====", "==========="]]; + let queries = config.all_queries()?; + let mut rows = vec![ + ["id", "type", "level", "description"], + ["==", "====", "=====", "==========="], + ]; for query in queries.values() { rows.push([ query.id.as_str(), query.required_update.as_str(), + query.lint_level.as_str(), query.description.as_str(), ]); } diff --git a/src/query.rs b/src/query.rs index e13aba75..36b4c9d1 100644 --- a/src/query.rs +++ b/src/query.rs @@ -6,10 +6,10 @@ use trustfall::TransparentValue; use crate::ReleaseType; #[non_exhaustive] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub enum RequiredSemverUpdate { - Major, Minor, + Major, } impl RequiredSemverUpdate { @@ -60,6 +60,35 @@ impl From for ActualSemverUpdate { } } +/// The level of intensity of error when a lint occurs +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +pub enum LintLevel { + /// If this lint occurs, do nothing + Allow, + /// If this lint occurs, print a warning + Warn, + /// If this lint occurs, raise an error + Deny, +} + +impl LintLevel { + pub fn as_str(self) -> &'static str { + match self { + LintLevel::Allow => "allow", + LintLevel::Warn => "warn", + LintLevel::Deny => "deny", + } + } +} + +#[derive(Debug, Clone, Default)] +pub struct QueryOverride { + /// the required version bump for this lint; see [`SemverQuery`].required_update + pub required_update: Option, + /// the lint level for the query; see [`SemverQuery`].lint_level + pub lint_level: Option, +} + /// A query that can be executed on a pair of rustdoc output files, /// returning instances of a particular kind of semver violation. #[non_exhaustive] @@ -73,6 +102,9 @@ pub struct SemverQuery { pub required_update: RequiredSemverUpdate, + /// The error level for when this lint procs (allow, warn, or deny) + pub lint_level: LintLevel, + #[serde(default)] pub reference: Option, @@ -115,6 +147,16 @@ Failed to parse a query: {e} queries } + + pub fn apply_override(&mut self, query_override: &QueryOverride) { + if let Some(lint_level) = query_override.lint_level { + self.lint_level = lint_level; + } + + if let Some(required_update) = query_override.required_update { + self.required_update = required_update; + } + } } #[cfg(test)] From 6990dc0f9e2a343dd1d3acb071cf32b9886b0c3b Mon Sep 17 00:00:00 2001 From: m Date: Sun, 2 Jun 2024 19:16:45 -0700 Subject: [PATCH 2/3] lint & doc fixes --- src/config.rs | 1 - src/main.rs | 3 +-- src/query.rs | 13 ++++++++++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/config.rs b/src/config.rs index 73084ab1..ce412d5f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -302,7 +302,6 @@ impl GlobalConfig { &self.query_overrides } - #[must_use] pub fn all_queries(&self) -> anyhow::Result> { let mut queries = SemverQuery::all_queries(); for (name, overrides) in &self.query_overrides { diff --git a/src/main.rs b/src/main.rs index 4f1322e2..fa8cee26 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,8 +3,7 @@ use std::{env, path::PathBuf}; use cargo_semver_checks::{ - GlobalConfig, PackageSelection, QueryOverride, ReleaseType, Rustdoc, ScopeSelection, - SemverQuery, + GlobalConfig, PackageSelection, ReleaseType, Rustdoc, ScopeSelection, SemverQuery, }; use clap::{Args, Parser, Subcommand}; diff --git a/src/query.rs b/src/query.rs index 36b4c9d1..676ea6a7 100644 --- a/src/query.rs +++ b/src/query.rs @@ -81,11 +81,18 @@ impl LintLevel { } } +/// Configured values for a [`SemverQuery`] that differ from the lint's default. #[derive(Debug, Clone, Default)] pub struct QueryOverride { - /// the required version bump for this lint; see [`SemverQuery`].required_update + /// The required version bump for this lint; see [`SemverQuery`].`required_update`. + /// + /// If this is `None`, use the default `required_update` for the lint when calculating + /// the effective required version bump. pub required_update: Option, - /// the lint level for the query; see [`SemverQuery`].lint_level + /// The lint level for the query; see [`SemverQuery`].`lint_level`. + /// + /// If this is `None`, use the default `required_update` for the lint when calculating + /// the effective lint level. pub lint_level: Option, } @@ -102,7 +109,7 @@ pub struct SemverQuery { pub required_update: RequiredSemverUpdate, - /// The error level for when this lint procs (allow, warn, or deny) + /// The error level for when this lint occurs (allow, warn, or deny). pub lint_level: LintLevel, #[serde(default)] From 6759fcb20f2f642d21068f80d11ea0fa73fca30b Mon Sep 17 00:00:00 2001 From: m Date: Mon, 3 Jun 2024 12:09:31 -0700 Subject: [PATCH 3/3] one idea for managing overrides --- src/check_release.rs | 29 ++++---- src/config.rs | 33 +-------- src/lib.rs | 44 ++++++++++- src/main.rs | 3 +- src/query.rs | 173 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 231 insertions(+), 51 deletions(-) diff --git a/src/check_release.rs b/src/check_release.rs index e92dbe18..8e59c846 100644 --- a/src/check_release.rs +++ b/src/check_release.rs @@ -11,7 +11,7 @@ use trustfall::{FieldValue, TransparentValue}; use trustfall_rustdoc::{VersionedCrate, VersionedIndexedCrate, VersionedRustdocAdapter}; use crate::{ - query::{ActualSemverUpdate, LintLevel, RequiredSemverUpdate, SemverQuery}, + query::{ActualSemverUpdate, LintLevel, QueryOverrideList, RequiredSemverUpdate, SemverQuery}, CrateReport, GlobalConfig, ReleaseType, }; @@ -160,8 +160,9 @@ pub(super) fn run_check_release( current_crate: VersionedCrate, baseline_crate: VersionedCrate, release_type: Option, + overrides: QueryOverrideList, ) -> anyhow::Result { - let queries = config.all_queries()?; + let queries = SemverQuery::all_queries(); let current_version = current_crate.crate_version(); let baseline_version = baseline_crate.crate_version(); @@ -193,8 +194,8 @@ pub(super) fn run_check_release( let adapter = VersionedRustdocAdapter::new(¤t, Some(&previous))?; let (queries_to_run, queries_to_skip): (Vec<_>, _) = queries.values().partition(|query| { - !version_change.supports_requirement(query.required_update) - && query.lint_level >= LintLevel::Warn + !version_change.supports_requirement(overrides.effective_required_update(query)) + && overrides.effective_lint_level(query) >= LintLevel::Warn }); let skipped_queries = queries_to_skip.len(); @@ -253,7 +254,7 @@ pub(super) fn run_check_release( for (semver_query, time_to_decide, results) in all_results { config .log_verbose(|config| { - let category = match semver_query.required_update { + let category = match overrides.effective_required_update(semver_query) { RequiredSemverUpdate::Major => "major", RequiredSemverUpdate::Minor => "minor", }; @@ -288,7 +289,7 @@ pub(super) fn run_check_release( }) .expect("print failed"); if !results.is_empty() { - if semver_query.lint_level == LintLevel::Deny { + if overrides.effective_lint_level(semver_query) == LintLevel::Deny { error_results.push((semver_query, results)); } else { warning_results.push((semver_query, results)); @@ -317,26 +318,26 @@ pub(super) fn run_check_release( let mut warnings = BTreeMap::new(); let mut errors = BTreeMap::new(); // print errors before warnings like clippy does - for (semver_query, results) in warning_results { - warnings - .entry(semver_query.required_update) + for (semver_query, results) in error_results { + errors + .entry(overrides.effective_required_update(semver_query)) .and_modify(|e| *e += 1) .or_insert(1); - config.shell_warn(format!( + config.shell_error(format!( "{}: {}", &semver_query.id, &semver_query.human_readable_name ))?; print_lint_failure(config, semver_query, results)?; } - for (semver_query, results) in error_results { - errors - .entry(semver_query.required_update) + for (semver_query, results) in warning_results { + warnings + .entry(overrides.effective_required_update(semver_query)) .and_modify(|e| *e += 1) .or_insert(1); - config.shell_error(format!( + config.shell_warn(format!( "{}: {}", &semver_query.id, &semver_query.human_readable_name ))?; diff --git a/src/config.rs b/src/config.rs index ce412d5f..2a251729 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,8 @@ use anstream::{AutoStream, ColorChoice}; use anstyle::{AnsiColor, Color, Reset, Style}; -use std::{collections::BTreeMap, io::Write}; +use std::io::Write; -use crate::{query::QueryOverride, templating::make_handlebars_registry, SemverQuery}; +use crate::templating::make_handlebars_registry; #[allow(dead_code)] pub struct GlobalConfig { @@ -14,9 +14,6 @@ pub struct GlobalConfig { minimum_rustc_version: semver::Version, stdout: AutoStream>, stderr: AutoStream>, - /// A mapping of lint names to values to override that lint's defaults, - /// such as its lint level and required semver bump. - query_overrides: BTreeMap, } impl Default for GlobalConfig { @@ -41,7 +38,6 @@ impl GlobalConfig { minimum_rustc_version: semver::Version::new(1, 74, 0), stdout: AutoStream::new(Box::new(std::io::stdout()), stdout_choice), stderr: AutoStream::new(Box::new(std::io::stderr()), stderr_choice), - query_overrides: BTreeMap::new(), } } @@ -288,31 +284,6 @@ impl GlobalConfig { ColorChoice::Never | ColorChoice::Auto => false, } } - - pub fn set_query_overrides( - &mut self, - query_overrides: BTreeMap, - ) -> &mut Self { - self.query_overrides = query_overrides; - self - } - - #[must_use] - pub fn query_overrides(&self) -> &BTreeMap { - &self.query_overrides - } - - pub fn all_queries(&self) -> anyhow::Result> { - let mut queries = SemverQuery::all_queries(); - for (name, overrides) in &self.query_overrides { - if let Some(query) = queries.get_mut(name) { - query.apply_override(overrides); - } else { - anyhow::bail!("Can't configure lint with unknown name `{name}`."); - } - } - Ok(queries) - } } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index 6d3e5a21..b4c7cea6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,10 +22,14 @@ use trustfall_rustdoc::{load_rustdoc, VersionedCrate}; use rustdoc_cmd::RustdocCommand; use std::collections::{BTreeMap, HashSet}; use std::path::{Path, PathBuf}; +use std::sync::Arc; use std::time::Instant; pub use config::GlobalConfig; -pub use query::{ActualSemverUpdate, LintLevel, QueryOverride, RequiredSemverUpdate, SemverQuery}; +pub use query::{ + ActualSemverUpdate, LintLevel, QueryOverride, QueryOverrideList, QueryOverrides, + RequiredSemverUpdate, SemverQuery, +}; /// Test a release for semver violations. #[non_exhaustive] @@ -40,6 +44,13 @@ pub struct Check { baseline_feature_config: rustdoc_gen::FeatureConfig, /// Which `--target` to use, if unset pass no flag build_target: Option, + /// Workspace-level configuration overrides to apply to all packages if + /// running in workspace scope. Package-level overrides for a given + /// crate in `package_overrides take precedence over this member if both are set. + workspace_overrides: Option>, + /// A mapping of package name to package-level configuration overrides for + /// that package. Takes precedence over `workspace_overrides` if both are set. + package_overrides: BTreeMap>, } /// The kind of release we're making. @@ -253,6 +264,8 @@ impl Check { current_feature_config: rustdoc_gen::FeatureConfig::default_for_current(), baseline_feature_config: rustdoc_gen::FeatureConfig::default_for_baseline(), build_target: None, + workspace_overrides: None, + package_overrides: BTreeMap::new(), } } @@ -372,6 +385,22 @@ impl Check { }) } + /// Helper function to get the configured workspace-level overrides if set and + /// running in workspace scope, otherwise return an empty list. + #[must_use] + fn workspace_overrides(&self) -> Vec> { + if let ScopeMode::DenyList(PackageSelection { + selection: ScopeSelection::Workspace, + .. + }) = self.scope.mode + { + if let Some(wksp) = &self.workspace_overrides { + return vec![Arc::clone(wksp)]; + } + } + vec![] + } + pub fn check_release(&self, config: &mut GlobalConfig) -> anyhow::Result { let rustdoc_cmd = RustdocCommand::new().deps(false).silence(config.is_info()); @@ -447,12 +476,19 @@ impl Check { }, )?; + let mut overrides = self.workspace_overrides(); + + if let Some(pkg) = self.package_overrides.get(&name) { + overrides.push(Arc::clone(pkg)); + } + let report = run_check_release( config, &name, current_crate, baseline_crate, self.release_type, + overrides.into(), )?; config.shell_status( "Finished", @@ -525,6 +561,11 @@ note: skipped the following crates since they have no library target: {skipped}" }, )?; + let mut overrides = self.workspace_overrides(); + if let Some(pkg) = self.package_overrides.get(&selected.name) { + overrides.push(Arc::clone(pkg)); + } + let result = Ok(( crate_name.clone(), Some(run_check_release( @@ -533,6 +574,7 @@ note: skipped the following crates since they have no library target: {skipped}" current_crate, baseline_crate, self.release_type, + overrides.into(), )?), )); config.shell_status( diff --git a/src/main.rs b/src/main.rs index fa8cee26..c642cb8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,8 @@ fn main() { let mut config = GlobalConfig::new(); config.set_log_level(args.check_release.verbosity.log_level()); - let queries = config.all_queries()?; + // TODO: do we want this to respect configuration in the current directory? + let queries = SemverQuery::all_queries(); let mut rows = vec![ ["id", "type", "level", "description"], ["==", "====", "=====", "==========="], diff --git a/src/query.rs b/src/query.rs index 676ea6a7..2d359462 100644 --- a/src/query.rs +++ b/src/query.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::{collections::BTreeMap, sync::Arc}; use serde::{Deserialize, Serialize}; use trustfall::TransparentValue; @@ -82,7 +82,7 @@ impl LintLevel { } /// Configured values for a [`SemverQuery`] that differ from the lint's default. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct QueryOverride { /// The required version bump for this lint; see [`SemverQuery`].`required_update`. /// @@ -96,6 +96,59 @@ pub struct QueryOverride { pub lint_level: Option, } +/// A mapping of lint level name to values to override that lint's defaults with. +pub type QueryOverrides = BTreeMap; + +/// Stores a list of [`QueryOverride`] references. +/// +/// Items later in the list (with higher indices) have *higher* precedence, +/// so e.g., store package-level overrides at index 1 and workspace-level +/// overrides at index 0 for package-level overrides to take priority over +/// workspace-level ones if both are set. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct QueryOverrideList(Vec>); + +impl QueryOverrideList { + /// Creates a new empty [`QueryOverrideList`] instance. + #[must_use] + pub fn new() -> Self { + Self(Vec::new()) + } + + /// Inserts the given element at the end of the list. + /// The inserted parameter `overrides` will take precedence over all + /// previous overrides in the list, if both set. + pub fn push(&mut self, overrides: Arc) { + self.0.push(overrides); + } + + #[must_use] + pub fn effective_lint_level(&self, query: &SemverQuery) -> LintLevel { + self.0 + .iter() + .rev() + .filter_map(|x| x.get(&query.id).and_then(|y| y.lint_level)) + .next() + .unwrap_or(query.lint_level) + } + + #[must_use] + pub fn effective_required_update(&self, query: &SemverQuery) -> RequiredSemverUpdate { + self.0 + .iter() + .rev() + .filter_map(|x| x.get(&query.id).and_then(|y| y.required_update)) + .next() + .unwrap_or(query.required_update) + } +} + +impl From>> for QueryOverrideList { + fn from(value: Vec>) -> Self { + Self(value) + } +} + /// A query that can be executed on a pair of rustdoc output files, /// returning instances of a particular kind of semver violation. #[non_exhaustive] @@ -168,7 +221,7 @@ Failed to parse a query: {e} #[cfg(test)] mod tests { - use std::sync::OnceLock; + use std::sync::{Arc, OnceLock}; use std::{collections::BTreeMap, path::Path}; use anyhow::Context; @@ -177,7 +230,10 @@ mod tests { load_rustdoc, VersionedCrate, VersionedIndexedCrate, VersionedRustdocAdapter, }; - use crate::query::SemverQuery; + use super::{ + LintLevel, QueryOverride, QueryOverrideList, QueryOverrides, RequiredSemverUpdate, + SemverQuery, + }; use crate::templating::make_handlebars_registry; static TEST_CRATE_NAMES: OnceLock> = OnceLock::new(); @@ -498,6 +554,115 @@ mod tests { } } } + + #[must_use] + fn make_blank_query( + id: String, + lint_level: LintLevel, + required_update: RequiredSemverUpdate, + ) -> SemverQuery { + SemverQuery { + id, + lint_level, + required_update, + human_readable_name: String::new(), + description: String::new(), + reference: None, + reference_link: None, + query: String::new(), + arguments: BTreeMap::new(), + error_message: String::new(), + per_result_error_template: None, + } + } + + #[test] + fn test_overrides() { + let qo: QueryOverrides = [( + "query1".into(), + QueryOverride { + lint_level: Some(crate::LintLevel::Deny), + required_update: Some(crate::RequiredSemverUpdate::Major), + }, + )] + .into_iter() + .collect(); + + let mut list = QueryOverrideList::new(); + list.push(Arc::new(qo)); + + let query1 = make_blank_query( + "query1".into(), + LintLevel::Allow, + RequiredSemverUpdate::Minor, + ); + + let query2 = make_blank_query( + "query2".into(), + LintLevel::Allow, + RequiredSemverUpdate::Minor, + ); + + // Overrides should apply on query 1. + assert_eq!(list.effective_lint_level(&query1), LintLevel::Deny); + assert_eq!( + list.effective_required_update(&query1), + RequiredSemverUpdate::Major + ); + + // But query 2 does not have overrides, so it should return the defaults. + assert_eq!(list.effective_lint_level(&query2), LintLevel::Allow); + assert_eq!( + list.effective_required_update(&query2), + RequiredSemverUpdate::Minor + ); + } + + #[test] + fn test_override_precedence() { + let mut list = QueryOverrideList::new(); + + // Since this one is pushed first, it has lower precedence than the following set of overrides. + list.push(Arc::new( + [( + "query1".into(), + QueryOverride { + lint_level: Some(LintLevel::Warn), + required_update: Some(RequiredSemverUpdate::Major), + }, + )] + .into_iter() + .collect(), + )); + + list.push(Arc::new( + [( + "query1".into(), + QueryOverride { + lint_level: Some(LintLevel::Deny), + required_update: None, + }, + )] + .into_iter() + .collect(), + )); + + let query1 = make_blank_query( + "query1".into(), + LintLevel::Allow, + RequiredSemverUpdate::Minor, + ); + + // Since both overrides are set on its lint level, the later one should take precedence. + assert_eq!(list.effective_lint_level(&query1), LintLevel::Deny); + + // Since only the lower-precedence override is set for version, it should return that one, + // not the query's default. + assert_eq!( + list.effective_required_update(&query1), + RequiredSemverUpdate::Major + ); + } } macro_rules! add_lints {