forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Auto merge of rust-lang#128985 - GrigorenkoPV:instantly-dangling-poin…
…ter, r=<try> Lint against getting pointers from immediately dropped temporaries Fixes rust-lang#123613 ## Changes: 1. New lint: `dangling_pointers_from_temporaries`. Is a generalization of `temporary_cstring_as_ptr` for more types and more ways to get a temporary. 2. `temporary_cstring_as_ptr` is marked as renamed to `dangling_pointers_from_temporaries`. 3. `clippy::temporary_cstring_as_ptr` is marked as renamed to `dangling_pointers_from_temporaries`. 4. `core::cell::Cell` is now `rustc_diagnostic_item = "Cell"` ## TODO: - [x] ~Add tests for different types~ - [x] ~Add tests for different ways of getting a temporary~ - [x] ~Regroup tests for this and `temporary_cstring_as_ptr`~ - [x] ~Fix a strange ICE when the lint is `allow`ed.~ - [x] ~Check what happens when you `break` with a bound variable from `loop`/`match`/`if`/`block`.~ - [x] ~Document the lint~ - [x] ~Fix tests rust-lang#128985 (comment) - [x] ~Fix clippy~ - [x] ~Fix miri rust-lang#128985 (review) - [ ] Crater run? ## Questions: - [ ] Should we make something like `is_temporary_rvalue` (but not bogus) available in compiler? - [x] ~Should `temporary_cstring_as_ptr` be deprecated? Across an edition boundary?~ - [ ] Instead of manually checking for a list of known methods, maybe add some sort of annotation to those methods in library and check for the presence of that annotation? - [ ] Maybe even introduce some form of primitive lifetimes for pointers and check those in borrow-checker? ## Future improvements: - [ ] Fix false negatives[^fn] - [ ] Add suggestions rust-lang#128985 (comment) ## Known limitations: ### False negatives[^fn]: [^fn]: lint **should** be emitted, but **is not** - `temporary_unsafe_cell.get()` - `temporary.field.as_ptr()` - `temporary[index].as_ptr()` - Converting `&temporary` to pointer with `as` or stuff like `ptr::from_ref`. - The `&raw [mut] temporary` ### False positives[^fp]: [^fp]: lint **should not** be emitted, but **is** Both this lint and the already existing `temporary_cstring_as_ptr` will fire on code like this: ```rust foo(CString::new("hello").unwrap().as_ptr()) ``` even though using the resulting pointer inside of `foo` is completely fine (at least according to miri), probably due to argument promotion logic.
Showing
26 changed files
with
765 additions
and
114 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
use rustc_hir::{Expr, ExprKind, LangItem}; | ||
use rustc_middle::ty::{Ty, TyCtxt}; | ||
use rustc_session::{declare_lint, declare_lint_pass}; | ||
use rustc_span::symbol::sym; | ||
|
||
use crate::lints::InstantlyDangling; | ||
use crate::{LateContext, LateLintPass, LintContext}; | ||
|
||
// FIXME: does not catch UnsafeCell::get | ||
// FIXME: does not catch getting a ref to a temporary and then converting it to a ptr | ||
declare_lint! { | ||
/// The `dangling_pointers_from_temporaries` lint detects getting a pointer to data | ||
/// of a temporary that will immediately get dropped. | ||
/// | ||
/// ### Example | ||
/// | ||
/// ```rust | ||
/// # #![allow(unused)] | ||
/// # unsafe fn use_data(ptr: *const u8) { | ||
/// # dbg!(unsafe { ptr.read() }); | ||
/// # } | ||
/// fn gather_and_use(bytes: impl Iterator<Item = u8>) { | ||
/// let x: *const u8 = bytes.collect::<Vec<u8>>().as_ptr(); | ||
/// unsafe { use_data(x) } | ||
/// } | ||
/// ``` | ||
/// | ||
/// {{produces}} | ||
/// | ||
/// ### Explanation | ||
/// | ||
/// Getting a pointer from a temporary value will not prolong its lifetime, | ||
/// which means that the value can be dropped and the allocation freed | ||
/// while the pointer still exists, making the pointer dangling. | ||
/// This is not an error (as far as the type system is concerned) | ||
/// but probably is not what the user intended either. | ||
/// | ||
/// If you need stronger guarantees, consider using references instead, | ||
/// as they are statically verified by the borrow-checker to never dangle. | ||
pub DANGLING_POINTERS_FROM_TEMPORARIES, | ||
Warn, | ||
"detects getting a pointer from a temporary" | ||
} | ||
|
||
declare_lint_pass!(DanglingPointers => [DANGLING_POINTERS_FROM_TEMPORARIES]); | ||
|
||
impl<'tcx> LateLintPass<'tcx> for DanglingPointers { | ||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | ||
if let ExprKind::MethodCall(method, receiver, _args, _span) = expr.kind | ||
&& matches!(method.ident.name, sym::as_ptr | sym::as_mut_ptr) | ||
&& is_temporary_rvalue(receiver) | ||
&& let ty = cx.typeck_results().expr_ty(receiver) | ||
&& is_interesting(cx.tcx, ty) | ||
{ | ||
cx.emit_span_lint( | ||
DANGLING_POINTERS_FROM_TEMPORARIES, | ||
method.ident.span, | ||
InstantlyDangling { | ||
callee: method.ident.name, | ||
ty, | ||
ptr_span: method.ident.span, | ||
temporary_span: receiver.span, | ||
}, | ||
) | ||
} | ||
} | ||
} | ||
|
||
fn is_temporary_rvalue(expr: &Expr<'_>) -> bool { | ||
match expr.kind { | ||
// Const is not temporary. | ||
ExprKind::ConstBlock(..) | ExprKind::Repeat(..) | ExprKind::Lit(..) => false, | ||
|
||
// This is literally lvalue. | ||
ExprKind::Path(..) => false, | ||
|
||
// Calls return rvalues. | ||
ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) => true, | ||
|
||
// Inner blocks are rvalues. | ||
ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::Block(..) => true, | ||
|
||
// FIXME: these should probably recurse and typecheck along the way. | ||
// Some false negatives are possible for now. | ||
ExprKind::Index(..) | ExprKind::Field(..) | ExprKind::Unary(..) => false, | ||
|
||
ExprKind::Struct(..) => true, | ||
|
||
// FIXME: this has false negatives, but I do not want to deal with 'static/const promotion just yet. | ||
ExprKind::Array(..) => false, | ||
|
||
// These typecheck to `!` | ||
ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) | ExprKind::Become(..) => { | ||
false | ||
} | ||
|
||
// These typecheck to `()` | ||
ExprKind::Assign(..) | ExprKind::AssignOp(..) | ExprKind::Yield(..) => false, | ||
|
||
// Compiler-magic macros | ||
ExprKind::AddrOf(..) | ExprKind::OffsetOf(..) | ExprKind::InlineAsm(..) => false, | ||
|
||
// We are not interested in these | ||
ExprKind::Cast(..) | ||
| ExprKind::Closure(..) | ||
| ExprKind::Tup(..) | ||
| ExprKind::DropTemps(..) | ||
| ExprKind::Let(..) => false, | ||
|
||
// Not applicable | ||
ExprKind::Type(..) | ExprKind::Err(..) => false, | ||
} | ||
} | ||
|
||
// Array, Vec, String, CString, MaybeUninit, Cell, Box<[_]>, Box<str>, Box<CStr>, | ||
// or any of the above in arbitrary many nested Box'es. | ||
fn is_interesting(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { | ||
if ty.is_array() { | ||
true | ||
} else if ty.is_box() { | ||
let inner = ty.boxed_ty(); | ||
inner.is_slice() | ||
|| inner.is_str() | ||
|| inner.ty_adt_def().is_some_and(|def| tcx.is_lang_item(def.did(), LangItem::CStr)) | ||
|| is_interesting(tcx, inner) | ||
} else if let Some(def) = ty.ty_adt_def() { | ||
for lang_item in [LangItem::String, LangItem::MaybeUninit] { | ||
if tcx.is_lang_item(def.did(), lang_item) { | ||
return true; | ||
} | ||
} | ||
tcx.get_diagnostic_name(def.did()) | ||
.is_some_and(|name| matches!(name, sym::cstring_type | sym::Vec | sym::Cell)) | ||
} else { | ||
false | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
//@ check-pass | ||
|
||
// This should not ICE. | ||
|
||
#![allow(dangling_pointers_from_temporaries)] | ||
|
||
fn main() { | ||
dbg!(String::new().as_ptr()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 13 additions & 5 deletions
18
...nt/lint-temporary-cstring-as-param.stderr → ...-from-temporaries/cstring-as-param.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,26 @@ | ||
error: getting the inner pointer of a temporary `CString` | ||
--> $DIR/lint-temporary-cstring-as-param.rs:9:45 | ||
warning: lint `temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` | ||
--> $DIR/cstring-as-param.rs:1:9 | ||
| | ||
LL | #![deny(temporary_cstring_as_ptr)] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries` | ||
| | ||
= note: `#[warn(renamed_and_removed_lints)]` on by default | ||
|
||
error: getting a pointer from a temporary `CString` will result in a dangling pointer | ||
--> $DIR/cstring-as-param.rs:10:45 | ||
| | ||
LL | some_function(CString::new("").unwrap().as_ptr()); | ||
| ------------------------- ^^^^^^ this pointer will be invalid | ||
| ------------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
note: the lint level is defined here | ||
--> $DIR/lint-temporary-cstring-as-param.rs:1:9 | ||
--> $DIR/cstring-as-param.rs:1:9 | ||
| | ||
LL | #![deny(temporary_cstring_as_ptr)] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: aborting due to 1 previous error | ||
error: aborting due to 1 previous error; 1 warning emitted | ||
|
5 changes: 3 additions & 2 deletions
5
.../ui/lint/lint-temporary-cstring-as-ptr.rs → ...inters-from-temporaries/cstring-as-ptr.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,18 @@ | ||
// this program is not technically incorrect, but is an obscure enough style to be worth linting | ||
#![deny(temporary_cstring_as_ptr)] | ||
//~^ WARNING lint `temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries` | ||
|
||
use std::ffi::CString; | ||
|
||
macro_rules! mymacro { | ||
() => { | ||
let s = CString::new("some text").unwrap().as_ptr(); | ||
//~^ ERROR getting the inner pointer of a temporary `CString` | ||
//~^ ERROR getting a pointer from a temporary `CString` will result in a dangling pointer | ||
} | ||
} | ||
|
||
fn main() { | ||
let s = CString::new("some text").unwrap().as_ptr(); | ||
//~^ ERROR getting the inner pointer of a temporary `CString` | ||
//~^ ERROR getting a pointer from a temporary `CString` will result in a dangling pointer | ||
mymacro!(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#![deny(dangling_pointers_from_temporaries)] | ||
|
||
const MAX_PATH: usize = 260; | ||
fn main() { | ||
let str1 = String::with_capacity(MAX_PATH).as_mut_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `String` will result in a dangling pointer | ||
let str2 = String::from("TotototototototototototototototototoT").as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `String` will result in a dangling pointer | ||
unsafe { | ||
std::ptr::copy_nonoverlapping(str2, str1, 30); | ||
println!("{:?}", String::from_raw_parts(str1, 30, 30)); | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
error: getting a pointer from a temporary `String` will result in a dangling pointer | ||
--> $DIR/example-from-issue123613.rs:5:48 | ||
| | ||
LL | let str1 = String::with_capacity(MAX_PATH).as_mut_ptr(); | ||
| ------------------------------- ^^^^^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_mut_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
note: the lint level is defined here | ||
--> $DIR/example-from-issue123613.rs:1:9 | ||
| | ||
LL | #![deny(dangling_pointers_from_temporaries)] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: getting a pointer from a temporary `String` will result in a dangling pointer | ||
--> $DIR/example-from-issue123613.rs:7:70 | ||
| | ||
LL | let str2 = String::from("TotototototototototototototototototoT").as_ptr(); | ||
| ----------------------------------------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: aborting due to 2 previous errors | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#![deny(dangling_pointers_from_temporaries)] | ||
|
||
fn main() { | ||
vec![0u8].as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Vec<u8>` will result in a dangling pointer | ||
vec![0u8].as_mut_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Vec<u8>` will result in a dangling pointer | ||
} |
29 changes: 29 additions & 0 deletions
29
tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
error: getting a pointer from a temporary `Vec<u8>` will result in a dangling pointer | ||
--> $DIR/methods.rs:4:15 | ||
| | ||
LL | vec![0u8].as_ptr(); | ||
| --------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Vec<u8>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Vec<u8>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
note: the lint level is defined here | ||
--> $DIR/methods.rs:1:9 | ||
| | ||
LL | #![deny(dangling_pointers_from_temporaries)] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: getting a pointer from a temporary `Vec<u8>` will result in a dangling pointer | ||
--> $DIR/methods.rs:6:15 | ||
| | ||
LL | vec![0u8].as_mut_ptr(); | ||
| --------- ^^^^^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Vec<u8>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_mut_ptr` the `Vec<u8>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: aborting due to 2 previous errors | ||
|
136 changes: 136 additions & 0 deletions
136
tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
#![allow(unused)] | ||
#![deny(dangling_pointers_from_temporaries)] | ||
|
||
fn string() -> String { | ||
"hello".into() | ||
} | ||
|
||
struct Wrapper(String); | ||
|
||
fn main() { | ||
// ConstBlock | ||
const { String::new() }.as_ptr(); | ||
|
||
// Array | ||
{ | ||
[string()].as_ptr(); // False negative | ||
[true].as_ptr(); | ||
} | ||
|
||
// Call | ||
string().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `String` will result in a dangling pointer | ||
|
||
// MethodCall | ||
"hello".to_string().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `String` will result in a dangling pointer | ||
|
||
// Tup | ||
// impossible | ||
|
||
// Binary | ||
(string() + "hello").as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `String` will result in a dangling pointer | ||
|
||
// Path | ||
{ | ||
let x = string(); | ||
x.as_ptr(); | ||
} | ||
|
||
// Unary | ||
{ | ||
let x = string(); | ||
let x: &String = &x; | ||
(*x).as_ptr(); | ||
(&[0u8]).as_ptr(); | ||
(&string()).as_ptr(); // False negative | ||
(*&string()).as_ptr(); // False negative | ||
} | ||
|
||
// Lit | ||
"hello".as_ptr(); | ||
|
||
// Cast | ||
// impossible | ||
|
||
// Type | ||
// impossible | ||
|
||
// DropTemps | ||
// impossible | ||
|
||
// Let | ||
// impossible | ||
|
||
// If | ||
{ | ||
(if true { String::new() } else { "hello".into() }).as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `String` will result in a dangling pointer | ||
} | ||
|
||
// Loop | ||
{ | ||
(loop { | ||
break String::new(); | ||
}) | ||
.as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `String` will result in a dangling pointer | ||
} | ||
|
||
// Match | ||
{ | ||
match string() { | ||
s => s, | ||
} | ||
.as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `String` will result in a dangling pointer | ||
} | ||
|
||
// Closure | ||
// impossible | ||
|
||
// Block | ||
{ string() }.as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `String` will result in a dangling pointer | ||
|
||
// Assign, AssignOp | ||
// impossible | ||
|
||
// Field | ||
{ | ||
Wrapper(string()).0.as_ptr(); // False negative | ||
let x = Wrapper(string()); | ||
x.0.as_ptr(); | ||
} | ||
|
||
// Index | ||
{ | ||
vec![string()][0].as_ptr(); // False negative | ||
let x = vec![string()]; | ||
x[0].as_ptr(); | ||
} | ||
|
||
// AddrOf, InlineAsm, OffsetOf | ||
// impossible | ||
|
||
// Break, Continue, Ret | ||
// are ! | ||
|
||
// Become, Yield | ||
// unstable, are ! | ||
|
||
// Repeat | ||
[0u8; 100].as_ptr(); | ||
[const { String::new() }; 100].as_ptr(); | ||
|
||
// Struct | ||
// Cannot test this without access to private fields of the linted types. | ||
|
||
// Err | ||
// impossible | ||
|
||
// Macro | ||
vec![0u8].as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Vec<u8>` will result in a dangling pointer | ||
} |
99 changes: 99 additions & 0 deletions
99
tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
error: getting a pointer from a temporary `String` will result in a dangling pointer | ||
--> $DIR/temporaries.rs:21:14 | ||
| | ||
LL | string().as_ptr(); | ||
| -------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
note: the lint level is defined here | ||
--> $DIR/temporaries.rs:2:9 | ||
| | ||
LL | #![deny(dangling_pointers_from_temporaries)] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: getting a pointer from a temporary `String` will result in a dangling pointer | ||
--> $DIR/temporaries.rs:25:25 | ||
| | ||
LL | "hello".to_string().as_ptr(); | ||
| ------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `String` will result in a dangling pointer | ||
--> $DIR/temporaries.rs:32:26 | ||
| | ||
LL | (string() + "hello").as_ptr(); | ||
| -------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `String` will result in a dangling pointer | ||
--> $DIR/temporaries.rs:68:61 | ||
| | ||
LL | (if true { String::new() } else { "hello".into() }).as_ptr(); | ||
| --------------------------------------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `String` will result in a dangling pointer | ||
--> $DIR/temporaries.rs:77:10 | ||
| | ||
LL | / (loop { | ||
LL | | break String::new(); | ||
LL | | }) | ||
| |__________- this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
LL | .as_ptr(); | ||
| ^^^^^^ this pointer will immediately be invalid | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `String` will result in a dangling pointer | ||
--> $DIR/temporaries.rs:86:10 | ||
| | ||
LL | / match string() { | ||
LL | | s => s, | ||
LL | | } | ||
| |_________- this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
LL | .as_ptr(); | ||
| ^^^^^^ this pointer will immediately be invalid | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `String` will result in a dangling pointer | ||
--> $DIR/temporaries.rs:94:18 | ||
| | ||
LL | { string() }.as_ptr(); | ||
| ------------ ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `Vec<u8>` will result in a dangling pointer | ||
--> $DIR/temporaries.rs:134:15 | ||
| | ||
LL | vec![0u8].as_ptr(); | ||
| --------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Vec<u8>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Vec<u8>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: aborting due to 8 previous errors | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#![deny(dangling_pointers_from_temporaries)] | ||
|
||
use std::cell::Cell; | ||
use std::ffi::{CStr, CString}; | ||
use std::mem::MaybeUninit; | ||
|
||
struct AsPtrFake; | ||
|
||
impl AsPtrFake { | ||
fn as_ptr(&self) -> *const () { | ||
std::ptr::null() | ||
} | ||
} | ||
|
||
fn declval<T>() -> T { | ||
loop {} | ||
} | ||
|
||
fn main() { | ||
declval::<CString>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `CString` will result in a dangling pointer | ||
declval::<String>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `String` will result in a dangling pointer | ||
declval::<Vec<u8>>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Vec<u8>` will result in a dangling pointer | ||
declval::<Box<CString>>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Box<CString>` will result in a dangling pointer | ||
declval::<Box<[u8]>>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Box<[u8]>` will result in a dangling pointer | ||
declval::<Box<str>>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Box<str>` will result in a dangling pointer | ||
declval::<Box<CStr>>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Box<CStr>` will result in a dangling pointer | ||
declval::<[u8; 10]>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `[u8; 10]` will result in a dangling pointer | ||
declval::<Box<[u8; 10]>>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Box<[u8; 10]>` will result in a dangling pointer | ||
declval::<Box<Vec<u8>>>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Box<Vec<u8>>` will result in a dangling pointer | ||
declval::<Box<String>>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Box<String>` will result in a dangling pointer | ||
declval::<Box<Box<Box<Box<[u8]>>>>>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Box<Box<Box<Box<[u8]>>>>` will result in a dangling pointer | ||
declval::<Cell<u8>>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Cell<u8>` will result in a dangling pointer | ||
declval::<MaybeUninit<u8>>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `MaybeUninit<u8>` will result in a dangling pointer | ||
declval::<Vec<AsPtrFake>>().as_ptr(); | ||
//~^ ERROR getting a pointer from a temporary `Vec<AsPtrFake>` will result in a dangling pointer | ||
declval::<Box<AsPtrFake>>().as_ptr(); | ||
declval::<AsPtrFake>().as_ptr(); | ||
} |
172 changes: 172 additions & 0 deletions
172
tests/ui/lint/dangling-pointers-from-temporaries/types.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
error: getting a pointer from a temporary `CString` will result in a dangling pointer | ||
--> $DIR/types.rs:20:26 | ||
| | ||
LL | declval::<CString>().as_ptr(); | ||
| -------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
note: the lint level is defined here | ||
--> $DIR/types.rs:1:9 | ||
| | ||
LL | #![deny(dangling_pointers_from_temporaries)] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: getting a pointer from a temporary `String` will result in a dangling pointer | ||
--> $DIR/types.rs:22:25 | ||
| | ||
LL | declval::<String>().as_ptr(); | ||
| ------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `Vec<u8>` will result in a dangling pointer | ||
--> $DIR/types.rs:24:26 | ||
| | ||
LL | declval::<Vec<u8>>().as_ptr(); | ||
| -------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Vec<u8>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Vec<u8>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `Box<CString>` will result in a dangling pointer | ||
--> $DIR/types.rs:26:31 | ||
| | ||
LL | declval::<Box<CString>>().as_ptr(); | ||
| ------------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Box<CString>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Box<CString>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `Box<[u8]>` will result in a dangling pointer | ||
--> $DIR/types.rs:28:28 | ||
| | ||
LL | declval::<Box<[u8]>>().as_ptr(); | ||
| ---------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Box<[u8]>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Box<[u8]>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `Box<str>` will result in a dangling pointer | ||
--> $DIR/types.rs:30:27 | ||
| | ||
LL | declval::<Box<str>>().as_ptr(); | ||
| --------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Box<str>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Box<str>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `Box<CStr>` will result in a dangling pointer | ||
--> $DIR/types.rs:32:28 | ||
| | ||
LL | declval::<Box<CStr>>().as_ptr(); | ||
| ---------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Box<CStr>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Box<CStr>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `[u8; 10]` will result in a dangling pointer | ||
--> $DIR/types.rs:34:27 | ||
| | ||
LL | declval::<[u8; 10]>().as_ptr(); | ||
| --------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `[u8; 10]` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `[u8; 10]` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `Box<[u8; 10]>` will result in a dangling pointer | ||
--> $DIR/types.rs:36:32 | ||
| | ||
LL | declval::<Box<[u8; 10]>>().as_ptr(); | ||
| -------------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Box<[u8; 10]>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Box<[u8; 10]>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `Box<Vec<u8>>` will result in a dangling pointer | ||
--> $DIR/types.rs:38:31 | ||
| | ||
LL | declval::<Box<Vec<u8>>>().as_ptr(); | ||
| ------------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Box<Vec<u8>>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Box<Vec<u8>>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `Box<String>` will result in a dangling pointer | ||
--> $DIR/types.rs:40:30 | ||
| | ||
LL | declval::<Box<String>>().as_ptr(); | ||
| ------------------------ ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Box<String>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Box<String>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `Box<Box<Box<Box<[u8]>>>>` will result in a dangling pointer | ||
--> $DIR/types.rs:42:43 | ||
| | ||
LL | declval::<Box<Box<Box<Box<[u8]>>>>>().as_ptr(); | ||
| ------------------------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Box<Box<Box<Box<[u8]>>>>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Box<Box<Box<Box<[u8]>>>>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `Cell<u8>` will result in a dangling pointer | ||
--> $DIR/types.rs:44:27 | ||
| | ||
LL | declval::<Cell<u8>>().as_ptr(); | ||
| --------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Cell<u8>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Cell<u8>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `MaybeUninit<u8>` will result in a dangling pointer | ||
--> $DIR/types.rs:46:34 | ||
| | ||
LL | declval::<MaybeUninit<u8>>().as_ptr(); | ||
| ---------------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `MaybeUninit<u8>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `MaybeUninit<u8>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: getting a pointer from a temporary `Vec<AsPtrFake>` will result in a dangling pointer | ||
--> $DIR/types.rs:48:33 | ||
| | ||
LL | declval::<Vec<AsPtrFake>>().as_ptr(); | ||
| --------------------------- ^^^^^^ this pointer will immediately be invalid | ||
| | | ||
| this `Vec<AsPtrFake>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime | ||
| | ||
= note: pointers do not have a lifetime; when calling `as_ptr` the `Vec<AsPtrFake>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned | ||
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html | ||
|
||
error: aborting due to 15 previous errors | ||
|