Skip to content

Commit 028f1cb

Browse files
authored
Merge pull request #20299 from paldepind/rust/certain-extensions
Rust: Minor tweaks to certain type inference
2 parents bb08611 + 08f025f commit 028f1cb

File tree

6 files changed

+1399
-1287
lines changed

6 files changed

+1399
-1287
lines changed

rust/ql/lib/codeql/rust/elements/RangeExprExt.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ final class RangeFromToExpr extends RangeExpr {
4646
}
4747
}
4848

49+
/**
50+
* A range-full expression. For example:
51+
* ```rust
52+
* let x = ..;
53+
* ```
54+
*/
55+
final class RangeFullExpr extends RangeExpr {
56+
RangeFullExpr() {
57+
this.getOperatorName() = ".." and
58+
not this.hasStart() and
59+
not this.hasEnd()
60+
}
61+
}
62+
4963
/**
5064
* A range-inclusive expression. For example:
5165
* ```rust

rust/ql/lib/codeql/rust/frameworks/stdlib/Stdlib.qll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ class RangeToStruct extends Struct {
9494
StructField getEnd() { result = this.getStructField("end") }
9595
}
9696

97+
/**
98+
* The [`RangeFull` struct][1].
99+
*
100+
* [1]: https://doc.rust-lang.org/core/ops/struct.RangeFull.html
101+
*/
102+
class RangeFullStruct extends Struct {
103+
pragma[nomagic]
104+
RangeFullStruct() { this.getCanonicalPath() = "core::ops::range::RangeFull" }
105+
}
106+
97107
/**
98108
* The [`RangeInclusive` struct][1].
99109
*

rust/ql/lib/codeql/rust/internal/TypeInference.qll

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -324,13 +324,19 @@ private module CertainTypeInference {
324324
or
325325
// A `let` statement with a type annotation is a coercion site and hence
326326
// is not a certain type equality.
327-
exists(LetStmt let | not let.hasTypeRepr() |
328-
let.getPat() = n1 and
327+
exists(LetStmt let |
328+
not let.hasTypeRepr() and
329+
// Due to "binding modes" the type of the pattern is not necessarily the
330+
// same as the type of the initializer. The pattern being an identifier
331+
// pattern is sufficient to ensure that this is not the case.
332+
let.getPat().(IdentPat) = n1 and
329333
let.getInitializer() = n2
330334
)
331335
or
332336
exists(LetExpr let |
333-
let.getPat() = n1 and
337+
// Similarly as for let statements, we need to rule out binding modes
338+
// changing the type.
339+
let.getPat().(IdentPat) = n1 and
334340
let.getScrutinee() = n2
335341
)
336342
or
@@ -377,6 +383,11 @@ private module CertainTypeInference {
377383
result = inferRefNodeType(n) and
378384
path.isEmpty()
379385
or
386+
result = inferLogicalOperationType(n, path)
387+
or
388+
result = inferRangeExprType(n) and
389+
path.isEmpty()
390+
or
380391
result = inferTupleRootType(n) and
381392
path.isEmpty()
382393
or
@@ -434,11 +445,10 @@ private module CertainTypeInference {
434445
}
435446

436447
private Type inferLogicalOperationType(AstNode n, TypePath path) {
437-
exists(Builtins::BuiltinType t, BinaryLogicalOperation be |
448+
exists(Builtins::Bool t, BinaryLogicalOperation be |
438449
n = [be, be.getLhs(), be.getRhs()] and
439450
path.isEmpty() and
440-
result = TStruct(t) and
441-
t instanceof Builtins::Bool
451+
result = TStruct(t)
442452
)
443453
}
444454

@@ -456,6 +466,9 @@ private Struct getRangeType(RangeExpr re) {
456466
re instanceof RangeToExpr and
457467
result instanceof RangeToStruct
458468
or
469+
re instanceof RangeFullExpr and
470+
result instanceof RangeFullStruct
471+
or
459472
re instanceof RangeFromToExpr and
460473
result instanceof RangeStruct
461474
or
@@ -486,6 +499,11 @@ private predicate typeEquality(AstNode n1, TypePath prefix1, AstNode n2, TypePat
486499
or
487500
n1 = n2.(MatchExpr).getAnArm().getExpr()
488501
or
502+
exists(LetExpr let |
503+
n1 = let.getScrutinee() and
504+
n2 = let.getPat()
505+
)
506+
or
489507
exists(MatchExpr me |
490508
n1 = me.getScrutinee() and
491509
n2 = me.getAnArm().getPat()
@@ -2370,8 +2388,6 @@ private module Cached {
23702388
(
23712389
result = inferAnnotatedType(n, path)
23722390
or
2373-
result = inferLogicalOperationType(n, path)
2374-
or
23752391
result = inferAssignmentOperationType(n, path)
23762392
or
23772393
result = inferTypeEquality(n, path)
@@ -2392,9 +2408,6 @@ private module Cached {
23922408
or
23932409
result = inferAwaitExprType(n, path)
23942410
or
2395-
result = inferRangeExprType(n) and
2396-
path.isEmpty()
2397-
or
23982411
result = inferIndexExprType(n, path)
23992412
or
24002413
result = inferForLoopExprType(n, path)

rust/ql/test/library-tests/type-inference/main.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2332,6 +2332,8 @@ mod loops {
23322332
for u in [0u8..10] {} // $ type=u:Range type=u:Idx.u8
23332333
let range = 0..10; // $ type=range:Range type=range:Idx.i32
23342334
for i in range {} // $ type=i:i32
2335+
let range_full = ..; // $ type=range_full:RangeFull
2336+
for i in &[1i64, 2i64, 3i64][range_full] {} // $ target=index MISSING: type=i:&T.i64
23352337

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

2561-
pub struct Path {
2562-
}
2563+
pub struct Path {}
25632564

25642565
impl Path {
25652566
pub const fn new() -> Path {
2566-
Path { }
2567+
Path {}
25672568
}
25682569

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

2574-
pub struct PathBuf {
2575-
}
2575+
pub struct PathBuf {}
25762576

25772577
impl PathBuf {
25782578
pub const fn new() -> PathBuf {
2579-
PathBuf { }
2579+
PathBuf {}
25802580
}
25812581
}
25822582

@@ -2587,7 +2587,7 @@ pub mod path_buf {
25872587
#[inline]
25882588
fn deref(&self) -> &Path {
25892589
// (very much not a real implementation)
2590-
static path : Path = Path::new(); // $ target=new
2590+
static path: Path = Path::new(); // $ target=new
25912591
&path
25922592
}
25932593
}

rust/ql/test/library-tests/type-inference/pattern_matching.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,19 @@ pub fn tuple_patterns() {
482482
println!("Single element tuple: {}", single_elem);
483483
}
484484
}
485+
486+
// Tuple pattern on reference to tuple in `let` expression
487+
let ref_tuple1: &(i32, i32) = &(1, 2);
488+
if let (n, m) = ref_tuple1 {
489+
println!("n: {}", n);
490+
println!("m: {}", m);
491+
}
492+
493+
// Tuple pattern on reference to tuple in `let` statement
494+
let ref_tuple2: &(i32, i32) = &(1, 2);
495+
let (n, m) = ref_tuple2;
496+
println!("n: {}", n);
497+
println!("m: {}", m);
485498
}
486499

487500
pub fn parenthesized_patterns() {

0 commit comments

Comments
 (0)