Skip to content

Commit a2c1041

Browse files
committed
instantly_dangling_pointer: test for temporaries & fix some false positives
1 parent 7acff86 commit a2c1041

File tree

3 files changed

+253
-26
lines changed

3 files changed

+253
-26
lines changed

compiler/rustc_lint/src/dangling.rs

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -100,49 +100,48 @@ fn lint_cstring_as_ptr(cx: &LateContext<'_>, source: &rustc_hir::Expr<'_>) -> bo
100100

101101
fn is_temporary_rvalue(expr: &Expr<'_>) -> bool {
102102
match expr.kind {
103-
// We are not interested in these
104-
ExprKind::Cast(_, _) | ExprKind::Closure(_) | ExprKind::Tup(_) => false,
105-
106103
// Const is not temporary.
107-
ExprKind::ConstBlock(_) | ExprKind::Repeat(_, _) => false,
104+
ExprKind::ConstBlock(..) | ExprKind::Repeat(..) | ExprKind::Lit(..) => false,
108105

109106
// This is literally lvalue.
110-
ExprKind::Path(_) => false,
107+
ExprKind::Path(..) => false,
111108

112109
// Calls return rvalues.
113-
ExprKind::Call(_, _) | ExprKind::MethodCall(_, _, _, _) | ExprKind::Binary(_, _, _) => true,
114-
115-
// Produces lvalue.
116-
ExprKind::Unary(_, _) | ExprKind::Index(_, _, _) => false,
110+
ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) => true,
117111

112+
// FIXME: this is probably wrong.
118113
// Inner blocks are rvalues.
119-
ExprKind::If(_, _, _)
120-
| ExprKind::Loop(_, _, _, _)
121-
| ExprKind::Match(_, _, _)
122-
| ExprKind::Block(_, _) => true,
114+
ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) | ExprKind::Block(..) => true,
115+
116+
// FIXME: these should probably recurse and typecheck along the way.
117+
// Some false negatives are possible for now.
118+
ExprKind::Index(..) | ExprKind::Field(..) | ExprKind::Unary(..) => false,
123119

124-
ExprKind::DropTemps(inner) => is_temporary_rvalue(inner),
125-
ExprKind::Field(parent, _) => is_temporary_rvalue(parent),
120+
ExprKind::Struct(..) => true,
126121

127-
ExprKind::Struct(_, _, _) => true,
128-
// These are 'static
129-
ExprKind::Lit(_) => false,
130-
// FIXME: False negatives are possible, but arrays get promoted to 'static way too often.
131-
ExprKind::Array(_) => false,
122+
// FIXME: this has false negatives, but I do not want to deal with 'static/const promotion just yet.
123+
ExprKind::Array(..) => false,
132124

133125
// These typecheck to `!`
134-
ExprKind::Break(_, _) | ExprKind::Continue(_) | ExprKind::Ret(_) | ExprKind::Become(_) => {
126+
ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) | ExprKind::Become(..) => {
135127
false
136128
}
137129

138130
// These typecheck to `()`
139-
ExprKind::Assign(_, _, _) | ExprKind::AssignOp(_, _, _) | ExprKind::Yield(_, _) => false,
131+
ExprKind::Assign(..) | ExprKind::AssignOp(..) | ExprKind::Yield(..) => false,
140132

141-
// Not applicable
142-
ExprKind::Type(_, _) | ExprKind::Err(_) | ExprKind::Let(_) => false,
133+
// Compiler-magic macros
134+
ExprKind::AddrOf(..) | ExprKind::OffsetOf(..) | ExprKind::InlineAsm(..) => false,
135+
136+
// We are not interested in these
137+
ExprKind::Cast(..)
138+
| ExprKind::Closure(..)
139+
| ExprKind::Tup(..)
140+
| ExprKind::DropTemps(..)
141+
| ExprKind::Let(..) => false,
143142

