Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2491,6 +2491,8 @@ private module Input3 implements InputSig3 {
final class Parameter = ParameterImpl;

abstract private class ParameterImpl extends VariableDeclarationImpl {
string getExternalName() { none() } // arguments cannot be passed by name in Rust

override predicate preservesInitializerType() { none() } // doesn't really matter, since there are no initializers/default values

override AstNode getInitializer() { none() }
Expand Down Expand Up @@ -2642,14 +2644,22 @@ private module Input3 implements InputSig3 {
)
}

class Argument extends Expr {
string getName() { none() }

Expr getValue() { result = this }
}

final class Invocation = InvocationImpl;

abstract private class InvocationImpl extends Expr {
abstract Type getTypeQualifier(TypePath path);

abstract Type getTypeArgument(int pos, TypePath path);

abstract Expr getArgument(int i);
abstract Argument getArgument(int i);

predicate hasImplicitArgument(int i) { none() }

abstract Callable getTarget(string derefChainBorrow);

Expand Down Expand Up @@ -2678,7 +2688,7 @@ private module Input3 implements InputSig3 {
this.(MethodCallExpr).getGenericArgList().getTypeArg(pos).(TypeMention).getTypeAt(path)
}

override Expr getArgument(int i) {
override Argument getArgument(int i) {
exists(FunctionPosition pos |
i = pos.asPosition() and
result = super.getNodeAt(pos)
Expand Down Expand Up @@ -2726,7 +2736,7 @@ private module Input3 implements InputSig3 {
result = getCallExprTypeArgument(this, pos, path)
}

override Expr getArgument(int i) { result = NonAssocCallExpr.super.getArgument(i) }
override Argument getArgument(int i) { result = NonAssocCallExpr.super.getArgument(i) }

pragma[nomagic]
private Callable getTarget() { result = super.resolveCallTargetViaPathResolution() }
Expand Down Expand Up @@ -2764,7 +2774,7 @@ private module Input3 implements InputSig3 {

override Type getTypeArgument(int pos, TypePath path) { none() }

override Expr getArgument(int i) { result = NonAssocCallExpr.super.getArgument(i) }
override Argument getArgument(int i) { result = NonAssocCallExpr.super.getArgument(i) }

override Constructor getTarget() { result = super.resolveCallTargetViaPathResolution() }
}
Expand All @@ -2782,7 +2792,7 @@ private module Input3 implements InputSig3 {
}

private class StructExprConstruction extends StructConstruction, StructExpr {
override Expr getArgument(int i) {
override Argument getArgument(int i) {
result =
this.getFieldExpr(pragma[only_bind_into](this.getNthStructField(i).getName().getText()))
.getExpr()
Expand All @@ -2793,7 +2803,7 @@ private module Input3 implements InputSig3 {
private class PathExprConstruction extends StructConstruction, PathExpr {
PathExprConstruction() { not exists(CallExpr ce | this = ce.getFunction()) }

override Expr getArgument(int i) { none() }
override Argument getArgument(int i) { none() }
}

pragma[nomagic]
Expand Down Expand Up @@ -2878,7 +2888,7 @@ private module Input3 implements InputSig3 {
)
}

Type inferInvocationArgumentTypeContextual(Expr arg, TypePath path) {
Type inferInvocationArgumentTypeContextual(Argument arg, TypePath path) {
exists(Invocation invocation, FunctionPosition pos, TypePath path0 |
result =
inferInvocationArgumentTypeContextualDefault(invocation, pos.asPosition(), arg, _, _, path0) and
Expand Down
129 changes: 108 additions & 21 deletions shared/typeinference/codeql/typeinference/internal/TypeInference.qll
Original file line number Diff line number Diff line change
Expand Up @@ -2361,7 +2361,10 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}

/** A parameter. */
class Parameter extends VariableDeclaration;
class Parameter extends VariableDeclaration {
/** Gets the external name of this parameter, if any. */
string getExternalName();
}

/**
* Holds if `p` is an implicit parameter declaration corresponding
Expand Down Expand Up @@ -2439,6 +2442,15 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
string toString();
}

/** An argument to an invocation. */
class Argument extends AstNode {
/** Gets the value of this argument. */
Expr getValue();

/** Gets the name of this argument, if any. */
string getName();
}

/** An invocation expression, for example a call or a variant construction. */
class Invocation extends Expr {
/**
Expand Down Expand Up @@ -2481,7 +2493,10 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
*
* This should include the receiver argument for method calls.
*/
Expr getArgument(int i);
Argument getArgument(int i);

/** Holds if the `i`th argument of this invocation is implicit. */
predicate hasImplicitArgument(int i);

/**
* Gets the target of this invocation in the given resolution context.
Expand Down Expand Up @@ -2537,7 +2552,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
* When no post-processing is needed, simply implement this predicate as
* `result = inferInvocationArgumentTypeContextualDefault(_, _, _, arg, path)`.
*/
Type inferInvocationArgumentTypeContextual(Expr arg, TypePath path);
Type inferInvocationArgumentTypeContextual(Argument arg, TypePath path);

/**
* Gets the inferred type of `invocation`, found by propagating type information
Expand Down Expand Up @@ -2776,6 +2791,9 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
)
or
n1 = n2.(ParenExpr).getExpr()
or
n1 = n2.(Argument).getValue() and
n1 != n2
)
}

Expand Down Expand Up @@ -3132,19 +3150,76 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
private module InvocationMatchingInput implements MatchingWithEnvironmentInputSig {
import InvocationTypeQualifierMatchingInput

class DeclarationPosition = int;
private predicate parameterPos(Callable c, Parameter p, int pos, string name) {
p = c.getParameter(pos) and
(
name = p.getExternalName()
or
not exists(p.getExternalName()) and
name = ""
)
}

class AccessPosition = DeclarationPosition;
private newtype TDeclarationPosition =
TParameterPosition(int pos, string name) { parameterPos(_, _, pos, name) } or
TReturnTypePosition()

bindingset[apos]
bindingset[dpos]
predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) {
apos = dpos
class DeclarationPosition extends TDeclarationPosition {
predicate isParameterPosition(int pos, string name) {
this = TParameterPosition(pos, name)
}

predicate isReturnTypePosition() { this = TReturnTypePosition() }

string toString() {
exists(int pos, string name |
this.isParameterPosition(pos, name) and
result = "pos: " + pos + ", name: " + name
)
or
this.isReturnTypePosition() and
result = "return"
}
}

private newtype TAccessPosition =
TPositionalArgumentPosition(int pos) { parameterPos(_, _, pos, _) } or
TNamedArgumentPosition(string name) { parameterPos(_, _, _, name) and name != "" } or
TReturnValuePosition()

class AccessPosition extends TAccessPosition {
int asPosition() { this = TPositionalArgumentPosition(result) }

string asNamed() { this = TNamedArgumentPosition(result) }

predicate isReturn() { this = TReturnValuePosition() }

string toString() {
result = "pos: " + this.asPosition()
or
result = "name: " + this.asNamed()
or
this.isReturn() and
result = "return"
}
}

pragma[nomagic]
additional AccessPosition getArgumentPosition(Invocation i, Argument arg) {
exists(int pos | arg = i.getArgument(pos) |
result.asNamed() = arg.getName()
or
not exists(arg.getName()) and
result.asPosition() = pos
)
}

/** Gets the position used to represent the return type of an invocation. */
additional int getReturnPosition() {
result = min(int i | i = 0 or exists(any(Callable c).getParameter(i)) | i) - 1
predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) {
dpos = TParameterPosition(apos.asPosition(), _)
or
dpos = TParameterPosition(_, apos.asNamed())
or
dpos = TReturnTypePosition() and apos = TReturnValuePosition()
}

private int getFirstTypeParameterPosition() {
Expand Down Expand Up @@ -3172,9 +3247,13 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
}

Type getDeclaredType(DeclarationPosition dpos, TypePath path) {
result = this.getParameter(dpos).getType().getTypeAt(path)
exists(Parameter p, int pos, string name |
parameterPos(this, p, pos, name) and
dpos.isParameterPosition(pos, name) and
result = p.getType().getTypeAt(path)
)
or
dpos = getReturnPosition() and
dpos.isReturnTypePosition() and
result = getCallableReturnType(this, path)
}
}
Expand All @@ -3201,12 +3280,17 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
pragma[nomagic]
private Type getInferredReturnType(AccessPosition apos, TypePath path) {
result = inferInvocationTypeContextual(this, path) and
apos = getReturnPosition()
apos.isReturn()
}

bindingset[e]
Type getInferredType(AccessEnvironment e, AccessPosition apos, TypePath path) {
result = inferInvocationArgumentType(this, e, apos, path)
exists(int pos | result = inferInvocationArgumentType(this, e, pos, path) |
apos = getArgumentPosition(this, this.getArgument(pos))
or
this.hasImplicitArgument(pos) and
apos.asPosition() = pos
)
or
result = this.getInferredReturnType(apos, path) and
exists(e)
Expand All @@ -3227,9 +3311,10 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
Type inferInvocationTypeDefault(
Invocation invocation, InvocationResolutionContext ctx, TypePath path
) {
result =
InvocationMatching::inferAccessType(invocation, ctx,
InvocationMatchingInput::getReturnPosition(), path)
exists(InvocationMatchingInput::AccessPosition apos |
result = InvocationMatching::inferAccessType(invocation, ctx, apos, path) and
apos.isReturn()
)
}

/**
Expand All @@ -3238,10 +3323,12 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
*/
pragma[nomagic]
Type inferInvocationArgumentTypeContextualDefault(
Invocation invocation, InvocationResolutionContext ctx, int pos, Expr arg, TypePath path
Invocation invocation, InvocationResolutionContext ctx, int pos, Argument arg, TypePath path
) {
arg = invocation.getArgument(pos) and
result = InvocationMatching::inferAccessType(invocation, ctx, pos, path) and
result =
InvocationMatching::inferAccessType(invocation, ctx,
InvocationMatchingInput::getArgumentPosition(invocation, arg), path) and
// `inferTypeContextualCand2` performs the proper check for contextual
// typing, but we can already rule out cases where arguments don't have
// an unknown type anywhere
Expand Down
29 changes: 29 additions & 0 deletions unified/ql/lib/codeql/unified/internal/AstExtra.qll
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ module Public {
final class EnumCaseClass extends ClassLikeDeclaration {
EnumCaseClass() { this.hasModifier("enum_case") }

/** Gets the enum declaration that this case class belongs to. */
ClassLikeDeclaration getEnumClass() { this = result.getAMember() }

/** Gets the constructor of this algebraic data type. */
EnumCaseConstructor getConstructor() { result = this.getAMember() }
}
Expand Down Expand Up @@ -168,5 +171,31 @@ module Public {

/** Gets the class containing this constructor. */
EnumCaseClass getEnumCaseClass() { result.getAMember() = this }

/** Gets the enum declaration that this constructor belongs to. */
ClassLikeDeclaration getEnumClass() { result = this.getEnumCaseClass().getEnumClass() }
}

/**
* A field of an enum case in an algebraic data type. For example `a` and
* `b` in
*
* ```swift
* enum E {
* case a
* case b
* }
* ```
*/
final class EnumField extends VariableDeclaration {
private ClassLikeDeclaration enum;

EnumField() {
this = enum.getAMember() and
this.hasModifier("enum_case")
}

/** Gets the enum declaration that this enum field belongs to. */
ClassLikeDeclaration getEnumClass() { result = enum }
}
}
22 changes: 18 additions & 4 deletions unified/ql/lib/codeql/unified/internal/ExprPositions.qll
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ private import NameBinding as NameBinding
private import ExprPositionsPlugin

/**
* Holds if `expr` appears in a context where it must refer to a type.
* Holds if `expr` appears in a context where it must refer to a type,
* based entirely on the syntactic context of the expression.
*/
predicate isInTypeContext(Expr expr) {
predicate isInSyntacticTypeContext(Expr expr) {
expr = any(TypeCastExpr n).getType()
or
expr = any(TypeTestExpr n).getType()
Expand Down Expand Up @@ -36,11 +37,24 @@ predicate isInTypeContext(Expr expr) {
or
expr.getParent() instanceof TypeConstraint
or
any(ExprPositionsPlugin p).isInSyntacticTypeContext(expr)
or
isInSyntacticTypeContext(expr.getEnclosingExpr())
}

private predicate isInTypeContext0(Expr expr) {
exists(NameBinding::getTypeFromRef(expr))
or
any(ExprPositionsPlugin p).isInTypeContext(expr)
isInTypeContext0(expr.getEnclosingExpr())
}

/**
* Holds if `expr` appears in a context where it must refer to a type.
*/
predicate isInTypeContext(Expr expr) {
isInSyntacticTypeContext(expr)
or
isInTypeContext(expr.getEnclosingExpr())
isInTypeContext0(expr)
}

/** Holds if `e` appears in a name-binding position inside `declaration` */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ private module Plugins {
}

class ExprPositionsPlugin extends Unit {
predicate isInTypeContext(Expr e) { none() }
predicate isInSyntacticTypeContext(Expr e) { none() }

predicate isReturnedExpr(Callable callable, Expr e) { none() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ private import unified
private import ExprPositionsPlugin

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

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