diff --git a/rust/ql/lib/codeql/rust/elements/RangeExprExt.qll b/rust/ql/lib/codeql/rust/elements/RangeExprExt.qll new file mode 100644 index 000000000000..3e5680aceffb --- /dev/null +++ b/rust/ql/lib/codeql/rust/elements/RangeExprExt.qll @@ -0,0 +1,73 @@ +/** + * This module provides sub classes of the `RangeExpr` class. + */ + +private import rust + +/** + * A range-from expression. For example: + * ```rust + * let x = 10..; + * ``` + */ +final class RangeFromExpr extends RangeExpr { + RangeFromExpr() { + this.getOperatorName() = ".." and + not this.hasEnd() + } +} + +/** + * A range-to expression. For example: + * ```rust + * let x = ..10; + * ``` + */ +final class RangeToExpr extends RangeExpr { + RangeToExpr() { + this.getOperatorName() = ".." and + not this.hasStart() + } +} + +/** + * A range-from-to expression. For example: + * ```rust + * let x = 10..20; + * ``` + */ +final class RangeFromToExpr extends RangeExpr { + RangeFromToExpr() { + this.getOperatorName() = ".." and + this.hasStart() and + this.hasEnd() + } +} + +/** + * A range-inclusive expression. For example: + * ```rust + * let x = 1..=10; + * ``` + */ +final class RangeInclusiveExpr extends RangeExpr { + RangeInclusiveExpr() { + this.getOperatorName() = "..=" and + this.hasStart() and + this.hasEnd() + } +} + +/** + * A range-to-inclusive expression. For example: + * ```rust + * let x = ..=10; + * ``` + */ +final class RangeToInclusiveExpr extends RangeExpr { + RangeToInclusiveExpr() { + this.getOperatorName() = "..=" and + not this.hasStart() and + this.hasEnd() + } +} diff --git a/rust/ql/lib/codeql/rust/frameworks/stdlib/Stdlib.qll b/rust/ql/lib/codeql/rust/frameworks/stdlib/Stdlib.qll index 51a943190c53..de9afb993c2c 100644 --- a/rust/ql/lib/codeql/rust/frameworks/stdlib/Stdlib.qll +++ b/rust/ql/lib/codeql/rust/frameworks/stdlib/Stdlib.qll @@ -50,6 +50,72 @@ class ResultEnum extends Enum { Variant getErr() { result = this.getVariant("Err") } } +/** + * The [`Range` struct][1]. + * + * [1]: https://doc.rust-lang.org/core/ops/struct.Range.html + */ +class RangeStruct extends Struct { + RangeStruct() { this.getCanonicalPath() = "core::ops::range::Range" } + + /** Gets the `start` field. */ + StructField getStart() { result = this.getStructField("start") } + + /** Gets the `end` field. */ + StructField getEnd() { result = this.getStructField("end") } +} + +/** + * The [`RangeFrom` struct][1]. + * + * [1]: https://doc.rust-lang.org/core/ops/struct.RangeFrom.html + */ +class RangeFromStruct extends Struct { + RangeFromStruct() { this.getCanonicalPath() = "core::ops::range::RangeFrom" } + + /** Gets the `start` field. */ + StructField getStart() { result = this.getStructField("start") } +} + +/** + * The [`RangeTo` struct][1]. + * + * [1]: https://doc.rust-lang.org/core/ops/struct.RangeTo.html + */ +class RangeToStruct extends Struct { + RangeToStruct() { this.getCanonicalPath() = "core::ops::range::RangeTo" } + + /** Gets the `end` field. */ + StructField getEnd() { result = this.getStructField("end") } +} + +/** + * The [`RangeInclusive` struct][1]. + * + * [1]: https://doc.rust-lang.org/core/ops/struct.RangeInclusive.html + */ +class RangeInclusiveStruct extends Struct { + RangeInclusiveStruct() { this.getCanonicalPath() = "core::ops::range::RangeInclusive" } + + /** Gets the `start` field. */ + StructField getStart() { result = this.getStructField("start") } + + /** Gets the `end` field. */ + StructField getEnd() { result = this.getStructField("end") } +} + +/** + * The [`RangeToInclusive` struct][1]. + * + * [1]: https://doc.rust-lang.org/core/ops/struct.RangeToInclusive.html + */ +class RangeToInclusiveStruct extends Struct { + RangeToInclusiveStruct() { this.getCanonicalPath() = "core::ops::range::RangeToInclusive" } + + /** Gets the `end` field. */ + StructField getEnd() { result = this.getStructField("end") } +} + /** * The [`Future` trait][1]. * @@ -66,6 +132,38 @@ class FutureTrait extends Trait { } } +/** + * The [`Iterator` trait][1]. + * + * [1]: https://doc.rust-lang.org/std/iter/trait.Iterator.html + */ +class IteratorTrait extends Trait { + IteratorTrait() { this.getCanonicalPath() = "core::iter::traits::iterator::Iterator" } + + /** Gets the `Item` associated type. */ + pragma[nomagic] + TypeAlias getItemType() { + result = this.getAssocItemList().getAnAssocItem() and + result.getName().getText() = "Item" + } +} + +/** + * The [`IntoIterator` trait][1]. + * + * [1]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html + */ +class IntoIteratorTrait extends Trait { + IntoIteratorTrait() { this.getCanonicalPath() = "core::iter::traits::collect::IntoIterator" } + + /** Gets the `Item` associated type. */ + pragma[nomagic] + TypeAlias getItemType() { + result = this.getAssocItemList().getAnAssocItem() and + result.getName().getText() = "Item" + } +} + /** * The [`String` struct][1]. * diff --git a/rust/ql/lib/codeql/rust/internal/Type.qll b/rust/ql/lib/codeql/rust/internal/Type.qll index 638ed4a4b74f..497639b95c06 100644 --- a/rust/ql/lib/codeql/rust/internal/Type.qll +++ b/rust/ql/lib/codeql/rust/internal/Type.qll @@ -421,21 +421,25 @@ final class ImplTypeAbstraction extends TypeAbstraction, Impl { } final class TraitTypeAbstraction extends TypeAbstraction, Trait { - override TypeParamTypeParameter getATypeParameter() { - result.getTypeParam() = this.getGenericParamList().getATypeParam() + override TypeParameter getATypeParameter() { + result.(TypeParamTypeParameter).getTypeParam() = this.getGenericParamList().getATypeParam() + or + result.(AssociatedTypeTypeParameter).getTrait() = this } } final class TypeBoundTypeAbstraction extends TypeAbstraction, TypeBound { - override TypeParamTypeParameter getATypeParameter() { none() } + override TypeParameter getATypeParameter() { none() } } final class SelfTypeBoundTypeAbstraction extends TypeAbstraction, Name { - SelfTypeBoundTypeAbstraction() { any(Trait trait).getName() = this } + private TraitTypeAbstraction trait; + + SelfTypeBoundTypeAbstraction() { trait.getName() = this } - override TypeParamTypeParameter getATypeParameter() { none() } + override TypeParameter getATypeParameter() { none() } } final class ImplTraitTypeReprAbstraction extends TypeAbstraction, ImplTraitTypeRepr { - override TypeParamTypeParameter getATypeParameter() { none() } + override TypeParameter getATypeParameter() { none() } } diff --git a/rust/ql/lib/codeql/rust/internal/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/TypeInference.qll index b1e7822dee36..b892aad2d0a9 100644 --- a/rust/ql/lib/codeql/rust/internal/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/TypeInference.qll @@ -296,6 +296,27 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat n1.(ArrayRepeatExpr).getRepeatOperand() = n2 and prefix1 = TypePath::singleton(TArrayTypeParameter()) and prefix2.isEmpty() + or + exists(Struct s | + n2 = [n1.(RangeExpr).getStart(), n1.(RangeExpr).getEnd()] and + prefix1 = TypePath::singleton(TTypeParamTypeParameter(s.getGenericParamList().getATypeParam())) and + prefix2.isEmpty() + | + n1 instanceof RangeFromExpr and + s instanceof RangeFromStruct + or + n1 instanceof RangeToExpr and + s instanceof RangeToStruct + or + n1 instanceof RangeFromToExpr and + s instanceof RangeStruct + or + n1 instanceof RangeInclusiveExpr and + s instanceof RangeInclusiveStruct + or + n1 instanceof RangeToInclusiveExpr and + s instanceof RangeToInclusiveStruct + ) } pragma[nomagic] @@ -1062,7 +1083,7 @@ private TraitType inferAsyncBlockExprRootType(AsyncBlockExpr abe) { result = getFutureTraitType() } -final class AwaitTarget extends Expr { +final private class AwaitTarget extends Expr { AwaitTarget() { this = any(AwaitExpr ae).getExpr() } Type getTypeAt(TypePath path) { result = inferType(this, path) } @@ -1098,6 +1119,29 @@ private class Vec extends Struct { pragma[nomagic] private Type inferArrayExprType(ArrayExpr ae) { exists(ae) and result = TArrayType() } +/** + * Gets the root type of the range expression `re`. + */ +pragma[nomagic] +private Type inferRangeExprType(RangeExpr re) { + exists(Struct s | result = TStruct(s) | + re instanceof RangeFromExpr and + s instanceof RangeFromStruct + or + re instanceof RangeToExpr and + s instanceof RangeToStruct + or + re instanceof RangeFromToExpr and + s instanceof RangeStruct + or + re instanceof RangeInclusiveExpr and + s instanceof RangeInclusiveStruct + or + re instanceof RangeToInclusiveExpr and + s instanceof RangeToInclusiveStruct + ) +} + /** * According to [the Rust reference][1]: _"array and slice-typed expressions * can be indexed with a `usize` index ... For other types an index expression @@ -1134,23 +1178,49 @@ private Type inferIndexExprType(IndexExpr ie, TypePath path) { ) } +final private class ForIterableExpr extends Expr { + ForIterableExpr() { this = any(ForExpr fe).getIterable() } + + Type getTypeAt(TypePath path) { result = inferType(this, path) } +} + +private module ForIterableSatisfiesConstraintInput implements + SatisfiesConstraintInputSig +{ + predicate relevantConstraint(ForIterableExpr term, Type constraint) { + exists(term) and + exists(Trait t | t = constraint.(TraitType).getTrait() | + // TODO: Remove the line below once we can handle the `impl IntoIterator for I` implementation + t instanceof IteratorTrait or + t instanceof IntoIteratorTrait + ) + } +} + +pragma[nomagic] +private AssociatedTypeTypeParameter getIteratorItemTypeParameter() { + result.getTypeAlias() = any(IteratorTrait t).getItemType() +} + +pragma[nomagic] +private AssociatedTypeTypeParameter getIntoIteratorItemTypeParameter() { + result.getTypeAlias() = any(IntoIteratorTrait t).getItemType() +} + pragma[nomagic] private Type inferForLoopExprType(AstNode n, TypePath path) { // type of iterable -> type of pattern (loop variable) - exists(ForExpr fe, Type iterableType, TypePath iterablePath | + exists(ForExpr fe, TypePath exprPath, AssociatedTypeTypeParameter tp | n = fe.getPat() and - iterableType = inferType(fe.getIterable(), iterablePath) and - result = iterableType and - ( - iterablePath.isCons(any(Vec v).getElementTypeParameter(), path) - or - iterablePath.isCons(any(ArrayTypeParameter tp), path) - or - iterablePath - .stripPrefix(TypePath::cons(TRefTypeParameter(), - TypePath::singleton(any(SliceTypeParameter tp)))) = path - // TODO: iterables (general case for containers, ranges etc) - ) + SatisfiesConstraint::satisfiesConstraintType(fe.getIterable(), + _, exprPath, result) and + exprPath.isCons(tp, path) + | + tp = getIntoIteratorItemTypeParameter() + or + // TODO: Remove once we can handle the `impl IntoIterator for I` implementation + tp = getIteratorItemTypeParameter() and + inferType(fe.getIterable()) != TArrayType() ) } @@ -1589,6 +1659,9 @@ private module Cached { result = inferArrayExprType(n) and path.isEmpty() or + result = inferRangeExprType(n) and + path.isEmpty() + or result = inferIndexExprType(n, path) or result = inferForLoopExprType(n, path) diff --git a/rust/ql/lib/rust.qll b/rust/ql/lib/rust.qll index 209a002663f4..f61972e46c9a 100644 --- a/rust/ql/lib/rust.qll +++ b/rust/ql/lib/rust.qll @@ -15,3 +15,4 @@ import codeql.rust.elements.AsyncBlockExpr import codeql.rust.elements.Variable import codeql.rust.elements.NamedFormatArgument import codeql.rust.elements.PositionalFormatArgument +import codeql.rust.elements.RangeExprExt diff --git a/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected b/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected index 4d95b9bb61b9..b5028f38f761 100644 --- a/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected +++ b/rust/ql/test/library-tests/dataflow/sources/TaintSources.expected @@ -55,10 +55,16 @@ | test.rs:412:31:412:38 | ...::read | Flow source 'FileSource' of type file (DEFAULT). | | test.rs:417:22:417:39 | ...::read_to_string | Flow source 'FileSource' of type file (DEFAULT). | | test.rs:417:22:417:39 | ...::read_to_string | Flow source 'FileSource' of type file (DEFAULT). | +| test.rs:423:22:423:25 | path | Flow source 'FileSource' of type file (DEFAULT). | +| test.rs:424:27:424:35 | file_name | Flow source 'FileSource' of type file (DEFAULT). | | test.rs:430:22:430:34 | ...::read_link | Flow source 'FileSource' of type file (DEFAULT). | | test.rs:439:31:439:45 | ...::read | Flow source 'FileSource' of type file (DEFAULT). | | test.rs:444:31:444:45 | ...::read | Flow source 'FileSource' of type file (DEFAULT). | | test.rs:449:22:449:46 | ...::read_to_string | Flow source 'FileSource' of type file (DEFAULT). | +| test.rs:455:26:455:29 | path | Flow source 'FileSource' of type file (DEFAULT). | +| test.rs:455:26:455:29 | path | Flow source 'FileSource' of type file (DEFAULT). | +| test.rs:456:31:456:39 | file_name | Flow source 'FileSource' of type file (DEFAULT). | +| test.rs:456:31:456:39 | file_name | Flow source 'FileSource' of type file (DEFAULT). | | test.rs:462:22:462:41 | ...::read_link | Flow source 'FileSource' of type file (DEFAULT). | | test.rs:472:20:472:38 | ...::open | Flow source 'FileSource' of type file (DEFAULT). | | test.rs:506:21:506:39 | ...::open | Flow source 'FileSource' of type file (DEFAULT). | diff --git a/rust/ql/test/library-tests/dataflow/sources/test.rs b/rust/ql/test/library-tests/dataflow/sources/test.rs index f0154c57014f..914350b68ceb 100644 --- a/rust/ql/test/library-tests/dataflow/sources/test.rs +++ b/rust/ql/test/library-tests/dataflow/sources/test.rs @@ -420,10 +420,10 @@ fn test_fs() -> Result<(), Box> { for entry in fs::read_dir("directory")? { let e = entry?; - let path = e.path(); // $ MISSING: Alert[rust/summary/taint-sources] - let file_name = e.file_name(); // $ MISSING: Alert[rust/summary/taint-sources] - sink(path); // $ MISSING: hasTaintFlow - sink(file_name); // $ MISSING: hasTaintFlow + let path = e.path(); // $ Alert[rust/summary/taint-sources] + let file_name = e.file_name(); // $ Alert[rust/summary/taint-sources] + sink(path); // $ hasTaintFlow + sink(file_name); // $ hasTaintFlow } { @@ -452,10 +452,10 @@ async fn test_tokio_fs() -> Result<(), Box> { let mut read_dir = tokio::fs::read_dir("directory").await?; for entry in read_dir.next_entry().await? { - let path = entry.path(); // $ MISSING: Alert[rust/summary/taint-sources] - let file_name = entry.file_name(); // $ MISSING: Alert[rust/summary/taint-sources] - sink(path); // $ MISSING: hasTaintFlow - sink(file_name); // $ MISSING: hasTaintFlow + let path = entry.path(); // $ Alert[rust/summary/taint-sources] + let file_name = entry.file_name(); // $ Alert[rust/summary/taint-sources] + sink(path); // $ hasTaintFlow + sink(file_name); // $ hasTaintFlow } { diff --git a/rust/ql/test/library-tests/type-inference/main.rs b/rust/ql/test/library-tests/type-inference/main.rs index 27b733eb102e..3c394db6505c 100644 --- a/rust/ql/test/library-tests/type-inference/main.rs +++ b/rust/ql/test/library-tests/type-inference/main.rs @@ -2077,8 +2077,8 @@ mod loops { for u in vals4 {} // $ type=u:u64 let mut strings1 = ["foo", "bar", "baz"]; // $ type=strings1:[T;...].str - for s in &strings1 {} // $ MISSING: type=s:&T.str - for s in &mut strings1 {} // $ MISSING: type=s:&T.str + for s in &strings1 {} // $ type=s:&T.str + for s in &mut strings1 {} // $ type=s:&T.str for s in strings1 {} // $ type=s:str let strings2 = // $ type=strings2:[T;...].String @@ -2106,17 +2106,17 @@ mod loops { // for loops with ranges - for i in 0..10 {} // $ MISSING: type=i:i32 - for u in [0u8..10] {} // $ MISSING: type=u:u8 - let range = 0..10; // $ MISSING: type=range:Range type=range:Idx.i32 - for i in range {} // $ MISSING: type=i:i32 + for i in 0..10 {} // $ type=i:i32 + for u in [0u8..10] {} // $ type=u:Range type=u:Idx.u8 + let range = 0..10; // $ type=range:Range type=range:Idx.i32 + for i in range {} // $ type=i:i32 let range1 = // $ type=range1:Range type=range1:Idx.u16 std::ops::Range { start: 0u16, end: 10u16, }; - for u in range1 {} // $ MISSING: type=u:u16 + for u in range1 {} // $ type=u:u16 // for loops with containers @@ -2140,11 +2140,11 @@ mod loops { for u in vals7 {} // $ MISSING: type=u:u8 let matrix1 = vec![vec![1, 2], vec![3, 4]]; // $ MISSING: type=matrix1:Vec type=matrix1:T.Vec type=matrix1:T.T.i32 - for row in matrix1 { - // $ MISSING: type=row:Vec type=row:T.i32 + #[rustfmt::skip] + let _ = for row in matrix1 { // $ MISSING: type=row:Vec type=row:T.i32 for cell in row { // $ MISSING: type=cell:i32 } - } + }; let mut map1 = std::collections::HashMap::new(); // $ MISSING: type=map1:Hashmap type=map1:K.i32 type=map1:V.Box type1=map1:V.T.&T.str map1.insert(1, Box::new("one")); // $ method=insert 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 c72396e489b1..9999c920c035 100644 --- a/rust/ql/test/library-tests/type-inference/type-inference.expected +++ b/rust/ql/test/library-tests/type-inference/type-inference.expected @@ -3157,11 +3157,17 @@ inferType | main.rs:2079:29:2079:33 | "foo" | | {EXTERNAL LOCATION} | str | | main.rs:2079:36:2079:40 | "bar" | | {EXTERNAL LOCATION} | str | | main.rs:2079:43:2079:47 | "baz" | | {EXTERNAL LOCATION} | str | +| main.rs:2080:13:2080:13 | s | | {EXTERNAL LOCATION} | Item | +| main.rs:2080:13:2080:13 | s | | file://:0:0:0:0 | & | +| main.rs:2080:13:2080:13 | s | &T | {EXTERNAL LOCATION} | str | | main.rs:2080:18:2080:26 | &strings1 | | file://:0:0:0:0 | & | | main.rs:2080:18:2080:26 | &strings1 | &T | file://:0:0:0:0 | [] | | main.rs:2080:18:2080:26 | &strings1 | &T.[T;...] | {EXTERNAL LOCATION} | str | | main.rs:2080:19:2080:26 | strings1 | | file://:0:0:0:0 | [] | | main.rs:2080:19:2080:26 | strings1 | [T;...] | {EXTERNAL LOCATION} | str | +| main.rs:2081:13:2081:13 | s | | {EXTERNAL LOCATION} | Item | +| main.rs:2081:13:2081:13 | s | | file://:0:0:0:0 | & | +| main.rs:2081:13:2081:13 | s | &T | {EXTERNAL LOCATION} | str | | main.rs:2081:18:2081:30 | &mut strings1 | | file://:0:0:0:0 | & | | main.rs:2081:18:2081:30 | &mut strings1 | &T | file://:0:0:0:0 | [] | | main.rs:2081:18:2081:30 | &mut strings1 | &T.[T;...] | {EXTERNAL LOCATION} | str | @@ -3197,6 +3203,9 @@ inferType | main.rs:2095:26:2095:30 | "bar" | | {EXTERNAL LOCATION} | str | | main.rs:2096:13:2096:31 | ...::from(...) | | {EXTERNAL LOCATION} | String | | main.rs:2096:26:2096:30 | "baz" | | {EXTERNAL LOCATION} | str | +| main.rs:2098:13:2098:13 | s | | {EXTERNAL LOCATION} | Item | +| main.rs:2098:13:2098:13 | s | | file://:0:0:0:0 | & | +| main.rs:2098:13:2098:13 | s | &T | {EXTERNAL LOCATION} | String | | main.rs:2098:18:2098:25 | strings3 | | file://:0:0:0:0 | & | | main.rs:2098:18:2098:25 | strings3 | &T | file://:0:0:0:0 | [] | | main.rs:2098:18:2098:25 | strings3 | &T.[T;...] | {EXTERNAL LOCATION} | String | @@ -3213,19 +3222,44 @@ inferType | main.rs:2104:17:2104:22 | result | | {EXTERNAL LOCATION} | i64 | | main.rs:2104:26:2104:26 | c | | main.rs:2048:5:2048:24 | MyCallable | | main.rs:2104:26:2104:33 | c.call() | | {EXTERNAL LOCATION} | i64 | +| main.rs:2109:13:2109:13 | i | | {EXTERNAL LOCATION} | Item | +| main.rs:2109:13:2109:13 | i | | {EXTERNAL LOCATION} | i32 | | main.rs:2109:18:2109:18 | 0 | | {EXTERNAL LOCATION} | i32 | +| main.rs:2109:18:2109:22 | 0..10 | | {EXTERNAL LOCATION} | Range | +| main.rs:2109:18:2109:22 | 0..10 | Idx | {EXTERNAL LOCATION} | i32 | | main.rs:2109:21:2109:22 | 10 | | {EXTERNAL LOCATION} | i32 | +| main.rs:2110:13:2110:13 | u | | {EXTERNAL LOCATION} | Range | +| main.rs:2110:13:2110:13 | u | Idx | {EXTERNAL LOCATION} | i32 | +| main.rs:2110:13:2110:13 | u | Idx | {EXTERNAL LOCATION} | u8 | | main.rs:2110:18:2110:26 | [...] | | file://:0:0:0:0 | [] | +| main.rs:2110:18:2110:26 | [...] | [T;...] | {EXTERNAL LOCATION} | Range | +| main.rs:2110:18:2110:26 | [...] | [T;...].Idx | {EXTERNAL LOCATION} | i32 | +| main.rs:2110:18:2110:26 | [...] | [T;...].Idx | {EXTERNAL LOCATION} | u8 | +| main.rs:2110:19:2110:21 | 0u8 | | {EXTERNAL LOCATION} | i32 | | main.rs:2110:19:2110:21 | 0u8 | | {EXTERNAL LOCATION} | u8 | +| main.rs:2110:19:2110:25 | 0u8..10 | | {EXTERNAL LOCATION} | Range | +| main.rs:2110:19:2110:25 | 0u8..10 | Idx | {EXTERNAL LOCATION} | i32 | +| main.rs:2110:19:2110:25 | 0u8..10 | Idx | {EXTERNAL LOCATION} | u8 | | main.rs:2110:24:2110:25 | 10 | | {EXTERNAL LOCATION} | i32 | +| main.rs:2110:24:2110:25 | 10 | | {EXTERNAL LOCATION} | u8 | +| main.rs:2111:13:2111:17 | range | | {EXTERNAL LOCATION} | Range | +| main.rs:2111:13:2111:17 | range | Idx | {EXTERNAL LOCATION} | i32 | | main.rs:2111:21:2111:21 | 0 | | {EXTERNAL LOCATION} | i32 | +| main.rs:2111:21:2111:25 | 0..10 | | {EXTERNAL LOCATION} | Range | +| main.rs:2111:21:2111:25 | 0..10 | Idx | {EXTERNAL LOCATION} | i32 | | main.rs:2111:24:2111:25 | 10 | | {EXTERNAL LOCATION} | i32 | +| main.rs:2112:13:2112:13 | i | | {EXTERNAL LOCATION} | Item | +| main.rs:2112:13:2112:13 | i | | {EXTERNAL LOCATION} | i32 | +| main.rs:2112:18:2112:22 | range | | {EXTERNAL LOCATION} | Range | +| main.rs:2112:18:2112:22 | range | Idx | {EXTERNAL LOCATION} | i32 | | main.rs:2114:13:2114:18 | range1 | | {EXTERNAL LOCATION} | Range | | main.rs:2114:13:2114:18 | range1 | Idx | {EXTERNAL LOCATION} | u16 | | main.rs:2115:9:2118:9 | ...::Range {...} | | {EXTERNAL LOCATION} | Range | | main.rs:2115:9:2118:9 | ...::Range {...} | Idx | {EXTERNAL LOCATION} | u16 | | main.rs:2116:20:2116:23 | 0u16 | | {EXTERNAL LOCATION} | u16 | | main.rs:2117:18:2117:22 | 10u16 | | {EXTERNAL LOCATION} | u16 | +| main.rs:2119:13:2119:13 | u | | {EXTERNAL LOCATION} | Item | +| main.rs:2119:13:2119:13 | u | | {EXTERNAL LOCATION} | u16 | | main.rs:2119:18:2119:23 | range1 | | {EXTERNAL LOCATION} | Range | | main.rs:2119:18:2119:23 | range1 | Idx | {EXTERNAL LOCATION} | u16 | | main.rs:2123:26:2123:26 | 1 | | {EXTERNAL LOCATION} | i32 | @@ -3246,7 +3280,11 @@ inferType | main.rs:2126:39:2126:39 | 2 | | {EXTERNAL LOCATION} | u16 | | main.rs:2126:42:2126:42 | 3 | | {EXTERNAL LOCATION} | i32 | | main.rs:2126:42:2126:42 | 3 | | {EXTERNAL LOCATION} | u16 | +| main.rs:2127:13:2127:13 | u | | {EXTERNAL LOCATION} | Vec | | main.rs:2127:13:2127:13 | u | | {EXTERNAL LOCATION} | u16 | +| main.rs:2127:13:2127:13 | u | | file://:0:0:0:0 | & | +| main.rs:2127:13:2127:13 | u | A | {EXTERNAL LOCATION} | Global | +| main.rs:2127:13:2127:13 | u | T | {EXTERNAL LOCATION} | u16 | | main.rs:2127:18:2127:23 | vals4a | | {EXTERNAL LOCATION} | Vec | | main.rs:2127:18:2127:23 | vals4a | A | {EXTERNAL LOCATION} | Global | | main.rs:2127:18:2127:23 | vals4a | T | {EXTERNAL LOCATION} | u16 | @@ -3274,7 +3312,11 @@ inferType | main.rs:2132:38:2132:38 | 2 | | {EXTERNAL LOCATION} | u32 | | main.rs:2132:41:2132:41 | 3 | | {EXTERNAL LOCATION} | i32 | | main.rs:2132:41:2132:41 | 3 | | {EXTERNAL LOCATION} | u32 | +| main.rs:2133:13:2133:13 | u | | {EXTERNAL LOCATION} | Vec | | main.rs:2133:13:2133:13 | u | | {EXTERNAL LOCATION} | u8 | +| main.rs:2133:13:2133:13 | u | | file://:0:0:0:0 | & | +| main.rs:2133:13:2133:13 | u | A | {EXTERNAL LOCATION} | Global | +| main.rs:2133:13:2133:13 | u | T | {EXTERNAL LOCATION} | u8 | | main.rs:2133:18:2133:22 | vals5 | | {EXTERNAL LOCATION} | Vec | | main.rs:2133:18:2133:22 | vals5 | A | {EXTERNAL LOCATION} | Global | | main.rs:2133:18:2133:22 | vals5 | T | {EXTERNAL LOCATION} | u8 | @@ -3295,8 +3337,12 @@ inferType | main.rs:2135:39:2135:39 | 2 | | {EXTERNAL LOCATION} | u64 | | main.rs:2135:42:2135:42 | 3 | | {EXTERNAL LOCATION} | i32 | | main.rs:2135:42:2135:42 | 3 | | {EXTERNAL LOCATION} | u64 | +| main.rs:2136:13:2136:13 | u | | {EXTERNAL LOCATION} | Vec | | main.rs:2136:13:2136:13 | u | | file://:0:0:0:0 | & | | main.rs:2136:13:2136:13 | u | &T | {EXTERNAL LOCATION} | u64 | +| main.rs:2136:13:2136:13 | u | A | {EXTERNAL LOCATION} | Global | +| main.rs:2136:13:2136:13 | u | T | file://:0:0:0:0 | & | +| main.rs:2136:13:2136:13 | u | T.&T | {EXTERNAL LOCATION} | u64 | | main.rs:2136:18:2136:22 | vals6 | | {EXTERNAL LOCATION} | Vec | | main.rs:2136:18:2136:22 | vals6 | A | {EXTERNAL LOCATION} | Global | | main.rs:2136:18:2136:22 | vals6 | T | file://:0:0:0:0 | & | @@ -3308,6 +3354,9 @@ inferType | main.rs:2139:9:2139:13 | vals7 | | {EXTERNAL LOCATION} | Vec | | main.rs:2139:9:2139:13 | vals7 | A | {EXTERNAL LOCATION} | Global | | main.rs:2139:20:2139:22 | 1u8 | | {EXTERNAL LOCATION} | u8 | +| main.rs:2140:13:2140:13 | u | | {EXTERNAL LOCATION} | Vec | +| main.rs:2140:13:2140:13 | u | | file://:0:0:0:0 | & | +| main.rs:2140:13:2140:13 | u | A | {EXTERNAL LOCATION} | Global | | main.rs:2140:18:2140:22 | vals7 | | {EXTERNAL LOCATION} | Vec | | main.rs:2140:18:2140:22 | vals7 | A | {EXTERNAL LOCATION} | Global | | main.rs:2142:33:2142:33 | 1 | | {EXTERNAL LOCATION} | i32 | @@ -3332,15 +3381,21 @@ inferType | main.rs:2151:24:2151:38 | ...::new(...) | | {EXTERNAL LOCATION} | Box | | main.rs:2151:24:2151:38 | ...::new(...) | A | {EXTERNAL LOCATION} | Global | | main.rs:2151:33:2151:37 | "two" | | {EXTERNAL LOCATION} | str | +| main.rs:2152:13:2152:15 | key | | {EXTERNAL LOCATION} | Item | +| main.rs:2152:13:2152:15 | key | | file://:0:0:0:0 | & | | main.rs:2152:20:2152:23 | map1 | | {EXTERNAL LOCATION} | HashMap | | main.rs:2152:20:2152:23 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2152:20:2152:30 | map1.keys() | | {EXTERNAL LOCATION} | Keys | +| main.rs:2153:13:2153:17 | value | | {EXTERNAL LOCATION} | Item | +| main.rs:2153:13:2153:17 | value | | file://:0:0:0:0 | & | | main.rs:2153:22:2153:25 | map1 | | {EXTERNAL LOCATION} | HashMap | | main.rs:2153:22:2153:25 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2153:22:2153:34 | map1.values() | | {EXTERNAL LOCATION} | Values | +| main.rs:2154:13:2154:24 | TuplePat | | {EXTERNAL LOCATION} | Item | | main.rs:2154:29:2154:32 | map1 | | {EXTERNAL LOCATION} | HashMap | | main.rs:2154:29:2154:32 | map1 | S | {EXTERNAL LOCATION} | RandomState | | main.rs:2154:29:2154:39 | map1.iter() | | {EXTERNAL LOCATION} | Iter | +| main.rs:2155:13:2155:24 | TuplePat | | {EXTERNAL LOCATION} | Item | | main.rs:2155:29:2155:33 | &map1 | | file://:0:0:0:0 | & | | main.rs:2155:29:2155:33 | &map1 | &T | {EXTERNAL LOCATION} | HashMap | | main.rs:2155:29:2155:33 | &map1 | &T.S | {EXTERNAL LOCATION} | RandomState |