144-
// These are compiler-magic macros
145-
ExprKind::AddrOf(_, _, _) | ExprKind::OffsetOf(_, _) | ExprKind::InlineAsm(_) => false,
143+
// Not applicable
144+
ExprKind::Type(..) | ExprKind::Err(..) => false,
146145
}
147146
}
148147

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#![allow(unused)]
2+
#![deny(instantly_dangling_pointer)]
3+
4+
fn string() -> String {
5+
"hello".into()
6+
}
7+
8+
struct Wrapper(String);
9+
10+
fn main() {
11+
// ConstBlock
12+
const { String::new() }.as_ptr();
13+
14+
// Array
15+
{
16+
[string()].as_ptr(); // False negative
17+
[true].as_ptr();
18+
}
19+
20+
// Call
21+
string().as_ptr(); //~ ERROR [instantly_dangling_pointer]
22+
23+
// MethodCall
24+
"hello".to_string().as_ptr(); //~ ERROR [instantly_dangling_pointer]
25+
26+
// Tup
27+
// impossible
28+
29+
// Binary
30+
(string() + "hello").as_ptr(); //~ ERROR [instantly_dangling_pointer]
31+
32+
// Path
33+
{
34+
let x = string();
35+
x.as_ptr();
36+
}
37+
38+
// Unary
39+
{
40+
let x = string();
41+
let x: &String = &x;
42+
(*x).as_ptr();
43+
(&[0u8]).as_ptr();
44+
(&string()).as_ptr(); // False negative
45+
(*&string()).as_ptr(); // False negative
46+
}
47+
48+
// Lit
49+
"hello".as_ptr();
50+
51+
// Cast
52+
// impossible
53+
54+
// Type
55+
// impossible
56+
57+
// DropTemps
58+
// impossible
59+
60+
// Let
61+
// impossible
62+
63+
// If
64+
{
65+
(if true { String::new() } else { "hello".into() }).as_ptr();
66+
//~^ ERROR [instantly_dangling_pointer]
67+
}
68+
69+
// Loop
70+
{
71+
(loop {
72+
break String::new();
73+
})
74+
.as_ptr(); //~ ERROR [instantly_dangling_pointer]
75+
}
76+
77+
// Match
78+
{
79+
match string() {
80+
s => s,
81+
}
82+
.as_ptr(); //~ ERROR [instantly_dangling_pointer]
83+
}
84+
85+
// Closure
86+
// impossible
87+
88+
// Block
89+
{ string() }.as_ptr(); //~ ERROR [instantly_dangling_pointer]
90+
91+
// Assign, AssignOp
92+
// impossible
93+
94+
// Field
95+
{
96+
Wrapper(string()).0.as_ptr(); // False negative
97+
let x = Wrapper(string());
98+
x.0.as_ptr();
99+
}
100+
101+
// Index
102+
{
103+
vec![string()][0].as_ptr(); // False negative
104+
let x = vec![string()];
105+
x[0].as_ptr();
106+
}
107+
108+
// AddrOf, InlineAsm, OffsetOf
109+
// impossible
110+
111+
// Break, Continue, Ret
112+
// are !
113+
114+
// Become, Yield
115+
// unstable, are !
116+
117+
// Repeat
118+
[0u8; 100].as_ptr();
119+
[const { String::new() }; 100].as_ptr();
120+
121+
// Struct
122+
// Cannot test this without access to private fields of the linted types.
123+
124+
// Err
125+
// impossible
126+
127+
// Macro
128+
vec![0u8].as_ptr(); //~ ERROR [instantly_dangling_pointer]
129+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
error: getting a pointer from a temporary `String` will result in a dangling pointer
2+
--> $DIR/instantly-dangling-pointer-temporaries.rs:21:14
3+
|
4+
LL | string().as_ptr();
5+
| -------- ^^^^^^ this pointer will immediately be invalid
6+
| |
7+
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
8+
|
9+
= 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
10+
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html
11+
note: the lint level is defined here
12+
--> $DIR/instantly-dangling-pointer-temporaries.rs:2:9
13+
|
14+
LL | #![deny(instantly_dangling_pointer)]
15+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
16+
17+
error: getting a pointer from a temporary `String` will result in a dangling pointer
18+
--> $DIR/instantly-dangling-pointer-temporaries.rs:24:25
19+
|
20+
LL | "hello".to_string().as_ptr();
21+
| ------------------- ^^^^^^ this pointer will immediately be invalid
22+
| |
23+
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
24+
|
25+
= 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
26+
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html
27+
28+
error: getting a pointer from a temporary `String` will result in a dangling pointer
29+
--> $DIR/instantly-dangling-pointer-temporaries.rs:30:26
30+
|
31+
LL | (string() + "hello").as_ptr();
32+
| -------------------- ^^^^^^ this pointer will immediately be invalid
33+
| |
34+
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
35+
|
36+
= 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
37+
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html
38+
39+
error: getting a pointer from a temporary `String` will result in a dangling pointer
40+
--> $DIR/instantly-dangling-pointer-temporaries.rs:65:61
41+
|
42+
LL | (if true { String::new() } else { "hello".into() }).as_ptr();
43+
| --------------------------------------------------- ^^^^^^ this pointer will immediately be invalid
44+
| |
45+
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
46+
|
47+
= 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
48+
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html
49+
50+
error: getting a pointer from a temporary `String` will result in a dangling pointer
51+
--> $DIR/instantly-dangling-pointer-temporaries.rs:74:10
52+
|
53+
LL | / (loop {
54+
LL | | break String::new();
55+
LL | | })
56+
| |__________- this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
57+
LL | .as_ptr();
58+
| ^^^^^^ this pointer will immediately be invalid
59+
|
60+
= 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
61+
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html
62+
63+
error: getting a pointer from a temporary `String` will result in a dangling pointer
64+
--> $DIR/instantly-dangling-pointer-temporaries.rs:82:10
65+
|
66+
LL | / match string() {
67+
LL | | s => s,
68+
LL | | }
69+
| |_________- this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
70+
LL | .as_ptr();
71+
| ^^^^^^ this pointer will immediately be invalid
72+
|
73+
= 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
74+
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html
75+
76+
error: getting a pointer from a temporary `String` will result in a dangling pointer
77+
--> $DIR/instantly-dangling-pointer-temporaries.rs:89:18
78+
|
79+
LL | { string() }.as_ptr();
80+
| ------------ ^^^^^^ this pointer will immediately be invalid
81+
| |
82+
| this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
83+
|
84+
= 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
85+
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html
86+
87+
error: getting a pointer from a temporary `Vec<u8>` will result in a dangling pointer
88+
--> $DIR/instantly-dangling-pointer-temporaries.rs:128:15
89+
|
90+
LL | vec![0u8].as_ptr();
91+
| --------- ^^^^^^ this pointer will immediately be invalid
92+
| |
93+
| this `Vec<u8>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
94+
|
95+
= 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
96+
= help: for more information, see https://doc.rust-lang.org/reference/destructors.html
97+
98+
error: aborting due to 8 previous errors
99+

0 commit comments

Comments
 (0)