Rust: Generalize certain type inference logic

This commit is contained in:
Tom Hvitved
2025-08-06 16:31:11 +02:00
parent dfe4401f13
commit 1be542ec56
4 changed files with 197 additions and 158 deletions

View File

@@ -15,6 +15,14 @@ private import codeql.rust.elements.internal.CallExprImpl::Impl as CallExprImpl
class Type = T::Type;
private newtype TTypeArgumentPosition =
// method type parameters are matched by position instead of by type
// parameter entity, to avoid extra recursion through method call resolution
TMethodTypeArgumentPosition(int pos) {
exists(any(MethodCallExpr mce).getGenericArgList().getTypeArg(pos))
} or
TTypeParamTypeArgumentPosition(TypeParam tp)
private module Input1 implements InputSig1<Location> {
private import Type as T
private import codeql.rust.elements.internal.generated.Raw
@@ -26,14 +34,6 @@ private module Input1 implements InputSig1<Location> {
class TypeAbstraction = T::TypeAbstraction;
private newtype TTypeArgumentPosition =
// method type parameters are matched by position instead of by type
// parameter entity, to avoid extra recursion through method call resolution
TMethodTypeArgumentPosition(int pos) {
exists(any(MethodCallExpr mce).getGenericArgList().getTypeArg(pos))
} or
TTypeParamTypeArgumentPosition(TypeParam tp)
class TypeArgumentPosition extends TTypeArgumentPosition {
int asMethodTypeArgumentPosition() { this = TMethodTypeArgumentPosition(result) }
@@ -257,75 +257,59 @@ private Type inferAnnotatedType(AstNode n, TypePath path) {
/** Module for inferring certain type information. */
private module CertainTypeInference {
/** Holds if the type mention does not contain any inferred types `_`. */
predicate typeMentionIsComplete(TypeMention tm) {
not exists(InferTypeRepr t | t.getParentNode*() = tm)
pragma[nomagic]
private predicate callResolvesTo(CallExpr ce, Path p, Function f) {
p = CallExprImpl::getFunctionPath(ce) and
f = resolvePath(p)
}
/**
* Holds if `ce` is a call where we can infer the type with certainty and if
* `f` is the target of the call and `p` the path invoked by the call.
*
* Necessary conditions for this are:
* - We are certain of the call target (i.e., the call target can not depend on type information).
* - The declared type of the function does not contain any generics that we
* need to infer.
* - The call does not contain any arguments, as arguments in calls are coercion sites.
*
* The current requirements are made to allow for call to `new` functions such
* as `Vec<Foo>::new()` but not much more.
*/
predicate certainCallExprTarget(CallExpr ce, Function f, Path p) {
p = CallExprImpl::getFunctionPath(ce) and
f = resolvePath(p) and
// The function is not in a trait
not any(TraitItemNode t).getAnAssocItem() = f and
// The function is not in a trait implementation
not any(ImplItemNode impl | impl.(Impl).hasTrait()).getAnAssocItem() = f and
// The function does not have parameters.
not f.getParamList().hasSelfParam() and
f.getParamList().getNumberOfParams() = 0 and
// The function is not async.
not f.isAsync() and
// For now, exclude functions in macro expansions.
not ce.isInMacroExpansion() and
// The function has no type parameters.
not f.hasGenericParamList() and
// The function does not have `impl` types among its parameters (these are type parameters).
not any(ImplTraitTypeRepr itt | not itt.isInReturnPos()).getFunction() = f and
(
not exists(ImplItemNode impl | impl.getAnAssocItem() = f)
or
// If the function is in an impl then the impl block has no type
// parameters or all the type parameters are given explicitly.
exists(ImplItemNode impl | impl.getAnAssocItem() = f |
not impl.(Impl).hasGenericParamList() or
impl.(Impl).getGenericParamList().getNumberOfGenericParams() =
p.getQualifier().getSegment().getGenericArgList().getNumberOfGenericArgs()
)
pragma[nomagic]
private Type getCallExprType(
CallExpr ce, Path p, CallExprBaseMatchingInput::FunctionDecl f, TypePath tp
) {
callResolvesTo(ce, p, f) and
result = f.getReturnType(tp)
}
pragma[nomagic]
private Type getCertainCallExprType(CallExpr ce, Path p, TypePath tp) {
forex(Function f | callResolvesTo(ce, p, f) | result = getCallExprType(ce, p, f, tp))
}
pragma[nomagic]
private TypePath getPathToImplSelfTypeParam(TypeParam tp) {
exists(ImplItemNode impl |
tp = impl.getTypeParam(_) and
TTypeParamTypeParameter(tp) = impl.(Impl).getSelfTy().(TypeMention).resolveTypeAt(result)
)
}
private ImplItemNode getFunctionImpl(FunctionItemNode f) { result.getAnAssocItem() = f }
pragma[nomagic]
Type inferCertainCallExprType(CallExpr ce, TypePath path) {
exists(Function f, Type ty, TypePath prefix, Path p |
certainCallExprTarget(ce, f, p) and
ty = f.getRetType().getTypeRepr().(TypeMention).resolveTypeAt(prefix)
|
if ty.(TypeParamTypeParameter).getTypeParam() = getFunctionImpl(f).getTypeParam(_)
then
exists(TypePath pathToTp, TypePath suffix |
// For type parameters of the `impl` block we must resolve their
// instantiation from the path. For instance, for `impl<A> for Foo<A>`
// and the path `Foo<i64>::bar` we must resolve `A` to `i64`.
ty = getFunctionImpl(f).(Impl).getSelfTy().(TypeMention).resolveTypeAt(pathToTp) and
result = p.getQualifier().(TypeMention).resolveTypeAt(pathToTp.appendInverse(suffix)) and
path = prefix.append(suffix)
exists(Type ty, TypePath prefix, Path p | ty = getCertainCallExprType(ce, p, prefix) |
exists(TypePath suffix, TypeParam tp |
tp = ty.(TypeParamTypeParameter).getTypeParam() and
path = prefix.append(suffix)
|
// For type parameters of the `impl` block we must resolve their
// instantiation from the path. For instance, for `impl<A> for Foo<A>`
// and the path `Foo<i64>::bar` we must resolve `A` to `i64`.
exists(TypePath pathToTp |
pathToTp = getPathToImplSelfTypeParam(tp) and
result = p.getQualifier().(TypeMention).resolveTypeAt(pathToTp.appendInverse(suffix))
)
else (
result = ty and path = prefix
or
// For type parameters of the function we must resolve their
// instantiation from the path. For instance, for `fn bar<A>(a: A) -> A`
// and the path `bar<i64>`, we must resolve `A` to `i64`.
result =
ce.(CallExprBaseMatchingInput::Access)
.getTypeArgument(TTypeParamTypeArgumentPosition(tp), suffix)
)
or
not ty instanceof TypeParameter and
result = ty and
path = prefix
)
}
@@ -343,6 +327,8 @@ private module CertainTypeInference {
let.getPat() = n1 and
let.getInitializer() = n2
)
or
n1 = n2.(ParenExpr).getExpr()
)
or
n1 =
@@ -373,13 +359,57 @@ private module CertainTypeInference {
Type inferCertainType(AstNode n, TypePath path) {
exists(TypeMention tm |
tm = getTypeAnnotation(n) and
typeMentionIsComplete(tm) and
result = tm.resolveTypeAt(path)
)
or
result = inferCertainCallExprType(n, path)
or
result = inferCertainTypeEquality(n, path)
or
result = inferLiteralType(n, path, true)
or
infersCertainTypeAt(n, path, result.getATypeParameter())
}
/**
* Holds if `n` has complete and certain type information at the type path
* `prefix.tp`. This entails that the type at `prefix` must be the type
* that declares `tp`.
*/
pragma[nomagic]
private predicate infersCertainTypeAt(AstNode n, TypePath prefix, TypeParameter tp) {
exists(TypePath path |
exists(inferCertainType(n, path)) and
path.isSnoc(prefix, tp)
)
}
/**
* Holds if `n` has complete and certain type information at _some_ type path.
*/
pragma[nomagic]
predicate hasInferredCertainType(AstNode n) { exists(inferCertainType(n, _)) }
/**
* Holds if `n` having type `t` at `path` conflicts with certain type information.
*/
bindingset[n, path, t]
pragma[inline_late]
predicate certainTypeConflict(AstNode n, TypePath path, Type t) {
inferCertainType(n, path) != t
or
// If we infer that `n` has _some_ type at `T1.T2....Tn`, and we also
// know that `n` certainly has type `certainType` at `T1.T2...Ti`, `i <=0 < n`,
// then it must be the case that `T(i+1)` is a type parameter of `certainType`,
// otherwise there is a conflict.
//
// Below, `prefix` is `T1.T2...Ti` and `tp` is `T(i+1)`.
exists(TypePath prefix, TypePath suffix, TypeParameter tp, Type certainType |
path = prefix.appendInverse(suffix) and
tp = suffix.getHead() and
inferCertainType(n, prefix) = certainType and
not certainType.getATypeParameter() = tp
)
}
}
@@ -432,8 +462,6 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
let.getInitializer() = n2
)
or
n1 = n2.(ParenExpr).getExpr()
or
n1 = n2.(IfExpr).getABranch()
or
n1 = n2.(MatchExpr).getAnArm().getExpr()
@@ -531,9 +559,6 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
pragma[nomagic]
private Type inferTypeEquality(AstNode n, TypePath path) {
// Don't propagate type information into a node for which we already have
// certain type information.
not exists(CertainTypeInference::inferCertainType(n, _)) and
exists(TypePath prefix1, AstNode n2, TypePath prefix2, TypePath suffix |
result = inferType(n2, prefix2.appendInverse(suffix)) and
path = prefix1.append(suffix)
@@ -842,7 +867,7 @@ private module CallExprBaseMatchingInput implements MatchingInputSig {
}
}
private class FunctionDecl extends Declaration, Function {
additional class FunctionDecl extends Declaration, Function {
override TypeParameter getTypeParameter(TypeParameterPosition ppos) {
typeParamMatchPosition(this.getGenericParamList().getATypeParam(), result, ppos)
or
@@ -944,8 +969,6 @@ private module CallExprBaseMatchingInput implements MatchingInputSig {
}
final class Access extends Call {
Access() { not CertainTypeInference::certainCallExprTarget(this, _, _) }
pragma[nomagic]
Type getTypeArgument(TypeArgumentPosition apos, TypePath path) {
exists(TypeMention arg | result = arg.resolveTypeAt(path) |
@@ -1308,17 +1331,22 @@ pragma[nomagic]
private StructType getStrStruct() { result = TStruct(any(Builtins::Str s)) }
pragma[nomagic]
private Type inferLiteralType(LiteralExpr le, TypePath path) {
private Type inferLiteralType(LiteralExpr le, TypePath path, boolean certain) {
path.isEmpty() and
exists(Builtins::BuiltinType t | result = TStruct(t) |
le instanceof CharLiteralExpr and
t instanceof Builtins::Char
t instanceof Builtins::Char and
certain = true
or
le =
any(NumberLiteralExpr ne |
t.getName() = ne.getSuffix()
t.getName() = ne.getSuffix() and
certain = true
or
// When a number literal has no suffix, the type may depend on the context.
// For simplicity, we assume either `i32` or `f64`.
not exists(ne.getSuffix()) and
certain = false and
(
ne instanceof IntegerLiteralExpr and
t instanceof Builtins::I32
@@ -1329,7 +1357,8 @@ private Type inferLiteralType(LiteralExpr le, TypePath path) {
)
or
le instanceof BooleanLiteralExpr and
t instanceof Builtins::Bool
t instanceof Builtins::Bool and
certain = true
)
or
le instanceof StringLiteralExpr and
@@ -1338,7 +1367,8 @@ private Type inferLiteralType(LiteralExpr le, TypePath path) {
or
path = TypePath::singleton(TRefTypeParameter()) and
result = getStrStruct()
)
) and
certain = true
}
pragma[nomagic]
@@ -2282,62 +2312,71 @@ private module Cached {
Stages::TypeInferenceStage::ref() and
result = CertainTypeInference::inferCertainType(n, path)
or
result = inferAnnotatedType(n, path)
or
result = inferLogicalOperationType(n, path)
or
result = inferAssignmentOperationType(n, path)
or
result = inferTypeEquality(n, path)
or
result = inferImplicitSelfType(n, path)
or
result = inferStructExprType(n, path)
or
result = inferTupleRootType(n) and
path.isEmpty()
or
result = inferPathExprType(n, path)
or
result = inferCallExprBaseType(n, path)
or
result = inferFieldExprType(n, path)
or
result = inferTupleIndexExprType(n, path)
or
result = inferTupleContainerExprType(n, path)
or
result = inferRefNodeType(n) and
path.isEmpty()
or
result = inferTryExprType(n, path)
or
result = inferLiteralType(n, path)
or
result = inferAsyncBlockExprRootType(n) and
path.isEmpty()
or
result = inferAwaitExprType(n, path)
or
result = inferArrayExprType(n) and
path.isEmpty()
or
result = inferRangeExprType(n) and
path.isEmpty()
or
result = inferIndexExprType(n, path)
or
result = inferForLoopExprType(n, path)
or
result = inferDynamicCallExprType(n, path)
or
result = inferClosureExprType(n, path)
or
result = inferCastExprType(n, path)
or
result = inferStructPatType(n, path)
or
result = inferTupleStructPatType(n, path)
// Don't propagate type information into a node which conflicts with certain
// type information.
(
if CertainTypeInference::hasInferredCertainType(n)
then not CertainTypeInference::certainTypeConflict(n, path, result)
else any()
) and
(
result = inferAnnotatedType(n, path)
or
result = inferLogicalOperationType(n, path)
or
result = inferAssignmentOperationType(n, path)
or
result = inferTypeEquality(n, path)
or
result = inferImplicitSelfType(n, path)
or
result = inferStructExprType(n, path)
or
result = inferTupleRootType(n) and
path.isEmpty()
or
result = inferPathExprType(n, path)
or
result = inferCallExprBaseType(n, path)
or
result = inferFieldExprType(n, path)
or
result = inferTupleIndexExprType(n, path)
or
result = inferTupleContainerExprType(n, path)
or
result = inferRefNodeType(n) and
path.isEmpty()
or
result = inferTryExprType(n, path)
or
result = inferLiteralType(n, path, false)
or
result = inferAsyncBlockExprRootType(n) and
path.isEmpty()
or
result = inferAwaitExprType(n, path)
or
result = inferArrayExprType(n) and
path.isEmpty()
or
result = inferRangeExprType(n) and
path.isEmpty()
or
result = inferIndexExprType(n, path)
or
result = inferForLoopExprType(n, path)
or
result = inferDynamicCallExprType(n, path)
or
result = inferClosureExprType(n, path)
or
result = inferCastExprType(n, path)
or
result = inferStructPatType(n, path)
or
result = inferTupleStructPatType(n, path)
)
}
}
@@ -2438,6 +2477,11 @@ private module Debug {
c = max(countTypePaths(_, _, _))
}
Type debugInferCertainType(AstNode n, TypePath path) {
n = getRelevantLocatable() and
result = CertainTypeInference::inferCertainType(n, path)
}
Type debugInferCertainNonUniqueType(AstNode n, TypePath path) {
n = getRelevantLocatable() and
Consistency::nonUniqueCertainType(n, path) and

View File

@@ -2074,7 +2074,7 @@ mod indexers {
// implicit dereference. We cannot currently handle a position that is
// both implicitly dereferenced and implicitly borrowed, so the extra
// type sneaks in.
let x = slice[0].foo(); // $ target=foo type=x:S target=index SPURIOUS: type=slice:[]
let x = slice[0].foo(); // $ target=foo type=x:S target=index
}
pub fn f() {

View File

@@ -288,7 +288,6 @@ inferType
| dereference.rs:60:14:60:17 | &'a' | &T | {EXTERNAL LOCATION} | char |
| dereference.rs:60:14:60:17 | &'a' | &T | file://:0:0:0:0 | & |
| dereference.rs:60:15:60:17 | 'a' | | {EXTERNAL LOCATION} | char |
| dereference.rs:60:15:60:17 | 'a' | | file://:0:0:0:0 | & |
| dereference.rs:61:9:61:11 | _f1 | | file://:0:0:0:0 | & |
| dereference.rs:61:15:61:16 | e1 | | file://:0:0:0:0 | & |
| dereference.rs:61:15:61:16 | e1 | &T | {EXTERNAL LOCATION} | char |
@@ -401,7 +400,6 @@ inferType
| dereference.rs:123:32:123:41 | key_to_key | K | file://:0:0:0:0 | & |
| dereference.rs:123:32:123:41 | key_to_key | K.&T | dereference.rs:99:5:100:21 | Key |
| dereference.rs:123:32:123:41 | key_to_key | S | {EXTERNAL LOCATION} | RandomState |
| dereference.rs:123:32:123:41 | key_to_key | V | dereference.rs:99:5:100:21 | Key |
| dereference.rs:123:32:123:41 | key_to_key | V | file://:0:0:0:0 | & |
| dereference.rs:123:32:123:41 | key_to_key | V.&T | dereference.rs:99:5:100:21 | Key |
| dereference.rs:123:32:123:50 | key_to_key.get(...) | | {EXTERNAL LOCATION} | Option |
@@ -425,13 +423,9 @@ inferType
| dereference.rs:127:9:127:18 | key_to_key | | {EXTERNAL LOCATION} | HashMap |
| dereference.rs:127:9:127:18 | key_to_key | K | file://:0:0:0:0 | & |
| dereference.rs:127:9:127:18 | key_to_key | K.&T | dereference.rs:99:5:100:21 | Key |
| dereference.rs:127:9:127:18 | key_to_key | K.&T | file://:0:0:0:0 | & |
| dereference.rs:127:9:127:18 | key_to_key | K.&T.&T | dereference.rs:99:5:100:21 | Key |
| dereference.rs:127:9:127:18 | key_to_key | S | {EXTERNAL LOCATION} | RandomState |
| dereference.rs:127:9:127:18 | key_to_key | V | file://:0:0:0:0 | & |
| dereference.rs:127:9:127:18 | key_to_key | V.&T | dereference.rs:99:5:100:21 | Key |
| dereference.rs:127:9:127:18 | key_to_key | V.&T | file://:0:0:0:0 | & |
| dereference.rs:127:9:127:18 | key_to_key | V.&T.&T | dereference.rs:99:5:100:21 | Key |
| dereference.rs:127:9:127:35 | key_to_key.insert(...) | | {EXTERNAL LOCATION} | Option |
| dereference.rs:127:9:127:35 | key_to_key.insert(...) | T | file://:0:0:0:0 | & |
| dereference.rs:127:9:127:35 | key_to_key.insert(...) | T.&T | dereference.rs:99:5:100:21 | Key |
@@ -3941,7 +3935,6 @@ inferType
| main.rs:2070:22:2070:26 | slice | &T.[T] | main.rs:2037:5:2038:13 | S |
| main.rs:2077:13:2077:13 | x | | main.rs:2037:5:2038:13 | S |
| main.rs:2077:17:2077:21 | slice | | file://:0:0:0:0 | & |
| main.rs:2077:17:2077:21 | slice | | file://:0:0:0:0 | [] |
| main.rs:2077:17:2077:21 | slice | &T | file://:0:0:0:0 | [] |
| main.rs:2077:17:2077:21 | slice | &T.[T] | main.rs:2037:5:2038:13 | S |
| main.rs:2077:17:2077:24 | slice[0] | | main.rs:2037:5:2038:13 | S |
@@ -4031,7 +4024,6 @@ inferType
| main.rs:2144:16:2144:19 | self | T | main.rs:2139:10:2139:17 | T |
| main.rs:2144:16:2144:21 | self.0 | | main.rs:2139:10:2139:17 | T |
| main.rs:2144:31:2144:35 | other | | main.rs:2137:5:2137:19 | S |
| main.rs:2144:31:2144:35 | other | T | main.rs:2099:5:2104:5 | Self [trait MyAdd] |
| main.rs:2144:31:2144:35 | other | T | main.rs:2139:10:2139:17 | T |
| main.rs:2144:31:2144:37 | other.0 | | main.rs:2099:5:2104:5 | Self [trait MyAdd] |
| main.rs:2144:31:2144:37 | other.0 | | main.rs:2139:10:2139:17 | T |
@@ -4047,7 +4039,6 @@ inferType
| main.rs:2153:16:2153:19 | self | | main.rs:2137:5:2137:19 | S |
| main.rs:2153:16:2153:19 | self | T | main.rs:2148:10:2148:17 | T |
| main.rs:2153:16:2153:21 | self.0 | | main.rs:2148:10:2148:17 | T |
| main.rs:2153:31:2153:35 | other | | main.rs:2099:5:2104:5 | Self [trait MyAdd] |
| main.rs:2153:31:2153:35 | other | | main.rs:2148:10:2148:17 | T |
| main.rs:2164:19:2164:22 | SelfParam | | main.rs:2137:5:2137:19 | S |
| main.rs:2164:19:2164:22 | SelfParam | T | main.rs:2157:14:2157:14 | T |
@@ -4215,7 +4206,6 @@ inferType
| main.rs:2295:21:2295:31 | [...] | | file://:0:0:0:0 | [] |
| main.rs:2295:21:2295:31 | [...] | [T;...] | {EXTERNAL LOCATION} | i32 |
| main.rs:2295:21:2295:31 | [...] | [T;...] | {EXTERNAL LOCATION} | u8 |
| main.rs:2295:22:2295:24 | 1u8 | | {EXTERNAL LOCATION} | i32 |
| main.rs:2295:22:2295:24 | 1u8 | | {EXTERNAL LOCATION} | u8 |
| main.rs:2295:27:2295:27 | 2 | | {EXTERNAL LOCATION} | i32 |
| main.rs:2295:27:2295:27 | 2 | | {EXTERNAL LOCATION} | u8 |
@@ -4366,7 +4356,6 @@ inferType
| main.rs:2338:18:2338:26 | [...] | [T;...] | {EXTERNAL LOCATION} | Range |
| main.rs:2338:18:2338:26 | [...] | [T;...].Idx | {EXTERNAL LOCATION} | i32 |
| main.rs:2338:18:2338:26 | [...] | [T;...].Idx | {EXTERNAL LOCATION} | u8 |
| main.rs:2338:19:2338:21 | 0u8 | | {EXTERNAL LOCATION} | i32 |
| main.rs:2338:19:2338:21 | 0u8 | | {EXTERNAL LOCATION} | u8 |
| main.rs:2338:19:2338:25 | 0u8..10 | | {EXTERNAL LOCATION} | Range |
| main.rs:2338:19:2338:25 | 0u8..10 | Idx | {EXTERNAL LOCATION} | i32 |
@@ -4405,7 +4394,6 @@ inferType
| main.rs:2354:32:2354:52 | ... .to_vec() | | {EXTERNAL LOCATION} | Vec |
| main.rs:2354:32:2354:52 | ... .to_vec() | A | {EXTERNAL LOCATION} | Global |
| main.rs:2354:32:2354:52 | ... .to_vec() | T | {EXTERNAL LOCATION} | u16 |
| main.rs:2354:33:2354:36 | 1u16 | | {EXTERNAL LOCATION} | i32 |
| main.rs:2354:33:2354:36 | 1u16 | | {EXTERNAL LOCATION} | u16 |
| main.rs:2354:39:2354:39 | 2 | | {EXTERNAL LOCATION} | i32 |
| main.rs:2354:39:2354:39 | 2 | | {EXTERNAL LOCATION} | u16 |
@@ -4419,7 +4407,6 @@ inferType
| main.rs:2357:22:2357:33 | [...] | | file://:0:0:0:0 | [] |
| main.rs:2357:22:2357:33 | [...] | [T;...] | {EXTERNAL LOCATION} | i32 |
| main.rs:2357:22:2357:33 | [...] | [T;...] | {EXTERNAL LOCATION} | u16 |
| main.rs:2357:23:2357:26 | 1u16 | | {EXTERNAL LOCATION} | i32 |
| main.rs:2357:23:2357:26 | 1u16 | | {EXTERNAL LOCATION} | u16 |
| main.rs:2357:29:2357:29 | 2 | | {EXTERNAL LOCATION} | i32 |
| main.rs:2357:29:2357:29 | 2 | | {EXTERNAL LOCATION} | u16 |
@@ -4436,7 +4423,6 @@ inferType
| main.rs:2360:31:2360:42 | [...] | | file://:0:0:0:0 | [] |
| main.rs:2360:31:2360:42 | [...] | [T;...] | {EXTERNAL LOCATION} | i32 |
| main.rs:2360:31:2360:42 | [...] | [T;...] | {EXTERNAL LOCATION} | u32 |
| main.rs:2360:32:2360:35 | 1u32 | | {EXTERNAL LOCATION} | i32 |
| main.rs:2360:32:2360:35 | 1u32 | | {EXTERNAL LOCATION} | u32 |
| main.rs:2360:38:2360:38 | 2 | | {EXTERNAL LOCATION} | i32 |
| main.rs:2360:38:2360:38 | 2 | | {EXTERNAL LOCATION} | u32 |
@@ -4460,7 +4446,6 @@ inferType
| main.rs:2363:32:2363:60 | ... .collect() | A | {EXTERNAL LOCATION} | Global |
| main.rs:2363:32:2363:60 | ... .collect() | T | file://:0:0:0:0 | & |
| main.rs:2363:32:2363:60 | ... .collect() | T.&T | {EXTERNAL LOCATION} | u64 |
| main.rs:2363:33:2363:36 | 1u64 | | {EXTERNAL LOCATION} | i32 |
| main.rs:2363:33:2363:36 | 1u64 | | {EXTERNAL LOCATION} | u64 |
| main.rs:2363:39:2363:39 | 2 | | {EXTERNAL LOCATION} | i32 |
| main.rs:2363:39:2363:39 | 2 | | {EXTERNAL LOCATION} | u64 |
@@ -5737,10 +5722,8 @@ inferType
| pattern_matching.rs:444:17:444:36 | TupleExpr | 2(3) | {EXTERNAL LOCATION} | f32 |
| pattern_matching.rs:444:17:444:36 | TupleExpr | 2(3) | {EXTERNAL LOCATION} | f64 |
| pattern_matching.rs:444:18:444:21 | 1i32 | | {EXTERNAL LOCATION} | i32 |
| pattern_matching.rs:444:24:444:27 | 2i64 | | {EXTERNAL LOCATION} | i32 |
| pattern_matching.rs:444:24:444:27 | 2i64 | | {EXTERNAL LOCATION} | i64 |
| pattern_matching.rs:444:30:444:35 | 3.0f32 | | {EXTERNAL LOCATION} | f32 |
| pattern_matching.rs:444:30:444:35 | 3.0f32 | | {EXTERNAL LOCATION} | f64 |
| pattern_matching.rs:447:11:447:15 | tuple | | file://:0:0:0:0 | (T_3) |
| pattern_matching.rs:447:11:447:15 | tuple | 0(3) | {EXTERNAL LOCATION} | i32 |
| pattern_matching.rs:447:11:447:15 | tuple | 1(3) | {EXTERNAL LOCATION} | i32 |

View File

@@ -334,7 +334,19 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
/** Holds if this path starts with `tp`, followed by `suffix`. */
bindingset[this]
predicate isCons(TypeParameter tp, TypePath suffix) {
suffix = this.stripPrefix(TypePath::singleton(tp))
exists(string regexp | regexp = "([0-9]+)\\.(.*)" |
tp = TypeParameter::decode(this.regexpCapture(regexp, 1)) and
suffix = this.regexpCapture(regexp, 2)
)
}
/** Holds if this path starts with `prefix`, followed by `tp`. */
bindingset[this]
predicate isSnoc(TypePath prefix, TypeParameter tp) {
exists(string regexp | regexp = "(|.+\\.)([0-9]+)\\." |
prefix = this.regexpCapture(regexp, 1) and
tp = TypeParameter::decode(this.regexpCapture(regexp, 2))
)
}
/** Gets the head of this path, if any. */