Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 49 additions & 20 deletions unified/extractor/src/languages/swift/swift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,23 @@ fn chained_modifier(ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>) -> Optio
}
}

fn implicit_parameterless_initializer(
ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>,
members: &[yeast::Id],
) -> Option<yeast::Id> {
let has_explicit_initializer = members.iter().any(|member| {
ctx.ast
.get_node(*member)
.is_some_and(|node| node.kind_name() == "constructor_declaration")
});
(!has_explicit_initializer).then(|| {
tree!((constructor_declaration
modifier: (modifier "generated")
name_node: (identifier "init")
body: (block)))
})
}

/// Combine a list of boolean sub-conditions into a single expression by
/// left-folding with the infix `&&` operator. Used by control-flow
/// rules (`if`, `guard`, `while`, `repeat-while`), which carry one or
Expand Down Expand Up @@ -1228,7 +1245,10 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
=>
(equality_type_constraint left: {left} right: {right})
),
// Class declaration with body containing members
// Class declaration with body containing members. If the class has no
// explicit initializer, add a (possibly dead) implicit `init`. The implicit
// `init` is dead when the class inherits a constructor from its base class,
// which we cannot check in the extractor.
rule!(
(classDecl
classKeyword: @@kind
Expand All @@ -1241,15 +1261,19 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
genericWhereClause: (genericWhereClause requirements: _* @declaration_constraints)?
memberBlock: (memberBlock members: _* @members))
=>
(class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name_node: (identifier #{name})
type_parameter: {params}
type_constraint: {parameter_constraints}
type_constraint: {declaration_constraints}
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members})
class_like_declaration {
let implicit_initializer = implicit_parameterless_initializer(&mut ctx, &members);
tree!((class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name_node: (identifier #{name})
type_parameter: {params}
type_constraint: {parameter_constraints}
type_constraint: {declaration_constraints}
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members}
member: {implicit_initializer}))
}
),
// Enum class declaration: same as a regular class but with an enum body.
rule!(
Expand All @@ -1274,7 +1298,8 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members})
),
// A `struct` declaration.
// A `struct` declaration, including an implicit parameter-less `init`
// when no explicit initializer is present.
rule!(
(structDecl
structKeyword: @@kind
Expand All @@ -1287,15 +1312,19 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
genericWhereClause: (genericWhereClause requirements: _* @declaration_constraints)?
memberBlock: (memberBlock members: _* @members))
=>
(class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name_node: (identifier #{name})
type_parameter: {params}
type_constraint: {parameter_constraints}
type_constraint: {declaration_constraints}
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members})
class_like_declaration {
let implicit_initializer = implicit_parameterless_initializer(&mut ctx, &members);
tree!((class_like_declaration
modifier: (modifier #{kind})
modifier: {mods}
name_node: (identifier #{name})
type_parameter: {params}
type_constraint: {parameter_constraints}
type_constraint: {declaration_constraints}
base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))}
member: {members}
member: {implicit_initializer}))
}
),
// Protocol declaration
rule!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,7 @@ top_level source="⟨body⟩"
body:
block source="{\n ⟨stmt⟩\n }"
stmt: unsupported_node "discard self" source="discard self"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ top_level source="⟨body⟩"
function_declaration source="func ⟨name_node⟩() ⟨body⟩"
name_node: identifier "update" source="update"
body: block "{}" source="{}"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
class_like_declaration source="⟨modifier⟩ ⟨name_node⟩: ⟨base_type⟩ {\n ⟨member⟩\n}"
modifier: modifier "class" source="class"
name_node: identifier "Derived" source="Derived"
Expand All @@ -182,3 +186,7 @@ top_level source="⟨body⟩"
member_access_expr source="⟨base⟩.⟨member_name_node⟩"
base: super_expr "super" source="super"
member_name_node: identifier "update" source="update"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,22 @@ top_level source="⟨body⟩"
class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {}"
modifier: modifier "class" source="class"
name_node: identifier "Foo" source="Foo"
member:
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
class_like_declaration source="⟨modifier⟩ ⟨name_node⟩<⟨type_parameter⟩> {}"
modifier: modifier "class" source="class"
name_node: identifier "C" source="C"
type_parameter:
type_parameter source="⟨name_node⟩"
name_node: identifier "T" source="T"
member:
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩"
modifier: modifier "let" source="let"
pattern: identifier "x" source="x"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,7 @@ top_level source="⟨body⟩"
modifier: modifier "class" source="class"
name_node: identifier "make" source="make"
body: block "{}" source="{}"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ top_level source="⟨body⟩"
base_type:
base_type source="⟨type⟩"
type: identifier "Animal" source="Animal"
member:
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,7 @@ top_level source="⟨body⟩"
left: identifier "n" source="n"
operator: infix_operator "+=" source="+="
right: int_literal "1" source="1"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,8 @@ top_level source="⟨body⟩"
type: identifier "Control" source="Control"
base_type source="⟨type⟩"
type: identifier "Drawable" source="Drawable"
member:
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,7 @@ top_level source="⟨body⟩"
modifier: modifier "var" source="var"
pattern: identifier "y" source="y"
type: identifier "Int" source="Int"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,7 @@ top_level source="⟨body⟩"
left: identifier "w" source="w"
operator: infix_operator "*" source="*"
right: identifier "h" source="h"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,9 @@ top_level source="⟨body⟩"
class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {\n⟨member⟩\n}"
modifier: modifier "class" source="class"
name_node: identifier "C" source="C"
member: unsupported_node "#if DEBUG\n init(x: Int) {}\n deinit {}\n#endif" source="#if DEBUG\n init(x: Int) {}\n deinit {}\n#endif"
member:
unsupported_node "#if DEBUG\n init(x: Int) {}\n deinit {}\n#endif" source="#if DEBUG\n init(x: Int) {}\n deinit {}\n#endif"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,8 @@ top_level source="⟨body⟩"
class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {}"
modifier: modifier "class" source="class"
name_node: identifier "Foo" source="Foo"
member:
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,8 @@ top_level source="⟨body⟩"
equality_type_constraint source="⟨left⟩ == ⟨right⟩"
left: identifier "U" source="U"
right: identifier "T" source="T"
member:
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,7 @@ top_level source="⟨body⟩"
modifier: modifier "let" source="let"
pattern: identifier "descriptor" source="descriptor"
type: identifier "Int" source="Int"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,7 @@ top_level source="⟨body⟩"
left: identifier "_v" source="_v"
operator: infix_operator "=" source="="
right: identifier "newValue" source="newValue"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ top_level source="⟨body⟩"
class_like_declaration source="⟨modifier⟩ ⟨name_node⟩ {}"
modifier: modifier "struct" source="struct"
name_node: identifier "Deep" source="Deep"
member:
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
variable_declaration source="⟨modifier⟩ ⟨pattern⟩: ⟨type⟩"
modifier: modifier "let" source="let"
pattern: identifier "value" source="value"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,7 @@ top_level source="⟨body⟩"
modifier: modifier "static" source="static"
name_node: identifier "make" source="make"
body: block "{}" source="{}"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
4 changes: 4 additions & 0 deletions unified/extractor/tests/corpus/swift/types/struct.output
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,7 @@ top_level source="⟨body⟩"
modifier: modifier "let" source="let"
pattern: identifier "y" source="y"
type: identifier "Int" source="Int"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,7 @@ top_level source="⟨body⟩"
argument:
argument source="⟨value⟩"
value: identifier "oldValue" source="oldValue"
constructor_declaration source=""
modifier: modifier "generated" source=""
name_node: identifier "init" source=""
body: block source=""
2 changes: 2 additions & 0 deletions unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ module Ast implements AstSig<Location> {
e instanceof Identifier and not e instanceof IdentifierExpr
or
e instanceof Operator
or
not e.fromSource()
}

