Skip to content

Commit a3ee6bb

Browse files
authored
Return DiagnosticGuard from Checker::report_diagnostic (#18232)
Summary -- This PR adds a `DiagnosticGuard` type to ruff that is adapted from the `DiagnosticGuard` and `LintDiagnosticGuard` types from ty. This guard is returned by `Checker::report_diagnostic` and derefs to a `ruff_diagnostics::Diagnostic` (`OldDiagnostic`), allowing methods like `OldDiagnostic::set_fix` to be called on the result. On `Drop` the `DiagnosticGuard` pushes its contained `OldDiagnostic` to the `Checker`. The main motivation for this is to make a following PR adding a `SourceFile` to each diagnostic easier. For every rule where a `Checker` is available, this will now only require modifying `Checker::report_diagnostic` rather than all the rules. In the few cases where we need to create a diagnostic before we know if we actually want to emit it, there is a `DiagnosticGuard::defuse` method, which consumes the guard without emitting the diagnostic. I was able to restructure about half of the rules that naively called this to avoid calling it, but a handful of rules still need it. One of the fairly common patterns where `defuse` was needed initially was something like ```rust let diagnostic = Diagnostic::new(DiagnosticKind, range); if !checker.enabled(diagnostic.rule()) { return; } ``` So I also added a `Checker::checked_report_diagnostic` method that handles this check internally. That helped to avoid some additional `defuse` calls. The name is a bit repetitive, so I'm definitely open to suggestions there. I included a warning against using it in the docs since, as we've seen, the conversion from a diagnostic to a rule is actually pretty expensive. Test Plan -- Existing tests
1 parent b60ba75 commit a3ee6bb

File tree

590 files changed

+2962
-3769
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

590 files changed

+2962
-3769
lines changed
Lines changed: 27 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use ruff_diagnostics::{Diagnostic, Fix};
1+
use ruff_diagnostics::Fix;
22
use ruff_text_size::Ranged;
33

44
use crate::checkers::ast::Checker;
@@ -38,92 +38,64 @@ pub(crate) fn bindings(checker: &Checker) {
3838
.dummy_variable_rgx
3939
.is_match(binding.name(checker.source()))
4040
{
41-
let mut diagnostic = Diagnostic::new(
42-
pyflakes::rules::UnusedVariable {
43-
name: binding.name(checker.source()).to_string(),
44-
},
45-
binding.range(),
46-
);
47-
diagnostic.try_set_fix(|| {
48-
pyflakes::fixes::remove_exception_handler_assignment(binding, checker.locator)
41+
checker
42+
.report_diagnostic(
43+
pyflakes::rules::UnusedVariable {
44+
name: binding.name(checker.source()).to_string(),
45+
},
46+
binding.range(),
47+
)
48+
.try_set_fix(|| {
49+
pyflakes::fixes::remove_exception_handler_assignment(
50+
binding,
51+
checker.locator,
52+
)
4953
.map(Fix::safe_edit)
50-
});
51-
checker.report_diagnostic(diagnostic);
54+
});
5255
}
5356
}
5457
if checker.enabled(Rule::InvalidAllFormat) {
55-
if let Some(diagnostic) = pylint::rules::invalid_all_format(binding) {
56-
checker.report_diagnostic(diagnostic);
57-
}
58+
pylint::rules::invalid_all_format(checker, binding);
5859
}
5960
if checker.enabled(Rule::InvalidAllObject) {
60-
if let Some(diagnostic) = pylint::rules::invalid_all_object(binding) {
61-
checker.report_diagnostic(diagnostic);
62-
}
61+
pylint::rules::invalid_all_object(checker, binding);
6362
}
6463
if checker.enabled(Rule::NonAsciiName) {
65-
if let Some(diagnostic) = pylint::rules::non_ascii_name(binding, checker.locator) {
66-
checker.report_diagnostic(diagnostic);
67-
}
64+
pylint::rules::non_ascii_name(checker, binding);
6865
}
6966
if checker.enabled(Rule::UnconventionalImportAlias) {
70-
if let Some(diagnostic) = flake8_import_conventions::rules::unconventional_import_alias(
67+
flake8_import_conventions::rules::unconventional_import_alias(
7168
checker,
7269
binding,
7370
&checker.settings.flake8_import_conventions.aliases,
74-
) {
75-
checker.report_diagnostic(diagnostic);
76-
}
71+
);
7772
}
7873
if checker.enabled(Rule::UnaliasedCollectionsAbcSetImport) {
79-
if let Some(diagnostic) =
80-
flake8_pyi::rules::unaliased_collections_abc_set_import(checker, binding)
81-
{
82-
checker.report_diagnostic(diagnostic);
83-
}
74+
flake8_pyi::rules::unaliased_collections_abc_set_import(checker, binding);
8475
}
8576
if !checker.source_type.is_stub() && checker.enabled(Rule::UnquotedTypeAlias) {
8677
flake8_type_checking::rules::unquoted_type_alias(checker, binding);
8778
}
8879
if checker.enabled(Rule::UnsortedDunderSlots) {
89-
if let Some(diagnostic) = ruff::rules::sort_dunder_slots(checker, binding) {
90-
checker.report_diagnostic(diagnostic);
91-
}
80+
ruff::rules::sort_dunder_slots(checker, binding);
9281
}
9382
if checker.enabled(Rule::UsedDummyVariable) {
94-
if let Some(diagnostic) = ruff::rules::used_dummy_variable(checker, binding, binding_id)
95-
{
96-
checker.report_diagnostic(diagnostic);
97-
}
83+
ruff::rules::used_dummy_variable(checker, binding, binding_id);
9884
}
9985
if checker.enabled(Rule::AssignmentInAssert) {
100-
if let Some(diagnostic) = ruff::rules::assignment_in_assert(checker, binding) {
101-
checker.report_diagnostic(diagnostic);
102-
}
86+
ruff::rules::assignment_in_assert(checker, binding);
10387
}
10488
if checker.enabled(Rule::PytestUnittestRaisesAssertion) {
105-
if let Some(diagnostic) =
106-
flake8_pytest_style::rules::unittest_raises_assertion_binding(checker, binding)
107-
{
108-
checker.report_diagnostic(diagnostic);
109-
}
89+
flake8_pytest_style::rules::unittest_raises_assertion_binding(checker, binding);
11090
}
11191
if checker.enabled(Rule::ForLoopWrites) {
112-
if let Some(diagnostic) = refurb::rules::for_loop_writes_binding(checker, binding) {
113-
checker.report_diagnostic(diagnostic);
114-
}
92+
refurb::rules::for_loop_writes_binding(checker, binding);
11593
}
11694
if checker.enabled(Rule::CustomTypeVarForSelf) {
117-
if let Some(diagnostic) =
118-
flake8_pyi::rules::custom_type_var_instead_of_self(checker, binding)
119-
{
120-
checker.report_diagnostic(diagnostic);
121-
}
95+
flake8_pyi::rules::custom_type_var_instead_of_self(checker, binding);
12296
}
12397
if checker.enabled(Rule::PrivateTypeParameter) {
124-
if let Some(diagnostic) = pyupgrade::rules::private_type_parameter(checker, binding) {
125-
checker.report_diagnostic(diagnostic);
126-
}
98+
pyupgrade::rules::private_type_parameter(checker, binding);
12799
}
128100
}
129101
}

