Skip to content

Commit c3b84f7

Browse files
committed
Unified: Fix locations of various tokens
Handles things like `try!` (which is represented as two separate tokens -- we explicitly union their ranges) and "let" binding modifiers (where we reuse the bindingSpecifier, getting its location and string value for free).
1 parent be9464b commit c3b84f7

16 files changed

Lines changed: 163 additions & 103 deletions

‎shared/yeast/src/build.rs‎

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@ pub struct BuildCtx<'a, C: 'a = ()> {
3333
pub ast: &'a mut Ast,
3434
pub captures: &'a Captures,
3535
pub fresh: &'a FreshScope,
36-
/// Optional source range explicitly inherited by every synthetic node built
37-
/// through this context.
38-
pub source_range: Option<Range>,
3936
/// Source range of the node matched by the current rule.
4037
///
4138
/// The `rule!` macro applies this range to locally-created result roots
@@ -66,26 +63,6 @@ impl<'a, C> BuildCtx<'a, C> {
6663
ast,
6764
captures,
6865
fresh,
69-
source_range: None,
70-
matched_source_range: None,
71-
user_ctx,
72-
translator: None,
73-
created_nodes: BTreeSet::new(),
74-
}
75-
}
76-
77-
pub fn with_source_range(
78-
ast: &'a mut Ast,
79-
captures: &'a Captures,
80-
fresh: &'a FreshScope,
81-
source_range: Option<Range>,
82-
user_ctx: &'a mut C,
83-
) -> Self {
84-
Self {
85-
ast,
86-
captures,
87-
fresh,
88-
source_range,
8966
matched_source_range: None,
9067
user_ctx,
9168
translator: None,
@@ -107,7 +84,6 @@ impl<'a, C> BuildCtx<'a, C> {
10784
ast,
10885
captures,
10986
fresh,
110-
source_range: None,
11187
matched_source_range: source_range,
11288
user_ctx,
11389
translator: Some(translator),
@@ -153,7 +129,7 @@ impl<'a, C> BuildCtx<'a, C> {
153129
fields: BTreeMap<FieldId, Vec<Id>>,
154130
is_named: bool,
155131
) -> Id {
156-
self.create_node_with_range(kind, content, fields, is_named, self.source_range)
132+
self.create_node_with_range(kind, content, fields, is_named, None)
157133
}
158134

159135
/// Create a named token and record it as constructed by this rule invocation.
@@ -179,7 +155,7 @@ impl<'a, C> BuildCtx<'a, C> {
179155

180156
/// Create a named token using this context's explicit default source range.
181157
pub fn create_named_token(&mut self, kind: &'static str, content: String) -> Id {
182-
self.create_named_token_with_range(kind, content, self.source_range)
158+
self.create_named_token_with_range(kind, content, None)
183159
}
184160

185161
/// Finish the current rule invocation by applying the matched source range
@@ -275,11 +251,7 @@ impl<'a, C> BuildCtx<'a, C> {
275251
value: &str,
276252
source_range: Option<Range>,
277253
) -> Id {
278-
self.create_named_token_with_range(
279-
kind,
280-
value.to_string(),
281-
source_range.or(self.source_range),
282-
)
254+
self.create_named_token_with_range(kind, value.to_string(), source_range)
283255
}
284256

285257
/// Create a literal with an empty range at another node's start.
@@ -360,7 +332,6 @@ impl<C: Clone> BuildCtx<'_, C> {
360332
ast: &mut *self.ast,
361333
captures: self.captures,
362334
fresh: self.fresh,
363-
source_range: self.source_range,
364335
matched_source_range: self.matched_source_range,
365336
user_ctx: &mut child_user_ctx,
366337
translator: self.translator,

‎unified/extractor/src/languages/swift/swift.rs‎

Lines changed: 73 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use codeql_extractor::extractor::desugaring;
2-
use yeast::{ConcreteDesugarer, DesugaringConfig, PhaseKind, Rule, rule, tree, tree_at};
2+
use yeast::{
3+
ConcreteDesugarer, DesugaringConfig, PhaseKind, Rule, rule, tree, tree_at, tree_spanning,
4+
};
35

46
/// User context propagated from outer rules down to the inner rules that
57
/// emit the corresponding output declarations, so that each emitted node
@@ -97,7 +99,9 @@ fn and_chain(
9799
conds
98100
.into_iter()
99101
.reduce(|acc, elem| {
100-
tree!((binary_expr operator: (infix_operator "&&") left: {acc} right: {elem}))
102+
let operator_range = ctx.empty_source_range_between(acc, elem);
103+
let operator = ctx.literal_with_source_range("infix_operator", "&&", operator_range);
104+
tree!((binary_expr operator: {operator} left: {acc} right: {elem}))
101105
})
102106
.expect("control-flow statement must have at least one condition")
103107
}
@@ -123,21 +127,15 @@ fn member_chain(
123127
ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>,
124128
parts: Vec<yeast::Id>,
125129
) -> yeast::Id {
126-
// `member_chain` builds the imported expression inside the larger import
127-
// declaration rule. The imported expression should span the import path,
128-
// not the whole declaration including the `import` keyword.
129-
let source_range = ctx.source_range.take();
130130
let mut iter = parts.into_iter();
131131
let first = iter
132132
.next()
133133
.expect("identifier with `part:` must have at least one part");
134134
let init = tree!((identifier #{first}));
135-
let result = iter.fold(
135+
iter.fold(
136136
init,
137137
|acc, elem| tree!((member_access_expr base: {acc} member_name_node: (identifier #{elem}))),
138-
);
139-
ctx.source_range = source_range;
140-
result
138+
)
141139
}
142140

143141
fn translation_rules() -> Vec<Rule<SwiftContext>> {
@@ -480,14 +478,24 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
480478
// `enumCaseDecl` rule below) and are tagged `enum_case`, after any
481479
// `chained_declaration` tag.
482480
rule!(
483-
(enumCaseElement name: @name parameterClause: (enumCaseParameterClause parameters: _* @params))
484-
=>
485-
(class_like_declaration
486-
modifier: {ctx.outer_modifiers.clone()}
487-
modifier: {chained_modifier(&mut ctx)}
488-
modifier: (modifier "enum_case")
489-
name_node: (identifier #{name})
490-
member: (constructor_declaration parameter: {params} body: (block)))
481+
(enumCaseElement
482+
name: @name
483+
parameterClause: (enumCaseParameterClause parameters: _* @params)) @@element
484+
=>
485+
class_like_declaration {
486+
let body = tree!((block));
487+
let constructor = tree_at!(
488+
ctx,
489+
element,
490+
(constructor_declaration parameter: {params} body: {body})
491+
);
492+
tree!((class_like_declaration
493+
modifier: {ctx.outer_modifiers.clone()}
494+
modifier: {chained_modifier(&mut ctx)}
495+
modifier: (modifier "enum_case")
496+
name_node: (identifier #{name})
497+
member: {constructor}))
498+
}
491499
),
492500
rule!(
493501
(enumCaseElement name: @name rawValue: (initializerClause value: @val))
@@ -687,12 +695,17 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
687695
label: _? @@lbl
688696
expression: (functionCallExpr
689697
calledExpression: @constructor
690-
arguments: _* @elements))
698+
arguments: _* @elements) @@call)
691699
=>
692700
argument {
701+
let value = tree_at!(
702+
ctx,
703+
call,
704+
(call_expr callee: {constructor} argument: {elements})
705+
);
693706
tree!((argument
694707
name_node: (identifier #{lbl})?
695-
value: (call_expr callee: {constructor} argument: {elements})))
708+
value: {value}))
696709
}
697710
),
698711
rule!(
@@ -865,6 +878,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
865878
// form is matched first.
866879
rule!(
867880
(optionalBindingCondition
881+
bindingSpecifier: @@spec
868882
pattern: (identifierPattern identifier: @name)
869883
initializer: (initializerClause value: @val))
870884
=>
@@ -873,18 +887,20 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
873887
pattern: (call_expr
874888
callee: (member_access_expr base: (identifier "Optional") member_name_node: (identifier "some"))
875889
argument: (argument value: (expr_pattern
876-
modifier: (modifier "let")
890+
modifier: (modifier #{spec})
877891
expr: (identifier #{name})))))
878892
),
879893
rule!(
880-
(optionalBindingCondition pattern: (identifierPattern identifier: @name))
894+
(optionalBindingCondition
895+
bindingSpecifier: @@spec
896+
pattern: (identifierPattern identifier: @name))
881897
=>
882898
(pattern_guard_expr
883899
value: (identifier #{name})
884900
pattern: (call_expr
885901
callee: (member_access_expr base: (identifier "Optional") member_name_node: (identifier "some"))
886902
argument: (argument value: (expr_pattern
887-
modifier: (modifier "let")
903+
modifier: (modifier #{spec})
888904
expr: (identifier #{name})))))
889905
),
890906
// A single condition in an `if`/`while`/`guard` condition list unwraps to
@@ -968,11 +984,19 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
968984
}),
969985
// try/try?/try! expr → unary_expr with operator "try", "try?" or "try!"
970986
rule!(
971-
(tryExpr questionOrExclamationMark: _? @@m expression: @e)
987+
(tryExpr
988+
tryKeyword: @@keyword
989+
questionOrExclamationMark: _? @@m
990+
expression: @e)
972991
=>
973992
expr {
974993
let op = format!("try{}", m.map(|m| ctx.source_text(m)).unwrap_or_default());
975-
tree!((unary_expr operator: (prefix_operator #{op}) operand: {e}))
994+
let operator = tree_spanning!(
995+
ctx,
996+
std::iter::once(keyword).chain(m),
997+
(prefix_operator #{op})
998+
);
999+
tree!((unary_expr operator: {operator} operand: {e}))
9761000
}
9771001
),
9781002
// Do-catch → try_expr
@@ -1006,17 +1030,29 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
10061030
// Catch block without error binding
10071031
rule!((catchClause body: @body) => (catch_clause body: {body})),
10081032
// As expression (type cast) — as?, as!
1009-
rule!((asExpr expression: @val questionOrExclamationMark: _? @@mark type: @ty) => type_cast_expr {
1033+
rule!((asExpr expression: @val asKeyword: @@keyword questionOrExclamationMark: _? @@mark type: @ty) => type_cast_expr {
10101034
let op = format!("as{}", mark.map(|m| ctx.source_text(m)).unwrap_or_default());
1011-
tree!((type_cast_expr expr: {val} operator: (infix_operator #{op}) type: {ty}))
1035+
let operator = tree_spanning!(
1036+
ctx,
1037+
std::iter::once(keyword).chain(mark),
1038+
(infix_operator #{op})
1039+
);
1040+
tree!((type_cast_expr expr: {val} operator: {operator} type: {ty}))
10121041
}),
10131042
// Check expression (`x is T`) → type_test_expr
1014-
rule!((isExpr expression: @val type: @ty) => (type_test_expr expr: {val} operator: (infix_operator "is") type: {ty})),
1043+
rule!((isExpr expression: @val isKeyword: @@keyword type: @ty) => (type_test_expr
1044+
expr: {val}
1045+
operator: {tree_at!(ctx, keyword, (infix_operator "is"))}
1046+
type: {ty})),
10151047
// Await expression → unary_expr with operator "await"
1016-
rule!((awaitExpr expression: @val) => (unary_expr operator: (prefix_operator "await") operand: {val})),
1048+
rule!((awaitExpr awaitKeyword: @@keyword expression: @val) => (unary_expr
1049+
operator: {tree_at!(ctx, keyword, (prefix_operator "await"))}
1050+
operand: {val})),
10171051
// Force-unwrap (`x!`) → postfix unary_expr, via swift-syntax's dedicated
10181052
// `forceUnwrapExpr` node.
1019-
rule!((forceUnwrapExpr expression: @e) => (unary_expr operator: (postfix_operator "!") operand: {e})),
1053+
rule!((forceUnwrapExpr expression: @e exclamationMark: @@mark) => (unary_expr
1054+
operator: {tree_at!(ctx, mark, (postfix_operator "!"))}
1055+
operand: {e})),
10201056
// ---- Imports ----
10211057
// An import declaration. The dotted path (a list of
10221058
// `importPathComponent`s) becomes a `name_node`/`member_access_expr`
@@ -1031,14 +1067,17 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
10311067
attributes: _* @attrs
10321068
modifiers: _* @mods
10331069
importKindSpecifier: _? @@kind
1034-
path: (importPathComponent name: @@parts)*)
1070+
path: (importPathComponent name: @@parts)*) @@decl
10351071
=>
10361072
import_declaration {
10371073
let last = *parts.last().ok_or("import has no path")?;
10381074
let pattern = match kind {
1039-
None => tree!((named_pattern
1040-
name_node: (identifier #{last})
1041-
sub_pattern: (bulk_importing_pattern))),
1075+
None => {
1076+
let bulk = tree_at!(ctx, decl, (bulk_importing_pattern));
1077+
tree!((named_pattern
1078+
name_node: (identifier #{last})
1079+
sub_pattern: {bulk}))
1080+
}
10421081
Some(_) => tree!((identifier #{last})),
10431082
};
10441083
tree!((import_declaration

‎unified/extractor/tests/corpus/swift/control-flow/guard-let.output‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ top_level source="⟨body⟩"
5252
argument:
5353
argument source="⟨value⟩"
5454
value:
55-
expr_pattern source="let ⟨expr⟩"
56-
modifier: modifier "let" source=""
55+
expr_pattern source="⟨modifier⟩ ⟨expr⟩"
56+
modifier: modifier "let" source="let"
5757
expr: identifier "value" source="value"
5858
value: identifier "optional" source="optional"
5959
else:

‎unified/extractor/tests/corpus/swift/control-flow/if-let-optional-binding.output‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ top_level source="⟨body⟩"
6565
argument:
6666
argument source="⟨value⟩"
6767
value:
68-
expr_pattern source="let ⟨expr⟩"
69-
modifier: modifier "let" source=""
68+
expr_pattern source="⟨modifier⟩ ⟨expr⟩"
69+
modifier: modifier "let" source="let"
7070
expr: identifier "value" source="value"
7171
value: identifier "optional" source="optional"
7272
then:

‎unified/extractor/tests/corpus/swift/control-flow/nested-enum-case-pattern.output‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,9 @@ top_level source="⟨body⟩"
173173
base: inferred_type_expr "." source="."
174174
member_name_node: identifier "received" source="received"
175175
argument:
176-
argument source="⟨value⟩),"
176+
argument source="⟨value⟩,"
177177
value:
178-
call_expr source="⟨callee⟩(⟨argument⟩"
178+
call_expr source="⟨callee⟩(⟨argument⟩)"
179179
callee:
180180
member_access_expr source="⟨base⟩⟨member_name_node⟩"
181181
base: inferred_type_expr "." source="."

‎unified/extractor/tests/corpus/swift/control-flow/switch-expression-pattern.output‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -350,23 +350,23 @@ top_level source="⟨body⟩"
350350
argument:
351351
argument source="⟨value⟩"
352352
value: identifier "optional" source="optional"
353-
unary_expr source="try ⟨operand⟩"
353+
unary_expr source="⟨operator⟩ ⟨operand⟩"
354354
operand: identifier "value" source="value"
355-
operator: prefix_operator "try" source=""
356-
unary_expr source="⟨operand⟩!"
355+
operator: prefix_operator "try" source="try"
356+
unary_expr source="⟨operand⟩⟨operator⟩"
357357
operand: identifier "value" source="value"
358-
operator: postfix_operator "!" source=""
359-
type_cast_expr source="⟨expr⟩ as ⟨type⟩"
358+
operator: postfix_operator "!" source="!"
359+
type_cast_expr source="⟨expr⟩ ⟨operator⟩ ⟨type⟩"
360360
expr: identifier "value" source="value"
361-
operator: infix_operator "as" source=""
361+
operator: infix_operator "as" source="as"
362362
type: identifier "Target" source="Target"
363-
type_test_expr source="⟨expr⟩ is ⟨type⟩"
363+
type_test_expr source="⟨expr⟩ ⟨operator⟩ ⟨type⟩"
364364
expr: identifier "value" source="value"
365-
operator: infix_operator "is" source=""
365+
operator: infix_operator "is" source="is"
366366
type: identifier "Target" source="Target"
367-
unary_expr source="await ⟨operand⟩"
367+
unary_expr source="⟨operator⟩ ⟨operand⟩"
368368
operand: identifier "value" source="value"
369-
operator: prefix_operator "await" source=""
369+
operator: prefix_operator "await" source="await"
370370
body:
371371
block source="⟨stmt⟩"
372372
stmt:

‎unified/extractor/tests/corpus/swift/desugar/import-with-deeply-nested-path-three-parts.output‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ top_level source="⟨body⟩"
3636
member_name_node: identifier "Networking" source="Networking"
3737
member_name_node: identifier "URLSession" source="URLSession"
3838
pattern:
39-
named_pattern source="import Foundation.Networking.⟨name_node⟩"
39+
named_pattern source="⟨sub_pattern⟩⟨name_node⟩"
4040
name_node: identifier "URLSession" source="URLSession"
41-
sub_pattern: bulk_importing_pattern source=""
41+
sub_pattern: bulk_importing_pattern "import Foundation.Networking.URLSession" source="import Foundation.Networking.URLSession"

‎unified/extractor/tests/corpus/swift/desugar/import-with-dotted-path-two-parts.output‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,6 @@ top_level source="⟨body⟩"
3030
base: identifier "Foundation" source="Foundation"
3131
member_name_node: identifier "Networking" source="Networking"
3232
pattern:
33-
named_pattern source="import Foundation.⟨name_node⟩"
33+
named_pattern source="⟨sub_pattern⟩⟨name_node⟩"
3434
name_node: identifier "Networking" source="Networking"
35-
sub_pattern: bulk_importing_pattern source=""
35+
sub_pattern: bulk_importing_pattern "import Foundation.Networking" source="import Foundation.Networking"

‎unified/extractor/tests/corpus/swift/desugar/simple-import-with-single-name.output‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ top_level source="⟨body⟩"
2424
import_declaration source="⟨pattern⟩⟨imported_expr⟩"
2525
imported_expr: identifier "Foundation" source="Foundation"
2626
pattern:
27-
named_pattern source="import ⟨name_node⟩"
27+
named_pattern source="⟨sub_pattern⟩⟨name_node⟩"
2828
name_node: identifier "Foundation" source="Foundation"
29-
sub_pattern: bulk_importing_pattern source=""
29+
sub_pattern: bulk_importing_pattern "import Foundation" source="import Foundation"

0 commit comments

Comments
 (0)