Skip to content

Commit 4eab76b

Browse files
committed
add SecondaryCode
1 parent 957c36c commit 4eab76b

File tree

8 files changed

+127
-48
lines changed

8 files changed

+127
-48
lines changed

crates/ruff/src/printer.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use ruff_linter::logging::LogLevel;
1414
use ruff_linter::message::{
1515
AzureEmitter, Emitter, EmitterContext, GithubEmitter, GitlabEmitter, GroupedEmitter,
1616
JsonEmitter, JsonLinesEmitter, JunitEmitter, OldDiagnostic, PylintEmitter, RdjsonEmitter,
17-
SarifEmitter, TextEmitter,
17+
SarifEmitter, SecondaryCode, TextEmitter,
1818
};
1919
use ruff_linter::notify_user;
2020
use ruff_linter::settings::flags::{self};
@@ -36,7 +36,7 @@ bitflags! {
3636

3737
#[derive(Serialize)]
3838
struct ExpandedStatistics<'a> {
39-
code: Option<&'a str>,
39+
code: Option<&'a SecondaryCode>,
4040
name: &'static str,
4141
count: usize,
4242
fixable: bool,
@@ -306,7 +306,8 @@ impl Printer {
306306
.sorted_by_key(|(code, message)| (*code, message.fixable()))
307307
.fold(
308308
vec![],
309-
|mut acc: Vec<((Option<&str>, &OldDiagnostic), usize)>, (code, message)| {
309+
|mut acc: Vec<((Option<&SecondaryCode>, &OldDiagnostic), usize)>,
310+
(code, message)| {
310311
if let Some(((prev_code, _prev_message), count)) = acc.last_mut() {
311312
if *prev_code == code {
312313
*count += 1;
@@ -348,7 +349,7 @@ impl Printer {
348349
);
349350
let code_width = statistics
350351
.iter()
351-
.map(|statistic| statistic.code.map_or(0, str::len))
352+
.map(|statistic| statistic.code.map_or(0, |s| s.len()))
352353
.max()
353354
.unwrap();
354355
let any_fixable = statistics.iter().any(|statistic| statistic.fixable);
@@ -362,7 +363,7 @@ impl Printer {
362363
writer,
363364
"{:>count_width$}\t{:<code_width$}\t{}{}",
364365
statistic.count.to_string().bold(),
365-
statistic.code.unwrap_or_default().red().bold(),
366+
statistic.code.map_or("", |s| s.as_str()).red().bold(),
366367
if any_fixable {
367368
if statistic.fixable {
368369
&fixable

crates/ruff_linter/src/checkers/noqa.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ pub(crate) fn check_noqa(
5151
continue;
5252
};
5353

54-
if code == Rule::BlanketNOQA.noqa_code() {
54+
if *code == Rule::BlanketNOQA.noqa_code() {
5555
continue;
5656
}
5757

58-
if exemption.contains(&code) {
58+
if exemption.contains(code) {
5959
ignored_diagnostics.push(index);
6060
continue;
6161
}
@@ -72,13 +72,13 @@ pub(crate) fn check_noqa(
7272
{
7373
let suppressed = match &directive_line.directive {
7474
Directive::All(_) => {
75-
directive_line.matches.push(code.to_string());
75+
directive_line.matches.push(code.clone());
7676
ignored_diagnostics.push(index);
7777
true
7878
}
7979
Directive::Codes(directive) => {
80-
if directive.includes(&code) {
81-
directive_line.matches.push(code.to_string());
80+
if directive.includes(code) {
81+
directive_line.matches.push(code.clone());
8282
ignored_diagnostics.push(index);
8383
true
8484
} else {
@@ -138,7 +138,7 @@ pub(crate) fn check_noqa(
138138
if seen_codes.insert(original_code) {
139139
let is_code_used = if is_file_level {
140140
context.iter().any(|diag| {
141-
diag.secondary_code().is_some_and(|noqa| noqa == code)
141+
diag.secondary_code().is_some_and(|noqa| *noqa == code)
142142
})
143143
} else {
144144
matches.iter().any(|match_| *match_ == code)

crates/ruff_linter/src/linter.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::checkers::tokens::check_tokens;
2525
use crate::directives::Directives;
2626
use crate::doc_lines::{doc_lines_from_ast, doc_lines_from_tokens};
2727
use crate::fix::{FixResult, fix_file};
28+
use crate::message::SecondaryCode;
2829
use crate::noqa::add_noqa;
2930
use crate::package::PackageRoot;
3031
use crate::preview::is_py314_support_enabled;
@@ -93,25 +94,25 @@ struct FixCount {
9394

9495
/// A mapping from a noqa code to the corresponding lint name and a count of applied fixes.
9596
#[derive(Debug, Default, PartialEq)]
96-
pub struct FixTable(hashbrown::HashMap<String, FixCount, rustc_hash::FxBuildHasher>);
97+
pub struct FixTable(hashbrown::HashMap<SecondaryCode, FixCount, rustc_hash::FxBuildHasher>);
9798

9899
impl FixTable {
99100
pub fn counts(&self) -> impl Iterator<Item = usize> {
100101
self.0.values().map(|fc| fc.count)
101102
}
102103

103-
pub fn entry<'a>(&'a mut self, code: &'a str) -> FixTableEntry<'a> {
104+
pub fn entry<'a>(&'a mut self, code: &'a SecondaryCode) -> FixTableEntry<'a> {
104105
FixTableEntry(self.0.entry_ref(code))
105106
}
106107

107-
pub fn iter(&self) -> impl Iterator<Item = (&str, &'static str, usize)> {
108+
pub fn iter(&self) -> impl Iterator<Item = (&SecondaryCode, &'static str, usize)> {
108109
self.0
109110
.iter()
110-
.map(|(code, FixCount { rule_name, count })| (code.as_str(), *rule_name, *count))
111+
.map(|(code, FixCount { rule_name, count })| (code, *rule_name, *count))
111112
}
112113

113-
pub fn keys(&self) -> impl Iterator<Item = &str> {
114-
self.0.keys().map(String::as_str)
114+
pub fn keys(&self) -> impl Iterator<Item = &SecondaryCode> {
115+
self.0.keys()
115116
}
116117

117118
pub fn is_empty(&self) -> bool {
@@ -120,7 +121,7 @@ impl FixTable {
120121
}
121122

122123
pub struct FixTableEntry<'a>(
123-
hashbrown::hash_map::EntryRef<'a, 'a, String, str, FixCount, FxBuildHasher>,
124+
hashbrown::hash_map::EntryRef<'a, 'a, SecondaryCode, SecondaryCode, FixCount, FxBuildHasher>,
124125
);
125126

126127
impl<'a> FixTableEntry<'a> {
@@ -678,7 +679,10 @@ pub fn lint_fix<'a>(
678679
}
679680
}
680681

681-
fn collect_rule_codes<'a>(rules: impl IntoIterator<Item = &'a str>) -> String {
682+
fn collect_rule_codes<T>(rules: impl IntoIterator<Item = T>) -> String
683+
where
684+
T: Ord + PartialEq + std::fmt::Display,
685+
{
682686
rules.into_iter().sorted_unstable().dedup().join(", ")
683687
}
684688

@@ -720,7 +724,7 @@ fn report_fix_syntax_error<'a>(
720724
path: &Path,
721725
transformed: &str,
722726
error: &ParseError,
723-
rules: impl IntoIterator<Item = &'a str>,
727+
rules: impl IntoIterator<Item = &'a SecondaryCode>,
724728
) {
725729
let codes = collect_rule_codes(rules);
726730
if cfg!(debug_assertions) {

crates/ruff_linter/src/message/gitlab.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ impl Serialize for SerializedMessages<'_> {
9191
fingerprints.insert(message_fingerprint);
9292

9393
let (description, check_name) = if let Some(code) = diagnostic.secondary_code() {
94-
(diagnostic.body().to_string(), code)
94+
(diagnostic.body().to_string(), code.as_str())
9595
} else {
9696
let description = diagnostic.body();
9797
let description_without_prefix = description

crates/ruff_linter/src/message/mod.rs

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub use sarif::SarifEmitter;
2525
pub use text::TextEmitter;
2626

2727
use crate::Fix;
28+
use crate::codes::NoqaCode;
2829
use crate::logging::DisplayParseErrorType;
2930
use crate::registry::Rule;
3031
use crate::{Locator, Violation};
@@ -61,7 +62,7 @@ pub struct OldDiagnostic {
6162
pub fix: Option<Fix>,
6263
pub parent: Option<TextSize>,
6364
pub(crate) noqa_offset: Option<TextSize>,
64-
pub(crate) secondary_code: Option<String>,
65+
pub(crate) secondary_code: Option<SecondaryCode>,
6566
}
6667

6768
impl OldDiagnostic {
@@ -114,7 +115,7 @@ impl OldDiagnostic {
114115
fix,
115116
parent,
116117
noqa_offset,
117-
secondary_code: Some(rule.noqa_code().to_string()),
118+
secondary_code: Some(SecondaryCode(rule.noqa_code().to_string())),
118119
}
119120
}
120121

@@ -247,8 +248,8 @@ impl OldDiagnostic {
247248
}
248249

249250
/// Returns the noqa code for the diagnostic message as a string.
250-
pub fn secondary_code(&self) -> Option<&str> {
251-
self.secondary_code.as_deref()
251+
pub fn secondary_code(&self) -> Option<&SecondaryCode> {
252+
self.secondary_code.as_ref()
252253
}
253254

254255
/// Returns the URL for the rule documentation, if it exists.
@@ -383,6 +384,76 @@ impl<'a> EmitterContext<'a> {
383384
}
384385
}
385386

387+
/// A secondary identifier for a lint diagnostic.
388+
///
389+
/// For Ruff rules this means the noqa code.
390+
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
391+
pub struct SecondaryCode(String);
392+
393+
impl SecondaryCode {
394+
pub fn new(code: String) -> Self {
395+
Self(code)
396+
}
397+
398+
pub fn as_str(&self) -> &str {
399+
&self.0
400+
}
401+
}
402+
403+
impl Display for SecondaryCode {
404+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
405+
f.write_str(&self.0)
406+
}
407+
}
408+
409+
impl std::ops::Deref for SecondaryCode {
410+
type Target = str;
411+
412+
fn deref(&self) -> &Self::Target {
413+
&self.0
414+
}
415+
}
416+
417+
impl PartialEq<&str> for SecondaryCode {
418+
fn eq(&self, other: &&str) -> bool {
419+
self.0 == *other
420+
}
421+
}
422+
423+
impl PartialEq<SecondaryCode> for &str {
424+
fn eq(&self, other: &SecondaryCode) -> bool {
425+
other.eq(self)
426+
}
427+
}
428+
429+
impl PartialEq<NoqaCode> for SecondaryCode {
430+
fn eq(&self, other: &NoqaCode) -> bool {
431+
&self.as_str() == other
432+
}
433+
}
434+
435+
impl PartialEq<SecondaryCode> for NoqaCode {
436+
fn eq(&self, other: &SecondaryCode) -> bool {
437+
other.eq(self)
438+
}
439+
}
440+
441+
impl serde::Serialize for SecondaryCode {
442+
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
443+
where
444+
S: serde::Serializer,
445+
{
446+
serializer.serialize_str(&self.0)
447+
}
448+
}
449+
450+
// for `hashbrown::EntryRef`
451+
impl From<&SecondaryCode> for SecondaryCode {
452+
fn from(value: &SecondaryCode) -> Self {
453+
value.clone()
454+
}
455+
}
456+
386457
#[cfg(test)]
387458
mod tests {
388459
use rustc_hash::FxHashMap;

crates/ruff_linter/src/message/sarif.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use ruff_source_file::OneIndexed;
99

1010
use crate::VERSION;
1111
use crate::fs::normalize_path;
12-
use crate::message::{Emitter, EmitterContext, OldDiagnostic};
12+
use crate::message::{Emitter, EmitterContext, OldDiagnostic, SecondaryCode};
1313
use crate::registry::{Linter, RuleNamespace};
1414

1515
pub struct SarifEmitter;
@@ -53,15 +53,15 @@ impl Emitter for SarifEmitter {
5353
#[derive(Debug, Clone)]
5454
struct SarifRule<'a> {
5555
name: &'a str,
56-
code: &'a str,
56+
code: &'a SecondaryCode,
5757
linter: &'a str,
5858
summary: &'a str,
5959
explanation: Option<&'a str>,
6060
url: Option<String>,
6161
}
6262

63-
impl<'a> From<&'a str> for SarifRule<'a> {
64-
fn from(code: &'a str) -> Self {
63+
impl<'a> From<&'a SecondaryCode> for SarifRule<'a> {
64+
fn from(code: &'a SecondaryCode) -> Self {
6565
// This is a manual re-implementation of Rule::from_code, but we also want the Linter. This
6666
// avoids calling Linter::parse_code twice.
6767
let (linter, suffix) = Linter::parse_code(code).unwrap();
@@ -110,7 +110,7 @@ impl Serialize for SarifRule<'_> {
110110

111111
#[derive(Debug)]
112112
struct SarifResult<'a> {
113-
code: Option<&'a str>,
113+
code: Option<&'a SecondaryCode>,
114114
level: String,
115115
message: String,
116116
uri: String,

crates/ruff_linter/src/message/text.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::Locator;
1414
use crate::fs::relativize_path;
1515
use crate::line_width::{IndentWidth, LineWidthBuilder};
1616
use crate::message::diff::Diff;
17-
use crate::message::{Emitter, EmitterContext, OldDiagnostic};
17+
use crate::message::{Emitter, EmitterContext, OldDiagnostic, SecondaryCode};
1818
use crate::settings::types::UnsafeFixes;
1919

2020
bitflags! {
@@ -252,7 +252,11 @@ impl Display for MessageCodeFrame<'_> {
252252
)
253253
.fix_up_empty_spans_after_line_terminator();
254254

255-
let label = self.message.secondary_code().unwrap_or_default();
255+
let label = self
256+
.message
257+
.secondary_code()
258+
.map(SecondaryCode::as_str)
259+
.unwrap_or_default();
256260

257261
let line_start = self.notebook_index.map_or_else(
258262
|| start_index.get(),

0 commit comments

Comments
 (0)