Merge pull request #20343 from paldepind/rust/certain-type-inline-expectation

Rust: Change inline expectation annotation for inferred certain types
This commit is contained in:
Simon Friis Vindum
2025-09-02 15:07:41 +02:00
committed by GitHub
5 changed files with 78 additions and 75 deletions

View File

@@ -257,7 +257,7 @@ private Type inferAnnotatedType(AstNode n, TypePath path) {
}
/** Module for inferring certain type information. */
private module CertainTypeInference {
module CertainTypeInference {
pragma[nomagic]
private predicate callResolvesTo(CallExpr ce, Path p, Function f) {
p = CallExprImpl::getFunctionPath(ce) and
@@ -286,7 +286,7 @@ private module CertainTypeInference {
}
pragma[nomagic]
Type inferCertainCallExprType(CallExpr ce, TypePath path) {
private Type inferCertainCallExprType(CallExpr ce, TypePath path) {
exists(Type ty, TypePath prefix, Path p | ty = getCertainCallExprType(ce, p, prefix) |
exists(TypePath suffix, TypeParam tp |
tp = ty.(TypeParamTypeParameter).getTypeParam() and

View File

@@ -5,7 +5,7 @@ mod simple_closures {
// A simple closure without type annotations or invocations.
let my_closure = |a, b| a && b;
let x: i64 = 1i64; // $ type=x:i64
let x: i64 = 1i64; // $ certainType=x:i64
let add_one = |n| n + 1i64; // $ target=add
let _y = add_one(x); // $ type=_y:i64
@@ -27,7 +27,7 @@ mod simple_closures {
// The return type of `id2` is inferred from the type of the call expression.
let id2 = |b| b;
let arg = Default::default(); // $ target=default type=arg:bool
let _b2: bool = id2(arg); // $ type=_b2:bool
let _b2: bool = id2(arg); // $ certainType=_b2:bool
}
}
@@ -60,7 +60,7 @@ mod fn_once_trait {
let _r = apply(f, true); // $ target=apply type=_r:i64
let f = |x| x + 1; // $ MISSING: type=x:i64 target=add
let _r2 = apply_two(f); // $ target=apply_two type=_r2:i64
let _r2 = apply_two(f); // $ target=apply_two certainType=_r2:i64
}
}

View File

@@ -1189,20 +1189,20 @@ mod type_aliases {
println!("{:?}", p1);
// Type can be only inferred from the type alias
let p2: MyPair = PairOption::PairNone(); // $ type=p2:Fst.S1 type=p2:Snd.S2
let p2: MyPair = PairOption::PairNone(); // $ certainType=p2:Fst.S1 certainType=p2:Snd.S2
println!("{:?}", p2);
// First type from alias, second from constructor
let p3: AnotherPair<_> = PairOption::PairSnd(S3); // $ type=p3:Fst.S2
let p3: AnotherPair<_> = PairOption::PairSnd(S3); // $ certainType=p3:Fst.S2
println!("{:?}", p3);
// First type from alias definition, second from argument to alias
let p3: AnotherPair<S3> = PairOption::PairNone(); // $ type=p3:Fst.S2 type=p3:Snd.S3
let p3: AnotherPair<S3> = PairOption::PairNone(); // $ certainType=p3:Fst.S2 certainType=p3:Snd.S3
println!("{:?}", p3);
g(PairOption::PairSnd(PairOption::PairSnd(S3))); // $ target=g
let x: S7<S2>; // $ type=x:Result $ type=x:E.S1 $ type=x:T.S4 $ type=x:T.T41.S2 $ type=x:T.T42.S5 $ type=x:T.T42.T5.S2
let x: S7<S2>; // $ certainType=x:Result $ certainType=x:E.S1 $ certainType=x:T.S4 $ certainType=x:T.T41.S2 $ certainType=x:T.T42.S5 $ certainType=x:T.T42.T5.S2
let y = GenS(true).get_input(); // $ type=y:Result type=y:T.bool type=y:E.bool target=get_input
}
@@ -1248,7 +1248,7 @@ mod option_methods {
struct S;
pub fn f() {
let x1 = MyOption::<S>::new(); // $ type=x1:T.S target=new
let x1 = MyOption::<S>::new(); // $ certainType=x1:T.S target=new
println!("{:?}", x1);
let mut x2 = MyOption::new(); // $ target=new
@@ -1376,7 +1376,7 @@ mod method_call_type_conversion {
let t = x7.m1(); // $ target=m1 type=t:& type=t:&T.S2
println!("{:?}", x7);
let x9: String = "Hello".to_string(); // $ type=x9:String
let x9: String = "Hello".to_string(); // $ certainType=x9:String
// Implicit `String` -> `str` conversion happens via the `Deref` trait:
// https://doc.rust-lang.org/std/string/struct.String.html#deref.
@@ -1551,23 +1551,23 @@ mod try_expressions {
mod builtins {
pub fn f() {
let x: i32 = 1; // $ type=x:i32
let x: i32 = 1; // $ certainType=x:i32
let y = 2; // $ type=y:i32
let z = x + y; // $ type=z:i32 target=add
let z = x.abs(); // $ target=abs $ type=z:i32
let c = 'c'; // $ type=c:char
let hello = "Hello"; // $ type=hello:&T.str
let f = 123.0f64; // $ type=f:f64
let t = true; // $ type=t:bool
let f = false; // $ type=f:bool
let c = 'c'; // $ certainType=c:char
let hello = "Hello"; // $ certainType=hello:&T.str
let f = 123.0f64; // $ certainType=f:f64
let t = true; // $ certainType=t:bool
let f = false; // $ certainType=f:bool
}
}
// Tests for non-overloaded operators.
mod operators {
pub fn f() {
let x = true && false; // $ type=x:bool
let y = true || false; // $ type=y:bool
let x = true && false; // $ certainType=x:bool
let y = true || false; // $ certainType=y:bool
let mut a;
let cond = 34 == 33; // $ target=eq
@@ -2341,10 +2341,10 @@ mod loops {
let vals2 = [1u16; 3]; // $ type=vals2:[T;...].u16
for u in vals2 {} // $ type=u:u16
let vals3: [u32; 3] = [1, 2, 3]; // $ type=vals3:[T;...].u32
let vals3: [u32; 3] = [1, 2, 3]; // $ certainType=vals3:[T;...].u32
for u in vals3 {} // $ type=u:u32
let vals4: [u64; 3] = [1; 3]; // $ type=vals4:[T;...].u64
let vals4: [u64; 3] = [1; 3]; // $ certainType=vals4:[T;...].u64
for u in vals4 {} // $ type=u:u64
let mut strings1 = ["foo", "bar", "baz"]; // $ type=strings1:[T;...].&T.str
@@ -2379,9 +2379,9 @@ mod loops {
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
let range = 0..10; // $ certainType=range:Range type=range:Idx.i32
for i in range {} // $ type=i:i32
let range_full = ..; // $ type=range_full:RangeFull
let range_full = ..; // $ certainType=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
@@ -2396,19 +2396,19 @@ mod loops {
let vals3 = vec![1, 2, 3]; // $ MISSING: type=vals3:Vec type=vals3:T.i32
for i in vals3 {} // $ MISSING: type=i:i32
let vals4a: Vec<u16> = [1u16, 2, 3].to_vec(); // $ type=vals4a:Vec type=vals4a:T.u16
let vals4a: Vec<u16> = [1u16, 2, 3].to_vec(); // $ certainType=vals4a:Vec certainType=vals4a:T.u16
for u in vals4a {} // $ type=u:u16
let vals4b = [1u16, 2, 3].to_vec(); // $ MISSING: type=vals4b:Vec type=vals4b:T.u16
for u in vals4b {} // $ MISSING: type=u:u16
let vals5 = Vec::from([1u32, 2, 3]); // $ type=vals5:Vec target=from type=vals5:T.u32
let vals5 = Vec::from([1u32, 2, 3]); // $ certainType=vals5:Vec target=from type=vals5:T.u32
for u in vals5 {} // $ type=u:u32
let vals6: Vec<&u64> = [1u64, 2, 3].iter().collect(); // $ type=vals6:Vec type=vals6:T.&T.u64
let vals6: Vec<&u64> = [1u64, 2, 3].iter().collect(); // $ certainType=vals6:Vec certainType=vals6:T.&T.u64
for u in vals6 {} // $ type=u:&T.u64
let mut vals7 = Vec::new(); // $ target=new type=vals7:Vec type=vals7:T.u8
let mut vals7 = Vec::new(); // $ target=new certainType=vals7:Vec type=vals7:T.u8
vals7.push(1u8); // $ target=push
for u in vals7 {} // $ type=u:u8
@@ -2429,11 +2429,11 @@ mod loops {
// while loops
let mut a: i64 = 0; // $ type=a:i64
let mut a: i64 = 0; // $ certainType=a:i64
#[rustfmt::skip]
let _ = while a < 10 // $ target=lt type=a:i64
let _ = while a < 10 // $ target=lt certainType=a:i64
{
a += 1; // $ type=a:i64 MISSING: target=add_assign
a += 1; // $ certainType=a:i64 MISSING: target=add_assign
};
}
}
@@ -2471,11 +2471,11 @@ mod explicit_type_args {
}
pub fn f() {
let x1: Option<S1<S2>> = S1::assoc_fun(); // $ type=x1:T.T.S2 target=assoc_fun
let x2 = S1::<S2>::assoc_fun(); // $ type=x2:T.T.S2 target=assoc_fun
let x3 = S3::assoc_fun(); // $ type=x3:T.T.S2 target=assoc_fun
let x4 = S1::<S2>::method(S1::default()); // $ target=method target=default type=x4:T.S2
let x5 = S3::method(S1::default()); // $ target=method target=default type=x5:T.S2
let x1: Option<S1<S2>> = S1::assoc_fun(); // $ certainType=x1:T.T.S2 target=assoc_fun
let x2 = S1::<S2>::assoc_fun(); // $ certainType=x2:T.T.S2 target=assoc_fun
let x3 = S3::assoc_fun(); // $ certainType=x3:T.T.S2 target=assoc_fun
let x4 = S1::<S2>::method(S1::default()); // $ target=method target=default certainType=x4:T.S2
let x5 = S3::method(S1::default()); // $ target=method target=default certainType=x5:T.S2
let x6 = S4::<S2>(Default::default()); // $ type=x6:T4.S2 target=default
let x7 = S4(S2); // $ type=x7:T4.S2
let x8 = S4(0); // $ type=x8:T4.i32
@@ -2490,7 +2490,7 @@ mod explicit_type_args {
{
field: S2::default(), // $ target=default
};
let x14 = foo::<i32>(Default::default()); // $ type=x14:i32 target=default target=foo
let x14 = foo::<i32>(Default::default()); // $ certainType=x14:i32 target=default target=foo
}
}
@@ -2506,8 +2506,8 @@ mod tuples {
}
pub fn f() {
let a = S1::get_pair(); // $ target=get_pair type=a:(T_2)
let mut b = S1::get_pair(); // $ target=get_pair type=b:(T_2)
let a = S1::get_pair(); // $ target=get_pair certainType=a:(T_2)
let mut b = S1::get_pair(); // $ target=get_pair certainType=b:(T_2)
let (c, d) = S1::get_pair(); // $ target=get_pair type=c:S1 type=d:S1
let (mut e, f) = S1::get_pair(); // $ target=get_pair type=e:S1 type=f:S1
let (mut g, mut h) = S1::get_pair(); // $ target=get_pair type=g:S1 type=h:S1
@@ -2642,11 +2642,11 @@ pub mod path_buf {
}
pub fn f() {
let path1 = Path::new(); // $ target=new type=path1:Path
let path1 = Path::new(); // $ target=new certainType=path1:Path
let path2 = path1.canonicalize(); // $ target=canonicalize
let path3 = path2.unwrap(); // $ target=unwrap type=path3:PathBuf
let pathbuf1 = PathBuf::new(); // $ target=new type=pathbuf1:PathBuf
let pathbuf1 = PathBuf::new(); // $ target=new certainType=pathbuf1:PathBuf
let pathbuf2 = pathbuf1.canonicalize(); // $ MISSING: target=canonicalize
let pathbuf3 = pathbuf2.unwrap(); // $ MISSING: target=unwrap type=pathbuf3:PathBuf
}

View File

@@ -171,15 +171,15 @@ pub fn literal_patterns() {
match value {
// LiteralPat - Literal patterns (including negative literals)
42 => {
let literal_match = value; // $ type=literal_match:i32
let literal_match = value; // $ certainType=literal_match:i32
println!("Literal pattern: {}", literal_match);
}
-1 => {
let negative_literal = value; // $ type=negative_literal:i32
let negative_literal = value; // $ certainType=negative_literal:i32
println!("Negative literal: {}", negative_literal);
}
0 => {
let zero_literal = value; // $ type=zero_literal:i32
let zero_literal = value; // $ certainType=zero_literal:i32
println!("Zero literal: {}", zero_literal);
}
_ => {}
@@ -188,7 +188,7 @@ pub fn literal_patterns() {
let float_val = 3.14f64;
match float_val {
3.14 => {
let pi_match = float_val; // $ type=pi_match:f64
let pi_match = float_val; // $ certainType=pi_match:f64
println!("Pi matched: {}", pi_match);
}
_ => {}
@@ -197,7 +197,7 @@ pub fn literal_patterns() {
let string_val = "hello";
match string_val {
"hello" => {
let hello_match = string_val; // $ type=hello_match:&T.str
let hello_match = string_val; // $ certainType=hello_match:&T.str
println!("String literal: {}", hello_match);
}
_ => {}
@@ -206,11 +206,11 @@ pub fn literal_patterns() {
let bool_val = true;
match bool_val {
true => {
let true_match = bool_val; // $ type=true_match:bool
let true_match = bool_val; // $ certainType=true_match:bool
println!("True literal: {}", true_match);
}
false => {
let false_match = bool_val; // $ type=false_match:bool
let false_match = bool_val; // $ certainType=false_match:bool
println!("False literal: {}", false_match);
}
}
@@ -283,7 +283,7 @@ pub fn wildcard_patterns() {
42 => println!("Specific match"),
// WildcardPat - Wildcard pattern
_ => {
let wildcard_context = value; // $ type=wildcard_context:i32
let wildcard_context = value; // $ certainType=wildcard_context:i32
println!("Wildcard pattern for: {}", wildcard_context);
}
}
@@ -295,15 +295,15 @@ pub fn range_patterns() {
match value {
// RangePat - Range patterns
1..=10 => {
let range_inclusive = value; // $ type=range_inclusive:i32
let range_inclusive = value; // $ certainType=range_inclusive:i32
println!("Range inclusive: {}", range_inclusive);
}
11.. => {
let range_from = value; // $ type=range_from:i32
let range_from = value; // $ certainType=range_from:i32
println!("Range from 11: {}", range_from);
}
..=0 => {
let range_to_inclusive = value; // $ type=range_to_inclusive:i32
let range_to_inclusive = value; // $ certainType=range_to_inclusive:i32
println!("Range to 0 inclusive: {}", range_to_inclusive);
}
_ => {}
@@ -312,11 +312,11 @@ pub fn range_patterns() {
let char_val = 'c';
match char_val {
'a'..='z' => {
let lowercase_char = char_val; // $ type=lowercase_char:char
let lowercase_char = char_val; // $ certainType=lowercase_char:char
println!("Lowercase char: {}", lowercase_char);
}
'A'..='Z' => {
let uppercase_char = char_val; // $ type=uppercase_char:char
let uppercase_char = char_val; // $ certainType=uppercase_char:char
println!("Uppercase char: {}", uppercase_char);
}
_ => {}
@@ -330,7 +330,7 @@ pub fn reference_patterns() {
// RefPat - Reference patterns
match &value {
&42 => {
let deref_match = value; // $ type=deref_match:i32
let deref_match = value; // $ certainType=deref_match:i32
println!("Dereferenced match: {}", deref_match);
}
&x => {
@@ -446,7 +446,7 @@ pub fn tuple_patterns() {
// TuplePat - Tuple patterns
match tuple {
(1, 2, 3.0) => {
let exact_tuple = tuple; // $ type=exact_tuple:(T_3)
let exact_tuple = tuple; // $ certainType=exact_tuple:(T_3)
println!("Exact tuple: {:?}", exact_tuple);
}
(a, b, c) => {
@@ -469,7 +469,7 @@ pub fn tuple_patterns() {
let unit = ();
match unit {
() => {
let unit_value = unit; // $ type=unit_value:()
let unit_value = unit; // $ certainType=unit_value:()
println!("Unit value: {:?}", unit_value);
}
}
@@ -525,7 +525,7 @@ pub fn slice_patterns() {
// SlicePat - Slice patterns
match slice {
[] => {
let empty_slice = slice; // $ type=empty_slice:&T.[T].i32
let empty_slice = slice; // $ certainType=empty_slice:&T.[T].i32
println!("Empty slice: {:?}", empty_slice);
}
[x] => {
@@ -569,7 +569,7 @@ pub fn path_patterns() {
match value {
CONSTANT => {
let const_match = value; // $ type=const_match:i32
let const_match = value; // $ certainType=const_match:i32
println!("Matches constant: {}", const_match);
}
_ => {}
@@ -606,11 +606,11 @@ pub fn or_patterns() {
// OrPat - Or patterns
match value {
1 | 2 | 3 => {
let small_num = value; // $ type=small_num:i32
let small_num = value; // $ certainType=small_num:i32
println!("Small number: {}", small_num);
}
10 | 20 => {
let round_num = value; // $ type=round_num:i32
let round_num = value; // $ certainType=round_num:i32
println!("Round number: {}", round_num);
}
_ => {}
@@ -630,7 +630,7 @@ pub fn or_patterns() {
// Or pattern with ranges
match value {
1..=10 | 90..=100 => {
let range_or_value = value; // $ type=range_or_value:i32
let range_or_value = value; // $ certainType=range_or_value:i32
println!("In range: {}", range_or_value);
}
_ => {}
@@ -750,11 +750,11 @@ pub fn patterns_in_let_statements() {
// Let with reference pattern
let value = 42i32;
let ref ref_val = value;
let let_ref = ref_val; // $ type=let_ref:&T.i32
let let_ref = ref_val; // $ certainType=let_ref:&T.i32
// Let with mutable pattern
let mut mut_val = 10i32;
let let_mut = mut_val; // $ type=let_mut:i32
let let_mut = mut_val; // $ certainType=let_mut:i32
}
pub fn patterns_in_function_parameters() {
@@ -779,13 +779,13 @@ pub fn patterns_in_function_parameters() {
// Call the functions to use them
let point = Point { x: 5, y: 10 };
let extracted = extract_point(point); // $ target=extract_point type=extracted:0(2).i32 type=extracted:1(2).i32
let extracted = extract_point(point); // $ target=extract_point certainType=extracted:0(2).i32 certainType=extracted:1(2).i32
let color = Color(200, 100, 50);
let red = extract_color(color); // $ target=extract_color type=red:u8
let red = extract_color(color); // $ target=extract_color certainType=red:u8
let tuple = (42i32, 3.14f64, true);
let tuple_extracted = extract_tuple(tuple); // $ target=extract_tuple type=tuple_extracted:0(2).i32 type=tuple_extracted:1(2).bool
let tuple_extracted = extract_tuple(tuple); // $ target=extract_tuple certainType=tuple_extracted:0(2).i32 certainType=tuple_extracted:1(2).bool
}
#[rustfmt::skip]

View File

@@ -48,24 +48,27 @@ module ResolveTest implements TestSig {
}
module TypeTest implements TestSig {
string getARelevantTag() { result = "type" }
string getARelevantTag() { result = ["type", "certainType"] }
predicate tagIsOptional(string expectedTag) { expectedTag = "type" }
predicate hasActualResult(Location location, string element, string tag, string value) { none() }
predicate hasOptionalResult(Location location, string element, string tag, string value) {
tag = "type" and
exists(AstNode n, TypePath path, Type t |
t = TypeInference::inferType(n, path) and
(
if t = TypeInference::CertainTypeInference::inferCertainType(n, path)
then tag = "certainType"
else tag = "type"
) and
location = n.getLocation() and
if path.isEmpty()
then value = element + ":" + t
else value = element + ":" + path.toString() + "." + t.toString()
|
element = n.toString()
or
element = n.(IdentPat).getName().getText()
(
if path.isEmpty()
then value = element + ":" + t
else value = element + ":" + path.toString() + "." + t.toString()
) and
element = [n.toString(), n.(IdentPat).getName().getText()]
)
}
}