diff --git a/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/auxiliary/external-macros.rs b/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/auxiliary/external-macros.rs new file mode 100644 index 000000000000..74717755ca33 --- /dev/null +++ b/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/auxiliary/external-macros.rs @@ -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())) } +} diff --git a/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/macro-extended-temporary-scopes.rs b/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/macro-extended-temporary-scopes.rs new file mode 100644 index 000000000000..0034b57c92e1 --- /dev/null +++ b/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/macro-extended-temporary-scopes.rs @@ -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:: }); + 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() }); +} diff --git a/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/non-extended.rs b/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/non-extended.rs new file mode 100644 index 000000000000..5b5a43c94735 --- /dev/null +++ b/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/non-extended.rs @@ -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 +} diff --git a/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/non-extended.stderr b/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/non-extended.stderr new file mode 100644 index 000000000000..6ea3dcacaefe --- /dev/null +++ b/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/non-extended.stderr @@ -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`. diff --git a/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/user-defined-macros.rs b/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/user-defined-macros.rs new file mode 100644 index 000000000000..24d12cf0d55f --- /dev/null +++ b/tests/ui/lifetimes/lint-macro-extended-temporary-scopes/user-defined-macros.rs @@ -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::), + + // 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::), + + // This does not promote; warn. + external_macros::wrap!(String::new()) + // TODO: warn + ); + + external_macros::print_with_internal_wrap!(); + // TODO: warn +}