Skip to content

Commit 4ea7ff5

Browse files
committed
Simplify LinterResult, avoid cloning ParseError
1 parent c54a6c1 commit 4ea7ff5

File tree

10 files changed

+80
-96
lines changed

10 files changed

+80
-96
lines changed

crates/ruff/src/diagnostics.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,8 @@ pub(crate) fn lint_path(
260260
// Lint the file.
261261
let (
262262
LinterResult {
263-
data: messages,
264-
error: parse_error,
263+
messages,
264+
has_error,
265265
},
266266
transformed,
267267
fixed,
@@ -330,7 +330,7 @@ pub(crate) fn lint_path(
330330

331331
if let Some((cache, relative_path, key)) = caching {
332332
// We don't cache parsing errors.
333-
if parse_error.is_none() {
333+
if !has_error {
334334
// `FixMode::Apply` and `FixMode::Diff` rely on side-effects (writing to disk,
335335
// and writing the diff to stdout, respectively). If a file has diagnostics, we
336336
// need to avoid reading from and writing to the cache in these modes.
@@ -396,7 +396,7 @@ pub(crate) fn lint_stdin(
396396
};
397397

398398
// Lint the inputs.
399-
let (LinterResult { data: messages, .. }, transformed, fixed) =
399+
let (LinterResult { messages, .. }, transformed, fixed) =
400400
if matches!(fix_mode, flags::FixMode::Apply | flags::FixMode::Diff) {
401401
if let Ok(FixerResult {
402402
result,

crates/ruff_benchmark/benches/linter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn benchmark_linter(mut group: BenchmarkGroup, settings: &LinterSettings) {
7373
);
7474

7575
// Assert that file contains no parse errors
76-
assert_eq!(result.error, None);
76+
assert!(!result.has_error);
7777
},
7878
criterion::BatchSize::SmallInput,
7979
);

crates/ruff_linter/src/linter.rs

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -36,29 +36,19 @@ use crate::settings::{flags, LinterSettings};
3636
use crate::source_kind::SourceKind;
3737
use crate::{directives, fs};
3838

39-
/// A [`Result`]-like type that returns both data and an error. Used to return
40-
/// diagnostics even in the face of parse errors, since many diagnostics can be
41-
/// generated without a full AST.
42-
pub struct LinterResult<T> {
43-
pub data: T,
44-
pub error: Option<ParseError>,
45-
}
46-
47-
impl<T> LinterResult<T> {
48-
const fn new(data: T, error: Option<ParseError>) -> Self {
49-
Self { data, error }
50-
}
51-
52-
fn map<U, F: FnOnce(T) -> U>(self, f: F) -> LinterResult<U> {
53-
LinterResult::new(f(self.data), self.error)
54-
}
39+
pub struct LinterResult {
40+
/// A collection of diagnostic messages generated by the linter.
41+
pub messages: Vec<Message>,
42+
/// A flag indicating the presence of syntax errors in the source file.
43+
/// If `true`, at least one syntax error was detected in the source file.
44+
pub has_error: bool,
5545
}
5646

5747
pub type FixTable = FxHashMap<Rule, usize>;
5848

5949
pub struct FixerResult<'a> {
6050
/// The result returned by the linter, after applying any fixes.
61-
pub result: LinterResult<Vec<Message>>,
51+
pub result: LinterResult,
6252
/// The resulting source code, after applying any fixes.
6353
pub transformed: Cow<'a, SourceKind>,
6454
/// The number of fixes applied for each [`Rule`].
@@ -80,7 +70,7 @@ pub fn check_path(
8070
source_kind: &SourceKind,
8171
source_type: PySourceType,
8272
parsed: &Parsed<ModModule>,
83-
) -> LinterResult<Vec<Diagnostic>> {
73+
) -> Vec<Diagnostic> {
8474
// Aggregate all diagnostics.
8575
let mut diagnostics = vec![];
8676

@@ -328,7 +318,7 @@ pub fn check_path(
328318
}
329319
}
330320

331-
LinterResult::new(diagnostics, parsed.errors().iter().next().cloned())
321+
diagnostics
332322
}
333323

334324
const MAX_ITERATIONS: usize = 100;
@@ -362,9 +352,7 @@ pub fn add_noqa_to_path(
362352
);
363353

364354
// Generate diagnostics, ignoring any existing `noqa` directives.
365-
let LinterResult {
366-
data: diagnostics, ..
367-
} = check_path(
355+
let diagnostics = check_path(
368356
path,
369357
package,
370358
&locator,
@@ -401,7 +389,7 @@ pub fn lint_only(
401389
source_kind: &SourceKind,
402390
source_type: PySourceType,
403391
source: ParseSource,
404-
) -> LinterResult<Vec<Message>> {
392+
) -> LinterResult {
405393
let parsed = source.into_parsed(source_kind, source_type);
406394

407395
// Map row and column locations to byte slices (lazily).
@@ -422,7 +410,7 @@ pub fn lint_only(
422410
);
423411

424412
// Generate diagnostics.
425-
let result = check_path(
413+
let diagnostics = check_path(
426414
path,
427415
package,
428416
&locator,
@@ -436,7 +424,10 @@ pub fn lint_only(
436424
&parsed,
437425
);
438426

439-
result.map(|diagnostics| diagnostics_to_messages(diagnostics, path, &locator, &directives))
427+
LinterResult {
428+
messages: diagnostics_to_messages(diagnostics, path, &locator, &directives),
429+
has_error: !parsed.is_valid(),
430+
}
440431
}
441432

442433
/// Convert from diagnostics to messages.
@@ -513,7 +504,7 @@ pub fn lint_fix<'a>(
513504
);
514505

515506
// Generate diagnostics.
516-
let result = check_path(
507+
let diagnostics = check_path(
517508
path,
518509
package,
519510
&locator,
@@ -528,19 +519,21 @@ pub fn lint_fix<'a>(
528519
);
529520

530521
if iterations == 0 {
531-
parseable = result.error.is_none();
522+
parseable = parsed.is_valid();
532523
} else {
533524
// If the source code was parseable on the first pass, but is no
534525
// longer parseable on a subsequent pass, then we've introduced a
535526
// syntax error. Return the original code.
536-
if parseable && result.error.is_some() {
537-
report_fix_syntax_error(
538-
path,
539-
transformed.source_code(),
540-
&result.error.unwrap(),
541-
fixed.keys().copied(),
542-
);
543-
return Err(anyhow!("Fix introduced a syntax error"));
527+
if parseable {
528+
if let [error, ..] = parsed.errors() {
529+
report_fix_syntax_error(
530+
path,
531+
transformed.source_code(),
532+
error,
533+
fixed.keys().copied(),
534+
);
535+
return Err(anyhow!("Fix introduced a syntax error"));
536+
}
544537
}
545538
}
546539

@@ -549,7 +542,7 @@ pub fn lint_fix<'a>(
549542
code: fixed_contents,
550543
fixes: applied,
551544
source_map,
552-
}) = fix_file(&result.data, &locator, unsafe_fixes)
545+
}) = fix_file(&diagnostics, &locator, unsafe_fixes)
553546
{
554547
if iterations < MAX_ITERATIONS {
555548
// Count the number of fixed errors.
@@ -566,13 +559,14 @@ pub fn lint_fix<'a>(
566559
continue;
567560
}
568561

569-
report_failed_to_converge_error(path, transformed.source_code(), &result.data);
562+
report_failed_to_converge_error(path, transformed.source_code(), &diagnostics);
570563
}
571564

572565
return Ok(FixerResult {
573-
result: result.map(|diagnostics| {
574-
diagnostics_to_messages(diagnostics, path, &locator, &directives)
575-
}),
566+
result: LinterResult {
567+
messages: diagnostics_to_messages(diagnostics, path, &locator, &directives),
568+
has_error: !parseable,
569+
},
576570
transformed,
577571
fixed,
578572
});

crates/ruff_linter/src/rules/pyflakes/mod.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ mod tests {
2222
use ruff_source_file::Locator;
2323
use ruff_text_size::Ranged;
2424

25-
use crate::linter::{check_path, LinterResult};
25+
use crate::linter::check_path;
2626
use crate::registry::{AsRule, Linter, Rule};
2727
use crate::rules::pyflakes;
2828
use crate::settings::types::PreviewMode;
@@ -649,10 +649,7 @@ mod tests {
649649
&locator,
650650
&indexer,
651651
);
652-
let LinterResult {
653-
data: mut diagnostics,
654-
..
655-
} = check_path(
652+
let mut diagnostics = check_path(
656653
Path::new("<filename>"),
657654
None,
658655
&locator,

crates/ruff_linter/src/test.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use ruff_text_size::Ranged;
2222

2323
use crate::directives;
2424
use crate::fix::{fix_file, FixResult};
25-
use crate::linter::{check_path, LinterResult};
25+
use crate::linter::check_path;
2626
use crate::message::{Emitter, EmitterContext, Message, TextEmitter};
2727
use crate::packaging::detect_package_root;
2828
use crate::registry::AsRule;
@@ -119,10 +119,7 @@ pub(crate) fn test_contents<'a>(
119119
&locator,
120120
&indexer,
121121
);
122-
let LinterResult {
123-
data: diagnostics,
124-
error,
125-
} = check_path(
122+
let diagnostics = check_path(
126123
path,
127124
path.parent()
128125
.and_then(|parent| detect_package_root(parent, &settings.namespace_packages)),
@@ -137,7 +134,7 @@ pub(crate) fn test_contents<'a>(
137134
&parsed,
138135
);
139136

140-
let source_has_errors = error.is_some();
137+
let source_has_errors = !parsed.is_valid();
141138

142139
// Detect fixes that don't converge after multiple iterations.
143140
let mut iterations = 0;
@@ -186,10 +183,7 @@ pub(crate) fn test_contents<'a>(
186183
&indexer,
187184
);
188185

189-
let LinterResult {
190-
data: fixed_diagnostics,
191-
error: fixed_error,
192-
} = check_path(
186+
let fixed_diagnostics = check_path(
193187
path,
194188
None,
195189
&locator,
@@ -203,13 +197,13 @@ pub(crate) fn test_contents<'a>(
203197
&parsed,
204198
);
205199

206-
if let Some(fixed_error) = fixed_error {
200+
if let [fixed_error, ..] = parsed.errors() {
207201
if !source_has_errors {
208202
// Previous fix introduced a syntax error, abort
209203
let fixes = print_diagnostics(diagnostics, path, source_kind);
210204

211205
let mut syntax_diagnostics = Vec::new();
212-
syntax_error(&mut syntax_diagnostics, &fixed_error, &locator);
206+
syntax_error(&mut syntax_diagnostics, fixed_error, &locator);
213207
let syntax_errors = print_diagnostics(syntax_diagnostics, path, &transformed);
214208

215209
panic!(

crates/ruff_server/src/fix.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub(crate) fn fix_all(
6868
// which is inconsistent with how `ruff check --fix` works.
6969
let FixerResult {
7070
transformed,
71-
result: LinterResult { error, .. },
71+
result: LinterResult { has_error, .. },
7272
..
7373
} = ruff_linter::linter::lint_fix(
7474
&query.virtual_file_path(),
@@ -80,11 +80,9 @@ pub(crate) fn fix_all(
8080
source_type,
8181
)?;
8282

83-
if let Some(error) = error {
83+
if has_error {
8484
// abort early if a parsing error occurred
85-
return Err(anyhow::anyhow!(
86-
"A parsing error occurred during `fix_all`: {error}"
87-
));
85+
return Err(anyhow::anyhow!("A parsing error occurred during `fix_all`"));
8886
}
8987

9088
// fast path: if `transformed` is still borrowed, no changes were made and we can return early

0 commit comments

Comments
 (0)