Skip to content

Commit 5f5db99

Browse files
authored
Merge pull request #22595 from asgerf/unified/binary-expr-assignment
Unified: Fix AST, CFG, and data flow rules for assignments
2 parents 2304a1a + e167281 commit 5f5db99

27 files changed

Lines changed: 372 additions & 220 deletions

shared/ssa/codeql/ssa/Ssa.qll

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,22 @@ module Make<
14191419
*/
14201420
default predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) { none() }
14211421

1422+
/**
1423+
* Holds if the post-update node corresponding to the given `read` occurs at `bb,i`, meaning
1424+
* it will propagate to the next use (strictly) after that point in the CFG.
1425+
*
1426+
* The default is to use the CFG node associated with the read itself, meaning the post-update
1427+
* always flows to the next use. The default can however lead to spurious flow in cases like:
1428+
* ```
1429+
* x.f = foo(x)
1430+
* ```
1431+
* where the post-update node for `x` on the left-hand side flows into the `x` on the right-hand side.
1432+
*
1433+
* NOTE: When implementing this predicate, you must ensure that `variableRead` is defined to contain
1434+
* a synthetic read of this variable at `bb,i`.
1435+
*/
1436+
default predicate postUpdateCfgNode(Expr read, BasicBlock bb, int i) { read.hasCfgNode(bb, i) }
1437+
14221438
/** An abstract value that a `Guard` may evaluate to. */
14231439
class GuardValue {
14241440
/** Gets a textual representation of this value. */
@@ -1892,6 +1908,13 @@ module Make<
18921908
override string toString() { result = "[input] " + def_.toString() }
18931909
}
18941910

