Skip to content

Commit 9a2da2f

Browse files
committed
Unified: Type inference for nested types
1 parent 824bba2 commit 9a2da2f

18 files changed

Lines changed: 1974 additions & 198 deletions

‎shared/typeinference/codeql/typeinference/internal/TypeInference.qll‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2140,13 +2140,24 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
21402140

21412141
/** An AST node. */
21422142
class AstNode {
2143+
/** Gets the parent of this AST node, if any. */
2144+
AstNode getParent();
2145+
21432146
/** Gets a textual representation of this AST node. */
21442147
string toString();
21452148

21462149
/** Gets the location of this AST node. */
21472150
Location getLocation();
21482151
}
21492152

2153+
/**
2154+
* Gets the scope of type parameter `tp`.
2155+
*
2156+
* Only used in a consistency check to ensure that type parameters do not
2157+
* escape their scope.
2158+
*/
2159+
AstNode getTypeParameterScope(TypeParameter tp);
2160+
21502161
/** An expression. */
21512162
class Expr extends AstNode;
21522163

@@ -3744,6 +3755,11 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
37443755
Input3::inferTypeForDefaults(n, path) = t and
37453756
not inferType(n, path) = t
37463757
}
3758+
3759+
query predicate typeParameterEscape(AstNode n, TypePath path, TypeParameter tp) {
3760+
tp = inferType(n, path) and
3761+
not n.getParent*() = getTypeParameterScope(tp)
3762+
}
37473763
}
37483764

37493765
/**

‎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 getEnum() { 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 getEnum() { result = this.getEnumCaseClass().getEnum() }
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 getEnum() { 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
@@ -38,12 +38,22 @@ predicate associatedTypeParameterInherited(
3838
name = a.getName()
3939
}
4040

41+
private predicate isNestedType(Member m, Identifier nested, ClassLikeDeclaration parent) {
42+
// exists(nested.getNameNode()) and
43+
// exists(Member m |
44+
exists(parent.getNameNode()) and
45+
nested = m.getNameNode() and
46+
m = parent.getAMemberIncludingExtensions()
47+
// )
48+
}
49+
4150
cached
4251
newtype TType =
4352
TClassLikeDeclarationType(ClassLikeDeclaration c) {
4453
CachedStage::ref() and
4554
exists(c.getNameNode())
4655
} or
56+
TAliasType(TypeAliasDeclaration alias) or
4757
TClosureParameterPseudoType(Parameter p) {
4858
exists(FunctionExpr fe |
4959
p = fe.getAParameter() and
@@ -56,6 +66,14 @@ newtype TType =
5666
) {
5767
associatedTypeParameter(c, a, inherited)
5868
} or
69+
TParentTypeParameterType(Member m) {
70+
isNestedType(m, _, _) and
71+
(
72+
exists(m.(ClassLikeDeclaration).getNameNode())
73+
or
74+
m instanceof TypeAliasDeclaration
75+
)
76+
} or
5977
TUnknownType() or
6078
TUnknownTypeTypeParameter(int i) { i in [0 .. 20] }
6179

@@ -78,7 +96,14 @@ abstract private class TypeImpl extends TType {
7896
* This includes both positional type parameters and synthetic type parameters,
7997
* such as associated types.
8098
*/
81-
TypeParameter getATypeParameter() { result = this.getPositionalTypeParameter(_) }
99+
TypeParameter getATypeParameter() {
100+
result = this.getPositionalTypeParameter(_)
101+
or
102+
exists(Member m |
103+
result = TParentTypeParameterType(m) and
104+
isNestedType(m, this.getNameNode(), _)
105+
)
106+
}
82107

83108
/** Gets the name node of this type, if any. */
84109
abstract Identifier getNameNode();
@@ -117,11 +142,29 @@ class ClassLikeDeclarationType extends TypeImpl, TClassLikeDeclarationType {
117142
result = TAssociatedTypeParameterType(c, _, _)
118143
}
119144

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

122147
override Location getLocation() { result = c.getLocation() }
123148
}
124149

