Skip to content

Commit 0413d51

Browse files
committed
internal: move missing unsafe diagnostic to hir
1 parent f800966 commit 0413d51

File tree

6 files changed

+145
-166
lines changed

6 files changed

+145
-166
lines changed

crates/hir/src/diagnostics.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,27 @@ impl Diagnostic for BreakOutsideOfLoop {
301301
self
302302
}
303303
}
304+
305+
// Diagnostic: missing-unsafe
306+
//
307+
// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
308+
#[derive(Debug)]
309+
pub struct MissingUnsafe {
310+
pub file: HirFileId,
311+
pub expr: AstPtr<ast::Expr>,
312+
}
313+
314+
impl Diagnostic for MissingUnsafe {
315+
fn code(&self) -> DiagnosticCode {
316+
DiagnosticCode("missing-unsafe")
317+
}
318+
fn message(&self) -> String {
319+
format!("This operation is unsafe and requires an unsafe function or block")
320+
}
321+
fn display_source(&self) -> InFile<SyntaxNodePtr> {
322+
InFile { file_id: self.file, value: self.expr.clone().into() }
323+
}
324+
fn as_any(&self) -> &(dyn Any + Send + 'static) {
325+
self
326+
}
327+
}

crates/hir/src/lib.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ use std::{iter, sync::Arc};
3636
use arrayvec::ArrayVec;
3737
use base_db::{CrateDisplayName, CrateId, Edition, FileId};
3838
use diagnostics::{
39-
BreakOutsideOfLoop, InactiveCode, MacroError, NoSuchField, UnimplementedBuiltinMacro,
40-
UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule,
41-
UnresolvedProcMacro,
39+
BreakOutsideOfLoop, InactiveCode, MacroError, MissingUnsafe, NoSuchField,
40+
UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
41+
UnresolvedModule, UnresolvedProcMacro,
4242
};
4343
use either::Either;
4444
use hir_def::{
4545
adt::{ReprKind, VariantData},
46-
body::BodyDiagnostic,
46+
body::{BodyDiagnostic, SyntheticSyntax},
4747
expr::{BindingAnnotation, LabelId, Pat, PatId},
4848
item_tree::ItemTreeNode,
4949
lang_item::LangItemTarget,
@@ -1060,6 +1060,18 @@ impl Function {
10601060
}
10611061
}
10621062

1063+
for expr in hir_ty::diagnostics::missing_unsafe(db, self.id.into()) {
1064+
match source_map.as_ref().expr_syntax(expr) {
1065+
Ok(in_file) => {
1066+
sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value })
1067+
}
1068+
Err(SyntheticSyntax) => {
1069+
// FIXME: The `expr` was desugared, report or assert that
1070+
// this dosen't happen.
1071+
}
1072+
}
1073+
}
1074+
10631075
hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink);
10641076
hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
10651077
}

crates/hir_ty/src/diagnostics.rs

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ use crate::{
1717
diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
1818
};
1919

20-
pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields};
20+
pub use crate::diagnostics::{
21+
expr::{record_literal_missing_fields, record_pattern_missing_fields},
22+
unsafe_check::missing_unsafe,
23+
};
2124

2225
pub fn validate_module_item(
2326
db: &dyn HirDatabase,
@@ -35,8 +38,6 @@ pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut Diag
3538
let infer = db.infer(owner);
3639
let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
3740
validator.validate_body(db);
38-
let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink);
39-
validator.validate_body(db);
4041
}
4142

4243
// Diagnostic: missing-structure-fields
@@ -219,30 +220,6 @@ impl Diagnostic for RemoveThisSemicolon {
219220
}
220221
}
221222

