Skip to content

Commit 16f5ce6

Browse files
committed
Unified: Resolve accessor accesses in type inference
1 parent 910ef24 commit 16f5ce6

8 files changed

Lines changed: 523 additions & 26 deletions

File tree

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,8 @@ module Unified {
451451
*/
452452
final class GetterDeclaration extends AccessorDeclaration {
453453
GetterDeclaration() { this.getAccessorKind().getValue() = "get" }
454+
455+
override string getNameOrFallback() { result = "get_" + this.getNameNode().getValue() }
454456
}
455457

456458
/**
@@ -466,6 +468,8 @@ module Unified {
466468
*/
467469
final class SetterDeclaration extends AccessorDeclaration {
468470
SetterDeclaration() { this.getAccessorKind().getValue() = "set" }
471+
472+
override string getNameOrFallback() { result = "set_" + this.getNameNode().getValue() }
469473
}
470474

471475
/** An `if` statement or conditional expression. */

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,33 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig<Locatio
179179
any(NameBindingPlugin p).isNonPattern(e)
180180
}
181181

182+
pragma[nomagic]
183+
private predicate accessorInScope(AstNode scope, string name, AccessorDeclaration accessor) {
184+
getChild(scope, _) = accessor and
185+
name = accessor.getName()
186+
}
187+
188+
pragma[nomagic]
189+
private predicate variableDeclarationInScope(AstNode scope, string name) {
190+
exists(VariableDeclaration decl |
191+
getChild(scope, _) = decl and
192+
name = decl.getName()
193+
)
194+
}
195+
196+
/**
197+
* Holds if the given accessor has a backing field with the same name.
198+
*
199+
* In this case we resolve all accesses to the field instead of any of the
200+
* accessors using the field.
201+
*/
202+
additional predicate hasBackingField(AccessorDeclaration accessor) {
203+
exists(AstNode scope, string name |
204+
accessorInScope(scope, name, accessor) and
205+
variableDeclarationInScope(scope, name)
206+
)
207+
}
208+
182209
cached
183210
additional predicate bindingContext(AstNode pattern, AstNode scope, AstNode declaration) {
184211
LocalNameBindingOutput::CachedStage::ref() and
@@ -253,6 +280,7 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig<Locatio
253280
)
254281
or
255282
exists(AccessorDeclaration decl |
283+
not hasBackingField(decl) and
256284
getChild(scope, _) = decl and
257285
pattern = decl.getNameNode() and
258286
declaration = decl
@@ -474,6 +502,10 @@ module Public {
474502
* ```
475503
*/
476504
class PotentialLocalNameAccess extends IdentifierExpr {
505+
PotentialLocalNameAccess() {
506+
not this = any(AccessorDeclaration a | LocalNameBindingInput::hasBackingField(a)).getNameNode()
507+
}
508+
477509
/** Gets the representative for the local name being accessed. */
478510
LocalName getLocalName() { result = this.(LocalNameBindingOutput::LocalAccess).getLocal() }
479511

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ predicate isInstanceMember(Member member) {
8181
(
8282
member instanceof VariableDeclaration or
8383
member instanceof FunctionDeclaration or
84-
member instanceof ConstructorDeclaration
84+
member instanceof ConstructorDeclaration or
85+
member instanceof GetterDeclaration or
86+
member instanceof SetterDeclaration
8587
) and
8688
exists(ClassLikeDeclaration cls | cls.getAMember() = member |
8789
any(NameBindingPlugin p).isInstanceMember(cls, member)

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

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ module Input3 implements InputSig3 {
123123
predicate cacheRevRef() {
124124
(exists(resolveFieldAccess(_)) implies any())
125125
or
126+
(exists(resolveAccessorAccess(_)) implies any())
127+
or
126128
(exists(resolveCallTarget(_)) implies any())
127129
}
128130

@@ -252,24 +254,55 @@ module Input3 implements InputSig3 {
252254
override Location getLocation() { result = decl.getLocation() }
253255
}
254256

255-
class Field extends AstVariableDeclaration {
257+
final class Field = FieldImpl;
258+
259+
abstract private class FieldImpl extends DeclarationImpl {
260+
abstract AstNode getAstNode();
261+
}
262+
263+
private class VariableDeclarationField extends FieldImpl, AstVariableDeclaration {
256264
ClassLikeDeclaration cls;
257265

258-
Field() { decl = cls.getAMember() }
266+
VariableDeclarationField() { decl = cls.getAMember() }
267+
268+
override AstNode getAstNode() { result = this.getDecl() }
259269

260270
override TypeMention getDeclaringType() { result = getClassLikeDeclarationTypeMention(cls) }
261271
}
262272

263-
private class EnumField_ extends Field {
273+
private class EnumField_ extends VariableDeclarationField {
264274
override EnumField decl;
265275

266276
override TypeMention getType() { result = decl.getEnum().getNameNode() }
267277
}
268278

279+
private class AccessorField extends FieldImpl, Callable {
280+
private AccessorDeclaration accessor;
281+
282+
AccessorField() { accessor = this.getCallable() }
283+
284+
override TypeMention getType() { result = accessor.getType() }
285+
286+
override AstNode getAstNode() { result = accessor }
287+
}
288+
269289
abstract class FieldAccess extends Expr {
270290
abstract Expr getReceiver();
271291

272-
abstract Field getField();
292+
abstract Field getFieldImpl();
293+
294+
Field getField() {
295+
result = this.getFieldImpl() and
296+
(
297+
result instanceof VariableDeclaration
298+
or
299+
hasIncomingValue(this, _) and
300+
result.getAstNode() instanceof SetterDeclaration
301+
or
302+
hasResultValue(this) and
303+
result.getAstNode() instanceof GetterDeclaration
304+
)
305+
}
273306
}
274307

275308
pragma[nomagic]
@@ -296,14 +329,14 @@ module Input3 implements InputSig3 {
296329
name = this.getMemberName()
297330
}
298331

299-
override Field getField() {
332+
override Field getFieldImpl() {
300333
// mutual recursion; resolving qualified fields requires resolving types and vice versa
301334
result = lookupInstanceMember(this)
302335
or
303336
// also mutual recursion
304337
exists(TupleType tuple, string name |
305338
this.isTupleProjection(tuple, name) and
306-
result.getDecl() = tuple.getField(name)
339+
result.getAstNode() = tuple.getField(name)
307340
)
308341
or
309342
// no mutual recursion; can be resolved directly with static name binding
@@ -322,7 +355,7 @@ module Input3 implements InputSig3 {
322355
private class UnqualifiedMemberAccessFieldAccess extends FieldAccess, UnqualifiedMemberAccess {
323356
override Expr getReceiver() { none() }
324357

325-
override Field getField() { result.getNameNode() = this.getTarget() }
358+
override Field getFieldImpl() { result.getNameNode() = this.getTarget() }
326359
}
327360

328361
Type inferFieldAccessReceiverType(FieldAccess fa, TypePath path) {
@@ -506,17 +539,21 @@ module Input3 implements InputSig3 {
506539
}
507540

508541
Callable getTargetImpl(boolean isFunctionExprInvoke) {
509-
// mutual recursion; call with explicit receiver: `obj.foo(42)`
510-
result = lookupInstanceMember(this.getCallee()) and
511-
isFunctionExprInvoke = false
512-
or
513-
// mutual recursion; call to a function expression: `callback(42)`
514-
result.asCallable() = this.getInvokeTarget(_) and
515-
isFunctionExprInvoke = true
516-
or
517-
// no mutual recursion; can be resolved directly with static name binding
518-
result = this.getTargetViaStaticNameBinding() and
519-
isFunctionExprInvoke = false
542+
(
543+
// mutual recursion; call with explicit receiver: `obj.foo(42)`
544+
result = lookupInstanceMember(this.getCallee()) and
545+
isFunctionExprInvoke = false
546+
or
547+
// mutual recursion; call to a function expression: `callback(42)`
548+
result.asCallable() = this.getInvokeTarget(_) and
549+
isFunctionExprInvoke = true
550+
or
551+
// no mutual recursion; can be resolved directly with static name binding
552+
result = this.getTargetViaStaticNameBinding() and
553+
isFunctionExprInvoke = false
554+
) and
555+
// handled as a field access
556+
not result.asCallable() instanceof AccessorDeclaration
520557
}
521558

522559
Callable getTarget(InvocationResolutionContext c) {
@@ -835,7 +872,19 @@ module CachedStage = M3::CachedStage;
835872
cached
836873
VariableDeclaration resolveFieldAccess(Expr access) {
837874
CachedStage::ref() and
838-
result = access.(Input3::FieldAccess).getField().getDecl()
875+
result = access.(Input3::FieldAccess).getField().getAstNode()
876+
}
877+
878+
/**
879+
* Gets the accessor that is accessed by the given expression.
880+
*
881+
* `access` is either a `MemberAccessExpr` or an `Identifier` (when
882+
* the field is accessed with an implicit receiver).
883+
*/
884+
cached
885+
AccessorDeclaration resolveAccessorAccess(Expr access) {
886+
CachedStage::ref() and
887+
result = access.(Input3::FieldAccess).getField().getAstNode()
839888
}
840889

841890
/**

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ abstract private class TypeMentionImpl extends AstNode {
2424
final class ExprTypeMention = ExprTypeMentionImpl;
2525

2626
abstract private class ExprTypeMentionImpl extends TypeMentionImpl, Expr {
27-
ExprTypeMentionImpl() { isInTypeContext(this) and not this instanceof GenericTypeExpr }
27+
ExprTypeMentionImpl() {
28+
isInTypeContext(this) and
29+
not this instanceof GenericTypeExpr and
30+
not this instanceof ModifiedTypeExpr
31+
}
2832

2933
pragma[nomagic]
3034
abstract TypePath getTypeArgumentPath(int i);
@@ -191,6 +195,14 @@ private class ClassLikeDeclarationTypeMention extends TypeMentionImpl, Identifie
191195
}
192196
}
193197

198+
private class ModifiedTypeExprMention extends TypeMentionImpl instanceof ModifiedTypeExpr {
199+
private TypeMention base;
200+
201+
ModifiedTypeExprMention() { base = ModifiedTypeExpr.super.getType() }
202+
203+
override Type getTypeAt(TypePath path) { result = base.getTypeAt(path) }
204+
}
205+
194206
TypeMention getClassLikeDeclarationTypeMention(ClassLikeDeclaration c) {
195207
result = c.getNameNode()
196208
or

0 commit comments

Comments
 (0)