Skip to content

Commit f1fbf1f

Browse files
committed
add positive and negative tests for temporary scope shortening FCW
1 parent bb624dc commit f1fbf1f

File tree

5 files changed

+214
-0
lines changed

5 files changed

+214
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//! The macros that are in `../user-defined-macros.rs`, but external to test diagnostics.
2+
//@ edition: 2024
3+
4+
#[macro_export]
5+
macro_rules! wrap {
6+
($arg:expr) => { { &$arg } }
7+
}
8+
9+
#[macro_export]
10+
macro_rules! print_with_internal_wrap {
11+
() => { println!("{:?}{}", (), $crate::wrap!(String::new())) }
12+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//! Future-compatibility warning test for #145838: make sure we catch all expected breakage.
2+
//! Shortening temporaries in the tails of block expressions should warn in Rust 2024, and
3+
//! shortening temporaries in the tails of `if` expressions' blocks should warn in all editions.
4+
//@ revisions: e2021 e2024
5+
//@ [e2021] edition: 2021
6+
//@ [e2024] edition: 2024
7+
//@ check-pass
8+
use std::pin::pin;
9+
10+
struct Struct { field: () }
11+
12+
fn cond() -> bool { true }
13+
fn temp() {}
14+
fn array_temp() -> [(); 1] { [()] }
15+
fn tuple_temp() -> ((),) { ((),) }
16+
fn struct_temp() -> Struct { Struct { field: () } }
17+
fn smart_ptr_temp() -> Box<()> { Box::new(()) }
18+
19+
const CONST_STRING: String = String::new();
20+
static STATIC_UNIT: () = ();
21+
22+
fn main() {
23+
let local = String::new();
24+
25+
// #145880 doesn't apply here, so this `temp()`'s lifetime is reduced by #145838 in Rust 2024.
26+
println!("{:?}{:?}", { &temp() }, ());
27+
// TODO: warn in Rust 2024
28+
29+
// In real-world projects, this breakage typically appeared in `if` expressions with a reference
30+
// to a `String` temporary in one branch's tail expression. This is edition-independent since
31+
// `if` expressions' blocks are temporary scopes in all editions.
32+
println!("{:?}{:?}", (), if cond() { &format!("") } else { "" });
33+
// TODO: warn in all editions
34+
println!("{:?}{:?}", (), if cond() { &"".to_string() } else { "" });
35+
// TODO: warn in all editions
36+
println!("{:?}{:?}", (), if cond() { &("string".to_owned() + "string") } else { "" });
37+
// TODO: warn in all editions
38+
39+
// Make sure we catch indexed and dereferenced temporaries.
40+
pin!(
41+
if cond() {
42+
&array_temp()[0]
43+
// TODO: warn in all editions
44+
} else if cond() {
45+
&tuple_temp().0
46+
// TODO: warn in all editions
47+
} else if cond() {
48+
&struct_temp().field
49+
// TODO: warn in all editions
50+
} else {
51+
&*&*smart_ptr_temp()
52+
// TODO: warn in all editions
53+
}
54+
);
55+
56+
// Test that `super let` extended by parent `super let`s in non-extending blocks are caught.
57+
pin!(pin!({ &temp() }));
58+
// TODO: warn in Rust 2024
59+
60+
// We shouldn't warn when lifetime extension applies.
61+
let _ = format_args!("{:?}{:?}", { &temp() }, if cond() { &temp() } else { &temp() });
62+
let _ = pin!(
63+
if cond() {
64+
&array_temp()[0]
65+
} else if cond() {
66+
&tuple_temp().0
67+
} else if cond() {
68+
&struct_temp().field
69+
} else {
70+
&*&*smart_ptr_temp()
71+
}
72+
);
73+
let _ = pin!(pin!({ &temp() }));
74+
75+
// We shouldn't warn when borrowing from non-temporary places.
76+
pin!({ &local });
77+
pin!({ &STATIC_UNIT });
78+
79+
// We shouldn't warn for promoted constants.
80+
pin!({ &size_of::<()>() });
81+
pin!({ &(1 / 1) });
82+
pin!({ &mut ([] as [(); 0]) });
83+
pin!({ &None::<String> });
84+
pin!({ &|| String::new() });
85+
86+
// But we do warn on these temporaries, since they aren't promoted.
87+
pin!({ &(1 / 0) });
88+
// TODO: warn in Rust 2024
89+
pin!({ &mut [()] });
90+
// TODO: warn in Rust 2024
91+
pin!({ &Some(String::new()) });
92+
// TODO: warn in Rust 2024
93+
pin!({ &(|| ())() });
94+
// TODO: warn in Rust 2024
95+
pin!({ &|| &local });
96+
// TODO: warn in Rust 2024
97+
pin!({ &CONST_STRING });
98+
// TODO: warn in Rust 2024
99+
100+
// This lint only catches future errors. Future dangling pointers do not produce warnings.
101+
pin!({ &raw const *&temp() });
102+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//! Test that `macro_extended_temporary_scopes` doesn't warn on non-extended temporaries.
2+
//@ edition: 2024
3+
#![deny(macro_extended_temporary_scopes)] //~ WARN unknown lint
4+
5+
fn temp() {}
6+
7+
fn main() {
8+
// Due to #145880, this argument isn't an extending context.
9+
println!("{:?}", { &temp() });
10+
//~^ ERROR temporary value dropped while borrowed
11+
12+
// Subexpressions of function call expressions are not extending.
13+
println!("{:?}{:?}", (), { std::convert::identity(&temp()) });
14+
//~^ ERROR temporary value dropped while borrowed
15+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
warning: unknown lint: `macro_extended_temporary_scopes`
2+
--> $DIR/non-extended.rs:3:9
3+
|
4+
LL | #![deny(macro_extended_temporary_scopes)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(unknown_lints)]` on by default
8+
9+
error[E0716]: temporary value dropped while borrowed
10+
--> $DIR/non-extended.rs:9:25
11+
|
12+
LL | println!("{:?}", { &temp() });
13+
| ---^^^^^---
14+
| | | |
15+
| | | temporary value is freed at the end of this statement
16+
| | creates a temporary value which is freed while still in use
17+
| borrow later used here
18+
|
19+
= note: consider using a `let` binding to create a longer lived value
20+
21+
error[E0716]: temporary value dropped while borrowed
22+
--> $DIR/non-extended.rs:13:56
23+
|
24+
LL | println!("{:?}{:?}", (), { std::convert::identity(&temp()) });
25+
| --------------------------^^^^^^---
26+
| | | |
27+
| | | temporary value is freed at the end of this statement
28+
| | creates a temporary value which is freed while still in use
29+
| borrow later used here
30+
|
31+
= note: consider using a `let` binding to create a longer lived value
32+
33+
error: aborting due to 2 previous errors; 1 warning emitted
34+
35+
For more information about this error, try `rustc --explain E0716`.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//! Test that the future-compatibility warning for #145838 doesn't break in the presence of
2+
//! user-defined macros.
3+
//@ build-pass
4+
//@ edition: 2024
5+
//@ aux-build:external-macros.rs
6+
//@ dont-require-annotations: NOTE
7+
8+
extern crate external_macros;
9+
10+
macro_rules! wrap {
11+
($arg:expr) => { { &$arg } }
12+
}
13+
14+
macro_rules! print_with_internal_wrap {
15+
() => { println!("{:?}{}", (), wrap!(String::new())) }
16+
}
17+
18+
fn main() {
19+
print!(
20+
"{:?}{}",
21+
(),
22+
format_args!(
23+
"{:?}{:?}",
24+
25+
// This is promoted; do not warn.
26+
wrap!(None::<String>),
27+
28+
// This does not promote; warn.
29+
wrap!(String::new())
30+
// TODO: warn
31+
)
32+
);
33+
34+
print_with_internal_wrap!();
35+
// TODO: warn
36+
37+
print!(
38+
"{:?}{:?}",
39+
40+
// This is promoted; do not warn.
41+
external_macros::wrap!(None::<String>),
42+
43+
// This does not promote; warn.
44+
external_macros::wrap!(String::new())
45+
// TODO: warn
46+
);
47+
48+
external_macros::print_with_internal_wrap!();
49+
// TODO: warn
50+
}

0 commit comments

Comments
 (0)