1
- use crate :: { Diagnostic , DiagnosticsContext } ;
1
+ use hir:: db:: AstDatabase ;
2
+ use ide_db:: { assists:: Assist , source_change:: SourceChange } ;
3
+ use syntax:: ast:: { ExprStmt , LetStmt } ;
4
+ use syntax:: AstNode ;
5
+ use syntax:: { ast, SyntaxNode } ;
6
+ use text_edit:: TextEdit ;
7
+
8
+ use crate :: { fix, Diagnostic , DiagnosticsContext } ;
2
9
3
10
// Diagnostic: missing-unsafe
4
11
//
@@ -9,11 +16,60 @@ pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsaf
9
16
"this operation is unsafe and requires an unsafe function or block" ,
10
17
ctx. sema . diagnostics_display_range ( d. expr . clone ( ) . map ( |it| it. into ( ) ) ) . range ,
11
18
)
19
+ . with_fixes ( fixes ( ctx, d) )
20
+ }
21
+
22
+ fn fixes ( ctx : & DiagnosticsContext < ' _ > , d : & hir:: MissingUnsafe ) -> Option < Vec < Assist > > {
23
+ let root = ctx. sema . db . parse_or_expand ( d. expr . file_id ) ?;
24
+ let expr = d. expr . value . to_node ( & root) ;
25
+
26
+ let node_to_add_unsafe_block = pick_best_node_to_add_unsafe_block ( ctx, & expr) ;
27
+
28
+ let replacement = format ! ( "unsafe {{ {} }}" , node_to_add_unsafe_block. text( ) ) ;
29
+ let edit = TextEdit :: replace ( node_to_add_unsafe_block. text_range ( ) , replacement) ;
30
+ let source_change =
31
+ SourceChange :: from_text_edit ( d. expr . file_id . original_file ( ctx. sema . db ) , edit) ;
32
+ Some ( vec ! [ fix( "add_unsafe" , "Add unsafe block" , source_change, expr. syntax( ) . text_range( ) ) ] )
33
+ }
34
+
35
+ // Find the let statement or expression statement closest to the `expr` in the
36
+ // ancestor chain.
37
+ //
38
+ // Why don't we just add an unsafe block around the `expr`?
39
+ //
40
+ // Consider this example:
41
+ // ```
42
+ // STATIC_MUT += 1;
43
+ // ```
44
+ // We can't add an unsafe block to the left-hand side of an assignment.
45
+ // ```
46
+ // unsafe { STATIC_MUT } += 1;
47
+ // ```
48
+ //
49
+ // Or this example:
50
+ // ```
51
+ // let z = STATIC_MUT.a;
52
+ // ```
53
+ // We can't add an unsafe block like this:
54
+ // ```
55
+ // let z = unsafe { STATIC_MUT } .a;
56
+ // ```
57
+ fn pick_best_node_to_add_unsafe_block (
58
+ ctx : & DiagnosticsContext < ' _ > ,
59
+ expr : & ast:: Expr ,
60
+ ) -> SyntaxNode {
61
+ let Some ( let_or_expr_stmt) = ctx. sema . ancestors_with_macros ( expr. syntax ( ) . clone ( ) ) . find ( |node| {
62
+ LetStmt :: can_cast ( node. kind ( ) ) || ExprStmt :: can_cast ( node. kind ( ) )
63
+ } ) else {
64
+ // Is this reachable?
65
+ return expr. syntax ( ) . clone ( ) ;
66
+ } ;
67
+ let_or_expr_stmt
12
68
}
13
69
14
70
#[ cfg( test) ]
15
71
mod tests {
16
- use crate :: tests:: check_diagnostics;
72
+ use crate :: tests:: { check_diagnostics, check_fix } ;
17
73
18
74
#[ test]
19
75
fn missing_unsafe_diagnostic_with_raw_ptr ( ) {
@@ -23,7 +79,7 @@ fn main() {
23
79
let x = &5 as *const usize;
24
80
unsafe { let y = *x; }
25
81
let z = *x;
26
- } //^^ error: this operation is unsafe and requires an unsafe function or block
82
+ } //^^💡 error: this operation is unsafe and requires an unsafe function or block
27
83
"# ,
28
84
)
29
85
}
@@ -48,9 +104,9 @@ unsafe fn unsafe_fn() {
48
104
49
105
fn main() {
50
106
unsafe_fn();
51
- //^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block
107
+ //^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
52
108
HasUnsafe.unsafe_fn();
53
- //^^^^^^^^^^^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block
109
+ //^^^^^^^^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
54
110
unsafe {
55
111
unsafe_fn();
56
112
HasUnsafe.unsafe_fn();
@@ -72,7 +128,7 @@ static mut STATIC_MUT: Ty = Ty { a: 0 };
72
128
73
129
fn main() {
74
130
let x = STATIC_MUT.a;
75
- //^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block
131
+ //^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
76
132
unsafe {
77
133
let x = STATIC_MUT.a;
78
134
}
@@ -94,9 +150,135 @@ extern "rust-intrinsic" {
94
150
fn main() {
95
151
let _ = bitreverse(12);
96
152
let _ = floorf32(12.0);
97
- //^^^^^^^^^^^^^^ error: this operation is unsafe and requires an unsafe function or block
153
+ //^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
98
154
}
99
155
"# ,
100
156
) ;
101
157
}
158
+
159
+ #[ test]
160
+ fn add_unsafe_block_when_dereferencing_a_raw_pointer ( ) {
161
+ check_fix (
162
+ r#"
163
+ fn main() {
164
+ let x = &5 as *const usize;
165
+ let z = *x$0;
166
+ }
167
+ "# ,
168
+ r#"
169
+ fn main() {
170
+ let x = &5 as *const usize;
171
+ unsafe { let z = *x; }
172
+ }
173
+ "# ,
174
+ ) ;
175
+ }
176
+
177
+ #[ test]
178
+ fn add_unsafe_block_when_calling_unsafe_function ( ) {
179
+ check_fix (
180
+ r#"
181
+ unsafe fn func() {
182
+ let x = &5 as *const usize;
183
+ let z = *x;
184
+ }
185
+ fn main() {
186
+ func$0();
187
+ }
188
+ "# ,
189
+ r#"
190
+ unsafe fn func() {
191
+ let x = &5 as *const usize;
192
+ let z = *x;
193
+ }
194
+ fn main() {
195
+ unsafe { func(); }
196
+ }
197
+ "# ,
198
+ )
199
+ }
200
+
201
+ #[ test]
202
+ fn add_unsafe_block_when_calling_unsafe_method ( ) {
203
+ check_fix (
204
+ r#"
205
+ struct S(usize);
206
+ impl S {
207
+ unsafe fn func(&self) {
208
+ let x = &self.0 as *const usize;
209
+ let z = *x;
210
+ }
211
+ }
212
+ fn main() {
213
+ let s = S(5);
214
+ s.func$0();
215
+ }
216
+ "# ,
217
+ r#"
218
+ struct S(usize);
219
+ impl S {
220
+ unsafe fn func(&self) {
221
+ let x = &self.0 as *const usize;
222
+ let z = *x;
223
+ }
224
+ }
225
+ fn main() {
226
+ let s = S(5);
227
+ unsafe { s.func(); }
228
+ }
229
+ "# ,
230
+ )
231
+ }
232
+
233
+ #[ test]
234
+ fn add_unsafe_block_when_accessing_mutable_static ( ) {
235
+ check_fix (
236
+ r#"
237
+ struct Ty {
238
+ a: u8,
239
+ }
240
+
241
+ static mut STATIC_MUT: Ty = Ty { a: 0 };
242
+
243
+ fn main() {
244
+ let x = STATIC_MUT$0.a;
245
+ }
246
+ "# ,
247
+ r#"
248
+ struct Ty {
249
+ a: u8,
250
+ }
251
+
252
+ static mut STATIC_MUT: Ty = Ty { a: 0 };
253
+
254
+ fn main() {
255
+ unsafe { let x = STATIC_MUT.a; }
256
+ }
257
+ "# ,
258
+ )
259
+ }
260
+
261
+ #[ test]
262
+ fn add_unsafe_block_when_calling_unsafe_intrinsic ( ) {
263
+ check_fix (
264
+ r#"
265
+ extern "rust-intrinsic" {
266
+ pub fn floorf32(x: f32) -> f32;
267
+ }
268
+
269
+ fn main() {
270
+ let _ = floorf32$0(12.0);
271
+ }
272
+ "# ,
273
+ r#"
274
+ extern "rust-intrinsic" {
275
+ pub fn floorf32(x: f32) -> f32;
276
+ }
277
+
278
+ fn main() {
279
+ unsafe { let _ = floorf32(12.0); }
280
+ }
281
+ "# ,
282
+ )
283
+ }
102
284
}
0 commit comments