crates/ruff_linter/src/checkers/ast/analyze/deferred_scopes.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use ruff_diagnostics::{Diagnostic, Fix};
1+
use ruff_diagnostics::Fix;
22
use ruff_python_semantic::analyze::visibility;
33
use ruff_python_semantic::{Binding, BindingKind, Imported, ResolvedReference, ScopeKind};
44
use ruff_text_size::Ranged;
@@ -112,12 +112,12 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
112112
.map(|id| checker.semantic.reference(*id))
113113
.all(ResolvedReference::is_load)
114114
{
115-
checker.report_diagnostic(Diagnostic::new(
115+
checker.report_diagnostic(
116116
pylint::rules::GlobalVariableNotAssigned {
117117
name: (*name).to_string(),
118118
},
119119
binding.range(),
120-
));
120+
);
121121
}
122122
}
123123
}
@@ -146,12 +146,12 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
146146
if scope.kind.is_generator() {
147147
continue;
148148
}
149-
checker.report_diagnostic(Diagnostic::new(
149+
checker.report_diagnostic(
150150
pylint::rules::RedefinedArgumentFromLocal {
151151
name: name.to_string(),
152152
},
153153
binding.range(),
154-
));
154+
);
155155
}
156156
}
157157
}
@@ -186,13 +186,13 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
186186
continue;
187187
}
188188

