aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/code_model.rs15
-rw-r--r--crates/ra_hir/src/source_analyzer.rs2
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs21
-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.rs10
-rw-r--r--crates/ra_hir_ty/src/lib.rs3
-rw-r--r--crates/ra_hir_ty/src/test_db.rs17
-rw-r--r--crates/test_utils/src/mark.rs4
10 files changed, 155 insertions, 153 deletions
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 9222009fe..42c9ca189 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -25,11 +25,8 @@ use hir_expand::{
25use hir_ty::{ 25use hir_ty::{
26 autoderef, 26 autoderef,
27 display::{HirDisplayError, HirFormatter}, 27 display::{HirDisplayError, HirFormatter},
28 expr::ExprValidator, 28 method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs,
29 method_resolution, 29 TraitEnvironment, Ty, TyDefId, TypeCtor,
30 unsafe_validation::UnsafeValidator,
31 ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs, TraitEnvironment, Ty,
32 TyDefId, TypeCtor,
33}; 30};
34use ra_db::{CrateId, Edition, FileId}; 31use ra_db::{CrateId, Edition, FileId};
35use ra_prof::profile; 32use ra_prof::profile;
@@ -680,13 +677,7 @@ impl Function {
680 } 677 }
681 678
682 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 679 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
683 let _p = profile("Function::diagnostics"); 680 hir_ty::diagnostics::validate_body(db, self.id.into(), sink)
684 let infer = db.infer(self.id.into());
685 infer.add_diagnostics(db, self.id, sink);
686 let mut validator = ExprValidator::new(self.id, infer.clone(), sink);
687 validator.validate_body(db);
688 let mut validator = UnsafeValidator::new(self.id, infer, sink);
689 validator.validate_body(db);
690 } 681 }
691} 682}
692 683
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index f74b78b23..d76345525 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -18,7 +18,7 @@ use hir_def::{
18}; 18};
19use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; 19use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
20use hir_ty::{ 20use hir_ty::{
21 expr::{record_literal_missing_fields, record_pattern_missing_fields}, 21 diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
22 InferenceResult, Substs, Ty, 22 InferenceResult, Substs, Ty,
23}; 23};
24use ra_syntax::{ 24use ra_syntax::{
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
2mod expr;
3mod match_check;
4mod unsafe_check;
2 5
3use std::any::Any; 6use std::any::Any;
4 7
8use hir_def::DefWithBodyId;
9use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink};
5use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; 10use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile};
11use ra_prof::profile;
6use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; 12use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
7use stdx::format_to; 13use stdx::format_to;
8 14
9pub use hir_def::{diagnostics::UnresolvedModule, expr::MatchArm, path::Path}; 15use crate::db::HirDatabase;
10pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; 16
17pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields};
18
19pub 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)]
13pub struct NoSuchField { 30pub 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
3use std::sync::Arc; 3use std::sync::Arc;
4 4
5use hir_def::{path::path, resolver::HasResolver, AdtId, FunctionId}; 5use hir_def::{path::path, resolver::HasResolver, AdtId, DefWithBodyId};
6use hir_expand::diagnostics::DiagnosticSink; 6use hir_expand::diagnostics::DiagnosticSink;
7use ra_syntax::{ast, AstPtr}; 7use ra_syntax::{ast, AstPtr};
8use rustc_hash::FxHashSet; 8use rustc_hash::FxHashSet;
@@ -10,9 +10,9 @@ use rustc_hash::FxHashSet;
10use crate::{ 10use 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
33pub struct ExprValidator<'a, 'b: 'a> { 33pub(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
39impl<'a, 'b> ExprValidator<'a, 'b> { 39impl<'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//! ```
219use std::sync::Arc; 219use std::sync::Arc;
220 220
221use smallvec::{smallvec, SmallVec}; 221use hir_def::{
222 222 adt::VariantData,
223use 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};
228use hir_def::{adt::VariantData, AdtId, EnumVariantId, VariantId};
229use ra_arena::Idx; 227use ra_arena::Idx;
228use smallvec::{smallvec, SmallVec};
229
230use 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)]
274pub enum MatchCheckErr { 275pub(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 `?`.
289pub type MatchCheckResult<T> = Result<T, MatchCheckErr>; 290pub(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`.
295pub(crate) struct PatStack(PatStackInner); 296pub(super) struct PatStack(PatStackInner);
296type PatStackInner = SmallVec<[PatIdOrWild; 2]>; 297type PatStackInner = SmallVec<[PatIdOrWild; 2]>;
297 298
298impl PatStack { 299impl 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`.
512pub(crate) struct Matrix(Vec<PatStack>); 513pub(super) struct Matrix(Vec<PatStack>);
513 514
514impl Matrix { 515impl 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.
581pub enum Usefulness { 582pub(super) enum Usefulness {
582 Useful, 583 Useful,
583 NotUseful, 584 NotUseful,
584} 585}
585 586
586pub struct MatchCheckCtx<'a> { 587pub(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.
601pub(crate) fn is_useful( 602pub(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)]
838mod tests { 839mod 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 {
2041mod 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;
6use hir_def::{ 6use 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};
11use hir_expand::diagnostics::DiagnosticSink; 11use hir_expand::diagnostics::DiagnosticSink;
12 12
@@ -15,26 +15,29 @@ use crate::{
15 InferenceResult, Ty, TypeCtor, 15 InferenceResult, Ty, TypeCtor,
16}; 16};
17 17
18pub struct UnsafeValidator<'a, 'b: 'a> { 18pub(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
24impl<'a, 'b> UnsafeValidator<'a, 'b> { 24impl<'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
762mod diagnostics { 762mod 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;
12pub mod method_resolution; 12pub mod method_resolution;
13mod op; 13mod op;
14mod lower; 14mod lower;
15mod match_checking;
16pub(crate) mod infer; 15pub(crate) mod infer;
17pub(crate) mod utils; 16pub(crate) mod utils;
18 17
19pub mod display; 18pub mod display;
20pub mod db; 19pub mod db;
21pub mod diagnostics; 20pub mod diagnostics;
22pub mod expr;
23pub mod unsafe_validation;
24 21
25#[cfg(test)] 22#[cfg(test)]
26mod tests; 23mod 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
8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; 8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId};
9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; 9use hir_expand::{
10 db::AstDatabase,
11 diagnostics::{Diagnostic, DiagnosticSink},
12};
10use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; 13use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast};
11use ra_syntax::TextRange; 14use ra_syntax::TextRange;
12use rustc_hash::{FxHashMap, FxHashSet}; 15use rustc_hash::{FxHashMap, FxHashSet};
13use stdx::format_to; 16use stdx::format_to;
14use test_utils::extract_annotations; 17use test_utils::extract_annotations;
15 18
16use crate::{ 19use 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 }
diff --git a/crates/test_utils/src/mark.rs b/crates/test_utils/src/mark.rs
index 7c309a894..97f5a93ad 100644
--- a/crates/test_utils/src/mark.rs
+++ b/crates/test_utils/src/mark.rs
@@ -62,7 +62,7 @@ pub struct MarkChecker {
62 62
63impl MarkChecker { 63impl MarkChecker {
64 pub fn new(mark: &'static AtomicUsize) -> MarkChecker { 64 pub fn new(mark: &'static AtomicUsize) -> MarkChecker {
65 let value_on_entry = mark.load(Ordering::SeqCst); 65 let value_on_entry = mark.load(Ordering::Relaxed);
66 MarkChecker { mark, value_on_entry } 66 MarkChecker { mark, value_on_entry }
67 } 67 }
68} 68}
@@ -72,7 +72,7 @@ impl Drop for MarkChecker {
72 if std::thread::panicking() { 72 if std::thread::panicking() {
73 return; 73 return;
74 } 74 }
75 let value_on_exit = self.mark.load(Ordering::SeqCst); 75 let value_on_exit = self.mark.load(Ordering::Relaxed);
76 assert!(value_on_exit > self.value_on_entry, "mark was not hit") 76 assert!(value_on_exit > self.value_on_entry, "mark was not hit")
77 } 77 }
78} 78}