Skip to content

Commit 5a876ed

Browse files
authored
Visit Identifier node as part of the SourceOrderVisitor (#17110)
## Summary I don't remember exactly when we made `Identifier` a node but it is now considered a node (it implements `AnyNodeRef`, it has a range). However, we never updated the `SourceOrderVisitor` to visit identifiers because we never had a use case for it and visiting new nodes can change how the formatter associates comments (breaking change!). This PR updates the `SourceOrderVisitor` to visit identifiers and changes the formatter comment visitor to skip identifiers (updating the visitor might be desired because it could help simplifying some comment placement logic but this is out of scope for this PR). ## Test Plan Tests, updated snapshot tests
1 parent 49c2599 commit 5a876ed

File tree

10 files changed

+142
-35
lines changed

10 files changed

+142
-35
lines changed

crates/ruff_linter/src/rules/ruff/rules/suppression_comment_visitor.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ where
195195
}
196196
}
197197
}
198+
199+
fn visit_identifier(&mut self, _identifier: &'ast ruff_python_ast::Identifier) {
200+
// Skip identifiers, matching the formatter comment extraction
201+
}
198202
}
199203

200204
#[derive(Clone, Debug)]

crates/ruff_python_ast/src/node.rs

Lines changed: 85 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use ruff_text_size::Ranged;
2+
13
use crate::visitor::source_order::SourceOrderVisitor;
24
use crate::{
35
self as ast, Alias, AnyNodeRef, AnyParameterRef, ArgOrKeyword, MatchCase, PatternArguments,
@@ -35,13 +37,17 @@ impl ast::StmtFunctionDef {
3537
decorator_list,
3638
returns,
3739
type_params,
38-
..
40+
range: _,
41+
is_async: _,
42+
name,
3943
} = self;
4044

4145
for decorator in decorator_list {
4246
visitor.visit_decorator(decorator);
4347
}
4448

49+
visitor.visit_identifier(name);
50+
4551
if let Some(type_params) = type_params {
4652
visitor.visit_type_params(type_params);
4753
}
@@ -66,13 +72,16 @@ impl ast::StmtClassDef {
6672
body,
6773
decorator_list,
6874
type_params,
69-
..
75+
name,
76+
range: _,
7077
} = self;
7178

7279
for decorator in decorator_list {
7380
visitor.visit_decorator(decorator);
7481
}
7582

83+
visitor.visit_identifier(name);
84+
7685
if let Some(type_params) = type_params {
7786
visitor.visit_type_params(type_params);
7887
}
@@ -197,7 +206,8 @@ impl ast::StmtFor {
197206
iter,
198207
body,
199208
orelse,
200-
..
209+
range: _,
210+
is_async: _,
201211
} = self;
202212

203213
visitor.visit_expr(target);
@@ -379,34 +389,44 @@ impl ast::StmtImportFrom {
379389
{
380390
let ast::StmtImportFrom {
381391
range: _,
382-
module: _,
392+
module,
383393
names,
384394
level: _,
385395
} = self;
386396

397+
if let Some(module) = module {
398+
visitor.visit_identifier(module);
399+
}
400+
387401
for alias in names {
388402
visitor.visit_alias(alias);
389403
}
390404
}
391405
}
392406

393407
impl ast::StmtGlobal {
394-
#[inline]
395-
pub(crate) fn visit_source_order<'a, V>(&'a self, _visitor: &mut V)
408+
pub(crate) fn visit_source_order<'a, V>(&'a self, visitor: &mut V)
396409
where
397410
V: SourceOrderVisitor<'a> + ?Sized,
398411
{
399-
let ast::StmtGlobal { range: _, names: _ } = self;
412+
let ast::StmtGlobal { range: _, names } = self;
413+
414+
for name in names {
415+
visitor.visit_identifier(name);
416+
}
400417
}
401418
}
402419

403420
impl ast::StmtNonlocal {
404-
#[inline]
405-
pub(crate) fn visit_source_order<'a, V>(&'a self, _visitor: &mut V)
421+
pub(crate) fn visit_source_order<'a, V>(&'a self, visitor: &mut V)
406422
where
407423
V: SourceOrderVisitor<'a> + ?Sized,
408424
{
409-
let ast::StmtNonlocal { range: _, names: _ } = self;
425+
let ast::StmtNonlocal { range: _, names } = self;
426+
427+
for name in names {
428+
visitor.visit_identifier(name);
429+
}
410430
}
411431
}
412432