150+
class AliasType extends TypeImpl, TAliasType {
151+
TypeAliasDeclaration alias;
152+
153+
AliasType() { this = TAliasType(alias) }
154+
155+
TypeAliasDeclaration getTypeAliasDeclaration() { result = alias }
156+
157+
override Identifier getNameNode() { result = alias.getNameNode() }
158+
159+
override TypeParameter getPositionalTypeParameter(int i) {
160+
result = TTypeParameterType(alias.getTypeParameter(i))
161+
}
162+
163+
override string toString() { result = alias.getQualifiedName() }
164+
165+
override Location getLocation() { result = alias.getLocation() }
166+
}
167+
125168
/**
126169
* A pseudo type that does not correspond to any concrete type in the source code.
127170
*/
@@ -159,13 +202,16 @@ class ClosureParameterPseudoType extends PseudoType, TClosureParameterPseudoType
159202

160203
/** A type parameter. */
161204
abstract class TypeParameter extends TypeImpl {
205+
abstract AstNode getScope();
206+
162207
override TypeParameter getPositionalTypeParameter(int i) { none() }
163208

164209
abstract AstNode getDeclaringItem();
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

@@ -181,6 +227,12 @@ class TypeParameterType extends TypeParameter, TTypeParameterType {
181227

182228
Unified::TypeParameter getTypeParameter() { result = typeParam }
183229

230+
override AstNode getScope() {
231+
result = typeParam.getParent()
232+
or
233+
result.(ClassLikeDeclaration).resolve().getEnclosingClassResolved*() = typeParam.getParent()
234+
}
235+
184236
override ClassLikeDeclaration getDeclaringItem() { typeParam = result.getATypeParameter() }
185237

186238
override Identifier getNameNode() { result = typeParam.getNameNode() }
@@ -202,6 +254,8 @@ class AssociatedTypeParameterType extends TypeParameter, TAssociatedTypeParamete
202254

203255
Unified::AssociatedTypeDeclaration getAssociatedTypeDeclaration() { result = assocTypeDecl }
204256

257+
override AstNode getScope() { result.(ClassLikeDeclaration).resolve() = c }
258+
205259
override ClassLikeDeclaration getDeclaringItem() { result = c }
206260

207261
override Identifier getNameNode() { result = assocTypeDecl.getNameNode() }
@@ -217,6 +271,35 @@ class AssociatedTypeParameterType extends TypeParameter, TAssociatedTypeParamete
217271
}
218272
}
219273

274+
/**
275+
* A special type parameter used to represent the parent type of a nested class.
276+
*/
277+
class ParentTypeParameterType extends TypeParameter, TParentTypeParameterType {
278+
private Member m;
279+
280+
ParentTypeParameterType() { this = TParentTypeParameterType(m) }
281+
282+
ClassLikeDeclaration getParent() { isNestedType(m, _, result) }
283+
284+
Type getChild() {
285+
result.(ClassLikeDeclarationType).getClassLikeDeclaration() = m
286+
or
287+
result.(AliasType).getTypeAliasDeclaration() = m
288+
}
289+
290+
Member getMember() { result = m }
291+
292+
override AstNode getScope() { none() }
293+
294+
override Member getDeclaringItem() { result = m }
295+
296+
override Identifier getNameNode() { none() }
297+
298+
override string toString() { result = "(parent:" + m.getNameOrFallback() + ")" }
299+
300+
override Location getLocation() { result = m.getLocation() }
301+
}
302+
220303
/**
221304
* A type parameter of the special `UnknownType`.
222305
*/
@@ -225,6 +308,8 @@ class UnknownTypeTypeParameter extends TypeParameter, TUnknownTypeTypeParameter
225308

226309
UnknownTypeTypeParameter() { this = TUnknownTypeTypeParameter(i) }
227310

311+
override AstNode getScope() { none() }
312+
228313
override AstNode getDeclaringItem() { none() }
229314

230315
override Identifier getNameNode() { none() }

‎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)