aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src/diagnostics/expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_ty/src/diagnostics/expr.rs')
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs117
1 files changed, 86 insertions, 31 deletions
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 86f82e3fa..3efbce773 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -2,9 +2,11 @@
2//! through the body using inference results: mismatched arg counts, missing 2//! through the body using inference results: mismatched arg counts, missing
3//! fields, etc. 3//! fields, etc.
4 4
5use std::sync::Arc; 5use std::{cell::RefCell, sync::Arc};
6 6
7use hir_def::{expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId}; 7use hir_def::{
8 expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule,
9};
8use hir_expand::name; 10use hir_expand::name;
9use rustc_hash::FxHashSet; 11use rustc_hash::FxHashSet;
10use syntax::{ast, AstPtr}; 12use syntax::{ast, AstPtr};
@@ -12,7 +14,10 @@ use syntax::{ast, AstPtr};
12use crate::{ 14use crate::{
13 db::HirDatabase, 15 db::HirDatabase,
14 diagnostics::{ 16 diagnostics::{
15 match_check::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, 17 match_check::{
18 self,
19 usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena},
20 },
16 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, 21 MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr,
17 MissingPatFields, RemoveThisSemicolon, 22 MissingPatFields, RemoveThisSemicolon,
18 }, 23 },
@@ -294,12 +299,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
294 &infer.type_of_expr[match_expr] 299 &infer.type_of_expr[match_expr]
295 }; 300 };
296 301
297 let cx = MatchCheckCtx { match_expr, body, infer: infer.clone(), db }; 302 let pattern_arena = RefCell::new(PatternArena::new());
298 let pats = arms.iter().map(|arm| arm.pat);
299 303
300 let mut seen = Matrix::empty(); 304 let mut m_arms = Vec::new();
301 for pat in pats { 305 let mut has_lowering_errors = false;
302 if let Some(pat_ty) = infer.type_of_pat.get(pat) { 306 for arm in arms {
307 if let Some(pat_ty) = infer.type_of_pat.get(arm.pat) {
303 // We only include patterns whose type matches the type 308 // We only include patterns whose type matches the type
304 // of the match expression. If we had a InvalidMatchArmPattern 309 // of the match expression. If we had a InvalidMatchArmPattern
305 // diagnostic or similar we could raise that in an else 310 // diagnostic or similar we could raise that in an else
@@ -315,14 +320,25 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
315 .as_reference() 320 .as_reference()
316 .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) 321 .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
317 .unwrap_or(false)) 322 .unwrap_or(false))
318 && types_of_subpatterns_do_match(pat, &cx.body, &infer) 323 && types_of_subpatterns_do_match(arm.pat, &body, &infer)
319 { 324 {
320 // If we had a NotUsefulMatchArm diagnostic, we could 325 // If we had a NotUsefulMatchArm diagnostic, we could
321 // check the usefulness of each pattern as we added it 326 // check the usefulness of each pattern as we added it
322 // to the matrix here. 327 // to the matrix here.
323 let v = PatStack::from_pattern(pat); 328 let m_arm = match_check::MatchArm {
324 seen.push(&cx, v); 329 pat: self.lower_pattern(
325 continue; 330 arm.pat,
331 &mut pattern_arena.borrow_mut(),
332 db,
333 &body,
334 &mut has_lowering_errors,
335 ),
336 has_guard: arm.guard.is_some(),
337 };
338 m_arms.push(m_arm);
339 if !has_lowering_errors {
340 continue;
341 }
326 } 342 }
327 } 343 }
328 344
@@ -330,34 +346,73 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
330 // fit the match expression, we skip this diagnostic. Skipping the entire 346 // fit the match expression, we skip this diagnostic. Skipping the entire
331 // diagnostic rather than just not including this match arm is preferred 347 // diagnostic rather than just not including this match arm is preferred
332 // to avoid the chance of false positives. 348 // to avoid the chance of false positives.
349 #[cfg(test)]
350 match_check::tests::report_bail_out(db, self.owner, arm.pat, self.sink);
333 return; 351 return;
334 } 352 }
335 353
336 match is_useful(&cx, &seen, &PatStack::from_wild()) { 354 let cx = MatchCheckCtx {
337 Ok(Usefulness::Useful) => (), 355 module: self.owner.module(db.upcast()),
338 // if a wildcard pattern is not useful, then all patterns are covered 356 match_expr,
339 Ok(Usefulness::NotUseful) => return, 357 infer: &infer,
340 // this path is for unimplemented checks, so we err on the side of not 358 db,
341 // reporting any errors 359 pattern_arena: &pattern_arena,
342 _ => return, 360 eprint_panic_context: &|| {
343 } 361 use syntax::AstNode;
362 if let Ok(scrutinee_sptr) = source_map.expr_syntax(match_expr) {
363 let root = scrutinee_sptr.file_syntax(db.upcast());
364 if let Some(match_ast) = scrutinee_sptr.value.to_node(&root).syntax().parent() {
365 eprintln!(
366 "Match checking is about to panic on this expression:\n{}",
367 match_ast.to_string(),
368 );
369 }
370 }
371 },
372 };
373 let report = compute_match_usefulness(&cx, &m_arms);
344 374
345 if let Ok(source_ptr) = source_map.expr_syntax(id) { 375 // FIXME Report unreacheble arms
346 let root = source_ptr.file_syntax(db.upcast()); 376 // https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200-L201
347 if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) { 377
348 if let (Some(match_expr), Some(arms)) = 378 let witnesses = report.non_exhaustiveness_witnesses;
349 (match_expr.expr(), match_expr.match_arm_list()) 379 // FIXME Report witnesses
350 { 380 // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses);
351 self.sink.push(MissingMatchArms { 381 if !witnesses.is_empty() {
352 file: source_ptr.file_id, 382 if let Ok(source_ptr) = source_map.expr_syntax(id) {
353 match_expr: AstPtr::new(&match_expr), 383 let root = source_ptr.file_syntax(db.upcast());
354 arms: AstPtr::new(&arms), 384 if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
355 }) 385 if let (Some(match_expr), Some(arms)) =
386 (match_expr.expr(), match_expr.match_arm_list())
387 {
388 self.sink.push(MissingMatchArms {
389 file: source_ptr.file_id,
390 match_expr: AstPtr::new(&match_expr),
391 arms: AstPtr::new(&arms),
392 })
393 }
356 } 394 }
357 } 395 }
358 } 396 }
359 } 397 }
360 398
399 fn lower_pattern(
400 &self,
401 pat: PatId,
402 pattern_arena: &mut PatternArena,
403 db: &dyn HirDatabase,
404 body: &Body,
405 have_errors: &mut bool,
406 ) -> match_check::PatId {
407 let mut patcx = match_check::PatCtxt::new(db, &self.infer, body);
408 let pattern = patcx.lower_pattern(pat);
409 let pattern = pattern_arena.alloc(expand_pattern(pattern));
410 if !patcx.errors.is_empty() {
411 *have_errors = true;
412 }
413 pattern
414 }
415
361 fn validate_results_in_tail_expr(&mut self, body_id: ExprId, id: ExprId, db: &dyn HirDatabase) { 416 fn validate_results_in_tail_expr(&mut self, body_id: ExprId, id: ExprId, db: &dyn HirDatabase) {
362 // the mismatch will be on the whole block currently 417 // the mismatch will be on the whole block currently
363 let mismatch = match self.infer.type_mismatch_for_expr(body_id) { 418 let mismatch = match self.infer.type_mismatch_for_expr(body_id) {