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
12 changes: 12 additions & 0 deletions unified/ql/lib/codeql/unified/internal/AstPlugin.qll
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ class AstPlugin extends Unit {

bindingset[decl]
string getVariableDeclarationKeyword(VariableDeclaration decl) { none() }

/**
* Holds if the default constructor `cd` needs a parameter at index `i` with
* the given name. `i = 0` is reserved for the implicit receiver parameter.
*/
bindingset[cd]
predicate defaultConstructorParameter(ConstructorDeclaration cd, int i, string name) { none() }
}

bindingset[f]
Expand All @@ -41,3 +48,8 @@ bindingset[decl]
string getVariableDeclarationKeyword(VariableDeclaration decl) {
result = any(AstPlugin p).getVariableDeclarationKeyword(decl)
}

bindingset[cd]
predicate defaultConstructorParameter(ConstructorDeclaration cd, int i, string name) {
any(AstPlugin p).defaultConstructorParameter(cd, i, name)
}
29 changes: 29 additions & 0 deletions unified/ql/lib/codeql/unified/internal/AstPluginSwift.qll
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ private class AstPluginSwift extends AstPlugin {
exists(f) and result = "func"
}

bindingset[c]
override string getConstructorDeclarationKeyword(ConstructorDeclaration c) {
c.hasModifier(result) and
result = "convenience"
}

override string getClassLikeDeclarationKeyword(ClassLikeDeclaration cls) {
cls.hasModifier(result) and
result in ["class", "struct", "enum", "actor", "extension", "protocol"]
Expand All @@ -19,4 +25,27 @@ private class AstPluginSwift extends AstPlugin {
decl.hasModifier(result) and
result in ["var", "let"]
}

bindingset[cd]
override predicate defaultConstructorParameter(ConstructorDeclaration cd, int i, string name) {
exists(ClassLikeDeclaration cls |
cd = cls.getAMember() and
cls.hasModifier("struct") and
name =
rank[i](VariableDeclaration decl, int j, string s |
decl = cls.getMember(j) and
not decl.hasModifier("static") and
Comment thread
hvitved marked this conversation as resolved.
(
// if `decl` has an initializer then this parameter has that initializer as a default value
decl.hasModifier("var")
or
decl.hasModifier("let") and
not exists(decl.getValue())
) and
s = decl.getPattern().(Identifier).getValue()
|
s order by j
)
)
}
}
105 changes: 105 additions & 0 deletions unified/ql/lib/codeql/unified/internal/ParameterEx.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/**
* Provides logic for synthesizing parameters.
*/

private import unified
private import AstPlugin
private import NameBinding

private newtype TParameterEx =
TAstParameter(Parameter p) or
TImplicitReceiverParameter(Callable c) {
exists(LocalVariable v | v.isImplicitReceiverParameter(c))
} or
TDefaultConstructorParameter(ConstructorDeclaration cd, int i, string name) {
cd.hasModifier("generated") and
defaultConstructorParameter(cd, i, name)
}

final class ParameterEx = ParameterExImpl;

/**
* A parameter from source code or a synthesized parameter.
*/
abstract private class ParameterExImpl extends TParameterEx {
/**
* Gets the source code parameter that this entity represents, if any.
*/
Parameter asParameter() { this = TAstParameter(result) }

/**
* Holds if this entity represents a regular parameter from source code.
*/
predicate isParameter() { exists(this.asParameter()) }

/**
* Holds if this entity represents an implicit receiver parameter of `c`.
*/
predicate isImplicitReceiverParameter(Callable c) { this = TImplicitReceiverParameter(c) }

/**
* Holds if this entity represents the `i`th parameter of the default
* constructor `cd` with name `name`.
*/
predicate isDefaultConstructorParameter(ConstructorDeclaration cd, int i, string name) {
this = TDefaultConstructorParameter(cd, i, name)
}

/** Gets the callable that this parameter belongs to. */
Callable getCallable() {
this.asParameter() =
[
result.(FunctionDeclaration).getAParameter(),
result.(ConstructorDeclaration).getAParameter(),
result.(AccessorDeclaration).getAParameter(),
result.(FunctionExpr).getAParameter()
]
or
this.isImplicitReceiverParameter(result)
or
this.isDefaultConstructorParameter(result, _, _)
}

/** Gets a textual representation of this parameter. */
abstract string toString();

/** Gets the location of this parameter. */
abstract Location getLocation();
}

private class AstParameterEx extends ParameterExImpl, TAstParameter {
Parameter p;

AstParameterEx() { this = TAstParameter(p) }

override string toString() { result = p.toString() }

override Location getLocation() { result = p.getLocation() }
}

