Skip to content

Commit 1e676e1

Browse files
committed
Unified: Type inference for nested types
1 parent 61ef133 commit 1e676e1

16 files changed

Lines changed: 1864 additions & 221 deletions

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ module Public {
138138
final class EnumCaseClass extends ClassLikeDeclaration {
139139
EnumCaseClass() { this.hasModifier("enum_case") }
140140

141+
/** Gets the enum declaration that this case class belongs to. */
142+
ClassLikeDeclaration getEnumClass() { this = result.getAMember() }
143+
141144
/** Gets the constructor of this algebraic data type. */
142145
EnumCaseConstructor getConstructor() { result = this.getAMember() }
143146
}
@@ -168,5 +171,31 @@ module Public {
168171

169172
/** Gets the class containing this constructor. */
170173
EnumCaseClass getEnumCaseClass() { result.getAMember() = this }
174+
175+
/** Gets the enum declaration that this constructor belongs to. */
176+
ClassLikeDeclaration getEnumClass() { result = this.getEnumCaseClass().getEnumClass() }
177+
}
178+
179+
/**
180+
* A field of an enum case in an algebraic data type. For example `a` and
181+
* `b` in
182+
*
183+
* ```swift
184+
* enum E {
185+
* case a
186+
* case b
187+
* }
188+
* ```
189+
*/
190+
final class EnumField extends VariableDeclaration {
191+
private ClassLikeDeclaration enum;
192+
193+
EnumField() {
194+
this = enum.getAMember() and
195+
this.hasModifier("enum_case")
196+
}
197+
198+
/** Gets the enum declaration that this enum field belongs to. */
199+
ClassLikeDeclaration getEnumClass() { result = enum }
171200
}
172201
}

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ private import NameBinding as NameBinding
33
private import ExprPositionsPlugin
44

