aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/diagnostics.rs24
-rw-r--r--crates/hir/src/lib.rs20
-rw-r--r--crates/hir_ty/src/diagnostics.rs31
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs148
-rw-r--r--crates/hir_ty/src/infer.rs4
-rw-r--r--crates/ide/src/diagnostics.rs84
6 files changed, 145 insertions, 166 deletions
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 2edb53765..e888fc23b 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -301,3 +301,27 @@ impl Diagnostic for BreakOutsideOfLoop {
301 self 301 self
302 } 302 }
303} 303}
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)]
309pub struct MissingUnsafe {
310 pub file: HirFileId,
311 pub expr: AstPtr<ast::Expr>,
312}
313
314impl 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}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index bce626b03..a21a9da21 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -36,14 +36,14 @@ use std::{iter, sync::Arc};
36use arrayvec::ArrayVec; 36use arrayvec::ArrayVec;
37use base_db::{CrateDisplayName, CrateId, Edition, FileId}; 37use base_db::{CrateDisplayName, CrateId, Edition, FileId};
38use diagnostics::{ 38use diagnostics::{
39 BreakOutsideOfLoop, InactiveCode, MacroError, NoSuchField, UnimplementedBuiltinMacro, 39 BreakOutsideOfLoop, InactiveCode, MacroError, MissingUnsafe, NoSuchField,
40 UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall, UnresolvedModule, 40 UnimplementedBuiltinMacro, UnresolvedExternCrate, UnresolvedImport, UnresolvedMacroCall,
41 UnresolvedProcMacro, 41 UnresolvedModule, UnresolvedProcMacro,
42}; 42};
43use either::Either; 43use either::Either;
44use hir_def::{ 44use hir_def::{
45 adt::{ReprKind, VariantData}, 45 adt::{ReprKind, VariantData},
46 body::BodyDiagnostic, 46 body::{BodyDiagnostic, SyntheticSyntax},
47 expr::{BindingAnnotation, LabelId, Pat, PatId}, 47 expr::{BindingAnnotation, LabelId, Pat, PatId},
48 item_tree::ItemTreeNode, 48 item_tree::ItemTreeNode,
49 lang_item::LangItemTarget, 49 lang_item::LangItemTarget,
@@ -1060,6 +1060,18 @@ impl Function {
1060 } 1060 }
1061 } 1061 }
1062 1062
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
1063 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); 1075 hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink);
1064 hir_ty::diagnostics::validate_body(db, self.id.into(), sink); 1076 hir_ty::diagnostics::validate_body(db, self.id.into(), sink);
1065 } 1077 }
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index 28d6ca567..12131d9bc 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -17,7 +17,10 @@ use crate::{
17 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink}, 17 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
18}; 18};
19 19
20pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; 20pub use crate::diagnostics::{
21 expr::{record_literal_missing_fields, record_pattern_missing_fields},
22 unsafe_check::missing_unsafe,
23};
21 24
22pub fn validate_module_item( 25pub fn validate_module_item(
23 db: &dyn HirDatabase, 26 db: &dyn HirDatabase,
@@ -35,8 +38,6 @@ pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut Diag
35 let infer = db.infer(owner); 38 let infer = db.infer(owner);
36 let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink); 39 let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink);
37 validator.validate_body(db); 40 validator.validate_body(db);
38 let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink);
39 validator.validate_body(db);
40} 41}
41 42
42// Diagnostic: missing-structure-fields 43// Diagnostic: missing-structure-fields
@@ -219,30 +220,6 @@ impl Diagnostic for RemoveThisSemicolon {
219 } 220 }
220} 221}
221 222
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)]
226pub struct MissingUnsafe {
227 pub file: HirFileId,
228 pub expr: AstPtr<ast::Expr>,
229}
230
231impl 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
246// Diagnostic: mismatched-arg-count 223// Diagnostic: mismatched-arg-count
247// 224//
248// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. 225// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs
index c3c483425..a4054cef9 100644
--- a/crates/hir_ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs
@@ -1,8 +1,6 @@
1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing 1//! Provides validations for unsafe code. Currently checks if unsafe functions are missing
2//! unsafe blocks. 2//! unsafe blocks.
3 3
4use std::sync::Arc;
5
6use hir_def::{ 4use hir_def::{
7 body::Body, 5 body::Body,
8 expr::{Expr, ExprId, UnaryOp}, 6 expr::{Expr, ExprId, UnaryOp},
@@ -10,52 +8,25 @@ use hir_def::{
10 DefWithBodyId, 8 DefWithBodyId,
11}; 9};
12 10
13use crate::{ 11use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind};
14 db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult,
15 Interner, TyExt, TyKind,
16};
17 12
18pub(super) struct UnsafeValidator<'a, 'b: 'a> { 13pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
19 owner: DefWithBodyId, 14 let infer = db.infer(def);
20 infer: Arc<InferenceResult>,
21 sink: &'a mut DiagnosticSink<'b>,
22}
23 15
24impl<'a, 'b> UnsafeValidator<'a, 'b> { 16 // let unsafe_expressions = ;
25 pub(super) fn new( 17 let is_unsafe = match def {
26 owner: DefWithBodyId, 18 DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
27 infer: Arc<InferenceResult>, 19 DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
28 sink: &'a mut DiagnosticSink<'b>, 20 };
29 ) -> UnsafeValidator<'a, 'b> { 21 if is_unsafe {
30 UnsafeValidator { owner, infer, sink } 22 return Vec::new();
31 } 23 }
32 24
33 pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { 25 unsafe_expressions(db, &infer, def)
34 let def = self.owner; 26 .into_iter()
35 let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); 27 .filter(|it| !it.inside_unsafe_block)
36 let is_unsafe = match self.owner { 28 .map(|it| it.expr)
37 DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), 29 .collect()
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 }
59} 30}
60 31
61pub(crate) struct UnsafeExpr { 32pub(crate) struct UnsafeExpr {
@@ -126,92 +97,3 @@ fn walk_unsafe(
126 walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); 97 walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block);
127 }); 98 });
128} 99}
129
130#[cfg(test)]
131mod tests {
132 use crate::diagnostics::tests::check_diagnostics;
133
134 #[test]
135 fn missing_unsafe_diagnostic_with_raw_ptr() {
136 check_diagnostics(
137 r#"
138fn 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#"
151struct HasUnsafe;
152
153impl HasUnsafe {
154 unsafe fn unsafe_fn(&self) {
155 let x = &5 as *const usize;
156 let y = *x;
157 }
158}
159
160unsafe fn unsafe_fn() {
161 let x = &5 as *const usize;
162 let y = *x;
163}
164
165fn 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#"
183struct Ty {
184 a: u8,
185}
186
187static mut STATIC_MUT: Ty = Ty { a: 0 };
188
189fn 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#"
204extern "rust-intrinsic" {
205 pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
206 pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
207}
208
209fn 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}
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 7b57593e4..2c667da25 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -36,8 +36,8 @@ use syntax::SmolStr;
36 36
37use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; 37use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
38use crate::{ 38use crate::{
39 db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, 39 db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy,
40 to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, TyBuilder, TyExt, TyKind, 40 Goal, Interner, Substitution, TyBuilder, TyExt, TyKind,
41}; 41};
42 42
43// This lint has a false positive here. See the link below for details. 43// This lint has a false positive here. See the link below for details.
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 7febd6eaf..b791747c2 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -858,4 +858,88 @@ impl Foo {
858"#, 858"#,
859 ); 859 );
860 } 860 }
861
862 #[test]
863 fn missing_unsafe_diagnostic_with_raw_ptr() {
864 check_diagnostics(
865 r#"
866fn 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#"
879struct HasUnsafe;
880
881impl HasUnsafe {
882 unsafe fn unsafe_fn(&self) {
883 let x = &5 as *const usize;
884 let y = *x;
885 }
886}
887
888unsafe fn unsafe_fn() {
889 let x = &5 as *const usize;
890 let y = *x;
891}
892
893fn 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#"
911struct Ty {
912 a: u8,
913}
914
915static mut STATIC_MUT: Ty = Ty { a: 0 };
916
917fn 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#"
932extern "rust-intrinsic" {
933 pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
934 pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
935}
936
937fn main() {
938 let _ = bitreverse(12);
939 let _ = floorf32(12.0);
940 //^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block
941}
942"#,
943 );
944 }
861} 945}