222-
// Diagnostic: missing-unsafe
223-
//
224-
// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
225-
#[derive(Debug)]
226-
pub struct MissingUnsafe {
227-
pub file: HirFileId,
228-
pub expr: AstPtr<ast::Expr>,
229-
}
230-
231-
impl Diagnostic for MissingUnsafe {
232-
fn code(&self) -> DiagnosticCode {
233-
DiagnosticCode("missing-unsafe")
234-
}
235-
fn message(&self) -> String {
236-
format!("This operation is unsafe and requires an unsafe function or block")
237-
}
238-
fn display_source(&self) -> InFile<SyntaxNodePtr> {
239-
InFile { file_id: self.file, value: self.expr.clone().into() }
240-
}
241-
fn as_any(&self) -> &(dyn Any + Send + 'static) {
242-
self
243-
}
244-
}
245-
246223
// Diagnostic: mismatched-arg-count
247224
//
248225
// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
Lines changed: 15 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,32 @@
11
//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
22
//! unsafe blocks.
33
4-
use std::sync::Arc;
5-
64
use hir_def::{
75
body::Body,
86
expr::{Expr, ExprId, UnaryOp},
97
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
108
DefWithBodyId,
119
};
1210

13-
use crate::{
14-
db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult,
15-
Interner, TyExt, TyKind,
16-
};
11+
use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind};
1712

18-
pub(super) struct UnsafeValidator<'a, 'b: 'a> {
19-
owner: DefWithBodyId,
20-
infer: Arc<InferenceResult>,
21-
sink: &'a mut DiagnosticSink<'b>,
22-
}
13+
pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
14+
let infer = db.infer(def);
2315

24-
impl<'a, 'b> UnsafeValidator<'a, 'b> {
25-
pub(super) fn new(
26-
owner: DefWithBodyId,
27-
infer: Arc<InferenceResult>,
28-
sink: &'a mut DiagnosticSink<'b>,
29-
) -> UnsafeValidator<'a, 'b> {
30-
UnsafeValidator { owner, infer, sink }
16+
// let unsafe_expressions = ;
17+
let is_unsafe = match def {
18+
DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
19+
DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
20+
};
21+
if is_unsafe {
22+
return Vec::new();
3123
}
3224

33-
pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) {
34-
let def = self.owner;
35-
let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def);
36-
let is_unsafe = match self.owner {
37-
DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
38-
DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
39-
};
40-
if is_unsafe
41-
|| unsafe_expressions
42-
.iter()
43-
.filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block)
44-
.count()
45-
== 0
46-
{
47-
return;
48-
}
49-
50-
let (_, body_source) = db.body_with_source_map(def);
51-
for unsafe_expr in unsafe_expressions {
52-
if !unsafe_expr.inside_unsafe_block {
53-
if let Ok(in_file) = body_source.as_ref().expr_syntax(unsafe_expr.expr) {
54-
self.sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value })
55-
}
56-
}
57-
}
58-
}
25+
unsafe_expressions(db, &infer, def)
26+
.into_iter()
27+
.filter(|it| !it.inside_unsafe_block)
28+
.map(|it| it.expr)
29+
.collect()
5930
}
6031

