Skip to content

Commit 82487f8

Browse files
committed
unified: Synthesize setter parameters and return type in accessors
Accessors previously had a 'type' field. Now, it has a return_type instead, which is only set for getters. For set, didSet, and willSet, we insert a synthetic newValue/oldValue parameter, which also carries the type previously stored in accessor_declaration.type.
1 parent f1f4d84 commit 82487f8

10 files changed

Lines changed: 114 additions & 53 deletions

‎unified/extractor/ast_types.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ named:
534534
name_node: identifier
535535
accessor_kind: accessor_kind
536536
parameter*: parameter
537-
type?: expr
537+
return_type?: expr
538538
body?: block
539539

540540
# "get", "set", or a language-specific kind like "didSet"

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

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ struct SwiftContext {
1212
/// `variableDecl` rule before translating the accessor block; read by the
1313
/// inner `accessorDecl` rules to name each `accessor_declaration`.
1414
property_name_node: Option<yeast::OutputId>,
15-
/// Translated type node for the property type. Set (for computed
16-
/// properties) by the accessor-bearing `variableDecl` rule; read by the
17-
/// inner `accessorDecl` rules. Left `None` for stored properties with
18-
/// observers, so their `willSet`/`didSet` accessors carry no type.
15+
/// Translated type node for the property type. Set by the
16+
/// accessor-bearing `variableDecl` rule; read by the inner `accessorDecl`
17+
/// rules for both the getter return type and its `newValue`/`oldValue`
18+
/// parameter type, when available.
1919
property_type: Option<yeast::OutputId>,
2020
/// Translated outer modifiers to attach to each child of a flattening
2121
/// outer rule — e.g. the `let`/`var` binding modifier on each
@@ -334,7 +334,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
334334
(accessor_declaration
335335
modifier: (modifier #{spec})
336336
name_node: (identifier #{name})
337-
type: {ty}
337+
return_type: {ty}
338338
accessor_kind: {ctx.literal_at_start_of("accessor_kind", "get", body)}
339339
body: {body})
340340
),
@@ -345,12 +345,12 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
345345
// * With an initializer (`var x: T = e { willSet {…} didSet {…} }`) it is
346346
// a *stored* property with observers: emit the backing
347347
// `variable_declaration` first, then one `accessor_declaration` per
348-
// observer (observers carry no type).
348+
// observer.
349349
// * Without an initializer (`var v: T { get set }`, incl. protocol
350-
// requirements) it is a *computed* property: no backing variable; the
351-
// type is published so the get/set accessors carry it.
350+
// requirements) it is a *computed* property: no backing variable.
352351
//
353-
// In both cases the first emitted declaration is unchained and every
352+
// In both cases the type is published so every accessor carries it, and
353+
// the first emitted declaration is unchained and every
354354
// subsequent one is tagged `chained_declaration` (the `!result.is_empty()`
355355
// test). Must precede the plain `variableDecl` rules.
356356
rule!(
@@ -365,6 +365,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
365365
member* {
366366
ctx.outer_modifiers = vec![tree!((modifier #{spec}))];
367367
ctx.property_name_node = Some(tree!((identifier #{name})));
368+
ctx.property_type = Some(ty);
368369
let mut result = Vec::new();
369370
if let Some(val) = val {
370371
// Stored property with observers: the initializer is not part
@@ -380,9 +381,6 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
380381
type: {ty}
381382
value: {val})
382383
));
383-
} else {
384-
// Computed property: the accessors carry the type.
385-
ctx.property_type = Some(ty);
386384
}
387385
for acc in accessors.into_iter() {
388386
ctx.is_chained = !result.is_empty();
@@ -400,27 +398,70 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
400398
// one (a protocol requirement) carries neither. The property context is
401399
// read out *before* translating the body, which resets `ctx` so the
402400
// accessor's context does not leak into the body subtree.
403-
rule!(
404-
(accessorDecl accessorSpecifier: @@spec body: _? @@body)
405-
=>
406-
(accessor_declaration
407-
modifier: {
408-
if body.is_some() {
409-
ctx.outer_modifiers.clone()
410-
} else {
411-
Vec::new()
401+
//
402+
// `set` and `willSet` accessors get a parameter holding the incoming
403+
// value, and `didSet` gets one holding the previous value. It is named
404+
// explicitly (`set(v)`) or else implicitly `newValue`/`oldValue`.
405+
rule!(
406+
(accessorDecl
407+
accessorSpecifier: @@spec
408+
parameters: (accessorParameters name: @@param_name)?
409+
body: _? @@body)
410+
=>
411+
accessor_declaration {
412+
let spec_text = ctx.source_text(spec);
413+
let implicit_param_name = match spec_text.as_str() {
414+
"set" | "willSet" => Some("newValue"),
415+
"didSet" => Some("oldValue"),
416+
_ => None,
417+
};
418+
let param_ty = ctx.property_type;
419+
let param = match (param_name, implicit_param_name) {
420+
(Some(n), _) => {
421+
Some(tree!((parameter pattern: (identifier #{n}) type: {param_ty})))
412422
}
413-
}
414-
modifier: {chained_modifier(&mut ctx)}
415-
name_node: {
416-
ctx.property_name_node
417-
.ok_or("accessor outside property context")?
418-
}
419-
type: {ctx.property_type}
420-
accessor_kind: (accessor_kind #{spec})
421-
body: {ctx.reset(); body}
422-
location: {spec}
423-
location: {body})
423+
(None, Some(n)) => {
424+
let n = n.to_string();
425+
Some(tree!((parameter
426+
modifier: (modifier "generated")
427+
pattern: (identifier #{n} location: {spec})
428+
type: {param_ty}
429+
location: {spec})))
430+
}
431+
(None, None) => None,
432+
};
433+
let binding = if body.is_some() {
434+
ctx.outer_modifiers.clone()
435+
} else {
436+
Vec::new()
437+
};
438+
let chained = chained_modifier(&mut ctx);
439+
let name = ctx
440+
.property_name_node
441+
.ok_or("accessor outside property context")?;
442+
let return_ty = if spec_text == "get" {
443+
ctx.property_type
444+
} else {
445+
None
446+
};
447+
let body = match body {
448+
Some(block) => {
449+
ctx.reset();
450+
ctx.translate(block)?.into_iter().next()
451+
}
452+
None => None,
453+
};
454+
tree!((accessor_declaration
455+
modifier: {binding}
456+
modifier: {chained}
457+
name_node: {name}
458+
return_type: {return_ty}
459+
accessor_kind: (accessor_kind #{spec})
460+
parameter: {param}
461+
body: {body}
462+
location: {spec}
463+
location: {body}))
464+
}
424465
),
425466
// ---- Variables ----
426467
// The individual bindings of a `variableDecl`. The binding modifier and

‎unified/extractor/tests/corpus/swift/types/binding-modifier-does-not-leak-into-accessor-body.output‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ top_level source="var p: Int {\n ⟨body⟩\n}"
100100
modifier: modifier "var" source="var" (external)
101101
name_node: identifier "p" source="p" (external)
102102
accessor_kind: accessor_kind "get" source="get"
103-
type: identifier "Int" source="Int" (external)
103+
return_type: identifier "Int" source="Int" (external)
104104
body:
105105
block source="{\n ⟨stmt⟩\n }"
106106
stmt:

‎unified/extractor/tests/corpus/swift/types/computed-property.output‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,11 @@ top_level source="⟨body⟩"
113113
modifier: modifier "var" source="var"
114114
pattern: identifier "h" source="h"
115115
type: identifier "Double" source="Double"
116-
accessor_declaration source="⟨modifier⟩ ⟨name_node⟩: ⟨type⟩ ⟨body⟩"
116+
accessor_declaration source="⟨modifier⟩ ⟨name_node⟩: ⟨return_type⟩ ⟨body⟩"
117117
modifier: modifier "var" source="var"
118118
name_node: identifier "area" source="area"
119119
accessor_kind: accessor_kind "get" source=""
120-
type: identifier "Double" source="Double"
120+
return_type: identifier "Double" source="Double"
121121
body:
122122
block source="{\n ⟨stmt⟩\n }"
123123
stmt:

‎unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,19 +120,23 @@ top_level source="⟨body⟩"
120120
modifier: modifier "var" source="var" (external)
121121
name_node: identifier "v" source="v" (external)
122122
accessor_kind: accessor_kind "get" source="get"
123-
type: identifier "Int" source="Int" (external)
123+
return_type: identifier "Int" source="Int" (external)
124124
body:
125125
block source="{ ⟨stmt⟩ }"
126126
stmt:
127127
return_expr source="return ⟨value⟩"
128128
value: identifier "_v" source="_v"
129-
accessor_declaration source="⟨accessor_kind⟩ ⟨body⟩"
129+
accessor_declaration source="⟨accessor_kind⟩⟨parameter⟩ ⟨body⟩"
130130
modifier:
131131
modifier "var" source="var" (external)
132132
modifier "chained_declaration" source=""
133133
name_node: identifier "v" source="v" (external)
134134
accessor_kind: accessor_kind "set" source="set"
135-
type: identifier "Int" source="Int" (external)
135+
parameter:
136+
parameter source="⟨pattern⟩"
137+
modifier: modifier "generated" source=""
138+
type: identifier "Int" source="Int" (external)
139+
pattern: identifier "newValue" source="set"
136140
body:
137141
block source="{ ⟨stmt⟩ }"
138142
stmt:

‎unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,17 +113,21 @@ top_level source="⟨body⟩"
113113
accessor_declaration source="⟨accessor_kind⟩"
114114
name_node: identifier "foo" source="foo" (external)
115115
accessor_kind: accessor_kind "get" source="get"
116-
type: identifier "Int" source="Int" (external)
116+
return_type: identifier "Int" source="Int" (external)
117117
accessor_declaration source="⟨accessor_kind⟩"
118118
name_node: identifier "bar" source="bar" (external)
119119
accessor_kind: accessor_kind "get" source="get"
120-
type: identifier "String" source="String" (external)
121-
accessor_declaration source="⟨accessor_kind⟩"
120+
return_type: identifier "String" source="String" (external)
121+
accessor_declaration source="⟨accessor_kind⟩⟨parameter⟩"
122122
modifier: modifier "chained_declaration" source=""
123123
name_node: identifier "bar" source="bar" (external)
124124
accessor_kind: accessor_kind "set" source="set"
125-
type: identifier "String" source="String" (external)
125+
parameter:
126+
parameter source="⟨pattern⟩"
127+
modifier: modifier "generated" source=""
128+
type: identifier "String" source="String" (external)
129+
pattern: identifier "newValue" source="set"
126130
accessor_declaration source="⟨accessor_kind⟩"
127131
name_node: identifier "count" source="count" (external)
128132
accessor_kind: accessor_kind "get" source="get"
129-
type: identifier "Int" source="Int" (external)
133+
return_type: identifier "Int" source="Int" (external)

‎unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,17 @@ top_level source="⟨body⟩"
110110
pattern: identifier "x" source="x"
111111
type: identifier "Int" source="Int"
112112
value: int_literal "0" source="0"
113-
accessor_declaration source="⟨accessor_kind⟩ ⟨body⟩"
113+
accessor_declaration source="⟨accessor_kind⟩⟨parameter⟩ ⟨body⟩"
114114
modifier:
115115
modifier "var" source="var" (external)
116116
modifier "chained_declaration" source=""
117117
name_node: identifier "x" source="x" (external)
118118
accessor_kind: accessor_kind "willSet" source="willSet"
119+
parameter:
120+
parameter source="⟨pattern⟩"
121+
modifier: modifier "generated" source=""
122+
type: identifier "Int" source="Int" (external)
123+
pattern: identifier "newValue" source="willSet"
119124
body:
120125
block source="{ ⟨stmt⟩ }"
121126
stmt:
@@ -124,12 +129,17 @@ top_level source="⟨body⟩"
124129
argument:
125130
argument source="⟨value⟩"
126131
value: identifier "newValue" source="newValue"
127-
accessor_declaration source="⟨accessor_kind⟩ ⟨body⟩"
132+
accessor_declaration source="⟨accessor_kind⟩⟨parameter⟩ ⟨body⟩"
128133
modifier:
129134
modifier "var" source="var" (external)
130135
modifier "chained_declaration" source=""
131136
name_node: identifier "x" source="x" (external)
132137
accessor_kind: accessor_kind "didSet" source="didSet"
138+
parameter:
139+
parameter source="⟨pattern⟩"
140+
modifier: modifier "generated" source=""
141+
type: identifier "Int" source="Int" (external)
142+
pattern: identifier "oldValue" source="didSet"
133143
body:
134144
block source="{ ⟨stmt⟩ }"
135145
stmt:

‎unified/ql/lib/codeql/unified/internal/Ast.qll‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,10 @@ module Unified {
123123
/** Gets the node corresponding to the field `parameter`. */
124124
final override F::Parameter getAParameter() { result = this.getParameter(_) }
125125

126-
/** Gets the node corresponding to the field `type`. */
127-
final F::Expr getType() { unified_accessor_declaration_type(this, result) }
126+
/** Gets the node corresponding to the field `return_type`. */
127+
final override F::Expr getReturnType() {
128+
unified_accessor_declaration_return_type(this, result)
129+
}
128130

129131
/** Gets a field or child node of this node. */
130132
final override F::AstNode getAFieldOrChild() {
@@ -133,7 +135,7 @@ module Unified {
133135
unified_accessor_declaration_modifier(this, _, result) or
134136
unified_accessor_declaration_def(this, _, result) or
135137
unified_accessor_declaration_parameter(this, _, result) or
136-
unified_accessor_declaration_type(this, result)
138+
unified_accessor_declaration_return_type(this, result)
137139
}
138140
}
139141

@@ -1624,7 +1626,7 @@ module Unified {
16241626
or
16251627
result = node.(AccessorDeclaration).getParameter(i) and name = "getParameter"
16261628
or
1627-
result = node.(AccessorDeclaration).getType() and i = -1 and name = "getType"
1629+
result = node.(AccessorDeclaration).getReturnType() and i = -1 and name = "getReturnType"
16281630
or
16291631
result = node.(Argument).getModifier(i) and name = "getModifier"
16301632
or

‎unified/ql/lib/codeql/unified/internal/ExprPositions.qll‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ predicate isInTypeContext(Expr expr) {
1616
or
1717
expr = any(FunctionExpr n).getReturnType()
1818
or
19-
expr = any(AccessorDeclaration n).getType()
19+
expr = any(AccessorDeclaration n).getReturnType()
2020
or
2121
expr = any(Parameter n).getType()
2222
or

‎unified/ql/lib/unified.dbscheme‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ unified_accessor_declaration_parameter(
155155
unique int parameter: @unified_parameter ref
156156
);
157157

158-
unified_accessor_declaration_type(
158+
unified_accessor_declaration_return_type(
159159
unique int unified_accessor_declaration: @unified_accessor_declaration ref,
160-
unique int type__: @unified_expr ref
160+
unique int return_type: @unified_expr ref
161161
);
162162

163163
unified_accessor_declaration_def(

0 commit comments

Comments
 (0)