mirror of
https://github.com/github/codeql.git
synced 2026-04-11 18:14:01 +02:00
Rust: Infer argument types based on trait bounds on parameters
This commit is contained in:
@@ -134,7 +134,7 @@ module SatisfiesBlanketConstraint<
|
||||
exists(ArgumentTypeAndBlanketOffset ato, Trait traitBound |
|
||||
ato = MkArgumentTypeAndBlanketOffset(at, _) and
|
||||
SatisfiesBlanketConstraintInput::relevantConstraint(ato, impl, traitBound) and
|
||||
SatisfiesBlanketConstraint::satisfiesConstraintType(ato, TTrait(traitBound), _, _)
|
||||
SatisfiesBlanketConstraint::satisfiesConstraint(ato, TTrait(traitBound), _, _)
|
||||
)
|
||||
or
|
||||
exists(TypeParam blanketTypeParam |
|
||||
|
||||
@@ -467,6 +467,41 @@ private predicate isPanicMacroCall(MacroExpr me) {
|
||||
me.getMacroCall().resolveMacro().(MacroRules).getName().getText() = "panic"
|
||||
}
|
||||
|
||||
// Due to "binding modes" the type of the pattern is not necessarily the
|
||||
// same as the type of the initializer. However, when the pattern is an
|
||||
// identifier pattern, its type is guaranteed to be the same as the type of the
|
||||
// initializer.
|
||||
private predicate identLetStmt(LetStmt let, IdentPat lhs, Expr rhs) {
|
||||
let.getPat() = lhs and
|
||||
let.getInitializer() = rhs
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the root type of a closure.
|
||||
*
|
||||
* We model closures as `dyn Fn` trait object types. A closure might implement
|
||||
* only `Fn`, `FnMut`, or `FnOnce`. But since `Fn` is a subtrait of the others,
|
||||
* giving closures the type `dyn Fn` works well in practice -- even if not
|
||||
* entirely accurate.
|
||||
*/
|
||||
private DynTraitType closureRootType() {
|
||||
result = TDynTraitType(any(FnTrait t)) // always exists because of the mention in `builtins/mentions.rs`
|
||||
}
|
||||
|
||||
/** Gets the path to a closure's return type. */
|
||||
private TypePath closureReturnPath() {
|
||||
result =
|
||||
TypePath::singleton(TDynTraitTypeParameter(any(FnTrait t), any(FnOnceTrait t).getOutputType()))
|
||||
}
|
||||
|
||||
/** Gets the path to a closure's `index`th parameter type, where the arity is `arity`. */
|
||||
pragma[nomagic]
|
||||
private TypePath closureParameterPath(int arity, int index) {
|
||||
result =
|
||||
TypePath::cons(TDynTraitTypeParameter(_, any(FnTrait t).getTypeParam()),
|
||||
TypePath::singleton(getTupleTypeParameter(arity, index)))
|
||||
}
|
||||
|
||||
/** Module for inferring certain type information. */
|
||||
module CertainTypeInference {
|
||||
pragma[nomagic]
|
||||
@@ -544,11 +579,7 @@ module CertainTypeInference {
|
||||
// is not a certain type equality.
|
||||
exists(LetStmt let |
|
||||
not let.hasTypeRepr() and
|
||||
// Due to "binding modes" the type of the pattern is not necessarily the
|
||||
// same as the type of the initializer. The pattern being an identifier
|
||||
// pattern is sufficient to ensure that this is not the case.
|
||||
let.getPat().(IdentPat) = n1 and
|
||||
let.getInitializer() = n2
|
||||
identLetStmt(let, n1, n2)
|
||||
)
|
||||
or
|
||||
exists(LetExpr let |
|
||||
@@ -572,6 +603,21 @@ module CertainTypeInference {
|
||||
)
|
||||
else prefix2.isEmpty()
|
||||
)
|
||||
or
|
||||
exists(CallExprImpl::DynamicCallExpr dce, TupleType tt, int i |
|
||||
n1 = dce.getArgList() and
|
||||
tt.getArity() = dce.getNumberOfSyntacticArguments() and
|
||||
n2 = dce.getSyntacticPositionalArgument(i) and
|
||||
prefix1 = TypePath::singleton(tt.getPositionalTypeParameter(i)) and
|
||||
prefix2.isEmpty()
|
||||
)
|
||||
or
|
||||
exists(ClosureExpr ce, int index |
|
||||
n1 = ce and
|
||||
n2 = ce.getParam(index).getPat() and
|
||||
prefix1 = closureParameterPath(ce.getNumberOfParams(), index) and
|
||||
prefix2.isEmpty()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -636,6 +682,10 @@ module CertainTypeInference {
|
||||
path.isEmpty() and
|
||||
result instanceof NeverType
|
||||
or
|
||||
n instanceof ClosureExpr and
|
||||
path.isEmpty() and
|
||||
result = closureRootType()
|
||||
or
|
||||
infersCertainTypeAt(n, path, result.getATypeParameter())
|
||||
}
|
||||
|
||||
@@ -835,17 +885,6 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
|
||||
n1.(ArrayRepeatExpr).getRepeatOperand() = n2 and
|
||||
prefix1 = TypePath::singleton(getArrayTypeParameter()) and
|
||||
prefix2.isEmpty()
|
||||
or
|
||||
exists(ClosureExpr ce, int index |
|
||||
n1 = ce and
|
||||
n2 = ce.getParam(index).getPat() and
|
||||
prefix1 = closureParameterPath(ce.getNumberOfParams(), index) and
|
||||
prefix2.isEmpty()
|
||||
)
|
||||
or
|
||||
n1.(ClosureExpr).getClosureBody() = n2 and
|
||||
prefix1 = closureReturnPath() and
|
||||
prefix2.isEmpty()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -881,6 +920,9 @@ private predicate lubCoercion(AstNode parent, AstNode child, TypePath prefix) {
|
||||
strictcount(Expr e | bodyReturns(parent, e)) > 1 and
|
||||
prefix.isEmpty()
|
||||
or
|
||||
parent = any(ClosureExpr ce | not ce.hasRetType() and ce.getClosureBody() = child) and
|
||||
prefix = closureReturnPath()
|
||||
or
|
||||
exists(Struct s |
|
||||
child = [parent.(RangeExpr).getStart(), parent.(RangeExpr).getEnd()] and
|
||||
prefix = TypePath::singleton(TTypeParamTypeParameter(s.getGenericParamList().getATypeParam())) and
|
||||
@@ -888,6 +930,19 @@ private predicate lubCoercion(AstNode parent, AstNode child, TypePath prefix) {
|
||||
)
|
||||
}
|
||||
|
||||
private Type inferUnknownTypeFromAnnotation(AstNode n, TypePath path) {
|
||||
inferType(n, path) = TUnknownType() and
|
||||
// Normally, these are coercion sites, but in case a type is unknown we
|
||||
// allow for type information to flow from the type annotation.
|
||||
exists(TypeMention tm | result = tm.getTypeAt(path) |
|
||||
tm = any(LetStmt let | identLetStmt(let, _, n)).getTypeRepr()
|
||||
or
|
||||
tm = any(ClosureExpr ce | n = ce.getBody()).getRetType().getTypeRepr()
|
||||
or
|
||||
tm = getReturnTypeMention(any(Function f | n = f.getBody()))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type tree of `n1` at `prefix1` should be equal to the type tree
|
||||
* of `n2` at `prefix2`, but type information should only propagate from `n1` to
|
||||
@@ -1545,12 +1600,14 @@ private module AssocFunctionResolution {
|
||||
*
|
||||
* This is either:
|
||||
*
|
||||
* 1. `AssocFunctionCallMethodCallExpr`: a method call, `x.m()`;
|
||||
* 2. `AssocFunctionCallIndexExpr`: an index expression, `x[i]`, which is [syntactic sugar][1]
|
||||
* 1. `MethodCallExprAssocFunctionCall`: a method call, `x.m()`;
|
||||
* 2. `IndexExprAssocFunctionCall`: an index expression, `x[i]`, which is [syntactic sugar][1]
|
||||
* for `*x.index(i)`;
|
||||
* 3. `AssocFunctionCallCallExpr`: a qualified function call, `Q::f(x)`; or
|
||||
* 4. `AssocFunctionCallOperation`: an operation expression, `x + y`, which is syntactic sugar
|
||||
* 3. `CallExprAssocFunctionCall`: a qualified function call, `Q::f(x)`; or
|
||||
* 4. `OperationAssocFunctionCall`: an operation expression, `x + y`, which is syntactic sugar
|
||||
* for `Add::add(x, y)`.
|
||||
* 5. `DynamicAssocFunctionCall`: a call to a closure, `c(x)`, which is syntactic sugar for
|
||||
* `c.call_once(x)`, `c.call_mut(x)`, or `c.call(x)`.
|
||||
*
|
||||
* Note that only in case 1 and 2 is auto-dereferencing and borrowing allowed.
|
||||
*
|
||||
@@ -1567,7 +1624,7 @@ private module AssocFunctionResolution {
|
||||
pragma[nomagic]
|
||||
abstract predicate hasNameAndArity(string name, int arity);
|
||||
|
||||
abstract Expr getNonReturnNodeAt(FunctionPosition pos);
|
||||
abstract AstNode getNonReturnNodeAt(FunctionPosition pos);
|
||||
|
||||
AstNode getNodeAt(FunctionPosition pos) {
|
||||
result = this.getNonReturnNodeAt(pos)
|
||||
@@ -2101,7 +2158,7 @@ private module AssocFunctionResolution {
|
||||
}
|
||||
}
|
||||
|
||||
private class AssocFunctionCallMethodCallExpr extends AssocFunctionCall instanceof MethodCallExpr {
|
||||
private class MethodCallExprAssocFunctionCall extends AssocFunctionCall instanceof MethodCallExpr {
|
||||
override predicate hasNameAndArity(string name, int arity) {
|
||||
name = super.getIdentifier().getText() and
|
||||
arity = super.getNumberOfSyntacticArguments()
|
||||
@@ -2121,7 +2178,7 @@ private module AssocFunctionResolution {
|
||||
override Trait getTrait() { none() }
|
||||
}
|
||||
|
||||
private class AssocFunctionCallIndexExpr extends AssocFunctionCall, IndexExpr {
|
||||
private class IndexExprAssocFunctionCall extends AssocFunctionCall, IndexExpr {
|
||||
private predicate isInMutableContext() {
|
||||
// todo: does not handle all cases yet
|
||||
VariableImpl::assignmentOperationDescendant(_, this)
|
||||
@@ -2151,8 +2208,8 @@ private module AssocFunctionResolution {
|
||||
}
|
||||
}
|
||||
|
||||
private class AssocFunctionCallCallExpr extends AssocFunctionCall, CallExpr {
|
||||
AssocFunctionCallCallExpr() {
|
||||
private class CallExprAssocFunctionCall extends AssocFunctionCall, CallExpr {
|
||||
CallExprAssocFunctionCall() {
|
||||
exists(getCallExprPathQualifier(this)) and
|
||||
// even if a target cannot be resolved by path resolution, it may still
|
||||
// be possible to resolve a blanket implementation (so not `forex`)
|
||||
@@ -2184,7 +2241,7 @@ private module AssocFunctionResolution {
|
||||
override Trait getTrait() { result = getCallExprTraitQualifier(this) }
|
||||
}
|
||||
|
||||
final class AssocFunctionCallOperation extends AssocFunctionCall, Operation {
|
||||
final class OperationAssocFunctionCall extends AssocFunctionCall, Operation {
|
||||
override predicate hasNameAndArity(string name, int arity) {
|
||||
this.isOverloaded(_, name, _) and
|
||||
arity = this.getNumberOfOperands()
|
||||
@@ -2242,6 +2299,29 @@ private module AssocFunctionResolution {
|
||||
override Trait getTrait() { this.isOverloaded(result, _, _) }
|
||||
}
|
||||
|
||||
private class DynamicAssocFunctionCall extends AssocFunctionCall instanceof CallExprImpl::DynamicCallExpr
|
||||
{
|
||||
pragma[nomagic]
|
||||
override predicate hasNameAndArity(string name, int arity) {
|
||||
name = "call_once" and // todo: handle call_mut and call
|
||||
arity = 2 // args are passed in a tuple
|
||||
}
|
||||
|
||||
override predicate hasReceiver() { any() }
|
||||
|
||||
override AstNode getNonReturnNodeAt(FunctionPosition pos) {
|
||||
pos.asPosition() = 0 and
|
||||
result = super.getFunction()
|
||||
or
|
||||
pos.asPosition() = 1 and
|
||||
result = super.getArgList()
|
||||
}
|
||||
|
||||
override predicate supportsAutoDerefAndBorrow() { any() }
|
||||
|
||||
override Trait getTrait() { result instanceof AnyFnTrait }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private AssocFunctionDeclaration getAssocFunctionSuccessor(
|
||||
ImplOrTraitItemNode i, string name, int arity
|
||||
@@ -2445,7 +2525,7 @@ private module AssocFunctionResolution {
|
||||
) {
|
||||
exists(CallDerefCand cdc, TypePath exprPath |
|
||||
cdc = MkCallDerefCand(afc, selfPos, derefChain) and
|
||||
CallSatisfiesDerefConstraint::satisfiesConstraintTypeThrough(cdc, impl, _, exprPath, result) and
|
||||
CallSatisfiesDerefConstraint::satisfiesConstraintThrough(cdc, impl, _, exprPath, result) and
|
||||
exprPath.isCons(getDerefTargetTypeParameter(), path)
|
||||
)
|
||||
}
|
||||
@@ -3198,7 +3278,7 @@ private module OperationMatchingInput implements MatchingInputSig {
|
||||
}
|
||||
}
|
||||
|
||||
class Access extends AssocFunctionResolution::AssocFunctionCallOperation {
|
||||
class Access extends AssocFunctionResolution::OperationAssocFunctionCall {
|
||||
Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -3566,7 +3646,7 @@ private module AwaitSatisfiesType = SatisfiesType<AwaitTarget, AwaitSatisfiesTyp
|
||||
pragma[nomagic]
|
||||
private Type inferAwaitExprType(AstNode n, TypePath path) {
|
||||
exists(TypePath exprPath |
|
||||
AwaitSatisfiesType::satisfiesConstraintType(n.(AwaitExpr).getExpr(), _, exprPath, result) and
|
||||
AwaitSatisfiesType::satisfiesConstraint(n.(AwaitExpr).getExpr(), _, exprPath, result) and
|
||||
exprPath.isCons(getFutureOutputTypeParameter(), path)
|
||||
)
|
||||
}
|
||||
@@ -3733,7 +3813,7 @@ private Type inferForLoopExprType(AstNode n, TypePath path) {
|
||||
// type of iterable -> type of pattern (loop variable)
|
||||
exists(ForExpr fe, TypePath exprPath, AssociatedTypeTypeParameter tp |
|
||||
n = fe.getPat() and
|
||||
ForIterableSatisfiesType::satisfiesConstraintType(fe.getIterable(), _, exprPath, result) and
|
||||
ForIterableSatisfiesType::satisfiesConstraint(fe.getIterable(), _, exprPath, result) and
|
||||
exprPath.isCons(tp, path)
|
||||
|
|
||||
tp = getIntoIteratorItemTypeParameter()
|
||||
@@ -3744,130 +3824,36 @@ private Type inferForLoopExprType(AstNode n, TypePath path) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An invoked expression, the target of a call that is either a local variable
|
||||
* or a non-path expression. This means that the expression denotes a
|
||||
* first-class function.
|
||||
*/
|
||||
final private class InvokedClosureExpr extends Expr {
|
||||
private CallExprImpl::DynamicCallExpr call;
|
||||
|
||||
InvokedClosureExpr() { call.getFunction() = this }
|
||||
|
||||
Type getTypeAt(TypePath path) { result = inferType(this, path) }
|
||||
|
||||
CallExpr getCall() { result = call }
|
||||
}
|
||||
|
||||
private module InvokedClosureSatisfiesTypeInput implements SatisfiesTypeInputSig<InvokedClosureExpr>
|
||||
{
|
||||
predicate relevantConstraint(InvokedClosureExpr term, Type constraint) {
|
||||
exists(term) and
|
||||
constraint.(TraitType).getTrait() instanceof FnOnceTrait
|
||||
}
|
||||
}
|
||||
|
||||
private module InvokedClosureSatisfiesType =
|
||||
SatisfiesType<InvokedClosureExpr, InvokedClosureSatisfiesTypeInput>;
|
||||
|
||||
/** Gets the type of `ce` when viewed as an implementation of `FnOnce`. */
|
||||
private Type invokedClosureFnTypeAt(InvokedClosureExpr ce, TypePath path) {
|
||||
InvokedClosureSatisfiesType::satisfiesConstraintType(ce, _, path, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the root type of a closure.
|
||||
*
|
||||
* We model closures as `dyn Fn` trait object types. A closure might implement
|
||||
* only `Fn`, `FnMut`, or `FnOnce`. But since `Fn` is a subtrait of the others,
|
||||
* giving closures the type `dyn Fn` works well in practice -- even if not
|
||||
* entirely accurate.
|
||||
*/
|
||||
private DynTraitType closureRootType() {
|
||||
result = TDynTraitType(any(FnTrait t)) // always exists because of the mention in `builtins/mentions.rs`
|
||||
}
|
||||
|
||||
/** Gets the path to a closure's return type. */
|
||||
private TypePath closureReturnPath() {
|
||||
result =
|
||||
TypePath::singleton(TDynTraitTypeParameter(any(FnTrait t), any(FnOnceTrait t).getOutputType()))
|
||||
}
|
||||
|
||||
/** Gets the path to a closure with arity `arity`'s `index`th parameter type. */
|
||||
pragma[nomagic]
|
||||
private TypePath closureParameterPath(int arity, int index) {
|
||||
result =
|
||||
TypePath::cons(TDynTraitTypeParameter(_, any(FnTrait t).getTypeParam()),
|
||||
TypePath::singleton(getTupleTypeParameter(arity, index)))
|
||||
}
|
||||
|
||||
/** Gets the path to the return type of the `FnOnce` trait. */
|
||||
private TypePath fnReturnPath() {
|
||||
result = TypePath::singleton(getAssociatedTypeTypeParameter(any(FnOnceTrait t).getOutputType()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path to the parameter type of the `FnOnce` trait with arity `arity`
|
||||
* and index `index`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private TypePath fnParameterPath(int arity, int index) {
|
||||
result =
|
||||
TypePath::cons(TTypeParamTypeParameter(any(FnOnceTrait t).getTypeParam()),
|
||||
TypePath::singleton(getTupleTypeParameter(arity, index)))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Type inferDynamicCallExprType(Expr n, TypePath path) {
|
||||
exists(InvokedClosureExpr ce |
|
||||
// Propagate the function's return type to the call expression
|
||||
exists(TypePath path0 | result = invokedClosureFnTypeAt(ce, path0) |
|
||||
n = ce.getCall() and
|
||||
path = path0.stripPrefix(fnReturnPath())
|
||||
private Type inferClosureExprType(AstNode n, TypePath path) {
|
||||
exists(ClosureExpr ce |
|
||||
n = ce and
|
||||
(
|
||||
path = TypePath::singleton(TDynTraitTypeParameter(_, any(FnTrait t).getTypeParam())) and
|
||||
result.(TupleType).getArity() = ce.getNumberOfParams()
|
||||
or
|
||||
// Propagate the function's parameter type to the arguments
|
||||
exists(int index |
|
||||
n = ce.getCall().getSyntacticPositionalArgument(index) and
|
||||
path =
|
||||
path0.stripPrefix(fnParameterPath(ce.getCall().getArgList().getNumberOfArgs(), index))
|
||||
exists(TypePath path0 |
|
||||
result = ce.getRetType().getTypeRepr().(TypeMention).getTypeAt(path0) and
|
||||
path = closureReturnPath().append(path0)
|
||||
)
|
||||
)
|
||||
or
|
||||
// _If_ the invoked expression has the type of a closure, then we propagate
|
||||
// the surrounding types into the closure.
|
||||
exists(int arity, TypePath path0 | ce.getTypeAt(TypePath::nil()) = closureRootType() |
|
||||
// Propagate the type of arguments to the parameter types of closure
|
||||
exists(int index, ArgList args |
|
||||
n = ce and
|
||||
args = ce.getCall().getArgList() and
|
||||
arity = args.getNumberOfArgs() and
|
||||
result = inferType(args.getArg(index), path0) and
|
||||
path = closureParameterPath(arity, index).append(path0)
|
||||
)
|
||||
or
|
||||
// Propagate the type of the call expression to the return type of the closure
|
||||
n = ce and
|
||||
arity = ce.getCall().getArgList().getNumberOfArgs() and
|
||||
result = inferType(ce.getCall(), path0) and
|
||||
path = closureReturnPath().append(path0)
|
||||
exists(Param p |
|
||||
p = ce.getAParam() and
|
||||
not p.hasTypeRepr() and
|
||||
n = p.getPat() and
|
||||
result = TUnknownType() and
|
||||
path.isEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Type inferClosureExprType(AstNode n, TypePath path) {
|
||||
exists(ClosureExpr ce |
|
||||
n = ce and
|
||||
path.isEmpty() and
|
||||
result = closureRootType()
|
||||
or
|
||||
n = ce and
|
||||
path = TypePath::singleton(TDynTraitTypeParameter(_, any(FnTrait t).getTypeParam())) and
|
||||
result.(TupleType).getArity() = ce.getNumberOfParams()
|
||||
or
|
||||
// Propagate return type annotation to body
|
||||
n = ce.getClosureBody() and
|
||||
result = ce.getRetType().getTypeRepr().(TypeMention).getTypeAt(path)
|
||||
private TupleType inferArgList(ArgList args, TypePath path) {
|
||||
exists(CallExprImpl::DynamicCallExpr dce |
|
||||
args = dce.getArgList() and
|
||||
result.getArity() = dce.getNumberOfSyntacticArguments() and
|
||||
path.isEmpty()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3915,7 +3901,8 @@ private module Cached {
|
||||
or
|
||||
i instanceof ImplItemNode and dispatch = false
|
||||
|
|
||||
result = call.(AssocFunctionResolution::AssocFunctionCall).resolveCallTarget(i, _, _, _)
|
||||
result = call.(AssocFunctionResolution::AssocFunctionCall).resolveCallTarget(i, _, _, _) and
|
||||
not call instanceof CallExprImpl::DynamicCallExpr
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4023,11 +4010,13 @@ private module Cached {
|
||||
or
|
||||
result = inferForLoopExprType(n, path)
|
||||
or
|
||||
result = inferDynamicCallExprType(n, path)
|
||||
or
|
||||
result = inferClosureExprType(n, path)
|
||||
or
|
||||
result = inferArgList(n, path)
|
||||
or
|
||||
result = inferDeconstructionPatType(n, path)
|
||||
or
|
||||
result = inferUnknownTypeFromAnnotation(n, path)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,7 +769,7 @@ private Type getPathConcreteAssocTypeAt(Path path, TypePath typePath) {
|
||||
TypeAlias alias, TypePath path0
|
||||
|
|
||||
pathConcreteTypeAssocType(path, tm, trait, traitOrTmTrait, alias) and
|
||||
PathSatisfiesConstraint::satisfiesConstraintTypeThrough(tm, impl, traitOrTmTrait, path0, result) and
|
||||
PathSatisfiesConstraint::satisfiesConstraintThrough(tm, impl, traitOrTmTrait, path0, result) and
|
||||
path0.isCons(TAssociatedTypeTypeParameter(trait, alias), typePath)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ mod fn_once_trait {
|
||||
};
|
||||
let _r = apply(f, true); // $ target=apply type=_r:i64
|
||||
|
||||
let f = |x| x + 1; // $ MISSING: type=x:i64 target=add
|
||||
let f = |x| x + 1; // $ type=x:i64 $ MISSING: target=add
|
||||
let _r2 = apply_two(f); // $ target=apply_two certainType=_r2:i64
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ mod fn_mut_trait {
|
||||
};
|
||||
let _r = apply(f, true); // $ target=apply type=_r:i64
|
||||
|
||||
let f = |x| x + 1; // $ MISSING: type=x:i64 target=add
|
||||
let f = |x| x + 1; // $ type=x:i64 $ MISSING: target=add
|
||||
let _r2 = apply_two(f); // $ target=apply_two certainType=_r2:i64
|
||||
}
|
||||
}
|
||||
@@ -137,7 +137,7 @@ mod fn_trait {
|
||||
};
|
||||
let _r = apply(f, true); // $ target=apply type=_r:i64
|
||||
|
||||
let f = |x| x + 1; // $ MISSING: type=x:i64 target=add
|
||||
let f = |x| x + 1; // $ type=x:i64 $ MISSING: target=add
|
||||
let _r2 = apply_two(f); // $ target=apply_two certainType=_r2:i64
|
||||
}
|
||||
}
|
||||
@@ -183,25 +183,25 @@ mod closure_infer_param {
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let f = |x| x; // $ MISSING: type=x:i64
|
||||
let f = |x| x; // $ type=x:i64
|
||||
let _r = apply1(f, 1i64); // $ target=apply1
|
||||
|
||||
let f = |x| x; // $ MISSING: type=x:i64
|
||||
let f = |x| x; // $ type=x:i64
|
||||
let _r = apply2(f, 2i64); // $ target=apply2
|
||||
|
||||
let f = |x| x; // $ MISSING: type=x:i64
|
||||
let f = |x| x; // $ type=x:i64
|
||||
let _r = apply3(&f, 3i64); // $ target=apply3
|
||||
|
||||
let f = |x| x; // $ MISSING: type=x:i64
|
||||
let f = |x| x; // $ type=x:i64
|
||||
let _r = apply4(f, 4i64); // $ target=apply4
|
||||
|
||||
let mut f = |x| x; // $ MISSING: type=x:i64
|
||||
let _r = apply5(&mut f, 5i64); // $ target=apply5
|
||||
|
||||
let f = |x| x; // $ MISSING: type=x:i64
|
||||
let f = |x| x; // $ type=x:i64
|
||||
let _r = apply6(f, 6i64); // $ target=apply6
|
||||
|
||||
let f = |x| x; // $ MISSING: type=x:i64
|
||||
let f = |x| x; // $ type=x:i64
|
||||
let _r = apply7(f, 7i64); // $ target=apply7
|
||||
}
|
||||
}
|
||||
@@ -221,15 +221,15 @@ mod implicit_deref {
|
||||
|
||||
pub fn test() {
|
||||
let x = 0i64;
|
||||
let v = Default::default(); // $ MISSING: type=v:i64 target=default
|
||||
let v = Default::default(); // $ type=v:i64 target=default
|
||||
let s = S(v);
|
||||
let _ret = s(x); // $ MISSING: type=_ret:bool
|
||||
let _ret = s(x); // $ type=_ret:bool
|
||||
|
||||
let x = 0i32;
|
||||
let v = Default::default(); // $ MISSING: type=v:i32 target=default
|
||||
let v = Default::default(); // $ type=v:i32 target=default
|
||||
let s = S(v);
|
||||
let s_ref = &s;
|
||||
let _ret = s_ref(x); // $ MISSING: type=_ret:bool
|
||||
let _ret = s_ref(x); // $ type=_ret:bool
|
||||
|
||||
// The call below is not an implicit deref, instead it will target
|
||||
// `impl<A, F> FnOnce<A> for &F` from
|
||||
|
||||
@@ -2259,7 +2259,7 @@ mod loops {
|
||||
// for loops with arrays
|
||||
|
||||
for i in [1, 2, 3] {} // $ type=i:i32
|
||||
for i in [1, 2, 3].map(|x| x + 1) {} // $ target=map MISSING: type=i:i32
|
||||
for i in [1, 2, 3].map(|x| x + 1) {} // $ target=map target=add type=i:i32
|
||||
for i in [1, 2, 3].into_iter() {} // $ target=into_iter type=i:i32
|
||||
|
||||
let vals1 = [1u8, 2, 3]; // $ type=vals1:TArray.u8
|
||||
@@ -2777,7 +2777,7 @@ mod arg_trait_bounds {
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let v = Default::default(); // $ MISSING: type=v:i64 target=default
|
||||
let v = Default::default(); // $ type=v:i64 target=default
|
||||
let g = Gen(v);
|
||||
let _ = my_get(&g); // $ target=my_get
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ mod regression5 {
|
||||
|
||||
fn foo() -> S2<S1> {
|
||||
let x = S1.into(); // $ target=into
|
||||
x
|
||||
x // $ SPURIOUS: type=x:T2.TRef.S1 -- happens because we currently do not consider the two `impl` blocks to be siblings
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -149,6 +149,8 @@ impl<T> MyOption<T> {
|
||||
|
||||
// summary=<test::option::MyOption>::map;Argument[0].ReturnValue;ReturnValue.Field[test::option::MyOption::MySome(0)];value;dfc-generated
|
||||
// summary=<test::option::MyOption>::map;Argument[self].Field[test::option::MyOption::MySome(0)];Argument[0].Parameter[0];value;dfc-generated
|
||||
// The spurious model below happens because `f` incorrectly resolves to the closure passed in from `as_deref`
|
||||
// SPURIOUS-summary=<test::option::MyOption>::map;Argument[self].Field[test::option::MyOption::MySome(0)].Reference;ReturnValue.Field[test::option::MyOption::MySome(0)].Reference;taint;dfc-generated
|
||||
pub fn map<U, F>(self, f: F) -> MyOption<U>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
@@ -217,7 +219,7 @@ impl<T> MyOption<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// MISSING: `Deref` trait
|
||||
// summary=<test::option::MyOption>::as_deref;Argument[self].Reference.Field[test::option::MyOption::MySome(0)];ReturnValue.Field[test::option::MyOption::MySome(0)].Reference;taint;dfc-generated
|
||||
pub fn as_deref(&self) -> MyOption<&T::Target>
|
||||
where
|
||||
T: Deref,
|
||||
|
||||
@@ -964,7 +964,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
/**
|
||||
* Holds if `term` does not satisfy `constraint`.
|
||||
*
|
||||
* This predicate is an approximation of `not hasConstraintMention(term, constraint)`.
|
||||
* This predicate is an approximation of `not hasConstraintMention(term, _, _, constraint, _, _)`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate hasNotConstraintMention(
|
||||
@@ -1072,7 +1072,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate satisfiesConstraintTypeMention1(
|
||||
private predicate satisfiesConstraint0(
|
||||
Term term, Constraint constraint, TypeAbstraction abs, TypeMention sub, TypePath path,
|
||||
Type t
|
||||
) {
|
||||
@@ -1100,37 +1100,55 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate satisfiesConstraintTypeMentionInline(
|
||||
Term term, Constraint constraint, TypeAbstraction abs, TypePath path,
|
||||
TypePath pathToTypeParamInSub
|
||||
private predicate satisfiesConstraintInline(
|
||||
Term term, Constraint constraint, TypeAbstraction abs, TypePath pathToTypeParamInConstraint,
|
||||
TypePath pathToTypeParamInSub, TypeParameter tp
|
||||
) {
|
||||
exists(TypeMention sub, TypeParameter tp |
|
||||
satisfiesConstraintTypeMention1(term, constraint, abs, sub, path, tp) and
|
||||
exists(TypeMention sub |
|
||||
satisfiesConstraint0(term, constraint, abs, sub, pathToTypeParamInConstraint, tp) and
|
||||
tp = abs.getATypeParameter() and
|
||||
sub.getTypeAt(pathToTypeParamInSub) = tp
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `term` satisfies the constraint `constraint` with _some_ type
|
||||
* parameter at `pathToTypeParamInConstraint`, and the type parameter occurs
|
||||
* at `pathToTypeParamInSub` in a satisfying condition.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```rust
|
||||
* struct MyThing<A> { ... }
|
||||
*
|
||||
* trait MyTrait<B> { ... }
|
||||
*
|
||||
* impl<T> MyTrait<T> for MyThing<T> { ... }
|
||||
*
|
||||
* fn foo<T: MyTrait<i32>>(x: T) { ... }
|
||||
*
|
||||
* let x = MyThing(Default::default());
|
||||
* foo(x);
|
||||
* ```
|
||||
*
|
||||
* At `term` = `foo(x)`, we have `constraint = MyTrait<i32>`, and because of the
|
||||
* `impl` block, `pathToTypeParamInConstraint` = `"B"`, and
|
||||
* `pathToTypeParamInSub` = `"A"`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate satisfiesConstraintTypeMention(
|
||||
Term term, Constraint constraint, TypePath path, TypePath pathToTypeParamInSub
|
||||
) {
|
||||
satisfiesConstraintTypeMentionInline(term, constraint, _, path, pathToTypeParamInSub)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate satisfiesConstraintTypeMentionThrough(
|
||||
Term term, Constraint constraint, TypeAbstraction abs, TypePath path,
|
||||
predicate satisfiesConstraintAtTypeParameter(
|
||||
Term term, Constraint constraint, TypePath pathToTypeParamInConstraint,
|
||||
TypePath pathToTypeParamInSub
|
||||
) {
|
||||
satisfiesConstraintTypeMentionInline(term, constraint, abs, path, pathToTypeParamInSub)
|
||||
satisfiesConstraintInline(term, constraint, _, pathToTypeParamInConstraint,
|
||||
pathToTypeParamInSub, _)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private predicate satisfiesConstraintTypeNonTypeParamInline(
|
||||
private predicate satisfiesConstraintNonTypeParamInline(
|
||||
Term term, TypeAbstraction abs, Constraint constraint, TypePath path, Type t
|
||||
) {
|
||||
satisfiesConstraintTypeMention1(term, constraint, abs, _, path, t) and
|
||||
satisfiesConstraint0(term, constraint, abs, _, path, t) and
|
||||
not t = abs.getATypeParameter()
|
||||
}
|
||||
|
||||
@@ -1142,15 +1160,14 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type tree at `term` satisfies the constraint `constraint`
|
||||
* with the type `t` at `path`.
|
||||
* Holds if `term` satisfies the constraint `constraint` with the type `t` at `path`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate satisfiesConstraintType(Term term, Constraint constraint, TypePath path, Type t) {
|
||||
satisfiesConstraintTypeNonTypeParamInline(term, _, constraint, path, t)
|
||||
predicate satisfiesConstraint(Term term, Constraint constraint, TypePath path, Type t) {
|
||||
satisfiesConstraintNonTypeParamInline(term, _, constraint, path, t)
|
||||
or
|
||||
exists(TypePath prefix0, TypePath pathToTypeParamInSub, TypePath suffix |
|
||||
satisfiesConstraintTypeMention(term, constraint, prefix0, pathToTypeParamInSub) and
|
||||
satisfiesConstraintAtTypeParameter(term, constraint, prefix0, pathToTypeParamInSub) and
|
||||
getTypeAt(term, pathToTypeParamInSub.appendInverse(suffix)) = t and
|
||||
path = prefix0.append(suffix)
|
||||
)
|
||||
@@ -1159,25 +1176,34 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
t = getTypeAt(term, path)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate satisfiesConstraintThrough0(
|
||||
Term term, Constraint constraint, TypeAbstraction abs, TypePath pathToTypeParamInConstraint,
|
||||
TypePath pathToTypeParamInSub
|
||||
) {
|
||||
satisfiesConstraintInline(term, constraint, abs, pathToTypeParamInConstraint,
|
||||
pathToTypeParamInSub, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type tree at `term` satisfies the constraint `constraint`
|
||||
* through `abs` with the type `t` at `path`.
|
||||
* Holds if `term` satisfies the constraint `constraint` through `abs` with
|
||||
* the type `t` at `path`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate satisfiesConstraintTypeThrough(
|
||||
predicate satisfiesConstraintThrough(
|
||||
Term term, TypeAbstraction abs, Constraint constraint, TypePath path, Type t
|
||||
) {
|
||||
satisfiesConstraintTypeNonTypeParamInline(term, abs, constraint, path, t)
|
||||
satisfiesConstraintNonTypeParamInline(term, abs, constraint, path, t)
|
||||
or
|
||||
exists(TypePath prefix0, TypePath pathToTypeParamInSub, TypePath suffix |
|
||||
satisfiesConstraintTypeMentionThrough(term, constraint, abs, prefix0, pathToTypeParamInSub) and
|
||||
satisfiesConstraintThrough0(term, constraint, abs, prefix0, pathToTypeParamInSub) and
|
||||
getTypeAt(term, pathToTypeParamInSub.appendInverse(suffix)) = t and
|
||||
path = prefix0.append(suffix)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type tree at `term` does _not_ satisfy the constraint `constraint`.
|
||||
* Holds if `term` does _not_ satisfy the constraint `constraint`.
|
||||
*
|
||||
* This is an approximation of `not satisfiesConstraintType(term, constraint, _, _)`,
|
||||
* but defined without a negative occurrence of `satisfiesConstraintType`.
|
||||
@@ -1495,7 +1521,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
TypeMention constraint
|
||||
) {
|
||||
target = a.getTarget(e) and
|
||||
typeParameterConstraintHasTypeParameter(target, apos, path, constraint, _, _)
|
||||
typeParameterHasConstraint(target, apos, _, path, constraint)
|
||||
}
|
||||
|
||||
private newtype TRelevantAccess =
|
||||
@@ -1556,13 +1582,40 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
SatisfiesTypeParameterConstraintInput>;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate satisfiesConstraintType(
|
||||
predicate satisfiesConstraintAtTypeParameter(
|
||||
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
|
||||
TypeMention constraint, TypePath pathToTypeParamInConstraint,
|
||||
TypePath pathToTypeParamInSub
|
||||
) {
|
||||
exists(RelevantAccess ra |
|
||||
ra = MkRelevantAccess(a, apos, e, prefix) and
|
||||
SatisfiesTypeParameterConstraint::satisfiesConstraintAtTypeParameter(ra, constraint,
|
||||
pathToTypeParamInConstraint, pathToTypeParamInSub) and
|
||||
constraint = ra.getConstraint(target)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate satisfiesConstraint(
|
||||
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
|
||||
TypeMention constraint, TypePath path, Type t
|
||||
) {
|
||||
exists(RelevantAccess ra |
|
||||
ra = MkRelevantAccess(a, apos, e, prefix) and
|
||||
SatisfiesTypeParameterConstraint::satisfiesConstraintType(ra, constraint, path, t) and
|
||||
SatisfiesTypeParameterConstraint::satisfiesConstraint(ra, constraint, path, t) and
|
||||
constraint = ra.getConstraint(target)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate satisfiesConstraintThrough(
|
||||
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
|
||||
TypeAbstraction abs, TypeMention constraint, TypePath path, Type t
|
||||
) {
|
||||
exists(RelevantAccess ra |
|
||||
ra = MkRelevantAccess(a, apos, e, prefix) and
|
||||
SatisfiesTypeParameterConstraint::satisfiesConstraintThrough(ra, abs, constraint, path,
|
||||
t) and
|
||||
constraint = ra.getConstraint(target)
|
||||
)
|
||||
}
|
||||
@@ -1707,7 +1760,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
not exists(getTypeArgument(a, target, tp, _)) and
|
||||
exists(TypeMention constraint, AccessPosition apos, TypePath pathToTp, TypePath pathToTp2 |
|
||||
typeParameterConstraintHasTypeParameter(target, apos, pathToTp2, constraint, pathToTp, tp) and
|
||||
AccessConstraint::satisfiesConstraintType(a, e, target, apos, pathToTp2, constraint,
|
||||
AccessConstraint::satisfiesConstraint(a, e, target, apos, pathToTp2, constraint,
|
||||
pathToTp.appendInverse(path), t)
|
||||
)
|
||||
}
|
||||
@@ -1785,6 +1838,82 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
not result instanceof TypeParameter
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
Declaration target, TypePath prefix, TypeMention constraint,
|
||||
TypePath pathToTypeParamInConstraint, TypePath pathToTypeParamInSub
|
||||
|
|
||||
AccessConstraint::satisfiesConstraintAtTypeParameter(a, e, target, apos, prefix,
|
||||
constraint, pathToTypeParamInConstraint, pathToTypeParamInSub)
|
||||
|
|
||||
exists(TypePath suffix |
|
||||
/*
|
||||
* Example:
|
||||
*
|
||||
* ```rust
|
||||
* struct MyThing<A> { ... }
|
||||
*
|
||||
* trait MyTrait<B> { ... }
|
||||
*
|
||||
* impl<T> MyTrait<T> for MyThing<T> { ... }
|
||||
*
|
||||
* fn foo<T: MyTrait<i32>>(x: T) { ... }
|
||||
*
|
||||
* let x = MyThing(Default::default());
|
||||
* foo(x);
|
||||
* ```
|
||||
*
|
||||
* At `term` = `foo(x)`, we have
|
||||
* - `constraint = MyTrait<i32>`,
|
||||
* - `pathToTypeParamInConstraint` = `"B"`,
|
||||
* - `pathToTypeParamInSub` = `"A"`,
|
||||
* - `prefix` = `suffix` = `""`, and
|
||||
* - `result` = `i32`.
|
||||
*
|
||||
* That is, it allows us to infer that the type of `x` is `MyThing<i32>`.
|
||||
*/
|
||||
|
||||
result = constraint.getTypeAt(pathToTypeParamInConstraint.appendInverse(suffix)) and
|
||||
not result instanceof TypeParameter and
|
||||
path = prefix.append(pathToTypeParamInSub.append(suffix))
|
||||
)
|
||||
or
|
||||
exists(TypeParameter tp, TypePath suffix, TypePath mid, TypePath pathToTp |
|
||||
/*
|
||||
* Example:
|
||||
*
|
||||
* ```rust
|
||||
* struct MyThing<A> { ... }
|
||||
*
|
||||
* trait MyTrait<B> { ... }
|
||||
*
|
||||
* impl<T> MyTrait<T> for MyThing<T> { ... }
|
||||
*
|
||||
* fn bar<T1, T2: MyTrait<T1>>(x: T1, y: T2) {}
|
||||
*
|
||||
* let x : i32 = ...;
|
||||
* let y = MyThing(Default::default());
|
||||
* bar(x, y);
|
||||
* ```
|
||||
*
|
||||
* At `term` = `bar(x, y)`, we have
|
||||
* - `constraint = MyTrait<T1>`,
|
||||
* - `pathToTypeParamInConstraint` = `"B"`,
|
||||
* - `pathToTypeParamInSub` = `"A"`,
|
||||
* - `prefix` = `suffix` = `mid` = `""`,
|
||||
* - `tp = T1`,
|
||||
* - `pathToTp` = `"B"`, and
|
||||
* - `result` = `i32`.
|
||||
*
|
||||
* That is, it allows us to infer that the type of `y` is `MyThing<i32>`.
|
||||
*/
|
||||
|
||||
typeMatch(a, e, target, suffix, result, tp) and
|
||||
typeParameterConstraintHasTypeParameter(target, apos, _, constraint, pathToTp, tp) and
|
||||
pathToTp = pathToTypeParamInConstraint.appendInverse(mid) and
|
||||
path = prefix.append(pathToTypeParamInSub.append(mid).append(suffix))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user