Skip to content

Commit ed5f381

Browse files
authored
Merge pull request #22790 from hvitved/unified/type-inference-param-ex
Unified: Use `ParameterEx` in type inference
2 parents 5d625d7 + 57e2a96 commit ed5f381

14 files changed

Lines changed: 371 additions & 308 deletions

File tree

‎unified/extractor/ast_types.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ supertypes:
7979
fields:
8080
parameter*: parameter
8181
body?: block
82+
return_type?: expr
8283
# A member is anything that can appear in the body of a class-like declaration
8384
member:
8485
subtypes:

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,9 @@ module Unified {
369369

370370
/** Gets the node corresponding to the field `parameter`. */
371371
F::Parameter getAParameter() { none() }
372+
373+
/** Gets the node corresponding to the field `return_type`. */
374+
F::Expr getReturnType() { none() }
372375
}
373376

374377
/** A class representing `catch_clause` nodes. */
@@ -721,7 +724,9 @@ module Unified {
721724
final override F::Parameter getAParameter() { result = this.getParameter(_) }
722725

723726
/** Gets the node corresponding to the field `return_type`. */
724-
final F::Expr getReturnType() { unified_function_declaration_return_type(this, result) }
727+
final override F::Expr getReturnType() {
728+
unified_function_declaration_return_type(this, result)
729+
}
725730

726731
/** Gets the node corresponding to the field `type_constraint`. */
727732
final F::TypeConstraint getTypeConstraint(int i) {
@@ -782,7 +787,7 @@ module Unified {
782787
final override F::Parameter getAParameter() { result = this.getParameter(_) }
783788

784789
/** Gets the node corresponding to the field `return_type`. */
785-
final F::Expr getReturnType() { unified_function_expr_return_type(this, result) }
790+
final override F::Expr getReturnType() { unified_function_expr_return_type(this, result) }
786791

787792
/** Gets a field or child node of this node. */
788793
final override F::AstNode getAFieldOrChild() {

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

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
overlay[local]
1+
overlay[local?]
22
module;
33

44
private import unified
@@ -23,10 +23,26 @@ class AstPlugin extends Unit {
2323

2424
/**
2525
* Holds if the default constructor `cd` needs a parameter at index `i` with
26-
* the given name. `i = 0` is reserved for the implicit receiver parameter.
26+
* the given name. `hasDefault` indicates whether the parameter has a default
27+
* value.
28+
*
29+
* `i = 0` is reserved for the implicit receiver parameter.
2730
*/
2831
bindingset[cd]
29-
predicate defaultConstructorParameter(ConstructorDeclaration cd, int i, string name) { none() }
32+
predicate defaultConstructorParameter(
33+
ConstructorDeclaration cd, int i, string name, boolean hasDefault
34+
) {
35+
none()
36+
}
37+
38+
/**
39+
* Holds if the type of the `i`th parameter of the default constructor `cd`
40+
* is `type`.
41+
*/
42+
bindingset[cd]
43+
predicate defaultConstructorParameterType(ConstructorDeclaration cd, int i, AstNode type) {
44+
none()
45+
}
3046
}
3147

3248
bindingset[f]
@@ -50,6 +66,13 @@ string getVariableDeclarationKeyword(VariableDeclaration decl) {
5066
}
5167

5268
bindingset[cd]
53-
predicate defaultConstructorParameter(ConstructorDeclaration cd, int i, string name) {
54-
any(AstPlugin p).defaultConstructorParameter(cd, i, name)
69+
predicate defaultConstructorParameter(
70+
ConstructorDeclaration cd, int i, string name, boolean hasDefault
71+
) {
72+
any(AstPlugin p).defaultConstructorParameter(cd, i, name, hasDefault)
73+
}
74+
75+
bindingset[cd]
76+
predicate defaultConstructorParameterType(ConstructorDeclaration cd, int i, AstNode type) {
77+
any(AstPlugin p).defaultConstructorParameterType(cd, i, type)
5578
}
Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,36 @@
1-
overlay[local]
1+
overlay[local?]
22
module;
33

44
private import unified
55
private import AstPlugin
66

7+
private predicate structHasParameterForField(
8+
ConstructorDeclaration cd, VariableDeclaration decl, int i, string name, boolean hasDefault
9+
) {
10+
exists(ClassLikeDeclaration struct |
11+
struct.hasModifier("struct") and
12+
cd = struct.getAMember() and
13+
decl = struct.getMember(i) and
14+
not decl.hasModifier("static") and
15+
name = decl.getPattern().(Identifier).getValue()
16+
|
17+
// if `decl` has an initializer or `Optional` type then the parameter has a default value
18+
decl.hasModifier("var") and
19+
if
20+
exists(decl.getValue())
21+
or
22+
// we could make this more precise using static name binding, but for now we
23+
// use a simpler approach to avoid the dependency on static name binding
24+
decl.getType().(GenericTypeExpr).getBase().(Identifier).getValue() = "Optional"
25+
then hasDefault = true
26+
else hasDefault = false
27+
or
28+
decl.hasModifier("let") and
29+
not exists(decl.getValue()) and
30+
hasDefault = false
31+
)
32+
}
33+
734
private class AstPluginSwift extends AstPlugin {
835
bindingset[f]
936
override string getFunctionDeclarationKeyword(FunctionDeclaration f) {
@@ -27,25 +54,19 @@ private class AstPluginSwift extends AstPlugin {
2754
}
2855

2956
bindingset[cd]
30-
override predicate defaultConstructorParameter(ConstructorDeclaration cd, int i, string name) {
31-
exists(ClassLikeDeclaration cls |
32-
cd = cls.getAMember() and
33-
cls.hasModifier("struct") and
34-
name =
35-
rank[i](VariableDeclaration decl, int j, string s |
36-
decl = cls.getMember(j) and
37-
not decl.hasModifier("static") and
38-
(
39-
// if `decl` has an initializer then this parameter has that initializer as a default value
40-
decl.hasModifier("var")
41-
or
42-
decl.hasModifier("let") and
43-
not exists(decl.getValue())
44-
) and
45-
s = decl.getPattern().(Identifier).getValue()
46-
|
47-
s order by j
48-
)
57+
override predicate defaultConstructorParameter(
58+
ConstructorDeclaration cd, int i, string name, boolean hasDefault
59+
) {
60+
name = rank[i](int j, string s | structHasParameterForField(cd, _, j, s, _) | s order by j) and
61+
structHasParameterForField(cd, _, _, name, hasDefault)
62+
}
63+
64+
bindingset[cd]
65+
override predicate defaultConstructorParameterType(ConstructorDeclaration cd, int i, AstNode type) {
66+
exists(VariableDeclaration decl, string name |
67+
this.defaultConstructorParameter(cd, i, name, _) and
68+
structHasParameterForField(cd, decl, _, name, _) and
69+
type = decl.getType()
4970
)
5071
}
5172
}

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

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ private newtype TParameterEx =
1111
TImplicitReceiverParameter(Callable c) {
1212
exists(LocalVariable v | v.isImplicitReceiverParameter(c))
1313
} or
14-
TDefaultConstructorParameter(ConstructorDeclaration cd, int i, string name) {
14+
TDefaultConstructorParameter(ConstructorDeclaration cd, int i, string name, boolean hasDefault) {
1515
cd.hasModifier("generated") and
16-
defaultConstructorParameter(cd, i, name)
16+
defaultConstructorParameter(cd, i, name, hasDefault)
1717
}
1818

1919
final class ParameterEx = ParameterExImpl;
@@ -42,7 +42,7 @@ abstract private class ParameterExImpl extends TParameterEx {
4242
* constructor `cd` with name `name`.
4343
*/
4444
predicate isDefaultConstructorParameter(ConstructorDeclaration cd, int i, string name) {
45-
this = TDefaultConstructorParameter(cd, i, name)
45+
this = TDefaultConstructorParameter(cd, i, name, _)
4646
}
4747

4848
/** Gets the callable that this parameter belongs to. */
@@ -60,6 +60,12 @@ abstract private class ParameterExImpl extends TParameterEx {
6060
this.isDefaultConstructorParameter(result, _, _)
6161
}
6262

63+
/** Holds if this parameter has a default value. */
64+
abstract predicate hasDefault();
65+
66+
/** Gets the type declaration of this parameter, if any. */
67+
abstract AstNode getType();
68+
6369
/** Gets a textual representation of this parameter. */
6470
abstract string toString();
6571

@@ -72,6 +78,10 @@ private class AstParameterEx extends ParameterExImpl, TAstParameter {
7278

7379
AstParameterEx() { this = TAstParameter(p) }
7480

81+
override predicate hasDefault() { exists(p.getDefault()) }
82+
83+
override AstNode getType() { result = p.getType() }
84+
7585
override string toString() { result = p.toString() }
7686

7787
override Location getLocation() { result = p.getLocation() }
@@ -82,6 +92,10 @@ private class ImplicitReceiverParameterEx extends ParameterExImpl, TImplicitRece
8292

8393
ImplicitReceiverParameterEx() { this = TImplicitReceiverParameter(c) }
8494

95+
override predicate hasDefault() { none() }
96+
97+
override AstNode getType() { none() }
98+
8599
override string toString() {
86100
exists(LocalVariable v |
87101
v.isImplicitReceiverParameter(c) and
@@ -96,8 +110,13 @@ private class DefaultConstructorParameterEx extends ParameterExImpl, TDefaultCon
96110
ConstructorDeclaration cd;
97111
int i;
98112
string name;
113+
boolean hasDefault;
114+
115+
DefaultConstructorParameterEx() { this = TDefaultConstructorParameter(cd, i, name, hasDefault) }
116+
117+
override AstNode getType() { defaultConstructorParameterType(cd, i, result) }
99118

100-
DefaultConstructorParameterEx() { this = TDefaultConstructorParameter(cd, i, name) }
119+
override predicate hasDefault() { hasDefault = true }
101120

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

‎unified/ql/lib/codeql/unified/internal/typeinference/Type.qll‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/** Provides classes representing types without type arguments. */
2+
overlay[local?]
3+
module;
24

35
import unified // reexport in order to shadow e.g. the `TypeParameter` class
46
private import unified as Unified

0 commit comments

Comments
 (0)