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 @@ -2487,6 +2487,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 @@ -2638,14 +2640,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 @@ -2674,7 +2684,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 @@ -2722,7 +2732,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 @@ -2760,7 +2770,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 @@ -2778,7 +2788,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 @@ -2789,7 +2799,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 @@ -2874,7 +2884,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 @@ -2356,7 +2356,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 @@ -2434,6 +2437,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 @@ -2476,7 +2488,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 @@ -2532,7 +2547,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 @@ -2769,6 +2784,9 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
n1 = decl.getInitializer() and
n2 = decl.getPattern()
)
or
n1 = n2.(Argument).getValue() and
n1 != n2
)
}

Expand Down Expand Up @@ -3125,19 +3143,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 @@ -3165,9 +3240,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 @@ -3194,12 +3273,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 @@ -3220,9 +3304,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 @@ -3231,10 +3316,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
9 changes: 9 additions & 0 deletions unified/ql/lib/codeql/unified/internal/ParameterEx.qll
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ abstract private class ParameterExImpl extends TParameterEx {
this.isDefaultConstructorParameter(result, _, _)
}

/** Gets the external name of this parameter, if any. */
abstract string getExternalName();

/** Holds if this parameter has a default value. */
abstract predicate hasDefault();

Expand All @@ -78,6 +81,8 @@ private class AstParameterEx extends ParameterExImpl, TAstParameter {

AstParameterEx() { this = TAstParameter(p) }

override string getExternalName() { result = p.getExternalName() }

override predicate hasDefault() { exists(p.getDefault()) }

override AstNode getType() { result = p.getType() }
Expand All @@ -92,6 +97,8 @@ private class ImplicitReceiverParameterEx extends ParameterExImpl, TImplicitRece

ImplicitReceiverParameterEx() { this = TImplicitReceiverParameter(c) }

override string getExternalName() { none() }

override predicate hasDefault() { none() }

override AstNode getType() { none() }
Expand All @@ -116,6 +123,8 @@ private class DefaultConstructorParameterEx extends ParameterExImpl, TDefaultCon

override AstNode getType() { defaultConstructorParameterType(cd, i, result) }

override string getExternalName() { result = name }

override predicate hasDefault() { hasDefault = true }

override string toString() { result = name + " [" + cd.getName() + " default constructor]" }
Expand Down
Loading
Loading