Skip to content

Commit 95203f1

Browse files
committed
add lint_mode to SemverQuery
1 parent 30e41d5 commit 95203f1

File tree

3 files changed

+163
-16
lines changed

3 files changed

+163
-16
lines changed

src/check_release.rs

+91-13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rayon::prelude::*;
1010
use trustfall::{FieldValue, TransparentValue};
1111

1212
use crate::data_generation::DataStorage;
13+
use crate::query::LintMode;
1314
use crate::{
1415
query::{ActualSemverUpdate, LintLevel, OverrideStack, RequiredSemverUpdate, SemverQuery},
1516
CrateReport, GlobalConfig, ReleaseType, WitnessGeneration,
@@ -215,8 +216,9 @@ pub(super) fn run_check_release(
215216

216217
let (queries_to_run, queries_to_skip): (Vec<_>, _) =
217218
SemverQuery::all_queries().into_values().partition(|query| {
218-
!version_change.supports_requirement(overrides.effective_required_update(query))
219-
&& overrides.effective_lint_level(query) > LintLevel::Allow
219+
overrides.effective_lint_mode(query) == LintMode::AlwaysRun
220+
|| !version_change.supports_requirement(overrides.effective_required_update(query))
221+
&& overrides.effective_lint_level(query) > LintLevel::Allow
220222
});
221223
let skipped_queries = queries_to_skip.len();
222224

@@ -272,6 +274,9 @@ pub(super) fn run_check_release(
272274

273275
let mut results_with_errors = vec![];
274276
let mut results_with_warnings = vec![];
277+
let mut results_with_always_run_errors = vec![];
278+
let mut results_with_always_run_warnings = vec![];
279+
275280
for (semver_query, time_to_decide, results) in all_results {
276281
config
277282
.log_verbose(|config| {
@@ -309,20 +314,42 @@ pub(super) fn run_check_release(
309314
.expect("print failed");
310315

311316
if !results.is_empty() {
312-
match overrides.effective_lint_level(semver_query) {
313-
LintLevel::Deny => results_with_errors.push((semver_query, results)),
314-
LintLevel::Warn => results_with_warnings.push((semver_query, results)),
315-
LintLevel::Allow => unreachable!(
317+
let lint_level = overrides.effective_lint_level(semver_query);
318+
let lint_mode = overrides.effective_lint_mode(semver_query);
319+
320+
match (lint_level, lint_mode) {
321+
(LintLevel::Deny, LintMode::SemVer) => {
322+
results_with_errors.push((semver_query, results))
323+
}
324+
(LintLevel::Warn, LintMode::SemVer) => {
325+
results_with_warnings.push((semver_query, results))
326+
}
327+
(LintLevel::Warn, LintMode::AlwaysRun) => {
328+
results_with_always_run_warnings.push((semver_query, results))
329+
}
330+
(LintLevel::Deny, LintMode::AlwaysRun) => {
331+
results_with_always_run_errors.push((semver_query, results))
332+
}
333+
(LintLevel::Allow, _) => unreachable!(
316334
"`LintLevel::Allow` lint was unexpectedly not skipped: {semver_query:?}"
317335
),
318336
};
319337
}
320338
}
321339

322-
let produced_errors = !results_with_errors.is_empty();
323-
let produced_warnings = !results_with_warnings.is_empty();
324-
if produced_errors || produced_warnings {
325-
let status_color = if produced_errors {
340+
let produced_semver_errors = !results_with_errors.is_empty();
341+
let produced_semver_warnings = !results_with_warnings.is_empty();
342+
let produced_always_run_errors = !results_with_always_run_errors.is_empty();
343+
let produced_always_run_warnings = !results_with_always_run_warnings.is_empty();
344+
let total_always_run_issues =
345+
results_with_always_run_errors.len() + results_with_always_run_warnings.len();
346+
let has_issues = produced_semver_errors
347+
|| produced_semver_warnings
348+
|| produced_always_run_errors
349+
|| produced_always_run_warnings;
350+
351+
if has_issues {
352+
let status_color = if produced_semver_errors || produced_always_run_errors {
326353
AnsiColor::Red
327354
} else {
328355
AnsiColor::Yellow
@@ -377,6 +404,35 @@ pub(super) fn run_check_release(
377404
print_triggered_lint(config, semver_query, results, witness_generation)?;
378405
}
379406

407+
for (semver_query, results) in results_with_always_run_errors {
408+
config.log_info(|config| {
409+
writeln!(
410+
config.stdout(),
411+
"\n--- risk failure {}: {} ---\n",
412+
&semver_query.id,
413+
&semver_query.human_readable_name
414+
)?;
415+
Ok(())
416+
})?;
417+
418+
print_triggered_lint(config, semver_query, results, witness_generation)?;
419+
}
420+
421+
// Process AlwaysRun warnings
422+
for (semver_query, results) in results_with_always_run_warnings {
423+
config.log_info(|config| {
424+
writeln!(
425+
config.stdout(),
426+
"\n--- risk warning {}: {} ---\n",
427+
semver_query.id,
428+
semver_query.human_readable_name
429+
)?;
430+
Ok(())
431+
})?;
432+
433+
print_triggered_lint(config, semver_query, results, witness_generation)?;
434+
}
435+
380436
let required_bump = required_versions.iter().max().copied();
381437
let suggested_bump = suggested_versions.iter().max().copied();
382438

@@ -399,16 +455,14 @@ pub(super) fn run_check_release(
399455
Color::Ansi(AnsiColor::Red),
400456
true,
401457
)?;
402-
} else if produced_warnings {
458+
} else if produced_semver_warnings {
403459
writeln!(config.stderr())?;
404460
config.shell_print(
405461
"Summary",
406462
"no semver update required",
407463
Color::Ansi(AnsiColor::Green),
408464
true,
409465
)?;
410-
} else {
411-
unreachable!("Expected either warnings or errors to be produced.");
412466
}
413467

414468
if let Some(suggested_bump) = suggested_bump {
@@ -439,6 +493,30 @@ pub(super) fn run_check_release(
439493
}
440494
}
441495

496+
if total_always_run_issues > 0 {
497+
writeln!(config.stderr())?;
498+
let label = if produced_always_run_errors {
499+
"Risk Alert"
500+
} else {
501+
"Risk Notice"
502+
};
503+
let color = if produced_always_run_errors {
504+
AnsiColor::Red
505+
} else {
506+
AnsiColor::Yellow
507+
};
508+
509+
config.shell_print(
510+
label,
511+
format_args!(
512+
"{} potentially risky changes detected that require attention regardless of version bump",
513+
total_always_run_issues
514+
),
515+
Color::Ansi(color),
516+
true,
517+
)?;
518+
}
519+
442520
Ok(CrateReport {
443521
required_bump: required_bump.map(ReleaseType::from),
444522
detected_bump: version_change,

src/manifest.rs

+32-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
33
use anyhow::Context;
44
use serde::Deserialize;
55

6-
use crate::{LintLevel, OverrideMap, QueryOverride, RequiredSemverUpdate};
6+
use crate::{query::LintMode, LintLevel, OverrideMap, QueryOverride, RequiredSemverUpdate};
77

88
#[derive(Debug, Clone)]
99
pub(crate) struct Manifest {
@@ -116,24 +116,28 @@ impl LintTable {
116116
QueryOverride {
117117
lint_level: Some(lint_level),
118118
required_update: None,
119+
lint_mode: None,
119120
},
120121
),
121122
OverrideConfig::Both {
122123
level,
123124
required_update,
125+
lint_mode,
124126
priority,
125127
} => (
126128
priority,
127129
QueryOverride {
128130
lint_level: Some(level),
129131
required_update: Some(required_update),
132+
lint_mode,
130133
},
131134
),
132135
OverrideConfig::LintLevel { level, priority } => (
133136
priority,
134137
QueryOverride {
135138
lint_level: Some(level),
136139
required_update: None,
140+
lint_mode: None,
137141
},
138142
),
139143
OverrideConfig::RequiredUpdate {
@@ -144,6 +148,15 @@ impl LintTable {
144148
QueryOverride {
145149
lint_level: None,
146150
required_update: Some(required_update),
151+
lint_mode: None,
152+
},
153+
),
154+
OverrideConfig::LintMode { mode, priority } => (
155+
priority,
156+
QueryOverride {
157+
lint_level: None,
158+
required_update: None,
159+
lint_mode: Some(mode),
147160
},
148161
),
149162
};
@@ -165,12 +178,14 @@ impl LintTable {
165178
#[derive(Debug, Clone, Deserialize)]
166179
#[serde(untagged)]
167180
pub(crate) enum OverrideConfig {
168-
/// Specify both lint level and required update by name, e.g.
181+
/// Specify both lint level and required update by name, with optional lint mode.
169182
/// `lint_name = { level = "deny", required-update = "major" }
183+
/// `lint_name = { level = "deny", required-update = "major", mode = "always-run" }
170184
#[serde(rename_all = "kebab-case")]
171185
Both {
172186
level: LintLevel,
173187
required_update: RequiredSemverUpdate,
188+
lint_mode: Option<LintMode>,
174189
/// The priority for this configuration. If there are multiple entries that
175190
/// configure a lint (e.g., a lint group containing a lint and the lint itself),
176191
/// the configuration entry with the **lowest** priority takes precedence.
@@ -194,6 +209,14 @@ pub(crate) enum OverrideConfig {
194209
#[serde(default)]
195210
priority: i64,
196211
},
212+
/// Specify just lint mode by name, with optional priority.
213+
/// `lint_name = { mode = "always-run" }`
214+
#[serde(rename_all = "kebab-case")]
215+
LintMode {
216+
mode: LintMode,
217+
#[serde(default)]
218+
priority: i64,
219+
},
197220
/// Shorthand for specifying just a lint level and leaving
198221
/// the other members (required_update and priority) as default: e.g.,
199222
/// `lint_name = "deny"`
@@ -301,13 +324,15 @@ mod tests {
301324
QueryOverride {
302325
lint_level: Some(Deny),
303326
required_update: None,
327+
lint_mode: None,
304328
}
305329
),]),
306330
OverrideMap::from_iter([(
307331
"six".into(),
308332
QueryOverride {
309333
lint_level: Some(Allow),
310334
required_update: None,
335+
lint_mode: None,
311336
}
312337
),]),
313338
]
@@ -321,21 +346,24 @@ mod tests {
321346
QueryOverride {
322347
lint_level: Some(Warn),
323348
required_update: None,
349+
lint_mode: None,
324350
}
325351
)]),
326352
OverrideMap::from_iter([
327353
(
328354
"two".into(),
329355
QueryOverride {
330356
lint_level: Some(Deny),
331-
required_update: None
357+
required_update: None,
358+
lint_mode: None,
332359
}
333360
),
334361
(
335362
"four".into(),
336363
QueryOverride {
337364
lint_level: None,
338365
required_update: Some(Major),
366+
lint_mode: None,
339367
}
340368
),
341369
]),
@@ -344,6 +372,7 @@ mod tests {
344372
QueryOverride {
345373
lint_level: Some(Allow),
346374
required_update: Some(Minor),
375+
lint_mode: None,
347376
}
348377
)])
349378
]

0 commit comments

Comments
 (0)