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 } ;
29
310// Diagnostic: missing-unsafe
411//
@@ -9,11 +16,60 @@ pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsaf
916 "this operation is unsafe and requires an unsafe function or block" ,
1017 ctx. sema . diagnostics_display_range ( d. expr . clone ( ) . map ( |it| it. into ( ) ) ) . range ,
1118 )
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
1268}
1369
1470#[ cfg( test) ]
1571mod tests {
16- use crate :: tests:: check_diagnostics;
72+ use crate :: tests:: { check_diagnostics, check_fix } ;
1773
1874 #[ test]
1975 fn missing_unsafe_diagnostic_with_raw_ptr ( ) {
@@ -23,7 +79,7 @@ fn main() {
2379 let x = &5 as *const usize;
2480 unsafe { let y = *x; }
2581 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
2783"# ,
2884 )
2985 }
@@ -48,9 +104,9 @@ unsafe fn unsafe_fn() {
48104
49105fn main() {
50106 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
52108 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
54110 unsafe {
55111 unsafe_fn();
56112 HasUnsafe.unsafe_fn();
@@ -72,7 +128,7 @@ static mut STATIC_MUT: Ty = Ty { a: 0 };
72128
73129fn main() {
74130 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
76132 unsafe {
77133 let x = STATIC_MUT.a;
78134 }
@@ -94,9 +150,135 @@ extern "rust-intrinsic" {
94150fn main() {
95151 let _ = bitreverse(12);
96152 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
98154}
99155"# ,
100156 ) ;
101157 }
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+ }
102284}
0 commit comments