Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions rust/ql/lib/codeql/rust/elements/RangeExprExt.qll
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ final class RangeFromToExpr extends RangeExpr {
}
}

/**
* A range-full expression. For example:
* ```rust
* let x = ..;
* ```
*/
final class RangeFullExpr extends RangeExpr {
RangeFullExpr() {
this.getOperatorName() = ".." and
not this.hasStart() and
not this.hasEnd()
}
}

/**
* A range-inclusive expression. For example:
* ```rust
Expand Down
10 changes: 10 additions & 0 deletions rust/ql/lib/codeql/rust/frameworks/stdlib/Stdlib.qll
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ class RangeToStruct extends Struct {
StructField getEnd() { result = this.getStructField("end") }
}

/**
* The [`RangeFull` struct][1].
*
* [1]: https://doc.rust-lang.org/core/ops/struct.RangeFull.html
*/
class RangeFullStruct extends Struct {
pragma[nomagic]
RangeFullStruct() { this.getCanonicalPath() = "core::ops::range::RangeFull" }
}

/**
* The [`RangeInclusive` struct][1].
*
Expand Down
35 changes: 24 additions & 11 deletions rust/ql/lib/codeql/rust/internal/TypeInference.qll
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,19 @@ private module CertainTypeInference {
or
// A `let` statement with a type annotation is a coercion site and hence
// is not a certain type equality.
exists(LetStmt let | not let.hasTypeRepr() |
let.getPat() = n1 and
exists(LetStmt let |
not let.hasTypeRepr() and
// Due to "binding modes" the type of the pattern is not necessarily the
// same as the type of the initializer. The pattern being an identifier
// pattern is sufficient to ensure that this is not the case.
let.getPat().(IdentPat) = n1 and
let.getInitializer() = n2
)
or
exists(LetExpr let |
let.getPat() = n1 and
// Similarly as for let statements, we need to rule out binding modes
// changing the type.
let.getPat().(IdentPat) = n1 and
let.getScrutinee() = n2
)
or
Expand Down Expand Up @@ -377,6 +383,11 @@ private module CertainTypeInference {
result = inferRefNodeType(n) and
path.isEmpty()
or
result = inferLogicalOperationType(n, path)
or
result = inferRangeExprType(n) and
path.isEmpty()
or
result = inferTupleRootType(n) and
path.isEmpty()
or
Expand Down Expand Up @@ -434,11 +445,10 @@ private module CertainTypeInference {
}

private Type inferLogicalOperationType(AstNode n, TypePath path) {
exists(Builtins::BuiltinType t, BinaryLogicalOperation be |
exists(Builtins::Bool t, BinaryLogicalOperation be |
n = [be, be.getLhs(), be.getRhs()] and
path.isEmpty() and
result = TStruct(t) and
t instanceof Builtins::Bool
result = TStruct(t)
)
}

Expand All @@ -456,6 +466,9 @@ private Struct getRangeType(RangeExpr re) {
re instanceof RangeToExpr and
result instanceof RangeToStruct
or
re instanceof RangeFullExpr and
result instanceof RangeFullStruct
or
re instanceof RangeFromToExpr and
result instanceof RangeStruct
or
Expand Down Expand Up @@ -486,6 +499,11 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
or
n1 = n2.(MatchExpr).getAnArm().getExpr()
or
exists(LetExpr let |
n1 = let.getScrutinee() and
n2 = let.getPat()
)
or
exists(MatchExpr me |
n1 = me.getScrutinee() and
n2 = me.getAnArm().getPat()
Expand Down Expand Up @@ -2370,8 +2388,6 @@ private module Cached {
(
result = inferAnnotatedType(n, path)
or
result = inferLogicalOperationType(n, path)
or
result = inferAssignmentOperationType(n, path)
or
result = inferTypeEquality(n, path)
Expand All @@ -2392,9 +2408,6 @@ private module Cached {
or
result = inferAwaitExprType(n, path)
or
result = inferRangeExprType(n) and
path.isEmpty()
or
result = inferIndexExprType(n, path)
or
result = inferForLoopExprType(n, path)
Expand Down
14 changes: 7 additions & 7 deletions rust/ql/test/library-tests/type-inference/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2332,6 +2332,8 @@ mod loops {
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 range_full = ..; // $ type=range_full:RangeFull
for i in &[1i64, 2i64, 3i64][range_full] {} // $ target=index MISSING: type=i:&T.i64

let range1 = // $ type=range1:Range type=range1:Idx.u16
std::ops::Range {
Expand Down Expand Up @@ -2558,25 +2560,23 @@ pub mod exec {
pub mod path_buf {
// a highly simplified model of `PathBuf::canonicalize`

pub struct Path {
}
pub struct Path {}

impl Path {
pub const fn new() -> Path {
Path { }
Path {}
}

pub fn canonicalize(&self) -> Result<PathBuf, ()> {
Ok(PathBuf::new()) // $ target=new
}
}

pub struct PathBuf {
}
pub struct PathBuf {}

impl PathBuf {
pub const fn new() -> PathBuf {
PathBuf { }
PathBuf {}
}
}

Expand All @@ -2587,7 +2587,7 @@ pub mod path_buf {
#[inline]
fn deref(&self) -> &Path {
// (very much not a real implementation)
static path : Path = Path::new(); // $ target=new
static path: Path = Path::new(); // $ target=new
&path
}
}
Expand Down
13 changes: 13 additions & 0 deletions rust/ql/test/library-tests/type-inference/pattern_matching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,19 @@ pub fn tuple_patterns() {
println!("Single element tuple: {}", single_elem);
}
}

// Tuple pattern on reference to tuple in `let` expression
let ref_tuple1: &(i32, i32) = &(1, 2);
if let (n, m) = ref_tuple1 {
println!("n: {}", n);
println!("m: {}", m);
}

// Tuple pattern on reference to tuple in `let` statement
let ref_tuple2: &(i32, i32) = &(1, 2);
let (n, m) = ref_tuple2;
println!("n: {}", n);
println!("m: {}", m);
}

pub fn parenthesized_patterns() {
Expand Down
Loading
Loading