55
/**
6-
* Holds if `expr` appears in a context where it must refer to a type.
6+
* Holds if `expr` appears in a context where it must refer to a type,
7+
* based entirely on the syntactic context of the expression.
78
*/
8-
predicate isInTypeContext(Expr expr) {
9+
predicate isInSyntacticTypeContext(Expr expr) {
910
expr = any(TypeCastExpr n).getType()
1011
or
1112
expr = any(TypeTestExpr n).getType()
@@ -36,11 +37,24 @@ predicate isInTypeContext(Expr expr) {
3637
or
3738
expr.getParent() instanceof TypeConstraint
3839
or
40+
any(ExprPositionsPlugin p).isInSyntacticTypeContext(expr)
41+
or
42+
isInSyntacticTypeContext(expr.getEnclosingExpr())
43+
}
44+
45+
private predicate isInTypeContext0(Expr expr) {
3946
exists(NameBinding::getTypeFromRef(expr))
4047
or
41-
any(ExprPositionsPlugin p).isInTypeContext(expr)
48+
isInTypeContext0(expr.getEnclosingExpr())
49+
}
50+
51+
/**
52+
* Holds if `expr` appears in a context where it must refer to a type.
53+
*/
54+
predicate isInTypeContext(Expr expr) {
55+
isInSyntacticTypeContext(expr)
4256
or
43-
isInTypeContext(expr.getEnclosingExpr())
57+
isInTypeContext0(expr)
4458
}
4559

4660
/** Holds if `e` appears in a name-binding position inside `declaration` */

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ private module Plugins {
66
}
77

88
class ExprPositionsPlugin extends Unit {
9-
predicate isInTypeContext(Expr e) { none() }
9+
predicate isInSyntacticTypeContext(Expr e) { none() }
1010

1111
predicate isReturnedExpr(Callable callable, Expr e) { none() }
1212
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ private import unified
22
private import ExprPositionsPlugin
33

44
private class ExprPositionsPluginSwift extends ExprPositionsPlugin {
5-
override predicate isInTypeContext(Expr e) { e = any(GenericTypeExpr g).getBase() }
5+
override predicate isInSyntacticTypeContext(Expr e) { e = any(GenericTypeExpr g).getBase() }
66

77
override predicate isReturnedExpr(Callable callable, Expr e) {
88
// Short-hand return from a function body, e.g. `func foo() -> String { "42" }`

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ private import unified as U
88
private import AstPlugin
99
private import codeql.files.FileSystem
1010
private import codeql.unified.internal.NameBinding as NameBinding
11+
private import codeql.unified.internal.typeinference.TypeMention as TypeMention
1112

1213
module Unified {
1314
private import Ast::Unified as G
@@ -50,6 +51,11 @@ module Unified {
5051
)
5152
}
5253

54+
/**
55+
* Gets the nearest resolved enclosing class declaration, if any.
56+
*/
57+
ClassLikeDeclaration getEnclosingClassResolved() { result = this.getEnclosingClass().resolve() }
58+
5359
private AstNode overrideEnclosingCallableParent() {
5460
exists(FunctionExpr func |
5561
// Capture declarations are evaluated as part of the outer context, and
@@ -239,15 +245,16 @@ module Unified {
239245
/** Gets the name of this declaration. */
240246
string getName() { result = this.getNameNode().getValue() }
241247

242-
/** Gets a direct base class of this class. */
248+
/** Gets a direct base class of this class, where type aliases are resolved. */
249+
pragma[nomagic]
243250
ClassLikeDeclaration getABaseClass() {
244-
result.getNameNode() =
245-
NameBinding::getStaticBindingTargetFromRef(this.getABaseType().getType())
251+
result = this.getABaseType().getType().(TypeMention::TypeMention).getClassLikeDeclaration()
246252
}
247253

248254
/**
249255
* Gets the target of this extension class, or this class itself if it is not an extension.
250256
*/
257+
pragma[nomagic]
251258
ClassLikeDeclaration resolve() {
252259
not exists(this.getExtensionTarget()) and
253260
result = this

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

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module;
44

55
import unified // reexport in order to shadow e.g. the `TypeParameter` class
66
private import unified as Unified
7+
private import TypeMention
78
private import TypeInference
89
private import codeql.unified.internal.StaticNameBinding
910

@@ -38,12 +39,24 @@ predicate associatedTypeParameterInherited(
3839
name = a.getName()
3940
}
4041

42+
private predicate isNestedType(Member m, Identifier nested, ClassLikeDeclaration parent) {
43+
exists(parent.getNameNode()) and
44+
nested = m.getNameNode() and
45+
m = parent.getAMemberIncludingExtensions() and
46+
(
47+
exists(m.(ClassLikeDeclaration).getNameNode())
48+
or
49+
m instanceof TypeAliasDeclaration
50+
)
51+
}
52+
4153
cached
4254
newtype TType =
4355
TClassLikeDeclarationType(ClassLikeDeclaration c) {
4456
CachedStage::ref() and
4557
exists(c.getNameNode())
4658
} or
59+
TAliasType(TypeAliasDeclaration alias) or
4760
TClosureParameterPseudoType(Parameter p) {
4861
exists(FunctionExpr fe |
4962
p = fe.getAParameter() and
@@ -56,6 +69,7 @@ newtype TType =
5669
) {
5770
associatedTypeParameter(c, a, inherited)
5871
} or
72+
TParentTypeParameterType(Member m) { isNestedType(m, _, _) } or
5973
TUnknownType() or
6074
TUnknownTypeTypeParameter(int i) { i in [0 .. 20] }
6175

@@ -78,7 +92,14 @@ abstract private class TypeImpl extends TType {
7892
* This includes both positional type parameters and synthetic type parameters,
7993
* such as associated types.
8094
*/
81-
TypeParameter getATypeParameter() { result = this.getPositionalTypeParameter(_) }
95+
TypeParameter getATypeParameter() {
96+
result = this.getPositionalTypeParameter(_)
97+
or
98+
exists(Member m |
99+
result = TParentTypeParameterType(m) and
100+
isNestedType(m, this.getNameNode(), _)
101+
)
102+
}
82103

83104
/** Gets the name node of this type, if any. */
84105
abstract Identifier getNameNode();
@@ -117,11 +138,35 @@ class ClassLikeDeclarationType extends TypeImpl, TClassLikeDeclarationType {
117138
result = TAssociatedTypeParameterType(c, _, _)
118139
}
119140

120-
override string toString() { result = c.getName() }
141+
override string toString() { result = c.getQualifiedName() }
121142

122143
override Location getLocation() { result = c.getLocation() }
123144
}
124145

146+
/**
147+
* A type representing a type alias. For type inference, type aliases are expanded,
148+
* but we need them for resolving type mentions.
149+
*/
150+
class AliasType extends TypeImpl, TAliasType {
151+
TypeAliasDeclaration alias;
152+
153+
AliasType() { this = TAliasType(alias) }
154+
155+
TypeAliasDeclaration getTypeAliasDeclaration() { result = alias }
156+
157+
TypeMention getAlias() { result = alias.getType() }
158+
159+
override Identifier getNameNode() { result = alias.getNameNode() }
160+
161+
override TypeParameter getPositionalTypeParameter(int i) {
162+
result = TTypeParameterType(alias.getTypeParameter(i))
163+
}
164+
165+
override string toString() { result = alias.getQualifiedName() }
166+
167+
override Location getLocation() { result = alias.getLocation() }
168+
}
169+
125170
/**
126171
* A pseudo type that does not correspond to any concrete type in the source code.
127172
*/
@@ -165,7 +210,8 @@ abstract class TypeParameter extends TypeImpl {
165210
}
166211

167212
private class IdAstNode =
168-
@unified_type_parameter or @unified_class_like_declaration or @unified_associated_type_declaration;
213+
@unified_type_parameter or @unified_class_like_declaration or
214+
@unified_associated_type_declaration or @unified_member;
169215

170216
private predicate id(IdAstNode x, IdAstNode y) { x = y }
171217

@@ -217,6 +263,45 @@ class AssociatedTypeParameterType extends TypeParameter, TAssociatedTypeParamete
217263
}
218264
}
219265

266+
/**
267+
* A special type parameter used to represent the parent type of a nested class.
268+
*
269+
* For example, for
270+
*
271+
* ```swift
272+
* class Outer<TOuter> {
273+
* class Inner<TInner> {
274+
* }
275+
* }
276+
* ```
277+
*
278+
* the type `Outer<Int>.Inner<String>` is represented as
279+
*
280+
* `TypePath` | `Type`
281+
* ----------------------- | --------
282+
* `""` | `Inner`
283+
* `"TInner"` | `String`
284+
* `"Inner_Parent"` | `Outer`
285+
* `"Inner_Parent.TOuter"` | `Int`
286+
*
287+
* where `Inner_Parent` is one of these type parameters.
288+
*/
289+
class ParentTypeParameterType extends TypeParameter, TParentTypeParameterType {
290+
private Member m;
291+
292+
ParentTypeParameterType() { this = TParentTypeParameterType(m) }
293+
294+
ClassLikeDeclaration getParent() { isNestedType(m, _, result) }
295+
296+
override Member getDeclaringItem() { result = m }
297+
298+
override Identifier getNameNode() { none() }
299+
300+
override string toString() { result = "(parent:" + m.getNameOrFallback() + ")" }
301+
302+
override Location getLocation() { result = m.getLocation() }
303+
}
304+
220305
/**
221306
* A type parameter of the special `UnknownType`.
222307
*/

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,10 @@ abstract class TypeAbstraction extends AstNode {
1515
*/
1616
private class ClassLikeDeclarationTypeAbstraction extends TypeAbstraction instanceof Unified::ClassLikeDeclaration
1717
{
18-
override TypeParameter getATypeParameter() { this = result.getDeclaringItem() }
18+
override TypeParameter getATypeParameter() {
19+
this = result.getDeclaringItem()
20+
or
21+
// type parameters from outer classes are also in scope
22+
super.resolve().getEnclosingClassResolved*() = result.getDeclaringItem()
23+
}
1924
}

0 commit comments

Comments
 (0)