diff options
Diffstat (limited to 'crates/ra_hir_ty/src')
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics.rs | 21 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/expr.rs (renamed from crates/ra_hir_ty/src/expr.rs) | 30 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/match_check.rs (renamed from crates/ra_hir_ty/src/match_checking.rs) | 183 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/diagnostics/unsafe_check.rs (renamed from crates/ra_hir_ty/src/unsafe_validation.rs) | 23 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/infer.rs | 10 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/test_db.rs | 17 |
7 files changed, 149 insertions, 138 deletions
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 5b0dda634..3623b8569 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -1,13 +1,30 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | mod expr; | ||
3 | mod match_check; | ||
4 | mod unsafe_check; | ||
2 | 5 | ||
3 | use std::any::Any; | 6 | use std::any::Any; |
4 | 7 | ||
8 | use hir_def::DefWithBodyId; | ||
9 | use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; | ||
5 | use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; | 10 | use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; |
11 | use ra_prof::profile; | ||
6 | use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; | 12 | use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; |
7 | use stdx::format_to; | 13 | use stdx::format_to; |
8 | 14 | ||
9 | pub use hir_def::{diagnostics::UnresolvedModule, expr::MatchArm, path::Path}; | 15 | use crate::db::HirDatabase; |
10 | pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; | 16 | |
17 | pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; | ||
18 | |||
19 | pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { | ||
20 | let _p = profile("validate_body"); | ||
21 | let infer = db.infer(owner); | ||
22 | infer.add_diagnostics(db, owner, sink); | ||
23 | let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink); | ||
24 | validator.validate_body(db); | ||
25 | let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink); | ||
26 | validator.validate_body(db); | ||
27 | } | ||
11 | 28 | ||
12 | #[derive(Debug)] | 29 | #[derive(Debug)] |
13 | pub struct NoSuchField { | 30 | pub struct NoSuchField { |
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/diagnostics/expr.rs index d44562b22..239be779f 100644 --- a/crates/ra_hir_ty/src/expr.rs +++ b/crates/ra_hir_ty/src/diagnostics/expr.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId}; | 5 | use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId}; |
6 | use hir_expand::diagnostics::DiagnosticSink; | 6 | use hir_expand::diagnostics::DiagnosticSink; |
7 | use ra_syntax::{ast, AstPtr}; | 7 | use ra_syntax::{ast, AstPtr}; |
8 | use rustc_hash::FxHashSet; | 8 | use rustc_hash::FxHashSet; |
@@ -10,9 +10,9 @@ use rustc_hash::FxHashSet; | |||
10 | use crate::{ | 10 | use crate::{ |
11 | db::HirDatabase, | 11 | db::HirDatabase, |
12 | diagnostics::{ | 12 | diagnostics::{ |
13 | match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, | ||
13 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields, | 14 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, MissingPatFields, |
14 | }, | 15 | }, |
15 | match_checking::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, | ||
16 | utils::variant_data, | 16 | utils::variant_data, |
17 | ApplicationTy, InferenceResult, Ty, TypeCtor, | 17 | ApplicationTy, InferenceResult, Ty, TypeCtor, |
18 | }; | 18 | }; |
@@ -30,23 +30,23 @@ pub use hir_def::{ | |||
30 | LocalFieldId, Lookup, VariantId, | 30 | LocalFieldId, Lookup, VariantId, |
31 | }; | 31 | }; |
32 | 32 | ||
33 | pub struct ExprValidator<'a, 'b: 'a> { | 33 | pub(super) struct ExprValidator<'a, 'b: 'a> { |
34 | func: FunctionId, | 34 | owner: DefWithBodyId, |
35 | infer: Arc<InferenceResult>, | 35 | infer: Arc<InferenceResult>, |
36 | sink: &'a mut DiagnosticSink<'b>, | 36 | sink: &'a mut DiagnosticSink<'b>, |
37 | } | 37 | } |
38 | 38 | ||
39 | impl<'a, 'b> ExprValidator<'a, 'b> { | 39 | impl<'a, 'b> ExprValidator<'a, 'b> { |
40 | pub fn new( | 40 | pub(super) fn new( |
41 | func: FunctionId, | 41 | owner: DefWithBodyId, |
42 | infer: Arc<InferenceResult>, | 42 | infer: Arc<InferenceResult>, |
43 | sink: &'a mut DiagnosticSink<'b>, | 43 | sink: &'a mut DiagnosticSink<'b>, |
44 | ) -> ExprValidator<'a, 'b> { | 44 | ) -> ExprValidator<'a, 'b> { |
45 | ExprValidator { func, infer, sink } | 45 | ExprValidator { owner, infer, sink } |
46 | } | 46 | } |
47 | 47 | ||
48 | pub fn validate_body(&mut self, db: &dyn HirDatabase) { | 48 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { |
49 | let body = db.body(self.func.into()); | 49 | let body = db.body(self.owner.into()); |
50 | 50 | ||
51 | for (id, expr) in body.exprs.iter() { | 51 | for (id, expr) in body.exprs.iter() { |
52 | if let Some((variant_def, missed_fields, true)) = | 52 | if let Some((variant_def, missed_fields, true)) = |
@@ -96,7 +96,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
96 | missed_fields: Vec<LocalFieldId>, | 96 | missed_fields: Vec<LocalFieldId>, |
97 | ) { | 97 | ) { |
98 | // XXX: only look at source_map if we do have missing fields | 98 | // XXX: only look at source_map if we do have missing fields |
99 | let (_, source_map) = db.body_with_source_map(self.func.into()); | 99 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
100 | 100 | ||
101 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 101 | if let Ok(source_ptr) = source_map.expr_syntax(id) { |
102 | let root = source_ptr.file_syntax(db.upcast()); | 102 | let root = source_ptr.file_syntax(db.upcast()); |
@@ -125,7 +125,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
125 | missed_fields: Vec<LocalFieldId>, | 125 | missed_fields: Vec<LocalFieldId>, |
126 | ) { | 126 | ) { |
127 | // XXX: only look at source_map if we do have missing fields | 127 | // XXX: only look at source_map if we do have missing fields |
128 | let (_, source_map) = db.body_with_source_map(self.func.into()); | 128 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
129 | 129 | ||
130 | if let Ok(source_ptr) = source_map.pat_syntax(id) { | 130 | if let Ok(source_ptr) = source_map.pat_syntax(id) { |
131 | if let Some(expr) = source_ptr.value.as_ref().left() { | 131 | if let Some(expr) = source_ptr.value.as_ref().left() { |
@@ -181,7 +181,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
181 | let mut arg_count = args.len(); | 181 | let mut arg_count = args.len(); |
182 | 182 | ||
183 | if arg_count != param_count { | 183 | if arg_count != param_count { |
184 | let (_, source_map) = db.body_with_source_map(self.func.into()); | 184 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
185 | if let Ok(source_ptr) = source_map.expr_syntax(call_id) { | 185 | if let Ok(source_ptr) = source_map.expr_syntax(call_id) { |
186 | if is_method_call { | 186 | if is_method_call { |
187 | param_count -= 1; | 187 | param_count -= 1; |
@@ -208,7 +208,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
208 | infer: Arc<InferenceResult>, | 208 | infer: Arc<InferenceResult>, |
209 | ) { | 209 | ) { |
210 | let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) = | 210 | let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) = |
211 | db.body_with_source_map(self.func.into()); | 211 | db.body_with_source_map(self.owner.into()); |
212 | 212 | ||
213 | let match_expr_ty = match infer.type_of_expr.get(match_expr) { | 213 | let match_expr_ty = match infer.type_of_expr.get(match_expr) { |
214 | Some(ty) => ty, | 214 | Some(ty) => ty, |
@@ -289,7 +289,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
289 | 289 | ||
290 | let core_result_path = path![core::result::Result]; | 290 | let core_result_path = path![core::result::Result]; |
291 | 291 | ||
292 | let resolver = self.func.resolver(db.upcast()); | 292 | let resolver = self.owner.resolver(db.upcast()); |
293 | let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) { | 293 | let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) { |
294 | Some(it) => it, | 294 | Some(it) => it, |
295 | _ => return, | 295 | _ => return, |
@@ -304,7 +304,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
304 | }; | 304 | }; |
305 | 305 | ||
306 | if params.len() == 2 && params[0] == mismatch.actual { | 306 | if params.len() == 2 && params[0] == mismatch.actual { |
307 | let (_, source_map) = db.body_with_source_map(self.func.into()); | 307 | let (_, source_map) = db.body_with_source_map(self.owner.into()); |
308 | 308 | ||
309 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 309 | if let Ok(source_ptr) = source_map.expr_syntax(id) { |
310 | self.sink | 310 | self.sink |
diff --git a/crates/ra_hir_ty/src/match_checking.rs b/crates/ra_hir_ty/src/diagnostics/match_check.rs index 5495ce284..899025a87 100644 --- a/crates/ra_hir_ty/src/match_checking.rs +++ b/crates/ra_hir_ty/src/diagnostics/match_check.rs | |||
@@ -218,15 +218,16 @@ | |||
218 | //! ``` | 218 | //! ``` |
219 | use std::sync::Arc; | 219 | use std::sync::Arc; |
220 | 220 | ||
221 | use smallvec::{smallvec, SmallVec}; | 221 | use hir_def::{ |
222 | 222 | adt::VariantData, | |
223 | use crate::{ | 223 | body::Body, |
224 | db::HirDatabase, | 224 | expr::{Expr, Literal, Pat, PatId}, |
225 | expr::{Body, Expr, Literal, Pat, PatId}, | 225 | AdtId, EnumVariantId, VariantId, |
226 | ApplicationTy, InferenceResult, Ty, TypeCtor, | ||
227 | }; | 226 | }; |
228 | use hir_def::{adt::VariantData, AdtId, EnumVariantId, VariantId}; | ||
229 | use ra_arena::Idx; | 227 | use ra_arena::Idx; |
228 | use smallvec::{smallvec, SmallVec}; | ||
229 | |||
230 | use crate::{db::HirDatabase, ApplicationTy, InferenceResult, Ty, TypeCtor}; | ||
230 | 231 | ||
231 | #[derive(Debug, Clone, Copy)] | 232 | #[derive(Debug, Clone, Copy)] |
232 | /// Either a pattern from the source code being analyzed, represented as | 233 | /// Either a pattern from the source code being analyzed, represented as |
@@ -271,7 +272,7 @@ impl From<&PatId> for PatIdOrWild { | |||
271 | } | 272 | } |
272 | 273 | ||
273 | #[derive(Debug, Clone, Copy, PartialEq)] | 274 | #[derive(Debug, Clone, Copy, PartialEq)] |
274 | pub enum MatchCheckErr { | 275 | pub(super) enum MatchCheckErr { |
275 | NotImplemented, | 276 | NotImplemented, |
276 | MalformedMatchArm, | 277 | MalformedMatchArm, |
277 | /// Used when type inference cannot resolve the type of | 278 | /// Used when type inference cannot resolve the type of |
@@ -286,21 +287,21 @@ pub enum MatchCheckErr { | |||
286 | /// | 287 | /// |
287 | /// The `std::result::Result` type is used here rather than a custom enum | 288 | /// The `std::result::Result` type is used here rather than a custom enum |
288 | /// to allow the use of `?`. | 289 | /// to allow the use of `?`. |
289 | pub type MatchCheckResult<T> = Result<T, MatchCheckErr>; | 290 | pub(super) type MatchCheckResult<T> = Result<T, MatchCheckErr>; |
290 | 291 | ||
291 | #[derive(Debug)] | 292 | #[derive(Debug)] |
292 | /// A row in a Matrix. | 293 | /// A row in a Matrix. |
293 | /// | 294 | /// |
294 | /// This type is modeled from the struct of the same name in `rustc`. | 295 | /// This type is modeled from the struct of the same name in `rustc`. |
295 | pub(crate) struct PatStack(PatStackInner); | 296 | pub(super) struct PatStack(PatStackInner); |
296 | type PatStackInner = SmallVec<[PatIdOrWild; 2]>; | 297 | type PatStackInner = SmallVec<[PatIdOrWild; 2]>; |
297 | 298 | ||
298 | impl PatStack { | 299 | impl PatStack { |
299 | pub(crate) fn from_pattern(pat_id: PatId) -> PatStack { | 300 | pub(super) fn from_pattern(pat_id: PatId) -> PatStack { |
300 | Self(smallvec!(pat_id.into())) | 301 | Self(smallvec!(pat_id.into())) |
301 | } | 302 | } |
302 | 303 | ||
303 | pub(crate) fn from_wild() -> PatStack { | 304 | pub(super) fn from_wild() -> PatStack { |
304 | Self(smallvec!(PatIdOrWild::Wild)) | 305 | Self(smallvec!(PatIdOrWild::Wild)) |
305 | } | 306 | } |
306 | 307 | ||
@@ -509,14 +510,14 @@ impl PatStack { | |||
509 | /// A collection of PatStack. | 510 | /// A collection of PatStack. |
510 | /// | 511 | /// |
511 | /// This type is modeled from the struct of the same name in `rustc`. | 512 | /// This type is modeled from the struct of the same name in `rustc`. |
512 | pub(crate) struct Matrix(Vec<PatStack>); | 513 | pub(super) struct Matrix(Vec<PatStack>); |
513 | 514 | ||
514 | impl Matrix { | 515 | impl Matrix { |
515 | pub(crate) fn empty() -> Self { | 516 | pub(super) fn empty() -> Self { |
516 | Self(vec![]) | 517 | Self(vec![]) |
517 | } | 518 | } |
518 | 519 | ||
519 | pub(crate) fn push(&mut self, cx: &MatchCheckCtx, row: PatStack) { | 520 | pub(super) fn push(&mut self, cx: &MatchCheckCtx, row: PatStack) { |
520 | if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) { | 521 | if let Some(Pat::Or(pat_ids)) = row.get_head().map(|pat_id| pat_id.as_pat(cx)) { |
521 | // Or patterns are expanded here | 522 | // Or patterns are expanded here |
522 | for pat_id in pat_ids { | 523 | for pat_id in pat_ids { |
@@ -578,16 +579,16 @@ impl Matrix { | |||
578 | /// not matched by an prior match arms. | 579 | /// not matched by an prior match arms. |
579 | /// | 580 | /// |
580 | /// We may eventually need an `Unknown` variant here. | 581 | /// We may eventually need an `Unknown` variant here. |
581 | pub enum Usefulness { | 582 | pub(super) enum Usefulness { |
582 | Useful, | 583 | Useful, |
583 | NotUseful, | 584 | NotUseful, |
584 | } | 585 | } |
585 | 586 | ||
586 | pub struct MatchCheckCtx<'a> { | 587 | pub(super) struct MatchCheckCtx<'a> { |
587 | pub match_expr: Idx<Expr>, | 588 | pub(super) match_expr: Idx<Expr>, |
588 | pub body: Arc<Body>, | 589 | pub(super) body: Arc<Body>, |
589 | pub infer: Arc<InferenceResult>, | 590 | pub(super) infer: Arc<InferenceResult>, |
590 | pub db: &'a dyn HirDatabase, | 591 | pub(super) db: &'a dyn HirDatabase, |
591 | } | 592 | } |
592 | 593 | ||
593 | /// Given a set of patterns `matrix`, and pattern to consider `v`, determines | 594 | /// Given a set of patterns `matrix`, and pattern to consider `v`, determines |
@@ -598,7 +599,7 @@ pub struct MatchCheckCtx<'a> { | |||
598 | /// expected that you have already type checked the match arms. All patterns in | 599 | /// expected that you have already type checked the match arms. All patterns in |
599 | /// matrix should be the same type as v, as well as they should all be the same | 600 | /// matrix should be the same type as v, as well as they should all be the same |
600 | /// type as the match expression. | 601 | /// type as the match expression. |
601 | pub(crate) fn is_useful( | 602 | pub(super) fn is_useful( |
602 | cx: &MatchCheckCtx, | 603 | cx: &MatchCheckCtx, |
603 | matrix: &Matrix, | 604 | matrix: &Matrix, |
604 | v: &PatStack, | 605 | v: &PatStack, |
@@ -836,23 +837,23 @@ fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: Enum | |||
836 | 837 | ||
837 | #[cfg(test)] | 838 | #[cfg(test)] |
838 | mod tests { | 839 | mod tests { |
839 | pub(super) use insta::assert_snapshot; | 840 | use insta::assert_snapshot; |
840 | pub(super) use ra_db::fixture::WithFixture; | 841 | use ra_db::fixture::WithFixture; |
841 | 842 | ||
842 | pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB}; | 843 | use crate::{diagnostics::MissingMatchArms, test_db::TestDB}; |
843 | 844 | ||
844 | pub(super) fn check_diagnostic_message(ra_fixture: &str) -> String { | 845 | fn check_diagnostic_message(ra_fixture: &str) -> String { |
845 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().0 | 846 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().0 |
846 | } | 847 | } |
847 | 848 | ||
848 | pub(super) fn check_diagnostic(ra_fixture: &str) { | 849 | fn check_diagnostic(ra_fixture: &str) { |
849 | let diagnostic_count = | 850 | let diagnostic_count = |
850 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1; | 851 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1; |
851 | 852 | ||
852 | assert_eq!(1, diagnostic_count, "no diagnostic reported"); | 853 | assert_eq!(1, diagnostic_count, "no diagnostic reported"); |
853 | } | 854 | } |
854 | 855 | ||
855 | pub(super) fn check_no_diagnostic(ra_fixture: &str) { | 856 | fn check_no_diagnostic(ra_fixture: &str) { |
856 | let (s, diagnostic_count) = | 857 | let (s, diagnostic_count) = |
857 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>(); | 858 | TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>(); |
858 | 859 | ||
@@ -2035,28 +2036,25 @@ mod tests { | |||
2035 | ", | 2036 | ", |
2036 | ); | 2037 | ); |
2037 | } | 2038 | } |
2038 | } | ||
2039 | 2039 | ||
2040 | #[cfg(test)] | 2040 | mod false_negatives { |
2041 | mod false_negatives { | 2041 | //! The implementation of match checking here is a work in progress. As we roll this out, we |
2042 | //! The implementation of match checking here is a work in progress. As we roll this out, we | 2042 | //! prefer false negatives to false positives (ideally there would be no false positives). This |
2043 | //! prefer false negatives to false positives (ideally there would be no false positives). This | 2043 | //! test module should document known false negatives. Eventually we will have a complete |
2044 | //! test module should document known false negatives. Eventually we will have a complete | 2044 | //! implementation of match checking and this module will be empty. |
2045 | //! implementation of match checking and this module will be empty. | 2045 | //! |
2046 | //! | 2046 | //! The reasons for documenting known false negatives: |
2047 | //! The reasons for documenting known false negatives: | 2047 | //! |
2048 | //! | 2048 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. |
2049 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | 2049 | //! 2. It ensures the code doesn't panic when handling these cases. |
2050 | //! 2. It ensures the code doesn't panic when handling these cases. | 2050 | use super::*; |
2051 | 2051 | ||
2052 | use super::tests::*; | 2052 | #[test] |
2053 | 2053 | fn integers() { | |
2054 | #[test] | 2054 | // This is a false negative. |
2055 | fn integers() { | 2055 | // We don't currently check integer exhaustiveness. |
2056 | // This is a false negative. | 2056 | check_no_diagnostic( |
2057 | // We don't currently check integer exhaustiveness. | 2057 | r" |
2058 | check_no_diagnostic( | ||
2059 | r" | ||
2060 | fn test_fn() { | 2058 | fn test_fn() { |
2061 | match 5 { | 2059 | match 5 { |
2062 | 10 => (), | 2060 | 10 => (), |
@@ -2064,15 +2062,15 @@ mod false_negatives { | |||
2064 | } | 2062 | } |
2065 | } | 2063 | } |
2066 | ", | 2064 | ", |
2067 | ); | 2065 | ); |
2068 | } | 2066 | } |
2069 | 2067 | ||
2070 | #[test] | 2068 | #[test] |
2071 | fn internal_or() { | 2069 | fn internal_or() { |
2072 | // This is a false negative. | 2070 | // This is a false negative. |
2073 | // We do not currently handle patterns with internal `or`s. | 2071 | // We do not currently handle patterns with internal `or`s. |
2074 | check_no_diagnostic( | 2072 | check_no_diagnostic( |
2075 | r" | 2073 | r" |
2076 | fn test_fn() { | 2074 | fn test_fn() { |
2077 | enum Either { | 2075 | enum Either { |
2078 | A(bool), | 2076 | A(bool), |
@@ -2083,17 +2081,17 @@ mod false_negatives { | |||
2083 | } | 2081 | } |
2084 | } | 2082 | } |
2085 | ", | 2083 | ", |
2086 | ); | 2084 | ); |
2087 | } | 2085 | } |
2088 | 2086 | ||
2089 | #[test] | 2087 | #[test] |
2090 | fn expr_loop_missing_arm() { | 2088 | fn expr_loop_missing_arm() { |
2091 | // This is a false negative. | 2089 | // This is a false negative. |
2092 | // We currently infer the type of `loop { break Foo::A }` to `!`, which | 2090 | // We currently infer the type of `loop { break Foo::A }` to `!`, which |
2093 | // causes us to skip the diagnostic since `Either::A` doesn't type check | 2091 | // causes us to skip the diagnostic since `Either::A` doesn't type check |
2094 | // with `!`. | 2092 | // with `!`. |
2095 | check_diagnostic( | 2093 | check_diagnostic( |
2096 | r" | 2094 | r" |
2097 | enum Either { | 2095 | enum Either { |
2098 | A, | 2096 | A, |
2099 | B, | 2097 | B, |
@@ -2104,45 +2102,45 @@ mod false_negatives { | |||
2104 | } | 2102 | } |
2105 | } | 2103 | } |
2106 | ", | 2104 | ", |
2107 | ); | 2105 | ); |
2108 | } | 2106 | } |
2109 | 2107 | ||
2110 | #[test] | 2108 | #[test] |
2111 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | 2109 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { |
2112 | // This is a false negative. | 2110 | // This is a false negative. |
2113 | // We don't currently handle tuple patterns with ellipsis. | 2111 | // We don't currently handle tuple patterns with ellipsis. |
2114 | check_no_diagnostic( | 2112 | check_no_diagnostic( |
2115 | r" | 2113 | r" |
2116 | fn test_fn() { | 2114 | fn test_fn() { |
2117 | match (false, true, false) { | 2115 | match (false, true, false) { |
2118 | (false, ..) => {}, | 2116 | (false, ..) => {}, |
2119 | } | 2117 | } |
2120 | } | 2118 | } |
2121 | ", | 2119 | ", |
2122 | ); | 2120 | ); |
2123 | } | 2121 | } |
2124 | 2122 | ||
2125 | #[test] | 2123 | #[test] |
2126 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | 2124 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { |
2127 | // This is a false negative. | 2125 | // This is a false negative. |
2128 | // We don't currently handle tuple patterns with ellipsis. | 2126 | // We don't currently handle tuple patterns with ellipsis. |
2129 | check_no_diagnostic( | 2127 | check_no_diagnostic( |
2130 | r" | 2128 | r" |
2131 | fn test_fn() { | 2129 | fn test_fn() { |
2132 | match (false, true, false) { | 2130 | match (false, true, false) { |
2133 | (.., false) => {}, | 2131 | (.., false) => {}, |
2134 | } | 2132 | } |
2135 | } | 2133 | } |
2136 | ", | 2134 | ", |
2137 | ); | 2135 | ); |
2138 | } | 2136 | } |
2139 | 2137 | ||
2140 | #[test] | 2138 | #[test] |
2141 | fn struct_missing_arm() { | 2139 | fn struct_missing_arm() { |
2142 | // This is a false negative. | 2140 | // This is a false negative. |
2143 | // We don't currently handle structs. | 2141 | // We don't currently handle structs. |
2144 | check_no_diagnostic( | 2142 | check_no_diagnostic( |
2145 | r" | 2143 | r" |
2146 | struct Foo { | 2144 | struct Foo { |
2147 | a: bool, | 2145 | a: bool, |
2148 | } | 2146 | } |
@@ -2152,6 +2150,7 @@ mod false_negatives { | |||
2152 | } | 2150 | } |
2153 | } | 2151 | } |
2154 | ", | 2152 | ", |
2155 | ); | 2153 | ); |
2154 | } | ||
2156 | } | 2155 | } |
2157 | } | 2156 | } |
diff --git a/crates/ra_hir_ty/src/unsafe_validation.rs b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs index c512c4f8e..b8ff95ee1 100644 --- a/crates/ra_hir_ty/src/unsafe_validation.rs +++ b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -6,7 +6,7 @@ use std::sync::Arc; | |||
6 | use hir_def::{ | 6 | use hir_def::{ |
7 | body::Body, | 7 | body::Body, |
8 | expr::{Expr, ExprId, UnaryOp}, | 8 | expr::{Expr, ExprId, UnaryOp}, |
9 | DefWithBodyId, FunctionId, | 9 | DefWithBodyId, |
10 | }; | 10 | }; |
11 | use hir_expand::diagnostics::DiagnosticSink; | 11 | use hir_expand::diagnostics::DiagnosticSink; |
12 | 12 | ||
@@ -15,26 +15,29 @@ use crate::{ | |||
15 | InferenceResult, Ty, TypeCtor, | 15 | InferenceResult, Ty, TypeCtor, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | pub struct UnsafeValidator<'a, 'b: 'a> { | 18 | pub(super) struct UnsafeValidator<'a, 'b: 'a> { |
19 | func: FunctionId, | 19 | owner: DefWithBodyId, |
20 | infer: Arc<InferenceResult>, | 20 | infer: Arc<InferenceResult>, |
21 | sink: &'a mut DiagnosticSink<'b>, | 21 | sink: &'a mut DiagnosticSink<'b>, |
22 | } | 22 | } |
23 | 23 | ||
24 | impl<'a, 'b> UnsafeValidator<'a, 'b> { | 24 | impl<'a, 'b> UnsafeValidator<'a, 'b> { |
25 | pub fn new( | 25 | pub(super) fn new( |
26 | func: FunctionId, | 26 | owner: DefWithBodyId, |
27 | infer: Arc<InferenceResult>, | 27 | infer: Arc<InferenceResult>, |
28 | sink: &'a mut DiagnosticSink<'b>, | 28 | sink: &'a mut DiagnosticSink<'b>, |
29 | ) -> UnsafeValidator<'a, 'b> { | 29 | ) -> UnsafeValidator<'a, 'b> { |
30 | UnsafeValidator { func, infer, sink } | 30 | UnsafeValidator { owner, infer, sink } |
31 | } | 31 | } |
32 | 32 | ||
33 | pub fn validate_body(&mut self, db: &dyn HirDatabase) { | 33 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { |
34 | let def = self.func.into(); | 34 | let def = self.owner.into(); |
35 | let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); | 35 | let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); |
36 | let func_data = db.function_data(self.func); | 36 | let is_unsafe = match self.owner { |
37 | if func_data.is_unsafe | 37 | DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe, |
38 | DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false, | ||
39 | }; | ||
40 | if is_unsafe | ||
38 | || unsafe_expressions | 41 | || unsafe_expressions |
39 | .iter() | 42 | .iter() |
40 | .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block) | 43 | .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block) |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 2ce4f65cc..28f32a0a4 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -168,7 +168,7 @@ impl InferenceResult { | |||
168 | pub fn add_diagnostics( | 168 | pub fn add_diagnostics( |
169 | &self, | 169 | &self, |
170 | db: &dyn HirDatabase, | 170 | db: &dyn HirDatabase, |
171 | owner: FunctionId, | 171 | owner: DefWithBodyId, |
172 | sink: &mut DiagnosticSink, | 172 | sink: &mut DiagnosticSink, |
173 | ) { | 173 | ) { |
174 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) | 174 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) |
@@ -760,7 +760,7 @@ impl std::ops::BitOrAssign for Diverges { | |||
760 | } | 760 | } |
761 | 761 | ||
762 | mod diagnostics { | 762 | mod diagnostics { |
763 | use hir_def::{expr::ExprId, FunctionId}; | 763 | use hir_def::{expr::ExprId, DefWithBodyId}; |
764 | use hir_expand::diagnostics::DiagnosticSink; | 764 | use hir_expand::diagnostics::DiagnosticSink; |
765 | 765 | ||
766 | use crate::{ | 766 | use crate::{ |
@@ -778,17 +778,17 @@ mod diagnostics { | |||
778 | pub(super) fn add_to( | 778 | pub(super) fn add_to( |
779 | &self, | 779 | &self, |
780 | db: &dyn HirDatabase, | 780 | db: &dyn HirDatabase, |
781 | owner: FunctionId, | 781 | owner: DefWithBodyId, |
782 | sink: &mut DiagnosticSink, | 782 | sink: &mut DiagnosticSink, |
783 | ) { | 783 | ) { |
784 | match self { | 784 | match self { |
785 | InferenceDiagnostic::NoSuchField { expr, field } => { | 785 | InferenceDiagnostic::NoSuchField { expr, field } => { |
786 | let (_, source_map) = db.body_with_source_map(owner.into()); | 786 | let (_, source_map) = db.body_with_source_map(owner); |
787 | let field = source_map.field_syntax(*expr, *field); | 787 | let field = source_map.field_syntax(*expr, *field); |
788 | sink.push(NoSuchField { file: field.file_id, field: field.value }) | 788 | sink.push(NoSuchField { file: field.file_id, field: field.value }) |
789 | } | 789 | } |
790 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { | 790 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { |
791 | let (_, source_map) = db.body_with_source_map(owner.into()); | 791 | let (_, source_map) = db.body_with_source_map(owner); |
792 | let ptr = source_map | 792 | let ptr = source_map |
793 | .expr_syntax(*expr) | 793 | .expr_syntax(*expr) |
794 | .expect("break outside of loop in synthetic syntax"); | 794 | .expect("break outside of loop in synthetic syntax"); |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 2652d200f..d54568e67 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -12,15 +12,12 @@ pub mod traits; | |||
12 | pub mod method_resolution; | 12 | pub mod method_resolution; |
13 | mod op; | 13 | mod op; |
14 | mod lower; | 14 | mod lower; |
15 | mod match_checking; | ||
16 | pub(crate) mod infer; | 15 | pub(crate) mod infer; |
17 | pub(crate) mod utils; | 16 | pub(crate) mod utils; |
18 | 17 | ||
19 | pub mod display; | 18 | pub mod display; |
20 | pub mod db; | 19 | pub mod db; |
21 | pub mod diagnostics; | 20 | pub mod diagnostics; |
22 | pub mod expr; | ||
23 | pub mod unsafe_validation; | ||
24 | 21 | ||
25 | #[cfg(test)] | 22 | #[cfg(test)] |
26 | mod tests; | 23 | mod tests; |
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index dc447955f..ddafd0ea1 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs | |||
@@ -6,17 +6,17 @@ use std::{ | |||
6 | }; | 6 | }; |
7 | 7 | ||
8 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; | 8 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; |
9 | use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; | 9 | use hir_expand::{ |
10 | db::AstDatabase, | ||
11 | diagnostics::{Diagnostic, DiagnosticSink}, | ||
12 | }; | ||
10 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; | 13 | use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; |
11 | use ra_syntax::TextRange; | 14 | use ra_syntax::TextRange; |
12 | use rustc_hash::{FxHashMap, FxHashSet}; | 15 | use rustc_hash::{FxHashMap, FxHashSet}; |
13 | use stdx::format_to; | 16 | use stdx::format_to; |
14 | use test_utils::extract_annotations; | 17 | use test_utils::extract_annotations; |
15 | 18 | ||
16 | use crate::{ | 19 | use crate::diagnostics::validate_body; |
17 | db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator, | ||
18 | unsafe_validation::UnsafeValidator, | ||
19 | }; | ||
20 | 20 | ||
21 | #[salsa::database( | 21 | #[salsa::database( |
22 | ra_db::SourceDatabaseExtStorage, | 22 | ra_db::SourceDatabaseExtStorage, |
@@ -118,13 +118,8 @@ impl TestDB { | |||
118 | } | 118 | } |
119 | 119 | ||
120 | for f in fns { | 120 | for f in fns { |
121 | let infer = self.infer(f.into()); | ||
122 | let mut sink = DiagnosticSink::new(&mut cb); | 121 | let mut sink = DiagnosticSink::new(&mut cb); |
123 | infer.add_diagnostics(self, f, &mut sink); | 122 | validate_body(self, f.into(), &mut sink); |
124 | let mut validator = ExprValidator::new(f, infer.clone(), &mut sink); | ||
125 | validator.validate_body(self); | ||
126 | let mut validator = UnsafeValidator::new(f, infer, &mut sink); | ||
127 | validator.validate_body(self); | ||
128 | } | 123 | } |
129 | } | 124 | } |
130 | } | 125 | } |