@@ -879,12 +899,13 @@ impl ast::ExprAttribute {
879899
{
880900
let ast::ExprAttribute {
881901
value,
882-
attr: _,
902+
attr,
883903
ctx: _,
884904
range: _,
885905
} = self;
886906

887907
visitor.visit_expr(value);
908+
visitor.visit_identifier(attr);
888909
}
889910
}
890911

@@ -1014,12 +1035,17 @@ impl ast::ExceptHandlerExceptHandler {
10141035
let ast::ExceptHandlerExceptHandler {
10151036
range: _,
10161037
type_,
1017-
name: _,
1038+
name,
10181039
body,
10191040
} = self;
10201041
if let Some(expr) = type_ {
10211042
visitor.visit_expr(expr);
10221043
}
1044+
1045+
if let Some(name) = name {
1046+
visitor.visit_identifier(name);
1047+
}
1048+
10231049
visitor.visit_body(body);
10241050
}
10251051
}
@@ -1064,13 +1090,26 @@ impl ast::PatternMatchMapping {
10641090
let ast::PatternMatchMapping {
10651091
keys,
10661092
patterns,
1093+
rest,
10671094
range: _,
1068-
rest: _,
10691095
} = self;
1096+
1097+
let mut rest = rest.as_ref();
1098+
10701099
for (key, pattern) in keys.iter().zip(patterns) {
1100+
if let Some(rest_identifier) = rest {
1101+
if rest_identifier.start() < key.start() {
1102+
visitor.visit_identifier(rest_identifier);
1103+
rest = None;
1104+
}
1105+
}
10711106
visitor.visit_expr(key);
10721107
visitor.visit_pattern(pattern);
10731108
}
1109+
1110+
if let Some(rest) = rest {
1111+
visitor.visit_identifier(rest);
1112+
}
10741113
}
10751114
}
10761115

@@ -1090,12 +1129,15 @@ impl ast::PatternMatchClass {
10901129
}
10911130

10921131
impl ast::PatternMatchStar {
1093-
#[inline]
1094-
pub(crate) fn visit_source_order<'a, V>(&'a self, _visitor: &mut V)
1132+
pub(crate) fn visit_source_order<'a, V>(&'a self, visitor: &mut V)
10951133
where
10961134
V: SourceOrderVisitor<'a> + ?Sized,
10971135
{
1098-
let ast::PatternMatchStar { range: _, name: _ } = self;
1136+
let ast::PatternMatchStar { range: _, name } = self;
1137+
1138+
if let Some(name) = name {
1139+
visitor.visit_identifier(name);
1140+
}
10991141
}
11001142
}
11011143

@@ -1107,11 +1149,15 @@ impl ast::PatternMatchAs {
11071149
let ast::PatternMatchAs {
11081150
pattern,
11091151
range: _,
1110-
name: _,
1152+
name,
11111153
} = self;
11121154
if let Some(pattern) = pattern {
11131155
visitor.visit_pattern(pattern);
11141156
}
1157+
1158+
if let Some(name) = name {
1159+
visitor.visit_identifier(name);
1160+
}
11151161
}
11161162
}
11171163

@@ -1155,10 +1201,11 @@ impl ast::PatternKeyword {
11551201
{
11561202
let PatternKeyword {
11571203
range: _,
1158-
attr: _,
1204+
attr,
11591205
pattern,
11601206
} = self;
11611207

1208+
visitor.visit_identifier(attr);
11621209
visitor.visit_pattern(pattern);
11631210
}
11641211
}
@@ -1221,10 +1268,11 @@ impl ast::Parameter {
12211268
{
12221269
let ast::Parameter {
12231270
range: _,
1224-
name: _,
1271+
name,
12251272
annotation,
12261273
} = self;
12271274

1275+
visitor.visit_identifier(name);
12281276
if let Some(expr) = annotation {
12291277
visitor.visit_annotation(expr);
12301278
}
@@ -1255,25 +1303,32 @@ impl ast::Keyword {
12551303
{
12561304
let ast::Keyword {
12571305
range: _,
1258-
arg: _,
1306+
arg,
12591307
value,
12601308
} = self;
12611309

1310+
if let Some(arg) = arg {
1311+
visitor.visit_identifier(arg);
1312+
}
12621313
visitor.visit_expr(value);
12631314
}
12641315
}
12651316

12661317
impl Alias {
1267-
#[inline]
1268-
pub(crate) fn visit_source_order<'a, V>(&'a self, _visitor: &mut V)
1318+
pub(crate) fn visit_source_order<'a, V>(&'a self, visitor: &mut V)
12691319
where
12701320
V: SourceOrderVisitor<'a> + ?Sized,
12711321
{
12721322
let ast::Alias {
12731323
range: _,
1274-
name: _,
1275-
asname: _,
1324+
name,
1325+
asname,
12761326
} = self;
1327+
1328+
visitor.visit_identifier(name);
1329+
if let Some(asname) = asname {
1330+
visitor.visit_identifier(asname);
1331+
}
12771332
}
12781333
}
12791334

