Skip to content

Commit 27ba20a

Browse files
committed
Unified: Resolve accessor accesses in type inference
1 parent 5dce8a0 commit 27ba20a

8 files changed

Lines changed: 551 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: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,35 @@ 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, VariableDeclaration decl) {
190+
getChild(scope, _) = decl and
191+
name = decl.getName()
192+
}
193+
194+
/**
195+
* Holds if the given accessor has a backing field with the same name.
196+
*
197+
* In this case we resolve all accesses to the field instead of any of the
198+
* accessors using the field.
199+
*/
200+
additional predicate hasBackingField(AccessorDeclaration accessor) {
201+
exists(AstNode scope, string name, VariableDeclaration decl |
202+
accessorInScope(scope, name, accessor) and
203+
variableDeclarationInScope(scope, name, decl) and
204+
(isInstanceMember(decl) implies isInstanceMember(accessor)) and
205+
(isInstanceMember(accessor) implies isInstanceMember(decl)) and
206+
(isStaticMember(decl) implies isStaticMember(accessor)) and
207+
(isStaticMember(accessor) implies isStaticMember(decl))
208+
)
209+
}
210+
182211
cached
183212
additional predicate bindingContext(AstNode pattern, AstNode scope, AstNode declaration) {
184213
LocalNameBindingOutput::CachedStage::ref() and
@@ -253,6 +282,7 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig<Locatio
253282
)
254283
or
255284
exists(AccessorDeclaration decl |
285+
not hasBackingField(decl) and
256286
getChild(scope, _) = decl and
257287
pattern = decl.getNameNode() and
258288
declaration = decl
@@ -474,6 +504,10 @@ module Public {
474504
* ```
475505
*/
476506
class PotentialLocalNameAccess extends IdentifierExpr {
507+
PotentialLocalNameAccess() {
508+
not this = any(AccessorDeclaration a | LocalNameBindingInput::hasBackingField(a)).getNameNode()
509+
}
510+
477511
/** Gets the representative for the local name being accessed. */
478512
LocalName getLocalName() { result = this.(LocalNameBindingOutput::LocalAccess).getLocal() }
479513

‎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: 80 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,19 @@ 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() }
359+
}
360+
361+
private class LocalAccessorFieldAccess extends FieldAccess, LocalNameAccess {
362+
AccessorDeclaration target;
363+
364+
LocalAccessorFieldAccess() {
365+
target = getStaticBindingTargetFromIdentifier(this).getDeclaration()
366+
}
367+
368+
override Expr getReceiver() { none() }
369+
370+
override Field getFieldImpl() { result.getAstNode() = target }
326371
}
327372

328373
Type inferFieldAccessReceiverType(FieldAccess fa, TypePath path) {
@@ -506,17 +551,21 @@ module Input3 implements InputSig3 {
506551
}
507552

508553
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
554+
(
555+
// mutual recursion; call with explicit receiver: `obj.foo(42)`
556+
result = lookupInstanceMember(this.getCallee()) and
557+
isFunctionExprInvoke = false
558+
or
559+
// mutual recursion; call to a function expression: `callback(42)`
560+
result.asCallable() = this.getInvokeTarget(_) and
561+
isFunctionExprInvoke = true
562+
or
563+
// no mutual recursion; can be resolved directly with static name binding
564+
result = this.getTargetViaStaticNameBinding() and
565+
isFunctionExprInvoke = false
566+
) and
567+
// handled as a field access
568+
not result.asCallable() instanceof AccessorDeclaration
520569
}
521570

522571
Callable getTarget(InvocationResolutionContext c) {
@@ -835,7 +884,19 @@ module CachedStage = M3::CachedStage;
835884
cached
836885
VariableDeclaration resolveFieldAccess(Expr access) {
837886
CachedStage::ref() and
838-
result = access.(Input3::FieldAccess).getField().getDecl()
887+
result = access.(Input3::FieldAccess).getField().getAstNode()
888+
}
889+
890+
/**
891+
* Gets the accessor that is accessed by the given expression.
892+
*
893+
* `access` is either a `MemberAccessExpr` or an `Identifier` (when
894+
* the field is accessed with an implicit receiver).
895+
*/
896+
cached
897+
AccessorDeclaration resolveAccessorAccess(Expr access) {
898+
CachedStage::ref() and
899+
result = access.(Input3::FieldAccess).getField().getAstNode()
839900
}
840901

841902
/**

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