#![feature(let_chains)] #![feature(if_let_guard)] use std::ops::AddAssign; fn print_str(s: &str) // s { println!("{}", s); // $ read_access=s } fn print_i64(i: i64) // i { println!("{}", i); // $ read_access=i } fn print_i64_ref(i: &i64) { print_i64(*i) // $ read_access=i } fn immutable_variable() { let x1 = "a"; // x1 print_str(x1); // $ read_access=x1 } fn mutable_variable() { let mut x2 = 4; // x2 print_i64(x2); // $ read_access=x2 x2 = 5; // $ write_access=x2 print_i64(x2); // $ read_access=x2 x2 = x2; // $ read_access=x2 $ write_access=x2 print_i64(x2); // $ read_access=x2 } fn mutable_variable_immutable_borrow() { let mut x = 1; print_i64_ref(&x); // $ access=x x = 2; // $ write_access=x print_i64_ref(&x); // $ access=x } fn variable_shadow1() { let x3 = 1; // x3_1 print_i64(x3); // $ read_access=x3_1 let x3 = // x3_2 x3 + 1; // $ read_access=x3_1 print_i64(x3); // $ read_access=x3_2 } fn variable_shadow2() { let x4 = "a"; // x4_1 print_str(x4); // $ read_access=x4_1 { let x4 = "b"; // x4_2 print_str(x4); // $ read_access=x4_2 } print_str(x4); // $ read_access=x4_1 } struct Point<'a> { x: &'a str, y: &'a str, } fn let_pattern1() { let ( ( a1, // a1 b1, // b1 ), Point { x, // x y, // y }, ) = (("a", "b"), Point { x: "x", y: "y" }); print_str(a1); // $ read_access=a1 print_str(b1); // $ read_access=b1 print_str(x); // $ read_access=x print_str(y); // $ read_access=y } fn let_pattern2() { let p1 = Point { x: "a", y: "b" }; // p1 let Point { x: a2, // a2 y: b2, // b2 } = p1; // $ read_access=p1 print_str(a2); // $ read_access=a2 print_str(b2); // $ read_access=b2 } fn let_pattern3() { let s1 = Some(String::from("Hello!")); // s1 if let Some(ref s2) // s2 = s1 { // $ read_access=s1 print_str(s2); // $ read_access=s2 } } fn let_pattern4() { let x = Some("x5"); // x1 let Some(x): Option<&str> // x2 = x // $ read_access=x1 else { let x = // x3 x; // $ read_access=x1 print_str(x.unwrap()); // $ read_access=x3 todo!() }; print_str(x); // $ read_access=x2 } fn let_pattern5() { let s1 = Some(String::from("Hello!")); // s1 while let Some(ref s2) // s2 = s1 { // $ read_access=s1 print_str(s2); // $ read_access=s2 } } fn match_pattern1() { let x6 = Some(5); // x6 let y1 = 10; // y1_1 match x6 { // $ read_access=x6 Some(50) => print_str("Got 50"), Some(y1) // y1_2 => { print_i64(y1)// $ read_access=y1_2 } None => print_str("NONE"), } print_i64(y1); // $ read_access=y1_1 } fn match_pattern2() { let numbers = (2, 4, 8, 16, 32); // numbers match numbers // $ read_access=numbers { ( first, // first _, third, // third _, fifth, // fifth ) => { print_i64(first); // $ read_access=first print_i64(third); // $ read_access=third print_i64(fifth); // $ read_access=fifth } } match numbers // $ read_access=numbers { ( first, // first .., last, // last ) => { print_i64(first); // $ read_access=first print_i64(last); // $ read_access=last } } } fn match_pattern3() { let p2 = Point { x: "x", y: "y" }; // p2 match p2 { // $ read_access=p2 Point { x: x7, .. // x7 } => print_str(x7), // $ read_access=x7 } } enum Message { Hello { id: i64 }, } fn match_pattern4() { let msg = Message::Hello { id: 0 }; // msg match msg // $ read_access=msg { Message::Hello { id: id_variable @ 3..=7, // id_variable } => print_i64(id_variable), // $ read_access=id_variable Message::Hello { id: 10..=12 } => { println!("Found an id in another range") } Message::Hello { id } // id => { print_i64(id) // $ read_access=id } } } enum Either { Left(i64), Right(i64), } fn match_pattern5() { let either = Either::Left(32); // either match either { // $ read_access=either Either::Left(a3) | Either::Right(a3) // a3 => print_i64(a3), // $ read_access=a3 } } enum ThreeValued { First(i64), Second(i64), Third(i64), } fn match_pattern6() { let tv = ThreeValued::Second(62); // tv match tv { // $ read_access=tv ThreeValued::First(a4) | ThreeValued::Second(a4) | ThreeValued::Third(a4) // a4 => print_i64(a4), // $ read_access=a4 } match tv { // $ read_access=tv (ThreeValued::First(a5) | ThreeValued::Second(a5)) | ThreeValued::Third(a5) // a5 => print_i64(a5), // $ read_access=a5 } match tv { // $ read_access=tv ThreeValued::First(a6) | (ThreeValued::Second(a6) | ThreeValued::Third(a6)) // a6 => print_i64(a6), // $ read_access=a6 } } fn match_pattern7() { let either = Either::Left(32); // either match either { // $ read_access=either Either::Left(a7) | Either::Right(a7) // a7 if a7 > 0 // $ read_access=a7 => print_i64(a7), // $ read_access=a7 _ => (), } } fn match_pattern8() { let either = Either::Left(32); // either match either { // $ read_access=either ref e @ // e (Either::Left(a11) | Either::Right(a11)) // a11 => { print_i64(a11); // $ read_access=a11 if let Either::Left(a12) // a12 = e { // $ read_access=e print_i64(*a12); // $ read_access=a12 } } _ => (), } } enum FourValued { First(i64), Second(i64), Third(i64), Fourth(i64), } fn match_pattern9() { let fv = FourValued::Second(62); // tv match fv { // $ read_access=tv FourValued::First(a13) | (FourValued::Second(a13) | FourValued::Third(a13)) | FourValued::Fourth(a13) // a13 => print_i64(a13), // $ read_access=a13 } } #[rustfmt::skip] fn match_pattern10() { let x= Some(42); // x1 if let Some(x) // x2 = x // $ read_access=x1 && x > 0 // $ read_access=x2 { print_i64(x); // $ read_access=x2 } else { let x = // x3 x; // $ read_access=x1 print_i64(x.unwrap()); // $ read_access=x3 } } #[rustfmt::skip] fn match_pattern11() { let x = Some(42); // x1 if let Some(x) // x2 = x // $ read_access=x1 && let Some(x) // x3 = Some(x) // $ read_access=x2 && x > 0 // $ read_access=x3 { print_i64(x); // $ read_access=x3 } else { let x = // x4 x; // $ read_access=x1 print_i64(x.unwrap()); // $ read_access=x4 } } #[rustfmt::skip] fn match_pattern12() { let x = Some(42); // x1 while let Some(x) // x2 = x // $ read_access=x1 && let Some(x) // x3 = Some(x) // $ read_access=x2 && x > 0 // $ read_access=x3 { print_i64(x); // $ read_access=x3 break; } print_i64(x.unwrap()); // $ read_access=x1 } #[rustfmt::skip] fn match_pattern13() { let x = Some(42); // x1 match x { // $ read_access=x1 Some(x) // x2 if let x // x3 = x // $ read_access=x2 && x > 0 => (), // $ read_access=x3 _ => () } print_i64(x.unwrap()); // $ read_access=x1 } #[rustfmt::skip] fn match_pattern14() { let x = Ok(42); // x1 if let Err(x) // x2 = x // $ read_access=x1 { print_i64(x); // $ read_access=x2 } else if let Ok(x) // x3 = x // $ read_access=x1 { print_i64(x); // $ read_access=x3 } else { print_i64(x.unwrap()); // $ read_access=x1 } } fn match_pattern15() { let x = Some(0); // x1 match x { // $ read_access=x1 Some(x) // x2 => x, // $ read_access=x2 _ => 0 }; } fn match_pattern16() { let x = Some(32); match x { // $ read_access=x Some(y) // y1 if let Some(y) = // y2 Some(y) // $ read_access=y1 => print_i64(y), // $ read_access=y2 _ => {}, } } fn param_pattern1( a8: &str, // a8 ( b3, // b3 c1, // c1 ): (&str, &str), ) -> () { print_str(a8); // $ read_access=a8 print_str(b3); // $ read_access=b3 print_str(c1); // $ read_access=c1 } fn param_pattern2((Either::Left(a9) | Either::Right(a9)): Either, // a9 ) -> () { print_i64(a9); // $ read_access=a9 } fn destruct_assignment() { let ( mut a10, // a10 mut b4, // b4 mut c2, // c2 ) = (1, 2, 3); print_i64(a10); // $ read_access=a10 print_i64(b4); // $ read_access=b4 print_i64(c2); // $ read_access=c2 ( c2, // $ write_access=c2 b4, // $ write_access=b4 a10, // $ write_access=a10 ) = ( a10, // $ read_access=a10 b4, // $ read_access=b4 c2, // $ read_access=c2 ); print_i64(a10); // $ read_access=a10 print_i64(b4); // $ read_access=b4 print_i64(c2); // $ read_access=c2 match (4, 5) { ( a10, // a10_2 b4, // b4 ) => { print_i64(a10); // $ read_access=a10_2 print_i64(b4); // $ read_access=b4 } } print_i64(a10); // $ read_access=a10 print_i64(b4); // $ read_access=b4 } fn closure_variable() { let example_closure = // example_closure |x: i64| // x_1 x; // $ read_access=x_1 let n1 = // n1 example_closure(5); // $ read_access=example_closure print_i64(n1); // $ read_access=n1 immutable_variable(); let immutable_variable = // immutable_variable |x: i64| // x_2 x; // $ read_access=x_2 let n2 = // n2 immutable_variable(6); // $ read_access=immutable_variable print_i64(n2); // $ read_access=n2 } fn nested_function() { // local variables shadow local functions let f = // f1 |x: i64| // x_1 x; // $ read_access=x_1 print_i64(f(1)); // $ read_access=f1 fn f(x: i64) -> i64 // x_2 { x + 1 // $ read_access=x_2 } print_i64(f(2)); // $ read_access=f1 { print_i64(f(3)); fn f(x: i64) -> i64 // x_3 { 2 * x // $ read_access=x_3 } { print_i64(f(4)); } let f = // f2 |x: i64| // x_4 x; // $ read_access=x_4 print_i64(f(5)); // $ read_access=f2 } } fn for_variable() { let v = &["apples", "cake", "coffee"]; // v for text // text in v { // $ read_access=v print_str(text); // $ read_access=text } } fn add_assign() { let mut a = 0; // a a += 1; // $ access=a print_i64(a); // $ read_access=a (&mut a).add_assign(10); // $ access=a print_i64(a); // $ read_access=a } fn mutate() { let mut i = 1; // i let ref_i = // ref_i &mut i; // $ access=i *ref_i = 2; // $ read_access=ref_i print_i64(i); // $ read_access=i } fn mutate_param(x: &mut i64) -> &mut i64 { *x = // $ read_access=x *x + // $ read_access=x *x; // $ read_access=x return x; // $ read_access=x } fn mutate_param2<'a>(x: &'a mut i64, y: &mut &'a mut i64) { *x = // $ read_access=x *x + // $ read_access=x *x; // $ read_access=x *y = // $ read_access=y x; // $ read_access=x } fn mutate_arg() { let mut x = 2; // x let y = // y mutate_param(&mut x); // $ access=x *y = 10; // $ read_access=y // prints 10, not 4 print_i64(x); // $ read_access=x let mut z = 4; // z let w = // w &mut &mut x; // $ access=x mutate_param2( &mut z, // $ access=z w, // $ read_access=w ); **w = 11; // $ read_access=w // prints 11, not 8 print_i64(z); // $ read_access=z } fn alias() { let mut x = 1; // x let y = // y &mut x; // $ access=x *y = 2; // $ read_access=y print_i64(x); // $ read_access=x } fn capture_immut() { let x = 100; // x // Captures immutable value by immutable reference let cap = || { print_i64(x); // $ read_access=x }; cap(); // $ read_access=cap print_i64(x); // $ read_access=x } fn capture_mut() { let mut x = 1; // x // Captures mutable value by immutable reference let closure1 = || { print_i64(x); // $ read_access=x }; closure1(); // $ read_access=closure1 print_i64(x); // $ read_access=x let mut y = 2; // y // Captures mutable value by mutable reference let mut closure2 = || { y = 3; // $ write_access=y }; closure2(); // $ read_access=closure2 print_i64(y); // $ read_access=y let mut z = 2; // z // Captures mutable value by mutable reference and calls mutating method let mut closure3 = || { z.add_assign(1); // $ read_access=z }; closure3(); // $ read_access=closure3 print_i64(z); // $ read_access=z } async fn async_block_capture() { let mut i: i64 = 0; // i let block = async { i = 1; // $ write_access=i }; // The await below causes write to `i` block.await; // $ read_access=block print_i64(i); // $ read_access=i } fn phi(b: bool) { let mut x = 1; // x print_i64(x); // $ read_access=x print_i64(x + 1); // $ read_access=x #[rustfmt::skip] let _ = if b // $ read_access=b { x = 2; // $ write_access=x print_i64(x); // $ read_access=x print_i64(x + 1); // $ read_access=x } else { x = 3; // $ write_access=x print_i64(x); // $ read_access=x print_i64(x + 1); // $ read_access=x }; print_i64(x); // $ read_access=x } fn phi_read(b1: bool, b2: bool) { let x = 1; // x #[rustfmt::skip] let _ = if b1 // $ read_access=b1 { print_i64(x); // $ read_access=x } else { print_i64(x); // $ read_access=x }; #[rustfmt::skip] let _ = if b2 // $ read_access=b2 { print_i64(x); // $ read_access=x } else { print_i64(x); // $ read_access=x }; } struct MyStruct { val: i64, } impl MyStruct { fn my_get(&mut self) -> i64 { return self.val; // $ read_access=self } fn id(self) -> Self { self // $ read_access=self } fn my_method(&mut self) { let mut f = |n| { // Capture of `self` self.val += n; // $ read_access=self read_access=n }; f(3); // $ read_access=f f(4); // $ read_access=f } } fn structs() { let mut a = MyStruct { val: 1 }; // a print_i64(a.my_get()); // $ read_access=a a.val = 5; // $ read_access=a print_i64(a.my_get()); // $ read_access=a a = MyStruct { val: 2 }; // $ write_access=a print_i64(a.my_get()); // $ read_access=a } fn arrays() { let mut a = [1, 2, 3]; // a print_i64(a[0]); // $ read_access=a a[1] = 5; // $ read_access=a print_i64(a[1]); // $ read_access=a a = [4, 5, 6]; // $ write_access=a print_i64(a[2]); // $ read_access=a } fn ref_arg() { let x = 16; // x print_i64_ref(&x); // $ access=x print_i64(x); // $ read_access=x let z = 17; // z print_i64_ref(&z); // $ access=z } trait Bar { fn bar(&self); } impl MyStruct { fn bar(&mut self) { *self = MyStruct { val: 3 }; // $ read_access=self } } fn ref_methodcall_receiver() { let mut a = MyStruct { val: 1 }; // a a.bar(); // $ read_access=a // prints 3, not 1 print_i64(a.val); // $ read_access=a } macro_rules! let_in_macro { ($e:expr) => {{ let var_in_macro = $e; var_in_macro }}; } macro_rules! let_in_macro2 { ($e:expr) => {{ let var_in_macro = 0; $e }}; } fn macro_invocation() { let var_from_macro = // var_from_macro1 let_in_macro!(37); // $ read_access=var_in_macro print_i64(var_from_macro); // $ read_access=var_from_macro1 let var_in_macro = 33; // var_in_macro1 // Our analysis does not currently respect the hygiene rules of Rust macros // (https://veykril.github.io/tlborm/decl-macros/minutiae/hygiene.html), because // all we have access to is the expanded AST print_i64(let_in_macro2!(var_in_macro)); // $ MISSING: read_access=var_in_macro1 $ SPURIOUS: read_access=var_in_macro print_i64(var_in_macro); // $ read_access=var_in_macro1 } fn let_without_initializer() { let x; // x x = 1; // $ write_access=x print_i64(x); // $ read_access=x } fn capture_phi() { let mut x = 100; // x let mut cap = |b: bool| { #[rustfmt::skip] let _ = if b { // $ read_access=b x = 200; // $ write_access=x }; }; cap(true); // $ read_access=cap print_i64(x); // $ read_access=x } mod patterns { #[rustfmt::skip] pub fn test() -> Option { let x = Some(42); // x let y : Option = // y1 match x { // $ read_access=x Some(y) => { // y2 None } None => None }; match y { // $ read_access=y1 N0ne => // n0ne N0ne // $ read_access=n0ne } } #[rustfmt::skip] fn test2() -> Option { let test_alias = // test_alias test; let test = // test test_alias(); // $ read_access=test_alias test // $ read_access=test } const z: i32 = 0; #[rustfmt::skip] fn test3() { let x = Some(0); // x1 match x { // $ read_access=x1 Some(x) // x2 => x, // $ read_access=x2 _ => 0 }; match x { // $ read_access=x1 Some(z) => z, _ => 0 }; } } fn main() { immutable_variable(); mutable_variable(); mutable_variable_immutable_borrow(); variable_shadow1(); variable_shadow2(); let_pattern1(); let_pattern2(); let_pattern3(); let_pattern4(); match_pattern1(); match_pattern2(); match_pattern3(); match_pattern4(); match_pattern5(); match_pattern6(); match_pattern7(); match_pattern8(); match_pattern9(); match_pattern10(); match_pattern11(); match_pattern12(); match_pattern13(); match_pattern14(); match_pattern15(); match_pattern16(); param_pattern1("a", ("b", "c")); param_pattern2(Either::Left(45)); destruct_assignment(); closure_variable(); nested_function(); for_variable(); add_assign(); mutate(); mutate_arg(); alias(); capture_mut(); capture_immut(); async_block_capture(); structs(); ref_arg(); ref_methodcall_receiver(); macro_invocation(); capture_phi(); }