@@ -1354,10 +1409,11 @@ impl ast::TypeParamTypeVar {
13541409
let ast::TypeParamTypeVar {
13551410
bound,
13561411
default,
1357-
name: _,
1412+
name,
13581413
range: _,
13591414
} = self;
13601415

1416+
visitor.visit_identifier(name);
13611417
if let Some(expr) = bound {
13621418
visitor.visit_expr(expr);
13631419
}
@@ -1375,9 +1431,10 @@ impl ast::TypeParamTypeVarTuple {
13751431
{
13761432
let ast::TypeParamTypeVarTuple {
13771433
range: _,
1378-
name: _,
1434+
name,
13791435
default,
13801436
} = self;
1437+
visitor.visit_identifier(name);
13811438
if let Some(expr) = default {
13821439
visitor.visit_expr(expr);
13831440
}
@@ -1392,9 +1449,10 @@ impl ast::TypeParamParamSpec {
13921449
{
13931450
let ast::TypeParamParamSpec {
13941451
range: _,
1395-
name: _,
1452+
name,
13961453
default,
13971454
} = self;
1455+
visitor.visit_identifier(name);
13981456
if let Some(expr) = default {
13991457
visitor.visit_expr(expr);
14001458
}

crates/ruff_python_ast/src/visitor/source_order.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use crate::AnyNodeRef;
21
use crate::{
32
Alias, Arguments, BoolOp, BytesLiteral, CmpOp, Comprehension, Decorator, ElifElseClause,
43
ExceptHandler, Expr, FString, FStringElement, Keyword, MatchCase, Mod, Operator, Parameter,
54
ParameterWithDefault, Parameters, Pattern, PatternArguments, PatternKeyword, Singleton, Stmt,
65
StringLiteral, TypeParam, TypeParams, UnaryOp, WithItem,
76
};
7+
use crate::{AnyNodeRef, Identifier};
88

99
/// Visitor that traverses all nodes recursively in the order they appear in the source.
1010
///
@@ -170,6 +170,11 @@ pub trait SourceOrderVisitor<'a> {
170170
fn visit_bytes_literal(&mut self, bytes_literal: &'a BytesLiteral) {
171171
walk_bytes_literal(self, bytes_literal);
172172
}
173+
174+
#[inline]
175+
fn visit_identifier(&mut self, identifier: &'a Identifier) {
176+
walk_identifier(self, identifier);
177+
}
173178
}
174179

175180
pub fn walk_module<'a, V>(visitor: &mut V, module: &'a Mod)
@@ -580,3 +585,15 @@ where
580585
}
581586
visitor.leave_node(node);
582587
}
588+
589+
#[inline]
590+
pub fn walk_identifier<'a, V: SourceOrderVisitor<'a> + ?Sized>(
591+
visitor: &mut V,
592+
identifier: &'a Identifier,
593+
) {
594+
let node = AnyNodeRef::from(identifier);
595+
if visitor.enter_node(node).is_traverse() {
596+
identifier.visit_source_order(visitor);
597+
}
598+
visitor.leave_node(node);
599+
}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
---
22
source: crates/ruff_python_ast_integration_tests/tests/source_order.rs
33
expression: trace
4-
snapshot_kind: text
54
---
65
- ModModule
76
- StmtClassDef
7+
- Identifier
88
- TypeParams
99
- TypeParamTypeVar
10+
- Identifier
1011
- ExprName
1112
- TypeParamTypeVar
13+
- Identifier
1214
- TypeParamTypeVarTuple
15+
- Identifier
1316
- TypeParamParamSpec
17+
- Identifier
1418
- StmtExpr
1519
- ExprEllipsisLiteral
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
---
22
source: crates/ruff_python_ast_integration_tests/tests/source_order.rs
33
expression: trace
4-
snapshot_kind: text
54
---
65
- ModModule
76
- StmtFunctionDef
87
- Decorator
98
- ExprName
9+
- Identifier
1010
- Parameters
1111
- StmtPass
1212
- StmtClassDef
1313
- Decorator
1414
- ExprName
15+
- Identifier
1516
- StmtPass

0 commit comments

Comments
 (0)