1911+
private predicate postUpdateNodeAt(
1912+
ExprPostUpdateNode node, BasicBlock bb, int i, SourceVariable v
1913+
) {
1914+
node.getPreUpdateNode().(ReadNode).readsAt(_, _, v) and
1915+
DfInput::postUpdateCfgNode(node.getExpr(), bb, i)
1916+
}
1917+
18951918
/**
18961919
* Holds if `nodeFrom` corresponds to the reference to `v` at index `i` in
18971920
* `bb`. The boolean `isUseStep` indicates whether `nodeFrom` is an actual
@@ -1907,7 +1930,10 @@ module Make<
19071930
isUseStep = false
19081931
)
19091932
or
1910-
[nodeFrom, nodeFrom.(ExprPostUpdateNode).getPreUpdateNode()].(ReadNode).readsAt(bb, i, v) and
1933+
nodeFrom.(ReadNode).readsAt(bb, i, v) and
1934+
isUseStep = true
1935+
or
1936+
postUpdateNodeAt(nodeFrom, bb, i, v) and
19111937
isUseStep = true
19121938
}
19131939

unified/extractor/ast_types.yml

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ supertypes:
2323
- type_cast_expr
2424
- type_test_expr
2525
- if_expr
26-
- assign_expr
27-
- compound_assign_expr
2826
- pattern_guard_expr
2927
- empty_expr
3028
- block
@@ -159,17 +157,6 @@ named:
159157
unresolved_operator_sequence:
160158
element*: expr_or_operator
161159

162-
# Plain assignment
163-
assign_expr:
164-
target: expr
165-
value: expr
166-
167-
# Compound assignment
168-
compound_assign_expr:
169-
target: expr
170-
operator: infix_operator
171-
value: expr
172-
173160
# A function or method call, such as `f(x)` or `obj.m(x)`.
174161
#
175162
# Method calls are represented as a call whose `function` is a `member_access_expr`.

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

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,6 @@ fn member_chain(
129129
result
130130
}
131131

132-
/// Compound-assignment operator spellings (`+=`, `<<=`, ...). Used to tell a
133-
/// compound assignment from an ordinary binary application, both of which
134-
/// arrive as a `binaryOperator`-based `infixOperatorExpr`.
135-
const COMPOUND_ASSIGN_OPS: &[&str] = &[
136-
"+=", "-=", "*=", "/=", "%=", "<<=", ">>=", "&=", "|=", "^=", "&+=", "&-=", "&*=",
137-
];
138-
139132
fn translation_rules() -> Vec<Rule<SwiftContext>> {
140133
vec![
141134
// ---- Top-level ----
@@ -243,29 +236,21 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
243236
// operator leaf. Used by `infixOperatorExpr` (folded) and `sequenceExpr`
244237
// (unresolved).
245238
rule!((binaryOperatorExpr operator: @op) => (infix_operator #{op})),
246-
// Compound assignment (`x += y`) vs. an ordinary binary application
247-
// (`a + b`): both are `binaryOperator`-based `infixOperatorExpr`s,
248-
// distinguishable only by the operator's spelling. The query engine
249-
// can't match on token text, so a small Rust block reads the spelling
250-
// and routes to `compound_assign_expr` or `binary_expr`. The operator
251-
// is captured raw (`@@op`) to read its spelling.
239+
// A `binaryOperator`-based `infixOperatorExpr` represents both ordinary
240+
// binary applications (`a + b`) and compound assignments (`x += y`).
241+
// Both have the same target AST shape; the QL library distinguishes
242+
// assignments by the operator spelling.
252243
rule!(
253244
(infixOperatorExpr leftOperand: @l operator: (binaryOperatorExpr) @@op rightOperand: @r)
254245
=>
255-
expr {
256-
if COMPOUND_ASSIGN_OPS.contains(&ctx.source_text(op).as_str()) {
257-
tree!((compound_assign_expr target: {l} operator: (infix_operator #{op}) value: {r}))
258-
} else {
259-
tree!((binary_expr left: {l} operator: (infix_operator #{op}) right: {r}))
260-
}
261-
}
246+
(binary_expr left: {l} operator: (infix_operator #{op}) right: {r})
262247
),
263-
// Plain assignment (`x = y`). In a folded chain the `=` is an
264-
// `assignmentExpr` node (distinct from other operators), matched by kind.
248+
// Plain assignment (`x = y`). In a folded chain the `=` is represented
249+
// by an `assignmentExpr` node rather than a `binaryOperatorExpr`.
265250
rule!(
266-
(infixOperatorExpr leftOperand: @l operator: (assignmentExpr) rightOperand: @r)
251+
(infixOperatorExpr leftOperand: @l operator: (assignmentExpr) @op rightOperand: @r)
267252
=>
268-
(assign_expr target: {l} value: {r})
253+
(binary_expr left: {l} operator: (infix_operator #{op}) right: {r})
269254
),
270255
// In an unresolved `sequenceExpr` (below) the operator positions are not
271256
// only `binaryOperatorExpr`s: a plain assignment (`=`), an `as`/`is` cast

unified/extractor/tests/corpus/swift/control-flow/if-case-let-with-shadowing-in-condition-value.output

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ top_level
151151
then:
152152
block
153153
stmt:
154-
compound_assign_expr
155-
target: identifier "y"
154+
binary_expr
155+
left: identifier "y"
156156
operator: infix_operator "+="
157-
value: int_literal "1"
157+
right: int_literal "1"

unified/extractor/tests/corpus/swift/expressions/array-type-constructor.output

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ top_level
167167
body:
168168
block
169169
stmt:
170-
assign_expr
171-
target: identifier "count"
172-
value: int_literal "0"
170+
binary_expr
171+
left: identifier "count"
172+
operator: infix_operator "="
173+
right: int_literal "0"

unified/extractor/tests/corpus/swift/functions/function-with-inout-parameter.output

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ top_level
7171
body:
7272
block
7373
stmt:
74-
compound_assign_expr
75-
target: identifier "x"
74+
binary_expr
75+
left: identifier "x"
7676
operator: infix_operator "+="
77-
value: int_literal "1"
77+
right: int_literal "1"

unified/extractor/tests/corpus/swift/loops/repeat-while-loop.output

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ top_level
5151
body:
5252
block
5353
stmt:
54-
compound_assign_expr
55-
target: identifier "x"
54+
binary_expr
55+
left: identifier "x"
5656
operator: infix_operator "-="
57-
value: int_literal "1"
57+
right: int_literal "1"
5858
condition:
5959
binary_expr
6060
left: identifier "x"

unified/extractor/tests/corpus/swift/loops/while-loop.output

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ top_level
5757
body:
5858
block
5959
stmt:
60-
compound_assign_expr
61-
target: identifier "x"
60+
binary_expr
61+
left: identifier "x"
6262
operator: infix_operator "-="
63-
value: int_literal "1"
63+
right: int_literal "1"

unified/extractor/tests/corpus/swift/types/class-with-initializer.output

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,10 @@ top_level
106106
body:
107107
block
108108
stmt:
109-
assign_expr
110-
target:
109+
binary_expr
110+
left:
111111
member_access_expr
112112
base: identifier "self"
113113
member_name_node: identifier "x"
114-
value: identifier "x"
114+
operator: infix_operator "="
115+
right: identifier "x"

unified/extractor/tests/corpus/swift/types/class-with-method.output

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ top_level
9090
body:
9191
block
9292
stmt:
93-
compound_assign_expr
94-
target: identifier "n"
93+
binary_expr
94+
left: identifier "n"
9595
operator: infix_operator "+="
96-
value: int_literal "1"
96+
right: int_literal "1"

0 commit comments

Comments
 (0)