189-
checker.report_diagnostic(Diagnostic::new(
189+
checker.report_diagnostic(
190190
pyflakes::rules::ImportShadowedByLoopVar {
191191
name: name.to_string(),
192192
row: checker.compute_source_row(shadowed.start()),
193193
},
194194
binding.range(),
195-
));
195+
);
196196
}
197197
}
198198
}
@@ -331,7 +331,7 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
331331
// Create diagnostics for each statement.
332332
for (source, entries) in &redefinitions {
333333
for (shadowed, binding) in entries {
334-
let mut diagnostic = Diagnostic::new(
334+
let mut diagnostic = checker.report_diagnostic(
335335
pyflakes::rules::RedefinedWhileUnused {
336336
name: binding.name(checker.source()).to_string(),
337337
row: checker.compute_source_row(shadowed.start()),
@@ -346,8 +346,6 @@ pub(crate) fn deferred_scopes(checker: &Checker) {
346346
if let Some(fix) = source.as_ref().and_then(|source| fixes.get(source)) {
347347
diagnostic.set_fix(fix.clone());
348348
}
349-
350-
checker.report_diagnostic(diagnostic);
351349
}
352350
}
353351
}

crates/ruff_linter/src/checkers/ast/analyze/except_handler.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,7 @@ pub(crate) fn except_handler(except_handler: &ExceptHandler, checker: &Checker)
1717
range: _,
1818
}) => {
1919
if checker.enabled(Rule::BareExcept) {
20-
if let Some(diagnostic) = pycodestyle::rules::bare_except(
21-
type_.as_deref(),
22-
body,
23-
except_handler,
24-
checker.locator,
25-
) {
26-
checker.report_diagnostic(diagnostic);
27-
}
20+
pycodestyle::rules::bare_except(checker, type_.as_deref(), body, except_handler);
2821
}
2922
if checker.enabled(Rule::RaiseWithoutFromInsideExcept) {
3023
flake8_bugbear::rules::raise_without_from_inside_except(

crates/ruff_linter/src/checkers/ast/analyze/expression.rs

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Operator};
22
use ruff_python_literal::cformat::{CFormatError, CFormatErrorType};
33

4-
use ruff_diagnostics::Diagnostic;
5-
64
use ruff_python_ast::types::Node;
75
use ruff_python_semantic::ScopeKind;
86
use ruff_python_semantic::analyze::typing;
@@ -195,14 +193,13 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
195193
let check_too_many_expressions = checker.enabled(Rule::ExpressionsInStarAssignment);
196194
let check_two_starred_expressions =
197195
checker.enabled(Rule::MultipleStarredExpressions);
198-
if let Some(diagnostic) = pyflakes::rules::starred_expressions(
196+
pyflakes::rules::starred_expressions(
197+
checker,
199198
elts,
200199
check_too_many_expressions,
201200
check_two_starred_expressions,
202201
expr.range(),
203-
) {
204-
checker.report_diagnostic(diagnostic);
205-
}
202+
);
206203
}
207204
}
208205
Expr::Name(ast::ExprName { id, ctx, range }) => {
@@ -527,12 +524,12 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
527524
match pyflakes::format::FormatSummary::try_from(string_value.to_str()) {
528525
Err(e) => {
529526
if checker.enabled(Rule::StringDotFormatInvalidFormat) {
530-
checker.report_diagnostic(Diagnostic::new(
527+
checker.report_diagnostic(
531528
pyflakes::rules::StringDotFormatInvalidFormat {
532529
message: pyflakes::format::error_to_string(&e),
533530
},
534531
location,
535-
));
532+
);
536533
}
537534
}
538535
Ok(summary) => {
@@ -936,9 +933,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
936933
pylint::rules::repeated_keyword_argument(checker, call);
937934
}
938935
if checker.enabled(Rule::PytestPatchWithLambda) {
939-
if let Some(diagnostic) = flake8_pytest_style::rules::patch_with_lambda(call) {
940-
checker.report_diagnostic(diagnostic);
941-
}
936+
flake8_pytest_style::rules::patch_with_lambda(checker, call);
942937
}
943938
if checker.any_enabled(&[
944939
Rule::PytestParametrizeNamesWrongType,
@@ -1286,22 +1281,22 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
12861281
..
12871282
}) => {
12881283
if checker.enabled(Rule::PercentFormatUnsupportedFormatCharacter) {
1289-
checker.report_diagnostic(Diagnostic::new(
1284+
checker.report_diagnostic(
12901285
pyflakes::rules::PercentFormatUnsupportedFormatCharacter {
12911286
char: c,
12921287
},
12931288
location,
1294-
));
1289+
);
12951290
}
12961291
}
12971292
Err(e) => {
12981293
if checker.enabled(Rule::PercentFormatInvalidFormat) {
1299-
checker.report_diagnostic(Diagnostic::new(
1294+
checker.report_diagnostic(
13001295
pyflakes::rules::PercentFormatInvalidFormat {
13011296
message: e.to_string(),
13021297
},
13031298
location,
1304-
));
1299+
);
13051300
}
13061301
}
13071302
Ok(summary) => {
@@ -1365,10 +1360,7 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
13651360
op: Operator::Add, ..
13661361
}) => {
13671362
if checker.enabled(Rule::ExplicitStringConcatenation) {
1368-
if let Some(diagnostic) = flake8_implicit_str_concat::rules::explicit(expr, checker)
1369-
{
1370-
checker.report_diagnostic(diagnostic);
1371-
}
1363+
flake8_implicit_str_concat::rules::explicit(checker, expr);
13721364
}
13731365
if checker.enabled(Rule::CollectionLiteralConcatenation) {
13741366
ruff::rules::collection_literal_concatenation(checker, expr);

0 commit comments

Comments
 (0)