diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/hir/src/diagnostics.rs | 24 | ||||
-rw-r--r-- | crates/hir/src/lib.rs | 20 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics.rs | 31 | ||||
-rw-r--r-- | crates/hir_ty/src/diagnostics/unsafe_check.rs | 148 | ||||
-rw-r--r-- | crates/hir_ty/src/infer.rs | 4 | ||||
-rw-r--r-- | crates/ide/src/diagnostics.rs | 84 |
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)] | ||
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 | } | ||
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}; | |||
36 | use arrayvec::ArrayVec; | 36 | use arrayvec::ArrayVec; |
37 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; | 37 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; |
38 | use diagnostics::{ | 38 | use 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 | }; |
43 | use either::Either; | 43 | use either::Either; |
44 | use hir_def::{ | 44 | use 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 | ||
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 | }; | ||
21 | 24 | ||
22 | pub fn validate_module_item( | 25 | pub 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)] | ||
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 | |||
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 | ||
4 | use std::sync::Arc; | ||
5 | |||
6 | use hir_def::{ | 4 | use 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 | ||
13 | use crate::{ | 11 | use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind}; |
14 | db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult, | ||
15 | Interner, TyExt, TyKind, | ||
16 | }; | ||
17 | 12 | ||
18 | pub(super) struct UnsafeValidator<'a, 'b: 'a> { | 13 | pub 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 | ||
24 | impl<'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 | ||
61 | pub(crate) struct UnsafeExpr { | 32 | pub(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)] | ||
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 | } | ||
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 | ||
37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; | 37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; |
38 | use crate::{ | 38 | use 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#" | ||
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 | } | ||
942 | "#, | ||
943 | ); | ||
944 | } | ||
861 | } | 945 | } |