6132
pub(crate) struct UnsafeExpr {
@@ -126,92 +97,3 @@ fn walk_unsafe(
12697
walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block);
12798
});
12899
}
129-
130-
#[cfg(test)]
131-
mod tests {
132-
use crate::diagnostics::tests::check_diagnostics;
133-
134-
#[test]
135-
fn missing_unsafe_diagnostic_with_raw_ptr() {
136-
check_diagnostics(
137-
r#"
138-
fn main() {
139-
let x = &5 as *const usize;
140-
unsafe { let y = *x; }
141-
let z = *x;
142-
} //^^ This operation is unsafe and requires an unsafe function or block
143-
"#,
144-
)
145-
}
146-
147-
#[test]
148-
fn missing_unsafe_diagnostic_with_unsafe_call() {
149-
check_diagnostics(
150-
r#"
151-
struct HasUnsafe;
152-
153-
impl HasUnsafe {
154-
unsafe fn unsafe_fn(&self) {
155-
let x = &5 as *const usize;
156-
let y = *x;
157-
}
158-
}
159-
160-
unsafe fn unsafe_fn() {
161-
let x = &5 as *const usize;
162-
let y = *x;
163-
}
164-
165-
fn main() {
166-
unsafe_fn();
167-
//^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
168-
HasUnsafe.unsafe_fn();
169-
//^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
170-
unsafe {
171-
unsafe_fn();
172-
HasUnsafe.unsafe_fn();
173-
}
174-
}
175-
"#,
176-
);
177-
}
178-
179-
#[test]
180-
fn missing_unsafe_diagnostic_with_static_mut() {
181-
check_diagnostics(
182-
r#"
183-
struct Ty {
184-
a: u8,
185-
}
186-
187-
static mut STATIC_MUT: Ty = Ty { a: 0 };
188-
189-
fn main() {
190-
let x = STATIC_MUT.a;
191-
//^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
192-
unsafe {
193-
let x = STATIC_MUT.a;
194-
}
195-
}
196-
"#,
197-
);
198-
}
199-
200-
#[test]
201-
fn no_missing_unsafe_diagnostic_with_safe_intrinsic() {
202-
check_diagnostics(
203-
r#"
204-
extern "rust-intrinsic" {
205-
pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
206-
pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
207-
}
208-
209-
fn main() {
210-
let _ = bitreverse(12);
211-
let _ = floorf32(12.0);
212-
//^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
213-
}
214-
"#,
215-
);
216-
}
217-
}

crates/hir_ty/src/infer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ use syntax::SmolStr;
3636

3737
use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
3838
use crate::{
39-
db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode,
40-
to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, TyBuilder, TyExt, TyKind,
39+
db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy,
40+
Goal, Interner, Substitution, TyBuilder, TyExt, TyKind,
4141
};
4242

4343
// This lint has a false positive here. See the link below for details.

crates/ide/src/diagnostics.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,90 @@ impl Foo {
855855
Foo { bar: 0 }
856856
}
857857
}
858+
"#,
859+
);
860+
}
861+
862+
#[test]
863+
fn missing_unsafe_diagnostic_with_raw_ptr() {
864+
check_diagnostics(
865+
r#"
866+
fn main() {
867+
let x = &5 as *const usize;
868+
unsafe { let y = *x; }
869+
let z = *x;
870+
} //^^ This operation is unsafe and requires an unsafe function or block
871+
"#,
872+
)
873+
}
874+
875+
#[test]
876+
fn missing_unsafe_diagnostic_with_unsafe_call() {
877+
check_diagnostics(
878+
r#"
879+
struct HasUnsafe;
880+
881+
impl HasUnsafe {
882+
unsafe fn unsafe_fn(&self) {
883+
let x = &5 as *const usize;
884+
let y = *x;
885+
}
886+
}
887+
888+
unsafe fn unsafe_fn() {
889+
let x = &5 as *const usize;
890+
let y = *x;
891+
}
892+
893+
fn main() {
894+
unsafe_fn();
895+
//^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
896+
HasUnsafe.unsafe_fn();
897+
//^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
898+
unsafe {
899+
unsafe_fn();
900+
HasUnsafe.unsafe_fn();
901+
}
902+
}
903+
"#,
904+
);
905+
}
906+
907+
#[test]
908+
fn missing_unsafe_diagnostic_with_static_mut() {
909+
check_diagnostics(
910+
r#"
911+
struct Ty {
912+
a: u8,
913+
}
914+
915+
static mut STATIC_MUT: Ty = Ty { a: 0 };
916+
917+
fn main() {
918+
let x = STATIC_MUT.a;
919+
//^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
920+
unsafe {
921+
let x = STATIC_MUT.a;
922+
}
923+
}
924+
"#,
925+
);
926+
}
927+
928+
#[test]
929+
fn no_missing_unsafe_diagnostic_with_safe_intrinsic() {
930+
check_diagnostics(
931+
r#"
932+
extern "rust-intrinsic" {
933+
pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
934+
pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
935+
}
936+
937+
fn main() {
938+
let _ = bitreverse(12);
939+
let _ = floorf32(12.0);
940+
//^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
941+
}
858942
"#,
859943
);
860944
}

0 commit comments

Comments
 (0)