add positive and negative tests for temporary scope shortening FCW

This commit is contained in:
dianne 2025-09-19 20:53:31 -07:00
parent bb624dcb4c
commit f1fbf1f6ae
5 changed files with 214 additions and 0 deletions

View file

@ -0,0 +1,12 @@
//! The macros that are in `../user-defined-macros.rs`, but external to test diagnostics.
//@ edition: 2024
#[macro_export]
macro_rules! wrap {
($arg:expr) => { { &$arg } }
}
#[macro_export]
macro_rules! print_with_internal_wrap {
() => { println!("{:?}{}", (), $crate::wrap!(String::new())) }
}

View file

@ -0,0 +1,102 @@
//! Future-compatibility warning test for #145838: make sure we catch all expected breakage.
//! Shortening temporaries in the tails of block expressions should warn in Rust 2024, and
//! shortening temporaries in the tails of `if` expressions' blocks should warn in all editions.
//@ revisions: e2021 e2024
//@ [e2021] edition: 2021
//@ [e2024] edition: 2024
//@ check-pass
use std::pin::pin;
struct Struct { field: () }
fn cond() -> bool { true }
fn temp() {}
fn array_temp() -> [(); 1] { [()] }
fn tuple_temp() -> ((),) { ((),) }
fn struct_temp() -> Struct { Struct { field: () } }
fn smart_ptr_temp() -> Box<()> { Box::new(()) }
const CONST_STRING: String = String::new();
static STATIC_UNIT: () = ();
fn main() {
let local = String::new();
// #145880 doesn't apply here, so this `temp()`'s lifetime is reduced by #145838 in Rust 2024.
println!("{:?}{:?}", { &temp() }, ());
// TODO: warn in Rust 2024
// In real-world projects, this breakage typically appeared in `if` expressions with a reference
// to a `String` temporary in one branch's tail expression. This is edition-independent since
// `if` expressions' blocks are temporary scopes in all editions.
println!("{:?}{:?}", (), if cond() { &format!("") } else { "" });
// TODO: warn in all editions
println!("{:?}{:?}", (), if cond() { &"".to_string() } else { "" });
// TODO: warn in all editions
println!("{:?}{:?}", (), if cond() { &("string".to_owned() + "string") } else { "" });
// TODO: warn in all editions
// Make sure we catch indexed and dereferenced temporaries.
pin!(
if cond() {
&array_temp()[0]
// TODO: warn in all editions
} else if cond() {
&tuple_temp().0
// TODO: warn in all editions
} else if cond() {
&struct_temp().field
// TODO: warn in all editions
} else {
&*&*smart_ptr_temp()
// TODO: warn in all editions
}
);
// Test that `super let` extended by parent `super let`s in non-extending blocks are caught.
pin!(pin!({ &temp() }));
// TODO: warn in Rust 2024
// We shouldn't warn when lifetime extension applies.
let _ = format_args!("{:?}{:?}", { &temp() }, if cond() { &temp() } else { &temp() });
let _ = pin!(
if cond() {
&array_temp()[0]
} else if cond() {
&tuple_temp().0
} else if cond() {
&struct_temp().field
} else {
&*&*smart_ptr_temp()
}
);
let _ = pin!(pin!({ &temp() }));
// We shouldn't warn when borrowing from non-temporary places.
pin!({ &local });
pin!({ &STATIC_UNIT });
// We shouldn't warn for promoted constants.
pin!({ &size_of::<()>() });
pin!({ &(1 / 1) });
pin!({ &mut ([] as [(); 0]) });
pin!({ &None::<String> });
pin!({ &|| String::new() });
// But we do warn on these temporaries, since they aren't promoted.
pin!({ &(1 / 0) });
// TODO: warn in Rust 2024
pin!({ &mut [()] });
// TODO: warn in Rust 2024
pin!({ &Some(String::new()) });
// TODO: warn in Rust 2024
pin!({ &(|| ())() });
// TODO: warn in Rust 2024
pin!({ &|| &local });
// TODO: warn in Rust 2024
pin!({ &CONST_STRING });
// TODO: warn in Rust 2024
// This lint only catches future errors. Future dangling pointers do not produce warnings.
pin!({ &raw const *&temp() });
}

View file

@ -0,0 +1,15 @@
//! Test that `macro_extended_temporary_scopes` doesn't warn on non-extended temporaries.
//@ edition: 2024
#![deny(macro_extended_temporary_scopes)] //~ WARN unknown lint
fn temp() {}
fn main() {
// Due to #145880, this argument isn't an extending context.
println!("{:?}", { &temp() });
//~^ ERROR temporary value dropped while borrowed
// Subexpressions of function call expressions are not extending.
println!("{:?}{:?}", (), { std::convert::identity(&temp()) });
//~^ ERROR temporary value dropped while borrowed
}

View file

@ -0,0 +1,35 @@
warning: unknown lint: `macro_extended_temporary_scopes`
--> $DIR/non-extended.rs:3:9
|
LL | #![deny(macro_extended_temporary_scopes)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unknown_lints)]` on by default
error[E0716]: temporary value dropped while borrowed
--> $DIR/non-extended.rs:9:25
|
LL | println!("{:?}", { &temp() });
| ---^^^^^---
| | | |
| | | temporary value is freed at the end of this statement
| | creates a temporary value which is freed while still in use
| borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error[E0716]: temporary value dropped while borrowed
--> $DIR/non-extended.rs:13:56
|
LL | println!("{:?}{:?}", (), { std::convert::identity(&temp()) });
| --------------------------^^^^^^---
| | | |
| | | temporary value is freed at the end of this statement
| | creates a temporary value which is freed while still in use
| borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0716`.

View file

@ -0,0 +1,50 @@
//! Test that the future-compatibility warning for #145838 doesn't break in the presence of
//! user-defined macros.
//@ build-pass
//@ edition: 2024
//@ aux-build:external-macros.rs
//@ dont-require-annotations: NOTE
extern crate external_macros;
macro_rules! wrap {
($arg:expr) => { { &$arg } }
}
macro_rules! print_with_internal_wrap {
() => { println!("{:?}{}", (), wrap!(String::new())) }
}
fn main() {
print!(
"{:?}{}",
(),
format_args!(
"{:?}{:?}",
// This is promoted; do not warn.
wrap!(None::<String>),
// This does not promote; warn.
wrap!(String::new())
// TODO: warn
)
);
print_with_internal_wrap!();
// TODO: warn
print!(
"{:?}{:?}",
// This is promoted; do not warn.
external_macros::wrap!(None::<String>),
// This does not promote; warn.
external_macros::wrap!(String::new())
// TODO: warn
);
external_macros::print_with_internal_wrap!();
// TODO: warn
}