Merge branch 'main' into mapfix

This commit is contained in:
Geoffrey White
2026-03-04 10:20:04 +00:00
committed by GitHub
829 changed files with 70632 additions and 32231 deletions

View File

@@ -8,7 +8,8 @@ module Impl {
TPositionalArgumentPosition(int i) {
i in [0 .. max([any(ParamList l).getNumberOfParams(), any(ArgList l).getNumberOfArgs()]) - 1]
} or
TSelfArgumentPosition()
TSelfArgumentPosition() or
TTypeQualifierArgumentPosition()
/** An argument position in a call. */
class ArgumentPosition extends TArgumentPosition {
@@ -16,13 +17,28 @@ module Impl {
int asPosition() { this = TPositionalArgumentPosition(result) }
/** Holds if this call position is a self argument. */
predicate isSelf() { this instanceof TSelfArgumentPosition }
predicate isSelf() { this = TSelfArgumentPosition() }
/**
* Holds if this call position is a type qualifier, that is, not an actual
* argument, but rather an annotation that is needed to resolve the call target,
* just like actual arguments may be needed to resolve the call target.
*
* Example:
* ```rust
* Vec<i64>::new();
* // ^^^^^^^^ type qualifier
* ```
*/
predicate isTypeQualifier() { this = TTypeQualifierArgumentPosition() }
/** Gets a string representation of this argument position. */
string toString() {
result = this.asPosition().toString()
or
this.isSelf() and result = "self"
or
this.isTypeQualifier() and result = "type qualifier"
}
}

View File

@@ -107,7 +107,7 @@ class SuccessorKind extends TSuccessorKind {
}
pragma[nomagic]
private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind kind) {
private ItemNode getAChildSuccessor0(ItemNode item, string name, SuccessorKind kind) {
item = result.getImmediateParent() and
name = result.getName() and
// Associated items in `impl` and `trait` blocks are handled elsewhere
@@ -116,7 +116,7 @@ private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind ki
if result instanceof TypeParam
then kind.isInternal()
else
if result.isPublic()
if result.isPublic() or item instanceof SourceFile
then kind.isBoth()
else kind.isInternal()
or
@@ -130,6 +130,41 @@ private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind ki
result = item
}
pragma[nomagic]
private NamedItemNode getANamedNonModuleChildSuccessor(
ItemNode item, string name, Namespace ns, int startline, int startcolumn, int endline,
int endcolumn
) {
result.getLocation().hasLocationInfo(_, startline, startcolumn, endline, endcolumn) and
result = getAChildSuccessor0(item, name, _) and
ns = result.getNamespace() and
not result instanceof ModuleItemNode
}
pragma[nomagic]
private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind kind) {
result = getAChildSuccessor0(item, name, kind) and
// In valid Rust code, there cannot be multiple children with the same name and namespace,
// but to safeguard against combinatorial explosions in invalid code, we always pick the
// last child, except for modules, where we take the union.
(
not result instanceof NamedItemNode
or
result instanceof ModuleItemNode
or
exists(Namespace ns |
result =
max(NamedItemNode i, int startline, int startcolumn, int endline, int endcolumn |
i =
getANamedNonModuleChildSuccessor(item, name, ns, startline, startcolumn, endline,
endcolumn)
|
i order by startline, startcolumn, endline, endcolumn
)
)
)
}
private module UseOption = Option<Use>;
private class UseOption = UseOption::Option;
@@ -288,10 +323,6 @@ abstract class ItemNode extends Locatable {
cached
ItemNode getASuccessor(string name, SuccessorKind kind, UseOption useOpt) {
Stages::PathResolutionStage::ref() and
sourceFileEdge(this, name, result) and
kind.isBoth() and
useOpt.isNone()
or
result = getAChildSuccessor(this, name, kind) and
useOpt.isNone()
or
@@ -471,6 +502,8 @@ abstract class ItemNode extends Locatable {
Location getLocation() { result = super.getLocation() }
}
abstract class NamedItemNode extends ItemNode { }
abstract class TypeItemNode extends ItemNode { }
/** A module or a source file. */
@@ -509,7 +542,7 @@ private class SourceFileItemNode extends ModuleLikeNode instanceof SourceFile {
override string getCanonicalPath(Crate c) { none() }
}
class CrateItemNode extends ItemNode instanceof Crate {
class CrateItemNode extends NamedItemNode instanceof Crate {
/**
* Gets the source file that defines this crate.
*/
@@ -565,7 +598,7 @@ class CrateItemNode extends ItemNode instanceof Crate {
override string getCanonicalPath(Crate c) { c = this and result = Crate.super.getName() }
}
class ExternCrateItemNode extends ItemNode instanceof ExternCrate {
class ExternCrateItemNode extends NamedItemNode instanceof ExternCrate {
override string getName() {
result = super.getRename().getName().getText()
or
@@ -573,7 +606,7 @@ class ExternCrateItemNode extends ItemNode instanceof ExternCrate {
result = super.getIdentifier().getText()
}
override Namespace getNamespace() { none() }
override Namespace getNamespace() { result.isType() }
override Visibility getVisibility() { result = ExternCrate.super.getVisibility() }
@@ -587,7 +620,7 @@ class ExternCrateItemNode extends ItemNode instanceof ExternCrate {
}
/** An item that can occur in a trait or an `impl` block. */
abstract private class AssocItemNode extends ItemNode instanceof AssocItem {
abstract private class AssocItemNode extends NamedItemNode instanceof AssocItem {
/** Holds if this associated item has an implementation. */
abstract predicate hasImplementation();
@@ -626,7 +659,7 @@ private class ConstItemNode extends AssocItemNode instanceof Const {
override TypeParam getTypeParam(int i) { none() }
}
private class TypeItemTypeItemNode extends TypeItemNode instanceof TypeItem {
private class TypeItemTypeItemNode extends NamedItemNode, TypeItemNode instanceof TypeItem {
override string getName() { result = TypeItem.super.getName().getText() }
override Namespace getNamespace() { result.isType() }
@@ -659,7 +692,7 @@ private class TypeItemTypeItemNode extends TypeItemNode instanceof TypeItem {
}
/** An item that can be referenced with arguments. */
abstract class ParameterizableItemNode extends ItemNode {
abstract class ParameterizableItemNode extends NamedItemNode {
/** Gets the arity this item. */
abstract int getArity();
}
@@ -911,7 +944,7 @@ private class ImplTraitTypeReprItemNodeImpl extends ImplTraitTypeReprItemNode {
ItemNode resolveABoundCand() { result = resolvePathCand(this.getABoundPath()) }
}
private class ModuleItemNode extends ModuleLikeNode instanceof Module {
private class ModuleItemNode extends NamedItemNode, ModuleLikeNode instanceof Module {
override string getName() { result = Module.super.getName().getText() }
override Namespace getNamespace() { result.isType() }
@@ -929,7 +962,7 @@ private class ModuleItemNode extends ModuleLikeNode instanceof Module {
(
exists(SourceFile f |
fileImport(this, f) and
sourceFileEdge(f, _, child)
child = getAChildSuccessor(f, _, _)
)
or
this = child.getImmediateParent()
@@ -1001,7 +1034,7 @@ private class StructItemNode extends TypeItemTypeItemNode, ParameterizableItemNo
}
}
final class TraitItemNode extends ImplOrTraitItemNode, TypeItemNode instanceof Trait {
final class TraitItemNode extends ImplOrTraitItemNode, NamedItemNode, TypeItemNode instanceof Trait {
pragma[nomagic]
Path getABoundPath() { result = super.getATypeBound().getTypeRepr().(PathTypeRepr).getPath() }
@@ -1126,7 +1159,7 @@ private class BlockExprItemNode extends ItemNode instanceof BlockExpr {
pragma[nomagic]
private Path getWherePredPath(WherePred wp) { result = wp.getTypeRepr().(PathTypeRepr).getPath() }
final class TypeParamItemNode extends TypeItemNode instanceof TypeParam {
final class TypeParamItemNode extends NamedItemNode, TypeItemNode instanceof TypeParam {
/** Gets a where predicate for this type parameter, if any */
pragma[nomagic]
private WherePred getAWherePred() {
@@ -1214,7 +1247,7 @@ final private class TypeParamItemNodeImpl extends TypeParamItemNode instanceof T
ItemNode resolveABoundCand() { result = resolvePathCand(this.getABoundPathCand()) }
}
abstract private class MacroItemNode extends ItemNode {
abstract private class MacroItemNode extends NamedItemNode {
override Namespace getNamespace() { result.isMacro() }
override TypeParam getTypeParam(int i) { none() }
@@ -1256,12 +1289,6 @@ private class MacroDefItemNode extends MacroItemNode instanceof MacroDef {
override Attr getAnAttr() { result = MacroDef.super.getAnAttr() }
}
/** Holds if `item` has the name `name` and is a top-level item inside `f`. */
private predicate sourceFileEdge(SourceFile f, string name, ItemNode item) {
item = f.(ItemNode).getADescendant() and
name = item.getName()
}
/** Holds if `f` is available as `mod name;` inside `folder`. */
pragma[nomagic]
private predicate fileModule(SourceFile f, string name, Folder folder) {

View File

@@ -0,0 +1,64 @@
/**
* Provides classes and helper predicates for associated types.
*/
private import rust
private import codeql.rust.internal.PathResolution
private import TypeMention
private import Type
private import TypeInference
/** An associated type, that is, a type alias in a trait block. */
final class AssocType extends TypeAlias {
Trait trait;
AssocType() { this = trait.getAssocItemList().getAnAssocItem() }
Trait getTrait() { result = trait }
string getText() { result = this.getName().getText() }
}
/** Gets an associated type of `trait` or of a supertrait of `trait`. */
AssocType getTraitAssocType(Trait trait) { result.getTrait() = trait.getSupertrait*() }
/** Holds if `path` is of the form `<type as trait>::name` */
pragma[nomagic]
predicate pathTypeAsTraitAssoc(Path path, TypeRepr typeRepr, Path traitPath, string name) {
exists(PathSegment segment |
segment = path.getQualifier().getSegment() and
typeRepr = segment.getTypeRepr() and
traitPath = segment.getTraitTypeRepr().getPath() and
name = path.getText()
)
}
/**
* Holds if `assoc` is accessed on `tp` in `path`.
*
* That is, this is the case when `path` is of the form `<tp as
* Trait>::AssocType` or `tp::AssocType`; and `AssocType` resolves to `assoc`.
*/
predicate tpAssociatedType(TypeParam tp, AssocType assoc, Path path) {
resolvePath(path.getQualifier()) = tp and
resolvePath(path) = assoc
or
exists(PathTypeRepr typeRepr, Path traitPath, string name |
pathTypeAsTraitAssoc(path, typeRepr, traitPath, name) and
tp = resolvePath(typeRepr.getPath()) and
assoc = resolvePath(traitPath).(TraitItemNode).getAssocItem(name)
)
}
/**
* Holds if `bound` is a type bound for `tp` that gives rise to `assoc` being
* present for `tp`.
*/
predicate tpBoundAssociatedType(
TypeParam tp, TypeBound bound, Path path, TraitItemNode trait, AssocType assoc
) {
bound = tp.getATypeBound() and
path = bound.getTypeRepr().(PathTypeRepr).getPath() and
trait = resolvePath(path) and
assoc = getTraitAssocType(trait)
}

View File

@@ -20,13 +20,12 @@ class DerefImplItemNode extends ImplItemNode {
Type resolveSelfTypeAt(TypePath path) { result = resolveImplSelfTypeAt(this, path) }
/**
* Holds if the target type of the dereference implemention mentions a type
* parameter at `path`.
* Holds if the target type of the dereference implementation mentions type
* parameter `tp` at `path`.
*/
pragma[nomagic]
predicate targetHasTypeParameterAt(TypePath path) {
this.getAssocItem("Target").(TypeAlias).getTypeRepr().(TypeMention).getTypeAt(path) instanceof
TypeParameter
predicate targetHasTypeParameterAt(TypePath path, TypeParameter tp) {
tp = this.getAssocItem("Target").(TypeAlias).getTypeRepr().(TypeMention).getTypeAt(path)
}
/** Gets the first type parameter of the type being implemented, if any. */

View File

@@ -77,32 +77,23 @@ pragma[nomagic]
private predicate implHasSibling(ImplItemNode impl, Trait trait) { implSiblings(trait, impl, _) }
/**
* Holds if type parameter `tp` of `trait` occurs in the function `f` with the name
* `functionName` at position `pos` and path `path`.
*
* Note that `pos` can also be the special `return` position, which is sometimes
* needed to disambiguate associated function calls like `Default::default()`
* (in this case, `tp` is the special `Self` type parameter).
*/
bindingset[trait]
pragma[inline_late]
predicate traitTypeParameterOccurrence(
TraitItemNode trait, Function f, string functionName, FunctionPosition pos, TypePath path,
TypeParameter tp
) {
f = trait.getASuccessor(functionName) and
tp = getAssocFunctionTypeAt(f, trait, pos, path) and
tp = trait.(TraitTypeAbstraction).getATypeParameter()
}
/**
* Holds if resolving the function `f` in `impl` with the name `functionName`
* requires inspecting the type of applied _arguments_ at position `pos` in
* order to determine whether it is the correct resolution.
* Holds if `f` is a function declared inside `trait`, and the type of `f` at
* `pos` and `path` is `traitTp`, which is a type parameter of `trait`.
*/
pragma[nomagic]
predicate functionResolutionDependsOnArgument(
ImplItemNode impl, Function f, FunctionPosition pos, TypePath path, Type type
predicate traitTypeParameterOccurrence(
TraitItemNode trait, Function f, string functionName, FunctionPosition pos, TypePath path,
TypeParameter traitTp
) {
f = trait.getAssocItem(functionName) and
traitTp = getAssocFunctionTypeAt(f, trait, pos, path) and
traitTp = trait.(TraitTypeAbstraction).getATypeParameter()
}
pragma[nomagic]
private predicate functionResolutionDependsOnArgumentCand(
ImplItemNode impl, Function f, string functionName, TypeParameter traitTp, FunctionPosition pos,
TypePath path
) {
/*
* As seen in the example below, when an implementation has a sibling for a
@@ -129,11 +120,114 @@ predicate functionResolutionDependsOnArgument(
* method. In that case we will still resolve several methods.
*/
exists(TraitItemNode trait, string functionName |
exists(TraitItemNode trait |
implHasSibling(impl, trait) and
traitTypeParameterOccurrence(trait, _, functionName, pos, path, _) and
type = getAssocFunctionTypeAt(f, impl, pos, path) and
traitTypeParameterOccurrence(trait, _, functionName, pos, path, traitTp) and
f = impl.getASuccessor(functionName) and
not pos.isSelfOrTypeQualifier()
)
}
private predicate functionResolutionDependsOnPositionalArgumentCand(
ImplItemNode impl, Function f, string functionName, TypeParameter traitTp
) {
exists(FunctionPosition pos |
functionResolutionDependsOnArgumentCand(impl, f, functionName, traitTp, pos, _) and
pos.isPosition()
)
}
pragma[nomagic]
private Type getAssocFunctionNonTypeParameterTypeAt(
ImplItemNode impl, Function f, FunctionPosition pos, TypePath path
) {
result = getAssocFunctionTypeAt(f, impl, pos, path) and
not result instanceof TypeParameter
}
/**
* Holds if `f` inside `impl` has a sibling implementation inside `sibling`, where
* those two implementations agree on the instantiation of `traitTp`, which occurs
* in a positional position inside `f`.
*/
pragma[nomagic]
private predicate hasEquivalentPositionalSibling(
ImplItemNode impl, ImplItemNode sibling, Function f, TypeParameter traitTp
) {
exists(string functionName, FunctionPosition pos, TypePath path |
functionResolutionDependsOnArgumentCand(impl, f, functionName, traitTp, pos, path) and
pos.isPosition()
|
exists(Function f1 |
implSiblings(_, impl, sibling) and
f1 = sibling.getASuccessor(functionName)
|
forall(TypePath path0, Type t |
t = getAssocFunctionNonTypeParameterTypeAt(impl, f, pos, path0) and
path = path0.getAPrefix()
|
t = getAssocFunctionNonTypeParameterTypeAt(sibling, f1, pos, path0)
) and
forall(TypePath path0, Type t |
t = getAssocFunctionNonTypeParameterTypeAt(sibling, f1, pos, path0) and
path = path0.getAPrefix()
|
t = getAssocFunctionNonTypeParameterTypeAt(impl, f, pos, path0)
)
)
)
}
/**
* Holds if resolving the function `f` in `impl` requires inspecting the type
* of applied _arguments_ or possibly knowing the return type.
*
* `traitTp` is a type parameter of the trait being implemented by `impl`, and
* we need to check that the type of `f` corresponding to `traitTp` is satisfied
* at any one of the positions `pos` in which that type occurs in `f`.
*
* Type parameters that only occur in return positions are only included when
* all other type parameters that occur in a positional position are insufficient
* to disambiguate.
*
* Example:
*
* ```rust
* trait Trait1<T1> {
* fn f(self, x: T1) -> T1;
* }
*
* impl Trait1<i32> for i32 {
* fn f(self, x: i32) -> i32 { 0 } // f1
* }
*
* impl Trait1<i64> for i32 {
* fn f(self, x: i64) -> i64 { 0 } // f2
* }
* ```
*
* The type for `T1` above occurs in both a positional position and a return position
* in `f`, so both may be used to disambiguate between `f1` and `f2`. That is, `f(0i32)`
* is sufficient to resolve to `f1`, and so is `let y: i64 = f(Default::default())`.
*/
pragma[nomagic]
predicate functionResolutionDependsOnArgument(
ImplItemNode impl, Function f, TypeParameter traitTp, FunctionPosition pos
) {
exists(string functionName |
functionResolutionDependsOnArgumentCand(impl, f, functionName, traitTp, pos, _)
|
if functionResolutionDependsOnPositionalArgumentCand(impl, f, functionName, traitTp)
then any()
else
// `traitTp` only occurs in return position; check that it is indeed needed for disambiguation
exists(ImplItemNode sibling |
implSiblings(_, impl, sibling) and
forall(TypeParameter otherTraitTp |
functionResolutionDependsOnPositionalArgumentCand(impl, f, functionName, otherTraitTp)
|
hasEquivalentPositionalSibling(impl, sibling, f, otherTraitTp)
)
)
)
}

View File

@@ -23,6 +23,10 @@ class FunctionPosition extends TFunctionPosition {
ArgumentPosition asArgumentPosition() { this = TArgumentFunctionPosition(result) }
predicate isTypeQualifier() { this.asArgumentPosition().isTypeQualifier() }
predicate isSelfOrTypeQualifier() { this.isSelf() or this.isTypeQualifier() }
predicate isReturn() { this = TReturnFunctionPosition() }
/** Gets the corresponding position when `f` is invoked via a function call. */
@@ -82,7 +86,9 @@ private newtype TAssocFunctionType =
// through `i`. This ensures that `parent` is either a supertrait of `i` or
// `i` in an `impl` block implementing `parent`.
(parent = i or BaseTypes::rootTypesSatisfaction(_, TTrait(parent), i, _, _)) and
exists(pos.getTypeMention(f))
// We always include the type qualifier position, even for non-methods, where it is used
// to match type qualifiers against the `impl` or trait type, such as in `Vec::new`.
(exists(pos.getTypeMention(f)) or pos.isTypeQualifier())
}
bindingset[abs, constraint, tp]
@@ -114,6 +120,10 @@ Type getAssocFunctionTypeAt(Function f, ImplOrTraitItemNode i, FunctionPosition
else result = getTraitConstraintTypeAt(i, constraint, tp, suffix)
)
)
or
f = i.getASuccessor(_) and
pos.isTypeQualifier() and
result = resolveImplOrTraitType(i, path)
}
/**
@@ -183,16 +193,19 @@ class AssocFunctionType extends MkAssocFunctionType {
)
}
private TypeMention getTypeMention() {
exists(Function f, FunctionPosition pos |
this.appliesTo(f, _, pos) and
private AstNode getIdentifyingNode() {
exists(Function f, ImplOrTraitItemNode i, FunctionPosition pos | this.appliesTo(f, i, pos) |
result = pos.getTypeMention(f)
or
pos.isSelf() and
not f.hasSelfParam() and
result = [i.(Impl).getSelfTy().(AstNode), i.(Trait).getName()]
)
}
string toString() { result = this.getTypeMention().toString() }
string toString() { result = this.getIdentifyingNode().toString() }
Location getLocation() { result = this.getTypeMention().getLocation() }
Location getLocation() { result = this.getIdentifyingNode().getLocation() }
}
pragma[nomagic]
@@ -294,10 +307,15 @@ module ArgIsInstantiationOf<
*/
signature module ArgsAreInstantiationsOfInputSig {
/**
* Holds if types need to be matched against the type `t` at position `pos` of
* `f` inside `i`.
* Holds if `f` inside `i` needs to have the type corresponding to type parameter
* `tp` checked.
*
* If `i` is an inherent implementation, `tp` is a type parameter of the type being
* implemented, otherwise `tp` is a type parameter of the trait (being implemented).
*
* `pos` is one of the positions in `f` in which the relevant type occours.
*/
predicate toCheck(ImplOrTraitItemNode i, Function f, FunctionPosition pos, AssocFunctionType t);
predicate toCheck(ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition pos);
/** A call whose argument types are to be checked. */
class Call {
@@ -318,23 +336,27 @@ signature module ArgsAreInstantiationsOfInputSig {
*/
module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
pragma[nomagic]
private predicate toCheckRanked(ImplOrTraitItemNode i, Function f, FunctionPosition pos, int rnk) {
Input::toCheck(i, f, pos, _) and
pos =
rank[rnk + 1](FunctionPosition pos0, int j |
Input::toCheck(i, f, pos0, _) and
(
j = pos0.asPosition()
or
pos0.isSelf() and j = -1
or
pos0.isReturn() and j = -2
)
private predicate toCheckRanked(
ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition pos, int rnk
) {
Input::toCheck(i, f, tp, pos) and
tp =
rank[rnk + 1](TypeParameter tp0, int j |
Input::toCheck(i, f, tp0, _) and
j = getTypeParameterId(tp0)
|
pos0 order by j
tp0 order by j
)
}
pragma[nomagic]
private predicate toCheck(
ImplOrTraitItemNode i, Function f, TypeParameter tp, FunctionPosition pos, AssocFunctionType t
) {
Input::toCheck(i, f, tp, pos) and
t.appliesTo(f, i, pos)
}
private newtype TCallAndPos =
MkCallAndPos(Input::Call call, FunctionPosition pos) { exists(call.getArgType(pos, _)) }
@@ -356,26 +378,26 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
string toString() { result = call.toString() + " [arg " + pos + "]" }
}
pragma[nomagic]
private predicate potentialInstantiationOf0(
CallAndPos cp, Input::Call call, TypeParameter tp, FunctionPosition pos, Function f,
TypeAbstraction abs, AssocFunctionType constraint
) {
cp = MkCallAndPos(call, pragma[only_bind_into](pos)) and
call.hasTargetCand(abs, f) and
toCheck(abs, f, tp, pragma[only_bind_into](pos), constraint)
}
private module ArgIsInstantiationOfToIndexInput implements
IsInstantiationOfInputSig<CallAndPos, AssocFunctionType>
{
pragma[nomagic]
private predicate potentialInstantiationOf0(
CallAndPos cp, Input::Call call, FunctionPosition pos, int rnk, Function f,
TypeAbstraction abs, AssocFunctionType constraint
) {
cp = MkCallAndPos(call, pragma[only_bind_into](pos)) and
call.hasTargetCand(abs, f) and
toCheckRanked(abs, f, pragma[only_bind_into](pos), rnk) and
Input::toCheck(abs, f, pragma[only_bind_into](pos), constraint)
}
pragma[nomagic]
predicate potentialInstantiationOf(
CallAndPos cp, TypeAbstraction abs, AssocFunctionType constraint
) {
exists(Input::Call call, int rnk, Function f |
potentialInstantiationOf0(cp, call, _, rnk, f, abs, constraint)
exists(Input::Call call, TypeParameter tp, FunctionPosition pos, int rnk, Function f |
potentialInstantiationOf0(cp, call, tp, pos, f, abs, constraint) and
toCheckRanked(abs, f, tp, pos, rnk)
|
rnk = 0
or
@@ -383,54 +405,83 @@ module ArgsAreInstantiationsOf<ArgsAreInstantiationsOfInputSig Input> {
)
}
predicate relevantConstraint(AssocFunctionType constraint) {
Input::toCheck(_, _, _, constraint)
}
predicate relevantConstraint(AssocFunctionType constraint) { toCheck(_, _, _, _, constraint) }
}
private module ArgIsInstantiationOfToIndex =
ArgIsInstantiationOf<CallAndPos, ArgIsInstantiationOfToIndexInput>;
pragma[nomagic]
private predicate argIsInstantiationOf(
Input::Call call, FunctionPosition pos, ImplOrTraitItemNode i, Function f, int rnk
) {
ArgIsInstantiationOfToIndex::argIsInstantiationOf(MkCallAndPos(call, pos), i, _) and
toCheckRanked(i, f, _, pos, rnk)
}
pragma[nomagic]
private predicate argsAreInstantiationsOfToIndex(
Input::Call call, ImplOrTraitItemNode i, Function f, int rnk
) {
exists(FunctionPosition pos |
ArgIsInstantiationOfToIndex::argIsInstantiationOf(MkCallAndPos(call, pos), i, _) and
call.hasTargetCand(i, f) and
toCheckRanked(i, f, pos, rnk)
argIsInstantiationOf(call, pos, i, f, rnk) and
call.hasTargetCand(i, f)
|
rnk = 0
or
argsAreInstantiationsOfToIndex(call, i, f, rnk - 1)
)
}
/**
* Holds if all arguments of `call` have types that are instantiations of the
* types of the corresponding parameters of `f` inside `i`.
*
* TODO: Check type parameter constraints as well.
*/
pragma[nomagic]
predicate argsAreInstantiationsOf(Input::Call call, ImplOrTraitItemNode i, Function f) {
exists(int rnk |
argsAreInstantiationsOfToIndex(call, i, f, rnk) and
rnk = max(int r | toCheckRanked(i, f, _, r))
rnk = max(int r | toCheckRanked(i, f, _, _, r))
)
}
private module ArgsAreNotInstantiationOfInput implements
IsInstantiationOfInputSig<CallAndPos, AssocFunctionType>
{
pragma[nomagic]
predicate potentialInstantiationOf(
CallAndPos cp, TypeAbstraction abs, AssocFunctionType constraint
) {
potentialInstantiationOf0(cp, _, _, _, _, abs, constraint)
}
predicate relevantConstraint(AssocFunctionType constraint) { toCheck(_, _, _, _, constraint) }
}
private module ArgsAreNotInstantiationOf =
ArgIsInstantiationOf<CallAndPos, ArgsAreNotInstantiationOfInput>;
pragma[nomagic]
private predicate argsAreNotInstantiationsOf0(
Input::Call call, FunctionPosition pos, ImplOrTraitItemNode i
) {
ArgIsInstantiationOfToIndex::argIsNotInstantiationOf(MkCallAndPos(call, pos), i, _, _)
ArgsAreNotInstantiationOf::argIsNotInstantiationOf(MkCallAndPos(call, pos), i, _, _)
}
/**
* Holds if _some_ argument of `call` has a type that is not an instantiation of the
* type of the corresponding parameter of `f` inside `i`.
*
* TODO: Check type parameter constraints as well.
*/
pragma[nomagic]
predicate argsAreNotInstantiationsOf(Input::Call call, ImplOrTraitItemNode i, Function f) {
exists(FunctionPosition pos |
argsAreNotInstantiationsOf0(call, pos, i) and
call.hasTargetCand(i, f) and
Input::toCheck(i, f, pos, _)
Input::toCheck(i, f, _, pos)
)
}
}

View File

@@ -8,11 +8,7 @@ private import codeql.rust.elements.internal.generated.Raw
private import codeql.rust.elements.internal.generated.Synth
private import codeql.rust.frameworks.stdlib.Stdlib
private import codeql.rust.frameworks.stdlib.Builtins as Builtins
/** Gets a type alias of `trait` or of a supertrait of `trait`. */
private TypeAlias getTraitTypeAlias(Trait trait) {
result = trait.getSupertrait*().getAssocItemList().getAnAssocItem()
}
private import AssociatedType
/**
* Holds if a dyn trait type for the trait `trait` should have a type parameter
@@ -31,7 +27,7 @@ private TypeAlias getTraitTypeAlias(Trait trait) {
*/
private predicate dynTraitTypeParameter(Trait trait, AstNode n) {
trait = any(DynTraitTypeRepr dt).getTrait() and
n = [trait.getGenericParamList().getATypeParam().(AstNode), getTraitTypeAlias(trait)]
n = [trait.getGenericParamList().getATypeParam().(AstNode), getTraitAssocType(trait)]
}
cached
@@ -43,8 +39,11 @@ newtype TType =
TNeverType() or
TUnknownType() or
TTypeParamTypeParameter(TypeParam t) or
TAssociatedTypeTypeParameter(Trait trait, TypeAlias typeAlias) {
getTraitTypeAlias(trait) = typeAlias
TAssociatedTypeTypeParameter(Trait trait, AssocType typeAlias) {
getTraitAssocType(trait) = typeAlias
} or
TTypeParamAssociatedTypeTypeParameter(TypeParam tp, AssocType assoc) {
tpAssociatedType(tp, assoc, _)
} or
TDynTraitTypeParameter(Trait trait, AstNode n) { dynTraitTypeParameter(trait, n) } or
TImplTraitTypeParameter(ImplTraitTypeRepr implTrait, TypeParam tp) {
@@ -464,6 +463,52 @@ class AssociatedTypeTypeParameter extends TypeParameter, TAssociatedTypeTypePara
override Location getLocation() { result = typeAlias.getLocation() }
}
/**
* A type parameter corresponding to an associated type accessed on a type
* parameter, for example `T::AssociatedType` where `T` is a type parameter.
*
* These type parameters are created when a function signature accesses an
* associated type on a type parameter. For example, in
* ```rust
* fn foo<T: SomeTrait>(arg: T::Assoc) { }
* ```
* we create a `TypeParamAssociatedTypeTypeParameter` for `Assoc` on `T` and the
* mention `T::Assoc` resolves to this type parameter. If denoting the type
* parameter by `T_Assoc` then the above function is treated as if it was
* ```rust
* fn foo<T: SomeTrait<Assoc = T_Assoc>, T_Assoc>(arg: T_Assoc) { }
* ```
*/
class TypeParamAssociatedTypeTypeParameter extends TypeParameter,
TTypeParamAssociatedTypeTypeParameter
{
private TypeParam typeParam;
private AssocType assoc;
TypeParamAssociatedTypeTypeParameter() {
this = TTypeParamAssociatedTypeTypeParameter(typeParam, assoc)
}
/** Gets the type parameter that this associated type is accessed on. */
TypeParam getTypeParam() { result = typeParam }
/** Gets the associated type alias. */
AssocType getTypeAlias() { result = assoc }
/** Gets a path that accesses this type parameter. */
Path getAPath() { tpAssociatedType(typeParam, assoc, result) }
override ItemNode getDeclaringItem() { result.getTypeParam(_) = typeParam }
override string toString() {
result =
typeParam.toString() + "::" + assoc.getName().getText() + "[" +
assoc.getTrait().getName().getText() + "]"
}
override Location getLocation() { result = typeParam.getLocation() }
}
/** Gets the associated type type-parameter corresponding directly to `typeAlias`. */
AssociatedTypeTypeParameter getAssociatedTypeTypeParameter(TypeAlias typeAlias) {
result.isDirect() and result.getTypeAlias() = typeAlias

View File

@@ -6,6 +6,7 @@ private import codeql.rust.frameworks.stdlib.Stdlib
private import Type
private import TypeAbstraction
private import TypeInference
private import AssociatedType
bindingset[trait, name]
pragma[inline_late]
@@ -206,12 +207,11 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
exists(TypeParamItemNode tp | this = tp.getABoundPath() and result = tp)
}
pragma[nomagic]
private Type getDefaultPositionalTypeArgument(int i, TypePath path) {
// If a type argument is not given in the path, then we use the default for
// the type parameter if one exists for the type.
not exists(getPathTypeArgument(this, i)) and
// Defaults only apply to type mentions in type annotations
this = any(PathTypeRepr ptp).getPath().getQualifier*() and
exists(Type ty, TypePath prefix |
ty = this.resolveRootType().getTypeParameterDefault(i).(TypeMention).getTypeAt(prefix) and
if not ty = TSelfTypeParameter(resolved)
@@ -226,10 +226,37 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
)
}
private predicate isInTypeAnnotation() {
this = any(PathTypeRepr ptp).getPath().getQualifier*()
}
pragma[nomagic]
private TypeParameter getPositionalTypeParameter(int i) {
result = this.resolveRootType().getPositionalTypeParameter(i)
}
/**
* Gets the default type for the type parameter `tp` at `path`, if any.
*
* This predicate is restricted to mentions that are _not_ part of a type
* annotation, such as a qualifier in a call, `Vec::new()`, where the
* default type for type parameter `A` of `Vec` is `Global`.
*
* In these cases, whether or not the default type actually applies may
* depend on the types of arguments.
*/
pragma[nomagic]
Type getDefaultTypeForTypeParameterInNonAnnotationAt(TypeParameter tp, TypePath path) {
not this.isInTypeAnnotation() and
exists(int i |
result = this.getDefaultPositionalTypeArgument(i, path) and
tp = this.getPositionalTypeParameter(i)
)
}
pragma[nomagic]
private Type getPositionalTypeArgument(int i, TypePath path) {
result = getPathTypeArgument(this, i).getTypeAt(path)
or
result = this.getDefaultPositionalTypeArgument(i, path)
}
/**
@@ -237,9 +264,12 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
* type parameter does not correspond directly to a type mention.
*/
private Type getTypeForTypeParameterAt(TypeParameter tp, TypePath path) {
exists(int i |
result = this.getPositionalTypeArgument(pragma[only_bind_into](i), path) and
tp = this.resolveRootType().getPositionalTypeParameter(pragma[only_bind_into](i))
exists(int i | tp = this.getPositionalTypeParameter(i) |
result = this.getPositionalTypeArgument(i, path)
or
// Defaults only apply to type mentions in type annotations
this.isInTypeAnnotation() and
result = this.getDefaultPositionalTypeArgument(i, path)
)
or
// Handle the special syntactic sugar for function traits. The syntactic
@@ -290,6 +320,22 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
tp = TAssociatedTypeTypeParameter(resolved, alias) and
path.isEmpty()
)
or
// If this path is a type parameter bound, then any associated types
// accessed on the type parameter, which originate from this bound, should
// be instantiated into the bound, as explained in the comment for
// `TypeParamAssociatedTypeTypeParameter`.
// ```rust
// fn foo<T: SomeTrait<Assoc = T_Assoc>, T_Assoc>(arg: T_Assoc) { }
// ^^^^^^^^^ ^^^^^ ^^^^^^^
// this path result
// ```
exists(TypeParam typeParam, Trait trait, AssocType assoc |
tpBoundAssociatedType(typeParam, _, this, trait, assoc) and
tp = TAssociatedTypeTypeParameter(resolved, assoc) and
result = TTypeParamAssociatedTypeTypeParameter(typeParam, assoc) and
path.isEmpty()
)
}
bindingset[name]
@@ -299,7 +345,7 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
/** Gets the type mention in this path for the type parameter `tp`, if any. */
pragma[nomagic]
private TypeMention getTypeMentionImplForTypeParameter(TypeParameter tp) {
private TypeMention getTypeMentionForAssociatedTypeTypeParameter(AssociatedTypeTypeParameter tp) {
exists(TypeAlias alias, string name |
result = this.getAssocTypeArg(name) and
tp = TAssociatedTypeTypeParameter(resolved, alias) and
@@ -343,6 +389,8 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
or
// Handles paths of the form `Self::AssocType` within a trait block
result = TAssociatedTypeTypeParameter(resolvePath(this.getQualifier()), resolved)
or
result.(TypeParamAssociatedTypeTypeParameter).getAPath() = this
}
override Type resolvePathTypeAt(TypePath typePath) {
@@ -352,7 +400,7 @@ private module MkTypeMention<getAdditionalPathTypeAtSig/2 getAdditionalPathTypeA
exists(TypeParameter tp, TypePath suffix | typePath = TypePath::cons(tp, suffix) |
result = this.getTypeForTypeParameterAt(tp, suffix)
or
result = this.getTypeMentionImplForTypeParameter(tp).getTypeAt(suffix)
result = this.getTypeMentionForAssociatedTypeTypeParameter(tp).getTypeAt(suffix)
)
or
// When the path refers to a trait, then the implicit `Self` type parameter
@@ -661,11 +709,10 @@ private predicate pathConcreteTypeAssocType(
|
// path of the form `<Type as Trait>::AssocType`
// ^^^ tm ^^^^^^^^^ name
exists(string name |
name = path.getText() and
trait = resolvePath(qualifier.getSegment().getTraitTypeRepr().getPath()) and
getTraitAssocType(trait, name) = alias and
tm = qualifier.getSegment().getTypeRepr()
exists(string name, Path traitPath |
pathTypeAsTraitAssoc(path, tm, traitPath, name) and
trait = resolvePath(traitPath) and
getTraitAssocType(trait, name) = alias
)
or
// path of the form `Self::AssocType` within an `impl` block