mirror of
https://github.com/github/codeql.git
synced 2026-04-25 08:45:14 +02:00
Merge pull request #19372 from paldepind/rust-ti-implementing-type-method
Rust: Support non-universal `impl` blocks
This commit is contained in:
@@ -14,7 +14,13 @@ private import codeql.rust.internal.TypeInference
|
||||
* be referenced directly.
|
||||
*/
|
||||
module Impl {
|
||||
private predicate isImplFunction(Function f) { f = any(ImplItemNode impl).getAnAssocItem() }
|
||||
private predicate isInherentImplFunction(Function f) {
|
||||
f = any(Impl impl | not impl.hasTrait()).(ImplItemNode).getAnAssocItem()
|
||||
}
|
||||
|
||||
private predicate isTraitImplFunction(Function f) {
|
||||
f = any(Impl impl | impl.hasTrait()).(ImplItemNode).getAnAssocItem()
|
||||
}
|
||||
|
||||
// the following QLdoc is generated: if you need to edit it, do it in the schema file
|
||||
/**
|
||||
@@ -25,23 +31,39 @@ module Impl {
|
||||
* ```
|
||||
*/
|
||||
class MethodCallExpr extends Generated::MethodCallExpr {
|
||||
override Function getStaticTarget() {
|
||||
private Function getStaticTargetFrom(boolean fromSource) {
|
||||
result = resolveMethodCallExpr(this) and
|
||||
(if result.fromSource() then fromSource = true else fromSource = false) and
|
||||
(
|
||||
// prioritize `impl` methods first
|
||||
isImplFunction(result)
|
||||
// prioritize inherent implementation methods first
|
||||
isInherentImplFunction(result)
|
||||
or
|
||||
not isImplFunction(resolveMethodCallExpr(this)) and
|
||||
not isInherentImplFunction(resolveMethodCallExpr(this)) and
|
||||
(
|
||||
// then trait methods with default implementations
|
||||
result.hasBody()
|
||||
// then trait implementation methods
|
||||
isTraitImplFunction(result)
|
||||
or
|
||||
// and finally trait methods without default implementations
|
||||
not resolveMethodCallExpr(this).hasBody()
|
||||
not isTraitImplFunction(resolveMethodCallExpr(this)) and
|
||||
(
|
||||
// then trait methods with default implementations
|
||||
result.hasBody()
|
||||
or
|
||||
// and finally trait methods without default implementations
|
||||
not resolveMethodCallExpr(this).hasBody()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override Function getStaticTarget() {
|
||||
// Functions in source code also gets extracted as library code, due to
|
||||
// this duplication we prioritize functions from source code.
|
||||
result = this.getStaticTargetFrom(true)
|
||||
or
|
||||
not exists(this.getStaticTargetFrom(true)) and
|
||||
result = this.getStaticTargetFrom(false)
|
||||
}
|
||||
|
||||
private string toStringPart(int index) {
|
||||
index = 0 and
|
||||
result = this.getReceiver().toAbbreviatedString()
|
||||
|
||||
@@ -446,61 +446,6 @@ class ImplItemNode extends ImplOrTraitItemNode instanceof Impl {
|
||||
|
||||
TraitItemNode resolveTraitTy() { result = resolvePathFull(this.getTraitPath()) }
|
||||
|
||||
pragma[nomagic]
|
||||
private TypeRepr getASelfTyArg() {
|
||||
result =
|
||||
this.getSelfPath().getSegment().getGenericArgList().getAGenericArg().(TypeArg).getTypeRepr()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this `impl` block is not fully parametric. That is, the implementing
|
||||
* type is generic and the implementation is not parametrically polymorphic in all
|
||||
* the implementing type's arguments.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* ```rust
|
||||
* impl Foo { ... } // fully parametric
|
||||
*
|
||||
* impl<T> Foo<T> { ... } // fully parametric
|
||||
*
|
||||
* impl Foo<i64> { ... } // not fully parametric
|
||||
*
|
||||
* impl<T> Foo<Foo<T>> { ... } // not fully parametric
|
||||
*
|
||||
* impl<T: Trait> Foo<T> { ... } // not fully parametric
|
||||
*
|
||||
* impl<T> Foo<T> where T: Trait { ... } // not fully parametric
|
||||
* ```
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate isNotFullyParametric() {
|
||||
exists(TypeRepr arg | arg = this.getASelfTyArg() |
|
||||
not exists(resolveTypeParamPathTypeRepr(arg))
|
||||
or
|
||||
resolveTypeParamPathTypeRepr(arg).hasTraitBound()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this `impl` block is fully parametric. Examples:
|
||||
*
|
||||
* ```rust
|
||||
* impl Foo { ... } // fully parametric
|
||||
*
|
||||
* impl<T> Foo<T> { ... } // fully parametric
|
||||
*
|
||||
* impl Foo<i64> { ... } // not fully parametric
|
||||
*
|
||||
* impl<T> Foo<Foo<T>> { ... } // not fully parametric
|
||||
*
|
||||
* impl<T: Trait> Foo<T> { ... } // not fully parametric
|
||||
*
|
||||
* impl<T> Foo<T> where T: Trait { ... } // not fully parametric
|
||||
* ```
|
||||
*/
|
||||
predicate isFullyParametric() { not this.isNotFullyParametric() }
|
||||
|
||||
override AssocItemNode getAnAssocItem() { result = super.getAssocItemList().getAnAssocItem() }
|
||||
|
||||
override string getName() { result = "(impl)" }
|
||||
|
||||
@@ -27,10 +27,6 @@ newtype TType =
|
||||
* types, such as traits and implementation blocks.
|
||||
*/
|
||||
abstract class Type extends TType {
|
||||
/** Gets the method `name` belonging to this type, if any. */
|
||||
pragma[nomagic]
|
||||
abstract Function getMethod(string name);
|
||||
|
||||
/** Gets the struct field `name` belonging to this type, if any. */
|
||||
pragma[nomagic]
|
||||
abstract StructField getStructField(string name);
|
||||
@@ -45,25 +41,6 @@ abstract class Type extends TType {
|
||||
/** Gets a type parameter of this type. */
|
||||
final TypeParameter getATypeParameter() { result = this.getTypeParameter(_) }
|
||||
|
||||
/**
|
||||
* Gets an AST node that mentions a base type of this type, if any.
|
||||
*
|
||||
* Although Rust doesn't have traditional OOP-style inheritance, we model trait
|
||||
* bounds and `impl` blocks as base types. Example:
|
||||
*
|
||||
* ```rust
|
||||
* trait T1 {}
|
||||
*
|
||||
* trait T2 {}
|
||||
*
|
||||
* trait T3 : T1, T2 {}
|
||||
* // ^^ `this`
|
||||
* // ^^ `result`
|
||||
* // ^^ `result`
|
||||
* ```
|
||||
*/
|
||||
abstract TypeMention getABaseTypeMention();
|
||||
|
||||
/** Gets a textual representation of this type. */
|
||||
abstract string toString();
|
||||
|
||||
@@ -73,21 +50,6 @@ abstract class Type extends TType {
|
||||
|
||||
abstract private class StructOrEnumType extends Type {
|
||||
abstract ItemNode asItemNode();
|
||||
|
||||
final override Function getMethod(string name) {
|
||||
result = this.asItemNode().getASuccessor(name) and
|
||||
exists(ImplOrTraitItemNode impl | result = impl.getAnAssocItem() |
|
||||
impl instanceof Trait
|
||||
or
|
||||
impl.(ImplItemNode).isFullyParametric()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets all of the fully parametric `impl` blocks that target this type. */
|
||||
final override ImplMention getABaseTypeMention() {
|
||||
this.asItemNode() = result.resolveSelfTy() and
|
||||
result.isFullyParametric()
|
||||
}
|
||||
}
|
||||
|
||||
/** A struct type. */
|
||||
@@ -138,8 +100,6 @@ class TraitType extends Type, TTrait {
|
||||
|
||||
TraitType() { this = TTrait(trait) }
|
||||
|
||||
override Function getMethod(string name) { result = trait.(ItemNode).getASuccessor(name) }
|
||||
|
||||
override StructField getStructField(string name) { none() }
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
@@ -151,14 +111,6 @@ class TraitType extends Type, TTrait {
|
||||
any(AssociatedTypeTypeParameter param | param.getTrait() = trait and param.getIndex() = i)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private TypeReprMention getABoundMention() {
|
||||
result = trait.getTypeBoundList().getABound().getTypeRepr()
|
||||
}
|
||||
|
||||
/** Gets any of the trait bounds of this trait. */
|
||||
override TypeMention getABaseTypeMention() { result = this.getABoundMention() }
|
||||
|
||||
override string toString() { result = trait.toString() }
|
||||
|
||||
override Location getLocation() { result = trait.getLocation() }
|
||||
@@ -220,8 +172,6 @@ class ImplType extends Type, TImpl {
|
||||
|
||||
ImplType() { this = TImpl(impl) }
|
||||
|
||||
override Function getMethod(string name) { result = impl.(ItemNode).getASuccessor(name) }
|
||||
|
||||
override StructField getStructField(string name) { none() }
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
@@ -230,9 +180,6 @@ class ImplType extends Type, TImpl {
|
||||
result = TTypeParamTypeParameter(impl.getGenericParamList().getTypeParam(i))
|
||||
}
|
||||
|
||||
/** Get the trait implemented by this `impl` block, if any. */
|
||||
override TypeMention getABaseTypeMention() { result = impl.getTrait() }
|
||||
|
||||
override string toString() { result = impl.toString() }
|
||||
|
||||
override Location getLocation() { result = impl.getLocation() }
|
||||
@@ -247,8 +194,6 @@ class ImplType extends Type, TImpl {
|
||||
class ArrayType extends Type, TArrayType {
|
||||
ArrayType() { this = TArrayType() }
|
||||
|
||||
override Function getMethod(string name) { none() }
|
||||
|
||||
override StructField getStructField(string name) { none() }
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
@@ -257,8 +202,6 @@ class ArrayType extends Type, TArrayType {
|
||||
none() // todo
|
||||
}
|
||||
|
||||
override TypeMention getABaseTypeMention() { none() }
|
||||
|
||||
override string toString() { result = "[]" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
@@ -273,8 +216,6 @@ class ArrayType extends Type, TArrayType {
|
||||
class RefType extends Type, TRefType {
|
||||
RefType() { this = TRefType() }
|
||||
|
||||
override Function getMethod(string name) { none() }
|
||||
|
||||
override StructField getStructField(string name) { none() }
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
@@ -284,8 +225,6 @@ class RefType extends Type, TRefType {
|
||||
i = 0
|
||||
}
|
||||
|
||||
override TypeMention getABaseTypeMention() { none() }
|
||||
|
||||
override string toString() { result = "&" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
@@ -293,8 +232,6 @@ class RefType extends Type, TRefType {
|
||||
|
||||
/** A type parameter. */
|
||||
abstract class TypeParameter extends Type {
|
||||
override TypeMention getABaseTypeMention() { none() }
|
||||
|
||||
override StructField getStructField(string name) { none() }
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
@@ -318,19 +255,9 @@ class TypeParamTypeParameter extends TypeParameter, TTypeParamTypeParameter {
|
||||
|
||||
TypeParam getTypeParam() { result = typeParam }
|
||||
|
||||
override Function getMethod(string name) {
|
||||
// NOTE: If the type parameter has trait bounds, then this finds methods
|
||||
// on the bounding traits.
|
||||
result = typeParam.(ItemNode).getASuccessor(name)
|
||||
}
|
||||
|
||||
override string toString() { result = typeParam.toString() }
|
||||
|
||||
override Location getLocation() { result = typeParam.getLocation() }
|
||||
|
||||
final override TypeMention getABaseTypeMention() {
|
||||
result = typeParam.getTypeBoundList().getABound().getTypeRepr()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,19 +304,13 @@ class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypePara
|
||||
|
||||
int getIndex() { traitAliasIndex(_, result, typeAlias) }
|
||||
|
||||
override Function getMethod(string name) { none() }
|
||||
|
||||
override string toString() { result = typeAlias.getName().getText() }
|
||||
|
||||
override Location getLocation() { result = typeAlias.getLocation() }
|
||||
|
||||
override TypeMention getABaseTypeMention() { none() }
|
||||
}
|
||||
|
||||
/** An implicit reference type parameter. */
|
||||
class RefTypeParameter extends TypeParameter, TRefTypeParameter {
|
||||
override Function getMethod(string name) { none() }
|
||||
|
||||
override string toString() { result = "&T" }
|
||||
|
||||
override Location getLocation() { result instanceof EmptyLocation }
|
||||
@@ -409,15 +330,43 @@ class SelfTypeParameter extends TypeParameter, TSelfTypeParameter {
|
||||
|
||||
Trait getTrait() { result = trait }
|
||||
|
||||
override TypeMention getABaseTypeMention() { result = trait }
|
||||
|
||||
override Function getMethod(string name) {
|
||||
// The `Self` type parameter is an implementation of the trait, so it has
|
||||
// all the trait's methods.
|
||||
result = trait.(ItemNode).getASuccessor(name)
|
||||
}
|
||||
|
||||
override string toString() { result = "Self [" + trait.toString() + "]" }
|
||||
|
||||
override Location getLocation() { result = trait.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A type abstraction. I.e., a place in the program where type variables are
|
||||
* introduced.
|
||||
*
|
||||
* Example:
|
||||
* ```rust
|
||||
* impl<A, B> Foo<A, B> { }
|
||||
* // ^^^^^^ a type abstraction
|
||||
* ```
|
||||
*/
|
||||
abstract class TypeAbstraction extends AstNode {
|
||||
abstract TypeParameter getATypeParameter();
|
||||
}
|
||||
|
||||
final class ImplTypeAbstraction extends TypeAbstraction, Impl {
|
||||
override TypeParamTypeParameter getATypeParameter() {
|
||||
result.getTypeParam() = this.getGenericParamList().getATypeParam()
|
||||
}
|
||||
}
|
||||
|
||||
final class TraitTypeAbstraction extends TypeAbstraction, Trait {
|
||||
override TypeParamTypeParameter getATypeParameter() {
|
||||
result.getTypeParam() = this.getGenericParamList().getATypeParam()
|
||||
}
|
||||
}
|
||||
|
||||
final class TypeBoundTypeAbstraction extends TypeAbstraction, TypeBound {
|
||||
override TypeParamTypeParameter getATypeParameter() { none() }
|
||||
}
|
||||
|
||||
final class SelfTypeBoundTypeAbstraction extends TypeAbstraction, Name {
|
||||
SelfTypeBoundTypeAbstraction() { any(Trait trait).getName() = this }
|
||||
|
||||
override TypeParamTypeParameter getATypeParameter() { none() }
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ private module Input1 implements InputSig1<Location> {
|
||||
|
||||
class TypeParameter = T::TypeParameter;
|
||||
|
||||
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
|
||||
@@ -108,7 +110,52 @@ private module Input2 implements InputSig2 {
|
||||
|
||||
class TypeMention = TM::TypeMention;
|
||||
|
||||
TypeMention getABaseTypeMention(Type t) { result = t.getABaseTypeMention() }
|
||||
TypeMention getABaseTypeMention(Type t) { none() }
|
||||
|
||||
TypeMention getATypeParameterConstraint(TypeParameter tp) {
|
||||
result = tp.(TypeParamTypeParameter).getTypeParam().getTypeBoundList().getABound().getTypeRepr()
|
||||
or
|
||||
result = tp.(SelfTypeParameter).getTrait()
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the constraint mechanism in the shared type inference library to
|
||||
* support traits. In Rust `constraint` is always a trait.
|
||||
*
|
||||
* See the documentation of `conditionSatisfiesConstraint` in the shared type
|
||||
* inference module for more information.
|
||||
*/
|
||||
predicate conditionSatisfiesConstraint(
|
||||
TypeAbstraction abs, TypeMention condition, TypeMention constraint
|
||||
) {
|
||||
// `impl` blocks implementing traits
|
||||
exists(Impl impl |
|
||||
abs = impl and
|
||||
condition = impl.getSelfTy() and
|
||||
constraint = impl.getTrait()
|
||||
)
|
||||
or
|
||||
// supertraits
|
||||
exists(Trait trait |
|
||||
abs = trait and
|
||||
condition = trait and
|
||||
constraint = trait.getTypeBoundList().getABound().getTypeRepr()
|
||||
)
|
||||
or
|
||||
// trait bounds on type parameters
|
||||
exists(TypeParam param |
|
||||
abs = param.getTypeBoundList().getABound() and
|
||||
condition = param and
|
||||
constraint = abs.(TypeBound).getTypeRepr()
|
||||
)
|
||||
or
|
||||
// the implicit `Self` type parameter satisfies the trait
|
||||
exists(SelfTypeParameterMention self |
|
||||
abs = self and
|
||||
condition = self and
|
||||
constraint = self.getTrait()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module M2 = Make2<Input2>;
|
||||
@@ -227,7 +274,7 @@ private Type getRefAdjustImplicitSelfType(SelfParam self, TypePath suffix, Type
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Type inferImplSelfType(Impl i, TypePath path) {
|
||||
private Type resolveImplSelfType(Impl i, TypePath path) {
|
||||
result = i.getSelfTy().(TypeReprMention).resolveTypeAt(path)
|
||||
}
|
||||
|
||||
@@ -239,7 +286,7 @@ private Type inferImplicitSelfType(SelfParam self, TypePath path) {
|
||||
self = f.getParamList().getSelfParam() and
|
||||
result = getRefAdjustImplicitSelfType(self, suffix, t, path)
|
||||
|
|
||||
t = inferImplSelfType(i, suffix)
|
||||
t = resolveImplSelfType(i, suffix)
|
||||
or
|
||||
t = TSelfTypeParameter(i) and suffix.isEmpty()
|
||||
)
|
||||
@@ -941,36 +988,104 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private Type getLookupType(AstNode n) {
|
||||
exists(Type t |
|
||||
t = inferType(n) and
|
||||
if t = TRefType()
|
||||
then
|
||||
// for reference types, lookup members in the type being referenced
|
||||
result = inferType(n, TypePath::singleton(TRefTypeParameter()))
|
||||
else result = t
|
||||
private class ReceiverExpr extends Expr {
|
||||
MethodCallExpr mce;
|
||||
|
||||
ReceiverExpr() { mce.getReceiver() = this }
|
||||
|
||||
string getField() { result = mce.getIdentifier().getText() }
|
||||
|
||||
int getNumberOfArgs() { result = mce.getArgList().getNumberOfArgs() }
|
||||
|
||||
pragma[nomagic]
|
||||
Type getTypeAt(TypePath path) {
|
||||
exists(TypePath path0 | result = inferType(this, path0) |
|
||||
path0 = TypePath::cons(TRefTypeParameter(), path)
|
||||
or
|
||||
not path0.isCons(TRefTypeParameter(), _) and
|
||||
not (path0.isEmpty() and result = TRefType()) and
|
||||
path = path0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if a method for `type` with the name `name` and the arity `arity` exists in `impl`. */
|
||||
pragma[nomagic]
|
||||
private predicate methodCandidate(Type type, string name, int arity, Impl impl) {
|
||||
type = impl.getSelfTy().(TypeReprMention).resolveType() and
|
||||
exists(Function f |
|
||||
f = impl.(ImplItemNode).getASuccessor(name) and
|
||||
f.getParamList().hasSelfParam() and
|
||||
arity = f.getParamList().getNumberOfParams()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Type getMethodCallExprLookupType(MethodCallExpr mce, string name) {
|
||||
result = getLookupType(mce.getReceiver()) and
|
||||
name = mce.getIdentifier().getText()
|
||||
private module IsInstantiationOfInput implements IsInstantiationOfInputSig<ReceiverExpr> {
|
||||
pragma[nomagic]
|
||||
predicate potentialInstantiationOf(
|
||||
ReceiverExpr receiver, TypeAbstraction impl, TypeMention constraint
|
||||
) {
|
||||
methodCandidate(receiver.getTypeAt(TypePath::nil()), receiver.getField(),
|
||||
receiver.getNumberOfArgs(), impl) and
|
||||
constraint = impl.(ImplTypeAbstraction).getSelfTy()
|
||||
}
|
||||
|
||||
predicate relevantTypeMention(TypeMention constraint) {
|
||||
exists(Impl impl | methodCandidate(_, _, _, impl) and constraint = impl.getSelfTy())
|
||||
}
|
||||
}
|
||||
|
||||
bindingset[item, name]
|
||||
pragma[inline_late]
|
||||
private Function getMethodSuccessor(ItemNode item, string name) {
|
||||
result = item.getASuccessor(name)
|
||||
}
|
||||
|
||||
bindingset[tp, name]
|
||||
pragma[inline_late]
|
||||
private Function getTypeParameterMethod(TypeParameter tp, string name) {
|
||||
result = getMethodSuccessor(tp.(TypeParamTypeParameter).getTypeParam(), name)
|
||||
or
|
||||
result = getMethodSuccessor(tp.(SelfTypeParameter).getTrait(), name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a method that the method call `mce` resolves to, if any.
|
||||
* Gets the method from an `impl` block with an implementing type that matches
|
||||
* the type of `receiver` and with a name of the method call in which
|
||||
* `receiver` occurs, if any.
|
||||
*/
|
||||
private Function getMethodFromImpl(ReceiverExpr receiver) {
|
||||
exists(Impl impl |
|
||||
IsInstantiationOf<ReceiverExpr, IsInstantiationOfInput>::isInstantiationOf(receiver, impl, _) and
|
||||
result = getMethodSuccessor(impl, receiver.getField())
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a method that the method call `mce` resolves to, if any. */
|
||||
cached
|
||||
Function resolveMethodCallExpr(MethodCallExpr mce) {
|
||||
exists(string name | result = getMethodCallExprLookupType(mce, name).getMethod(name))
|
||||
exists(ReceiverExpr receiver | mce.getReceiver() = receiver |
|
||||
// The method comes from an `impl` block targeting the type of `receiver`.
|
||||
result = getMethodFromImpl(receiver)
|
||||
or
|
||||
// The type of `receiver` is a type parameter and the method comes from a
|
||||
// trait bound on the type parameter.
|
||||
result = getTypeParameterMethod(receiver.getTypeAt(TypePath::nil()), receiver.getField())
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private Type inferRootTypeDeref(AstNode n) {
|
||||
result = inferType(n) and
|
||||
result != TRefType()
|
||||
or
|
||||
// for reference types, lookup members in the type being referenced
|
||||
result = inferType(n, TypePath::singleton(TRefTypeParameter()))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Type getFieldExprLookupType(FieldExpr fe, string name) {
|
||||
result = getLookupType(fe.getContainer()) and
|
||||
name = fe.getIdentifier().getText()
|
||||
result = inferRootTypeDeref(fe.getContainer()) and name = fe.getIdentifier().getText()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -248,3 +248,19 @@ class TraitMention extends TypeMention, TraitItemNode {
|
||||
|
||||
override Type resolveType() { result = TTrait(this) }
|
||||
}
|
||||
|
||||
// NOTE: Since the implicit type parameter for the self type parameter never
|
||||
// appears in the AST, we (somewhat arbitrarily) choose the name of a trait as a
|
||||
// type mention. This works because there is a one-to-one correspondence between
|
||||
// a trait and its name.
|
||||
class SelfTypeParameterMention extends TypeMention, Name {
|
||||
Trait trait;
|
||||
|
||||
SelfTypeParameterMention() { trait.getName() = this }
|
||||
|
||||
Trait getTrait() { result = trait }
|
||||
|
||||
override Type resolveType() { result = TSelfTypeParameter(trait) }
|
||||
|
||||
override TypeReprMention getTypeArgument(int i) { none() }
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
multipleMethodCallTargets
|
||||
| regular.rs:29:5:29:9 | s.g() | anonymous.rs:15:9:15:22 | fn g |
|
||||
| regular.rs:29:5:29:9 | s.g() | regular.rs:13:5:13:18 | fn g |
|
||||
@@ -1,3 +0,0 @@
|
||||
multipleMethodCallTargets
|
||||
| regular.rs:32:5:32:9 | s.g() | anonymous.rs:18:9:18:22 | fn g |
|
||||
| regular.rs:32:5:32:9 | s.g() | regular.rs:16:5:16:18 | fn g |
|
||||
@@ -1,17 +0,0 @@
|
||||
multipleMethodCallTargets
|
||||
| main.rs:11:5:18:5 | conn.execute(...) | file://:0:0:0:0 | fn execute |
|
||||
| main.rs:11:5:18:5 | conn.execute(...) | file://:0:0:0:0 | fn execute |
|
||||
| main.rs:22:5:22:37 | conn.execute(...) | file://:0:0:0:0 | fn execute |
|
||||
| main.rs:22:5:22:37 | conn.execute(...) | file://:0:0:0:0 | fn execute |
|
||||
| main.rs:23:5:23:38 | conn.batch_execute(...) | file://:0:0:0:0 | fn batch_execute |
|
||||
| main.rs:23:5:23:38 | conn.batch_execute(...) | file://:0:0:0:0 | fn batch_execute |
|
||||
| main.rs:25:5:25:32 | conn.prepare(...) | file://:0:0:0:0 | fn prepare |
|
||||
| main.rs:25:5:25:32 | conn.prepare(...) | file://:0:0:0:0 | fn prepare |
|
||||
| main.rs:28:5:28:35 | conn.query(...) | file://:0:0:0:0 | fn query |
|
||||
| main.rs:28:5:28:35 | conn.query(...) | file://:0:0:0:0 | fn query |
|
||||
| main.rs:29:5:29:39 | conn.query_one(...) | file://:0:0:0:0 | fn query_one |
|
||||
| main.rs:29:5:29:39 | conn.query_one(...) | file://:0:0:0:0 | fn query_one |
|
||||
| main.rs:30:5:30:39 | conn.query_opt(...) | file://:0:0:0:0 | fn query_opt |
|
||||
| main.rs:30:5:30:39 | conn.query_opt(...) | file://:0:0:0:0 | fn query_opt |
|
||||
| main.rs:35:17:35:67 | conn.query(...) | file://:0:0:0:0 | fn query |
|
||||
| main.rs:35:17:35:67 | conn.query(...) | file://:0:0:0:0 | fn query |
|
||||
@@ -610,8 +610,8 @@ mod m24 {
|
||||
let impl_obj = Implementor; // $ item=I118
|
||||
let generic = GenericStruct { data: impl_obj }; // $ item=I115
|
||||
|
||||
generic.call_trait_a(); // $ MISSING: item=I116
|
||||
generic.call_both(); // $ MISSING: item=I117
|
||||
generic.call_trait_a(); // $ item=I116
|
||||
generic.call_both(); // $ item=I117
|
||||
|
||||
// Access through where clause type parameter constraint
|
||||
GenericStruct::<Implementor>::call_trait_a(&generic); // $ item=I116 item=I118
|
||||
|
||||
@@ -129,8 +129,8 @@ mod method_non_parametric_impl {
|
||||
println!("{:?}", x.a); // $ fieldof=MyThing
|
||||
println!("{:?}", y.a); // $ fieldof=MyThing
|
||||
|
||||
println!("{:?}", x.m1()); // $ MISSING: method=MyThing<S1>::m1
|
||||
println!("{:?}", y.m1().a); // $ MISSING: method=MyThing<S2>::m1, field=MyThing
|
||||
println!("{:?}", x.m1()); // $ method=MyThing<S1>::m1
|
||||
println!("{:?}", y.m1().a); // $ method=MyThing<S2>::m1 fieldof=MyThing
|
||||
|
||||
let x = MyThing { a: S1 };
|
||||
let y = MyThing { a: S2 };
|
||||
@@ -141,15 +141,23 @@ mod method_non_parametric_impl {
|
||||
}
|
||||
|
||||
mod method_non_parametric_trait_impl {
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct MyThing<A> {
|
||||
a: A,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct MyPair<P1, P2> {
|
||||
p1: P1,
|
||||
p2: P2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct S1;
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct S2;
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
struct S3;
|
||||
|
||||
trait MyTrait<A> {
|
||||
fn m1(self) -> A;
|
||||
@@ -162,6 +170,13 @@ mod method_non_parametric_trait_impl {
|
||||
}
|
||||
}
|
||||
|
||||
trait MyProduct<A, B> {
|
||||
// MyProduct::fst
|
||||
fn fst(self) -> A;
|
||||
// MyProduct::snd
|
||||
fn snd(self) -> B;
|
||||
}
|
||||
|
||||
fn call_trait_m1<T1, T2: MyTrait<T1>>(x: T2) -> T1 {
|
||||
x.m1() // $ method=m1
|
||||
}
|
||||
@@ -180,18 +195,211 @@ mod method_non_parametric_trait_impl {
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation where the type parameter `TD` only occurs in the
|
||||
// implemented trait and not the implementing type.
|
||||
impl<TD> MyTrait<TD> for MyThing<S3>
|
||||
where
|
||||
TD: Default,
|
||||
{
|
||||
// MyThing<S3>::m1
|
||||
fn m1(self) -> TD {
|
||||
TD::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> MyTrait<I> for MyPair<I, S1> {
|
||||
// MyTrait<I>::m1
|
||||
fn m1(self) -> I {
|
||||
self.p1 // $ fieldof=MyPair
|
||||
}
|
||||
}
|
||||
|
||||
impl MyTrait<S3> for MyPair<S1, S2> {
|
||||
// MyTrait<S3>::m1
|
||||
fn m1(self) -> S3 {
|
||||
S3
|
||||
}
|
||||
}
|
||||
|
||||
impl<TT> MyTrait<TT> for MyPair<MyThing<TT>, S3> {
|
||||
// MyTrait<TT>::m1
|
||||
fn m1(self) -> TT {
|
||||
let alpha = self.p1; // $ fieldof=MyPair
|
||||
alpha.a // $ fieldof=MyThing
|
||||
}
|
||||
}
|
||||
|
||||
// This implementation only applies if the two type parameters are equal.
|
||||
impl<A> MyProduct<A, A> for MyPair<A, A> {
|
||||
// MyPair<A,A>::fst
|
||||
fn fst(self) -> A {
|
||||
self.p1 // $ fieldof=MyPair
|
||||
}
|
||||
|
||||
// MyPair<A,A>::snd
|
||||
fn snd(self) -> A {
|
||||
self.p2 // $ fieldof=MyPair
|
||||
}
|
||||
}
|
||||
|
||||
// This implementation swaps the type parameters.
|
||||
impl MyProduct<S1, S2> for MyPair<S2, S1> {
|
||||
// MyPair<S2,S1>::fst
|
||||
fn fst(self) -> S1 {
|
||||
self.p2 // $ fieldof=MyPair
|
||||
}
|
||||
|
||||
// MyPair<S2,S1>::snd
|
||||
fn snd(self) -> S2 {
|
||||
self.p1 // $ fieldof=MyPair
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fst<V1, V2, P: MyProduct<V1, V2>>(p: P) -> V1 {
|
||||
p.fst() // $ method=MyProduct::fst
|
||||
}
|
||||
|
||||
fn get_snd<V1, V2, P: MyProduct<V1, V2>>(p: P) -> V2 {
|
||||
p.snd() // $ method=MyProduct::snd
|
||||
}
|
||||
|
||||
fn get_snd_fst<V0, V1, V2, P: MyProduct<V1, V2>>(p: MyPair<V0, P>) -> V1 {
|
||||
p.p2.fst() // $ fieldof=MyPair method=MyProduct::fst
|
||||
}
|
||||
|
||||
trait ConvertTo<T> {
|
||||
// ConvertTo::convert_to
|
||||
fn convert_to(self) -> T;
|
||||
}
|
||||
|
||||
impl<T: MyTrait<S1>> ConvertTo<S1> for T {
|
||||
// T::convert_to
|
||||
fn convert_to(self) -> S1 {
|
||||
self.m1() // $ method=m1
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_to<TS, T: ConvertTo<TS>>(thing: T) -> TS {
|
||||
thing.convert_to() // $ method=ConvertTo::convert_to
|
||||
}
|
||||
|
||||
fn type_bound_type_parameter_impl<TP: MyTrait<S1>>(thing: TP) -> S1 {
|
||||
// The trait bound on `TP` makes the implementation of `ConvertTo` valid
|
||||
thing.convert_to() // $ MISSING: method=T::convert_to
|
||||
}
|
||||
|
||||
pub fn f() {
|
||||
let x = MyThing { a: S1 };
|
||||
let y = MyThing { a: S2 };
|
||||
let thing_s1 = MyThing { a: S1 };
|
||||
let thing_s2 = MyThing { a: S2 };
|
||||
let thing_s3 = MyThing { a: S3 };
|
||||
|
||||
println!("{:?}", x.m1()); // $ MISSING: method=MyThing<S1>::m1
|
||||
println!("{:?}", y.m1().a); // $ MISSING: method=MyThing<S2>::m1, field=MyThing
|
||||
// Tests for method resolution
|
||||
|
||||
let x = MyThing { a: S1 };
|
||||
let y = MyThing { a: S2 };
|
||||
println!("{:?}", thing_s1.m1()); // $ method=MyThing<S1>::m1
|
||||
println!("{:?}", thing_s2.m1().a); // $ method=MyThing<S2>::m1 fieldof=MyThing
|
||||
let s3: S3 = thing_s3.m1(); // $ method=MyThing<S3>::m1
|
||||
println!("{:?}", s3);
|
||||
|
||||
println!("{:?}", call_trait_m1(x)); // MISSING: type=call_trait_m1(...):S1
|
||||
println!("{:?}", call_trait_m1(y).a); // MISSING: field=MyThing
|
||||
let p1 = MyPair { p1: S1, p2: S1 };
|
||||
println!("{:?}", p1.m1()); // $ method=MyTrait<I>::m1
|
||||
|
||||
let p2 = MyPair { p1: S1, p2: S2 };
|
||||
println!("{:?}", p2.m1()); // $ method=MyTrait<S3>::m1
|
||||
|
||||
let p3 = MyPair {
|
||||
p1: MyThing { a: S1 },
|
||||
p2: S3,
|
||||
};
|
||||
println!("{:?}", p3.m1()); // $ method=MyTrait<TT>::m1
|
||||
|
||||
// These calls go to the first implementation of `MyProduct` for `MyPair`
|
||||
let a = MyPair { p1: S1, p2: S1 };
|
||||
let x = a.fst(); // $ method=MyPair<A,A>::fst
|
||||
println!("{:?}", x);
|
||||
let y = a.snd(); // $ method=MyPair<A,A>::snd
|
||||
println!("{:?}", y);
|
||||
|
||||
// These calls go to the last implementation of `MyProduct` for
|
||||
// `MyPair`. The first implementation does not apply as the type
|
||||
// parameters of the implementation enforce that the two generics must
|
||||
// be equal.
|
||||
let b = MyPair { p1: S2, p2: S1 };
|
||||
let x = b.fst(); // $ method=MyPair<S2,S1>::fst
|
||||
println!("{:?}", x);
|
||||
let y = b.snd(); // $ method=MyPair<S2,S1>::snd
|
||||
println!("{:?}", y);
|
||||
|
||||
// Tests for inference of type parameters based on trait implementations.
|
||||
|
||||
let x = call_trait_m1(thing_s1); // $ type=x:S1
|
||||
println!("{:?}", x);
|
||||
let y = call_trait_m1(thing_s2); // $ type=y:MyThing type=y:A.S2
|
||||
println!("{:?}", y.a); // $ fieldof=MyThing
|
||||
|
||||
// First implementation
|
||||
let a = MyPair { p1: S1, p2: S1 };
|
||||
let x = get_fst(a); // $ type=x:S1
|
||||
println!("{:?}", x);
|
||||
let y = get_snd(a); // $ type=y:S1
|
||||
println!("{:?}", y);
|
||||
|
||||
// Second implementation
|
||||
let b = MyPair { p1: S2, p2: S1 };
|
||||
let x = get_fst(b); // $ type=x:S1
|
||||
println!("{:?}", x);
|
||||
let y = get_snd(b); // $ type=y:S2
|
||||
println!("{:?}", y);
|
||||
|
||||
let c = MyPair {
|
||||
p1: S3,
|
||||
p2: MyPair { p1: S2, p2: S1 },
|
||||
};
|
||||
let x = get_snd_fst(c); // $ type=x:S1
|
||||
|
||||
let thing = MyThing { a: S1 };
|
||||
let i = thing.convert_to(); // $ MISSING: type=i:S1 method=T::convert_to
|
||||
let j = convert_to(thing); // $ type=j:S1
|
||||
}
|
||||
}
|
||||
|
||||
mod impl_overlap {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct S1;
|
||||
|
||||
trait OverlappingTrait {
|
||||
fn common_method(self) -> S1;
|
||||
|
||||
fn common_method_2(self, s1: S1) -> S1;
|
||||
}
|
||||
|
||||
impl OverlappingTrait for S1 {
|
||||
// <S1_as_OverlappingTrait>::common_method
|
||||
fn common_method(self) -> S1 {
|
||||
panic!("not called");
|
||||
}
|
||||
|
||||
// <S1_as_OverlappingTrait>::common_method_2
|
||||
fn common_method_2(self, s1: S1) -> S1 {
|
||||
panic!("not called");
|
||||
}
|
||||
}
|
||||
|
||||
impl S1 {
|
||||
// S1::common_method
|
||||
fn common_method(self) -> S1 {
|
||||
self
|
||||
}
|
||||
|
||||
// S1::common_method_2
|
||||
fn common_method_2(self) -> S1 {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn f() {
|
||||
let x = S1;
|
||||
println!("{:?}", x.common_method()); // $ method=S1::common_method
|
||||
println!("{:?}", x.common_method_2()); // $ method=S1::common_method_2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,13 +427,13 @@ mod type_parameter_bounds {
|
||||
fn call_first_trait_per_bound<I: Debug, T: SecondTrait<I>>(x: T) {
|
||||
// The type parameter bound determines which method this call is resolved to.
|
||||
let s1 = x.method(); // $ method=SecondTrait::method
|
||||
println!("{:?}", s1);
|
||||
println!("{:?}", s1); // $ type=s1:I
|
||||
}
|
||||
|
||||
fn call_second_trait_per_bound<I: Debug, T: SecondTrait<I>>(x: T) {
|
||||
// The type parameter bound determines which method this call is resolved to.
|
||||
let s2 = x.method(); // $ method=SecondTrait::method
|
||||
println!("{:?}", s2);
|
||||
println!("{:?}", s2); // $ type=s2:I
|
||||
}
|
||||
|
||||
fn trait_bound_with_type<T: FirstTrait<S1>>(x: T) {
|
||||
@@ -235,7 +443,7 @@ mod type_parameter_bounds {
|
||||
|
||||
fn trait_per_bound_with_type<T: FirstTrait<S1>>(x: T) {
|
||||
let s = x.method(); // $ method=FirstTrait::method
|
||||
println!("{:?}", s);
|
||||
println!("{:?}", s); // $ type=s:S1
|
||||
}
|
||||
|
||||
trait Pair<P1, P2> {
|
||||
@@ -323,8 +531,10 @@ mod function_trait_bounds {
|
||||
a: MyThing { a: S2 },
|
||||
};
|
||||
|
||||
println!("{:?}", call_trait_thing_m1(x3));
|
||||
println!("{:?}", call_trait_thing_m1(y3));
|
||||
let a = call_trait_thing_m1(x3); // $ type=a:S1
|
||||
println!("{:?}", a);
|
||||
let b = call_trait_thing_m1(y3); // $ type=b:S2
|
||||
println!("{:?}", b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,6 +784,16 @@ mod method_supertraits {
|
||||
|
||||
impl<T> MyTrait3<T> for MyThing2<T> {}
|
||||
|
||||
fn call_trait_m1<T1, T2: MyTrait1<T1>>(x: T2) -> T1 {
|
||||
x.m1() // $ method=MyTrait1::m1
|
||||
}
|
||||
|
||||
fn type_param_trait_to_supertrait<T: MyTrait3<S1>>(x: T) {
|
||||
// Test that `MyTrait3` is a subtrait of `MyTrait1<MyThing<S1>>`
|
||||
let a = x.m1(); // $ method=MyTrait1::m1 type=a:MyThing type=a:A.S1
|
||||
println!("{:?}", a);
|
||||
}
|
||||
|
||||
pub fn f() {
|
||||
let x = MyThing { a: S1 };
|
||||
let y = MyThing { a: S2 };
|
||||
@@ -584,14 +804,20 @@ mod method_supertraits {
|
||||
let x = MyThing { a: S1 };
|
||||
let y = MyThing { a: S2 };
|
||||
|
||||
println!("{:?}", x.m2()); // $ method=m2
|
||||
println!("{:?}", y.m2()); // $ method=m2
|
||||
println!("{:?}", x.m2()); // $ method=m2 type=x.m2():S1
|
||||
println!("{:?}", y.m2()); // $ method=m2 type=y.m2():S2
|
||||
|
||||
let x = MyThing2 { a: S1 };
|
||||
let y = MyThing2 { a: S2 };
|
||||
|
||||
println!("{:?}", x.m3()); // $ method=m3
|
||||
println!("{:?}", y.m3()); // $ method=m3
|
||||
println!("{:?}", x.m3()); // $ method=m3 type=x.m3():S1
|
||||
println!("{:?}", y.m3()); // $ method=m3 type=y.m3():S2
|
||||
|
||||
let x = MyThing { a: S1 };
|
||||
let s = call_trait_m1(x); // $ type=s:S1
|
||||
|
||||
let x = MyThing2 { a: S2 };
|
||||
let s = call_trait_m1(x); // $ type=s:MyThing type=s:A.S2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -767,7 +993,7 @@ mod option_methods {
|
||||
println!("{:?}", x4);
|
||||
|
||||
let x5 = MyOption::MySome(MyOption::<S>::MyNone());
|
||||
println!("{:?}", x5.flatten()); // MISSING: method=flatten
|
||||
println!("{:?}", x5.flatten()); // $ method=flatten
|
||||
|
||||
let x6 = MyOption::MySome(MyOption::<S>::MyNone());
|
||||
println!("{:?}", MyOption::<MyOption<S>>::flatten(x6));
|
||||
@@ -844,6 +1070,12 @@ mod method_call_type_conversion {
|
||||
let x6 = &S(S2);
|
||||
// explicit dereference
|
||||
println!("{:?}", (*x6).m1()); // $ method=m1
|
||||
|
||||
let x7 = S(&S2);
|
||||
// Non-implicit dereference with nested borrow in order to test that the
|
||||
// implicit dereference handling doesn't affect nested borrows.
|
||||
let t = x7.m1(); // $ method=m1 type=t:& type=t:&T.S2
|
||||
println!("{:?}", x7);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,7 @@ module ResolveTest implements TestSig {
|
||||
location = source.getLocation() and
|
||||
element = source.toString()
|
||||
|
|
||||
target = resolveMethodCallExpr(source) and
|
||||
target = source.(MethodCallExpr).getStaticTarget() and
|
||||
functionHasValue(target, value) and
|
||||
tag = "method"
|
||||
or
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
multipleMethodCallTargets
|
||||
| sqlx.rs:69:30:69:52 | unsafe_query_2.as_str() | file://:0:0:0:0 | fn as_str |
|
||||
| sqlx.rs:69:30:69:52 | unsafe_query_2.as_str() | file://:0:0:0:0 | fn as_str |
|
||||
| sqlx.rs:69:30:69:52 | unsafe_query_2.as_str() | file://:0:0:0:0 | fn as_str |
|
||||
| sqlx.rs:69:30:69:52 | unsafe_query_2.as_str() | file://:0:0:0:0 | fn as_str |
|
||||
| sqlx.rs:69:30:69:52 | unsafe_query_2.as_str() | file://:0:0:0:0 | fn as_str |
|
||||
| sqlx.rs:69:30:69:52 | unsafe_query_2.as_str() | file://:0:0:0:0 | fn as_str |
|
||||
| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | file://:0:0:0:0 | fn as_str |
|
||||
| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | file://:0:0:0:0 | fn as_str |
|
||||
| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | file://:0:0:0:0 | fn as_str |
|
||||
| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | file://:0:0:0:0 | fn as_str |
|
||||
| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | file://:0:0:0:0 | fn as_str |
|
||||
| sqlx.rs:80:29:80:51 | unsafe_query_2.as_str() | file://:0:0:0:0 | fn as_str |
|
||||
@@ -32,6 +32,33 @@ signature module InputSig1<LocationSig Location> {
|
||||
/** A type parameter. */
|
||||
class TypeParameter extends Type;
|
||||
|
||||
/**
|
||||
* A type abstraction. I.e., a place in the program where type variables are
|
||||
* introduced.
|
||||
*
|
||||
* Example in C#:
|
||||
* ```csharp
|
||||
* class C<A, B> : D<A, B> { }
|
||||
* // ^^^^^^ a type abstraction
|
||||
* ```
|
||||
*
|
||||
* Example in Rust:
|
||||
* ```rust
|
||||
* impl<A, B> Foo<A, B> { }
|
||||
* // ^^^^^^ a type abstraction
|
||||
* ```
|
||||
*/
|
||||
class TypeAbstraction {
|
||||
/** Gets a type parameter introduced by this abstraction. */
|
||||
TypeParameter getATypeParameter();
|
||||
|
||||
/** Gets a textual representation of this type abstraction. */
|
||||
string toString();
|
||||
|
||||
/** Gets the location of this type abstraction. */
|
||||
Location getLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique identifier of type parameter `tp`.
|
||||
*
|
||||
@@ -91,11 +118,9 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
predicate getRank = getTypeParameterId/1;
|
||||
}
|
||||
|
||||
private int getTypeParameterRank(TypeParameter tp) {
|
||||
tp = DenseRank<DenseRankInput>::denseRank(result)
|
||||
}
|
||||
int getRank(TypeParameter tp) { tp = DenseRank<DenseRankInput>::denseRank(result) }
|
||||
|
||||
string encode(TypeParameter tp) { result = getTypeParameterRank(tp).toString() }
|
||||
string encode(TypeParameter tp) { result = getRank(tp).toString() }
|
||||
|
||||
bindingset[s]
|
||||
TypeParameter decode(string s) { encode(result) = s }
|
||||
@@ -212,6 +237,23 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
TypePath cons(TypeParameter tp, TypePath suffix) { result = singleton(tp).append(suffix) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class that has a type tree associated with it.
|
||||
*
|
||||
* The type tree is represented by the `getTypeAt` predicate, which for every
|
||||
* path into the tree gives the type at that path.
|
||||
*/
|
||||
signature class HasTypeTreeSig {
|
||||
/** Gets the type at `path` in the type tree. */
|
||||
Type getTypeAt(TypePath path);
|
||||
|
||||
/** Gets a textual representation of this type. */
|
||||
string toString();
|
||||
|
||||
/** Gets the location of this type. */
|
||||
Location getLocation();
|
||||
}
|
||||
|
||||
/** Provides the input to `Make2`. */
|
||||
signature module InputSig2 {
|
||||
/** A type mention, for example a type annotation in a local variable declaration. */
|
||||
@@ -253,6 +295,67 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
* ```
|
||||
*/
|
||||
TypeMention getABaseTypeMention(Type t);
|
||||
|
||||
/**
|
||||
* Gets a type constraint on the type parameter `tp`, if any. All
|
||||
* instantiations of the type parameter must satisfy the constraint.
|
||||
*
|
||||
* For example, in
|
||||
* ```csharp
|
||||
* class GenericClass<T> : IComparable<GenericClass<T>>
|
||||
* // ^ `tp`
|
||||
* where T : IComparable<T> { }
|
||||
* // ^^^^^^^^^^^^^^ `result`
|
||||
* ```
|
||||
* the type parameter `T` has the constraint `IComparable<T>`.
|
||||
*/
|
||||
TypeMention getATypeParameterConstraint(TypeParameter tp);
|
||||
|
||||
/**
|
||||
* Holds if
|
||||
* - `abs` is a type abstraction that introduces type variables that are
|
||||
* free in `condition` and `constraint`,
|
||||
* - and for every instantiation of the type parameters from `abs` the
|
||||
* resulting `condition` satisifies the constraint given by `constraint`.
|
||||
*
|
||||
* Example in C#:
|
||||
* ```csharp
|
||||
* class C<T> : IComparable<C<T>> { }
|
||||
* // ^^^ `abs`
|
||||
* // ^^^^ `condition`
|
||||
* // ^^^^^^^^^^^^^^^^^ `constraint`
|
||||
* ```
|
||||
*
|
||||
* Example in Rust:
|
||||
* ```rust
|
||||
* impl<A> Trait<i64, A> for Type<String, A> { }
|
||||
* // ^^^ `abs` ^^^^^^^^^^^^^^^ `condition`
|
||||
* // ^^^^^^^^^^^^^ `constraint`
|
||||
* ```
|
||||
*
|
||||
* To see how `abs` changes the meaning of the type parameters that occur in
|
||||
* `condition`, consider the following examples in Rust:
|
||||
* ```rust
|
||||
* impl<T> Trait for T { }
|
||||
* // ^^^ `abs` ^ `condition`
|
||||
* // ^^^^^ `constraint`
|
||||
* ```
|
||||
* Here the meaning is "for all type parameters `T` it is the case that `T`
|
||||
* implements `Trait`". On the other hand, in
|
||||
* ```rust
|
||||
* fn foo<T: Trait>() { }
|
||||
* // ^ `condition`
|
||||
* // ^^^^^ `constraint`
|
||||
* ```
|
||||
* the meaning is "`T` implements `Trait`" where the constraint is only
|
||||
* valid for the specific `T`. Note that `condition` and `condition` are
|
||||
* identical in the two examples. To encode the difference, `abs` in the
|
||||
* first example should contain `T` whereas in the seconds example `abs`
|
||||
* should be empty.
|
||||
*/
|
||||
predicate conditionSatisfiesConstraint(
|
||||
TypeAbstraction abs, TypeMention condition, TypeMention constraint
|
||||
);
|
||||
}
|
||||
|
||||
module Make2<InputSig2 Input2> {
|
||||
@@ -265,8 +368,285 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
result = tm.resolveTypeAt(TypePath::nil())
|
||||
}
|
||||
|
||||
/** Provides the input to `IsInstantiationOf`. */
|
||||
signature module IsInstantiationOfInputSig<HasTypeTreeSig App> {
|
||||
/**
|
||||
* Holds if `abs` is a type abstraction, `tm` occurs in the scope of
|
||||
* `abs`, and `app` is potentially an application/instantiation of `abs`.
|
||||
*
|
||||
* For example:
|
||||
* ```rust
|
||||
* impl<A> Foo<A, A> {
|
||||
* // ^^^ `abs`
|
||||
* // ^^^^^^^^^ `tm`
|
||||
* fn bar(self) { ... }
|
||||
* }
|
||||
* // ...
|
||||
* foo.bar();
|
||||
* // ^^^ `app`
|
||||
* ```
|
||||
* Here `abs` introduces the type parameter `A` and `tm` occurs in the
|
||||
* scope of `abs` (i.e., `A` is bound in `tm` by `abs`). On the last line,
|
||||
* accessing the `bar` method of `foo` potentially instantiates the `impl`
|
||||
* block with a type argument for `A`.
|
||||
*/
|
||||
predicate potentialInstantiationOf(App app, TypeAbstraction abs, TypeMention tm);
|
||||
|
||||
/**
|
||||
* Holds if `constraint` might occur as the third argument of
|
||||
* `potentialInstantiationOf`. Defaults to simply projecting the third
|
||||
* argument of `potentialInstantiationOf`.
|
||||
*/
|
||||
default predicate relevantTypeMention(TypeMention tm) { potentialInstantiationOf(_, _, tm) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides functionality for determining if a type is a possible
|
||||
* instantiation of a type mention containing type parameters.
|
||||
*/
|
||||
module IsInstantiationOf<HasTypeTreeSig App, IsInstantiationOfInputSig<App> Input> {
|
||||
private import Input
|
||||
|
||||
/** Gets the `i`th path in `tm` per some arbitrary order. */
|
||||
pragma[nomagic]
|
||||
private TypePath getNthPath(TypeMention tm, int i) {
|
||||
result =
|
||||
rank[i + 1](TypePath path |
|
||||
exists(tm.resolveTypeAt(path)) and relevantTypeMention(tm)
|
||||
|
|
||||
path
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `app` is a possible instantiation of `tm` at `path`. That is
|
||||
* the type at `path` in `tm` is either a type parameter or equal to the
|
||||
* type at the same path in `app`.
|
||||
*/
|
||||
bindingset[app, abs, tm, path]
|
||||
private predicate satisfiesConcreteTypeAt(
|
||||
App app, TypeAbstraction abs, TypeMention tm, TypePath path
|
||||
) {
|
||||
exists(Type t |
|
||||
tm.resolveTypeAt(path) = t and
|
||||
if t = abs.getATypeParameter() then any() else app.getTypeAt(path) = t
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate satisfiesConcreteTypesFromIndex(
|
||||
App app, TypeAbstraction abs, TypeMention tm, int i
|
||||
) {
|
||||
potentialInstantiationOf(app, abs, tm) and
|
||||
satisfiesConcreteTypeAt(app, abs, tm, getNthPath(tm, i)) and
|
||||
// Recurse unless we are at the first path
|
||||
if i = 0 then any() else satisfiesConcreteTypesFromIndex(app, abs, tm, i - 1)
|
||||
}
|
||||
|
||||
/** Holds if all the concrete types in `tm` also occur in `app`. */
|
||||
pragma[nomagic]
|
||||
private predicate satisfiesConcreteTypes(App app, TypeAbstraction abs, TypeMention tm) {
|
||||
satisfiesConcreteTypesFromIndex(app, abs, tm, max(int i | exists(getNthPath(tm, i))))
|
||||
}
|
||||
|
||||
private TypeParameter getNthTypeParameter(TypeAbstraction abs, int i) {
|
||||
result =
|
||||
rank[i + 1](TypeParameter tp |
|
||||
tp = abs.getATypeParameter()
|
||||
|
|
||||
tp order by TypeParameter::getRank(tp)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path to the `i`th occurrence of `tp` within `tm` per some
|
||||
* arbitrary order, if any.
|
||||
*/
|
||||
private TypePath getNthTypeParameterPath(TypeMention tm, TypeParameter tp, int i) {
|
||||
result =
|
||||
rank[i + 1](TypePath path | tp = tm.resolveTypeAt(path) and relevantTypeMention(tm) | path)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate typeParametersEqualFromIndex(
|
||||
App app, TypeAbstraction abs, TypeMention tm, TypeParameter tp, Type t, int i
|
||||
) {
|
||||
satisfiesConcreteTypes(app, abs, tm) and
|
||||
exists(TypePath path |
|
||||
path = getNthTypeParameterPath(tm, tp, i) and
|
||||
t = app.getTypeAt(path) and
|
||||
if i = 0
|
||||
then
|
||||
// no need to compute this predicate if there is only one path
|
||||
exists(getNthTypeParameterPath(tm, tp, 1))
|
||||
else typeParametersEqualFromIndex(app, abs, tm, tp, t, i - 1)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate typeParametersEqual(
|
||||
App app, TypeAbstraction abs, TypeMention tm, TypeParameter tp
|
||||
) {
|
||||
satisfiesConcreteTypes(app, abs, tm) and
|
||||
tp = getNthTypeParameter(abs, _) and
|
||||
(
|
||||
not exists(getNthTypeParameterPath(tm, tp, _))
|
||||
or
|
||||
exists(int n | n = max(int i | exists(getNthTypeParameterPath(tm, tp, i))) |
|
||||
// If the largest index is 0, then there are no equalities to check as
|
||||
// the type parameter only occurs once.
|
||||
if n = 0 then any() else typeParametersEqualFromIndex(app, abs, tm, tp, _, n)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate typeParametersHaveEqualInstantiationFromIndex(
|
||||
App app, TypeAbstraction abs, TypeMention tm, int i
|
||||
) {
|
||||
exists(TypeParameter tp | tp = getNthTypeParameter(abs, i) |
|
||||
typeParametersEqual(app, abs, tm, tp) and
|
||||
if i = 0
|
||||
then any()
|
||||
else typeParametersHaveEqualInstantiationFromIndex(app, abs, tm, i - 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `app` is a possible instantiation of `tm`. That is, by making
|
||||
* appropriate substitutions for the free type parameters in `tm` given by
|
||||
* `abs`, it is possible to obtain `app`.
|
||||
*
|
||||
* For instance, if `A` and `B` are free type parameters we have:
|
||||
* - `Pair<int, string>` is an instantiation of `A`
|
||||
* - `Pair<int, string>` is an instantiation of `Pair<A, B>`
|
||||
* - `Pair<int, int>` is an instantiation of `Pair<A, A>`
|
||||
* - `Pair<int, bool>` is _not_ an instantiation of `Pair<A, A>`
|
||||
* - `Pair<int, string>` is _not_ an instantiation of `Pair<string, string>`
|
||||
*/
|
||||
predicate isInstantiationOf(App app, TypeAbstraction abs, TypeMention tm) {
|
||||
// We only need to check equality if the concrete types are satisfied.
|
||||
satisfiesConcreteTypes(app, abs, tm) and
|
||||
// Check if all the places where the same type parameter occurs in `tm`
|
||||
// are equal in `app`.
|
||||
//
|
||||
// TODO: As of now this only checks equality at the root of the types
|
||||
// instantiated for type parameters. So, for instance, `Pair<Vec<i64>, Vec<bool>>`
|
||||
// is mistakenly considered an instantiation of `Pair<A, A>`.
|
||||
(
|
||||
not exists(getNthTypeParameter(abs, _))
|
||||
or
|
||||
exists(int n | n = max(int i | exists(getNthTypeParameter(abs, i))) |
|
||||
typeParametersHaveEqualInstantiationFromIndex(app, abs, tm, n)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides logic related to base types. */
|
||||
private module BaseTypes {
|
||||
/**
|
||||
* Holds if, when `tm1` is considered an instantiation of `tm2`, then at
|
||||
* the type parameter `tp` it has the type `t` at `path`.
|
||||
*
|
||||
* For instance, if the type `Map<int, List<int>>` is considered an
|
||||
* instantion of `Map<K, V>` then it has the type `int` at `K` and the
|
||||
* type `List<int>` at `V`.
|
||||
*/
|
||||
bindingset[tm1, tm2]
|
||||
private predicate instantiatesWith(
|
||||
TypeMention tm1, TypeMention tm2, TypeParameter tp, TypePath path, Type t
|
||||
) {
|
||||
exists(TypePath prefix |
|
||||
tm2.resolveTypeAt(prefix) = tp and t = tm1.resolveTypeAt(prefix.append(path))
|
||||
)
|
||||
}
|
||||
|
||||
final private class FinalTypeMention = TypeMention;
|
||||
|
||||
final private class TypeMentionTypeTree extends FinalTypeMention {
|
||||
Type getTypeAt(TypePath path) { result = this.resolveTypeAt(path) }
|
||||
}
|
||||
|
||||
private module IsInstantiationOfInput implements
|
||||
IsInstantiationOfInputSig<TypeMentionTypeTree>
|
||||
{
|
||||
pragma[nomagic]
|
||||
private predicate typeCondition(Type type, TypeAbstraction abs, TypeMentionTypeTree lhs) {
|
||||
conditionSatisfiesConstraint(abs, lhs, _) and type = resolveTypeMentionRoot(lhs)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate typeConstraint(Type type, TypeMentionTypeTree rhs) {
|
||||
conditionSatisfiesConstraint(_, _, rhs) and type = resolveTypeMentionRoot(rhs)
|
||||
}
|
||||
|
||||
predicate potentialInstantiationOf(
|
||||
TypeMentionTypeTree condition, TypeAbstraction abs, TypeMention constraint
|
||||
) {
|
||||
exists(Type type |
|
||||
typeConstraint(type, condition) and typeCondition(type, abs, constraint)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type mention `condition` satisfies `constraint` with the
|
||||
* type `t` at the path `path`.
|
||||
*/
|
||||
predicate conditionSatisfiesConstraintTypeAt(
|
||||
TypeAbstraction abs, TypeMention condition, TypeMention constraint, TypePath path, Type t
|
||||
) {
|
||||
// base case
|
||||
conditionSatisfiesConstraint(abs, condition, constraint) and
|
||||
constraint.resolveTypeAt(path) = t
|
||||
or
|
||||
// recursive case
|
||||
exists(TypeAbstraction midAbs, TypeMention midSup, TypeMention midSub |
|
||||
conditionSatisfiesConstraint(abs, condition, midSup) and
|
||||
// NOTE: `midAbs` describe the free type variables in `midSub`, hence
|
||||
// we use that for instantiation check.
|
||||
IsInstantiationOf<TypeMentionTypeTree, IsInstantiationOfInput>::isInstantiationOf(midSup,
|
||||
midAbs, midSub)
|
||||
|
|
||||
conditionSatisfiesConstraintTypeAt(midAbs, midSub, constraint, path, t) and
|
||||
not t = midAbs.getATypeParameter()
|
||||
or
|
||||
exists(TypePath prefix, TypePath suffix, TypeParameter tp |
|
||||
tp = midAbs.getATypeParameter() and
|
||||
conditionSatisfiesConstraintTypeAt(midAbs, midSub, constraint, prefix, tp) and
|
||||
instantiatesWith(midSup, midSub, tp, suffix, t) and
|
||||
path = prefix.append(suffix)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if its possible for a type with `conditionRoot` at the root to
|
||||
* satisfy a constraint with `constraintRoot` at the root through `abs`,
|
||||
* `condition`, and `constraint`.
|
||||
*/
|
||||
predicate rootTypesSatisfaction(
|
||||
Type conditionRoot, Type constraintRoot, TypeAbstraction abs, TypeMention condition,
|
||||
TypeMention constraint
|
||||
) {
|
||||
conditionSatisfiesConstraintTypeAt(abs, condition, constraint, _, _) and
|
||||
conditionRoot = resolveTypeMentionRoot(condition) and
|
||||
constraintRoot = resolveTypeMentionRoot(constraint)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of ways in which it is possible for a type with
|
||||
* `conditionRoot` at the root to satisfy a constraint with
|
||||
* `constraintRoot` at the root.
|
||||
*/
|
||||
int countConstraintImplementations(Type conditionRoot, Type constraintRoot) {
|
||||
result =
|
||||
strictcount(TypeAbstraction abs, TypeMention tm, TypeMention constraint |
|
||||
rootTypesSatisfaction(conditionRoot, constraintRoot, abs, tm, constraint)
|
||||
|
|
||||
constraint
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `baseMention` is a (transitive) base type mention of `sub`,
|
||||
* and `t` is mentioned (implicitly) at `path` inside `baseMention`. For
|
||||
@@ -528,24 +908,19 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
* Holds if inferring types at `a` might depend on the type at `path` of
|
||||
* `apos` having `base` as a transitive base type.
|
||||
*/
|
||||
private predicate relevantAccess(Access a, AccessPosition apos, TypePath path, Type base) {
|
||||
private predicate relevantAccess(Access a, AccessPosition apos, Type base) {
|
||||
exists(Declaration target, DeclarationPosition dpos |
|
||||
adjustedAccessType(a, apos, target, _, _) and
|
||||
accessDeclarationPositionMatch(apos, dpos)
|
||||
|
|
||||
path.isEmpty() and declarationBaseType(target, dpos, base, _, _)
|
||||
or
|
||||
typeParameterConstraintHasTypeParameter(target, dpos, path, _, base, _, _)
|
||||
accessDeclarationPositionMatch(apos, dpos) and
|
||||
declarationBaseType(target, dpos, base, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Type inferTypeAt(
|
||||
Access a, AccessPosition apos, TypePath prefix, TypeParameter tp, TypePath suffix
|
||||
) {
|
||||
relevantAccess(a, apos, prefix, _) and
|
||||
private Type inferTypeAt(Access a, AccessPosition apos, TypeParameter tp, TypePath suffix) {
|
||||
relevantAccess(a, apos, _) and
|
||||
exists(TypePath path0 |
|
||||
result = a.getInferredType(apos, prefix.append(path0)) and
|
||||
result = a.getInferredType(apos, path0) and
|
||||
path0.isCons(tp, suffix)
|
||||
)
|
||||
}
|
||||
@@ -581,24 +956,134 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
* `Base<C<T3>>` | `"T2.T1"` | ``C`1``
|
||||
* `Base<C<T3>>` | `"T2.T1.T1"` | `int`
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate hasBaseTypeMention(
|
||||
Access a, AccessPosition apos, TypePath pathToSub, TypeMention baseMention, TypePath path,
|
||||
Type t
|
||||
Access a, AccessPosition apos, TypeMention baseMention, TypePath path, Type t
|
||||
) {
|
||||
relevantAccess(a, apos, pathToSub, resolveTypeMentionRoot(baseMention)) and
|
||||
exists(Type sub | sub = a.getInferredType(apos, pathToSub) |
|
||||
relevantAccess(a, apos, resolveTypeMentionRoot(baseMention)) and
|
||||
exists(Type sub | sub = a.getInferredType(apos, TypePath::nil()) |
|
||||
baseTypeMentionHasNonTypeParameterAt(sub, baseMention, path, t)
|
||||
or
|
||||
exists(TypePath prefix, TypePath suffix, TypeParameter tp |
|
||||
baseTypeMentionHasTypeParameterAt(sub, baseMention, prefix, tp) and
|
||||
t = inferTypeAt(a, apos, pathToSub, tp, suffix) and
|
||||
t = inferTypeAt(a, apos, tp, suffix) and
|
||||
path = prefix.append(suffix)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module AccessConstraint {
|
||||
predicate relevantAccessConstraint(
|
||||
Access a, AccessPosition apos, TypePath path, Type constraint
|
||||
) {
|
||||
exists(DeclarationPosition dpos |
|
||||
accessDeclarationPositionMatch(apos, dpos) and
|
||||
typeParameterConstraintHasTypeParameter(a.getTarget(), dpos, path, _, constraint, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TRelevantAccess =
|
||||
MkRelevantAccess(Access a, AccessPosition apos, TypePath path) {
|
||||
relevantAccessConstraint(a, apos, path, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* If the access `a` for `apos` and `path` has an inferred type which
|
||||
* type inference requires to satisfy some constraint.
|
||||
*/
|
||||
private class RelevantAccess extends MkRelevantAccess {
|
||||
Access a;
|
||||
AccessPosition apos;
|
||||
TypePath path;
|
||||
|
||||
RelevantAccess() { this = MkRelevantAccess(a, apos, path) }
|
||||
|
||||
Type getTypeAt(TypePath suffix) { a.getInferredType(apos, path.append(suffix)) = result }
|
||||
|
||||
/** Holds if this relevant access has the type `type` and should satisfy `constraint`. */
|
||||
predicate hasTypeConstraint(Type type, Type constraint) {
|
||||
type = a.getInferredType(apos, path) and
|
||||
relevantAccessConstraint(a, apos, path, constraint)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
result = a.toString() + ", " + apos.toString() + ", " + path.toString()
|
||||
}
|
||||
|
||||
Location getLocation() { result = a.getLocation() }
|
||||
}
|
||||
|
||||
private module IsInstantiationOfInput implements IsInstantiationOfInputSig<RelevantAccess> {
|
||||
predicate potentialInstantiationOf(
|
||||
RelevantAccess at, TypeAbstraction abs, TypeMention cond
|
||||
) {
|
||||
exists(Type constraint, Type type |
|
||||
at.hasTypeConstraint(type, constraint) and
|
||||
rootTypesSatisfaction(type, constraint, abs, cond, _) and
|
||||
// We only need to check instantiations where there are multiple candidates.
|
||||
countConstraintImplementations(type, constraint) > 1
|
||||
)
|
||||
}
|
||||
|
||||
predicate relevantTypeMention(TypeMention constraint) {
|
||||
rootTypesSatisfaction(_, _, _, constraint, _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `at` satisfies `constraint` through `abs`, `sub`, and `constraintMention`.
|
||||
*/
|
||||
private predicate hasConstraintMention(
|
||||
RelevantAccess at, TypeAbstraction abs, TypeMention sub, Type constraint,
|
||||
TypeMention constraintMention
|
||||
) {
|
||||
exists(Type type | at.hasTypeConstraint(type, constraint) |
|
||||
not exists(countConstraintImplementations(type, constraint)) and
|
||||
conditionSatisfiesConstraintTypeAt(abs, sub, constraintMention, _, _) and
|
||||
resolveTypeMentionRoot(sub) = abs.getATypeParameter() and
|
||||
constraint = resolveTypeMentionRoot(constraintMention)
|
||||
or
|
||||
countConstraintImplementations(type, constraint) > 0 and
|
||||
rootTypesSatisfaction(type, constraint, abs, sub, constraintMention) and
|
||||
// When there are multiple ways the type could implement the
|
||||
// constraint we need to find the right implementation, which is the
|
||||
// one where the type instantiates the precondition.
|
||||
if countConstraintImplementations(type, constraint) > 1
|
||||
then
|
||||
IsInstantiationOf<RelevantAccess, IsInstantiationOfInput>::isInstantiationOf(at, abs,
|
||||
sub)
|
||||
else any()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type at `a`, `apos`, and `path` satisfies the constraint
|
||||
* `constraint` with the type `t` at `path`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate satisfiesConstraintTypeMention(
|
||||
Access a, AccessPosition apos, TypePath prefix, Type constraint, TypePath path, Type t
|
||||
) {
|
||||
exists(
|
||||
RelevantAccess at, TypeAbstraction abs, TypeMention sub, Type t0, TypePath prefix0,
|
||||
TypeMention constraintMention
|
||||
|
|
||||
at = MkRelevantAccess(a, apos, prefix) and
|
||||
hasConstraintMention(at, abs, sub, constraint, constraintMention) and
|
||||
conditionSatisfiesConstraintTypeAt(abs, sub, constraintMention, prefix0, t0)
|
||||
|
|
||||
not t0 = abs.getATypeParameter() and t = t0 and path = prefix0
|
||||
or
|
||||
t0 = abs.getATypeParameter() and
|
||||
exists(TypePath path3, TypePath suffix |
|
||||
sub.resolveTypeAt(path3) = t0 and
|
||||
at.getTypeAt(path3.append(suffix)) = t and
|
||||
path = prefix0.append(suffix)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type of `a` at `apos` has the base type `base`, and when
|
||||
* viewed as an element of that type has the type `t` at `path`.
|
||||
@@ -608,7 +1093,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
Access a, AccessPosition apos, Type base, TypePath path, Type t
|
||||
) {
|
||||
exists(TypeMention tm |
|
||||
AccessBaseType::hasBaseTypeMention(a, apos, TypePath::nil(), tm, path, t) and
|
||||
AccessBaseType::hasBaseTypeMention(a, apos, tm, path, t) and
|
||||
base = resolveTypeMentionRoot(tm)
|
||||
)
|
||||
}
|
||||
@@ -712,7 +1197,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
tp1 != tp2 and
|
||||
tp1 = target.getDeclaredType(dpos, path1) and
|
||||
exists(TypeMention tm |
|
||||
tm = getABaseTypeMention(tp1) and
|
||||
tm = getATypeParameterConstraint(tp1) and
|
||||
tm.resolveTypeAt(path2) = tp2 and
|
||||
constraint = resolveTypeMentionRoot(tm)
|
||||
)
|
||||
@@ -725,13 +1210,14 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
not exists(getTypeArgument(a, target, tp, _)) and
|
||||
target = a.getTarget() and
|
||||
exists(
|
||||
TypeMention base, AccessPosition apos, DeclarationPosition dpos, TypePath pathToTp,
|
||||
Type constraint, AccessPosition apos, DeclarationPosition dpos, TypePath pathToTp,
|
||||
TypePath pathToTp2
|
||||
|
|
||||
accessDeclarationPositionMatch(apos, dpos) and
|
||||
typeParameterConstraintHasTypeParameter(target, dpos, pathToTp2, _,
|
||||
resolveTypeMentionRoot(base), pathToTp, tp) and
|
||||
AccessBaseType::hasBaseTypeMention(a, apos, pathToTp2, base, pathToTp.append(path), t)
|
||||
typeParameterConstraintHasTypeParameter(target, dpos, pathToTp2, _, constraint, pathToTp,
|
||||
tp) and
|
||||
AccessConstraint::satisfiesConstraintTypeMention(a, apos, pathToTp2, constraint,
|
||||
pathToTp.append(path), t)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -749,7 +1235,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
// We can infer the type of `tp` by going up the type hiearchy
|
||||
baseTypeMatch(a, target, path, t, tp)
|
||||
or
|
||||
// We can infer the type of `tp` by a type bound
|
||||
// We can infer the type of `tp` by a type constraint
|
||||
typeConstraintBaseTypeMatch(a, target, path, t, tp)
|
||||
}
|
||||
|
||||
@@ -811,7 +1297,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides consitency checks. */
|
||||
/** Provides consistency checks. */
|
||||
module Consistency {
|
||||
query predicate missingTypeParameterId(TypeParameter tp) {
|
||||
not exists(getTypeParameterId(tp))
|
||||
|
||||
Reference in New Issue
Block a user