AstNode getChild(AstNode n, int index) {
Expand Down
11 changes: 10 additions & 1 deletion unified/ql/lib/codeql/unified/internal/FacadeAst.qll
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@ module Unified {
/** Gets the file containing this AST node. */
File getFile() { result = this.getLocation().getFile() }

private predicate isGenerated() {
this.hasModifier("generated")
or
this.getParent().isGenerated()
}

/** Holds if this AST node comes from ordinary source code. */
predicate fromSource() { this.getFile().fromSource() }
predicate fromSource() {
this.getFile().fromSource() and
not this.isGenerated()
}

/** Holds if this AST node has a modifier with the given text. */
predicate hasModifier(string text) {
Expand Down
45 changes: 45 additions & 0 deletions unified/ql/test/library-tests/constructors/constructors.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
class C1 {} // implicit parameterless `init`

class C2: C1 {} // inherits `init`

class C3 {
init() {}
}

class C4: C3 {
init(_ x: Int) {
super.init()
}

override convenience init() {
self.init(0)
}
}

class C5: C4 {} // inherits `init` and `convenience init`

class C6<T1, T2> {
init(x: T1, y: T2) {}

private init(s: String) {}

convenience init(x: T1) {
fatalError("Convenience initializer not implemented")
}
}

class C7<T3, T4>: C6<T4, T3> {} // inherits `init(x: T4, y: T3)` and `convenience init(x: T4)`

class C8: C7<Int, String> { // inherits `init(x: String, y: Int)`
convenience init(x: String) {
self.init(x: x, y: 0)
}
}

struct S1 { // implicit `init(f1: Int = 0, f2: String, f4: Double)`
var f1: Int = 0
var f2: String
let f3: Bool = false
let f4: Double
static var f5: Float = 0.0
}
5 changes: 5 additions & 0 deletions unified/ql/test/library-tests/constructors/test.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
| constructors.swift:1:1:1:0 | init |
| constructors.swift:3:1:3:0 | init |
| constructors.swift:19:1:19:0 | init |
| constructors.swift:31:1:31:0 | init |
| constructors.swift:39:1:39:0 | init |
6 changes: 6 additions & 0 deletions unified/ql/test/library-tests/constructors/test.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import unified

query predicate generatedConstructor(ConstructorDeclaration c) {
c = any(ClassLikeDeclaration cls | cls.fromSource()).getAMember() and
c.hasModifier("generated")
}
Loading
Loading