private class ImplicitReceiverParameterEx extends ParameterExImpl, TImplicitReceiverParameter {
Callable c;

ImplicitReceiverParameterEx() { this = TImplicitReceiverParameter(c) }

override string toString() {
exists(LocalVariable v |
v.isImplicitReceiverParameter(c) and
result = v.getName()
)
}

override Location getLocation() { result = c.getLocation() }
}

private class DefaultConstructorParameterEx extends ParameterExImpl, TDefaultConstructorParameter {
ConstructorDeclaration cd;
int i;
string name;

DefaultConstructorParameterEx() { this = TDefaultConstructorParameter(cd, i, name) }

override string toString() { result = name + " [" + cd.getName() + " default constructor]" }

override Location getLocation() { result = cd.getLocation() }
}
18 changes: 17 additions & 1 deletion unified/ql/test/library-tests/constructors/constructors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,26 @@ class C8: C7<Int, String> { // inherits `init(x: String, y: Int)`
}
}

struct S1 { // implicit `init(f1: Int = 0, f2: String, f4: Double)`
struct S1 { // implicit `init(f1: Int = 0, f2: String, f4: Double, f6: Double, f7: Double = 0)`
var f1: Int = 0
var f2: String
let f3: Bool = false
let f4: Double
static var f5: Float = 0.0
var f6: Double { // todo: unlike `f7`, this is not extracted as a `VariableDeclaration`
willSet {
print("Changing from \(f6) to \(newValue)")
}
didSet {
print("Changed from \(oldValue) to \(f6)")
}
}
var f7: Double = 0.0 {
willSet {
print("Changing from \(f7) to \(newValue)")
}
didSet {
print("Changed from \(oldValue) to \(f7)")
}
}
}
23 changes: 23 additions & 0 deletions unified/ql/test/library-tests/constructors/test.expected
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
generatedConstructor
| 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 |
implicitReceiverParameter
| constructors.swift:1:1:1:0 | self | constructors.swift:1:1:1:0 | init |
| constructors.swift:3:1:3:0 | self | constructors.swift:3:1:3:0 | init |
| constructors.swift:6:5:6:13 | self | constructors.swift:6:5:6:13 | init |
| constructors.swift:10:5:12:5 | self | constructors.swift:10:5:12:5 | init |
| constructors.swift:14:5:16:5 | self | constructors.swift:14:5:16:5 | convenience init |
| constructors.swift:19:1:19:0 | self | constructors.swift:19:1:19:0 | init |
| constructors.swift:22:5:22:25 | self | constructors.swift:22:5:22:25 | init |
| constructors.swift:24:5:24:30 | self | constructors.swift:24:5:24:30 | init |
| constructors.swift:26:5:28:5 | self | constructors.swift:26:5:28:5 | convenience init |
| constructors.swift:31:1:31:0 | self | constructors.swift:31:1:31:0 | init |
| constructors.swift:34:5:36:5 | self | constructors.swift:34:5:36:5 | convenience init |
| constructors.swift:39:1:39:0 | self | constructors.swift:39:1:39:0 | init |
| constructors.swift:46:9:48:9 | self | constructors.swift:46:9:48:9 | AccessorDeclaration |
| constructors.swift:49:9:51:9 | self | constructors.swift:49:9:51:9 | AccessorDeclaration |
| constructors.swift:54:9:56:9 | self | constructors.swift:54:9:56:9 | AccessorDeclaration |
| constructors.swift:57:9:59:9 | self | constructors.swift:57:9:59:9 | AccessorDeclaration |
callableExDefaultConstructorParameter
| constructors.swift:39:1:39:0 | init | 1 | constructors.swift:39:1:39:0 | f1 [init default constructor] | f1 |
| constructors.swift:39:1:39:0 | init | 2 | constructors.swift:39:1:39:0 | f2 [init default constructor] | f2 |
| constructors.swift:39:1:39:0 | init | 3 | constructors.swift:39:1:39:0 | f4 [init default constructor] | f4 |
| constructors.swift:39:1:39:0 | init | 4 | constructors.swift:39:1:39:0 | f7 [init default constructor] | f7 |
13 changes: 13 additions & 0 deletions unified/ql/test/library-tests/constructors/test.ql
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import unified
import codeql.unified.internal.ParameterEx

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

query predicate implicitReceiverParameter(ParameterEx p, Callable c) {
p.isImplicitReceiverParameter(c) and
c.getFile().fromSource()
}

query predicate callableExDefaultConstructorParameter(
ConstructorDeclaration cd, int i, ParameterEx p, string name
) {
p.isDefaultConstructorParameter(cd, i, name) and
cd.getFile().fromSource()
}
Loading