From 3a586112713306adc52af38c43da980075f56374 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 6 Mar 2025 10:42:34 +0100 Subject: [PATCH 01/11] Shared: Add shared type inference library --- .../typeinference/internal/TypeInference.qll | 783 ++++++++++++++++++ shared/typeinference/qlpack.yml | 7 + 2 files changed, 790 insertions(+) create mode 100644 shared/typeinference/codeql/typeinference/internal/TypeInference.qll create mode 100644 shared/typeinference/qlpack.yml diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll new file mode 100644 index 000000000000..c9f1bc348893 --- /dev/null +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -0,0 +1,783 @@ +/** + * Provides shared functionality for computing type inference in QL. + * + * The code examples in this file use C# syntax, but the concepts should + * carry over to other languages as well. + * + * The library is initialized in two phases: `Make1`, which constructs + * the `TypePath` type, and `Make2`, which (using `TypePath` in the input + * signature) constructs the `Matching` module. + */ + +private import codeql.util.Location + +/** Provides the input to `Make1`. */ +signature module InputSig1 { + /** + * A type without type arguments. + * + * For example `int` or ``IEnumerable`1``. + */ + class Type { + /** Gets a type parameter of this type, if any. */ + TypeParameter getATypeParameter(); + + /** Gets a textual representation of this type. */ + string toString(); + + /** Gets the location of this type. */ + Location getLocation(); + } + + /** A type parameter. */ + class TypeParameter extends Type; + + /** + * Gets the unique identifier of type parameter `tp`. + * + * This is used to represent type paths `.`-separated type parameter + * identifiers. + */ + int getTypeParameterId(TypeParameter tp); + + /** + * A type argument position, for example an integer. + * + * Type argument positions are used when type arguments are supplied explicitly, + * for example in a method call like `M()` the type argument `int` is at + * position `0`. + */ + bindingset[this] + class TypeArgumentPosition { + /** Gets the textual representation of this position. */ + bindingset[this] + string toString(); + } + + /** A type parameter position, for example an integer. */ + bindingset[this] + class TypeParameterPosition { + /** Gets the textual representation of this position. */ + bindingset[this] + string toString(); + } + + /** Holds if `tapos` and `tppos` match. */ + bindingset[tapos] + bindingset[tppos] + predicate typeArgumentParameterPositionMatch( + TypeArgumentPosition tapos, TypeParameterPosition tppos + ); +} + +module Make1 Input1> { + private import Input1 + private import codeql.util.DenseRank + + private module DenseRankInput implements DenseRankInputSig { + class Ranked = TypeParameter; + + predicate getRank = getTypeParameterId/1; + } + + private int getTypeParameterRank(TypeParameter tp) { + tp = DenseRank::denseRank(result) + } + + bindingset[s] + private predicate decodeTypePathComponent(string s, TypeParameter tp) { + getTypeParameterRank(tp) = s.toInt() + } + + final private class String = string; + + /** + * A path into a type. + * + * Paths are represented in left-to-right order, for example, a path `"0.1"` into the + * type `C1,C3>` points at the type `B`. + * + * Type paths are used to represent constructed types without using a `newtype`, which + * makes it practically feasible to do type inference in mutual recursion with call + * resolution. + * + * As an example, the type above can be represented by the following set of tuples + * + * `TypePath` | `Type` + * ---------- | -------- + * `""` | ``C1`2`` + * `"0"` | ``C2`2`` + * `"0.0"` | `A` + * `"0.1"` | `B` + * `"1"` | ``C3`2`` + * `"1.0"` | `C` + * `"1.1"` | `D` + * + * Note that while we write type paths using type parameter positions (e.g. `"0.1"`), + * the actual implementation uses unique type parameter identifiers, in order to not + * mix up type parameters from different types. + */ + class TypePath extends String { + bindingset[this] + TypePath() { exists(this) } + + bindingset[this] + private TypeParameter getTypeParameter(int i) { + exists(string s | + s = this.splitAt(".", i) and + decodeTypePathComponent(s, result) + ) + } + + /** Gets a textual representation of this type path. */ + bindingset[this] + string toString() { + result = + concat(int i, TypeParameter tp | + tp = this.getTypeParameter(i) + | + tp.toString(), "." order by i + ) + } + + /** Holds if this type path is empty. */ + predicate isEmpty() { this = "" } + + /** Gets the path obtained by appending `suffix` onto this path. */ + bindingset[suffix, result] + bindingset[this, result] + bindingset[this, suffix] + TypePath append(TypePath suffix) { + if this.isEmpty() + then result = suffix + else + if suffix.isEmpty() + then result = this + else result = this + "." + suffix + } + + /** Holds if this path starts with `prefix`, followed by `tp`. */ + bindingset[this] + predicate endsWith(TypePath prefix, TypeParameter tp) { + decodeTypePathComponent(this, tp) and + prefix.isEmpty() + or + exists(int last | + last = max(this.indexOf(".")) and + prefix = this.prefix(last) and + decodeTypePathComponent(this.suffix(last + 1), tp) + ) + } + + /** Holds if this path starts with `tp`, followed by `suffix`. */ + bindingset[this] + predicate startsWith(TypeParameter tp, TypePath suffix) { + decodeTypePathComponent(this, tp) and + suffix.isEmpty() + or + exists(int first | + first = min(this.indexOf(".")) and + suffix = this.suffix(first + 1) and + decodeTypePathComponent(this.prefix(first), tp) + ) + } + } + + /** Provides predicates for constructing `TypePath`s. */ + module TypePath { + /** Gets the empty type path. */ + TypePath nil() { result.isEmpty() } + + /** Gets the singleton type path `tp`. */ + TypePath singleton(TypeParameter tp) { result = getTypeParameterRank(tp).toString() } + + /** + * Gets the type path obtained by appending the singleton type path `tp` + * onto `suffix`. + */ + bindingset[result] + bindingset[suffix] + TypePath cons(TypeParameter tp, TypePath suffix) { result = singleton(tp).append(suffix) } + } + + /** Provides the input to `Make2`. */ + signature module InputSig2 { + /** A type mention, for example a type annotation in a local variable declaration. */ + class TypeMention { + /** + * Gets the type being mentioned at `path` inside this type mention. + * + * For example, in + * + * ```csharp + * C x = ... + * ``` + * + * the type mention in the declaration of `x` resolves to the following + * types: + * + * `TypePath` | `Type` + * ---------- | ------- + * `""` | ``C`1`` + * `"0"` | `int` + */ + Type resolveTypeAt(TypePath path); + + /** Gets a textual representation of this type mention. */ + string toString(); + + /** Gets the location of this type mention. */ + Location getLocation(); + } + + /** + * Gets a base type mention of `t`, if any. Example: + * + * ```csharp + * class C : Base, Interface { } + * // ^ `t` + * // ^^^^^^^ `result` + * // ^^^^^^^^^ `result` + * ``` + */ + TypeMention getABaseTypeMention(Type t); + } + + module Make2 { + private import Input2 + + pragma[nomagic] + private Type resolveTypeMentionRoot(TypeMention tm) { + result = tm.resolveTypeAt(TypePath::nil()) + } + + /** Provides logic related to base types. */ + private module BaseTypes { + /** + * Holds if `baseMention` is a (transitive) base type mention of `sub`, + * and type parameter `tp` (belonging to `sub`) is mentioned (implicitly) + * at `path` inside the type that `baseMention` resolves to. + * + * For example, in + * + * ```csharp + * class C { } + * + * class Base { } + * + * class Mid : Base> { } + * + * class Sub : Mid> { } + * ``` + * + * - `T3` is mentioned at `0.0` for immediate base type mention `Base>` + * of `Mid`, + * - `T4` is mentioned at `0.0` for immediate base type mention `Mid>` + * of `Sub`, and + * - `T4` is mentioned implicitly at `0.0.0` for transitive base type mention + * `Base>` of `Sub`. + */ + pragma[nomagic] + predicate baseTypeMentionHasTypeParameterAt( + Type sub, TypeMention baseMention, TypePath path, TypeParameter tp + ) { + exists(TypeMention immediateBaseMention, TypePath pathToTypeParam | + tp = sub.getATypeParameter() and + immediateBaseMention = getABaseTypeMention(sub) and + tp = immediateBaseMention.resolveTypeAt(pathToTypeParam) + | + // immediate base class + baseMention = immediateBaseMention and + path = pathToTypeParam + or + // transitive base class + exists(Type immediateBase, TypePath prefix, TypePath suffix, TypeParameter mid | + /* + * Example: + * + * - `prefix = "0.0"`, + * - `pathToTypeParam = "0.0"`, + * - `suffix = "0"`, + * - `path = "0.0.0"` + * + * ```csharp + * class C { } + * + * class Base { } + * + * class Mid : Base> { } + * // ^^^ `immediateBase` + * // ^^ `mid` + * // ^^^^^^^^^^^ `baseMention` + * + * class Sub : Mid> { } + * // ^^^ `sub` + * // ^^ `tp` + * // ^^^^^^^^^^ `immediateBaseMention` + * ``` + */ + + immediateBase = resolveTypeMentionRoot(immediateBaseMention) and + baseTypeMentionHasTypeParameterAt(immediateBase, baseMention, prefix, mid) and + pathToTypeParam.startsWith(mid, suffix) and + path = prefix.append(suffix) + ) + ) + } + + /** + * Holds if `baseMention` is a (transitive) base type mention of `sub`, and + * non-type-parameter `t` is mentioned (implicitly) at `path` inside + * `baseMention`. For example, in + * + * ```csharp + * class C { } + * + * class Base { } + * + * class Mid : Base> { } + * + * class Sub : Mid> { } + * ``` + * + * - ``C`1`` is mentioned at `0` for immediate base type mention `Base>` + * of `Mid`, + * - ``C`1`` is mentioned at `0` for immediate base type mention `Mid>` + * of `Sub`, and + * - ``C`1`` is mentioned at `0` and implicitly at `0.0` for transitive base type + * mention `Base>` of `Sub`. + */ + pragma[nomagic] + predicate baseTypeMentionHasNonTypeParameterAt( + Type sub, TypeMention baseMention, TypePath path, Type t + ) { + not t instanceof TypeParameter and + exists(TypeMention immediateBaseMention | + pragma[only_bind_into](immediateBaseMention) = + getABaseTypeMention(pragma[only_bind_into](sub)) + | + // immediate base class + baseMention = immediateBaseMention and + t = immediateBaseMention.resolveTypeAt(path) + or + // transitive base class + exists(Type immediateBase | immediateBase = resolveTypeMentionRoot(immediateBaseMention) | + baseTypeMentionHasNonTypeParameterAt(immediateBase, baseMention, path, t) + or + exists(TypePath path0, TypePath prefix, TypePath suffix, TypeParameter tp | + /* + * Example: + * + * - `prefix = "0.0"`, + * - `path0 = "0"`, + * - `suffix = ""`, + * - `path = "0.0"` + * + * ```csharp + * class C { } + * ^ `t` + * + * class Base { } + * + * class Mid : Base> { } + * // ^^^ `immediateBase` + * // ^^ `tp` + * // ^^^^^^^^^^^ `baseMention` + * + * class Sub : Mid> { } + * // ^^^ `sub` + * // ^^^^^^^^^^ `immediateBaseMention` + * ``` + */ + + baseTypeMentionHasTypeParameterAt(immediateBase, baseMention, prefix, tp) and + t = immediateBaseMention.resolveTypeAt(path0) and + path0.startsWith(tp, suffix) and + path = prefix.append(suffix) + ) + ) + ) + } + } + + private import BaseTypes + + /** Provides the input to `Matching`. */ + signature module MatchingInputSig { + /** + * A position inside a declaration. For example, the integer position of a + * parameter inside a method or the return type of a method. + */ + bindingset[this] + class DeclarationPosition { + /** Gets a textual representation of this position. */ + bindingset[this] + string toString(); + } + + /** A declaration, for example a method. */ + class Declaration { + /** Gets a textual representation of this declaration. */ + string toString(); + + /** Gets the location of this declaration. */ + Location getLocation(); + + /** Gets the type parameter at position `tppos` of this declaration, if any. */ + TypeParameter getTypeParameter(TypeParameterPosition tppos); + + /** + * Gets the declared type of this declaration at `path` for position `dpos`. + * + * For example, if this declaration is the method `int M(bool b)`, + * then the declared type at parameter position `0` is `bool` and the + * declared return type is `int`. + */ + Type getDeclaredType(DeclarationPosition dpos, TypePath path); + } + + /** + * A position inside an access. For example, the integer position of an + * argument inside a method call. + */ + bindingset[this] + class AccessPosition { + /** Gets a textual representation of this position. */ + bindingset[this] + string toString(); + } + + /** An access that targets a declaration, for example a method call. */ + class Access { + /** Gets a textual representation of this access. */ + string toString(); + + /** Gets the location of this access. */ + Location getLocation(); + + /** + * Gets the type at `path` for the explicit type argument at position + * `tapos` of this access, if any. + * + * For example, in a method call like `M()`, `int` is an explicit + * type argument at position `0`. + */ + Type getExplicitTypeArgument(TypeArgumentPosition tapos, TypePath path); + + /** + * Gets the resolved type at `path` for the position `apos` of this access. + * + * For example, if this access is the method call `M(42)`, then the resolved + * type at argument position `0` is `int`. + */ + Type getResolvedType(AccessPosition apos, TypePath path); + + /** Gets the declaration that this access targets. */ + Declaration getTarget(); + } + + /** Holds if `apos` and `dpos` match. */ + bindingset[apos] + bindingset[dpos] + predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos); + + /** + * Holds if matching a resolved type `t` at `path` inside an access at `apos` + * against the declaration `target` means that the type should be adjusted to + * `tAdj` at `pathAdj`. + * + * For example, in + * + * ```csharp + * void M(int? i) {} + * M(42); + * ``` + * + * the resolved type of `42` is `int`, but it should be adjusted to `int?` + * when matching against `M`. + */ + bindingset[apos, target, path, t] + default predicate adjustAccessType( + AccessPosition apos, Declaration target, TypePath path, Type t, TypePath pathAdj, Type tAdj + ) { + pathAdj = path and + tAdj = t + } + } + + /** + * Provides logic for matching types at accesses against types at the + * declarations that the accesses target. + * + * Matching takes both base types and explicit type arguments into account. + */ + module Matching { + private import Input + + pragma[nomagic] + private predicate accessType( + Access a, AccessPosition apos, Declaration target, TypePath path, Type t + ) { + target = a.getTarget() and + exists(TypePath path0, Type t0 | + t0 = a.getResolvedType(apos, path0) and + adjustAccessType(apos, target, path0, t0, path, t) + ) + } + + bindingset[a, target] + pragma[inline_late] + private Type explicitTypeArgument( + Access a, Declaration target, TypeParameter tp, TypePath path + ) { + exists(TypeArgumentPosition tapos, TypeParameterPosition tppos | + result = a.getExplicitTypeArgument(tapos, path) and + tp = target.getTypeParameter(tppos) and + typeArgumentParameterPositionMatch(tapos, tppos) + ) + } + + /** + * Holds if the type `t` at `path` of `a` matches the type parameter `tp` + * of `target`. + */ + pragma[nomagic] + private predicate directTypeMatch( + Access a, Declaration target, TypePath path, Type t, TypeParameter tp + ) { + exists(AccessPosition apos, DeclarationPosition dpos, TypePath pathToTypeParam | + accessType(a, apos, target, pathToTypeParam.append(path), t) and + tp = target.getDeclaredType(dpos, pathToTypeParam) and + not exists(explicitTypeArgument(a, target, tp, _)) and + accessDeclarationPositionMatch(apos, dpos) + ) + } + + private module AccessBaseType { + private predicate relevantAccess(Access a, AccessPosition apos) { + exists(Declaration target | + accessType(a, apos, target, _, _) and + target.getDeclaredType(_, _) instanceof TypeParameter + ) + } + + pragma[nomagic] + private Type resolveRootType(Access a, AccessPosition apos) { + relevantAccess(a, apos) and + result = a.getResolvedType(apos, TypePath::nil()) + } + + pragma[nomagic] + private Type resolveTypeAt(Access a, AccessPosition apos, TypeParameter tp, TypePath suffix) { + relevantAccess(a, apos) and + exists(TypePath path0 | + result = a.getResolvedType(apos, path0) and + path0.startsWith(tp, suffix) + ) + } + + /** + * Holds if `baseMention` is a (transitive) base type mention of the type of + * `a` at position `apos`, and `t` is mentioned (implicitly) at `path` inside + * `base`. For example, in + * + * ```csharp + * class C { } + * + * class Base { } + * + * class Mid : Base> { } + * + * class Sub : Mid> { } + * + * new Sub().ToString(); + * ``` + * + * for the node `new Sub()`, which is the receiver of a method call, we + * have: + * + * `baseMention` | `path` | `t` + * ------------- | --------- | --- + * `Mid>` | `"0"` | ``C`1`` + * `Mid>` | `"0.1"` | `int` + * `Base>` | `"0"` | ``C`1`` + * `Base>` | `"0.0"` | ``C`1`` + * `Base>` | `"0.0.1"` | `int` + */ + pragma[nomagic] + predicate hasBaseTypeMention( + Access a, AccessPosition apos, TypeMention baseMention, TypePath path, Type t + ) { + exists(Type sub | sub = resolveRootType(a, apos) | + baseTypeMentionHasNonTypeParameterAt(sub, baseMention, path, t) + or + exists(TypePath prefix, TypePath suffix, TypeParameter i | + baseTypeMentionHasTypeParameterAt(sub, baseMention, prefix, i) and + t = resolveTypeAt(a, apos, i, suffix) and + path = prefix.append(suffix) + ) + ) + } + } + + pragma[nomagic] + private predicate accessBaseType( + Access a, AccessPosition apos, Declaration target, Type base, TypePath path, Type t + ) { + exists(TypeMention tm | + target = a.getTarget() and + AccessBaseType::hasBaseTypeMention(a, apos, tm, path, t) and + base = resolveTypeMentionRoot(tm) + ) + } + + pragma[nomagic] + private predicate declarationBaseType( + Declaration decl, DeclarationPosition dpos, Type base, TypePath path, Type t + ) { + t = decl.getDeclaredType(dpos, path) and + path.startsWith(base.getATypeParameter(), _) + } + + /** + * Holds if the (transitive) base type `t` at `path` of `a` matches the type + * parameter `tp`, which is used in the declared types of `target`. + * + * For example, in + * + * ```csharp + * class C { } + * + * class Base { + * // ^^ `tp` + * public C Method() { ... } + * // ^^^^^^ `target` + * } + * + * class Mid : Base> { } + * + * class Sub : Mid> { } + * + * new Sub().Method(); + * // ^^^^^^^^^^^^^^^^^^^^^^^ `a` + * ``` + * + * we have that type parameter `T2` of `Base` is matched as follows: + * + * `path` | `t` + * --------- | ------- + * `"0"` | ``C`1`` + * `"0.0"` | ``C`1`` + * `"0.0.1"` | `int` + */ + pragma[nomagic] + private predicate baseTypeMatch( + Access a, Declaration target, TypePath path, Type t, TypeParameter tp + ) { + exists(AccessPosition apos, DeclarationPosition dpos, Type base, TypePath pathToTypeParam | + accessBaseType(a, apos, target, base, pathToTypeParam.append(path), t) and + declarationBaseType(target, dpos, base, pathToTypeParam, tp) and + not exists(explicitTypeArgument(a, target, tp, _)) and + accessDeclarationPositionMatch(apos, dpos) + ) + } + + pragma[nomagic] + private predicate explicitTypeMatch( + Access a, Declaration target, TypePath path, Type t, TypeParameter tp + ) { + target = a.getTarget() and + t = explicitTypeArgument(a, target, tp, path) + } + + pragma[nomagic] + private predicate implicitTypeMatch( + Access a, Declaration target, TypePath path, Type t, TypeParameter tp + ) { + directTypeMatch(a, target, path, t, tp) + or + baseTypeMatch(a, target, path, t, tp) + } + + pragma[inline] + private predicate typeMatch( + Access a, Declaration target, TypePath path, Type t, TypeParameter tp + ) { + explicitTypeMatch(a, target, path, t, tp) + or + implicitTypeMatch(a, target, path, t, tp) + } + + /** + * Gets the resolved type of `a` at `path` for position `apos`. + * + * For example, in + * + * ```csharp + * class C { } + * + * class Base { + * public C Method() { ... } + * } + * + * class Mid : Base> { } + * + * class Sub : Mid> { } + * + * new Sub().Method(); + * // ^^^^^^^^^^^^^^^^^^^^^^^ `a` + * ``` + * + * we resolve the following types for the return position: + * + * `path` | `t` + * ----------- | ------- + * `"0"` | ``C`1`` + * `"0.0"` | ``C`1`` + * `"0.0.0"` | ``C`1`` + * `"0.0.0.1"` | `int` + * + * We also resolve the following types for the receiver position: + * + * `path` | `t` + * ----------- | ------- + * `"0"` | ``Base`1`` + * `"0.0"` | ``C`1`` + * `"0.0.0"` | ``C`1`` + * `"0.0.0.1"` | `int` + */ + pragma[nomagic] + Type resolveAccessType(Access a, AccessPosition apos, TypePath path) { + exists(DeclarationPosition dpos | accessDeclarationPositionMatch(apos, dpos) | + exists(Declaration target, TypePath prefix, TypeParameter tp, TypePath suffix | + tp = target.getDeclaredType(pragma[only_bind_into](dpos), prefix) and + typeMatch(a, target, suffix, result, tp) and + path = prefix.append(suffix) + ) + or + exists(Declaration target | + result = target.getDeclaredType(pragma[only_bind_into](dpos), path) and + target = a.getTarget() and + not result instanceof TypeParameter + ) + ) + } + } + + /** Provides consitency checks. */ + module Consistency { + query predicate missingTypeParameterId(TypeParameter tp) { + not exists(getTypeParameterId(tp)) + } + + query predicate nonFunctionalTypeParameterId(TypeParameter tp) { + getTypeParameterId(tp) != getTypeParameterId(tp) + } + + query predicate nonInjectiveTypeParameterId(TypeParameter tp1, TypeParameter tp2) { + getTypeParameterId(tp1) = getTypeParameterId(tp2) and + tp1 != tp2 + } + } + } +} diff --git a/shared/typeinference/qlpack.yml b/shared/typeinference/qlpack.yml new file mode 100644 index 000000000000..dc1d4e81ed4e --- /dev/null +++ b/shared/typeinference/qlpack.yml @@ -0,0 +1,7 @@ +name: codeql/typeinference +version: 0.0.1-dev +groups: shared +library: true +dependencies: + codeql/util: ${workspace} +warnOnImplicitThis: true From 62d4e6fe3f09b63fb308a445befe83e38d2183b8 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 7 Mar 2025 08:38:28 +0100 Subject: [PATCH 02/11] Rust: Implement basic type inference in QL --- rust/ql/.generated.list | 1 - rust/ql/.gitattributes | 1 - .../lib/codeql/rust/controlflow/CfgNodes.qll | 5 +- .../rust/elements/internal/CallExprImpl.qll | 20 +- .../rust/elements/internal/FieldExprImpl.qll | 9 + .../elements/internal/GenericArgListImpl.qll | 15 + .../internal/GenericParamListImpl.qll | 18 +- .../rust/elements/internal/PathResolution.qll | 170 ++- .../rust/elements/internal/RecordExprImpl.qll | 18 +- .../rust/elements/internal/RecordPatImpl.qll | 11 +- .../rust/elements/internal/StructImpl.qll | 12 + .../codeql/rust/elements/internal/Type.qll | 329 ++++++ .../rust/elements/internal/TypeInference.qll | 999 ++++++++++++++++++ .../rust/elements/internal/TypeMention.qll | 173 +++ .../rust/elements/internal/TypeParamImpl.qll | 5 + .../rust/elements/internal/VariantImpl.qll | 15 + .../lib/codeql/rust/internal/CachedStages.qll | 30 + rust/ql/lib/qlpack.yml | 1 + .../Trait/Trait_getGenericParamList.expected | 2 +- .../ql/test/extractor-tests/utf8/ast.expected | 2 +- .../CONSISTENCY/AstConsistency.expected | 5 + .../test/library-tests/type-inference/main.rs | 574 ++++++++++ .../type-inference/type-inference.expected | 797 ++++++++++++++ .../type-inference/type-inference.ql | 18 + 24 files changed, 3196 insertions(+), 34 deletions(-) create mode 100644 rust/ql/lib/codeql/rust/elements/internal/Type.qll create mode 100644 rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll create mode 100644 rust/ql/lib/codeql/rust/elements/internal/TypeMention.qll create mode 100644 rust/ql/test/library-tests/type-inference/CONSISTENCY/AstConsistency.expected create mode 100644 rust/ql/test/library-tests/type-inference/main.rs create mode 100644 rust/ql/test/library-tests/type-inference/type-inference.expected create mode 100644 rust/ql/test/library-tests/type-inference/type-inference.ql diff --git a/rust/ql/.generated.list b/rust/ql/.generated.list index c016dc44dd2e..0ebf3ad12301 100644 --- a/rust/ql/.generated.list +++ b/rust/ql/.generated.list @@ -286,7 +286,6 @@ lib/codeql/rust/elements/internal/GenericArgImpl.qll 6b1b804c357425c223f926e560a lib/codeql/rust/elements/internal/GenericArgListConstructor.qll 46859bb3eb09d77987a18642d65ba2e13471a4dc9c0a83a192fddc82e37c335c 2c7d54c876269a88d3461b05745e73b06532b1616cae9b614ac94b28735d8fc4 lib/codeql/rust/elements/internal/GenericParamImpl.qll f435f80d7f275803c1311d362467f4a367deb5a2c0245b17a9e12468a2c3ce2f 8e8fcc29f510efa03ce194ad3a1e2ae3fbd7f8e04ab5a4a2d1db03e95f388446 lib/codeql/rust/elements/internal/GenericParamListConstructor.qll 7221146d1724e0add3a8e70e0e46670142589eb7143425e1871ac4358a8c8bdb 2fbb7576444d6b2da6164245e2660d592d276ae2c1ca9f2bda5656b1c5c0a73a -lib/codeql/rust/elements/internal/GenericParamListImpl.qll 524aa0949df6d4d2cb9bee6226650f63a6f181d7644933fa265673b281074885 27b0210e5eaa2040bc8a093e35e1394befb6994b25369544738d0447ef269a9c lib/codeql/rust/elements/internal/IdentPatConstructor.qll 09792f5a070996b65f095dc6b1b9e0fb096a56648eed26c0643c59f82377cab0 0bb1a9fcdc62b5197aef3dd6e0ea4d679dde10d5be54b57b5209727ba66e078b lib/codeql/rust/elements/internal/IfExprConstructor.qll 03088b54c8fa623f93a5b5a7eb896f680e8b0e9025488157a02c48aaebc6ad56 906f916c3690d0721a31dd31b302dcdcec4233bb507683007d82cf10793a648f lib/codeql/rust/elements/internal/ImplConstructor.qll 24edccca59f70d812d1458b412a45310ddc096d095332f6e3258903c54c1bb44 7eb673b3ab33a0873ee5ce189105425066b376821cce0fc9eb8ace22995f0bc7 diff --git a/rust/ql/.gitattributes b/rust/ql/.gitattributes index 4802a68f7a33..333beeb3d2b0 100644 --- a/rust/ql/.gitattributes +++ b/rust/ql/.gitattributes @@ -288,7 +288,6 @@ /lib/codeql/rust/elements/internal/GenericArgListConstructor.qll linguist-generated /lib/codeql/rust/elements/internal/GenericParamImpl.qll linguist-generated /lib/codeql/rust/elements/internal/GenericParamListConstructor.qll linguist-generated -/lib/codeql/rust/elements/internal/GenericParamListImpl.qll linguist-generated /lib/codeql/rust/elements/internal/IdentPatConstructor.qll linguist-generated /lib/codeql/rust/elements/internal/IfExprConstructor.qll linguist-generated /lib/codeql/rust/elements/internal/ImplConstructor.qll linguist-generated diff --git a/rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll b/rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll index 583ba9fea26a..ceac9e8547db 100644 --- a/rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll +++ b/rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll @@ -239,9 +239,8 @@ final class RecordExprCfgNode extends Nodes::RecordExprCfgNode { pragma[nomagic] ExprCfgNode getFieldExpr(string field) { exists(RecordExprField ref | - ref = node.getRecordExprFieldList().getAField() and - any(ChildMapping mapping).hasCfgChild(node, ref.getExpr(), this, result) and - field = ref.getFieldName() + ref = node.getFieldExpr(field) and + any(ChildMapping mapping).hasCfgChild(node, ref.getExpr(), this, result) ) } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll index 116bb3c7d582..367502c75205 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll @@ -15,6 +15,14 @@ module Impl { private import rust private import PathResolution as PathResolution + pragma[nomagic] + Path getFunctionPath(CallExpr ce) { result = ce.getFunction().(PathExpr).getPath() } + + pragma[nomagic] + PathResolution::ItemNode getResolvedFunction(CallExpr ce) { + result = PathResolution::resolvePath(getFunctionPath(ce)) + } + // the following QLdoc is generated: if you need to edit it, do it in the schema file /** * A function call expression. For example: @@ -28,9 +36,15 @@ module Impl { class CallExpr extends Generated::CallExpr { override string toString() { result = this.getFunction().toAbbreviatedString() + "(...)" } + /** Gets the struct that this call resolves to, if any. */ + Struct getStruct() { result = getResolvedFunction(this) } + + /** Gets the variant that this call resolves to, if any. */ + Variant getVariant() { result = getResolvedFunction(this) } + pragma[nomagic] - private PathResolution::ItemNode getResolvedFunction(int pos) { - result = PathResolution::resolvePath(this.getFunction().(PathExpr).getPath()) and + private PathResolution::ItemNode getResolvedFunctionAndPos(int pos) { + result = getResolvedFunction(this) and exists(this.getArgList().getArg(pos)) } @@ -42,7 +56,7 @@ module Impl { */ pragma[nomagic] TupleField getTupleField(int pos) { - exists(PathResolution::ItemNode i | i = this.getResolvedFunction(pos) | + exists(PathResolution::ItemNode i | i = this.getResolvedFunctionAndPos(pos) | result.isStructField(i, pos) or result.isVariantField(i, pos) ) diff --git a/rust/ql/lib/codeql/rust/elements/internal/FieldExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/FieldExprImpl.qll index b3cd63b79229..a9f35432a90b 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/FieldExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/FieldExprImpl.qll @@ -11,6 +11,9 @@ private import codeql.rust.elements.internal.generated.FieldExpr * be referenced directly. */ module Impl { + private import rust + private import TypeInference as TypeInference + // the following QLdoc is generated: if you need to edit it, do it in the schema file /** * A field access expression. For example: @@ -19,6 +22,12 @@ module Impl { * ``` */ class FieldExpr extends Generated::FieldExpr { + /** Gets the record field that this access references, if any. */ + RecordField getRecordField() { result = TypeInference::resolveRecordFieldExpr(this) } + + /** Gets the tuple field that this access references, if any. */ + TupleField getTupleField() { result = TypeInference::resolveTupleFieldExpr(this) } + override string toString() { exists(string abbr, string name | abbr = this.getExpr().toAbbreviatedString() and diff --git a/rust/ql/lib/codeql/rust/elements/internal/GenericArgListImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/GenericArgListImpl.qll index a701801b68cf..d9856e157308 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/GenericArgListImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/GenericArgListImpl.qll @@ -11,6 +11,8 @@ private import codeql.rust.elements.internal.generated.GenericArgList * be referenced directly. */ module Impl { + private import rust + // the following QLdoc is generated: if you need to edit it, do it in the schema file /** * The base class for generic arguments. @@ -22,5 +24,18 @@ module Impl { override string toString() { result = this.toAbbreviatedString() } override string toAbbreviatedString() { result = "<...>" } + + /** Gets the `i`th type argument of this list. */ + TypeRepr getTypeArgument(int i) { + result = + rank[i + 1](TypeRepr res, int j | + res = this.getGenericArg(j).(TypeArg).getTypeRepr() + | + res order by j + ) + } + + /** Gets a type argument of this list. */ + TypeRepr getATypeArgument() { result = this.getTypeArgument(_) } } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/GenericParamListImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/GenericParamListImpl.qll index a9e9004e4c40..704ce0e6daba 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/GenericParamListImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/GenericParamListImpl.qll @@ -1,4 +1,3 @@ -// generated by codegen, remove this comment if you wish to edit this file /** * This module provides a hand-modifiable wrapper around the generated class `GenericParamList`. * @@ -12,11 +11,26 @@ private import codeql.rust.elements.internal.generated.GenericParamList * be referenced directly. */ module Impl { + private import rust + + // the following QLdoc is generated: if you need to edit it, do it in the schema file /** * A GenericParamList. For example: * ```rust * todo!() * ``` */ - class GenericParamList extends Generated::GenericParamList { } + class GenericParamList extends Generated::GenericParamList { + override string toString() { result = this.toAbbreviatedString() } + + override string toAbbreviatedString() { result = "<...>" } + + /** Gets the `i`th type parameter of this list. */ + TypeParam getTypeParam(int i) { + result = rank[i + 1](TypeParam res, int j | res = this.getGenericParam(j) | res order by j) + } + + /** Gets a type parameter of this list. */ + TypeParam getATypeParam() { result = this.getTypeParam(_) } + } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll index b0923930af30..ced7395c4500 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll @@ -83,6 +83,9 @@ abstract class ItemNode extends AstNode { /** Gets the visibility of this item, if any. */ abstract Visibility getVisibility(); + /** Gets the `i`th type parameter of this item, if any. */ + abstract TypeParam getTypeParam(int i); + /** Holds if this item is declared as `pub`. */ bindingset[this] pragma[inline_late] @@ -207,6 +210,8 @@ private class SourceFileItemNode extends ModuleLikeNode, SourceFile { } override Visibility getVisibility() { none() } + + override TypeParam getTypeParam(int i) { none() } } /** An item that can occur in a trait or an `impl` block. */ @@ -223,6 +228,8 @@ private class ConstItemNode extends AssocItemNode instanceof Const { override Namespace getNamespace() { result.isValue() } override Visibility getVisibility() { result = Const.super.getVisibility() } + + override TypeParam getTypeParam(int i) { none() } } private class EnumItemNode extends ItemNode instanceof Enum { @@ -231,6 +238,8 @@ private class EnumItemNode extends ItemNode instanceof Enum { override Namespace getNamespace() { result.isType() } override Visibility getVisibility() { result = Enum.super.getVisibility() } + + override TypeParam getTypeParam(int i) { result = super.getGenericParamList().getTypeParam(i) } } private class VariantItemNode extends ItemNode instanceof Variant { @@ -240,6 +249,10 @@ private class VariantItemNode extends ItemNode instanceof Variant { if super.getFieldList() instanceof RecordFieldList then result.isType() else result.isValue() } + override TypeParam getTypeParam(int i) { + result = super.getEnum().getGenericParamList().getTypeParam(i) + } + override Visibility getVisibility() { result = Variant.super.getVisibility() } } @@ -250,10 +263,12 @@ class FunctionItemNode extends AssocItemNode instanceof Function { override Namespace getNamespace() { result.isValue() } + override TypeParam getTypeParam(int i) { result = super.getGenericParamList().getTypeParam(i) } + override Visibility getVisibility() { result = Function.super.getVisibility() } } -abstract private class ImplOrTraitItemNode extends ItemNode { +abstract class ImplOrTraitItemNode extends ItemNode { /** Gets an item that may refer to this node using `Self`. */ pragma[nomagic] ItemNode getAnItemInSelfScope() { @@ -265,6 +280,19 @@ abstract private class ImplOrTraitItemNode extends ItemNode { not mid instanceof ImplOrTraitItemNode ) } + + /** Gets a `Self` path that refers to this item. */ + Path getASelfPath() { + isUnqualifiedSelfPath(result) and + this = unqualifiedPathLookup(result) + } + + /** Gets an associated item belonging to this trait or `impl` block. */ + abstract AssocItemNode getAnAssocItem(); + + /** Holds if this trait or `impl` block declares an associated item named `name`. */ + pragma[nomagic] + predicate hasAssocItem(string name) { name = this.getAnAssocItem().getName() } } class ImplItemNode extends ImplOrTraitItemNode instanceof Impl { @@ -276,18 +304,73 @@ class ImplItemNode extends ImplOrTraitItemNode instanceof Impl { TraitItemNode resolveTraitTy() { result = resolvePath(this.getTraitPath()) } - /** Holds if this `impl` block declares an associated item named `name`. */ pragma[nomagic] - predicate hasAssocItem(string name) { - name = super.getAssocItemList().getAnAssocItem().(AssocItemNode).getName() + private TypeRepr getASelfTyArg() { + result = + this.getSelfPath().getPart().getGenericArgList().getAGenericArg().(TypeArg).getTypeRepr() } + pragma[nomagic] + private TypeParamItemNode getASelfTyTypeParamArg(TypeRepr arg) { + arg = this.getASelfTyArg() and + result = resolvePath(arg.(PathTypeRepr).getPath()) + } + + /** + * Holds if this `impl` block is constrained. Examples: + * + * ```rust + * impl Foo { ... } // unconstrained + * + * impl Foo { ... } // unconstrained + * + * impl Foo { ... } // constrained + * + * impl Foo> { ... } // constrained + * + * impl Foo { ... } // constrained + * + * impl Foo where T: Trait { ... } // constrained + * ``` + */ + pragma[nomagic] + predicate isConstrained() { + exists(TypeRepr arg | arg = this.getASelfTyArg() | + not exists(this.getASelfTyTypeParamArg(arg)) + or + this.getASelfTyTypeParamArg(arg).isConstrained() + ) + } + + /** + * Holds if this `impl` block is unconstrained. Examples: + * + * ```rust + * impl Foo { ... } // unconstrained + * + * impl Foo { ... } // unconstrained + * + * impl Foo { ... } // constrained + * + * impl Foo> { ... } // constrained + * + * impl Foo { ... } // constrained + * + * impl Foo where T: Trait { ... } // constrained + * ``` + */ + predicate isUnconstrained() { not this.isConstrained() } + + override AssocItemNode getAnAssocItem() { result = super.getAssocItemList().getAnAssocItem() } + override string getName() { result = "(impl)" } override Namespace getNamespace() { result.isType() // can be referenced with `Self` } + override TypeParam getTypeParam(int i) { result = super.getGenericParamList().getTypeParam(i) } + override Visibility getVisibility() { result = Impl.super.getVisibility() } } @@ -298,6 +381,8 @@ private class MacroCallItemNode extends AssocItemNode instanceof MacroCall { override Namespace getNamespace() { none() } + override TypeParam getTypeParam(int i) { none() } + override Visibility getVisibility() { none() } } @@ -307,6 +392,8 @@ private class ModuleItemNode extends ModuleLikeNode instanceof Module { override Namespace getNamespace() { result.isType() } override Visibility getVisibility() { result = Module.super.getVisibility() } + + override TypeParam getTypeParam(int i) { none() } } private class StructItemNode extends ItemNode instanceof Struct { @@ -320,6 +407,8 @@ private class StructItemNode extends ItemNode instanceof Struct { } override Visibility getVisibility() { result = Struct.super.getVisibility() } + + override TypeParam getTypeParam(int i) { result = super.getGenericParamList().getTypeParam(i) } } class TraitItemNode extends ImplOrTraitItemNode instanceof Trait { @@ -330,17 +419,15 @@ class TraitItemNode extends ImplOrTraitItemNode instanceof Trait { ItemNode resolveABound() { result = resolvePath(this.getABoundPath()) } - /** Holds if this trait declares an associated item named `name`. */ - pragma[nomagic] - predicate hasAssocItem(string name) { - name = super.getAssocItemList().getAnAssocItem().(AssocItemNode).getName() - } + override AssocItemNode getAnAssocItem() { result = super.getAssocItemList().getAnAssocItem() } override string getName() { result = Trait.super.getName().getText() } override Namespace getNamespace() { result.isType() } override Visibility getVisibility() { result = Trait.super.getVisibility() } + + override TypeParam getTypeParam(int i) { result = super.getGenericParamList().getTypeParam(i) } } class TypeAliasItemNode extends AssocItemNode instanceof TypeAlias { @@ -351,6 +438,8 @@ class TypeAliasItemNode extends AssocItemNode instanceof TypeAlias { override Namespace getNamespace() { result.isType() } override Visibility getVisibility() { result = TypeAlias.super.getVisibility() } + + override TypeParam getTypeParam(int i) { result = super.getGenericParamList().getTypeParam(i) } } private class UnionItemNode extends ItemNode instanceof Union { @@ -359,6 +448,8 @@ private class UnionItemNode extends ItemNode instanceof Union { override Namespace getNamespace() { result.isType() } override Visibility getVisibility() { result = Union.super.getVisibility() } + + override TypeParam getTypeParam(int i) { result = super.getGenericParamList().getTypeParam(i) } } private class UseItemNode extends ItemNode instanceof Use { @@ -367,6 +458,8 @@ private class UseItemNode extends ItemNode instanceof Use { override Namespace getNamespace() { none() } override Visibility getVisibility() { none() } + + override TypeParam getTypeParam(int i) { none() } } private class BlockExprItemNode extends ItemNode instanceof BlockExpr { @@ -375,6 +468,8 @@ private class BlockExprItemNode extends ItemNode instanceof BlockExpr { override Namespace getNamespace() { none() } override Visibility getVisibility() { none() } + + override TypeParam getTypeParam(int i) { none() } } private class TypeParamItemNode extends ItemNode instanceof TypeParam { @@ -385,12 +480,50 @@ private class TypeParamItemNode extends ItemNode instanceof TypeParam { ItemNode resolveABound() { result = resolvePath(this.getABoundPath()) } + /** + * Holds if this type parameter is constrained. Examples: + * + * ```rust + * impl Foo { ... } // unconstrained + * + * impl Foo { ... } // constrained + * + * impl Foo where T: Trait { ... } // constrained + * ``` + */ + pragma[nomagic] + predicate isConstrained() { + exists(this.getABoundPath()) + or + exists(ItemNode declaringItem, WherePred wp | + this = resolvePath(wp.getTypeRepr().(PathTypeRepr).getPath()) and + wp = declaringItem.getADescendant() and + this = declaringItem.getADescendant() + ) + } + + /** + * Holds if this type parameter is unconstrained. Examples: + * + * ```rust + * impl Foo { ... } // unconstrained + * + * impl Foo { ... } // constrained + * + * impl Foo where T: Trait { ... } // constrained + * ``` + */ + pragma[nomagic] + predicate isUnconstrained() { not this.isConstrained() } + override string getName() { result = TypeParam.super.getName().getText() } override Namespace getNamespace() { result.isType() } override Visibility getVisibility() { none() } + override TypeParam getTypeParam(int i) { none() } + override Location getLocation() { result = TypeParam.super.getName().getLocation() } } @@ -547,14 +680,23 @@ private ItemNode getASuccessor(ItemNode pred, string name, Namespace ns) { } pragma[nomagic] -private ItemNode resolvePath0(RelevantPath path) { - exists(ItemNode encl, Namespace ns, string name, ItemNode res | +private ItemNode unqualifiedPathLookup(RelevantPath path) { + exists(ItemNode encl, Namespace ns, string name | unqualifiedPathLookup(path, name, ns, encl) and - res = getASuccessor(encl, name, ns) - | + result = getASuccessor(encl, name, ns) + ) +} + +pragma[nomagic] +private predicate isUnqualifiedSelfPath(RelevantPath path) { path.isUnqualified("Self") } + +pragma[nomagic] +private ItemNode resolvePath0(RelevantPath path) { + exists(ItemNode res | + res = unqualifiedPathLookup(path) and if not any(RelevantPath parent).getQualifier() = path and - name = "Self" and + isUnqualifiedSelfPath(path) and res instanceof ImplItemNode then result = res.(ImplItemNode).resolveSelfTy() else result = res diff --git a/rust/ql/lib/codeql/rust/elements/internal/RecordExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/RecordExprImpl.qll index 7a0c0275c99a..cf763467c697 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/RecordExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/RecordExprImpl.qll @@ -27,13 +27,23 @@ module Impl { class RecordExpr extends Generated::RecordExpr { override string toString() { result = this.getPath().toString() + " {...}" } + /** Gets the record expression for the field `name`. */ + pragma[nomagic] + RecordExprField getFieldExpr(string name) { + result = this.getRecordExprFieldList().getAField() and + name = result.getFieldName() + } + + pragma[nomagic] + private PathResolution::ItemNode getResolvedPath(string name) { + result = PathResolution::resolvePath(this.getPath()) and + exists(this.getFieldExpr(name)) + } + /** Gets the record field that matches the `name` field of this record expression. */ pragma[nomagic] RecordField getRecordField(string name) { - exists(PathResolution::ItemNode i | - i = PathResolution::resolvePath(this.getPath()) and - name = this.getRecordExprFieldList().getAField().getFieldName() - | + exists(PathResolution::ItemNode i | i = this.getResolvedPath(name) | result.isStructField(i, name) or result.isVariantField(i, name) ) diff --git a/rust/ql/lib/codeql/rust/elements/internal/RecordPatImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/RecordPatImpl.qll index 037ac67bbde9..8a5e90f1c407 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/RecordPatImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/RecordPatImpl.qll @@ -27,13 +27,16 @@ module Impl { class RecordPat extends Generated::RecordPat { override string toString() { result = this.getPath().toAbbreviatedString() + " {...}" } + pragma[nomagic] + private PathResolution::ItemNode getResolvedPath(string name) { + result = PathResolution::resolvePath(this.getPath()) and + name = this.getRecordPatFieldList().getAField().getFieldName() + } + /** Gets the record field that matches the `name` pattern of this pattern. */ pragma[nomagic] RecordField getRecordField(string name) { - exists(PathResolution::ItemNode i | - i = PathResolution::resolvePath(this.getPath()) and - name = this.getRecordPatFieldList().getAField().getFieldName() - | + exists(PathResolution::ItemNode i | i = this.getResolvedPath(name) | result.isStructField(i, name) or result.isVariantField(i, name) ) diff --git a/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll index 23d04a2ffed6..414dec3f06bb 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll @@ -32,5 +32,17 @@ module Impl { /** Gets the `i`th tuple field, if any. */ pragma[nomagic] TupleField getTupleField(int i) { result = this.getFieldList().(TupleFieldList).getField(i) } + + /** Holds if this struct uses tuple fields. */ + pragma[nomagic] + predicate isTuple() { this.getFieldList() instanceof TupleFieldList } + + /** + * Holds if this struct uses record fields. + * + * Empty structs are considered to use record fields. + */ + pragma[nomagic] + predicate isRecord() { not this.isTuple() } } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/Type.qll b/rust/ql/lib/codeql/rust/elements/internal/Type.qll new file mode 100644 index 000000000000..4b1aecdb7708 --- /dev/null +++ b/rust/ql/lib/codeql/rust/elements/internal/Type.qll @@ -0,0 +1,329 @@ +/** Provides classes representing types without type arguments. */ + +private import rust +private import PathResolution +private import TypeInference +private import TypeMention +private import codeql.rust.internal.CachedStages + +cached +newtype TType = + TStruct(Struct s) { Stages::TypeInference::ref() } or + TEnum(Enum e) or + TTrait(Trait t) or + TImpl(Impl i) or + TArrayType() or // todo: add size? + TRefType() or // todo: add mut? + TTypeParamTypeParameter(TypeParam t) or + TRefTypeParameter() or + TSelfTypeParameter() + +/** A type without type arguments. */ +abstract class Type extends TType { + /** Gets the method `name` belonging to this type, if any. */ + pragma[nomagic] + abstract Function getMethod(string name); + + /** Gets the record field `name` belonging to this type, if any. */ + pragma[nomagic] + abstract RecordField getRecordField(string name); + + /** Gets the `i`th tuple field belonging to this type, if any. */ + pragma[nomagic] + abstract TupleField getTupleField(int i); + + /** Gets the `i`th type parameter of this type, if any. */ + abstract TypeParameter getTypeParameter(int i); + + /** 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(); + + /** Gets the location of this type. */ + abstract Location getLocation(); +} + +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).isUnconstrained() + ) + } + + final override ImplMention getABaseTypeMention() { + this.asItemNode() = result.resolveSelfTy() and + result.isUnconstrained() + } +} + +/** A struct type. */ +class StructType extends StructOrEnumType, TStruct { + private Struct struct; + + StructType() { this = TStruct(struct) } + + override ItemNode asItemNode() { result = struct } + + override RecordField getRecordField(string name) { result = struct.getRecordField(name) } + + override TupleField getTupleField(int i) { result = struct.getTupleField(i) } + + override TypeParameter getTypeParameter(int i) { + result = TTypeParamTypeParameter(struct.getGenericParamList().getTypeParam(i)) + } + + override string toString() { result = struct.toString() } + + override Location getLocation() { result = struct.getLocation() } +} + +/** An enum type. */ +class EnumType extends StructOrEnumType, TEnum { + private Enum enum; + + EnumType() { this = TEnum(enum) } + + override ItemNode asItemNode() { result = enum } + + override RecordField getRecordField(string name) { none() } + + override TupleField getTupleField(int i) { none() } + + override TypeParameter getTypeParameter(int i) { + result = TTypeParamTypeParameter(enum.getGenericParamList().getTypeParam(i)) + } + + override string toString() { result = enum.toString() } + + override Location getLocation() { result = enum.getLocation() } +} + +/** A trait type. */ +class TraitType extends Type, TTrait { + private Trait trait; + + TraitType() { this = TTrait(trait) } + + override Function getMethod(string name) { result = trait.(ItemNode).getASuccessor(name) } + + override RecordField getRecordField(string name) { none() } + + override TupleField getTupleField(int i) { none() } + + override TypeParameter getTypeParameter(int i) { + result = TTypeParamTypeParameter(trait.getGenericParamList().getTypeParam(i)) + or + result = TSelfTypeParameter() and + i = -1 + } + + pragma[nomagic] + private TypeReprMention getABoundMention() { + result = trait.getTypeBoundList().getABound().getTypeRepr() + } + + override TypeMention getABaseTypeMention() { result = this.getABoundMention() } + + override string toString() { result = trait.toString() } + + override Location getLocation() { result = trait.getLocation() } +} + +/** + * An `impl` block type. + * + * Although `impl` blocks are not really types, we treat them as such in order + * to be able to match type parameters from structs (or enums) with type + * parameters from `impl` blocks. For example, in + * + * ```rust + * struct S(T1); + * + * impl S { + * fn id(self) -> S { self } + * } + * + * let x : S(i64) = S(42); + * x.id(); + * ``` + * + * we pretend that the `impl` block is a base type mention of the struct `S`, + * with type argument `T1`. This means that from knowing that `x` has type + * `S(i64)`, we can first match `i64` with `T1`, and then by matching `T1` with + * `T2`, we can match `i64` with `T2`. + * + * `impl` blocks can also have base type mentions, namely the trait that they + * implement (if any). Example: + * + * ```rust + * struct S(T1); + * + * trait Trait { + * fn f(self) -> T2; + * + * fn g(self) -> T2 { self.f() } + * } + * + * impl Trait for S { // `Trait` is a base type mention of this `impl` block + * fn f(self) -> T3 { + * match self { + * S(x) => x + * } + * } + * } + * + * let x : S(i64) = S(42); + * x.g(); + * ``` + * + * In this case we can match `i64` with `T1`, `T1` with `T3`, and `T3` with `T2`, + * allowing us match `i64` with `T2`, and hence infer that the return type of `g` + * is `i64`. + */ +class ImplType extends Type, TImpl { + private Impl impl; + + ImplType() { this = TImpl(impl) } + + override Function getMethod(string name) { result = impl.(ItemNode).getASuccessor(name) } + + override RecordField getRecordField(string name) { none() } + + override TupleField getTupleField(int i) { none() } + + override TypeParameter getTypeParameter(int i) { + result = TTypeParamTypeParameter(impl.getGenericParamList().getTypeParam(i)) + or + result = TSelfTypeParameter() and + i = -1 + } + + override TypeMention getABaseTypeMention() { result = impl.getTrait() } + + override string toString() { result = impl.toString() } + + override Location getLocation() { result = impl.getLocation() } +} + +/** + * An array type. + * + * Array types like `[i64; 5]` are modeled as normal generic types + * with a single type argument. + */ +class ArrayType extends Type, TArrayType { + ArrayType() { this = TArrayType() } + + override Function getMethod(string name) { none() } + + override RecordField getRecordField(string name) { none() } + + override TupleField getTupleField(int i) { none() } + + override TypeParameter getTypeParameter(int i) { + none() // todo + } + + override TypeMention getABaseTypeMention() { none() } + + override string toString() { result = "[]" } + + override Location getLocation() { result instanceof EmptyLocation } +} + +/** + * A reference type. + * + * Reference types like `& i64` are modeled as normal generic types + * with a single type argument. + */ +class RefType extends Type, TRefType { + RefType() { this = TRefType() } + + override Function getMethod(string name) { none() } + + override RecordField getRecordField(string name) { none() } + + override TupleField getTupleField(int i) { none() } + + override TypeParameter getTypeParameter(int i) { + result = TRefTypeParameter() and + i = 0 + } + + override TypeMention getABaseTypeMention() { none() } + + override string toString() { result = "&" } + + override Location getLocation() { result instanceof EmptyLocation } +} + +/** A type parameter. */ +abstract class TypeParameter extends Type { + override TypeMention getABaseTypeMention() { none() } + + override RecordField getRecordField(string name) { none() } + + override TupleField getTupleField(int i) { none() } + + override TypeParameter getTypeParameter(int i) { none() } +} + +/** A type parameter from source code. */ +class TypeParamTypeParameter extends TypeParameter, TTypeParamTypeParameter { + private TypeParam typeParam; + + TypeParamTypeParameter() { this = TTypeParamTypeParameter(typeParam) } + + TypeParam getTypeParam() { result = typeParam } + + override Function getMethod(string name) { result = typeParam.(ItemNode).getASuccessor(name) } + + override string toString() { result = typeParam.toString() } + + override Location getLocation() { result = typeParam.getLocation() } +} + +/** 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 } +} + +/** An implicit `Self` type parameter. */ +class SelfTypeParameter extends TypeParameter, TSelfTypeParameter { + override Function getMethod(string name) { none() } + + override string toString() { result = "(Self)" } + + override Location getLocation() { result instanceof EmptyLocation } +} diff --git a/rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll b/rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll new file mode 100644 index 000000000000..025b3ac5d487 --- /dev/null +++ b/rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll @@ -0,0 +1,999 @@ +/** Provides functionality for inferring types. */ + +private import rust +private import PathResolution +private import Type +private import Type as T +private import TypeMention +private import codeql.typeinference.internal.TypeInference + +class Type = T::Type; + +private module Input1 implements InputSig1 { + private import Type as T + private import codeql.rust.elements.internal.generated.Raw + private import codeql.rust.elements.internal.generated.Synth + + class Type = T::Type; + + class TypeParameter = T::TypeParameter; + + private newtype TTypeArgumentPosition = + // method type parameters are matched by position instead of by type + // parameter entity, to avoid extra recursion through method call resolution + TMethodTypeArgumentPosition(int pos) { + exists(any(MethodCallExpr mce).getGenericArgList().getTypeArgument(pos)) + } or + TTypeParamTypeArgumentPosition(TypeParam tp) + + class TypeArgumentPosition extends TTypeArgumentPosition { + int asMethodTypeArgumentPosition() { this = TMethodTypeArgumentPosition(result) } + + TypeParam asTypeParam() { this = TTypeParamTypeArgumentPosition(result) } + + string toString() { + result = this.asMethodTypeArgumentPosition().toString() + or + result = this.asTypeParam().toString() + } + } + + class TypeParameterPosition = TypeParam; + + bindingset[apos] + bindingset[ppos] + predicate typeArgumentParameterPositionMatch(TypeArgumentPosition apos, TypeParameterPosition ppos) { + apos.asTypeParam() = ppos + or + apos.asMethodTypeArgumentPosition() = ppos.getPosition() + } + + private predicate id(Raw::TypeParam x, Raw::TypeParam y) { x = y } + + private predicate idOfRaw(Raw::TypeParam x, int y) = equivalenceRelation(id/2)(x, y) + + private int idOf(TypeParam node) { idOfRaw(Synth::convertAstNodeToRaw(node), result) } + + int getTypeParameterId(TypeParameter tp) { + tp = + rank[result](TypeParameter tp0, int kind, int id | + tp0 instanceof RefTypeParameter and + kind = 0 and + id = 0 + or + tp0 instanceof SelfTypeParameter and + kind = 0 and + id = 1 + or + id = idOf(tp0.(TypeParamTypeParameter).getTypeParam()) and + kind = 1 + | + tp0 order by kind, id + ) + } +} + +private import Input1 + +private module M1 = Make1; + +private import M1 + +class TypePath = M1::TypePath; + +module TypePath = M1::TypePath; + +private module Input2 implements InputSig2 { + private import TypeMention as TM + + class TypeMention = TM::TypeMention; + + TypeMention getABaseTypeMention(Type t) { result = t.getABaseTypeMention() } +} + +private module M2 = Make2; + +private import M2 + +module Consistency = M2::Consistency; + +/** Gets the type annotation that applies to `n`, if any. */ +private TypeMention getTypeAnnotation(AstNode n) { + exists(LetStmt let | + n = let.getPat() and + result = let.getTypeRepr() + ) + or + result = n.(SelfParam).getTypeRepr() + or + exists(Param p | + n = p.getPat() and + result = p.getTypeRepr() + ) + or + exists(Function f | + result = f.getRetType().getTypeRepr() and + n = f.getBody() + ) +} + +/** Gets the type of `n`, which has an explicit type annotation. */ +pragma[nomagic] +private Type resolveAnnotatedType(AstNode n, TypePath path) { + result = getTypeAnnotation(n).resolveTypeAt(path) +} + +/** + * Holds if the type of `n1` at `path1` is the same as the type of `n2` at `path2`. + */ +bindingset[path1] +bindingset[path2] +private predicate typeSymmetry(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { + exists(Variable v | + path1 = path2 and + n1 = v.getAnAccess() + | + n2 = v.getPat() + or + n2 = v.getParameter().(SelfParam) + ) + or + exists(LetStmt let | + let.getPat() = n1 and + let.getInitializer() = n2 and + path1 = path2 + ) + or + n2 = + any(PrefixExpr pe | + pe.getOperatorName() = "*" and + pe.getExpr() = n1 and + path1 = TypePath::cons(TRefTypeParameter(), path2) + ) + or + n1 = n2.(ParenExpr).getExpr() and + path1 = path2 + or + n1 = n2.(BlockExpr).getStmtList().getTailExpr() and + path1 = path2 +} + +pragma[nomagic] +private Type resolveTypeSymmetry(AstNode n, TypePath path) { + exists(AstNode n2, TypePath path2 | result = resolveType(n2, path2) | + typeSymmetry(n, path, n2, path2) + or + typeSymmetry(n2, path2, n, path) + ) +} + +/** + * Gets the type of the implicitly typed `self` parameter, taking into account + * whether the parameter is passed by value or by reference. + */ +bindingset[self, suffix, t] +pragma[inline_late] +private Type getRefAdjustImplicitSelfType(SelfParam self, TypePath suffix, Type t, TypePath path) { + not self.hasTypeRepr() and + ( + if self.isRef() + then + // `fn f(&self, ...)` + path.isEmpty() and + result = TRefType() + or + path = TypePath::cons(TRefTypeParameter(), suffix) and + result = t + else ( + // `fn f(self, ...)` + path = suffix and + result = t + ) + ) +} + +pragma[nomagic] +private Type resolveImplSelfType(Impl i, TypePath path) { + result = i.getSelfTy().(TypeReprMention).resolveTypeAt(path) +} + +pragma[nomagic] +private Type resolveTraitSelfType(Trait t, TypePath path) { + result = TTrait(t) and + path.isEmpty() + or + result = TTypeParamTypeParameter(t.getGenericParamList().getATypeParam()) and + path = TypePath::singleton(result) +} + +/** Gets the type at `path` of the implicitly typed `self` parameter. */ +pragma[nomagic] +private Type resolveImplicitSelfType(SelfParam self, TypePath path) { + exists(ImplOrTraitItemNode i, Function f, TypePath suffix, Type t | + f = i.getAnAssocItem() and + self = f.getParamList().getSelfParam() and + result = getRefAdjustImplicitSelfType(self, suffix, t, path) + | + t = resolveImplSelfType(i, suffix) + or + t = resolveTraitSelfType(i, suffix) + ) +} + +private TypeMention getExplicitTypeArgMention(Path path, TypeParam tp) { + exists(int i | + result = path.getPart().getGenericArgList().getTypeArgument(pragma[only_bind_into](i)) and + tp = resolvePath(path).getTypeParam(pragma[only_bind_into](i)) + ) + or + result = getExplicitTypeArgMention(path.getQualifier(), tp) +} + +/** + * A matching configuration for resolving types of record expressions + * like `Foo { bar = baz }`. + */ +private module RecordExprMatchingInput implements MatchingInputSig { + private newtype TPos = + TFieldPos(string name) { exists(any(Declaration decl).getField(name)) } or + TRecordPos() + + class DeclarationPosition extends TPos { + string asFieldPos() { this = TFieldPos(result) } + + predicate isRecordPos() { this = TRecordPos() } + + string toString() { + result = this.asFieldPos() + or + this.isRecordPos() and + result = "(record)" + } + } + + abstract class Declaration extends AstNode { + abstract TypeParam getATypeParam(); + + final TypeParameter getTypeParameter(TypeParameterPosition ppos) { + result.(TypeParamTypeParameter).getTypeParam() = ppos and + ppos = this.getATypeParam() + } + + abstract RecordField getField(string name); + + Type getDeclaredType(DeclarationPosition dpos, TypePath path) { + // type of a field + exists(TypeReprMention tp | + tp = this.getField(dpos.asFieldPos()).getTypeRepr() and + result = tp.resolveTypeAt(path) + ) + or + // type parameter of the record itself + dpos.isRecordPos() and + result = this.getTypeParameter(_) and + path = TypePath::singleton(result) + } + } + + private class RecordStructDecl extends Declaration, Struct { + RecordStructDecl() { this.isRecord() } + + override TypeParam getATypeParam() { result = this.getGenericParamList().getATypeParam() } + + override RecordField getField(string name) { result = this.getRecordField(name) } + + override Type getDeclaredType(DeclarationPosition dpos, TypePath path) { + result = super.getDeclaredType(dpos, path) + or + // type of the struct itself + dpos.isRecordPos() and + path.isEmpty() and + result = TStruct(this) + } + } + + private class RecordVariantDecl extends Declaration, Variant { + RecordVariantDecl() { this.isRecord() } + + Enum getEnum() { result.getVariantList().getAVariant() = this } + + override TypeParam getATypeParam() { + result = this.getEnum().getGenericParamList().getATypeParam() + } + + override RecordField getField(string name) { result = this.getRecordField(name) } + + override Type getDeclaredType(DeclarationPosition dpos, TypePath path) { + result = super.getDeclaredType(dpos, path) + or + // type of the enum itself + dpos.isRecordPos() and + path.isEmpty() and + result = TEnum(this.getEnum()) + } + } + + class AccessPosition = DeclarationPosition; + + class Access extends RecordExpr { + Type getExplicitTypeArgument(TypeArgumentPosition apos, TypePath path) { + result = getExplicitTypeArgMention(this.getPath(), apos.asTypeParam()).resolveTypeAt(path) + } + + AstNode getNodeAt(AccessPosition apos) { + result = this.getFieldExpr(apos.asFieldPos()).getExpr() + or + result = this and + apos.isRecordPos() + } + + Type getResolvedType(AccessPosition apos, TypePath path) { + result = resolveType(this.getNodeAt(apos), path) + } + + Declaration getTarget() { result = resolvePath(this.getPath()) } + } + + predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) { + apos = dpos + } +} + +private module RecordExprMatching = Matching; + +/** + * Gets the type of `n` at `path`, where `n` is either a record expression or + * a field expression of a record expression. + */ +pragma[nomagic] +private Type resolveRecordExprType(AstNode n, TypePath path) { + exists(RecordExprMatchingInput::Access a, RecordExprMatchingInput::AccessPosition apos | + n = a.getNodeAt(apos) and + result = RecordExprMatching::resolveAccessType(a, apos, path) + ) +} + +pragma[nomagic] +private Type resolvePathExprType(PathExpr pe, TypePath path) { + // nullary struct/variant constructors + not exists(CallExpr ce | pe = ce.getFunction()) and + path.isEmpty() and + exists(ItemNode i | i = resolvePath(pe.getPath()) | + result = TEnum(i.(Variant).getEnum()) + or + result = TStruct(i) + ) +} + +/** + * A matching configuration for resolving types of call expressions + * like `foo::bar(baz)` and `foo.bar(baz)`. + */ +private module CallExprBaseMatchingInput implements MatchingInputSig { + private predicate paramPos(ParamList pl, Param p, int pos, boolean inMethod) { + p = pl.getParam(pos) and + if pl.hasSelfParam() then inMethod = true else inMethod = false + } + + private newtype TDeclarationPosition = + TSelfDeclarationPosition() or + TPositionalDeclarationPosition(int pos, boolean inMethod) { paramPos(_, _, pos, inMethod) } or + TReturnDeclarationPosition() + + class DeclarationPosition extends TDeclarationPosition { + predicate isSelf() { this = TSelfDeclarationPosition() } + + int asPosition(boolean inMethod) { this = TPositionalDeclarationPosition(result, inMethod) } + + predicate isReturn() { this = TReturnDeclarationPosition() } + + string toString() { + this.isSelf() and + result = "self" + or + result = this.asPosition(_).toString() + or + this.isReturn() and + result = "(return)" + } + } + + abstract class Declaration extends AstNode { + abstract TypeParam getATypeParam(); + + final TypeParameter getTypeParameter(TypeParameterPosition ppos) { + result.(TypeParamTypeParameter).getTypeParam() = ppos and + ppos = this.getATypeParam() + } + + pragma[nomagic] + abstract Type getParameterType(DeclarationPosition dpos, TypePath path); + + abstract Type getReturnType(TypePath path); + + final Type getDeclaredType(DeclarationPosition dpos, TypePath path) { + result = this.getParameterType(dpos, path) + or + dpos.isReturn() and + result = this.getReturnType(path) + } + } + + private class TupleStructDecl extends Declaration, Struct { + TupleStructDecl() { this.isTuple() } + + override TypeParam getATypeParam() { result = this.getGenericParamList().getATypeParam() } + + override Type getParameterType(DeclarationPosition dpos, TypePath path) { + exists(int pos | + result = this.getTupleField(pos).getTypeRepr().(TypeReprMention).resolveTypeAt(path) and + dpos = TPositionalDeclarationPosition(pos, false) + ) + } + + override Type getReturnType(TypePath path) { + result = TStruct(this) and + path.isEmpty() + or + result = TTypeParamTypeParameter(this.getGenericParamList().getATypeParam()) and + path = TypePath::singleton(result) + } + } + + private class TupleVariantDecl extends Declaration, Variant { + TupleVariantDecl() { this.isTuple() } + + override TypeParam getATypeParam() { + result = this.getEnum().getGenericParamList().getATypeParam() + } + + override Type getParameterType(DeclarationPosition dpos, TypePath path) { + exists(int p | + result = this.getTupleField(p).getTypeRepr().(TypeReprMention).resolveTypeAt(path) and + dpos = TPositionalDeclarationPosition(p, false) + ) + } + + override Type getReturnType(TypePath path) { + exists(Enum enum | enum = this.getEnum() | + result = TEnum(enum) and + path.isEmpty() + or + result = TTypeParamTypeParameter(enum.getGenericParamList().getATypeParam()) and + path = TypePath::singleton(result) + ) + } + } + + pragma[nomagic] + private Type resolveAnnotatedTypeInclSelf(AstNode n, TypePath path) { + result = getTypeAnnotation(n).resolveTypeAtInclSelf(path) + } + + private class FunctionDecl extends Declaration, Function { + override TypeParam getATypeParam() { result = this.getGenericParamList().getATypeParam() } + + override Type getParameterType(DeclarationPosition dpos, TypePath path) { + exists(Param p, int i, boolean inMethod | + paramPos(this.getParamList(), p, i, inMethod) and + dpos = TPositionalDeclarationPosition(i, inMethod) and + result = resolveAnnotatedTypeInclSelf(p.getPat(), path) + ) + or + exists(SelfParam self | + self = pragma[only_bind_into](this.getParamList().getSelfParam()) and + dpos.isSelf() + | + // `self` parameter with type annotation + result = resolveAnnotatedTypeInclSelf(self, path) + or + // `self` parameter without type annotation + result = resolveImplicitSelfType(self, path) + or + // `self` parameter without type annotation should also have the special `Self` type + result = getRefAdjustImplicitSelfType(self, TypePath::nil(), TSelfTypeParameter(), path) + ) + } + + override Type getReturnType(TypePath path) { + result = this.getRetType().getTypeRepr().(TypeReprMention).resolveTypeAtInclSelf(path) + } + } + + private predicate argPos(CallExprBase call, Expr e, int pos, boolean isMethodCall) { + exists(ArgList al | + e = al.getArg(pos) and + call.getArgList() = al and + if call instanceof MethodCallExpr then isMethodCall = true else isMethodCall = false + ) + } + + private newtype TAccessPosition = + TSelfAccessPosition() or + TPositionalAccessPosition(int pos, boolean isMethodCall) { argPos(_, _, pos, isMethodCall) } or + TReturnAccessPosition() + + class AccessPosition extends TAccessPosition { + predicate isSelf() { this = TSelfAccessPosition() } + + int asPosition(boolean isMethodCall) { this = TPositionalAccessPosition(result, isMethodCall) } + + predicate isReturn() { this = TReturnAccessPosition() } + + string toString() { + this.isSelf() and + result = "self" + or + result = this.asPosition(_).toString() + or + this.isReturn() and + result = "(return)" + } + } + + private import codeql.rust.elements.internal.CallExprImpl::Impl as CallExprImpl + + class Access extends CallExprBase { + private TypeReprMention getMethodTypeArg(int i) { + result = this.(MethodCallExpr).getGenericArgList().getTypeArgument(i) + } + + Type getExplicitTypeArgument(TypeArgumentPosition apos, TypePath path) { + exists(TypeMention arg | result = arg.resolveTypeAt(path) | + arg = getExplicitTypeArgMention(CallExprImpl::getFunctionPath(this), apos.asTypeParam()) + or + arg = this.getMethodTypeArg(apos.asMethodTypeArgumentPosition()) + ) + } + + AstNode getNodeAt(AccessPosition apos) { + exists(int p, boolean isMethodCall | + argPos(this, result, p, isMethodCall) and + apos = TPositionalAccessPosition(p, isMethodCall) + ) + or + result = this.(MethodCallExpr).getReceiver() and + apos = TSelfAccessPosition() + or + result = this and + apos = TReturnAccessPosition() + } + + Type getResolvedType(AccessPosition apos, TypePath path) { + result = resolveType(this.getNodeAt(apos), path) + } + + Declaration getTarget() { + result = + [ + CallExprImpl::getResolvedFunction(this).(AstNode), + this.(CallExpr).getStruct(), + this.(CallExpr).getVariant(), + // mutual recursion; resolving method calls requires resolving types and vice versa + resolveMethodCallExpr(this) + ] + } + } + + predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) { + apos.isSelf() and + dpos.isSelf() + or + exists(int pos, boolean isMethodCall | pos = apos.asPosition(isMethodCall) | + pos = 0 and + isMethodCall = false and + dpos.isSelf() + or + isMethodCall = false and + pos = dpos.asPosition(true) + 1 + or + pos = dpos.asPosition(isMethodCall) + ) + or + apos.isReturn() and + dpos.isReturn() + } + + bindingset[apos, target, path, t] + pragma[inline_late] + predicate adjustAccessType( + AccessPosition apos, Declaration target, TypePath path, Type t, TypePath pathAdj, Type tAdj + ) { + if apos.isSelf() + then + exists(Type selfParamType | + selfParamType = target.getParameterType(TSelfDeclarationPosition(), TypePath::nil()) + | + if selfParamType = TRefType() + then + if t != TRefType() and path.isEmpty() + then + // adjust for implicit borrow + pathAdj.isEmpty() and + tAdj = TRefType() + or + // adjust for implicit borrow + pathAdj = TypePath::singleton(TRefTypeParameter()) and + tAdj = t + else + if path.startsWith(TRefTypeParameter(), _) + then + pathAdj = path and + tAdj = t + else ( + // adjust for implicit borrow + not (t = TRefType() and path.isEmpty()) and + pathAdj = TypePath::cons(TRefTypeParameter(), path) and + tAdj = t + ) + else ( + // adjust for implicit deref + path.startsWith(TRefTypeParameter(), pathAdj) and + tAdj = t + or + not path.startsWith(TRefTypeParameter(), _) and + not (t = TRefType() and path.isEmpty()) and + pathAdj = path and + tAdj = t + ) + ) + else ( + pathAdj = path and + tAdj = t + ) + } + + pragma[nomagic] + additional Type resolveReceiverType(AstNode n) { + exists(Access a, AccessPosition apos | + result = resolveType(n) and + n = a.getNodeAt(apos) and + apos.isSelf() + ) + } +} + +private module CallExprBaseMatching = Matching; + +/** + * Gets the type of `n` at `path`, where `n` is either a call or an + * argument/receiver of a call. + */ +pragma[nomagic] +private Type resolveCallExprBaseType(AstNode n, TypePath path) { + exists( + CallExprBaseMatchingInput::Access a, CallExprBaseMatchingInput::AccessPosition apos, + TypePath path0 + | + n = a.getNodeAt(apos) and + result = CallExprBaseMatching::resolveAccessType(a, apos, path0) + | + if apos.isSelf() + then + exists(Type receiverType | receiverType = CallExprBaseMatchingInput::resolveReceiverType(n) | + if receiverType = TRefType() + then + path = path0 and + path0.startsWith(TRefTypeParameter(), _) + or + // adjust for implicit deref + not path0.startsWith(TRefTypeParameter(), _) and + not (path0.isEmpty() and result = TRefType()) and + path = TypePath::cons(TRefTypeParameter(), path0) + else ( + not path0.startsWith(TRefTypeParameter(), _) and + not (path0.isEmpty() and result = TRefType()) and + path = path0 + or + // adjust for implicit borrow + path0.startsWith(TRefTypeParameter(), path) + ) + ) + else path = path0 + ) +} + +/** + * A matching configuration for resolving types of field expressions + * like `x.field`. + */ +private module FieldExprMatchingInput implements MatchingInputSig { + private newtype TDeclarationPosition = + TSelfDeclarationPosition() or + TFieldPos() + + class DeclarationPosition extends TDeclarationPosition { + predicate isSelf() { this = TSelfDeclarationPosition() } + + predicate isField() { this = TFieldPos() } + + string toString() { + this.isSelf() and + result = "self" + or + this.isField() and + result = "(field)" + } + } + + abstract class Declaration extends AstNode { + TypeParameter getTypeParameter(TypeParameterPosition ppos) { none() } + + abstract TypeRepr getTypeRepr(); + + Type getDeclaredType(DeclarationPosition dpos, TypePath path) { + dpos.isSelf() and + // no case for variants as those can only be destructured using pattern matching + exists(Struct s | s.getRecordField(_) = this or s.getTupleField(_) = this | + result = TStruct(s) and + path.isEmpty() + or + result = TTypeParamTypeParameter(s.getGenericParamList().getATypeParam()) and + path = TypePath::singleton(result) + ) + or + dpos.isField() and + result = this.getTypeRepr().(TypeReprMention).resolveTypeAt(path) + } + } + + private class RecordFieldDecl extends Declaration instanceof RecordField { + override TypeRepr getTypeRepr() { result = RecordField.super.getTypeRepr() } + } + + private class TupleFieldDecl extends Declaration instanceof TupleField { + override TypeRepr getTypeRepr() { result = TupleField.super.getTypeRepr() } + } + + class AccessPosition = DeclarationPosition; + + class Access extends FieldExpr { + Type getExplicitTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } + + AstNode getNodeAt(AccessPosition apos) { + result = this.getExpr() and + apos.isSelf() + or + result = this and + apos.isField() + } + + Type getResolvedType(AccessPosition apos, TypePath path) { + result = resolveType(this.getNodeAt(apos), path) + } + + Declaration getTarget() { + // mutual recursion; resolving fields requires resolving types and vice versa + result = [resolveRecordFieldExpr(this).(AstNode), resolveTupleFieldExpr(this)] + } + } + + predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) { + apos = dpos + } + + bindingset[apos, target, path, t] + pragma[inline_late] + predicate adjustAccessType( + AccessPosition apos, Declaration target, TypePath path, Type t, TypePath pathAdj, Type tAdj + ) { + exists(target) and + if apos.isSelf() + then + // adjust for implicit deref + path.startsWith(TRefTypeParameter(), pathAdj) and + tAdj = t + or + not path.startsWith(TRefTypeParameter(), _) and + not (t = TRefType() and path.isEmpty()) and + pathAdj = path and + tAdj = t + else ( + pathAdj = path and + tAdj = t + ) + } + + pragma[nomagic] + additional Type resolveReceiverType(AstNode n) { + exists(Access a, AccessPosition apos | + result = resolveType(n) and + n = a.getNodeAt(apos) and + apos.isSelf() + ) + } +} + +private module FieldExprMatching = Matching; + +/** + * Gets the type of `n` at `path`, where `n` is either a field expression or + * the receiver of field expression call. + */ +pragma[nomagic] +private Type resolveFieldExprType(AstNode n, TypePath path) { + exists( + FieldExprMatchingInput::Access a, FieldExprMatchingInput::AccessPosition apos, TypePath path0 + | + n = a.getNodeAt(apos) and + result = FieldExprMatching::resolveAccessType(a, apos, path0) + | + if apos.isSelf() + then + exists(Type receiverType | receiverType = FieldExprMatchingInput::resolveReceiverType(n) | + if receiverType = TRefType() + then + // adjust for implicit deref + not path0.startsWith(TRefTypeParameter(), _) and + not (path0.isEmpty() and result = TRefType()) and + path = TypePath::cons(TRefTypeParameter(), path0) + else path = path0 + ) + else path = path0 + ) +} + +/** + * Gets the type of `n` at `path`, where `n` is either a reference expression + * `& x` or an expression `x` inside a reference expression `& x`. + */ +pragma[nomagic] +private Type resolveRefExprType(Expr e, TypePath path) { + exists(RefExpr re | + e = re and + path.isEmpty() and + result = TRefType() + or + e = re and + exists(TypePath exprPath | result = resolveType(re.getExpr(), exprPath) | + if exprPath.startsWith(TRefTypeParameter(), _) + then + // `&x` simply means `x` when `x` already has reference type + path = exprPath + else ( + path = TypePath::cons(TRefTypeParameter(), exprPath) and + not (exprPath.isEmpty() and result = TRefType()) + ) + ) + or + e = re.getExpr() and + exists(TypePath exprPath, TypePath refPath, Type exprType | + result = resolveType(re, exprPath) and + exprPath = TypePath::cons(TRefTypeParameter(), refPath) and + exprType = resolveType(e) + | + if exprType = TRefType() + then + // `&x` simply means `x` when `x` already has reference type + path = exprPath + else path = refPath + ) + ) +} + +cached +private module Cached { + private import codeql.rust.internal.CachedStages + + pragma[inline] + private Type getLookupType(AstNode n) { + exists(Type t | + t = resolveType(n) and + if t = TRefType() + then + // for reference types, lookup members in the type being referenced + result = resolveType(n, TypePath::singleton(TRefTypeParameter())) + else result = t + ) + } + + pragma[nomagic] + private Type getMethodCallExprLookupType(MethodCallExpr mce, string name) { + result = getLookupType(mce.getReceiver()) and + name = mce.getNameRef().getText() + } + + /** + * 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)) + } + + pragma[nomagic] + private Type getFieldExprLookupType(FieldExpr fe, string name) { + result = getLookupType(fe.getExpr()) and + name = fe.getNameRef().getText() + } + + /** + * Gets the record field that the field expression `fe` resolves to, if any. + */ + cached + RecordField resolveRecordFieldExpr(FieldExpr fe) { + exists(string name | result = getFieldExprLookupType(fe, name).getRecordField(name)) + } + + pragma[nomagic] + private Type getTupleFieldExprLookupType(FieldExpr fe, int pos) { + exists(string name | + result = getFieldExprLookupType(fe, name) and + pos = name.toInt() + ) + } + + /** + * Gets the tuple field that the field expression `fe` resolves to, if any. + */ + cached + TupleField resolveTupleFieldExpr(FieldExpr fe) { + exists(int i | result = getTupleFieldExprLookupType(fe, i).getTupleField(i)) + } + + /** + * Gets a type at `path` that `n` resolves to, if any. + * + * The type inference implementation works by computing all possible types, so + * the result is not necessarily unique. For example, in + * + * ```rust + * trait MyTrait { + * fn foo(&self) -> &Self; + * + * fn bar(&self) -> &Self { + * self.foo() + * } + * } + * + * struct MyStruct; + * + * impl MyTrait for MyStruct { + * fn foo(&self) -> &MyStruct { + * self + * } + * } + * + * fn baz() { + * let x = MyStruct; + * x.bar(); + * } + * ``` + * + * the type inference engine will roughly make the following deductions: + * + * 1. `MyStruct` has type `MyStruct`. + * 2. `x` has type `MyStruct` (via 1.). + * 3. The return type of `bar` is `&Self`. + * 3. `x.bar()` has type `&MyStruct` (via 2 and 3, by matching the implicit `Self` + * type parameter with `MyStruct`.). + * 4. The return type of `bar` is `&MyTrait`. + * 5. `x.bar()` has type `&MyTrait` (via 2 and 4). + */ + cached + Type resolveType(AstNode n, TypePath path) { + Stages::TypeInference::backref() and + result = resolveAnnotatedType(n, path) + or + result = resolveTypeSymmetry(n, path) + or + result = resolveImplicitSelfType(n, path) + or + result = resolveRecordExprType(n, path) + or + result = resolvePathExprType(n, path) + or + result = resolveCallExprBaseType(n, path) + or + result = resolveFieldExprType(n, path) + or + result = resolveRefExprType(n, path) + } +} + +import Cached + +/** + * Gets a type that `n` resolves to, if any. + */ +Type resolveType(AstNode n) { result = resolveType(n, TypePath::nil()) } diff --git a/rust/ql/lib/codeql/rust/elements/internal/TypeMention.qll b/rust/ql/lib/codeql/rust/elements/internal/TypeMention.qll new file mode 100644 index 000000000000..dca40f27133b --- /dev/null +++ b/rust/ql/lib/codeql/rust/elements/internal/TypeMention.qll @@ -0,0 +1,173 @@ +/** Provides classes for representing type mentions, used in type inference. */ + +private import rust +private import Type +private import PathResolution +private import TypeInference + +/** An AST node that may mention a type. */ +abstract class TypeMention extends AstNode { + /** Gets the `i`th type argument mention, if any. */ + abstract TypeMention getTypeArgument(int i); + + /** Gets the type that this node resolves to, if any. */ + abstract Type resolveType(); + + /** Gets the sub mention at `path`. */ + pragma[nomagic] + private TypeMention getMentionAt(TypePath path) { + path.isEmpty() and + result = this + or + exists(int i, TypeParameter tp, TypeMention arg, TypePath suffix | + arg = this.getTypeArgument(pragma[only_bind_into](i)) and + result = arg.getMentionAt(suffix) and + path = TypePath::cons(tp, suffix) and + tp = this.resolveType().getTypeParameter(pragma[only_bind_into](i)) + ) + } + + /** Gets the type that the sub mention at `path` resolves to, if any. */ + Type resolveTypeAt(TypePath path) { result = this.getMentionAt(path).resolveType() } + + /** + * Like `resolveTypeAt`, but also resolves `Self` mentions to the implicit + * `Self` type parameter. + * + * This is only needed when resolving types for calls to methods; inside the + * methods themselves, `Self` only resolves to the relevant trait or type + * being implemented. + */ + final Type resolveTypeAtInclSelf(TypePath path) { + result = this.resolveTypeAt(path) + or + exists(TypeMention tm, ImplOrTraitItemNode node | + tm = this.getMentionAt(path) and + result = TSelfTypeParameter() + | + tm = node.getASelfPath() + or + tm.(PathTypeRepr).getPath() = node.getASelfPath() + ) + } +} + +class TypeReprMention extends TypeMention, TypeRepr { + TypeReprMention() { not this instanceof InferTypeRepr } + + override TypeReprMention getTypeArgument(int i) { + result = this.(ArrayTypeRepr).getElementTypeRepr() and + i = 0 + or + result = this.(RefTypeRepr).getTypeRepr() and + i = 0 + or + result = this.(PathTypeRepr).getPath().(PathMention).getTypeArgument(i) + } + + override Type resolveType() { + this instanceof ArrayTypeRepr and + result = TArrayType() + or + this instanceof RefTypeRepr and + result = TRefType() + or + result = this.(PathTypeRepr).getPath().(PathMention).resolveType() + } +} + +class PathMention extends TypeMention, Path { + override TypeMention getTypeArgument(int i) { + result = this.getPart().getGenericArgList().getTypeArgument(i) + or + // `Self` paths inside traits and `impl` blocks have implicit type arguments + // that are the type parameters of the trait or impl. For example, in + // + // ```rust + // impl Foo { + // fn m(self) -> Self { + // self + // } + // } + // ``` + // + // the `Self` return type is shorthand for `Foo`. + exists(ImplOrTraitItemNode node | this = node.getASelfPath() | + result = node.(ImplItemNode).getSelfPath().getPart().getGenericArgList().getTypeArgument(i) + or + result = node.(Trait).getGenericParamList().getTypeParam(i) + ) + } + + override Type resolveType() { + exists(ItemNode i | i = resolvePath(this) | + result = TStruct(i) + or + result = TEnum(i) + or + result = TTrait(i) + or + result = TTypeParamTypeParameter(i) + or + result = i.(TypeAlias).getTypeRepr().(TypeReprMention).resolveType() + ) + } +} + +// Used to represent implicit `Self` type arguments in traits and `impl` blocks, +// see `PathMention` for details. +class TypeParamMention extends TypeMention, TypeParam { + override TypeReprMention getTypeArgument(int i) { none() } + + override Type resolveType() { result = TTypeParamTypeParameter(this) } +} + +/** + * Holds if the `i`th type argument of `selfPath`, belonging to `impl`, resolves + * to type parameter `tp`. + * + * Example: + * + * ```rust + * impl Foo for Bar { ... } + * // ^^^^^^ selfPath + * // ^ tp + * ``` + */ +pragma[nomagic] +private predicate isImplSelfTypeParam( + ImplItemNode impl, PathMention selfPath, int i, TypeParameter tp +) { + exists(PathMention path | + selfPath = impl.getSelfPath() and + path = selfPath.getPart().getGenericArgList().getTypeArgument(i).(PathTypeRepr).getPath() and + tp = path.resolveType() + ) +} + +class ImplMention extends TypeMention, ImplItemNode { + override TypeReprMention getTypeArgument(int i) { none() } + + override Type resolveType() { result = TImpl(this) } + + override Type resolveTypeAt(TypePath path) { + result = TImpl(this) and + path.isEmpty() + or + // For example, in + // + // ```rust + // struct S(T1); + // + // impl S { ... } + // ``` + // + // We get that the type path "0" resolves to `T1` for the `impl` block, + // which is considered a base type mention of `S`. + exists(PathMention selfPath, TypeParameter tp, int i | + isImplSelfTypeParam(this, selfPath, pragma[only_bind_into](i), tp) and + result = selfPath.resolveType().getTypeParameter(pragma[only_bind_into](i)) and + path = TypePath::singleton(tp) + ) + } +} diff --git a/rust/ql/lib/codeql/rust/elements/internal/TypeParamImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/TypeParamImpl.qll index f2b36a8c786b..09fb7f4b33de 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/TypeParamImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/TypeParamImpl.qll @@ -11,6 +11,8 @@ private import codeql.rust.elements.internal.generated.TypeParam * be referenced directly. */ module Impl { + private import rust + // the following QLdoc is generated: if you need to edit it, do it in the schema file /** * A TypeParam. For example: @@ -19,6 +21,9 @@ module Impl { * ``` */ class TypeParam extends Generated::TypeParam { + /** Gets the position of this type parameter. */ + int getPosition() { this = any(GenericParamList l).getTypeParam(result) } + override string toAbbreviatedString() { result = this.getName().getText() } override string toString() { result = this.getName().getText() } diff --git a/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll index c19da1a760a0..6e03f779de55 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll @@ -32,5 +32,20 @@ module Impl { /** Gets the `i`th tuple field, if any. */ pragma[nomagic] TupleField getTupleField(int i) { result = this.getFieldList().(TupleFieldList).getField(i) } + + /** Holds if this variant uses tuple fields. */ + pragma[nomagic] + predicate isTuple() { this.getFieldList() instanceof TupleFieldList } + + /** + * Holds if this variant uses record fields. + * + * Empty variants are considered to use record fields. + */ + pragma[nomagic] + predicate isRecord() { not this.isTuple() } + + /** Gets the enum that this variant belongs to. */ + Enum getEnum() { this = result.getVariantList().getAVariant() } } } diff --git a/rust/ql/lib/codeql/rust/internal/CachedStages.qll b/rust/ql/lib/codeql/rust/internal/CachedStages.qll index 0cf3c32921e8..c42d6b95c94d 100644 --- a/rust/ql/lib/codeql/rust/internal/CachedStages.qll +++ b/rust/ql/lib/codeql/rust/internal/CachedStages.qll @@ -95,6 +95,36 @@ module Stages { } } + /** + * The type inference stage. + */ + cached + module TypeInference { + private import codeql.rust.elements.internal.Type + private import codeql.rust.elements.internal.TypeInference + + /** + * Always holds. + * Ensures that a predicate is evaluated as part of the CFG stage. + */ + cached + predicate ref() { 1 = 1 } + + /** + * DO NOT USE! + * + * Contains references to each predicate that use the above `ref` predicate. + */ + cached + predicate backref() { + 1 = 1 + or + exists(Type t) + or + exists(resolveType(_)) + } + } + /** * The data flow stage. */ diff --git a/rust/ql/lib/qlpack.yml b/rust/ql/lib/qlpack.yml index fee6db9154f1..860795788f5a 100644 --- a/rust/ql/lib/qlpack.yml +++ b/rust/ql/lib/qlpack.yml @@ -12,6 +12,7 @@ dependencies: codeql/mad: ${workspace} codeql/ssa: ${workspace} codeql/tutorial: ${workspace} + codeql/typeinference: ${workspace} codeql/util: ${workspace} dataExtensions: - /**/*.model.yml diff --git a/rust/ql/test/extractor-tests/generated/Trait/Trait_getGenericParamList.expected b/rust/ql/test/extractor-tests/generated/Trait/Trait_getGenericParamList.expected index 1ea7592a5973..ba9e996565f4 100644 --- a/rust/ql/test/extractor-tests/generated/Trait/Trait_getGenericParamList.expected +++ b/rust/ql/test/extractor-tests/generated/Trait/Trait_getGenericParamList.expected @@ -1 +1 @@ -| gen_trait.rs:10:1:10:57 | trait Foo | gen_trait.rs:10:14:10:30 | GenericParamList | +| gen_trait.rs:10:1:10:57 | trait Foo | gen_trait.rs:10:14:10:30 | <...> | diff --git a/rust/ql/test/extractor-tests/utf8/ast.expected b/rust/ql/test/extractor-tests/utf8/ast.expected index 14b96482021c..0a8bbefe725d 100644 --- a/rust/ql/test/extractor-tests/utf8/ast.expected +++ b/rust/ql/test/extractor-tests/utf8/ast.expected @@ -4,7 +4,7 @@ | utf8_identifiers.rs:1:1:4:6 | fn foo | | utf8_identifiers.rs:1:1:12:2 | SourceFile | | utf8_identifiers.rs:1:4:1:6 | foo | -| utf8_identifiers.rs:1:7:4:1 | GenericParamList | +| utf8_identifiers.rs:1:7:4:1 | <...> | | utf8_identifiers.rs:2:5:2:6 | ''\u03b2 | | utf8_identifiers.rs:2:5:2:6 | LifetimeParam | | utf8_identifiers.rs:3:5:3:5 | \u03b3 | diff --git a/rust/ql/test/library-tests/type-inference/CONSISTENCY/AstConsistency.expected b/rust/ql/test/library-tests/type-inference/CONSISTENCY/AstConsistency.expected new file mode 100644 index 000000000000..3503ddec2ca0 --- /dev/null +++ b/rust/ql/test/library-tests/type-inference/CONSISTENCY/AstConsistency.expected @@ -0,0 +1,5 @@ +multiplePathResolutions +| main.rs:385:34:385:34 | S | main.rs:384:19:384:19 | S | +| main.rs:385:34:385:34 | S | main.rs:411:5:412:13 | struct S | +| main.rs:387:39:387:39 | S | main.rs:384:19:384:19 | S | +| main.rs:387:39:387:39 | S | main.rs:411:5:412:13 | struct S | diff --git a/rust/ql/test/library-tests/type-inference/main.rs b/rust/ql/test/library-tests/type-inference/main.rs new file mode 100644 index 000000000000..13287a8c2af2 --- /dev/null +++ b/rust/ql/test/library-tests/type-inference/main.rs @@ -0,0 +1,574 @@ +mod m1 { + pub struct Foo {} + + impl Foo { + pub fn m1(self) -> Self { + self + } + + pub fn m2(self) -> Foo { + self + } + } + + pub fn f() -> Foo { + println!("main.rs::m1::f"); + let x = Foo {}; + let y: _ = Foo {}; + x + } + + pub fn g(x: Foo, y: Foo) -> Foo { + println!("main.rs::m1::g"); + x.m1(); + y.m2() + } +} + +mod m2 { + #[derive(Debug)] + struct MyThing { + a: A, + } + + #[derive(Debug)] + struct S1; + #[derive(Debug)] + struct S2; + + impl MyThing { + fn m1(self) -> S1 { + self.a + } + } + + impl MyThing { + fn m1(self) -> Self { + Self { a: self.a } + } + } + + impl MyThing { + fn m2(self) -> T { + self.a + } + } + + pub fn f() { + let x = MyThing { a: S1 }; + let y = MyThing { a: S2 }; + + println!("{:?}", x.m1()); // missing call target + println!("{:?}", y.m1().a); // missing call target + + let x = MyThing { a: S1 }; + let y = MyThing { a: S2 }; + + println!("{:?}", x.m2()); + println!("{:?}", y.m2()); + } +} + +mod m3 { + #[derive(Debug)] + struct MyThing { + a: A, + } + + #[derive(Debug)] + struct S1; + #[derive(Debug)] + struct S2; + + trait MyTrait { + fn m1(self) -> A; + + fn m2(self) -> Self + where + Self: Sized, + { + self + } + } + + fn call_trait_m1>(x: T2) -> T1 { + x.m1() + } + + impl MyTrait for MyThing { + fn m1(self) -> S1 { + self.a + } + } + + impl MyTrait for MyThing { + fn m1(self) -> Self { + Self { a: self.a } + } + } + + pub fn f() { + let x = MyThing { a: S1 }; + let y = MyThing { a: S2 }; + + println!("{:?}", x.m1()); // missing call target + println!("{:?}", y.m1().a); // missing call target + + let x = MyThing { a: S1 }; + let y = MyThing { a: S2 }; + + println!("{:?}", call_trait_m1(x)); // missing + println!("{:?}", call_trait_m1(y).a); // missing + } +} + +mod m4 { + #[derive(Debug)] + struct MyThing { + a: A, + } + + #[derive(Debug)] + struct S1; + #[derive(Debug)] + struct S2; + + trait MyTrait { + fn m1(self) -> A; + + fn m2(self) -> A + where + Self: Sized, + { + self.m1() + } + } + + fn call_trait_m1>(x: T2) -> T1 { + x.m1() + } + + impl MyTrait for MyThing { + fn m1(self) -> T { + self.a + } + } + + pub fn f() { + let x = MyThing { a: S1 }; + let y = MyThing { a: S2 }; + + println!("{:?}", x.m1()); + println!("{:?}", y.m1()); + + let x = MyThing { a: S1 }; + let y = MyThing { a: S2 }; + + println!("{:?}", x.m2()); + println!("{:?}", y.m2()); + + let x = MyThing { a: S1 }; + let y = MyThing { a: S2 }; + + println!("{:?}", call_trait_m1(x)); // missing + println!("{:?}", call_trait_m1(y)); // missing + } +} + +mod m5 { + trait MyTrait { + type AssociatedType; + + fn m1(self) -> Self::AssociatedType; + + fn m2(self) -> Self::AssociatedType + where + Self::AssociatedType: Default, + Self: Sized, + { + Self::AssociatedType::default() + } + } + + #[derive(Debug, Default)] + struct S; + + impl MyTrait for S { + type AssociatedType = S; + + fn m1(self) -> Self::AssociatedType { + S + } + } + + pub fn f() { + let x = S; + println!("{:?}", x.m1()); + + let x = S; + println!("{:?}", x.m2()); // missing + } +} + +mod m6 { + #[derive(Debug)] + enum MyEnum { + C1(A), + C2 { a: A }, + } + + #[derive(Debug)] + struct S1; + #[derive(Debug)] + struct S2; + + impl MyEnum { + fn m1(self) -> T { + match self { + MyEnum::C1(a) => a, // missing + MyEnum::C2 { a } => a, // missing + } + } + } + + pub fn f() { + let x = MyEnum::C1(S1); + let y = MyEnum::C2 { a: S2 }; + + println!("{:?}", x.m1()); + println!("{:?}", y.m1()); + } +} + +mod m7 { + #[derive(Debug)] + struct MyThing { + a: A, + } + + #[derive(Debug)] + struct MyThing2 { + a: A, + } + + #[derive(Debug)] + struct S1; + #[derive(Debug)] + struct S2; + + trait MyTrait1 { + fn m1(self) -> A; + } + + trait MyTrait2: MyTrait1 { + fn m2(self) -> A + where + Self: Sized, + { + if 1 + 1 > 2 { + self.m1() + } else { + Self::m1(self) + } + } + } + + trait MyTrait3: MyTrait2> { + fn m3(self) -> A + where + Self: Sized, + { + if 1 + 1 > 2 { + self.m2().a + } else { + Self::m2(self).a + } + } + } + + impl MyTrait1 for MyThing { + fn m1(self) -> T { + self.a + } + } + + impl MyTrait2 for MyThing {} + + impl MyTrait1> for MyThing2 { + fn m1(self) -> MyThing { + MyThing { a: self.a } + } + } + + impl MyTrait2> for MyThing2 {} + + impl MyTrait3 for MyThing2 {} + + pub fn f() { + let x = MyThing { a: S1 }; + let y = MyThing { a: S2 }; + + println!("{:?}", x.m1()); + println!("{:?}", y.m1()); + + let x = MyThing { a: S1 }; + let y = MyThing { a: S2 }; + + println!("{:?}", x.m2()); + println!("{:?}", y.m2()); + + let x = MyThing2 { a: S1 }; + let y = MyThing2 { a: S2 }; + + println!("{:?}", x.m3()); + println!("{:?}", y.m3()); + } +} + +mod m8 { + use std::convert::From; + use std::fmt::Debug; + + #[derive(Debug)] + struct S1; + + #[derive(Debug)] + struct S2; + + trait Trait: Debug {} + + impl Trait for S1 {} + + fn id(x: &T) -> &T { + x + } + + impl Into for S1 { + fn into(self) -> S2 { + S2 + } + } + + fn into(x: T1) -> T2 + where + T1: Into, + { + x.into() + } + + pub fn f() { + let x = S1; + println!("{:?}", id(&x)); + + let x = S1; + println!("{:?}", id::(&x)); + + let x = S1; + println!("{:?}", id::(&x)); // incorrectly has type `S1` instead of `Trait` + + let x = S1; + into::(x); + + let x = S1; + let y: S2 = into(x); + } +} + +mod m9 { + #[derive(Debug)] + enum MyOption { + MyNone(), + MySome(T), + } + + trait MyTrait { + fn set(&mut self, value: S); + + fn call_set(&mut self, value: S) { + self.set(value); + } + } + + impl MyTrait for MyOption { + fn set(&mut self, value: T) {} + } + + impl MyOption { + fn new() -> Self { + MyOption::MyNone() + } + } + + impl MyOption> { + fn flatten(self) -> MyOption { + match self { + MyOption::MyNone() => MyOption::MyNone(), // missing inner type `Option` + MyOption::MySome(x) => x, // missing + } + } + } + + #[derive(Debug)] + struct S; + + pub fn f() { + let x1 = MyOption::::new(); // `::new` missing type `S` + println!("{:?}", x1); + + let mut x2 = MyOption::new(); + x2.set(S); + println!("{:?}", x2); + + let mut x3 = MyOption::new(); // missing type `S` from `MyOption` (but can resolve `MyTrait`) + x3.call_set(S); + println!("{:?}", x3); + + let mut x4 = MyOption::new(); + MyOption::set(&mut x4, S); + println!("{:?}", x4); + + let x5 = MyOption::MySome(MyOption::::MyNone()); + println!("{:?}", x5.flatten()); // missing call target + + let x6 = MyOption::MySome(MyOption::::MyNone()); + println!("{:?}", MyOption::>::flatten(x6)); + } +} + +mod m10 { + + #[derive(Debug, Copy, Clone)] + struct S(T); + + #[derive(Debug, Copy, Clone)] + struct S2; + + impl S { + fn m1(self) -> T { + self.0 + } + + fn m2(&self) -> &T { + &self.0 + } + + fn m3(self: &S) -> &T { + &self.0 + } + } + + pub fn f() { + let x1 = S(S2); + println!("{:?}", x1.m1()); + + let x2 = S(S2); + // implicit borrow + println!("{:?}", x2.m2()); + println!("{:?}", x2.m3()); + + let x3 = S(S2); + // explicit borrow + println!("{:?}", S::::m2(&x3)); + println!("{:?}", S::::m3(&x3)); + + let x4 = &S(S2); + // explicit borrow + println!("{:?}", x4.m2()); + println!("{:?}", x4.m3()); + + let x5 = &S(S2); + // implicit dereference + println!("{:?}", x5.m1()); + println!("{:?}", x5.0); + + let x6 = &S(S2); + // explicit dereference + println!("{:?}", (*x6).m1()); + } +} + +mod m11 { + trait MyTrait { + fn foo(&self) -> &Self; + + fn bar(&self) -> &Self { + self.foo() + } + } + + struct MyStruct; + + impl MyTrait for MyStruct { + fn foo(&self) -> &MyStruct { + self + } + } + + pub fn f() { + let x = MyStruct; + x.bar(); + } +} + +mod m12 { + struct S; + + struct MyStruct(T); + + impl MyStruct { + fn foo(&self) -> &Self { + self + } + } + + pub fn f() { + let x = MyStruct(S); + x.foo(); + } +} + +mod m13 { + struct S; + + impl S { + fn f1(&self) -> &Self { + &&&self + } + + fn f2(self: &Self) -> &Self { + &&&self + } + + fn f3(x: &Self) -> &Self { + x + } + + fn f4(x: &Self) -> &Self { + &&&x + } + } + + pub fn f() { + let x = S; + x.f1(); + x.f2(); + S::f3(&x); + } +} + +fn main() { + m1::f(); + m1::g(m1::Foo {}, m1::Foo {}); + m2::f(); + m3::f(); + m4::f(); + m5::f(); + m6::f(); + m7::f(); + m8::f(); + m9::f(); + m10::f(); + m11::f(); + m12::f(); + m13::f(); +} diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected new file mode 100644 index 000000000000..b991d4bd96b7 --- /dev/null +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -0,0 +1,797 @@ +resolveType +| main.rs:5:19:5:22 | SelfParam | | main.rs:2:5:2:21 | struct Foo | +| main.rs:5:33:7:9 | { ... } | | main.rs:2:5:2:21 | struct Foo | +| main.rs:6:13:6:16 | self | | main.rs:2:5:2:21 | struct Foo | +| main.rs:9:19:9:22 | SelfParam | | main.rs:2:5:2:21 | struct Foo | +| main.rs:9:32:11:9 | { ... } | | main.rs:2:5:2:21 | struct Foo | +| main.rs:10:13:10:16 | self | | main.rs:2:5:2:21 | struct Foo | +| main.rs:14:23:19:5 | { ... } | | main.rs:2:5:2:21 | struct Foo | +| main.rs:16:13:16:13 | x | | main.rs:2:5:2:21 | struct Foo | +| main.rs:16:17:16:22 | Foo {...} | | main.rs:2:5:2:21 | struct Foo | +| main.rs:17:13:17:13 | y | | main.rs:2:5:2:21 | struct Foo | +| main.rs:17:20:17:25 | Foo {...} | | main.rs:2:5:2:21 | struct Foo | +| main.rs:18:9:18:9 | x | | main.rs:2:5:2:21 | struct Foo | +| main.rs:21:14:21:14 | x | | main.rs:2:5:2:21 | struct Foo | +| main.rs:21:22:21:22 | y | | main.rs:2:5:2:21 | struct Foo | +| main.rs:21:37:25:5 | { ... } | | main.rs:2:5:2:21 | struct Foo | +| main.rs:23:9:23:9 | x | | main.rs:2:5:2:21 | struct Foo | +| main.rs:23:9:23:14 | x.m1(...) | | main.rs:2:5:2:21 | struct Foo | +| main.rs:24:9:24:9 | y | | main.rs:2:5:2:21 | struct Foo | +| main.rs:24:9:24:14 | y.m2(...) | | main.rs:2:5:2:21 | struct Foo | +| main.rs:40:15:40:18 | SelfParam | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:40:15:40:18 | SelfParam | A | main.rs:34:5:35:14 | struct S1 | +| main.rs:40:27:42:9 | { ... } | | main.rs:34:5:35:14 | struct S1 | +| main.rs:41:13:41:16 | self | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:41:13:41:16 | self | A | main.rs:34:5:35:14 | struct S1 | +| main.rs:41:13:41:18 | self.a | | main.rs:34:5:35:14 | struct S1 | +| main.rs:46:15:46:18 | SelfParam | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:46:15:46:18 | SelfParam | A | main.rs:36:5:37:14 | struct S2 | +| main.rs:46:29:48:9 | { ... } | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:46:29:48:9 | { ... } | A | main.rs:36:5:37:14 | struct S2 | +| main.rs:47:13:47:30 | Self {...} | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:47:13:47:30 | Self {...} | A | main.rs:36:5:37:14 | struct S2 | +| main.rs:47:23:47:26 | self | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:47:23:47:26 | self | A | main.rs:36:5:37:14 | struct S2 | +| main.rs:47:23:47:28 | self.a | | main.rs:36:5:37:14 | struct S2 | +| main.rs:52:15:52:18 | SelfParam | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:52:15:52:18 | SelfParam | A | main.rs:51:10:51:10 | T | +| main.rs:52:26:54:9 | { ... } | | main.rs:51:10:51:10 | T | +| main.rs:53:13:53:16 | self | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:53:13:53:16 | self | A | main.rs:51:10:51:10 | T | +| main.rs:53:13:53:18 | self.a | | main.rs:51:10:51:10 | T | +| main.rs:58:13:58:13 | x | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:58:13:58:13 | x | A | main.rs:34:5:35:14 | struct S1 | +| main.rs:58:17:58:33 | MyThing {...} | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:58:17:58:33 | MyThing {...} | A | main.rs:34:5:35:14 | struct S1 | +| main.rs:58:30:58:31 | S1 | | main.rs:34:5:35:14 | struct S1 | +| main.rs:59:13:59:13 | y | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:59:13:59:13 | y | A | main.rs:36:5:37:14 | struct S2 | +| main.rs:59:17:59:33 | MyThing {...} | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:59:17:59:33 | MyThing {...} | A | main.rs:36:5:37:14 | struct S2 | +| main.rs:59:30:59:31 | S2 | | main.rs:36:5:37:14 | struct S2 | +| main.rs:61:26:61:26 | x | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:61:26:61:26 | x | A | main.rs:34:5:35:14 | struct S1 | +| main.rs:62:26:62:26 | y | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:62:26:62:26 | y | A | main.rs:36:5:37:14 | struct S2 | +| main.rs:64:13:64:13 | x | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:64:13:64:13 | x | A | main.rs:34:5:35:14 | struct S1 | +| main.rs:64:17:64:33 | MyThing {...} | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:64:17:64:33 | MyThing {...} | A | main.rs:34:5:35:14 | struct S1 | +| main.rs:64:30:64:31 | S1 | | main.rs:34:5:35:14 | struct S1 | +| main.rs:65:13:65:13 | y | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:65:13:65:13 | y | A | main.rs:36:5:37:14 | struct S2 | +| main.rs:65:17:65:33 | MyThing {...} | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:65:17:65:33 | MyThing {...} | A | main.rs:36:5:37:14 | struct S2 | +| main.rs:65:30:65:31 | S2 | | main.rs:36:5:37:14 | struct S2 | +| main.rs:67:26:67:26 | x | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:67:26:67:26 | x | A | main.rs:34:5:35:14 | struct S1 | +| main.rs:67:26:67:31 | x.m2(...) | | main.rs:34:5:35:14 | struct S1 | +| main.rs:68:26:68:26 | y | | main.rs:29:5:32:5 | struct MyThing | +| main.rs:68:26:68:26 | y | A | main.rs:36:5:37:14 | struct S2 | +| main.rs:68:26:68:31 | y.m2(...) | | main.rs:36:5:37:14 | struct S2 | +| main.rs:84:15:84:18 | SelfParam | | main.rs:83:5:92:5 | trait MyTrait | +| main.rs:84:15:84:18 | SelfParam | A | main.rs:83:19:83:19 | A | +| main.rs:86:15:86:18 | SelfParam | | main.rs:83:5:92:5 | trait MyTrait | +| main.rs:86:15:86:18 | SelfParam | A | main.rs:83:19:83:19 | A | +| main.rs:89:9:91:9 | { ... } | | main.rs:83:5:92:5 | trait MyTrait | +| main.rs:89:9:91:9 | { ... } | A | main.rs:83:19:83:19 | A | +| main.rs:90:13:90:16 | self | | main.rs:83:5:92:5 | trait MyTrait | +| main.rs:90:13:90:16 | self | A | main.rs:83:19:83:19 | A | +| main.rs:94:43:94:43 | x | | main.rs:83:5:92:5 | trait MyTrait | +| main.rs:94:43:94:43 | x | | main.rs:94:26:94:40 | T2 | +| main.rs:94:43:94:43 | x | A | main.rs:94:22:94:23 | T1 | +| main.rs:94:56:96:5 | { ... } | | main.rs:94:22:94:23 | T1 | +| main.rs:95:9:95:9 | x | | main.rs:83:5:92:5 | trait MyTrait | +| main.rs:95:9:95:9 | x | | main.rs:94:26:94:40 | T2 | +| main.rs:95:9:95:9 | x | A | main.rs:94:22:94:23 | T1 | +| main.rs:95:9:95:14 | x.m1(...) | | main.rs:94:22:94:23 | T1 | +| main.rs:99:15:99:18 | SelfParam | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:99:15:99:18 | SelfParam | A | main.rs:78:5:79:14 | struct S1 | +| main.rs:99:27:101:9 | { ... } | | main.rs:78:5:79:14 | struct S1 | +| main.rs:100:13:100:16 | self | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:100:13:100:16 | self | A | main.rs:78:5:79:14 | struct S1 | +| main.rs:100:13:100:18 | self.a | | main.rs:78:5:79:14 | struct S1 | +| main.rs:105:15:105:18 | SelfParam | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:105:15:105:18 | SelfParam | A | main.rs:80:5:81:14 | struct S2 | +| main.rs:105:29:107:9 | { ... } | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:105:29:107:9 | { ... } | A | main.rs:80:5:81:14 | struct S2 | +| main.rs:106:13:106:30 | Self {...} | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:106:13:106:30 | Self {...} | A | main.rs:80:5:81:14 | struct S2 | +| main.rs:106:23:106:26 | self | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:106:23:106:26 | self | A | main.rs:80:5:81:14 | struct S2 | +| main.rs:106:23:106:28 | self.a | | main.rs:80:5:81:14 | struct S2 | +| main.rs:111:13:111:13 | x | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:111:13:111:13 | x | A | main.rs:78:5:79:14 | struct S1 | +| main.rs:111:17:111:33 | MyThing {...} | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:111:17:111:33 | MyThing {...} | A | main.rs:78:5:79:14 | struct S1 | +| main.rs:111:30:111:31 | S1 | | main.rs:78:5:79:14 | struct S1 | +| main.rs:112:13:112:13 | y | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:112:13:112:13 | y | A | main.rs:80:5:81:14 | struct S2 | +| main.rs:112:17:112:33 | MyThing {...} | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:112:17:112:33 | MyThing {...} | A | main.rs:80:5:81:14 | struct S2 | +| main.rs:112:30:112:31 | S2 | | main.rs:80:5:81:14 | struct S2 | +| main.rs:114:26:114:26 | x | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:114:26:114:26 | x | A | main.rs:78:5:79:14 | struct S1 | +| main.rs:115:26:115:26 | y | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:115:26:115:26 | y | A | main.rs:80:5:81:14 | struct S2 | +| main.rs:117:13:117:13 | x | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:117:13:117:13 | x | A | main.rs:78:5:79:14 | struct S1 | +| main.rs:117:17:117:33 | MyThing {...} | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:117:17:117:33 | MyThing {...} | A | main.rs:78:5:79:14 | struct S1 | +| main.rs:117:30:117:31 | S1 | | main.rs:78:5:79:14 | struct S1 | +| main.rs:118:13:118:13 | y | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:118:13:118:13 | y | A | main.rs:80:5:81:14 | struct S2 | +| main.rs:118:17:118:33 | MyThing {...} | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:118:17:118:33 | MyThing {...} | A | main.rs:80:5:81:14 | struct S2 | +| main.rs:118:30:118:31 | S2 | | main.rs:80:5:81:14 | struct S2 | +| main.rs:120:40:120:40 | x | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:120:40:120:40 | x | A | main.rs:78:5:79:14 | struct S1 | +| main.rs:121:40:121:40 | y | | main.rs:73:5:76:5 | struct MyThing | +| main.rs:121:40:121:40 | y | A | main.rs:80:5:81:14 | struct S2 | +| main.rs:137:15:137:18 | SelfParam | | main.rs:136:5:145:5 | trait MyTrait | +| main.rs:137:15:137:18 | SelfParam | A | main.rs:136:19:136:19 | A | +| main.rs:139:15:139:18 | SelfParam | | main.rs:136:5:145:5 | trait MyTrait | +| main.rs:139:15:139:18 | SelfParam | A | main.rs:136:19:136:19 | A | +| main.rs:142:9:144:9 | { ... } | | main.rs:136:19:136:19 | A | +| main.rs:143:13:143:16 | self | | main.rs:136:5:145:5 | trait MyTrait | +| main.rs:143:13:143:16 | self | A | main.rs:136:19:136:19 | A | +| main.rs:143:13:143:21 | self.m1(...) | | main.rs:136:19:136:19 | A | +| main.rs:147:43:147:43 | x | | main.rs:136:5:145:5 | trait MyTrait | +| main.rs:147:43:147:43 | x | | main.rs:147:26:147:40 | T2 | +| main.rs:147:43:147:43 | x | A | main.rs:147:22:147:23 | T1 | +| main.rs:147:56:149:5 | { ... } | | main.rs:147:22:147:23 | T1 | +| main.rs:148:9:148:9 | x | | main.rs:136:5:145:5 | trait MyTrait | +| main.rs:148:9:148:9 | x | | main.rs:147:26:147:40 | T2 | +| main.rs:148:9:148:9 | x | A | main.rs:147:22:147:23 | T1 | +| main.rs:148:9:148:14 | x.m1(...) | | main.rs:147:22:147:23 | T1 | +| main.rs:152:15:152:18 | SelfParam | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:152:15:152:18 | SelfParam | A | main.rs:151:10:151:10 | T | +| main.rs:152:26:154:9 | { ... } | | main.rs:151:10:151:10 | T | +| main.rs:153:13:153:16 | self | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:153:13:153:16 | self | A | main.rs:151:10:151:10 | T | +| main.rs:153:13:153:18 | self.a | | main.rs:151:10:151:10 | T | +| main.rs:158:13:158:13 | x | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:158:13:158:13 | x | A | main.rs:131:5:132:14 | struct S1 | +| main.rs:158:17:158:33 | MyThing {...} | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:158:17:158:33 | MyThing {...} | A | main.rs:131:5:132:14 | struct S1 | +| main.rs:158:30:158:31 | S1 | | main.rs:131:5:132:14 | struct S1 | +| main.rs:159:13:159:13 | y | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:159:13:159:13 | y | A | main.rs:133:5:134:14 | struct S2 | +| main.rs:159:17:159:33 | MyThing {...} | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:159:17:159:33 | MyThing {...} | A | main.rs:133:5:134:14 | struct S2 | +| main.rs:159:30:159:31 | S2 | | main.rs:133:5:134:14 | struct S2 | +| main.rs:161:26:161:26 | x | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:161:26:161:26 | x | A | main.rs:131:5:132:14 | struct S1 | +| main.rs:161:26:161:31 | x.m1(...) | | main.rs:131:5:132:14 | struct S1 | +| main.rs:162:26:162:26 | y | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:162:26:162:26 | y | A | main.rs:133:5:134:14 | struct S2 | +| main.rs:162:26:162:31 | y.m1(...) | | main.rs:133:5:134:14 | struct S2 | +| main.rs:164:13:164:13 | x | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:164:13:164:13 | x | | main.rs:136:5:145:5 | trait MyTrait | +| main.rs:164:13:164:13 | x | A | main.rs:131:5:132:14 | struct S1 | +| main.rs:164:13:164:13 | x | A | main.rs:131:5:132:14 | struct S1 | +| main.rs:164:17:164:33 | MyThing {...} | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:164:17:164:33 | MyThing {...} | | main.rs:136:5:145:5 | trait MyTrait | +| main.rs:164:17:164:33 | MyThing {...} | A | main.rs:131:5:132:14 | struct S1 | +| main.rs:164:17:164:33 | MyThing {...} | A | main.rs:131:5:132:14 | struct S1 | +| main.rs:164:30:164:31 | S1 | | main.rs:131:5:132:14 | struct S1 | +| main.rs:165:13:165:13 | y | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:165:13:165:13 | y | | main.rs:136:5:145:5 | trait MyTrait | +| main.rs:165:13:165:13 | y | A | main.rs:133:5:134:14 | struct S2 | +| main.rs:165:13:165:13 | y | A | main.rs:133:5:134:14 | struct S2 | +| main.rs:165:17:165:33 | MyThing {...} | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:165:17:165:33 | MyThing {...} | | main.rs:136:5:145:5 | trait MyTrait | +| main.rs:165:17:165:33 | MyThing {...} | A | main.rs:133:5:134:14 | struct S2 | +| main.rs:165:17:165:33 | MyThing {...} | A | main.rs:133:5:134:14 | struct S2 | +| main.rs:165:30:165:31 | S2 | | main.rs:133:5:134:14 | struct S2 | +| main.rs:167:26:167:26 | x | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:167:26:167:26 | x | | main.rs:136:5:145:5 | trait MyTrait | +| main.rs:167:26:167:26 | x | A | main.rs:131:5:132:14 | struct S1 | +| main.rs:167:26:167:26 | x | A | main.rs:131:5:132:14 | struct S1 | +| main.rs:167:26:167:31 | x.m2(...) | | main.rs:131:5:132:14 | struct S1 | +| main.rs:168:26:168:26 | y | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:168:26:168:26 | y | | main.rs:136:5:145:5 | trait MyTrait | +| main.rs:168:26:168:26 | y | A | main.rs:133:5:134:14 | struct S2 | +| main.rs:168:26:168:26 | y | A | main.rs:133:5:134:14 | struct S2 | +| main.rs:168:26:168:31 | y.m2(...) | | main.rs:133:5:134:14 | struct S2 | +| main.rs:170:13:170:13 | x | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:170:13:170:13 | x | A | main.rs:131:5:132:14 | struct S1 | +| main.rs:170:17:170:33 | MyThing {...} | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:170:17:170:33 | MyThing {...} | A | main.rs:131:5:132:14 | struct S1 | +| main.rs:170:30:170:31 | S1 | | main.rs:131:5:132:14 | struct S1 | +| main.rs:171:13:171:13 | y | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:171:13:171:13 | y | A | main.rs:133:5:134:14 | struct S2 | +| main.rs:171:17:171:33 | MyThing {...} | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:171:17:171:33 | MyThing {...} | A | main.rs:133:5:134:14 | struct S2 | +| main.rs:171:30:171:31 | S2 | | main.rs:133:5:134:14 | struct S2 | +| main.rs:173:40:173:40 | x | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:173:40:173:40 | x | A | main.rs:131:5:132:14 | struct S1 | +| main.rs:174:40:174:40 | y | | main.rs:126:5:129:5 | struct MyThing | +| main.rs:174:40:174:40 | y | A | main.rs:133:5:134:14 | struct S2 | +| main.rs:182:15:182:18 | SelfParam | | main.rs:179:5:191:5 | trait MyTrait | +| main.rs:184:15:184:18 | SelfParam | | main.rs:179:5:191:5 | trait MyTrait | +| main.rs:199:15:199:18 | SelfParam | | main.rs:193:5:194:13 | struct S | +| main.rs:199:45:201:9 | { ... } | | main.rs:193:5:194:13 | struct S | +| main.rs:200:13:200:13 | S | | main.rs:193:5:194:13 | struct S | +| main.rs:205:13:205:13 | x | | main.rs:193:5:194:13 | struct S | +| main.rs:205:17:205:17 | S | | main.rs:193:5:194:13 | struct S | +| main.rs:206:26:206:26 | x | | main.rs:193:5:194:13 | struct S | +| main.rs:206:26:206:31 | x.m1(...) | | main.rs:193:5:194:13 | struct S | +| main.rs:208:13:208:13 | x | | main.rs:179:5:191:5 | trait MyTrait | +| main.rs:208:13:208:13 | x | | main.rs:193:5:194:13 | struct S | +| main.rs:208:17:208:17 | S | | main.rs:179:5:191:5 | trait MyTrait | +| main.rs:208:17:208:17 | S | | main.rs:193:5:194:13 | struct S | +| main.rs:209:26:209:26 | x | | main.rs:179:5:191:5 | trait MyTrait | +| main.rs:209:26:209:26 | x | | main.rs:193:5:194:13 | struct S | +| main.rs:226:15:226:18 | SelfParam | | main.rs:214:5:218:5 | enum MyEnum | +| main.rs:226:15:226:18 | SelfParam | A | main.rs:225:10:225:10 | T | +| main.rs:226:26:231:9 | { ... } | | main.rs:225:10:225:10 | T | +| main.rs:227:13:230:13 | match self { ... } | | main.rs:225:10:225:10 | T | +| main.rs:227:19:227:22 | self | | main.rs:214:5:218:5 | enum MyEnum | +| main.rs:227:19:227:22 | self | A | main.rs:225:10:225:10 | T | +| main.rs:235:13:235:13 | x | | main.rs:214:5:218:5 | enum MyEnum | +| main.rs:235:13:235:13 | x | A | main.rs:220:5:221:14 | struct S1 | +| main.rs:235:17:235:30 | ...::C1(...) | | main.rs:214:5:218:5 | enum MyEnum | +| main.rs:235:17:235:30 | ...::C1(...) | A | main.rs:220:5:221:14 | struct S1 | +| main.rs:235:28:235:29 | S1 | | main.rs:220:5:221:14 | struct S1 | +| main.rs:236:13:236:13 | y | | main.rs:214:5:218:5 | enum MyEnum | +| main.rs:236:13:236:13 | y | A | main.rs:222:5:223:14 | struct S2 | +| main.rs:236:17:236:36 | ...::C2 {...} | | main.rs:214:5:218:5 | enum MyEnum | +| main.rs:236:17:236:36 | ...::C2 {...} | A | main.rs:222:5:223:14 | struct S2 | +| main.rs:236:33:236:34 | S2 | | main.rs:222:5:223:14 | struct S2 | +| main.rs:238:26:238:26 | x | | main.rs:214:5:218:5 | enum MyEnum | +| main.rs:238:26:238:26 | x | A | main.rs:220:5:221:14 | struct S1 | +| main.rs:238:26:238:31 | x.m1(...) | | main.rs:220:5:221:14 | struct S1 | +| main.rs:239:26:239:26 | y | | main.rs:214:5:218:5 | enum MyEnum | +| main.rs:239:26:239:26 | y | A | main.rs:222:5:223:14 | struct S2 | +| main.rs:239:26:239:31 | y.m1(...) | | main.rs:222:5:223:14 | struct S2 | +| main.rs:260:15:260:18 | SelfParam | | main.rs:259:5:261:5 | trait MyTrait1 | +| main.rs:260:15:260:18 | SelfParam | A | main.rs:259:20:259:20 | A | +| main.rs:264:15:264:18 | SelfParam | | main.rs:259:5:261:5 | trait MyTrait1 | +| main.rs:264:15:264:18 | SelfParam | | main.rs:263:5:274:5 | trait MyTrait2 | +| main.rs:264:15:264:18 | SelfParam | A | main.rs:263:20:263:20 | A | +| main.rs:264:15:264:18 | SelfParam | A | main.rs:263:20:263:20 | A | +| main.rs:267:9:273:9 | { ... } | | main.rs:263:20:263:20 | A | +| main.rs:268:13:272:13 | if ... {...} else {...} | | main.rs:263:20:263:20 | A | +| main.rs:268:26:270:13 | { ... } | | main.rs:263:20:263:20 | A | +| main.rs:269:17:269:20 | self | | main.rs:259:5:261:5 | trait MyTrait1 | +| main.rs:269:17:269:20 | self | | main.rs:263:5:274:5 | trait MyTrait2 | +| main.rs:269:17:269:20 | self | A | main.rs:263:20:263:20 | A | +| main.rs:269:17:269:20 | self | A | main.rs:263:20:263:20 | A | +| main.rs:269:17:269:25 | self.m1(...) | | main.rs:263:20:263:20 | A | +| main.rs:270:20:272:13 | { ... } | | main.rs:263:20:263:20 | A | +| main.rs:271:17:271:30 | ...::m1(...) | | main.rs:263:20:263:20 | A | +| main.rs:271:26:271:29 | self | | main.rs:259:5:261:5 | trait MyTrait1 | +| main.rs:271:26:271:29 | self | | main.rs:263:5:274:5 | trait MyTrait2 | +| main.rs:271:26:271:29 | self | A | main.rs:263:20:263:20 | A | +| main.rs:271:26:271:29 | self | A | main.rs:263:20:263:20 | A | +| main.rs:277:15:277:18 | SelfParam | | main.rs:263:5:274:5 | trait MyTrait2 | +| main.rs:277:15:277:18 | SelfParam | | main.rs:276:5:287:5 | trait MyTrait3 | +| main.rs:277:15:277:18 | SelfParam | A | main.rs:244:5:247:5 | struct MyThing | +| main.rs:277:15:277:18 | SelfParam | A | main.rs:276:20:276:20 | A | +| main.rs:277:15:277:18 | SelfParam | A.A | main.rs:276:20:276:20 | A | +| main.rs:280:9:286:9 | { ... } | | main.rs:276:20:276:20 | A | +| main.rs:281:13:285:13 | if ... {...} else {...} | | main.rs:276:20:276:20 | A | +| main.rs:281:26:283:13 | { ... } | | main.rs:276:20:276:20 | A | +| main.rs:282:17:282:20 | self | | main.rs:263:5:274:5 | trait MyTrait2 | +| main.rs:282:17:282:20 | self | | main.rs:276:5:287:5 | trait MyTrait3 | +| main.rs:282:17:282:20 | self | A | main.rs:244:5:247:5 | struct MyThing | +| main.rs:282:17:282:20 | self | A | main.rs:276:20:276:20 | A | +| main.rs:282:17:282:20 | self | A.A | main.rs:276:20:276:20 | A | +| main.rs:282:17:282:25 | self.m2(...) | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:282:17:282:25 | self.m2(...) | A | main.rs:276:20:276:20 | A | +| main.rs:282:17:282:27 | ... .a | | main.rs:276:20:276:20 | A | +| main.rs:283:20:285:13 | { ... } | | main.rs:276:20:276:20 | A | +| main.rs:284:17:284:30 | ...::m2(...) | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:284:17:284:30 | ...::m2(...) | A | main.rs:276:20:276:20 | A | +| main.rs:284:17:284:32 | ... .a | | main.rs:276:20:276:20 | A | +| main.rs:284:26:284:29 | self | | main.rs:263:5:274:5 | trait MyTrait2 | +| main.rs:284:26:284:29 | self | | main.rs:276:5:287:5 | trait MyTrait3 | +| main.rs:284:26:284:29 | self | A | main.rs:244:5:247:5 | struct MyThing | +| main.rs:284:26:284:29 | self | A | main.rs:276:20:276:20 | A | +| main.rs:284:26:284:29 | self | A.A | main.rs:276:20:276:20 | A | +| main.rs:290:15:290:18 | SelfParam | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:290:15:290:18 | SelfParam | A | main.rs:289:10:289:10 | T | +| main.rs:290:26:292:9 | { ... } | | main.rs:289:10:289:10 | T | +| main.rs:291:13:291:16 | self | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:291:13:291:16 | self | A | main.rs:289:10:289:10 | T | +| main.rs:291:13:291:18 | self.a | | main.rs:289:10:289:10 | T | +| main.rs:298:15:298:18 | SelfParam | | main.rs:249:5:252:5 | struct MyThing2 | +| main.rs:298:15:298:18 | SelfParam | A | main.rs:297:10:297:10 | T | +| main.rs:298:35:300:9 | { ... } | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:298:35:300:9 | { ... } | A | main.rs:297:10:297:10 | T | +| main.rs:299:13:299:33 | MyThing {...} | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:299:13:299:33 | MyThing {...} | A | main.rs:297:10:297:10 | T | +| main.rs:299:26:299:29 | self | | main.rs:249:5:252:5 | struct MyThing2 | +| main.rs:299:26:299:29 | self | A | main.rs:297:10:297:10 | T | +| main.rs:299:26:299:31 | self.a | | main.rs:297:10:297:10 | T | +| main.rs:308:13:308:13 | x | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:308:13:308:13 | x | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:308:17:308:33 | MyThing {...} | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:308:17:308:33 | MyThing {...} | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:308:30:308:31 | S1 | | main.rs:254:5:255:14 | struct S1 | +| main.rs:309:13:309:13 | y | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:309:13:309:13 | y | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:309:17:309:33 | MyThing {...} | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:309:17:309:33 | MyThing {...} | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:309:30:309:31 | S2 | | main.rs:256:5:257:14 | struct S2 | +| main.rs:311:26:311:26 | x | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:311:26:311:26 | x | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:311:26:311:31 | x.m1(...) | | main.rs:254:5:255:14 | struct S1 | +| main.rs:312:26:312:26 | y | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:312:26:312:26 | y | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:312:26:312:31 | y.m1(...) | | main.rs:256:5:257:14 | struct S2 | +| main.rs:314:13:314:13 | x | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:314:13:314:13 | x | | main.rs:263:5:274:5 | trait MyTrait2 | +| main.rs:314:13:314:13 | x | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:314:13:314:13 | x | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:314:17:314:33 | MyThing {...} | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:314:17:314:33 | MyThing {...} | | main.rs:263:5:274:5 | trait MyTrait2 | +| main.rs:314:17:314:33 | MyThing {...} | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:314:17:314:33 | MyThing {...} | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:314:30:314:31 | S1 | | main.rs:254:5:255:14 | struct S1 | +| main.rs:315:13:315:13 | y | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:315:13:315:13 | y | | main.rs:263:5:274:5 | trait MyTrait2 | +| main.rs:315:13:315:13 | y | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:315:13:315:13 | y | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:315:17:315:33 | MyThing {...} | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:315:17:315:33 | MyThing {...} | | main.rs:263:5:274:5 | trait MyTrait2 | +| main.rs:315:17:315:33 | MyThing {...} | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:315:17:315:33 | MyThing {...} | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:315:30:315:31 | S2 | | main.rs:256:5:257:14 | struct S2 | +| main.rs:317:26:317:26 | x | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:317:26:317:26 | x | | main.rs:263:5:274:5 | trait MyTrait2 | +| main.rs:317:26:317:26 | x | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:317:26:317:26 | x | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:317:26:317:31 | x.m2(...) | | main.rs:254:5:255:14 | struct S1 | +| main.rs:318:26:318:26 | y | | main.rs:244:5:247:5 | struct MyThing | +| main.rs:318:26:318:26 | y | | main.rs:263:5:274:5 | trait MyTrait2 | +| main.rs:318:26:318:26 | y | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:318:26:318:26 | y | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:318:26:318:31 | y.m2(...) | | main.rs:256:5:257:14 | struct S2 | +| main.rs:320:13:320:13 | x | | main.rs:249:5:252:5 | struct MyThing2 | +| main.rs:320:13:320:13 | x | | main.rs:276:5:287:5 | trait MyTrait3 | +| main.rs:320:13:320:13 | x | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:320:13:320:13 | x | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:320:17:320:34 | MyThing2 {...} | | main.rs:249:5:252:5 | struct MyThing2 | +| main.rs:320:17:320:34 | MyThing2 {...} | | main.rs:276:5:287:5 | trait MyTrait3 | +| main.rs:320:17:320:34 | MyThing2 {...} | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:320:17:320:34 | MyThing2 {...} | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:320:31:320:32 | S1 | | main.rs:254:5:255:14 | struct S1 | +| main.rs:321:13:321:13 | y | | main.rs:249:5:252:5 | struct MyThing2 | +| main.rs:321:13:321:13 | y | | main.rs:276:5:287:5 | trait MyTrait3 | +| main.rs:321:13:321:13 | y | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:321:13:321:13 | y | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:321:17:321:34 | MyThing2 {...} | | main.rs:249:5:252:5 | struct MyThing2 | +| main.rs:321:17:321:34 | MyThing2 {...} | | main.rs:276:5:287:5 | trait MyTrait3 | +| main.rs:321:17:321:34 | MyThing2 {...} | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:321:17:321:34 | MyThing2 {...} | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:321:31:321:32 | S2 | | main.rs:256:5:257:14 | struct S2 | +| main.rs:323:26:323:26 | x | | main.rs:249:5:252:5 | struct MyThing2 | +| main.rs:323:26:323:26 | x | | main.rs:276:5:287:5 | trait MyTrait3 | +| main.rs:323:26:323:26 | x | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:323:26:323:26 | x | A | main.rs:254:5:255:14 | struct S1 | +| main.rs:323:26:323:31 | x.m3(...) | | main.rs:254:5:255:14 | struct S1 | +| main.rs:324:26:324:26 | y | | main.rs:249:5:252:5 | struct MyThing2 | +| main.rs:324:26:324:26 | y | | main.rs:276:5:287:5 | trait MyTrait3 | +| main.rs:324:26:324:26 | y | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:324:26:324:26 | y | A | main.rs:256:5:257:14 | struct S2 | +| main.rs:324:26:324:31 | y.m3(...) | | main.rs:256:5:257:14 | struct S2 | +| main.rs:342:22:342:22 | x | | file://:0:0:0:0 | & | +| main.rs:342:22:342:22 | x | &T | main.rs:342:11:342:19 | T | +| main.rs:342:35:344:5 | { ... } | | file://:0:0:0:0 | & | +| main.rs:342:35:344:5 | { ... } | &T | main.rs:342:11:342:19 | T | +| main.rs:343:9:343:9 | x | | file://:0:0:0:0 | & | +| main.rs:343:9:343:9 | x | &T | main.rs:342:11:342:19 | T | +| main.rs:347:17:347:20 | SelfParam | | main.rs:332:5:333:14 | struct S1 | +| main.rs:347:29:349:9 | { ... } | | main.rs:335:5:336:14 | struct S2 | +| main.rs:348:13:348:14 | S2 | | main.rs:335:5:336:14 | struct S2 | +| main.rs:352:21:352:21 | x | | main.rs:352:13:352:14 | T1 | +| main.rs:355:5:357:5 | { ... } | | main.rs:352:17:352:18 | T2 | +| main.rs:356:9:356:9 | x | | main.rs:352:13:352:14 | T1 | +| main.rs:356:9:356:16 | x.into(...) | | main.rs:352:17:352:18 | T2 | +| main.rs:360:13:360:13 | x | | main.rs:332:5:333:14 | struct S1 | +| main.rs:360:17:360:18 | S1 | | main.rs:332:5:333:14 | struct S1 | +| main.rs:361:26:361:31 | id(...) | | file://:0:0:0:0 | & | +| main.rs:361:26:361:31 | id(...) | &T | main.rs:332:5:333:14 | struct S1 | +| main.rs:361:29:361:30 | &x | | file://:0:0:0:0 | & | +| main.rs:361:29:361:30 | &x | &T | main.rs:332:5:333:14 | struct S1 | +| main.rs:361:30:361:30 | x | | main.rs:332:5:333:14 | struct S1 | +| main.rs:363:13:363:13 | x | | main.rs:332:5:333:14 | struct S1 | +| main.rs:363:17:363:18 | S1 | | main.rs:332:5:333:14 | struct S1 | +| main.rs:364:26:364:37 | id::<...>(...) | | file://:0:0:0:0 | & | +| main.rs:364:26:364:37 | id::<...>(...) | &T | main.rs:332:5:333:14 | struct S1 | +| main.rs:364:35:364:36 | &x | | file://:0:0:0:0 | & | +| main.rs:364:35:364:36 | &x | &T | main.rs:332:5:333:14 | struct S1 | +| main.rs:364:36:364:36 | x | | main.rs:332:5:333:14 | struct S1 | +| main.rs:366:13:366:13 | x | | main.rs:332:5:333:14 | struct S1 | +| main.rs:366:17:366:18 | S1 | | main.rs:332:5:333:14 | struct S1 | +| main.rs:367:26:367:44 | id::<...>(...) | | file://:0:0:0:0 | & | +| main.rs:367:26:367:44 | id::<...>(...) | &T | main.rs:332:5:333:14 | struct S1 | +| main.rs:367:42:367:43 | &x | | file://:0:0:0:0 | & | +| main.rs:367:42:367:43 | &x | &T | main.rs:332:5:333:14 | struct S1 | +| main.rs:367:43:367:43 | x | | main.rs:332:5:333:14 | struct S1 | +| main.rs:369:13:369:13 | x | | main.rs:332:5:333:14 | struct S1 | +| main.rs:369:17:369:18 | S1 | | main.rs:332:5:333:14 | struct S1 | +| main.rs:370:9:370:25 | into::<...>(...) | | main.rs:335:5:336:14 | struct S2 | +| main.rs:370:24:370:24 | x | | main.rs:332:5:333:14 | struct S1 | +| main.rs:372:13:372:13 | x | | main.rs:332:5:333:14 | struct S1 | +| main.rs:372:17:372:18 | S1 | | main.rs:332:5:333:14 | struct S1 | +| main.rs:373:13:373:13 | y | | main.rs:335:5:336:14 | struct S2 | +| main.rs:373:21:373:27 | into(...) | | main.rs:335:5:336:14 | struct S2 | +| main.rs:373:26:373:26 | x | | main.rs:332:5:333:14 | struct S1 | +| main.rs:385:16:385:24 | SelfParam | | file://:0:0:0:0 | & | +| main.rs:385:16:385:24 | SelfParam | &T | main.rs:384:5:390:5 | trait MyTrait | +| main.rs:385:16:385:24 | SelfParam | &T.S | main.rs:384:19:384:19 | S | +| main.rs:385:27:385:31 | value | | main.rs:384:19:384:19 | S | +| main.rs:385:27:385:31 | value | | main.rs:411:5:412:13 | struct S | +| main.rs:387:21:387:29 | SelfParam | | file://:0:0:0:0 | & | +| main.rs:387:21:387:29 | SelfParam | &T | main.rs:384:5:390:5 | trait MyTrait | +| main.rs:387:21:387:29 | SelfParam | &T.S | main.rs:384:19:384:19 | S | +| main.rs:387:21:387:29 | SelfParam | &T.S | main.rs:411:5:412:13 | struct S | +| main.rs:387:32:387:36 | value | | main.rs:384:19:384:19 | S | +| main.rs:387:32:387:36 | value | | main.rs:411:5:412:13 | struct S | +| main.rs:388:13:388:16 | self | | file://:0:0:0:0 | & | +| main.rs:388:13:388:16 | self | &T | main.rs:384:5:390:5 | trait MyTrait | +| main.rs:388:13:388:16 | self | &T.S | main.rs:384:19:384:19 | S | +| main.rs:388:13:388:16 | self | &T.S | main.rs:411:5:412:13 | struct S | +| main.rs:388:22:388:26 | value | | main.rs:384:19:384:19 | S | +| main.rs:388:22:388:26 | value | | main.rs:411:5:412:13 | struct S | +| main.rs:393:16:393:24 | SelfParam | | file://:0:0:0:0 | & | +| main.rs:393:16:393:24 | SelfParam | &T | main.rs:378:5:382:5 | enum MyOption | +| main.rs:393:16:393:24 | SelfParam | &T.T | main.rs:392:10:392:10 | T | +| main.rs:393:27:393:31 | value | | main.rs:392:10:392:10 | T | +| main.rs:397:26:399:9 | { ... } | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:397:26:399:9 | { ... } | T | main.rs:396:10:396:10 | T | +| main.rs:398:13:398:30 | ...::MyNone(...) | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:398:13:398:30 | ...::MyNone(...) | T | main.rs:396:10:396:10 | T | +| main.rs:403:20:403:23 | SelfParam | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:403:20:403:23 | SelfParam | T | main.rs:378:5:382:5 | enum MyOption | +| main.rs:403:20:403:23 | SelfParam | T.T | main.rs:402:10:402:10 | T | +| main.rs:403:41:408:9 | { ... } | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:403:41:408:9 | { ... } | T | main.rs:402:10:402:10 | T | +| main.rs:404:13:407:13 | match self { ... } | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:404:13:407:13 | match self { ... } | T | main.rs:402:10:402:10 | T | +| main.rs:404:19:404:22 | self | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:404:19:404:22 | self | T | main.rs:378:5:382:5 | enum MyOption | +| main.rs:404:19:404:22 | self | T.T | main.rs:402:10:402:10 | T | +| main.rs:405:39:405:56 | ...::MyNone(...) | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:415:13:415:14 | x1 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:415:18:415:37 | ...::new(...) | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:416:26:416:27 | x1 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:418:13:418:18 | mut x2 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:418:13:418:18 | mut x2 | T | main.rs:411:5:412:13 | struct S | +| main.rs:418:22:418:36 | ...::new(...) | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:418:22:418:36 | ...::new(...) | T | main.rs:411:5:412:13 | struct S | +| main.rs:419:9:419:10 | x2 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:419:9:419:10 | x2 | T | main.rs:411:5:412:13 | struct S | +| main.rs:419:16:419:16 | S | | main.rs:411:5:412:13 | struct S | +| main.rs:420:26:420:27 | x2 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:420:26:420:27 | x2 | T | main.rs:411:5:412:13 | struct S | +| main.rs:422:13:422:18 | mut x3 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:422:13:422:18 | mut x3 | | main.rs:384:5:390:5 | trait MyTrait | +| main.rs:422:13:422:18 | mut x3 | S | main.rs:411:5:412:13 | struct S | +| main.rs:422:22:422:36 | ...::new(...) | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:422:22:422:36 | ...::new(...) | | main.rs:384:5:390:5 | trait MyTrait | +| main.rs:422:22:422:36 | ...::new(...) | S | main.rs:411:5:412:13 | struct S | +| main.rs:423:9:423:10 | x3 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:423:9:423:10 | x3 | | main.rs:384:5:390:5 | trait MyTrait | +| main.rs:423:9:423:10 | x3 | S | main.rs:411:5:412:13 | struct S | +| main.rs:423:21:423:21 | S | | main.rs:411:5:412:13 | struct S | +| main.rs:424:26:424:27 | x3 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:424:26:424:27 | x3 | | main.rs:384:5:390:5 | trait MyTrait | +| main.rs:424:26:424:27 | x3 | S | main.rs:411:5:412:13 | struct S | +| main.rs:426:13:426:18 | mut x4 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:426:13:426:18 | mut x4 | T | main.rs:411:5:412:13 | struct S | +| main.rs:426:22:426:36 | ...::new(...) | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:426:22:426:36 | ...::new(...) | T | main.rs:411:5:412:13 | struct S | +| main.rs:427:23:427:29 | &mut x4 | | file://:0:0:0:0 | & | +| main.rs:427:23:427:29 | &mut x4 | &T | main.rs:378:5:382:5 | enum MyOption | +| main.rs:427:23:427:29 | &mut x4 | &T.T | main.rs:411:5:412:13 | struct S | +| main.rs:427:28:427:29 | x4 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:427:28:427:29 | x4 | T | main.rs:411:5:412:13 | struct S | +| main.rs:427:32:427:32 | S | | main.rs:411:5:412:13 | struct S | +| main.rs:428:26:428:27 | x4 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:428:26:428:27 | x4 | T | main.rs:411:5:412:13 | struct S | +| main.rs:430:13:430:14 | x5 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:430:13:430:14 | x5 | T | main.rs:378:5:382:5 | enum MyOption | +| main.rs:430:13:430:14 | x5 | T.T | main.rs:411:5:412:13 | struct S | +| main.rs:430:18:430:58 | ...::MySome(...) | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:430:18:430:58 | ...::MySome(...) | T | main.rs:378:5:382:5 | enum MyOption | +| main.rs:430:18:430:58 | ...::MySome(...) | T.T | main.rs:411:5:412:13 | struct S | +| main.rs:430:35:430:57 | ...::MyNone(...) | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:430:35:430:57 | ...::MyNone(...) | T | main.rs:411:5:412:13 | struct S | +| main.rs:431:26:431:27 | x5 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:431:26:431:27 | x5 | T | main.rs:378:5:382:5 | enum MyOption | +| main.rs:431:26:431:27 | x5 | T.T | main.rs:411:5:412:13 | struct S | +| main.rs:433:13:433:14 | x6 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:433:13:433:14 | x6 | T | main.rs:378:5:382:5 | enum MyOption | +| main.rs:433:13:433:14 | x6 | T.T | main.rs:411:5:412:13 | struct S | +| main.rs:433:18:433:58 | ...::MySome(...) | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:433:18:433:58 | ...::MySome(...) | T | main.rs:378:5:382:5 | enum MyOption | +| main.rs:433:18:433:58 | ...::MySome(...) | T.T | main.rs:411:5:412:13 | struct S | +| main.rs:433:35:433:57 | ...::MyNone(...) | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:433:35:433:57 | ...::MyNone(...) | T | main.rs:411:5:412:13 | struct S | +| main.rs:434:26:434:61 | ...::flatten(...) | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:434:26:434:61 | ...::flatten(...) | T | main.rs:411:5:412:13 | struct S | +| main.rs:434:59:434:60 | x6 | | main.rs:378:5:382:5 | enum MyOption | +| main.rs:434:59:434:60 | x6 | T | main.rs:378:5:382:5 | enum MyOption | +| main.rs:434:59:434:60 | x6 | T.T | main.rs:411:5:412:13 | struct S | +| main.rs:447:15:447:18 | SelfParam | | main.rs:440:5:441:19 | struct S | +| main.rs:447:15:447:18 | SelfParam | T | main.rs:446:10:446:10 | T | +| main.rs:447:26:449:9 | { ... } | | main.rs:446:10:446:10 | T | +| main.rs:448:13:448:16 | self | | main.rs:440:5:441:19 | struct S | +| main.rs:448:13:448:16 | self | T | main.rs:446:10:446:10 | T | +| main.rs:448:13:448:18 | self.0 | | main.rs:446:10:446:10 | T | +| main.rs:451:15:451:19 | SelfParam | | file://:0:0:0:0 | & | +| main.rs:451:15:451:19 | SelfParam | &T | main.rs:440:5:441:19 | struct S | +| main.rs:451:15:451:19 | SelfParam | &T.T | main.rs:446:10:446:10 | T | +| main.rs:451:28:453:9 | { ... } | | file://:0:0:0:0 | & | +| main.rs:451:28:453:9 | { ... } | &T | main.rs:446:10:446:10 | T | +| main.rs:452:13:452:19 | &... | | file://:0:0:0:0 | & | +| main.rs:452:13:452:19 | &... | &T | main.rs:446:10:446:10 | T | +| main.rs:452:14:452:17 | self | | file://:0:0:0:0 | & | +| main.rs:452:14:452:17 | self | &T | main.rs:440:5:441:19 | struct S | +| main.rs:452:14:452:17 | self | &T.T | main.rs:446:10:446:10 | T | +| main.rs:452:14:452:19 | self.0 | | main.rs:446:10:446:10 | T | +| main.rs:455:15:455:25 | SelfParam | | file://:0:0:0:0 | & | +| main.rs:455:15:455:25 | SelfParam | &T | main.rs:440:5:441:19 | struct S | +| main.rs:455:15:455:25 | SelfParam | &T.T | main.rs:446:10:446:10 | T | +| main.rs:455:34:457:9 | { ... } | | file://:0:0:0:0 | & | +| main.rs:455:34:457:9 | { ... } | &T | main.rs:446:10:446:10 | T | +| main.rs:456:13:456:19 | &... | | file://:0:0:0:0 | & | +| main.rs:456:13:456:19 | &... | &T | main.rs:446:10:446:10 | T | +| main.rs:456:14:456:17 | self | | file://:0:0:0:0 | & | +| main.rs:456:14:456:17 | self | &T | main.rs:440:5:441:19 | struct S | +| main.rs:456:14:456:17 | self | &T.T | main.rs:446:10:446:10 | T | +| main.rs:456:14:456:19 | self.0 | | main.rs:446:10:446:10 | T | +| main.rs:461:13:461:14 | x1 | | main.rs:440:5:441:19 | struct S | +| main.rs:461:13:461:14 | x1 | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:461:18:461:22 | S(...) | | main.rs:440:5:441:19 | struct S | +| main.rs:461:18:461:22 | S(...) | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:461:20:461:21 | S2 | | main.rs:443:5:444:14 | struct S2 | +| main.rs:462:26:462:27 | x1 | | main.rs:440:5:441:19 | struct S | +| main.rs:462:26:462:27 | x1 | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:462:26:462:32 | x1.m1(...) | | main.rs:443:5:444:14 | struct S2 | +| main.rs:464:13:464:14 | x2 | | main.rs:440:5:441:19 | struct S | +| main.rs:464:13:464:14 | x2 | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:464:18:464:22 | S(...) | | main.rs:440:5:441:19 | struct S | +| main.rs:464:18:464:22 | S(...) | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:464:20:464:21 | S2 | | main.rs:443:5:444:14 | struct S2 | +| main.rs:466:26:466:27 | x2 | | main.rs:440:5:441:19 | struct S | +| main.rs:466:26:466:27 | x2 | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:466:26:466:32 | x2.m2(...) | | file://:0:0:0:0 | & | +| main.rs:466:26:466:32 | x2.m2(...) | &T | main.rs:443:5:444:14 | struct S2 | +| main.rs:467:26:467:27 | x2 | | main.rs:440:5:441:19 | struct S | +| main.rs:467:26:467:27 | x2 | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:467:26:467:32 | x2.m3(...) | | file://:0:0:0:0 | & | +| main.rs:467:26:467:32 | x2.m3(...) | &T | main.rs:443:5:444:14 | struct S2 | +| main.rs:469:13:469:14 | x3 | | main.rs:440:5:441:19 | struct S | +| main.rs:469:13:469:14 | x3 | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:469:18:469:22 | S(...) | | main.rs:440:5:441:19 | struct S | +| main.rs:469:18:469:22 | S(...) | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:469:20:469:21 | S2 | | main.rs:443:5:444:14 | struct S2 | +| main.rs:471:26:471:41 | ...::m2(...) | | file://:0:0:0:0 | & | +| main.rs:471:26:471:41 | ...::m2(...) | &T | main.rs:443:5:444:14 | struct S2 | +| main.rs:471:38:471:40 | &x3 | | file://:0:0:0:0 | & | +| main.rs:471:38:471:40 | &x3 | &T | main.rs:440:5:441:19 | struct S | +| main.rs:471:38:471:40 | &x3 | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:471:39:471:40 | x3 | | main.rs:440:5:441:19 | struct S | +| main.rs:471:39:471:40 | x3 | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:472:26:472:41 | ...::m3(...) | | file://:0:0:0:0 | & | +| main.rs:472:26:472:41 | ...::m3(...) | &T | main.rs:443:5:444:14 | struct S2 | +| main.rs:472:38:472:40 | &x3 | | file://:0:0:0:0 | & | +| main.rs:472:38:472:40 | &x3 | &T | main.rs:440:5:441:19 | struct S | +| main.rs:472:38:472:40 | &x3 | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:472:39:472:40 | x3 | | main.rs:440:5:441:19 | struct S | +| main.rs:472:39:472:40 | x3 | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:474:13:474:14 | x4 | | file://:0:0:0:0 | & | +| main.rs:474:13:474:14 | x4 | &T | main.rs:440:5:441:19 | struct S | +| main.rs:474:13:474:14 | x4 | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:474:18:474:23 | &... | | file://:0:0:0:0 | & | +| main.rs:474:18:474:23 | &... | &T | main.rs:440:5:441:19 | struct S | +| main.rs:474:18:474:23 | &... | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:474:19:474:23 | S(...) | | main.rs:440:5:441:19 | struct S | +| main.rs:474:19:474:23 | S(...) | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:474:21:474:22 | S2 | | main.rs:443:5:444:14 | struct S2 | +| main.rs:476:26:476:27 | x4 | | file://:0:0:0:0 | & | +| main.rs:476:26:476:27 | x4 | &T | main.rs:440:5:441:19 | struct S | +| main.rs:476:26:476:27 | x4 | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:476:26:476:32 | x4.m2(...) | | file://:0:0:0:0 | & | +| main.rs:476:26:476:32 | x4.m2(...) | &T | main.rs:443:5:444:14 | struct S2 | +| main.rs:477:26:477:27 | x4 | | file://:0:0:0:0 | & | +| main.rs:477:26:477:27 | x4 | &T | main.rs:440:5:441:19 | struct S | +| main.rs:477:26:477:27 | x4 | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:477:26:477:32 | x4.m3(...) | | file://:0:0:0:0 | & | +| main.rs:477:26:477:32 | x4.m3(...) | &T | main.rs:443:5:444:14 | struct S2 | +| main.rs:479:13:479:14 | x5 | | file://:0:0:0:0 | & | +| main.rs:479:13:479:14 | x5 | &T | main.rs:440:5:441:19 | struct S | +| main.rs:479:13:479:14 | x5 | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:479:18:479:23 | &... | | file://:0:0:0:0 | & | +| main.rs:479:18:479:23 | &... | &T | main.rs:440:5:441:19 | struct S | +| main.rs:479:18:479:23 | &... | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:479:19:479:23 | S(...) | | main.rs:440:5:441:19 | struct S | +| main.rs:479:19:479:23 | S(...) | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:479:21:479:22 | S2 | | main.rs:443:5:444:14 | struct S2 | +| main.rs:481:26:481:27 | x5 | | file://:0:0:0:0 | & | +| main.rs:481:26:481:27 | x5 | &T | main.rs:440:5:441:19 | struct S | +| main.rs:481:26:481:27 | x5 | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:481:26:481:32 | x5.m1(...) | | main.rs:443:5:444:14 | struct S2 | +| main.rs:482:26:482:27 | x5 | | file://:0:0:0:0 | & | +| main.rs:482:26:482:27 | x5 | &T | main.rs:440:5:441:19 | struct S | +| main.rs:482:26:482:27 | x5 | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:482:26:482:29 | x5.0 | | main.rs:443:5:444:14 | struct S2 | +| main.rs:484:13:484:14 | x6 | | file://:0:0:0:0 | & | +| main.rs:484:13:484:14 | x6 | &T | main.rs:440:5:441:19 | struct S | +| main.rs:484:13:484:14 | x6 | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:484:18:484:23 | &... | | file://:0:0:0:0 | & | +| main.rs:484:18:484:23 | &... | &T | main.rs:440:5:441:19 | struct S | +| main.rs:484:18:484:23 | &... | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:484:19:484:23 | S(...) | | main.rs:440:5:441:19 | struct S | +| main.rs:484:19:484:23 | S(...) | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:484:21:484:22 | S2 | | main.rs:443:5:444:14 | struct S2 | +| main.rs:486:26:486:30 | (...) | | main.rs:440:5:441:19 | struct S | +| main.rs:486:26:486:30 | (...) | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:486:26:486:35 | ... .m1(...) | | main.rs:443:5:444:14 | struct S2 | +| main.rs:486:27:486:29 | * ... | | main.rs:440:5:441:19 | struct S | +| main.rs:486:27:486:29 | * ... | T | main.rs:443:5:444:14 | struct S2 | +| main.rs:486:28:486:29 | x6 | | file://:0:0:0:0 | & | +| main.rs:486:28:486:29 | x6 | &T | main.rs:440:5:441:19 | struct S | +| main.rs:486:28:486:29 | x6 | &T.T | main.rs:443:5:444:14 | struct S2 | +| main.rs:492:16:492:20 | SelfParam | | file://:0:0:0:0 | & | +| main.rs:492:16:492:20 | SelfParam | &T | main.rs:491:5:497:5 | trait MyTrait | +| main.rs:494:16:494:20 | SelfParam | | file://:0:0:0:0 | & | +| main.rs:494:16:494:20 | SelfParam | &T | main.rs:491:5:497:5 | trait MyTrait | +| main.rs:494:32:496:9 | { ... } | | file://:0:0:0:0 | & | +| main.rs:494:32:496:9 | { ... } | &T | main.rs:491:5:497:5 | trait MyTrait | +| main.rs:495:13:495:16 | self | | file://:0:0:0:0 | & | +| main.rs:495:13:495:16 | self | &T | main.rs:491:5:497:5 | trait MyTrait | +| main.rs:495:13:495:22 | self.foo(...) | | file://:0:0:0:0 | & | +| main.rs:495:13:495:22 | self.foo(...) | &T | main.rs:491:5:497:5 | trait MyTrait | +| main.rs:502:16:502:20 | SelfParam | | file://:0:0:0:0 | & | +| main.rs:502:16:502:20 | SelfParam | &T | main.rs:499:5:499:20 | struct MyStruct | +| main.rs:502:36:504:9 | { ... } | | file://:0:0:0:0 | & | +| main.rs:502:36:504:9 | { ... } | &T | main.rs:499:5:499:20 | struct MyStruct | +| main.rs:503:13:503:16 | self | | file://:0:0:0:0 | & | +| main.rs:503:13:503:16 | self | &T | main.rs:499:5:499:20 | struct MyStruct | +| main.rs:508:13:508:13 | x | | main.rs:491:5:497:5 | trait MyTrait | +| main.rs:508:13:508:13 | x | | main.rs:499:5:499:20 | struct MyStruct | +| main.rs:508:17:508:24 | MyStruct | | main.rs:491:5:497:5 | trait MyTrait | +| main.rs:508:17:508:24 | MyStruct | | main.rs:499:5:499:20 | struct MyStruct | +| main.rs:509:9:509:9 | x | | main.rs:491:5:497:5 | trait MyTrait | +| main.rs:509:9:509:9 | x | | main.rs:499:5:499:20 | struct MyStruct | +| main.rs:509:9:509:15 | x.bar(...) | | file://:0:0:0:0 | & | +| main.rs:509:9:509:15 | x.bar(...) | &T | main.rs:491:5:497:5 | trait MyTrait | +| main.rs:509:9:509:15 | x.bar(...) | &T | main.rs:499:5:499:20 | struct MyStruct | +| main.rs:519:16:519:20 | SelfParam | | file://:0:0:0:0 | & | +| main.rs:519:16:519:20 | SelfParam | &T | main.rs:516:5:516:26 | struct MyStruct | +| main.rs:519:16:519:20 | SelfParam | &T.T | main.rs:518:10:518:10 | T | +| main.rs:519:32:521:9 | { ... } | | file://:0:0:0:0 | & | +| main.rs:519:32:521:9 | { ... } | &T | main.rs:516:5:516:26 | struct MyStruct | +| main.rs:519:32:521:9 | { ... } | &T.T | main.rs:518:10:518:10 | T | +| main.rs:520:13:520:16 | self | | file://:0:0:0:0 | & | +| main.rs:520:13:520:16 | self | &T | main.rs:516:5:516:26 | struct MyStruct | +| main.rs:520:13:520:16 | self | &T.T | main.rs:518:10:518:10 | T | +| main.rs:525:13:525:13 | x | | main.rs:516:5:516:26 | struct MyStruct | +| main.rs:525:13:525:13 | x | T | main.rs:514:5:514:13 | struct S | +| main.rs:525:17:525:27 | MyStruct(...) | | main.rs:516:5:516:26 | struct MyStruct | +| main.rs:525:17:525:27 | MyStruct(...) | T | main.rs:514:5:514:13 | struct S | +| main.rs:525:26:525:26 | S | | main.rs:514:5:514:13 | struct S | +| main.rs:526:9:526:9 | x | | main.rs:516:5:516:26 | struct MyStruct | +| main.rs:526:9:526:9 | x | T | main.rs:514:5:514:13 | struct S | +| main.rs:526:9:526:15 | x.foo(...) | | file://:0:0:0:0 | & | +| main.rs:526:9:526:15 | x.foo(...) | &T | main.rs:516:5:516:26 | struct MyStruct | +| main.rs:526:9:526:15 | x.foo(...) | &T.T | main.rs:514:5:514:13 | struct S | +| main.rs:534:15:534:19 | SelfParam | | file://:0:0:0:0 | & | +| main.rs:534:15:534:19 | SelfParam | &T | main.rs:531:5:531:13 | struct S | +| main.rs:534:31:536:9 | { ... } | | file://:0:0:0:0 | & | +| main.rs:534:31:536:9 | { ... } | &T | main.rs:531:5:531:13 | struct S | +| main.rs:535:13:535:19 | &... | | file://:0:0:0:0 | & | +| main.rs:535:13:535:19 | &... | &T | main.rs:531:5:531:13 | struct S | +| main.rs:535:14:535:19 | &... | | file://:0:0:0:0 | & | +| main.rs:535:14:535:19 | &... | &T | main.rs:531:5:531:13 | struct S | +| main.rs:535:15:535:19 | &self | | file://:0:0:0:0 | & | +| main.rs:535:15:535:19 | &self | &T | main.rs:531:5:531:13 | struct S | +| main.rs:535:16:535:19 | self | | file://:0:0:0:0 | & | +| main.rs:535:16:535:19 | self | &T | main.rs:531:5:531:13 | struct S | +| main.rs:538:15:538:25 | SelfParam | | file://:0:0:0:0 | & | +| main.rs:538:15:538:25 | SelfParam | &T | main.rs:531:5:531:13 | struct S | +| main.rs:538:37:540:9 | { ... } | | file://:0:0:0:0 | & | +| main.rs:538:37:540:9 | { ... } | &T | main.rs:531:5:531:13 | struct S | +| main.rs:539:13:539:19 | &... | | file://:0:0:0:0 | & | +| main.rs:539:13:539:19 | &... | &T | main.rs:531:5:531:13 | struct S | +| main.rs:539:14:539:19 | &... | | file://:0:0:0:0 | & | +| main.rs:539:14:539:19 | &... | &T | main.rs:531:5:531:13 | struct S | +| main.rs:539:15:539:19 | &self | | file://:0:0:0:0 | & | +| main.rs:539:15:539:19 | &self | &T | main.rs:531:5:531:13 | struct S | +| main.rs:539:16:539:19 | self | | file://:0:0:0:0 | & | +| main.rs:539:16:539:19 | self | &T | main.rs:531:5:531:13 | struct S | +| main.rs:542:15:542:15 | x | | file://:0:0:0:0 | & | +| main.rs:542:15:542:15 | x | &T | main.rs:531:5:531:13 | struct S | +| main.rs:542:34:544:9 | { ... } | | file://:0:0:0:0 | & | +| main.rs:542:34:544:9 | { ... } | &T | main.rs:531:5:531:13 | struct S | +| main.rs:543:13:543:13 | x | | file://:0:0:0:0 | & | +| main.rs:543:13:543:13 | x | &T | main.rs:531:5:531:13 | struct S | +| main.rs:546:15:546:15 | x | | file://:0:0:0:0 | & | +| main.rs:546:15:546:15 | x | &T | main.rs:531:5:531:13 | struct S | +| main.rs:546:34:548:9 | { ... } | | file://:0:0:0:0 | & | +| main.rs:546:34:548:9 | { ... } | &T | main.rs:531:5:531:13 | struct S | +| main.rs:547:13:547:16 | &... | | file://:0:0:0:0 | & | +| main.rs:547:13:547:16 | &... | &T | main.rs:531:5:531:13 | struct S | +| main.rs:547:14:547:16 | &... | | file://:0:0:0:0 | & | +| main.rs:547:14:547:16 | &... | &T | main.rs:531:5:531:13 | struct S | +| main.rs:547:15:547:16 | &x | | file://:0:0:0:0 | & | +| main.rs:547:15:547:16 | &x | &T | main.rs:531:5:531:13 | struct S | +| main.rs:547:16:547:16 | x | | file://:0:0:0:0 | & | +| main.rs:547:16:547:16 | x | &T | main.rs:531:5:531:13 | struct S | +| main.rs:552:13:552:13 | x | | main.rs:531:5:531:13 | struct S | +| main.rs:552:17:552:17 | S | | main.rs:531:5:531:13 | struct S | +| main.rs:553:9:553:9 | x | | main.rs:531:5:531:13 | struct S | +| main.rs:553:9:553:14 | x.f1(...) | | file://:0:0:0:0 | & | +| main.rs:553:9:553:14 | x.f1(...) | &T | main.rs:531:5:531:13 | struct S | +| main.rs:554:9:554:9 | x | | main.rs:531:5:531:13 | struct S | +| main.rs:554:9:554:14 | x.f2(...) | | file://:0:0:0:0 | & | +| main.rs:554:9:554:14 | x.f2(...) | &T | main.rs:531:5:531:13 | struct S | +| main.rs:555:9:555:17 | ...::f3(...) | | file://:0:0:0:0 | & | +| main.rs:555:9:555:17 | ...::f3(...) | &T | main.rs:531:5:531:13 | struct S | +| main.rs:555:15:555:16 | &x | | file://:0:0:0:0 | & | +| main.rs:555:15:555:16 | &x | &T | main.rs:531:5:531:13 | struct S | +| main.rs:555:16:555:16 | x | | main.rs:531:5:531:13 | struct S | +| main.rs:560:5:560:11 | ...::f(...) | | main.rs:2:5:2:21 | struct Foo | +| main.rs:561:5:561:33 | ...::g(...) | | main.rs:2:5:2:21 | struct Foo | +| main.rs:561:11:561:20 | ...::Foo {...} | | main.rs:2:5:2:21 | struct Foo | +| main.rs:561:23:561:32 | ...::Foo {...} | | main.rs:2:5:2:21 | struct Foo | +resolveMethodCallExpr +| main.rs:23:9:23:14 | x.m1(...) | main.rs:5:9:7:9 | fn m1 | +| main.rs:24:9:24:14 | y.m2(...) | main.rs:9:9:11:9 | fn m2 | +| main.rs:67:26:67:31 | x.m2(...) | main.rs:52:9:54:9 | fn m2 | +| main.rs:68:26:68:31 | y.m2(...) | main.rs:52:9:54:9 | fn m2 | +| main.rs:95:9:95:14 | x.m1(...) | main.rs:84:9:84:25 | fn m1 | +| main.rs:143:13:143:21 | self.m1(...) | main.rs:137:9:137:25 | fn m1 | +| main.rs:148:9:148:14 | x.m1(...) | main.rs:137:9:137:25 | fn m1 | +| main.rs:161:26:161:31 | x.m1(...) | main.rs:152:9:154:9 | fn m1 | +| main.rs:162:26:162:31 | y.m1(...) | main.rs:152:9:154:9 | fn m1 | +| main.rs:167:26:167:31 | x.m2(...) | main.rs:139:9:144:9 | fn m2 | +| main.rs:168:26:168:31 | y.m2(...) | main.rs:139:9:144:9 | fn m2 | +| main.rs:206:26:206:31 | x.m1(...) | main.rs:199:9:201:9 | fn m1 | +| main.rs:209:26:209:31 | x.m2(...) | main.rs:184:9:190:9 | fn m2 | +| main.rs:238:26:238:31 | x.m1(...) | main.rs:226:9:231:9 | fn m1 | +| main.rs:239:26:239:31 | y.m1(...) | main.rs:226:9:231:9 | fn m1 | +| main.rs:269:17:269:25 | self.m1(...) | main.rs:260:9:260:25 | fn m1 | +| main.rs:282:17:282:25 | self.m2(...) | main.rs:264:9:273:9 | fn m2 | +| main.rs:311:26:311:31 | x.m1(...) | main.rs:290:9:292:9 | fn m1 | +| main.rs:312:26:312:31 | y.m1(...) | main.rs:290:9:292:9 | fn m1 | +| main.rs:317:26:317:31 | x.m2(...) | main.rs:264:9:273:9 | fn m2 | +| main.rs:318:26:318:31 | y.m2(...) | main.rs:264:9:273:9 | fn m2 | +| main.rs:323:26:323:31 | x.m3(...) | main.rs:277:9:286:9 | fn m3 | +| main.rs:324:26:324:31 | y.m3(...) | main.rs:277:9:286:9 | fn m3 | +| main.rs:388:13:388:27 | self.set(...) | main.rs:385:9:385:36 | fn set | +| main.rs:419:9:419:17 | x2.set(...) | main.rs:393:9:393:38 | fn set | +| main.rs:423:9:423:22 | x3.call_set(...) | main.rs:387:9:389:9 | fn call_set | +| main.rs:462:26:462:32 | x1.m1(...) | main.rs:447:9:449:9 | fn m1 | +| main.rs:466:26:466:32 | x2.m2(...) | main.rs:451:9:453:9 | fn m2 | +| main.rs:467:26:467:32 | x2.m3(...) | main.rs:455:9:457:9 | fn m3 | +| main.rs:476:26:476:32 | x4.m2(...) | main.rs:451:9:453:9 | fn m2 | +| main.rs:477:26:477:32 | x4.m3(...) | main.rs:455:9:457:9 | fn m3 | +| main.rs:481:26:481:32 | x5.m1(...) | main.rs:447:9:449:9 | fn m1 | +| main.rs:486:26:486:35 | ... .m1(...) | main.rs:447:9:449:9 | fn m1 | +| main.rs:495:13:495:22 | self.foo(...) | main.rs:492:9:492:31 | fn foo | +| main.rs:509:9:509:15 | x.bar(...) | main.rs:494:9:496:9 | fn bar | +| main.rs:526:9:526:15 | x.foo(...) | main.rs:519:9:521:9 | fn foo | +| main.rs:553:9:553:14 | x.f1(...) | main.rs:534:9:536:9 | fn f1 | +| main.rs:554:9:554:14 | x.f2(...) | main.rs:538:9:540:9 | fn f2 | +resolveFieldExpr +| main.rs:41:13:41:18 | self.a | main.rs:31:9:31:12 | RecordField | +| main.rs:47:23:47:28 | self.a | main.rs:31:9:31:12 | RecordField | +| main.rs:53:13:53:18 | self.a | main.rs:31:9:31:12 | RecordField | +| main.rs:100:13:100:18 | self.a | main.rs:75:9:75:12 | RecordField | +| main.rs:106:23:106:28 | self.a | main.rs:75:9:75:12 | RecordField | +| main.rs:153:13:153:18 | self.a | main.rs:128:9:128:12 | RecordField | +| main.rs:282:17:282:27 | ... .a | main.rs:246:9:246:12 | RecordField | +| main.rs:284:17:284:32 | ... .a | main.rs:246:9:246:12 | RecordField | +| main.rs:291:13:291:18 | self.a | main.rs:246:9:246:12 | RecordField | +| main.rs:299:26:299:31 | self.a | main.rs:251:9:251:12 | RecordField | +| main.rs:448:13:448:18 | self.0 | main.rs:441:17:441:17 | TupleField | +| main.rs:452:14:452:19 | self.0 | main.rs:441:17:441:17 | TupleField | +| main.rs:456:14:456:19 | self.0 | main.rs:441:17:441:17 | TupleField | +| main.rs:482:26:482:29 | x5.0 | main.rs:441:17:441:17 | TupleField | diff --git a/rust/ql/test/library-tests/type-inference/type-inference.ql b/rust/ql/test/library-tests/type-inference/type-inference.ql new file mode 100644 index 000000000000..0e31dc01b49e --- /dev/null +++ b/rust/ql/test/library-tests/type-inference/type-inference.ql @@ -0,0 +1,18 @@ +import rust +import codeql.rust.elements.internal.TypeInference as TypeInference +import TypeInference +import utils.test.InlineExpectationsTest + +query predicate resolveType(AstNode n, TypePath path, Type t) { + t = TypeInference::resolveType(n, path) +} + +query predicate resolveMethodCallExpr(MethodCallExpr mce, Function f) { + f = resolveMethodCallExpr(mce) +} + +query predicate resolveFieldExpr(FieldExpr fe, AstNode target) { + target = resolveRecordFieldExpr(fe) + or + target = resolveTupleFieldExpr(fe) +} From e8505ad33d6ac9d0f62554dcd38d94d64f986afd Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 6 Mar 2025 15:10:35 +0100 Subject: [PATCH 03/11] Rust: Use type inference to resolve method calls and field accesses --- .../rust/dataflow/internal/DataFlowImpl.qll | 4 +- .../elements/internal/CallExprBaseImpl.qll | 17 +--- .../rust/elements/internal/CallExprImpl.qll | 2 + .../elements/internal/MethodCallExprImpl.qll | 22 +++++ .../dataflow/global/inline-flow.expected | 91 ++++++++----------- .../library-tests/dataflow/global/main.rs | 7 +- .../dataflow/global/viableCallable.expected | 53 +++++------ .../dataflow/local/DataFlowStep.expected | 13 +++ .../dataflow/local/inline-flow.expected | 37 ++++++++ .../test/library-tests/dataflow/local/main.rs | 8 +- .../library-tests/dataflow/models/main.rs | 2 +- .../dataflow/models/models.expected | 47 ++++++++-- 12 files changed, 187 insertions(+), 116 deletions(-) diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 6c2d2a4db011..037b5322185a 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -847,7 +847,7 @@ class TupleFieldContent extends FieldContent, TTupleFieldContent { predicate isStructField(Struct s, int pos) { field.isStructField(s, pos) } - override FieldExprCfgNode getAnAccess() { none() } // TODO + override FieldExprCfgNode getAnAccess() { field = result.getFieldExpr().getTupleField() } final override string toString() { exists(Variant v, int pos, string vname | @@ -878,7 +878,7 @@ class RecordFieldContent extends FieldContent, TRecordFieldContent { predicate isStructField(Struct s, string name) { field.isStructField(s, name) } - override FieldExprCfgNode getAnAccess() { none() } // TODO + override FieldExprCfgNode getAnAccess() { field = result.getFieldExpr().getRecordField() } final override string toString() { exists(Variant v, string name, string vname | diff --git a/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll index e93abe999d26..a535ced76f8b 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll @@ -12,11 +12,7 @@ private import codeql.rust.elements.Resolvable * be referenced directly. */ module Impl { - private import codeql.rust.elements.internal.CallableImpl::Impl - private import codeql.rust.elements.internal.MethodCallExprImpl::Impl - private import codeql.rust.elements.internal.CallExprImpl::Impl - private import codeql.rust.elements.internal.PathExprImpl::Impl - private import codeql.rust.elements.internal.PathResolution + private import rust pragma[nomagic] Resolvable getCallResolvable(CallExprBase call) { @@ -30,14 +26,7 @@ module Impl { * A function or method call expression. See `CallExpr` and `MethodCallExpr` for further details. */ class CallExprBase extends Generated::CallExprBase { - /** - * Gets the target callable of this call, if a unique such target can - * be statically resolved. - */ - Callable getStaticTarget() { - getCallResolvable(this).resolvesAsItem(result) - or - result = resolvePath(this.(CallExpr).getFunction().(PathExpr).getPath()) - } + /** Gets the static target of this call, if any. */ + Callable getStaticTarget() { none() } } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll index 367502c75205..d5dcec253536 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll @@ -36,6 +36,8 @@ module Impl { class CallExpr extends Generated::CallExpr { override string toString() { result = this.getFunction().toAbbreviatedString() + "(...)" } + override Callable getStaticTarget() { result = getResolvedFunction(this) } + /** Gets the struct that this call resolves to, if any. */ Struct getStruct() { result = getResolvedFunction(this) } diff --git a/rust/ql/lib/codeql/rust/elements/internal/MethodCallExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/MethodCallExprImpl.qll index 7037f5ebbca5..b0bda8afc493 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/MethodCallExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/MethodCallExprImpl.qll @@ -4,13 +4,18 @@ * INTERNAL: Do not use. */ +private import rust private import codeql.rust.elements.internal.generated.MethodCallExpr +private import codeql.rust.elements.internal.PathResolution +private import codeql.rust.elements.internal.TypeInference /** * INTERNAL: This module contains the customizable definition of `MethodCallExpr` and should not * be referenced directly. */ module Impl { + private predicate isImplFunction(Function f) { f = any(ImplItemNode impl).getAnAssocItem() } + // the following QLdoc is generated: if you need to edit it, do it in the schema file /** * A method call expression. For example: @@ -20,6 +25,23 @@ module Impl { * ``` */ class MethodCallExpr extends Generated::MethodCallExpr { + override Function getStaticTarget() { + result = resolveMethodCallExpr(this) and + ( + // prioritize `impl` methods first + isImplFunction(result) + or + not isImplFunction(resolveMethodCallExpr(this)) and + ( + // then trait methods with default implementations + result.hasBody() + or + // and finally trait methods without default implementations + not resolveMethodCallExpr(this).hasBody() + ) + ) + } + override string toString() { exists(string base, string separator | base = this.getReceiver().toAbbreviatedString() and diff --git a/rust/ql/test/library-tests/dataflow/global/inline-flow.expected b/rust/ql/test/library-tests/dataflow/global/inline-flow.expected index c799f45a8e7d..54b70e0ac128 100644 --- a/rust/ql/test/library-tests/dataflow/global/inline-flow.expected +++ b/rust/ql/test/library-tests/dataflow/global/inline-flow.expected @@ -60,28 +60,21 @@ edges | main.rs:134:24:134:33 | source(...) | main.rs:134:13:134:34 | ...::new(...) [MyInt] | provenance | | | main.rs:135:9:135:26 | MyInt {...} [MyInt] | main.rs:135:24:135:24 | m | provenance | | | main.rs:135:24:135:24 | m | main.rs:136:10:136:10 | m | provenance | | -| main.rs:175:18:175:21 | SelfParam [MyInt] | main.rs:175:48:177:5 | { ... } [MyInt] | provenance | | -| main.rs:179:26:179:37 | ...: MyInt [MyInt] | main.rs:179:49:181:5 | { ... } [MyInt] | provenance | | -| main.rs:185:9:185:9 | a [MyInt] | main.rs:187:49:187:49 | a [MyInt] | provenance | | -| main.rs:185:13:185:38 | MyInt {...} [MyInt] | main.rs:185:9:185:9 | a [MyInt] | provenance | | -| main.rs:185:28:185:36 | source(...) | main.rs:185:13:185:38 | MyInt {...} [MyInt] | provenance | | -| main.rs:187:9:187:26 | MyInt {...} [MyInt] | main.rs:187:24:187:24 | c | provenance | | -| main.rs:187:24:187:24 | c | main.rs:188:10:188:10 | c | provenance | | -| main.rs:187:30:187:53 | ...::take_self(...) [MyInt] | main.rs:187:9:187:26 | MyInt {...} [MyInt] | provenance | | -| main.rs:187:49:187:49 | a [MyInt] | main.rs:175:18:175:21 | SelfParam [MyInt] | provenance | | -| main.rs:187:49:187:49 | a [MyInt] | main.rs:187:30:187:53 | ...::take_self(...) [MyInt] | provenance | | -| main.rs:191:9:191:9 | b [MyInt] | main.rs:192:54:192:54 | b [MyInt] | provenance | | -| main.rs:191:13:191:39 | MyInt {...} [MyInt] | main.rs:191:9:191:9 | b [MyInt] | provenance | | -| main.rs:191:28:191:37 | source(...) | main.rs:191:13:191:39 | MyInt {...} [MyInt] | provenance | | -| main.rs:192:9:192:26 | MyInt {...} [MyInt] | main.rs:192:24:192:24 | c | provenance | | -| main.rs:192:24:192:24 | c | main.rs:193:10:193:10 | c | provenance | | -| main.rs:192:30:192:55 | ...::take_second(...) [MyInt] | main.rs:192:9:192:26 | MyInt {...} [MyInt] | provenance | | -| main.rs:192:54:192:54 | b [MyInt] | main.rs:179:26:179:37 | ...: MyInt [MyInt] | provenance | | -| main.rs:192:54:192:54 | b [MyInt] | main.rs:192:30:192:55 | ...::take_second(...) [MyInt] | provenance | | -| main.rs:202:9:202:9 | a | main.rs:203:10:203:10 | a | provenance | | -| main.rs:202:13:202:21 | source(...) | main.rs:202:9:202:9 | a | provenance | | -| main.rs:212:13:212:13 | c | main.rs:213:14:213:14 | c | provenance | | -| main.rs:212:17:212:25 | source(...) | main.rs:212:13:212:13 | c | provenance | | +| main.rs:142:12:142:15 | SelfParam [MyInt] | main.rs:144:24:144:27 | self [MyInt] | provenance | | +| main.rs:144:9:144:35 | MyInt {...} [MyInt] | main.rs:142:42:145:5 | { ... } [MyInt] | provenance | | +| main.rs:144:24:144:27 | self [MyInt] | main.rs:144:24:144:33 | self.value | provenance | | +| main.rs:144:24:144:33 | self.value | main.rs:144:9:144:35 | MyInt {...} [MyInt] | provenance | | +| main.rs:159:9:159:9 | a [MyInt] | main.rs:142:12:142:15 | SelfParam [MyInt] | provenance | | +| main.rs:159:9:159:9 | a [MyInt] | main.rs:161:13:161:20 | a.add(...) [MyInt] | provenance | | +| main.rs:159:13:159:38 | MyInt {...} [MyInt] | main.rs:159:9:159:9 | a [MyInt] | provenance | | +| main.rs:159:28:159:36 | source(...) | main.rs:159:13:159:38 | MyInt {...} [MyInt] | provenance | | +| main.rs:161:9:161:9 | d [MyInt] | main.rs:162:10:162:10 | d [MyInt] | provenance | | +| main.rs:161:13:161:20 | a.add(...) [MyInt] | main.rs:161:9:161:9 | d [MyInt] | provenance | | +| main.rs:162:10:162:10 | d [MyInt] | main.rs:162:10:162:16 | d.value | provenance | | +| main.rs:201:9:201:9 | a | main.rs:202:10:202:10 | a | provenance | | +| main.rs:201:13:201:21 | source(...) | main.rs:201:9:201:9 | a | provenance | | +| main.rs:211:13:211:13 | c | main.rs:212:14:212:14 | c | provenance | | +| main.rs:211:17:211:25 | source(...) | main.rs:211:13:211:13 | c | provenance | | nodes | main.rs:12:28:14:1 | { ... } | semmle.label | { ... } | | main.rs:13:5:13:13 | source(...) | semmle.label | source(...) | @@ -151,32 +144,24 @@ nodes | main.rs:135:9:135:26 | MyInt {...} [MyInt] | semmle.label | MyInt {...} [MyInt] | | main.rs:135:24:135:24 | m | semmle.label | m | | main.rs:136:10:136:10 | m | semmle.label | m | -| main.rs:175:18:175:21 | SelfParam [MyInt] | semmle.label | SelfParam [MyInt] | -| main.rs:175:48:177:5 | { ... } [MyInt] | semmle.label | { ... } [MyInt] | -| main.rs:179:26:179:37 | ...: MyInt [MyInt] | semmle.label | ...: MyInt [MyInt] | -| main.rs:179:49:181:5 | { ... } [MyInt] | semmle.label | { ... } [MyInt] | -| main.rs:185:9:185:9 | a [MyInt] | semmle.label | a [MyInt] | -| main.rs:185:13:185:38 | MyInt {...} [MyInt] | semmle.label | MyInt {...} [MyInt] | -| main.rs:185:28:185:36 | source(...) | semmle.label | source(...) | -| main.rs:187:9:187:26 | MyInt {...} [MyInt] | semmle.label | MyInt {...} [MyInt] | -| main.rs:187:24:187:24 | c | semmle.label | c | -| main.rs:187:30:187:53 | ...::take_self(...) [MyInt] | semmle.label | ...::take_self(...) [MyInt] | -| main.rs:187:49:187:49 | a [MyInt] | semmle.label | a [MyInt] | -| main.rs:188:10:188:10 | c | semmle.label | c | -| main.rs:191:9:191:9 | b [MyInt] | semmle.label | b [MyInt] | -| main.rs:191:13:191:39 | MyInt {...} [MyInt] | semmle.label | MyInt {...} [MyInt] | -| main.rs:191:28:191:37 | source(...) | semmle.label | source(...) | -| main.rs:192:9:192:26 | MyInt {...} [MyInt] | semmle.label | MyInt {...} [MyInt] | -| main.rs:192:24:192:24 | c | semmle.label | c | -| main.rs:192:30:192:55 | ...::take_second(...) [MyInt] | semmle.label | ...::take_second(...) [MyInt] | -| main.rs:192:54:192:54 | b [MyInt] | semmle.label | b [MyInt] | -| main.rs:193:10:193:10 | c | semmle.label | c | -| main.rs:202:9:202:9 | a | semmle.label | a | -| main.rs:202:13:202:21 | source(...) | semmle.label | source(...) | -| main.rs:203:10:203:10 | a | semmle.label | a | -| main.rs:212:13:212:13 | c | semmle.label | c | -| main.rs:212:17:212:25 | source(...) | semmle.label | source(...) | -| main.rs:213:14:213:14 | c | semmle.label | c | +| main.rs:142:12:142:15 | SelfParam [MyInt] | semmle.label | SelfParam [MyInt] | +| main.rs:142:42:145:5 | { ... } [MyInt] | semmle.label | { ... } [MyInt] | +| main.rs:144:9:144:35 | MyInt {...} [MyInt] | semmle.label | MyInt {...} [MyInt] | +| main.rs:144:24:144:27 | self [MyInt] | semmle.label | self [MyInt] | +| main.rs:144:24:144:33 | self.value | semmle.label | self.value | +| main.rs:159:9:159:9 | a [MyInt] | semmle.label | a [MyInt] | +| main.rs:159:13:159:38 | MyInt {...} [MyInt] | semmle.label | MyInt {...} [MyInt] | +| main.rs:159:28:159:36 | source(...) | semmle.label | source(...) | +| main.rs:161:9:161:9 | d [MyInt] | semmle.label | d [MyInt] | +| main.rs:161:13:161:20 | a.add(...) [MyInt] | semmle.label | a.add(...) [MyInt] | +| main.rs:162:10:162:10 | d [MyInt] | semmle.label | d [MyInt] | +| main.rs:162:10:162:16 | d.value | semmle.label | d.value | +| main.rs:201:9:201:9 | a | semmle.label | a | +| main.rs:201:13:201:21 | source(...) | semmle.label | source(...) | +| main.rs:202:10:202:10 | a | semmle.label | a | +| main.rs:211:13:211:13 | c | semmle.label | c | +| main.rs:211:17:211:25 | source(...) | semmle.label | source(...) | +| main.rs:212:14:212:14 | c | semmle.label | c | subpaths | main.rs:36:26:36:26 | a | main.rs:30:17:30:22 | ...: i64 | main.rs:30:32:32:1 | { ... } | main.rs:36:13:36:27 | pass_through(...) | | main.rs:41:26:44:5 | { ... } | main.rs:30:17:30:22 | ...: i64 | main.rs:30:32:32:1 | { ... } | main.rs:41:13:44:6 | pass_through(...) | @@ -184,8 +169,7 @@ subpaths | main.rs:103:29:103:29 | a | main.rs:79:27:79:32 | ...: i64 | main.rs:79:42:85:5 | { ... } | main.rs:103:13:103:30 | mn.data_through(...) | | main.rs:116:38:116:38 | a | main.rs:79:27:79:32 | ...: i64 | main.rs:79:42:85:5 | { ... } | main.rs:116:13:116:39 | ...::data_through(...) | | main.rs:134:24:134:33 | source(...) | main.rs:128:12:128:17 | ...: i64 | main.rs:128:28:130:5 | { ... } [MyInt] | main.rs:134:13:134:34 | ...::new(...) [MyInt] | -| main.rs:187:49:187:49 | a [MyInt] | main.rs:175:18:175:21 | SelfParam [MyInt] | main.rs:175:48:177:5 | { ... } [MyInt] | main.rs:187:30:187:53 | ...::take_self(...) [MyInt] | -| main.rs:192:54:192:54 | b [MyInt] | main.rs:179:26:179:37 | ...: MyInt [MyInt] | main.rs:179:49:181:5 | { ... } [MyInt] | main.rs:192:30:192:55 | ...::take_second(...) [MyInt] | +| main.rs:159:9:159:9 | a [MyInt] | main.rs:142:12:142:15 | SelfParam [MyInt] | main.rs:142:42:145:5 | { ... } [MyInt] | main.rs:161:13:161:20 | a.add(...) [MyInt] | testFailures #select | main.rs:18:10:18:10 | a | main.rs:13:5:13:13 | source(...) | main.rs:18:10:18:10 | a | $@ | main.rs:13:5:13:13 | source(...) | source(...) | @@ -199,7 +183,6 @@ testFailures | main.rs:104:10:104:10 | b | main.rs:102:13:102:21 | source(...) | main.rs:104:10:104:10 | b | $@ | main.rs:102:13:102:21 | source(...) | source(...) | | main.rs:117:10:117:10 | b | main.rs:115:13:115:22 | source(...) | main.rs:117:10:117:10 | b | $@ | main.rs:115:13:115:22 | source(...) | source(...) | | main.rs:136:10:136:10 | m | main.rs:134:24:134:33 | source(...) | main.rs:136:10:136:10 | m | $@ | main.rs:134:24:134:33 | source(...) | source(...) | -| main.rs:188:10:188:10 | c | main.rs:185:28:185:36 | source(...) | main.rs:188:10:188:10 | c | $@ | main.rs:185:28:185:36 | source(...) | source(...) | -| main.rs:193:10:193:10 | c | main.rs:191:28:191:37 | source(...) | main.rs:193:10:193:10 | c | $@ | main.rs:191:28:191:37 | source(...) | source(...) | -| main.rs:203:10:203:10 | a | main.rs:202:13:202:21 | source(...) | main.rs:203:10:203:10 | a | $@ | main.rs:202:13:202:21 | source(...) | source(...) | -| main.rs:213:14:213:14 | c | main.rs:212:17:212:25 | source(...) | main.rs:213:14:213:14 | c | $@ | main.rs:212:17:212:25 | source(...) | source(...) | +| main.rs:162:10:162:16 | d.value | main.rs:159:28:159:36 | source(...) | main.rs:162:10:162:16 | d.value | $@ | main.rs:159:28:159:36 | source(...) | source(...) | +| main.rs:202:10:202:10 | a | main.rs:201:13:201:21 | source(...) | main.rs:202:10:202:10 | a | $@ | main.rs:201:13:201:21 | source(...) | source(...) | +| main.rs:212:14:212:14 | c | main.rs:211:17:211:25 | source(...) | main.rs:212:14:212:14 | c | $@ | main.rs:211:17:211:25 | source(...) | source(...) | diff --git a/rust/ql/test/library-tests/dataflow/global/main.rs b/rust/ql/test/library-tests/dataflow/global/main.rs index a9d26e46b073..c1e3ee4e8387 100644 --- a/rust/ql/test/library-tests/dataflow/global/main.rs +++ b/rust/ql/test/library-tests/dataflow/global/main.rs @@ -159,8 +159,7 @@ fn test_operator_overloading() { let a = MyInt { value: source(7) }; let b = MyInt { value: 2 }; let d = a.add(b); - sink(d.value); // $ MISSING: hasValueFlow=7 - + sink(d.value); // $ hasValueFlow=7 } trait MyTrait { @@ -185,12 +184,12 @@ fn data_through_trait_method_called_as_function() { let a = MyInt { value: source(8) }; let b = MyInt { value: 2 }; let MyInt { value: c } = MyTrait::take_self(a, b); - sink(c); // $ hasValueFlow=8 + sink(c); // $ MISSING: hasValueFlow=8 let a = MyInt { value: 0 }; let b = MyInt { value: source(37) }; let MyInt { value: c } = MyTrait::take_second(a, b); - sink(c); // $ hasValueFlow=37 + sink(c); // $ MISSING: hasValueFlow=37 let a = MyInt { value: 0 }; let b = MyInt { value: source(38) }; diff --git a/rust/ql/test/library-tests/dataflow/global/viableCallable.expected b/rust/ql/test/library-tests/dataflow/global/viableCallable.expected index 776300d8943e..2bbff8e33c3f 100644 --- a/rust/ql/test/library-tests/dataflow/global/viableCallable.expected +++ b/rust/ql/test/library-tests/dataflow/global/viableCallable.expected @@ -37,31 +37,28 @@ | main.rs:159:28:159:36 | source(...) | main.rs:1:1:3:1 | fn source | | main.rs:161:13:161:20 | a.add(...) | main.rs:142:5:145:5 | fn add | | main.rs:162:5:162:17 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:185:28:185:36 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:187:30:187:53 | ...::take_self(...) | main.rs:175:5:177:5 | fn take_self | -| main.rs:188:5:188:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:191:28:191:37 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:192:30:192:55 | ...::take_second(...) | main.rs:179:5:181:5 | fn take_second | -| main.rs:193:5:193:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:196:28:196:37 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:197:30:197:53 | ...::take_self(...) | main.rs:175:5:177:5 | fn take_self | -| main.rs:198:5:198:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:202:13:202:21 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:203:5:203:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:208:13:208:26 | async_source(...) | main.rs:201:1:205:1 | fn async_source | -| main.rs:209:5:209:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:212:17:212:25 | source(...) | main.rs:1:1:3:1 | fn source | -| main.rs:213:9:213:15 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:216:5:216:17 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:220:41:220:54 | async_source(...) | main.rs:201:1:205:1 | fn async_source | -| main.rs:221:5:221:11 | sink(...) | main.rs:5:1:7:1 | fn sink | -| main.rs:223:33:223:61 | test_async_await_async_part(...) | main.rs:207:1:217:1 | fn test_async_await_async_part | -| main.rs:227:5:227:22 | data_out_of_call(...) | main.rs:16:1:19:1 | fn data_out_of_call | -| main.rs:228:5:228:21 | data_in_to_call(...) | main.rs:25:1:28:1 | fn data_in_to_call | -| main.rs:229:5:229:23 | data_through_call(...) | main.rs:34:1:38:1 | fn data_through_call | -| main.rs:230:5:230:34 | data_through_nested_function(...) | main.rs:48:1:57:1 | fn data_through_nested_function | -| main.rs:232:5:232:24 | data_out_of_method(...) | main.rs:88:1:92:1 | fn data_out_of_method | -| main.rs:233:5:233:28 | data_in_to_method_call(...) | main.rs:94:1:98:1 | fn data_in_to_method_call | -| main.rs:234:5:234:25 | data_through_method(...) | main.rs:100:1:105:1 | fn data_through_method | -| main.rs:236:5:236:31 | test_operator_overloading(...) | main.rs:148:1:164:1 | fn test_operator_overloading | -| main.rs:237:5:237:22 | test_async_await(...) | main.rs:219:1:224:1 | fn test_async_await | +| main.rs:184:28:184:36 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:187:5:187:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:190:28:190:37 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:192:5:192:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:195:28:195:37 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:197:5:197:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:201:13:201:21 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:202:5:202:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:207:13:207:26 | async_source(...) | main.rs:200:1:204:1 | fn async_source | +| main.rs:208:5:208:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:211:17:211:25 | source(...) | main.rs:1:1:3:1 | fn source | +| main.rs:212:9:212:15 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:215:5:215:17 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:219:41:219:54 | async_source(...) | main.rs:200:1:204:1 | fn async_source | +| main.rs:220:5:220:11 | sink(...) | main.rs:5:1:7:1 | fn sink | +| main.rs:222:33:222:61 | test_async_await_async_part(...) | main.rs:206:1:216:1 | fn test_async_await_async_part | +| main.rs:226:5:226:22 | data_out_of_call(...) | main.rs:16:1:19:1 | fn data_out_of_call | +| main.rs:227:5:227:21 | data_in_to_call(...) | main.rs:25:1:28:1 | fn data_in_to_call | +| main.rs:228:5:228:23 | data_through_call(...) | main.rs:34:1:38:1 | fn data_through_call | +| main.rs:229:5:229:34 | data_through_nested_function(...) | main.rs:48:1:57:1 | fn data_through_nested_function | +| main.rs:231:5:231:24 | data_out_of_method(...) | main.rs:88:1:92:1 | fn data_out_of_method | +| main.rs:232:5:232:28 | data_in_to_method_call(...) | main.rs:94:1:98:1 | fn data_in_to_method_call | +| main.rs:233:5:233:25 | data_through_method(...) | main.rs:100:1:105:1 | fn data_through_method | +| main.rs:235:5:235:31 | test_operator_overloading(...) | main.rs:148:1:163:1 | fn test_operator_overloading | +| main.rs:236:5:236:22 | test_async_await(...) | main.rs:218:1:223:1 | fn test_async_await | diff --git a/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected b/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected index 8a8c5988426a..8d4ea1546149 100644 --- a/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected +++ b/rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected @@ -2321,6 +2321,7 @@ storeStep | main.rs:137:38:137:38 | 2 | Point.y | main.rs:137:13:137:40 | Point {...} | | main.rs:143:28:143:36 | source(...) | Point.x | main.rs:143:17:143:44 | Point {...} | | main.rs:143:42:143:42 | 2 | Point.y | main.rs:143:17:143:44 | Point {...} | +| main.rs:145:11:145:20 | source(...) | Point.y | main.rs:145:5:145:5 | [post] p | | main.rs:151:12:151:21 | source(...) | Point.x | main.rs:150:13:153:5 | Point {...} | | main.rs:152:12:152:12 | 2 | Point.y | main.rs:150:13:153:5 | Point {...} | | main.rs:166:16:169:9 | Point {...} | Point3D.plane | main.rs:165:13:171:5 | Point3D {...} | @@ -2956,13 +2957,25 @@ readStep | main.rs:124:10:124:10 | b | tuple.0 | main.rs:124:10:124:12 | b.0 | | main.rs:124:10:124:12 | b.0 | tuple.1 | main.rs:124:10:124:15 | ... .1 | | main.rs:125:10:125:10 | b | tuple.1 | main.rs:125:10:125:12 | b.1 | +| main.rs:138:10:138:10 | p | Point.x | main.rs:138:10:138:12 | p.x | +| main.rs:139:10:139:10 | p | Point.y | main.rs:139:10:139:12 | p.y | +| main.rs:144:10:144:10 | p | Point.y | main.rs:144:10:144:12 | p.y | +| main.rs:145:5:145:5 | p | Point.y | main.rs:145:5:145:7 | p.y | +| main.rs:146:10:146:10 | p | Point.y | main.rs:146:10:146:12 | p.y | | main.rs:154:9:154:28 | Point {...} | Point.x | main.rs:154:20:154:20 | a | | main.rs:154:9:154:28 | Point {...} | Point.y | main.rs:154:26:154:26 | b | +| main.rs:172:10:172:10 | p | Point3D.plane | main.rs:172:10:172:16 | p.plane | +| main.rs:172:10:172:16 | p.plane | Point.x | main.rs:172:10:172:18 | ... .x | +| main.rs:173:10:173:10 | p | Point3D.plane | main.rs:173:10:173:16 | p.plane | +| main.rs:173:10:173:16 | p.plane | Point.y | main.rs:173:10:173:18 | ... .y | +| main.rs:174:10:174:10 | p | Point3D.z | main.rs:174:10:174:12 | p.z | | main.rs:184:9:187:9 | Point3D {...} | Point3D.plane | main.rs:185:20:185:33 | Point {...} | | main.rs:184:9:187:9 | Point3D {...} | Point3D.z | main.rs:186:13:186:13 | z | | main.rs:185:20:185:33 | Point {...} | Point.x | main.rs:185:28:185:28 | x | | main.rs:185:20:185:33 | Point {...} | Point.y | main.rs:185:31:185:31 | y | +| main.rs:199:10:199:10 | s | MyTupleStruct(0) | main.rs:199:10:199:12 | s.0 | | main.rs:199:10:199:10 | s | tuple.0 | main.rs:199:10:199:12 | s.0 | +| main.rs:200:10:200:10 | s | MyTupleStruct(1) | main.rs:200:10:200:12 | s.1 | | main.rs:200:10:200:10 | s | tuple.1 | main.rs:200:10:200:12 | s.1 | | main.rs:203:9:203:27 | MyTupleStruct(...) | MyTupleStruct(0) | main.rs:203:23:203:23 | x | | main.rs:203:9:203:27 | MyTupleStruct(...) | MyTupleStruct(1) | main.rs:203:26:203:26 | y | diff --git a/rust/ql/test/library-tests/dataflow/local/inline-flow.expected b/rust/ql/test/library-tests/dataflow/local/inline-flow.expected index 47018a54719c..851f497faefb 100644 --- a/rust/ql/test/library-tests/dataflow/local/inline-flow.expected +++ b/rust/ql/test/library-tests/dataflow/local/inline-flow.expected @@ -46,11 +46,24 @@ edges | main.rs:122:14:122:14 | a [tuple.1] | main.rs:122:13:122:18 | TupleExpr [tuple.0, tuple.1] | provenance | | | main.rs:124:10:124:10 | b [tuple.0, tuple.1] | main.rs:124:10:124:12 | b.0 [tuple.1] | provenance | | | main.rs:124:10:124:12 | b.0 [tuple.1] | main.rs:124:10:124:15 | ... .1 | provenance | | +| main.rs:137:9:137:9 | p [Point.x] | main.rs:138:10:138:10 | p [Point.x] | provenance | | +| main.rs:137:13:137:40 | Point {...} [Point.x] | main.rs:137:9:137:9 | p [Point.x] | provenance | | +| main.rs:137:24:137:32 | source(...) | main.rs:137:13:137:40 | Point {...} [Point.x] | provenance | | +| main.rs:138:10:138:10 | p [Point.x] | main.rs:138:10:138:12 | p.x | provenance | | +| main.rs:145:5:145:5 | [post] p [Point.y] | main.rs:146:10:146:10 | p [Point.y] | provenance | | +| main.rs:145:11:145:20 | source(...) | main.rs:145:5:145:5 | [post] p [Point.y] | provenance | | +| main.rs:146:10:146:10 | p [Point.y] | main.rs:146:10:146:12 | p.y | provenance | | | main.rs:150:9:150:9 | p [Point.x] | main.rs:154:9:154:28 | Point {...} [Point.x] | provenance | | | main.rs:150:13:153:5 | Point {...} [Point.x] | main.rs:150:9:150:9 | p [Point.x] | provenance | | | main.rs:151:12:151:21 | source(...) | main.rs:150:13:153:5 | Point {...} [Point.x] | provenance | | | main.rs:154:9:154:28 | Point {...} [Point.x] | main.rs:154:20:154:20 | a | provenance | | | main.rs:154:20:154:20 | a | main.rs:155:10:155:10 | a | provenance | | +| main.rs:165:9:165:9 | p [Point3D.plane, Point.y] | main.rs:173:10:173:10 | p [Point3D.plane, Point.y] | provenance | | +| main.rs:165:13:171:5 | Point3D {...} [Point3D.plane, Point.y] | main.rs:165:9:165:9 | p [Point3D.plane, Point.y] | provenance | | +| main.rs:166:16:169:9 | Point {...} [Point.y] | main.rs:165:13:171:5 | Point3D {...} [Point3D.plane, Point.y] | provenance | | +| main.rs:168:16:168:25 | source(...) | main.rs:166:16:169:9 | Point {...} [Point.y] | provenance | | +| main.rs:173:10:173:10 | p [Point3D.plane, Point.y] | main.rs:173:10:173:16 | p.plane [Point.y] | provenance | | +| main.rs:173:10:173:16 | p.plane [Point.y] | main.rs:173:10:173:18 | ... .y | provenance | | | main.rs:178:9:178:9 | y | main.rs:180:30:180:30 | y | provenance | | | main.rs:178:13:178:22 | source(...) | main.rs:178:9:178:9 | y | provenance | | | main.rs:179:9:179:9 | p [Point3D.plane, Point.y] | main.rs:183:11:183:11 | p [Point3D.plane, Point.y] | provenance | | @@ -61,9 +74,11 @@ edges | main.rs:184:9:187:9 | Point3D {...} [Point3D.plane, Point.y] | main.rs:185:20:185:33 | Point {...} [Point.y] | provenance | | | main.rs:185:20:185:33 | Point {...} [Point.y] | main.rs:185:31:185:31 | y | provenance | | | main.rs:185:31:185:31 | y | main.rs:189:18:189:18 | y | provenance | | +| main.rs:198:9:198:9 | s [MyTupleStruct(0)] | main.rs:199:10:199:10 | s [MyTupleStruct(0)] | provenance | | | main.rs:198:9:198:9 | s [MyTupleStruct(0)] | main.rs:202:11:202:11 | s [MyTupleStruct(0)] | provenance | | | main.rs:198:13:198:40 | MyTupleStruct(...) [MyTupleStruct(0)] | main.rs:198:9:198:9 | s [MyTupleStruct(0)] | provenance | | | main.rs:198:27:198:36 | source(...) | main.rs:198:13:198:40 | MyTupleStruct(...) [MyTupleStruct(0)] | provenance | | +| main.rs:199:10:199:10 | s [MyTupleStruct(0)] | main.rs:199:10:199:12 | s.0 | provenance | | | main.rs:202:11:202:11 | s [MyTupleStruct(0)] | main.rs:203:9:203:27 | MyTupleStruct(...) [MyTupleStruct(0)] | provenance | | | main.rs:203:9:203:27 | MyTupleStruct(...) [MyTupleStruct(0)] | main.rs:203:23:203:23 | x | provenance | | | main.rs:203:23:203:23 | x | main.rs:204:18:204:18 | x | provenance | | @@ -264,12 +279,28 @@ nodes | main.rs:124:10:124:10 | b [tuple.0, tuple.1] | semmle.label | b [tuple.0, tuple.1] | | main.rs:124:10:124:12 | b.0 [tuple.1] | semmle.label | b.0 [tuple.1] | | main.rs:124:10:124:15 | ... .1 | semmle.label | ... .1 | +| main.rs:137:9:137:9 | p [Point.x] | semmle.label | p [Point.x] | +| main.rs:137:13:137:40 | Point {...} [Point.x] | semmle.label | Point {...} [Point.x] | +| main.rs:137:24:137:32 | source(...) | semmle.label | source(...) | +| main.rs:138:10:138:10 | p [Point.x] | semmle.label | p [Point.x] | +| main.rs:138:10:138:12 | p.x | semmle.label | p.x | +| main.rs:145:5:145:5 | [post] p [Point.y] | semmle.label | [post] p [Point.y] | +| main.rs:145:11:145:20 | source(...) | semmle.label | source(...) | +| main.rs:146:10:146:10 | p [Point.y] | semmle.label | p [Point.y] | +| main.rs:146:10:146:12 | p.y | semmle.label | p.y | | main.rs:150:9:150:9 | p [Point.x] | semmle.label | p [Point.x] | | main.rs:150:13:153:5 | Point {...} [Point.x] | semmle.label | Point {...} [Point.x] | | main.rs:151:12:151:21 | source(...) | semmle.label | source(...) | | main.rs:154:9:154:28 | Point {...} [Point.x] | semmle.label | Point {...} [Point.x] | | main.rs:154:20:154:20 | a | semmle.label | a | | main.rs:155:10:155:10 | a | semmle.label | a | +| main.rs:165:9:165:9 | p [Point3D.plane, Point.y] | semmle.label | p [Point3D.plane, Point.y] | +| main.rs:165:13:171:5 | Point3D {...} [Point3D.plane, Point.y] | semmle.label | Point3D {...} [Point3D.plane, Point.y] | +| main.rs:166:16:169:9 | Point {...} [Point.y] | semmle.label | Point {...} [Point.y] | +| main.rs:168:16:168:25 | source(...) | semmle.label | source(...) | +| main.rs:173:10:173:10 | p [Point3D.plane, Point.y] | semmle.label | p [Point3D.plane, Point.y] | +| main.rs:173:10:173:16 | p.plane [Point.y] | semmle.label | p.plane [Point.y] | +| main.rs:173:10:173:18 | ... .y | semmle.label | ... .y | | main.rs:178:9:178:9 | y | semmle.label | y | | main.rs:178:13:178:22 | source(...) | semmle.label | source(...) | | main.rs:179:9:179:9 | p [Point3D.plane, Point.y] | semmle.label | p [Point3D.plane, Point.y] | @@ -284,6 +315,8 @@ nodes | main.rs:198:9:198:9 | s [MyTupleStruct(0)] | semmle.label | s [MyTupleStruct(0)] | | main.rs:198:13:198:40 | MyTupleStruct(...) [MyTupleStruct(0)] | semmle.label | MyTupleStruct(...) [MyTupleStruct(0)] | | main.rs:198:27:198:36 | source(...) | semmle.label | source(...) | +| main.rs:199:10:199:10 | s [MyTupleStruct(0)] | semmle.label | s [MyTupleStruct(0)] | +| main.rs:199:10:199:12 | s.0 | semmle.label | s.0 | | main.rs:202:11:202:11 | s [MyTupleStruct(0)] | semmle.label | s [MyTupleStruct(0)] | | main.rs:203:9:203:27 | MyTupleStruct(...) [MyTupleStruct(0)] | semmle.label | MyTupleStruct(...) [MyTupleStruct(0)] | | main.rs:203:23:203:23 | x | semmle.label | x | @@ -478,8 +511,12 @@ testFailures | main.rs:113:10:113:12 | a.1 | main.rs:111:21:111:30 | source(...) | main.rs:113:10:113:12 | a.1 | $@ | main.rs:111:21:111:30 | source(...) | source(...) | | main.rs:116:10:116:12 | a.0 | main.rs:114:11:114:20 | source(...) | main.rs:116:10:116:12 | a.0 | $@ | main.rs:114:11:114:20 | source(...) | source(...) | | main.rs:124:10:124:15 | ... .1 | main.rs:121:17:121:26 | source(...) | main.rs:124:10:124:15 | ... .1 | $@ | main.rs:121:17:121:26 | source(...) | source(...) | +| main.rs:138:10:138:12 | p.x | main.rs:137:24:137:32 | source(...) | main.rs:138:10:138:12 | p.x | $@ | main.rs:137:24:137:32 | source(...) | source(...) | +| main.rs:146:10:146:12 | p.y | main.rs:145:11:145:20 | source(...) | main.rs:146:10:146:12 | p.y | $@ | main.rs:145:11:145:20 | source(...) | source(...) | | main.rs:155:10:155:10 | a | main.rs:151:12:151:21 | source(...) | main.rs:155:10:155:10 | a | $@ | main.rs:151:12:151:21 | source(...) | source(...) | +| main.rs:173:10:173:18 | ... .y | main.rs:168:16:168:25 | source(...) | main.rs:173:10:173:18 | ... .y | $@ | main.rs:168:16:168:25 | source(...) | source(...) | | main.rs:189:18:189:18 | y | main.rs:178:13:178:22 | source(...) | main.rs:189:18:189:18 | y | $@ | main.rs:178:13:178:22 | source(...) | source(...) | +| main.rs:199:10:199:12 | s.0 | main.rs:198:27:198:36 | source(...) | main.rs:199:10:199:12 | s.0 | $@ | main.rs:198:27:198:36 | source(...) | source(...) | | main.rs:204:18:204:18 | x | main.rs:198:27:198:36 | source(...) | main.rs:204:18:204:18 | x | $@ | main.rs:198:27:198:36 | source(...) | source(...) | | main.rs:217:33:217:33 | n | main.rs:214:27:214:36 | source(...) | main.rs:217:33:217:33 | n | $@ | main.rs:214:27:214:36 | source(...) | source(...) | | main.rs:230:25:230:25 | n | main.rs:227:19:227:28 | source(...) | main.rs:230:25:230:25 | n | $@ | main.rs:227:19:227:28 | source(...) | source(...) | diff --git a/rust/ql/test/library-tests/dataflow/local/main.rs b/rust/ql/test/library-tests/dataflow/local/main.rs index b8622173b468..3d95386407e7 100644 --- a/rust/ql/test/library-tests/dataflow/local/main.rs +++ b/rust/ql/test/library-tests/dataflow/local/main.rs @@ -135,7 +135,7 @@ struct Point { fn struct_field() { let p = Point { x: source(9), y: 2 }; - sink(p.x); // $ MISSING: hasValueFlow=9 + sink(p.x); // $ hasValueFlow=9 sink(p.y); } @@ -143,7 +143,7 @@ fn struct_mutation() { let mut p = Point { x: source(9), y: 2 }; sink(p.y); p.y = source(54); - sink(p.y); // $ MISSING: hasValueFlow=54 + sink(p.y); // $ hasValueFlow=54 } fn struct_pattern_match() { @@ -170,7 +170,7 @@ fn struct_nested_field() { z: 4, }; sink(p.plane.x); - sink(p.plane.y); // $ MISSING: hasValueFlow=77 + sink(p.plane.y); // $ hasValueFlow=77 sink(p.z); } @@ -196,7 +196,7 @@ struct MyTupleStruct(i64, i64); fn tuple_struct() { let s = MyTupleStruct(source(94), 2); - sink(s.0); // $ MISSING: hasValueFlow=94 + sink(s.0); // $ hasValueFlow=94 sink(s.1); match s { diff --git a/rust/ql/test/library-tests/dataflow/models/main.rs b/rust/ql/test/library-tests/dataflow/models/main.rs index 35c307671592..62e79c804482 100644 --- a/rust/ql/test/library-tests/dataflow/models/main.rs +++ b/rust/ql/test/library-tests/dataflow/models/main.rs @@ -126,7 +126,7 @@ fn test_set_struct_field() { let s = source(7); let my_struct = set_struct_field(s); sink(my_struct.field1); - sink(my_struct.field2); // $ MISSING: hasValueFlow=7 + sink(my_struct.field2); // $ hasValueFlow=7 } // has a flow model diff --git a/rust/ql/test/library-tests/dataflow/models/models.expected b/rust/ql/test/library-tests/dataflow/models/models.expected index d20b05c8bf36..c434d3f13703 100644 --- a/rust/ql/test/library-tests/dataflow/models/models.expected +++ b/rust/ql/test/library-tests/dataflow/models/models.expected @@ -15,9 +15,10 @@ models | 14 | Summary: repo::test; crate::get_var_field; Argument[0].Field[crate::MyFieldEnum::C::field_c]; ReturnValue; value | | 15 | Summary: repo::test; crate::get_var_pos; Argument[0].Field[crate::MyPosEnum::A(0)]; ReturnValue; value | | 16 | Summary: repo::test; crate::set_array_element; Argument[0]; ReturnValue.Element; value | -| 17 | Summary: repo::test; crate::set_tuple_element; Argument[0]; ReturnValue.Field[1]; value | -| 18 | Summary: repo::test; crate::set_var_field; Argument[0]; ReturnValue.Field[crate::MyFieldEnum::D::field_d]; value | -| 19 | Summary: repo::test; crate::set_var_pos; Argument[0]; ReturnValue.Field[crate::MyPosEnum::B(0)]; value | +| 17 | Summary: repo::test; crate::set_struct_field; Argument[0]; ReturnValue.Field[crate::MyStruct::field2]; value | +| 18 | Summary: repo::test; crate::set_tuple_element; Argument[0]; ReturnValue.Field[1]; value | +| 19 | Summary: repo::test; crate::set_var_field; Argument[0]; ReturnValue.Field[crate::MyFieldEnum::D::field_d]; value | +| 20 | Summary: repo::test; crate::set_var_pos; Argument[0]; ReturnValue.Field[crate::MyPosEnum::B(0)]; value | edges | main.rs:15:9:15:9 | s | main.rs:16:19:16:19 | s | provenance | | | main.rs:15:9:15:9 | s | main.rs:16:19:16:19 | s | provenance | | @@ -48,8 +49,8 @@ edges | main.rs:54:9:54:10 | e1 [B] | main.rs:55:11:55:12 | e1 [B] | provenance | | | main.rs:54:14:54:27 | set_var_pos(...) [B] | main.rs:54:9:54:10 | e1 [B] | provenance | | | main.rs:54:14:54:27 | set_var_pos(...) [B] | main.rs:54:9:54:10 | e1 [B] | provenance | | -| main.rs:54:26:54:26 | s | main.rs:54:14:54:27 | set_var_pos(...) [B] | provenance | MaD:19 | -| main.rs:54:26:54:26 | s | main.rs:54:14:54:27 | set_var_pos(...) [B] | provenance | MaD:19 | +| main.rs:54:26:54:26 | s | main.rs:54:14:54:27 | set_var_pos(...) [B] | provenance | MaD:20 | +| main.rs:54:26:54:26 | s | main.rs:54:14:54:27 | set_var_pos(...) [B] | provenance | MaD:20 | | main.rs:55:11:55:12 | e1 [B] | main.rs:57:9:57:23 | ...::B(...) [B] | provenance | | | main.rs:55:11:55:12 | e1 [B] | main.rs:57:9:57:23 | ...::B(...) [B] | provenance | | | main.rs:57:9:57:23 | ...::B(...) [B] | main.rs:57:22:57:22 | i | provenance | | @@ -76,8 +77,8 @@ edges | main.rs:86:9:86:10 | e1 [D] | main.rs:87:11:87:12 | e1 [D] | provenance | | | main.rs:86:14:86:29 | set_var_field(...) [D] | main.rs:86:9:86:10 | e1 [D] | provenance | | | main.rs:86:14:86:29 | set_var_field(...) [D] | main.rs:86:9:86:10 | e1 [D] | provenance | | -| main.rs:86:28:86:28 | s | main.rs:86:14:86:29 | set_var_field(...) [D] | provenance | MaD:18 | -| main.rs:86:28:86:28 | s | main.rs:86:14:86:29 | set_var_field(...) [D] | provenance | MaD:18 | +| main.rs:86:28:86:28 | s | main.rs:86:14:86:29 | set_var_field(...) [D] | provenance | MaD:19 | +| main.rs:86:28:86:28 | s | main.rs:86:14:86:29 | set_var_field(...) [D] | provenance | MaD:19 | | main.rs:87:11:87:12 | e1 [D] | main.rs:89:9:89:37 | ...::D {...} [D] | provenance | | | main.rs:87:11:87:12 | e1 [D] | main.rs:89:9:89:37 | ...::D {...} [D] | provenance | | | main.rs:89:9:89:37 | ...::D {...} [D] | main.rs:89:35:89:35 | i | provenance | | @@ -96,6 +97,18 @@ edges | main.rs:106:17:106:17 | s | main.rs:105:21:108:5 | MyStruct {...} [MyStruct.field1] | provenance | | | main.rs:109:27:109:35 | my_struct [MyStruct.field1] | main.rs:109:10:109:36 | get_struct_field(...) | provenance | MaD:12 | | main.rs:109:27:109:35 | my_struct [MyStruct.field1] | main.rs:109:10:109:36 | get_struct_field(...) | provenance | MaD:12 | +| main.rs:126:9:126:9 | s | main.rs:127:38:127:38 | s | provenance | | +| main.rs:126:9:126:9 | s | main.rs:127:38:127:38 | s | provenance | | +| main.rs:126:13:126:21 | source(...) | main.rs:126:9:126:9 | s | provenance | | +| main.rs:126:13:126:21 | source(...) | main.rs:126:9:126:9 | s | provenance | | +| main.rs:127:9:127:17 | my_struct [MyStruct.field2] | main.rs:129:10:129:18 | my_struct [MyStruct.field2] | provenance | | +| main.rs:127:9:127:17 | my_struct [MyStruct.field2] | main.rs:129:10:129:18 | my_struct [MyStruct.field2] | provenance | | +| main.rs:127:21:127:39 | set_struct_field(...) [MyStruct.field2] | main.rs:127:9:127:17 | my_struct [MyStruct.field2] | provenance | | +| main.rs:127:21:127:39 | set_struct_field(...) [MyStruct.field2] | main.rs:127:9:127:17 | my_struct [MyStruct.field2] | provenance | | +| main.rs:127:38:127:38 | s | main.rs:127:21:127:39 | set_struct_field(...) [MyStruct.field2] | provenance | MaD:17 | +| main.rs:127:38:127:38 | s | main.rs:127:21:127:39 | set_struct_field(...) [MyStruct.field2] | provenance | MaD:17 | +| main.rs:129:10:129:18 | my_struct [MyStruct.field2] | main.rs:129:10:129:25 | my_struct.field2 | provenance | | +| main.rs:129:10:129:18 | my_struct [MyStruct.field2] | main.rs:129:10:129:25 | my_struct.field2 | provenance | | | main.rs:138:9:138:9 | s | main.rs:139:29:139:29 | s | provenance | | | main.rs:138:9:138:9 | s | main.rs:139:29:139:29 | s | provenance | | | main.rs:138:13:138:21 | source(...) | main.rs:138:9:138:9 | s | provenance | | @@ -136,8 +149,8 @@ edges | main.rs:173:9:173:9 | t [tuple.1] | main.rs:175:10:175:10 | t [tuple.1] | provenance | | | main.rs:173:13:173:32 | set_tuple_element(...) [tuple.1] | main.rs:173:9:173:9 | t [tuple.1] | provenance | | | main.rs:173:13:173:32 | set_tuple_element(...) [tuple.1] | main.rs:173:9:173:9 | t [tuple.1] | provenance | | -| main.rs:173:31:173:31 | s | main.rs:173:13:173:32 | set_tuple_element(...) [tuple.1] | provenance | MaD:17 | -| main.rs:173:31:173:31 | s | main.rs:173:13:173:32 | set_tuple_element(...) [tuple.1] | provenance | MaD:17 | +| main.rs:173:31:173:31 | s | main.rs:173:13:173:32 | set_tuple_element(...) [tuple.1] | provenance | MaD:18 | +| main.rs:173:31:173:31 | s | main.rs:173:13:173:32 | set_tuple_element(...) [tuple.1] | provenance | MaD:18 | | main.rs:175:10:175:10 | t [tuple.1] | main.rs:175:10:175:12 | t.1 | provenance | | | main.rs:175:10:175:10 | t [tuple.1] | main.rs:175:10:175:12 | t.1 | provenance | | | main.rs:184:9:184:9 | s | main.rs:189:11:189:11 | s | provenance | | @@ -337,6 +350,20 @@ nodes | main.rs:109:10:109:36 | get_struct_field(...) | semmle.label | get_struct_field(...) | | main.rs:109:27:109:35 | my_struct [MyStruct.field1] | semmle.label | my_struct [MyStruct.field1] | | main.rs:109:27:109:35 | my_struct [MyStruct.field1] | semmle.label | my_struct [MyStruct.field1] | +| main.rs:126:9:126:9 | s | semmle.label | s | +| main.rs:126:9:126:9 | s | semmle.label | s | +| main.rs:126:13:126:21 | source(...) | semmle.label | source(...) | +| main.rs:126:13:126:21 | source(...) | semmle.label | source(...) | +| main.rs:127:9:127:17 | my_struct [MyStruct.field2] | semmle.label | my_struct [MyStruct.field2] | +| main.rs:127:9:127:17 | my_struct [MyStruct.field2] | semmle.label | my_struct [MyStruct.field2] | +| main.rs:127:21:127:39 | set_struct_field(...) [MyStruct.field2] | semmle.label | set_struct_field(...) [MyStruct.field2] | +| main.rs:127:21:127:39 | set_struct_field(...) [MyStruct.field2] | semmle.label | set_struct_field(...) [MyStruct.field2] | +| main.rs:127:38:127:38 | s | semmle.label | s | +| main.rs:127:38:127:38 | s | semmle.label | s | +| main.rs:129:10:129:18 | my_struct [MyStruct.field2] | semmle.label | my_struct [MyStruct.field2] | +| main.rs:129:10:129:18 | my_struct [MyStruct.field2] | semmle.label | my_struct [MyStruct.field2] | +| main.rs:129:10:129:25 | my_struct.field2 | semmle.label | my_struct.field2 | +| main.rs:129:10:129:25 | my_struct.field2 | semmle.label | my_struct.field2 | | main.rs:138:9:138:9 | s | semmle.label | s | | main.rs:138:9:138:9 | s | semmle.label | s | | main.rs:138:13:138:21 | source(...) | semmle.label | source(...) | @@ -532,6 +559,8 @@ invalidSpecComponent | main.rs:89:47:89:47 | i | main.rs:85:13:85:21 | source(...) | main.rs:89:47:89:47 | i | $@ | main.rs:85:13:85:21 | source(...) | source(...) | | main.rs:109:10:109:36 | get_struct_field(...) | main.rs:104:13:104:21 | source(...) | main.rs:109:10:109:36 | get_struct_field(...) | $@ | main.rs:104:13:104:21 | source(...) | source(...) | | main.rs:109:10:109:36 | get_struct_field(...) | main.rs:104:13:104:21 | source(...) | main.rs:109:10:109:36 | get_struct_field(...) | $@ | main.rs:104:13:104:21 | source(...) | source(...) | +| main.rs:129:10:129:25 | my_struct.field2 | main.rs:126:13:126:21 | source(...) | main.rs:129:10:129:25 | my_struct.field2 | $@ | main.rs:126:13:126:21 | source(...) | source(...) | +| main.rs:129:10:129:25 | my_struct.field2 | main.rs:126:13:126:21 | source(...) | main.rs:129:10:129:25 | my_struct.field2 | $@ | main.rs:126:13:126:21 | source(...) | source(...) | | main.rs:139:10:139:31 | get_array_element(...) | main.rs:138:13:138:21 | source(...) | main.rs:139:10:139:31 | get_array_element(...) | $@ | main.rs:138:13:138:21 | source(...) | source(...) | | main.rs:139:10:139:31 | get_array_element(...) | main.rs:138:13:138:21 | source(...) | main.rs:139:10:139:31 | get_array_element(...) | $@ | main.rs:138:13:138:21 | source(...) | source(...) | | main.rs:150:10:150:15 | arr[0] | main.rs:148:13:148:21 | source(...) | main.rs:150:10:150:15 | arr[0] | $@ | main.rs:148:13:148:21 | source(...) | source(...) | From fcdffc4e73c4e4b7a8e7437b6ef4c23c516deb5b Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 6 Mar 2025 15:11:24 +0100 Subject: [PATCH 04/11] Rust: Use type inference in path resolution test --- .../library-tests/path-resolution/main.rs | 24 +++++++++---------- .../path-resolution/path-resolution.ql | 14 +++++++---- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/rust/ql/test/library-tests/path-resolution/main.rs b/rust/ql/test/library-tests/path-resolution/main.rs index cb955da3eefb..1f047ffa23e9 100644 --- a/rust/ql/test/library-tests/path-resolution/main.rs +++ b/rust/ql/test/library-tests/path-resolution/main.rs @@ -184,11 +184,11 @@ mod m8 { > // $ MISSING: item=52 ::f(&x); // $ MISSING: item=I53 let x = MyStruct {}; // $ item=I50 - x.f(); // $ MISSING: item=I53 + x.f(); // $ item=I53 let x = MyStruct {}; // $ item=I50 - x.g(); // $ MISSING: item=I54 + x.g(); // $ item=I54 MyStruct::h(&x); // $ item=I74 - x.h(); // $ MISSING: item=I74 + x.h(); // $ item=I74 } // I55 } // I46 @@ -304,7 +304,7 @@ mod m15 { fn f(&self) { println!("m15::Trait2::f"); Self::g(self); // $ item=I80 - self.g(); // $ MISSING: item=I80 + self.g(); // $ item=I80 } } // I82 @@ -316,7 +316,7 @@ mod m15 { fn f(&self) { println!("m15::::f"); Self::g(self); // $ item=I77 - self.g(); // $ MISSING: item=I77 + self.g(); // $ item=I77 } // I76 fn g(&self) { @@ -343,7 +343,7 @@ mod m15 { as Trait2 // $ item=I82 >::f(&x); // $ MISSING: item=I78 S::g(&x); // $ item=I77 - x.g(); // $ MISSING: item=I77 + x.g(); // $ item=I77 } // I75 } @@ -359,7 +359,7 @@ mod m16 { fn h(&self) -> T { // $ item=I84 Self::g(&self); // $ item=I85 - self.g() // $ MISSING: item=I85 + self.g() // $ item=I85 } // I96 const c: T // $ item=I84 @@ -376,7 +376,7 @@ mod m16 { fn f(&self) -> T { // $ item=I87 println!("m16::Trait2::f"); Self::g(self); // $ item=I85 - self.g(); // $ MISSING: item=I85 + self.g(); // $ item=I85 Self::c // $ item=I94 } } // I89 @@ -391,7 +391,7 @@ mod m16 { fn f(&self) -> S { // $ item=I90 println!("m16::>::f"); Self::g(self); // $ item=I92 - self.g() // $ MISSING: item=I92 + self.g() // $ item=I92 } // I91 fn g(&self) -> S { // $ item=I90 @@ -429,9 +429,9 @@ mod m16 { > // $ item=I89 >::f(&x); // $ MISSING: item=I93 S::g(&x); // $ item=I92 - x.g(); // $ MISSING: item=I92 + x.g(); // $ item=I92 S::h(&x); // $ item=I96 - x.h(); // $ MISSING: item=I96 + x.h(); // $ item=I96 S::c; // $ item=I95 (x: T) { // $ item=I5 - x.f(); // $ MISSING: item=I1 + x.f(); // $ item=I1 T::f(&x); // $ item=I1 MyTrait::f(&x); // $ item=I1 } // I6 diff --git a/rust/ql/test/library-tests/path-resolution/path-resolution.ql b/rust/ql/test/library-tests/path-resolution/path-resolution.ql index f33e312af28a..fbdedf809f3e 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.ql +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.ql @@ -1,5 +1,6 @@ import rust import codeql.rust.elements.internal.PathResolution +import codeql.rust.elements.internal.TypeInference import utils.test.InlineExpectationsTest import TestUtils @@ -32,12 +33,15 @@ module ResolveTest implements TestSig { } predicate hasActualResult(Location location, string element, string tag, string value) { - exists(Path p | - not p = any(Path parent).getQualifier() and - location = p.getLocation() and - element = p.toString() and - item(resolvePath(p), value) and + exists(AstNode n | + not n = any(Path parent).getQualifier() and + location = n.getLocation() and + element = n.toString() and tag = "item" + | + item(resolvePath(n), value) + or + item(n.(MethodCallExpr).getStaticTarget(), value) ) } } From 795ba2589534be1d12d023d04b5924f69787a368 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 7 Mar 2025 08:38:37 +0100 Subject: [PATCH 05/11] Rust: Add more consistency checks --- .../codeql/rust/internal/AstConsistency.qll | 29 +++++++++++++++++++ .../CONSISTENCY/AstConsistency.expected | 3 ++ .../diagnostics/AstConsistencyCounts.expected | 3 ++ 3 files changed, 35 insertions(+) diff --git a/rust/ql/lib/codeql/rust/internal/AstConsistency.qll b/rust/ql/lib/codeql/rust/internal/AstConsistency.qll index c0fbeeb7842a..0991135267ea 100644 --- a/rust/ql/lib/codeql/rust/internal/AstConsistency.qll +++ b/rust/ql/lib/codeql/rust/internal/AstConsistency.qll @@ -89,6 +89,26 @@ query predicate multiplePathResolutions(Path p, ItemNode i) { strictcount(resolvePath(p)) > 1 } +/** Holds if `call` has multiple static call targets including `target`. */ +query predicate multipleStaticCallTargets(CallExprBase call, Callable target) { + target = call.getStaticTarget() and + strictcount(call.getStaticTarget()) > 1 +} + +/** Holds if `fe` resolves to multiple record fields including `field`. */ +query predicate multipleRecordFields(FieldExpr fe, RecordField field) { + field = fe.getRecordField() and + strictcount(fe.getRecordField()) > 1 +} + +/** Holds if `fe` resolves to multiple tuple fields including `field`. */ +query predicate multipleTupleFields(FieldExpr fe, TupleField field) { + field = fe.getTupleField() and + strictcount(fe.getTupleField()) > 1 +} + +import codeql.rust.elements.internal.TypeInference::Consistency + /** * Gets counts of abstract syntax tree inconsistencies of each type. */ @@ -117,4 +137,13 @@ int getAstInconsistencyCounts(string type) { or type = "Multiple path resolutions" and result = count(Path p | multiplePathResolutions(p, _) | p) + or + type = "Multiple static call targets" and + result = count(CallExprBase call | multipleStaticCallTargets(call, _) | call) + or + type = "Multiple record fields" and + result = count(FieldExpr fe | multipleRecordFields(fe, _) | fe) + or + type = "Multiple tuple fields" and + result = count(FieldExpr fe | multipleTupleFields(fe, _) | fe) } diff --git a/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected b/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected index 9d4e175192b4..fa8dd32840be 100644 --- a/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected +++ b/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected @@ -1,3 +1,6 @@ multiplePathResolutions | main.rs:118:9:118:9 | f | main.rs:104:5:106:5 | fn f | | main.rs:118:9:118:9 | f | main.rs:110:5:112:5 | fn f | +multipleStaticCallTargets +| main.rs:118:9:118:11 | f(...) | main.rs:104:5:106:5 | fn f | +| main.rs:118:9:118:11 | f(...) | main.rs:110:5:112:5 | fn f | diff --git a/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected b/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected index 1a79d92303dc..f2ab639c82fb 100644 --- a/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected +++ b/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected @@ -4,5 +4,8 @@ | Multiple path resolutions | 0 | | Multiple positions | 0 | | Multiple primary QL classes | 0 | +| Multiple record fields | 0 | +| Multiple static call targets | 0 | | Multiple toStrings | 0 | +| Multiple tuple fields | 0 | | No location | 0 | From 2394f2fab80350d365ae0b214830d47a6b18be19 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 6 Mar 2025 20:41:01 +0100 Subject: [PATCH 06/11] Rust: Fix bug in path resolution library --- .../rust/elements/internal/PathResolution.qll | 21 ++++++++++--------- .../CONSISTENCY/AstConsistency.expected | 5 ----- .../type-inference/type-inference.expected | 5 ----- 3 files changed, 11 insertions(+), 20 deletions(-) delete mode 100644 rust/ql/test/library-tests/type-inference/CONSISTENCY/AstConsistency.expected diff --git a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll index ced7395c4500..db74205424e7 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll @@ -284,7 +284,7 @@ abstract class ImplOrTraitItemNode extends ItemNode { /** Gets a `Self` path that refers to this item. */ Path getASelfPath() { isUnqualifiedSelfPath(result) and - this = unqualifiedPathLookup(result) + this = unqualifiedPathLookup(result, _) } /** Gets an associated item belonging to this trait or `impl` block. */ @@ -680,8 +680,8 @@ private ItemNode getASuccessor(ItemNode pred, string name, Namespace ns) { } pragma[nomagic] -private ItemNode unqualifiedPathLookup(RelevantPath path) { - exists(ItemNode encl, Namespace ns, string name | +private ItemNode unqualifiedPathLookup(RelevantPath path, Namespace ns) { + exists(ItemNode encl, string name | unqualifiedPathLookup(path, name, ns, encl) and result = getASuccessor(encl, name, ns) ) @@ -691,9 +691,9 @@ pragma[nomagic] private predicate isUnqualifiedSelfPath(RelevantPath path) { path.isUnqualified("Self") } pragma[nomagic] -private ItemNode resolvePath0(RelevantPath path) { +private ItemNode resolvePath0(RelevantPath path, Namespace ns) { exists(ItemNode res | - res = unqualifiedPathLookup(path) and + res = unqualifiedPathLookup(path, ns) and if not any(RelevantPath parent).getQualifier() = path and isUnqualifiedSelfPath(path) and @@ -704,10 +704,12 @@ private ItemNode resolvePath0(RelevantPath path) { or exists(ItemNode q, string name | q = resolvePathQualifier(path, name) and - result = q.getASuccessor(name) + result = q.getASuccessor(name) and + ns = result.getNamespace() ) or - result = resolveUseTreeListItem(_, _, path) + result = resolveUseTreeListItem(_, _, path) and + ns = result.getNamespace() } /** Holds if path `p` must be looked up in namespace `n`. */ @@ -743,9 +745,8 @@ private predicate pathUsesNamespace(Path p, Namespace n) { /** Gets the item that `path` resolves to, if any. */ cached ItemNode resolvePath(RelevantPath path) { - result = resolvePath0(path) and - ( - pathUsesNamespace(path, result.getNamespace()) + exists(Namespace ns | result = resolvePath0(path, ns) | + pathUsesNamespace(path, ns) or not pathUsesNamespace(path, _) ) diff --git a/rust/ql/test/library-tests/type-inference/CONSISTENCY/AstConsistency.expected b/rust/ql/test/library-tests/type-inference/CONSISTENCY/AstConsistency.expected deleted file mode 100644 index 3503ddec2ca0..000000000000 --- a/rust/ql/test/library-tests/type-inference/CONSISTENCY/AstConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -multiplePathResolutions -| main.rs:385:34:385:34 | S | main.rs:384:19:384:19 | S | -| main.rs:385:34:385:34 | S | main.rs:411:5:412:13 | struct S | -| main.rs:387:39:387:39 | S | main.rs:384:19:384:19 | S | -| main.rs:387:39:387:39 | S | main.rs:411:5:412:13 | struct S | diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected index b991d4bd96b7..fb8161d7687f 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -424,19 +424,14 @@ resolveType | main.rs:385:16:385:24 | SelfParam | &T | main.rs:384:5:390:5 | trait MyTrait | | main.rs:385:16:385:24 | SelfParam | &T.S | main.rs:384:19:384:19 | S | | main.rs:385:27:385:31 | value | | main.rs:384:19:384:19 | S | -| main.rs:385:27:385:31 | value | | main.rs:411:5:412:13 | struct S | | main.rs:387:21:387:29 | SelfParam | | file://:0:0:0:0 | & | | main.rs:387:21:387:29 | SelfParam | &T | main.rs:384:5:390:5 | trait MyTrait | | main.rs:387:21:387:29 | SelfParam | &T.S | main.rs:384:19:384:19 | S | -| main.rs:387:21:387:29 | SelfParam | &T.S | main.rs:411:5:412:13 | struct S | | main.rs:387:32:387:36 | value | | main.rs:384:19:384:19 | S | -| main.rs:387:32:387:36 | value | | main.rs:411:5:412:13 | struct S | | main.rs:388:13:388:16 | self | | file://:0:0:0:0 | & | | main.rs:388:13:388:16 | self | &T | main.rs:384:5:390:5 | trait MyTrait | | main.rs:388:13:388:16 | self | &T.S | main.rs:384:19:384:19 | S | -| main.rs:388:13:388:16 | self | &T.S | main.rs:411:5:412:13 | struct S | | main.rs:388:22:388:26 | value | | main.rs:384:19:384:19 | S | -| main.rs:388:22:388:26 | value | | main.rs:411:5:412:13 | struct S | | main.rs:393:16:393:24 | SelfParam | | file://:0:0:0:0 | & | | main.rs:393:16:393:24 | SelfParam | &T | main.rs:378:5:382:5 | enum MyOption | | main.rs:393:16:393:24 | SelfParam | &T.T | main.rs:392:10:392:10 | T | From 78280af5702808ed535499908a961353db9b227e Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 13 Mar 2025 13:34:43 +0100 Subject: [PATCH 07/11] Rust: Use 'infer' instead of 'resolve' in type inference library --- .../rust/elements/internal/TypeInference.qll | 104 +++++++++--------- .../lib/codeql/rust/internal/CachedStages.qll | 2 +- .../type-inference/type-inference.expected | 2 +- .../type-inference/type-inference.ql | 4 +- .../typeinference/internal/TypeInference.qll | 32 +++--- 5 files changed, 72 insertions(+), 72 deletions(-) diff --git a/rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll b/rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll index 025b3ac5d487..9ebb591c7728 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll @@ -119,7 +119,7 @@ private TypeMention getTypeAnnotation(AstNode n) { /** Gets the type of `n`, which has an explicit type annotation. */ pragma[nomagic] -private Type resolveAnnotatedType(AstNode n, TypePath path) { +private Type inferAnnotatedType(AstNode n, TypePath path) { result = getTypeAnnotation(n).resolveTypeAt(path) } @@ -159,8 +159,8 @@ private predicate typeSymmetry(AstNode n1, TypePath path1, AstNode n2, TypePath } pragma[nomagic] -private Type resolveTypeSymmetry(AstNode n, TypePath path) { - exists(AstNode n2, TypePath path2 | result = resolveType(n2, path2) | +private Type inferTypeSymmetry(AstNode n, TypePath path) { + exists(AstNode n2, TypePath path2 | result = inferType(n2, path2) | typeSymmetry(n, path, n2, path2) or typeSymmetry(n2, path2, n, path) @@ -193,12 +193,12 @@ private Type getRefAdjustImplicitSelfType(SelfParam self, TypePath suffix, Type } pragma[nomagic] -private Type resolveImplSelfType(Impl i, TypePath path) { +private Type inferImplSelfType(Impl i, TypePath path) { result = i.getSelfTy().(TypeReprMention).resolveTypeAt(path) } pragma[nomagic] -private Type resolveTraitSelfType(Trait t, TypePath path) { +private Type inferTraitSelfType(Trait t, TypePath path) { result = TTrait(t) and path.isEmpty() or @@ -208,15 +208,15 @@ private Type resolveTraitSelfType(Trait t, TypePath path) { /** Gets the type at `path` of the implicitly typed `self` parameter. */ pragma[nomagic] -private Type resolveImplicitSelfType(SelfParam self, TypePath path) { +private Type inferImplicitSelfType(SelfParam self, TypePath path) { exists(ImplOrTraitItemNode i, Function f, TypePath suffix, Type t | f = i.getAnAssocItem() and self = f.getParamList().getSelfParam() and result = getRefAdjustImplicitSelfType(self, suffix, t, path) | - t = resolveImplSelfType(i, suffix) + t = inferImplSelfType(i, suffix) or - t = resolveTraitSelfType(i, suffix) + t = inferTraitSelfType(i, suffix) ) } @@ -327,8 +327,8 @@ private module RecordExprMatchingInput implements MatchingInputSig { apos.isRecordPos() } - Type getResolvedType(AccessPosition apos, TypePath path) { - result = resolveType(this.getNodeAt(apos), path) + Type getInferredType(AccessPosition apos, TypePath path) { + result = inferType(this.getNodeAt(apos), path) } Declaration getTarget() { result = resolvePath(this.getPath()) } @@ -346,15 +346,15 @@ private module RecordExprMatching = Matching; * a field expression of a record expression. */ pragma[nomagic] -private Type resolveRecordExprType(AstNode n, TypePath path) { +private Type inferRecordExprType(AstNode n, TypePath path) { exists(RecordExprMatchingInput::Access a, RecordExprMatchingInput::AccessPosition apos | n = a.getNodeAt(apos) and - result = RecordExprMatching::resolveAccessType(a, apos, path) + result = RecordExprMatching::inferAccessType(a, apos, path) ) } pragma[nomagic] -private Type resolvePathExprType(PathExpr pe, TypePath path) { +private Type inferPathExprType(PathExpr pe, TypePath path) { // nullary struct/variant constructors not exists(CallExpr ce | pe = ce.getFunction()) and path.isEmpty() and @@ -466,7 +466,7 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { } pragma[nomagic] - private Type resolveAnnotatedTypeInclSelf(AstNode n, TypePath path) { + private Type inferAnnotatedTypeInclSelf(AstNode n, TypePath path) { result = getTypeAnnotation(n).resolveTypeAtInclSelf(path) } @@ -477,7 +477,7 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { exists(Param p, int i, boolean inMethod | paramPos(this.getParamList(), p, i, inMethod) and dpos = TPositionalDeclarationPosition(i, inMethod) and - result = resolveAnnotatedTypeInclSelf(p.getPat(), path) + result = inferAnnotatedTypeInclSelf(p.getPat(), path) ) or exists(SelfParam self | @@ -485,10 +485,10 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { dpos.isSelf() | // `self` parameter with type annotation - result = resolveAnnotatedTypeInclSelf(self, path) + result = inferAnnotatedTypeInclSelf(self, path) or // `self` parameter without type annotation - result = resolveImplicitSelfType(self, path) + result = inferImplicitSelfType(self, path) or // `self` parameter without type annotation should also have the special `Self` type result = getRefAdjustImplicitSelfType(self, TypePath::nil(), TSelfTypeParameter(), path) @@ -559,8 +559,8 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { apos = TReturnAccessPosition() } - Type getResolvedType(AccessPosition apos, TypePath path) { - result = resolveType(this.getNodeAt(apos), path) + Type getInferredType(AccessPosition apos, TypePath path) { + result = inferType(this.getNodeAt(apos), path) } Declaration getTarget() { @@ -644,9 +644,9 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { } pragma[nomagic] - additional Type resolveReceiverType(AstNode n) { + additional Type inferReceiverType(AstNode n) { exists(Access a, AccessPosition apos | - result = resolveType(n) and + result = inferType(n) and n = a.getNodeAt(apos) and apos.isSelf() ) @@ -660,17 +660,17 @@ private module CallExprBaseMatching = Matching; * argument/receiver of a call. */ pragma[nomagic] -private Type resolveCallExprBaseType(AstNode n, TypePath path) { +private Type inferCallExprBaseType(AstNode n, TypePath path) { exists( CallExprBaseMatchingInput::Access a, CallExprBaseMatchingInput::AccessPosition apos, TypePath path0 | n = a.getNodeAt(apos) and - result = CallExprBaseMatching::resolveAccessType(a, apos, path0) + result = CallExprBaseMatching::inferAccessType(a, apos, path0) | if apos.isSelf() then - exists(Type receiverType | receiverType = CallExprBaseMatchingInput::resolveReceiverType(n) | + exists(Type receiverType | receiverType = CallExprBaseMatchingInput::inferReceiverType(n) | if receiverType = TRefType() then path = path0 and @@ -758,8 +758,8 @@ private module FieldExprMatchingInput implements MatchingInputSig { apos.isField() } - Type getResolvedType(AccessPosition apos, TypePath path) { - result = resolveType(this.getNodeAt(apos), path) + Type getInferredType(AccessPosition apos, TypePath path) { + result = inferType(this.getNodeAt(apos), path) } Declaration getTarget() { @@ -795,9 +795,9 @@ private module FieldExprMatchingInput implements MatchingInputSig { } pragma[nomagic] - additional Type resolveReceiverType(AstNode n) { + additional Type inferReceiverType(AstNode n) { exists(Access a, AccessPosition apos | - result = resolveType(n) and + result = inferType(n) and n = a.getNodeAt(apos) and apos.isSelf() ) @@ -811,16 +811,16 @@ private module FieldExprMatching = Matching; * the receiver of field expression call. */ pragma[nomagic] -private Type resolveFieldExprType(AstNode n, TypePath path) { +private Type inferFieldExprType(AstNode n, TypePath path) { exists( FieldExprMatchingInput::Access a, FieldExprMatchingInput::AccessPosition apos, TypePath path0 | n = a.getNodeAt(apos) and - result = FieldExprMatching::resolveAccessType(a, apos, path0) + result = FieldExprMatching::inferAccessType(a, apos, path0) | if apos.isSelf() then - exists(Type receiverType | receiverType = FieldExprMatchingInput::resolveReceiverType(n) | + exists(Type receiverType | receiverType = FieldExprMatchingInput::inferReceiverType(n) | if receiverType = TRefType() then // adjust for implicit deref @@ -838,14 +838,14 @@ private Type resolveFieldExprType(AstNode n, TypePath path) { * `& x` or an expression `x` inside a reference expression `& x`. */ pragma[nomagic] -private Type resolveRefExprType(Expr e, TypePath path) { +private Type inferRefExprType(Expr e, TypePath path) { exists(RefExpr re | e = re and path.isEmpty() and result = TRefType() or e = re and - exists(TypePath exprPath | result = resolveType(re.getExpr(), exprPath) | + exists(TypePath exprPath | result = inferType(re.getExpr(), exprPath) | if exprPath.startsWith(TRefTypeParameter(), _) then // `&x` simply means `x` when `x` already has reference type @@ -858,9 +858,9 @@ private Type resolveRefExprType(Expr e, TypePath path) { or e = re.getExpr() and exists(TypePath exprPath, TypePath refPath, Type exprType | - result = resolveType(re, exprPath) and + result = inferType(re, exprPath) and exprPath = TypePath::cons(TRefTypeParameter(), refPath) and - exprType = resolveType(e) + exprType = inferType(e) | if exprType = TRefType() then @@ -878,11 +878,11 @@ private module Cached { pragma[inline] private Type getLookupType(AstNode n) { exists(Type t | - t = resolveType(n) and + t = inferType(n) and if t = TRefType() then // for reference types, lookup members in the type being referenced - result = resolveType(n, TypePath::singleton(TRefTypeParameter())) + result = inferType(n, TypePath::singleton(TRefTypeParameter())) else result = t ) } @@ -894,7 +894,7 @@ private module Cached { } /** - * Gets a method that the method call `mce` resolves to, if any. + * Gets a method that the method call `mce` infers to, if any. */ cached Function resolveMethodCallExpr(MethodCallExpr mce) { @@ -908,7 +908,7 @@ private module Cached { } /** - * Gets the record field that the field expression `fe` resolves to, if any. + * Gets the record field that the field expression `fe` infers to, if any. */ cached RecordField resolveRecordFieldExpr(FieldExpr fe) { @@ -924,7 +924,7 @@ private module Cached { } /** - * Gets the tuple field that the field expression `fe` resolves to, if any. + * Gets the tuple field that the field expression `fe` infers to, if any. */ cached TupleField resolveTupleFieldExpr(FieldExpr fe) { @@ -932,7 +932,7 @@ private module Cached { } /** - * Gets a type at `path` that `n` resolves to, if any. + * Gets a type at `path` that `n` infers to, if any. * * The type inference implementation works by computing all possible types, so * the result is not necessarily unique. For example, in @@ -971,29 +971,29 @@ private module Cached { * 5. `x.bar()` has type `&MyTrait` (via 2 and 4). */ cached - Type resolveType(AstNode n, TypePath path) { + Type inferType(AstNode n, TypePath path) { Stages::TypeInference::backref() and - result = resolveAnnotatedType(n, path) + result = inferAnnotatedType(n, path) or - result = resolveTypeSymmetry(n, path) + result = inferTypeSymmetry(n, path) or - result = resolveImplicitSelfType(n, path) + result = inferImplicitSelfType(n, path) or - result = resolveRecordExprType(n, path) + result = inferRecordExprType(n, path) or - result = resolvePathExprType(n, path) + result = inferPathExprType(n, path) or - result = resolveCallExprBaseType(n, path) + result = inferCallExprBaseType(n, path) or - result = resolveFieldExprType(n, path) + result = inferFieldExprType(n, path) or - result = resolveRefExprType(n, path) + result = inferRefExprType(n, path) } } import Cached /** - * Gets a type that `n` resolves to, if any. + * Gets a type that `n` infers to, if any. */ -Type resolveType(AstNode n) { result = resolveType(n, TypePath::nil()) } +Type inferType(AstNode n) { result = inferType(n, TypePath::nil()) } diff --git a/rust/ql/lib/codeql/rust/internal/CachedStages.qll b/rust/ql/lib/codeql/rust/internal/CachedStages.qll index c42d6b95c94d..f4b2ba83aee9 100644 --- a/rust/ql/lib/codeql/rust/internal/CachedStages.qll +++ b/rust/ql/lib/codeql/rust/internal/CachedStages.qll @@ -121,7 +121,7 @@ module Stages { or exists(Type t) or - exists(resolveType(_)) + exists(inferType(_)) } } diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected index fb8161d7687f..4b2cb92d0480 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -1,4 +1,4 @@ -resolveType +inferType | main.rs:5:19:5:22 | SelfParam | | main.rs:2:5:2:21 | struct Foo | | main.rs:5:33:7:9 | { ... } | | main.rs:2:5:2:21 | struct Foo | | main.rs:6:13:6:16 | self | | main.rs:2:5:2:21 | struct Foo | diff --git a/rust/ql/test/library-tests/type-inference/type-inference.ql b/rust/ql/test/library-tests/type-inference/type-inference.ql index 0e31dc01b49e..797d0142bfc2 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.ql +++ b/rust/ql/test/library-tests/type-inference/type-inference.ql @@ -3,8 +3,8 @@ import codeql.rust.elements.internal.TypeInference as TypeInference import TypeInference import utils.test.InlineExpectationsTest -query predicate resolveType(AstNode n, TypePath path, Type t) { - t = TypeInference::resolveType(n, path) +query predicate inferType(AstNode n, TypePath path, Type t) { + t = TypeInference::inferType(n, path) } query predicate resolveMethodCallExpr(MethodCallExpr mce, Function f) { diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index c9f1bc348893..f5816962f3a1 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -465,12 +465,12 @@ module Make1 Input1> { Type getExplicitTypeArgument(TypeArgumentPosition tapos, TypePath path); /** - * Gets the resolved type at `path` for the position `apos` of this access. + * Gets the inferred type at `path` for the position `apos` of this access. * - * For example, if this access is the method call `M(42)`, then the resolved + * For example, if this access is the method call `M(42)`, then the inferred * type at argument position `0` is `int`. */ - Type getResolvedType(AccessPosition apos, TypePath path); + Type getInferredType(AccessPosition apos, TypePath path); /** Gets the declaration that this access targets. */ Declaration getTarget(); @@ -482,7 +482,7 @@ module Make1 Input1> { predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos); /** - * Holds if matching a resolved type `t` at `path` inside an access at `apos` + * Holds if matching an inferred type `t` at `path` inside an access at `apos` * against the declaration `target` means that the type should be adjusted to * `tAdj` at `pathAdj`. * @@ -493,7 +493,7 @@ module Make1 Input1> { * M(42); * ``` * - * the resolved type of `42` is `int`, but it should be adjusted to `int?` + * the inferred type of `42` is `int`, but it should be adjusted to `int?` * when matching against `M`. */ bindingset[apos, target, path, t] @@ -520,7 +520,7 @@ module Make1 Input1> { ) { target = a.getTarget() and exists(TypePath path0, Type t0 | - t0 = a.getResolvedType(apos, path0) and + t0 = a.getInferredType(apos, path0) and adjustAccessType(apos, target, path0, t0, path, t) ) } @@ -562,16 +562,16 @@ module Make1 Input1> { } pragma[nomagic] - private Type resolveRootType(Access a, AccessPosition apos) { + private Type inferRootType(Access a, AccessPosition apos) { relevantAccess(a, apos) and - result = a.getResolvedType(apos, TypePath::nil()) + result = a.getInferredType(apos, TypePath::nil()) } pragma[nomagic] - private Type resolveTypeAt(Access a, AccessPosition apos, TypeParameter tp, TypePath suffix) { + private Type inferTypeAt(Access a, AccessPosition apos, TypeParameter tp, TypePath suffix) { relevantAccess(a, apos) and exists(TypePath path0 | - result = a.getResolvedType(apos, path0) and + result = a.getInferredType(apos, path0) and path0.startsWith(tp, suffix) ) } @@ -608,12 +608,12 @@ module Make1 Input1> { predicate hasBaseTypeMention( Access a, AccessPosition apos, TypeMention baseMention, TypePath path, Type t ) { - exists(Type sub | sub = resolveRootType(a, apos) | + exists(Type sub | sub = inferRootType(a, apos) | baseTypeMentionHasNonTypeParameterAt(sub, baseMention, path, t) or exists(TypePath prefix, TypePath suffix, TypeParameter i | baseTypeMentionHasTypeParameterAt(sub, baseMention, prefix, i) and - t = resolveTypeAt(a, apos, i, suffix) and + t = inferTypeAt(a, apos, i, suffix) and path = prefix.append(suffix) ) ) @@ -709,7 +709,7 @@ module Make1 Input1> { } /** - * Gets the resolved type of `a` at `path` for position `apos`. + * Gets the inferred type of `a` at `path` for position `apos`. * * For example, in * @@ -728,7 +728,7 @@ module Make1 Input1> { * // ^^^^^^^^^^^^^^^^^^^^^^^ `a` * ``` * - * we resolve the following types for the return position: + * we infer the following types for the return position: * * `path` | `t` * ----------- | ------- @@ -737,7 +737,7 @@ module Make1 Input1> { * `"0.0.0"` | ``C`1`` * `"0.0.0.1"` | `int` * - * We also resolve the following types for the receiver position: + * We also infer the following types for the receiver position: * * `path` | `t` * ----------- | ------- @@ -747,7 +747,7 @@ module Make1 Input1> { * `"0.0.0.1"` | `int` */ pragma[nomagic] - Type resolveAccessType(Access a, AccessPosition apos, TypePath path) { + Type inferAccessType(Access a, AccessPosition apos, TypePath path) { exists(DeclarationPosition dpos | accessDeclarationPositionMatch(apos, dpos) | exists(Declaration target, TypePath prefix, TypeParameter tp, TypePath suffix | tp = target.getDeclaredType(pragma[only_bind_into](dpos), prefix) and From af91152f5cb0ac9585b517e28324d96d11d7b8df Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 13 Mar 2025 14:51:23 +0100 Subject: [PATCH 08/11] Address review comments --- .../PathResolutionConsistency.ql | 8 +++ .../TypeInferenceConsistency.ql | 8 +++ .../elements/internal/CallExprBaseImpl.qll | 2 +- .../elements/internal/GenericArgListImpl.qll | 4 +- .../rust/elements/internal/PathResolution.qll | 58 ++++++++++-------- .../codeql/rust/elements/internal/Type.qll | 11 +++- .../rust/elements/internal/TypeInference.qll | 48 ++++++++------- .../rust/elements/internal/TypeMention.qll | 6 +- .../codeql/rust/internal/AstConsistency.qll | 48 +-------------- .../internal/PathResolutionConsistency.qll | 53 ++++++++++++++++ .../internal/TypeInferenceConsistency.qll | 5 ++ rust/ql/src/queries/summary/Stats.qll | 9 +++ rust/ql/src/queries/summary/SummaryStats.ql | 2 + ...ted => PathResolutionConsistency.expected} | 0 .../test/library-tests/type-inference/main.rs | 2 +- .../type-inference/type-inference.expected | 2 +- .../typeinference/internal/TypeInference.qll | 61 ++++++++----------- 17 files changed, 185 insertions(+), 142 deletions(-) create mode 100644 rust/ql/consistency-queries/PathResolutionConsistency.ql create mode 100644 rust/ql/consistency-queries/TypeInferenceConsistency.ql create mode 100644 rust/ql/lib/codeql/rust/internal/PathResolutionConsistency.qll create mode 100644 rust/ql/lib/codeql/rust/internal/TypeInferenceConsistency.qll rename rust/ql/test/library-tests/path-resolution/CONSISTENCY/{AstConsistency.expected => PathResolutionConsistency.expected} (100%) diff --git a/rust/ql/consistency-queries/PathResolutionConsistency.ql b/rust/ql/consistency-queries/PathResolutionConsistency.ql new file mode 100644 index 000000000000..368b2c1e559a --- /dev/null +++ b/rust/ql/consistency-queries/PathResolutionConsistency.ql @@ -0,0 +1,8 @@ +/** + * @name Path resolution inconsistencies + * @description Lists the path resolution inconsistencies in the database. This query is intended for internal use. + * @kind table + * @id rust/diagnostics/path-resolution-consistency + */ + +import codeql.rust.internal.PathResolutionConsistency diff --git a/rust/ql/consistency-queries/TypeInferenceConsistency.ql b/rust/ql/consistency-queries/TypeInferenceConsistency.ql new file mode 100644 index 000000000000..2ca9e55e964e --- /dev/null +++ b/rust/ql/consistency-queries/TypeInferenceConsistency.ql @@ -0,0 +1,8 @@ +/** + * @name Type inference inconsistencies + * @description Lists the type inference inconsistencies in the database. This query is intended for internal use. + * @kind table + * @id rust/diagnostics/type-inference-consistency + */ + +import codeql.rust.internal.TypeInferenceConsistency diff --git a/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll index a535ced76f8b..8b3700f08693 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll @@ -27,6 +27,6 @@ module Impl { */ class CallExprBase extends Generated::CallExprBase { /** Gets the static target of this call, if any. */ - Callable getStaticTarget() { none() } + Callable getStaticTarget() { none() } // overridden by subclasses } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/GenericArgListImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/GenericArgListImpl.qll index d9856e157308..affe01e77478 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/GenericArgListImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/GenericArgListImpl.qll @@ -26,7 +26,7 @@ module Impl { override string toAbbreviatedString() { result = "<...>" } /** Gets the `i`th type argument of this list. */ - TypeRepr getTypeArgument(int i) { + TypeRepr getTypeArg(int i) { result = rank[i + 1](TypeRepr res, int j | res = this.getGenericArg(j).(TypeArg).getTypeRepr() @@ -36,6 +36,6 @@ module Impl { } /** Gets a type argument of this list. */ - TypeRepr getATypeArgument() { result = this.getTypeArgument(_) } + TypeRepr getATypeArg() { result = this.getTypeArg(_) } } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll index db74205424e7..2bc95ff613cd 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll @@ -317,49 +317,53 @@ class ImplItemNode extends ImplOrTraitItemNode instanceof Impl { } /** - * Holds if this `impl` block is constrained. Examples: + * 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 { ... } // unconstrained + * impl Foo { ... } // fully parametric * - * impl Foo { ... } // unconstrained + * impl Foo { ... } // fully parametric * - * impl Foo { ... } // constrained + * impl Foo { ... } // not fully parametric * - * impl Foo> { ... } // constrained + * impl Foo> { ... } // not fully parametric * - * impl Foo { ... } // constrained + * impl Foo { ... } // not fully parametric * - * impl Foo where T: Trait { ... } // constrained + * impl Foo where T: Trait { ... } // not fully parametric * ``` */ pragma[nomagic] - predicate isConstrained() { + predicate isNotFullyParametric() { exists(TypeRepr arg | arg = this.getASelfTyArg() | not exists(this.getASelfTyTypeParamArg(arg)) or - this.getASelfTyTypeParamArg(arg).isConstrained() + this.getASelfTyTypeParamArg(arg).hasTraitBound() ) } /** - * Holds if this `impl` block is unconstrained. Examples: + * Holds if this `impl` block is fully parametric. Examples: * * ```rust - * impl Foo { ... } // unconstrained + * impl Foo { ... } // fully parametric * - * impl Foo { ... } // unconstrained + * impl Foo { ... } // fully parametric * - * impl Foo { ... } // constrained + * impl Foo { ... } // not fully parametric * - * impl Foo> { ... } // constrained + * impl Foo> { ... } // not fully parametric * - * impl Foo { ... } // constrained + * impl Foo { ... } // not fully parametric * - * impl Foo where T: Trait { ... } // constrained + * impl Foo where T: Trait { ... } // not fully parametric * ``` */ - predicate isUnconstrained() { not this.isConstrained() } + predicate isFullyParametric() { not this.isNotFullyParametric() } override AssocItemNode getAnAssocItem() { result = super.getAssocItemList().getAnAssocItem() } @@ -481,18 +485,18 @@ private class TypeParamItemNode extends ItemNode instanceof TypeParam { ItemNode resolveABound() { result = resolvePath(this.getABoundPath()) } /** - * Holds if this type parameter is constrained. Examples: + * Holds if this type parameter has a trait bound. Examples: * * ```rust - * impl Foo { ... } // unconstrained + * impl Foo { ... } // has no trait bound * - * impl Foo { ... } // constrained + * impl Foo { ... } // has trait bound * - * impl Foo where T: Trait { ... } // constrained + * impl Foo where T: Trait { ... } // has trait bound * ``` */ pragma[nomagic] - predicate isConstrained() { + predicate hasTraitBound() { exists(this.getABoundPath()) or exists(ItemNode declaringItem, WherePred wp | @@ -503,18 +507,18 @@ private class TypeParamItemNode extends ItemNode instanceof TypeParam { } /** - * Holds if this type parameter is unconstrained. Examples: + * Holds if this type parameter has no trait bound. Examples: * * ```rust - * impl Foo { ... } // unconstrained + * impl Foo { ... } // has no trait bound * - * impl Foo { ... } // constrained + * impl Foo { ... } // has trait bound * - * impl Foo where T: Trait { ... } // constrained + * impl Foo where T: Trait { ... } // has trait bound * ``` */ pragma[nomagic] - predicate isUnconstrained() { not this.isConstrained() } + predicate hasNoTraitBound() { not this.hasTraitBound() } override string getName() { result = TypeParam.super.getName().getText() } diff --git a/rust/ql/lib/codeql/rust/elements/internal/Type.qll b/rust/ql/lib/codeql/rust/elements/internal/Type.qll index 4b1aecdb7708..6ede960b0385 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/Type.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/Type.qll @@ -18,7 +18,12 @@ newtype TType = TRefTypeParameter() or TSelfTypeParameter() -/** A type without type arguments. */ +/** + * A type without type arguments. + * + * Note that this type includes things that, strictly speaking, are not Rust + * types, such as traits and implementation blocks. + */ abstract class Type extends TType { /** Gets the method `name` belonging to this type, if any. */ pragma[nomagic] @@ -72,13 +77,13 @@ abstract private class StructOrEnumType extends Type { exists(ImplOrTraitItemNode impl | result = impl.getAnAssocItem() | impl instanceof Trait or - impl.(ImplItemNode).isUnconstrained() + impl.(ImplItemNode).isFullyParametric() ) } final override ImplMention getABaseTypeMention() { this.asItemNode() = result.resolveSelfTy() and - result.isUnconstrained() + result.isFullyParametric() } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll b/rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll index 9ebb591c7728..92457214bf08 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll @@ -22,7 +22,7 @@ private module Input1 implements InputSig1 { // method type parameters are matched by position instead of by type // parameter entity, to avoid extra recursion through method call resolution TMethodTypeArgumentPosition(int pos) { - exists(any(MethodCallExpr mce).getGenericArgList().getTypeArgument(pos)) + exists(any(MethodCallExpr mce).getGenericArgList().getTypeArg(pos)) } or TTypeParamTypeArgumentPosition(TypeParam tp) @@ -124,11 +124,13 @@ private Type inferAnnotatedType(AstNode n, TypePath path) { } /** - * Holds if the type of `n1` at `path1` is the same as the type of `n2` at `path2`. + * Holds if the type of `n1` at `path1` is the same as the type of `n2` at + * `path2` and type information should propagate in both directions through the + * type equality. */ bindingset[path1] bindingset[path2] -private predicate typeSymmetry(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { +private predicate typeEquality(AstNode n1, TypePath path1, AstNode n2, TypePath path2) { exists(Variable v | path1 = path2 and n1 = v.getAnAccess() @@ -159,11 +161,11 @@ private predicate typeSymmetry(AstNode n1, TypePath path1, AstNode n2, TypePath } pragma[nomagic] -private Type inferTypeSymmetry(AstNode n, TypePath path) { +private Type inferTypeEquality(AstNode n, TypePath path) { exists(AstNode n2, TypePath path2 | result = inferType(n2, path2) | - typeSymmetry(n, path, n2, path2) + typeEquality(n, path, n2, path2) or - typeSymmetry(n2, path2, n, path) + typeEquality(n2, path2, n, path) ) } @@ -222,7 +224,7 @@ private Type inferImplicitSelfType(SelfParam self, TypePath path) { private TypeMention getExplicitTypeArgMention(Path path, TypeParam tp) { exists(int i | - result = path.getPart().getGenericArgList().getTypeArgument(pragma[only_bind_into](i)) and + result = path.getPart().getGenericArgList().getTypeArg(pragma[only_bind_into](i)) and tp = resolvePath(path).getTypeParam(pragma[only_bind_into](i)) ) or @@ -316,7 +318,7 @@ private module RecordExprMatchingInput implements MatchingInputSig { class AccessPosition = DeclarationPosition; class Access extends RecordExpr { - Type getExplicitTypeArgument(TypeArgumentPosition apos, TypePath path) { + Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { result = getExplicitTypeArgMention(this.getPath(), apos.asTypeParam()).resolveTypeAt(path) } @@ -535,10 +537,10 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { class Access extends CallExprBase { private TypeReprMention getMethodTypeArg(int i) { - result = this.(MethodCallExpr).getGenericArgList().getTypeArgument(i) + result = this.(MethodCallExpr).getGenericArgList().getTypeArg(i) } - Type getExplicitTypeArgument(TypeArgumentPosition apos, TypePath path) { + Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { exists(TypeMention arg | result = arg.resolveTypeAt(path) | arg = getExplicitTypeArgMention(CallExprImpl::getFunctionPath(this), apos.asTypeParam()) or @@ -616,7 +618,7 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { pathAdj = TypePath::singleton(TRefTypeParameter()) and tAdj = t else - if path.startsWith(TRefTypeParameter(), _) + if path.isCons(TRefTypeParameter(), _) then pathAdj = path and tAdj = t @@ -628,10 +630,10 @@ private module CallExprBaseMatchingInput implements MatchingInputSig { ) else ( // adjust for implicit deref - path.startsWith(TRefTypeParameter(), pathAdj) and + path.isCons(TRefTypeParameter(), pathAdj) and tAdj = t or - not path.startsWith(TRefTypeParameter(), _) and + not path.isCons(TRefTypeParameter(), _) and not (t = TRefType() and path.isEmpty()) and pathAdj = path and tAdj = t @@ -674,19 +676,19 @@ private Type inferCallExprBaseType(AstNode n, TypePath path) { if receiverType = TRefType() then path = path0 and - path0.startsWith(TRefTypeParameter(), _) + path0.isCons(TRefTypeParameter(), _) or // adjust for implicit deref - not path0.startsWith(TRefTypeParameter(), _) and + not path0.isCons(TRefTypeParameter(), _) and not (path0.isEmpty() and result = TRefType()) and path = TypePath::cons(TRefTypeParameter(), path0) else ( - not path0.startsWith(TRefTypeParameter(), _) and + not path0.isCons(TRefTypeParameter(), _) and not (path0.isEmpty() and result = TRefType()) and path = path0 or // adjust for implicit borrow - path0.startsWith(TRefTypeParameter(), path) + path0.isCons(TRefTypeParameter(), path) ) ) else path = path0 @@ -748,7 +750,7 @@ private module FieldExprMatchingInput implements MatchingInputSig { class AccessPosition = DeclarationPosition; class Access extends FieldExpr { - Type getExplicitTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } + Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } AstNode getNodeAt(AccessPosition apos) { result = this.getExpr() and @@ -781,10 +783,10 @@ private module FieldExprMatchingInput implements MatchingInputSig { if apos.isSelf() then // adjust for implicit deref - path.startsWith(TRefTypeParameter(), pathAdj) and + path.isCons(TRefTypeParameter(), pathAdj) and tAdj = t or - not path.startsWith(TRefTypeParameter(), _) and + not path.isCons(TRefTypeParameter(), _) and not (t = TRefType() and path.isEmpty()) and pathAdj = path and tAdj = t @@ -824,7 +826,7 @@ private Type inferFieldExprType(AstNode n, TypePath path) { if receiverType = TRefType() then // adjust for implicit deref - not path0.startsWith(TRefTypeParameter(), _) and + not path0.isCons(TRefTypeParameter(), _) and not (path0.isEmpty() and result = TRefType()) and path = TypePath::cons(TRefTypeParameter(), path0) else path = path0 @@ -846,7 +848,7 @@ private Type inferRefExprType(Expr e, TypePath path) { or e = re and exists(TypePath exprPath | result = inferType(re.getExpr(), exprPath) | - if exprPath.startsWith(TRefTypeParameter(), _) + if exprPath.isCons(TRefTypeParameter(), _) then // `&x` simply means `x` when `x` already has reference type path = exprPath @@ -975,7 +977,7 @@ private module Cached { Stages::TypeInference::backref() and result = inferAnnotatedType(n, path) or - result = inferTypeSymmetry(n, path) + result = inferTypeEquality(n, path) or result = inferImplicitSelfType(n, path) or diff --git a/rust/ql/lib/codeql/rust/elements/internal/TypeMention.qll b/rust/ql/lib/codeql/rust/elements/internal/TypeMention.qll index dca40f27133b..7def62da5541 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/TypeMention.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/TypeMention.qll @@ -78,7 +78,7 @@ class TypeReprMention extends TypeMention, TypeRepr { class PathMention extends TypeMention, Path { override TypeMention getTypeArgument(int i) { - result = this.getPart().getGenericArgList().getTypeArgument(i) + result = this.getPart().getGenericArgList().getTypeArg(i) or // `Self` paths inside traits and `impl` blocks have implicit type arguments // that are the type parameters of the trait or impl. For example, in @@ -93,7 +93,7 @@ class PathMention extends TypeMention, Path { // // the `Self` return type is shorthand for `Foo`. exists(ImplOrTraitItemNode node | this = node.getASelfPath() | - result = node.(ImplItemNode).getSelfPath().getPart().getGenericArgList().getTypeArgument(i) + result = node.(ImplItemNode).getSelfPath().getPart().getGenericArgList().getTypeArg(i) or result = node.(Trait).getGenericParamList().getTypeParam(i) ) @@ -140,7 +140,7 @@ private predicate isImplSelfTypeParam( ) { exists(PathMention path | selfPath = impl.getSelfPath() and - path = selfPath.getPart().getGenericArgList().getTypeArgument(i).(PathTypeRepr).getPath() and + path = selfPath.getPart().getGenericArgList().getTypeArg(i).(PathTypeRepr).getPath() and tp = path.resolveType() ) } diff --git a/rust/ql/lib/codeql/rust/internal/AstConsistency.qll b/rust/ql/lib/codeql/rust/internal/AstConsistency.qll index 0991135267ea..b86ebc9a0f90 100644 --- a/rust/ql/lib/codeql/rust/internal/AstConsistency.qll +++ b/rust/ql/lib/codeql/rust/internal/AstConsistency.qll @@ -1,5 +1,5 @@ /** - * Provides classes for recognizing control flow graph inconsistencies. + * Provides classes for recognizing AST inconsistencies. */ private import rust @@ -75,40 +75,6 @@ query predicate multiplePositions(Element parent, int pos1, int pos2, string acc pos1 != pos2 } -private import codeql.rust.elements.internal.PathResolution - -/** Holds if `p` may resolve to multiple items including `i`. */ -query predicate multiplePathResolutions(Path p, ItemNode i) { - i = resolvePath(p) and - // `use foo::bar` may use both a type `bar` and a value `bar` - not p = - any(UseTree use | - not use.isGlob() and - not use.hasUseTreeList() - ).getPath() and - strictcount(resolvePath(p)) > 1 -} - -/** Holds if `call` has multiple static call targets including `target`. */ -query predicate multipleStaticCallTargets(CallExprBase call, Callable target) { - target = call.getStaticTarget() and - strictcount(call.getStaticTarget()) > 1 -} - -/** Holds if `fe` resolves to multiple record fields including `field`. */ -query predicate multipleRecordFields(FieldExpr fe, RecordField field) { - field = fe.getRecordField() and - strictcount(fe.getRecordField()) > 1 -} - -/** Holds if `fe` resolves to multiple tuple fields including `field`. */ -query predicate multipleTupleFields(FieldExpr fe, TupleField field) { - field = fe.getTupleField() and - strictcount(fe.getTupleField()) > 1 -} - -import codeql.rust.elements.internal.TypeInference::Consistency - /** * Gets counts of abstract syntax tree inconsistencies of each type. */ @@ -134,16 +100,4 @@ int getAstInconsistencyCounts(string type) { or type = "Multiple positions" and result = count(Element e | multiplePositions(_, _, _, _, e) | e) - or - type = "Multiple path resolutions" and - result = count(Path p | multiplePathResolutions(p, _) | p) - or - type = "Multiple static call targets" and - result = count(CallExprBase call | multipleStaticCallTargets(call, _) | call) - or - type = "Multiple record fields" and - result = count(FieldExpr fe | multipleRecordFields(fe, _) | fe) - or - type = "Multiple tuple fields" and - result = count(FieldExpr fe | multipleTupleFields(fe, _) | fe) } diff --git a/rust/ql/lib/codeql/rust/internal/PathResolutionConsistency.qll b/rust/ql/lib/codeql/rust/internal/PathResolutionConsistency.qll new file mode 100644 index 000000000000..0c54f08fbd75 --- /dev/null +++ b/rust/ql/lib/codeql/rust/internal/PathResolutionConsistency.qll @@ -0,0 +1,53 @@ +/** + * Provides classes for recognizing path resolution inconsistencies. + */ + +private import rust +private import codeql.rust.elements.internal.PathResolution + +/** Holds if `p` may resolve to multiple items including `i`. */ +query predicate multiplePathResolutions(Path p, ItemNode i) { + i = resolvePath(p) and + // `use foo::bar` may use both a type `bar` and a value `bar` + not p = + any(UseTree use | + not use.isGlob() and + not use.hasUseTreeList() + ).getPath() and + strictcount(resolvePath(p)) > 1 +} + +/** Holds if `call` has multiple static call targets including `target`. */ +query predicate multipleStaticCallTargets(CallExprBase call, Callable target) { + target = call.getStaticTarget() and + strictcount(call.getStaticTarget()) > 1 +} + +/** Holds if `fe` resolves to multiple record fields including `field`. */ +query predicate multipleRecordFields(FieldExpr fe, RecordField field) { + field = fe.getRecordField() and + strictcount(fe.getRecordField()) > 1 +} + +/** Holds if `fe` resolves to multiple tuple fields including `field`. */ +query predicate multipleTupleFields(FieldExpr fe, TupleField field) { + field = fe.getTupleField() and + strictcount(fe.getTupleField()) > 1 +} + +/** + * Gets counts of path resolution inconsistencies of each type. + */ +int getPathResolutionInconsistencyCounts(string type) { + type = "Multiple path resolutions" and + result = count(Path p | multiplePathResolutions(p, _) | p) + or + type = "Multiple static call targets" and + result = count(CallExprBase call | multipleStaticCallTargets(call, _) | call) + or + type = "Multiple record fields" and + result = count(FieldExpr fe | multipleRecordFields(fe, _) | fe) + or + type = "Multiple tuple fields" and + result = count(FieldExpr fe | multipleTupleFields(fe, _) | fe) +} diff --git a/rust/ql/lib/codeql/rust/internal/TypeInferenceConsistency.qll b/rust/ql/lib/codeql/rust/internal/TypeInferenceConsistency.qll new file mode 100644 index 000000000000..213ab100d4a4 --- /dev/null +++ b/rust/ql/lib/codeql/rust/internal/TypeInferenceConsistency.qll @@ -0,0 +1,5 @@ +/** + * Provides classes for recognizing type inference inconsistencies. + */ + +import codeql.rust.elements.internal.TypeInference::Consistency diff --git a/rust/ql/src/queries/summary/Stats.qll b/rust/ql/src/queries/summary/Stats.qll index e129490247ac..a2220398b415 100644 --- a/rust/ql/src/queries/summary/Stats.qll +++ b/rust/ql/src/queries/summary/Stats.qll @@ -7,6 +7,7 @@ private import codeql.rust.dataflow.DataFlow private import codeql.rust.dataflow.internal.DataFlowImpl private import codeql.rust.dataflow.internal.TaintTrackingImpl private import codeql.rust.internal.AstConsistency as AstConsistency +private import codeql.rust.internal.PathResolutionConsistency as PathResolutionConsistency private import codeql.rust.controlflow.internal.CfgConsistency as CfgConsistency private import codeql.rust.dataflow.internal.DataFlowConsistency as DataFlowConsistency private import codeql.rust.Concepts @@ -35,6 +36,14 @@ int getTotalAstInconsistencies() { result = sum(string type | | AstConsistency::getAstInconsistencyCounts(type)) } +/** + * Gets a count of the total number of path resolution inconsistencies in the database. + */ +int getTotalPathResolutionInconsistencies() { + result = + sum(string type | | PathResolutionConsistency::getPathResolutionInconsistencyCounts(type)) +} + /** * Gets a count of the total number of control flow graph inconsistencies in the database. */ diff --git a/rust/ql/src/queries/summary/SummaryStats.ql b/rust/ql/src/queries/summary/SummaryStats.ql index 4795dee51063..7e84371384a0 100644 --- a/rust/ql/src/queries/summary/SummaryStats.ql +++ b/rust/ql/src/queries/summary/SummaryStats.ql @@ -54,6 +54,8 @@ where or key = "Inconsistencies - AST" and value = getTotalAstInconsistencies() or + key = "Inconsistencies - Path resolution" and value = getTotalPathResolutionInconsistencies() + or key = "Inconsistencies - CFG" and value = getTotalCfgInconsistencies() or key = "Inconsistencies - data flow" and value = getTotalDataFlowInconsistencies() diff --git a/rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected b/rust/ql/test/library-tests/path-resolution/CONSISTENCY/PathResolutionConsistency.expected similarity index 100% rename from rust/ql/test/library-tests/path-resolution/CONSISTENCY/AstConsistency.expected rename to rust/ql/test/library-tests/path-resolution/CONSISTENCY/PathResolutionConsistency.expected diff --git a/rust/ql/test/library-tests/type-inference/main.rs b/rust/ql/test/library-tests/type-inference/main.rs index 13287a8c2af2..1dd17ed9d764 100644 --- a/rust/ql/test/library-tests/type-inference/main.rs +++ b/rust/ql/test/library-tests/type-inference/main.rs @@ -549,7 +549,7 @@ mod m13 { } pub fn f() { - let x = S; + let x = S {}; x.f1(); x.f2(); S::f3(&x); diff --git a/rust/ql/test/library-tests/type-inference/type-inference.expected b/rust/ql/test/library-tests/type-inference/type-inference.expected index 4b2cb92d0480..2d19586cbdb5 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -720,7 +720,7 @@ inferType | main.rs:547:16:547:16 | x | | file://:0:0:0:0 | & | | main.rs:547:16:547:16 | x | &T | main.rs:531:5:531:13 | struct S | | main.rs:552:13:552:13 | x | | main.rs:531:5:531:13 | struct S | -| main.rs:552:17:552:17 | S | | main.rs:531:5:531:13 | struct S | +| main.rs:552:17:552:20 | S {...} | | main.rs:531:5:531:13 | struct S | | main.rs:553:9:553:9 | x | | main.rs:531:5:531:13 | struct S | | main.rs:553:9:553:14 | x.f1(...) | | file://:0:0:0:0 | & | | main.rs:553:9:553:14 | x.f1(...) | &T | main.rs:531:5:531:13 | struct S | diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index f5816962f3a1..02c12a1a7817 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -156,22 +156,9 @@ module Make1 Input1> { else result = this + "." + suffix } - /** Holds if this path starts with `prefix`, followed by `tp`. */ - bindingset[this] - predicate endsWith(TypePath prefix, TypeParameter tp) { - decodeTypePathComponent(this, tp) and - prefix.isEmpty() - or - exists(int last | - last = max(this.indexOf(".")) and - prefix = this.prefix(last) and - decodeTypePathComponent(this.suffix(last + 1), tp) - ) - } - /** Holds if this path starts with `tp`, followed by `suffix`. */ bindingset[this] - predicate startsWith(TypeParameter tp, TypePath suffix) { + predicate isCons(TypeParameter tp, TypePath suffix) { decodeTypePathComponent(this, tp) and suffix.isEmpty() or @@ -319,7 +306,7 @@ module Make1 Input1> { immediateBase = resolveTypeMentionRoot(immediateBaseMention) and baseTypeMentionHasTypeParameterAt(immediateBase, baseMention, prefix, mid) and - pathToTypeParam.startsWith(mid, suffix) and + pathToTypeParam.isCons(mid, suffix) and path = prefix.append(suffix) ) ) @@ -392,7 +379,7 @@ module Make1 Input1> { baseTypeMentionHasTypeParameterAt(immediateBase, baseMention, prefix, tp) and t = immediateBaseMention.resolveTypeAt(path0) and - path0.startsWith(tp, suffix) and + path0.isCons(tp, suffix) and path = prefix.append(suffix) ) ) @@ -456,13 +443,13 @@ module Make1 Input1> { Location getLocation(); /** - * Gets the type at `path` for the explicit type argument at position - * `tapos` of this access, if any. + * Gets the type at `path` for the type argument at position `tapos` of + * this access, if any. * * For example, in a method call like `M()`, `int` is an explicit * type argument at position `0`. */ - Type getExplicitTypeArgument(TypeArgumentPosition tapos, TypePath path); + Type getTypeArgument(TypeArgumentPosition tapos, TypePath path); /** * Gets the inferred type at `path` for the position `apos` of this access. @@ -514,8 +501,12 @@ module Make1 Input1> { module Matching { private import Input + /** + * Holds if `a` targets `target` and the type for `apos` at `path` in `a` + * is `t` after adjustment by `target`. + */ pragma[nomagic] - private predicate accessType( + private predicate adjustedAccessType( Access a, AccessPosition apos, Declaration target, TypePath path, Type t ) { target = a.getTarget() and @@ -525,13 +516,15 @@ module Make1 Input1> { ) } + /** + * Gets the type of the type argument at `path` in `a` that corresponds to + * the type parameter `tp` in `target`. + */ bindingset[a, target] pragma[inline_late] - private Type explicitTypeArgument( - Access a, Declaration target, TypeParameter tp, TypePath path - ) { + private Type getTypeArgument(Access a, Declaration target, TypeParameter tp, TypePath path) { exists(TypeArgumentPosition tapos, TypeParameterPosition tppos | - result = a.getExplicitTypeArgument(tapos, path) and + result = a.getTypeArgument(tapos, path) and tp = target.getTypeParameter(tppos) and typeArgumentParameterPositionMatch(tapos, tppos) ) @@ -546,9 +539,9 @@ module Make1 Input1> { Access a, Declaration target, TypePath path, Type t, TypeParameter tp ) { exists(AccessPosition apos, DeclarationPosition dpos, TypePath pathToTypeParam | - accessType(a, apos, target, pathToTypeParam.append(path), t) and + adjustedAccessType(a, apos, target, pathToTypeParam.append(path), t) and tp = target.getDeclaredType(dpos, pathToTypeParam) and - not exists(explicitTypeArgument(a, target, tp, _)) and + not exists(getTypeArgument(a, target, tp, _)) and accessDeclarationPositionMatch(apos, dpos) ) } @@ -556,7 +549,7 @@ module Make1 Input1> { private module AccessBaseType { private predicate relevantAccess(Access a, AccessPosition apos) { exists(Declaration target | - accessType(a, apos, target, _, _) and + adjustedAccessType(a, apos, target, _, _) and target.getDeclaredType(_, _) instanceof TypeParameter ) } @@ -572,7 +565,7 @@ module Make1 Input1> { relevantAccess(a, apos) and exists(TypePath path0 | result = a.getInferredType(apos, path0) and - path0.startsWith(tp, suffix) + path0.isCons(tp, suffix) ) } @@ -611,9 +604,9 @@ module Make1 Input1> { exists(Type sub | sub = inferRootType(a, apos) | baseTypeMentionHasNonTypeParameterAt(sub, baseMention, path, t) or - exists(TypePath prefix, TypePath suffix, TypeParameter i | - baseTypeMentionHasTypeParameterAt(sub, baseMention, prefix, i) and - t = inferTypeAt(a, apos, i, suffix) and + exists(TypePath prefix, TypePath suffix, TypeParameter tp | + baseTypeMentionHasTypeParameterAt(sub, baseMention, prefix, tp) and + t = inferTypeAt(a, apos, tp, suffix) and path = prefix.append(suffix) ) ) @@ -636,7 +629,7 @@ module Make1 Input1> { Declaration decl, DeclarationPosition dpos, Type base, TypePath path, Type t ) { t = decl.getDeclaredType(dpos, path) and - path.startsWith(base.getATypeParameter(), _) + path.isCons(base.getATypeParameter(), _) } /** @@ -677,7 +670,7 @@ module Make1 Input1> { exists(AccessPosition apos, DeclarationPosition dpos, Type base, TypePath pathToTypeParam | accessBaseType(a, apos, target, base, pathToTypeParam.append(path), t) and declarationBaseType(target, dpos, base, pathToTypeParam, tp) and - not exists(explicitTypeArgument(a, target, tp, _)) and + not exists(getTypeArgument(a, target, tp, _)) and accessDeclarationPositionMatch(apos, dpos) ) } @@ -687,7 +680,7 @@ module Make1 Input1> { Access a, Declaration target, TypePath path, Type t, TypeParameter tp ) { target = a.getTarget() and - t = explicitTypeArgument(a, target, tp, path) + t = getTypeArgument(a, target, tp, path) } pragma[nomagic] From 3bb89ea863c4f498eae3e6f4bef5d669c9d5405c Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 13 Mar 2025 15:01:26 +0100 Subject: [PATCH 09/11] Rust: Move type inference/path resolution out of `elements` folder --- rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll | 2 +- rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll | 2 +- rust/ql/lib/codeql/rust/elements/internal/FieldExprImpl.qll | 2 +- .../lib/codeql/rust/elements/internal/MethodCallExprImpl.qll | 4 ++-- rust/ql/lib/codeql/rust/elements/internal/RecordExprImpl.qll | 2 +- rust/ql/lib/codeql/rust/elements/internal/RecordPatImpl.qll | 2 +- .../lib/codeql/rust/elements/internal/TupleStructPatImpl.qll | 2 +- rust/ql/lib/codeql/rust/internal/CachedStages.qll | 4 ++-- .../codeql/rust/{elements => }/internal/PathResolution.qll | 0 .../ql/lib/codeql/rust/internal/PathResolutionConsistency.qll | 2 +- rust/ql/lib/codeql/rust/{elements => }/internal/Type.qll | 0 .../lib/codeql/rust/{elements => }/internal/TypeInference.qll | 0 rust/ql/lib/codeql/rust/internal/TypeInferenceConsistency.qll | 2 +- .../lib/codeql/rust/{elements => }/internal/TypeMention.qll | 0 rust/ql/test/library-tests/path-resolution/path-resolution.ql | 4 ++-- rust/ql/test/library-tests/type-inference/type-inference.ql | 2 +- 16 files changed, 15 insertions(+), 15 deletions(-) rename rust/ql/lib/codeql/rust/{elements => }/internal/PathResolution.qll (100%) rename rust/ql/lib/codeql/rust/{elements => }/internal/Type.qll (100%) rename rust/ql/lib/codeql/rust/{elements => }/internal/TypeInference.qll (100%) rename rust/ql/lib/codeql/rust/{elements => }/internal/TypeMention.qll (100%) diff --git a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll index 037b5322185a..ea24d4bfe370 100644 --- a/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll +++ b/rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll @@ -10,7 +10,7 @@ private import codeql.dataflow.internal.DataFlowImpl private import rust private import SsaImpl as SsaImpl private import codeql.rust.controlflow.internal.Scope as Scope -private import codeql.rust.elements.internal.PathResolution +private import codeql.rust.internal.PathResolution private import codeql.rust.controlflow.ControlFlowGraph private import codeql.rust.controlflow.CfgNodes private import codeql.rust.dataflow.Ssa diff --git a/rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll index d5dcec253536..f26fd3109ea2 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/CallExprImpl.qll @@ -13,7 +13,7 @@ private import codeql.rust.elements.PathExpr */ module Impl { private import rust - private import PathResolution as PathResolution + private import codeql.rust.internal.PathResolution as PathResolution pragma[nomagic] Path getFunctionPath(CallExpr ce) { result = ce.getFunction().(PathExpr).getPath() } diff --git a/rust/ql/lib/codeql/rust/elements/internal/FieldExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/FieldExprImpl.qll index a9f35432a90b..18896c5b4e0f 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/FieldExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/FieldExprImpl.qll @@ -12,7 +12,7 @@ private import codeql.rust.elements.internal.generated.FieldExpr */ module Impl { private import rust - private import TypeInference as TypeInference + private import codeql.rust.internal.TypeInference as TypeInference // the following QLdoc is generated: if you need to edit it, do it in the schema file /** diff --git a/rust/ql/lib/codeql/rust/elements/internal/MethodCallExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/MethodCallExprImpl.qll index b0bda8afc493..bc8dfaabb198 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/MethodCallExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/MethodCallExprImpl.qll @@ -6,8 +6,8 @@ private import rust private import codeql.rust.elements.internal.generated.MethodCallExpr -private import codeql.rust.elements.internal.PathResolution -private import codeql.rust.elements.internal.TypeInference +private import codeql.rust.internal.PathResolution +private import codeql.rust.internal.TypeInference /** * INTERNAL: This module contains the customizable definition of `MethodCallExpr` and should not diff --git a/rust/ql/lib/codeql/rust/elements/internal/RecordExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/RecordExprImpl.qll index cf763467c697..ae8717f49dd8 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/RecordExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/RecordExprImpl.qll @@ -12,7 +12,7 @@ private import codeql.rust.elements.internal.generated.RecordExpr */ module Impl { private import rust - private import PathResolution as PathResolution + private import codeql.rust.internal.PathResolution as PathResolution // the following QLdoc is generated: if you need to edit it, do it in the schema file /** diff --git a/rust/ql/lib/codeql/rust/elements/internal/RecordPatImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/RecordPatImpl.qll index 8a5e90f1c407..02ea4726df6a 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/RecordPatImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/RecordPatImpl.qll @@ -12,7 +12,7 @@ private import codeql.rust.elements.internal.generated.RecordPat */ module Impl { private import rust - private import PathResolution as PathResolution + private import codeql.rust.internal.PathResolution as PathResolution // the following QLdoc is generated: if you need to edit it, do it in the schema file /** diff --git a/rust/ql/lib/codeql/rust/elements/internal/TupleStructPatImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/TupleStructPatImpl.qll index 37904d4d0035..4120762db7ba 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/TupleStructPatImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/TupleStructPatImpl.qll @@ -12,7 +12,7 @@ private import codeql.rust.elements.internal.generated.TupleStructPat */ module Impl { private import rust - private import PathResolution as PathResolution + private import codeql.rust.internal.PathResolution as PathResolution // the following QLdoc is generated: if you need to edit it, do it in the schema file /** diff --git a/rust/ql/lib/codeql/rust/internal/CachedStages.qll b/rust/ql/lib/codeql/rust/internal/CachedStages.qll index f4b2ba83aee9..e59efb110b15 100644 --- a/rust/ql/lib/codeql/rust/internal/CachedStages.qll +++ b/rust/ql/lib/codeql/rust/internal/CachedStages.qll @@ -100,8 +100,8 @@ module Stages { */ cached module TypeInference { - private import codeql.rust.elements.internal.Type - private import codeql.rust.elements.internal.TypeInference + private import codeql.rust.internal.Type + private import codeql.rust.internal.TypeInference /** * Always holds. diff --git a/rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/internal/PathResolution.qll similarity index 100% rename from rust/ql/lib/codeql/rust/elements/internal/PathResolution.qll rename to rust/ql/lib/codeql/rust/internal/PathResolution.qll diff --git a/rust/ql/lib/codeql/rust/internal/PathResolutionConsistency.qll b/rust/ql/lib/codeql/rust/internal/PathResolutionConsistency.qll index 0c54f08fbd75..08ecb0c1a7cf 100644 --- a/rust/ql/lib/codeql/rust/internal/PathResolutionConsistency.qll +++ b/rust/ql/lib/codeql/rust/internal/PathResolutionConsistency.qll @@ -3,7 +3,7 @@ */ private import rust -private import codeql.rust.elements.internal.PathResolution +private import PathResolution /** Holds if `p` may resolve to multiple items including `i`. */ query predicate multiplePathResolutions(Path p, ItemNode i) { diff --git a/rust/ql/lib/codeql/rust/elements/internal/Type.qll b/rust/ql/lib/codeql/rust/internal/Type.qll similarity index 100% rename from rust/ql/lib/codeql/rust/elements/internal/Type.qll rename to rust/ql/lib/codeql/rust/internal/Type.qll diff --git a/rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/TypeInference.qll similarity index 100% rename from rust/ql/lib/codeql/rust/elements/internal/TypeInference.qll rename to rust/ql/lib/codeql/rust/internal/TypeInference.qll diff --git a/rust/ql/lib/codeql/rust/internal/TypeInferenceConsistency.qll b/rust/ql/lib/codeql/rust/internal/TypeInferenceConsistency.qll index 213ab100d4a4..214ee3e6d493 100644 --- a/rust/ql/lib/codeql/rust/internal/TypeInferenceConsistency.qll +++ b/rust/ql/lib/codeql/rust/internal/TypeInferenceConsistency.qll @@ -2,4 +2,4 @@ * Provides classes for recognizing type inference inconsistencies. */ -import codeql.rust.elements.internal.TypeInference::Consistency +import TypeInference::Consistency diff --git a/rust/ql/lib/codeql/rust/elements/internal/TypeMention.qll b/rust/ql/lib/codeql/rust/internal/TypeMention.qll similarity index 100% rename from rust/ql/lib/codeql/rust/elements/internal/TypeMention.qll rename to rust/ql/lib/codeql/rust/internal/TypeMention.qll diff --git a/rust/ql/test/library-tests/path-resolution/path-resolution.ql b/rust/ql/test/library-tests/path-resolution/path-resolution.ql index fbdedf809f3e..342931d54dea 100644 --- a/rust/ql/test/library-tests/path-resolution/path-resolution.ql +++ b/rust/ql/test/library-tests/path-resolution/path-resolution.ql @@ -1,6 +1,6 @@ import rust -import codeql.rust.elements.internal.PathResolution -import codeql.rust.elements.internal.TypeInference +import codeql.rust.internal.PathResolution +import codeql.rust.internal.TypeInference import utils.test.InlineExpectationsTest import TestUtils diff --git a/rust/ql/test/library-tests/type-inference/type-inference.ql b/rust/ql/test/library-tests/type-inference/type-inference.ql index 797d0142bfc2..dfd13125796b 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.ql +++ b/rust/ql/test/library-tests/type-inference/type-inference.ql @@ -1,5 +1,5 @@ import rust -import codeql.rust.elements.internal.TypeInference as TypeInference +import codeql.rust.internal.TypeInference as TypeInference import TypeInference import utils.test.InlineExpectationsTest From 255f06b65abf8aad7a3ebd397a649c50fb8b2cd4 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 13 Mar 2025 15:51:33 +0100 Subject: [PATCH 10/11] Rust: Update expected test output --- rust/ql/integration-tests/hello-project/summary.expected | 1 + .../integration-tests/hello-workspace/summary.cargo.expected | 1 + .../hello-workspace/summary.rust-project.expected | 1 + .../query-tests/diagnostics/AstConsistencyCounts.expected | 4 ---- rust/ql/test/query-tests/diagnostics/SummaryStats.expected | 1 + 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/ql/integration-tests/hello-project/summary.expected b/rust/ql/integration-tests/hello-project/summary.expected index 92056f34bcf7..3599902243cd 100644 --- a/rust/ql/integration-tests/hello-project/summary.expected +++ b/rust/ql/integration-tests/hello-project/summary.expected @@ -8,6 +8,7 @@ | Files extracted - without errors % | 80 | | Inconsistencies - AST | 0 | | Inconsistencies - CFG | 0 | +| Inconsistencies - Path resolution | 0 | | Inconsistencies - data flow | 0 | | Lines of code extracted | 6 | | Lines of user code extracted | 6 | diff --git a/rust/ql/integration-tests/hello-workspace/summary.cargo.expected b/rust/ql/integration-tests/hello-workspace/summary.cargo.expected index cdf47a58ee33..3fbea6c46417 100644 --- a/rust/ql/integration-tests/hello-workspace/summary.cargo.expected +++ b/rust/ql/integration-tests/hello-workspace/summary.cargo.expected @@ -8,6 +8,7 @@ | Files extracted - without errors % | 100 | | Inconsistencies - AST | 0 | | Inconsistencies - CFG | 0 | +| Inconsistencies - Path resolution | 0 | | Inconsistencies - data flow | 0 | | Lines of code extracted | 9 | | Lines of user code extracted | 9 | diff --git a/rust/ql/integration-tests/hello-workspace/summary.rust-project.expected b/rust/ql/integration-tests/hello-workspace/summary.rust-project.expected index cdf47a58ee33..3fbea6c46417 100644 --- a/rust/ql/integration-tests/hello-workspace/summary.rust-project.expected +++ b/rust/ql/integration-tests/hello-workspace/summary.rust-project.expected @@ -8,6 +8,7 @@ | Files extracted - without errors % | 100 | | Inconsistencies - AST | 0 | | Inconsistencies - CFG | 0 | +| Inconsistencies - Path resolution | 0 | | Inconsistencies - data flow | 0 | | Lines of code extracted | 9 | | Lines of user code extracted | 9 | diff --git a/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected b/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected index f2ab639c82fb..7f8d388fdc50 100644 --- a/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected +++ b/rust/ql/test/query-tests/diagnostics/AstConsistencyCounts.expected @@ -1,11 +1,7 @@ | Multiple children | 0 | | Multiple locations | 0 | | Multiple parents | 0 | -| Multiple path resolutions | 0 | | Multiple positions | 0 | | Multiple primary QL classes | 0 | -| Multiple record fields | 0 | -| Multiple static call targets | 0 | | Multiple toStrings | 0 | -| Multiple tuple fields | 0 | | No location | 0 | diff --git a/rust/ql/test/query-tests/diagnostics/SummaryStats.expected b/rust/ql/test/query-tests/diagnostics/SummaryStats.expected index 09327f99979b..aaf68558f09c 100644 --- a/rust/ql/test/query-tests/diagnostics/SummaryStats.expected +++ b/rust/ql/test/query-tests/diagnostics/SummaryStats.expected @@ -8,6 +8,7 @@ | Files extracted - without errors % | 57 | | Inconsistencies - AST | 0 | | Inconsistencies - CFG | 0 | +| Inconsistencies - Path resolution | 0 | | Inconsistencies - data flow | 0 | | Lines of code extracted | 60 | | Lines of user code extracted | 60 | From c3739d4f2366df8cc8998469e89ba65fec385af3 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 13 Mar 2025 21:10:48 +0100 Subject: [PATCH 11/11] Address review comments --- .../rust/elements/internal/CallExprBaseImpl.qll | 2 +- .../lib/codeql/rust/internal/PathResolution.qll | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll index 8b3700f08693..c916b717bf65 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/CallExprBaseImpl.qll @@ -27,6 +27,6 @@ module Impl { */ class CallExprBase extends Generated::CallExprBase { /** Gets the static target of this call, if any. */ - Callable getStaticTarget() { none() } // overridden by subclasses + Callable getStaticTarget() { none() } // overridden by subclasses, but cannot be made abstract } } diff --git a/rust/ql/lib/codeql/rust/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/internal/PathResolution.qll index 2bc95ff613cd..cacf1fede000 100644 --- a/rust/ql/lib/codeql/rust/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/internal/PathResolution.qll @@ -295,6 +295,11 @@ abstract class ImplOrTraitItemNode extends ItemNode { predicate hasAssocItem(string name) { name = this.getAnAssocItem().getName() } } +pragma[nomagic] +private TypeParamItemNode resolveTypeParamPathTypeRepr(PathTypeRepr ptr) { + result = resolvePath(ptr.getPath()) +} + class ImplItemNode extends ImplOrTraitItemNode instanceof Impl { Path getSelfPath() { result = super.getSelfTy().(PathTypeRepr).getPath() } @@ -310,12 +315,6 @@ class ImplItemNode extends ImplOrTraitItemNode instanceof Impl { this.getSelfPath().getPart().getGenericArgList().getAGenericArg().(TypeArg).getTypeRepr() } - pragma[nomagic] - private TypeParamItemNode getASelfTyTypeParamArg(TypeRepr arg) { - arg = this.getASelfTyArg() and - result = resolvePath(arg.(PathTypeRepr).getPath()) - } - /** * 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 @@ -340,9 +339,9 @@ class ImplItemNode extends ImplOrTraitItemNode instanceof Impl { pragma[nomagic] predicate isNotFullyParametric() { exists(TypeRepr arg | arg = this.getASelfTyArg() | - not exists(this.getASelfTyTypeParamArg(arg)) + not exists(resolveTypeParamPathTypeRepr(arg)) or - this.getASelfTyTypeParamArg(arg).hasTraitBound() + resolveTypeParamPathTypeRepr(arg).hasTraitBound() ) } @@ -500,7 +499,7 @@ private class TypeParamItemNode extends ItemNode instanceof TypeParam { exists(this.getABoundPath()) or exists(ItemNode declaringItem, WherePred wp | - this = resolvePath(wp.getTypeRepr().(PathTypeRepr).getPath()) and + this = resolveTypeParamPathTypeRepr(wp.getTypeRepr()) and wp = declaringItem.getADescendant() and this = declaringItem.getADescendant() )