diff options
Diffstat (limited to 'crates/hir_ty/src/diagnostics/expr.rs')
-rw-r--r-- | crates/hir_ty/src/diagnostics/expr.rs | 117 |
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 | ||
5 | use std::sync::Arc; | 5 | use std::{cell::RefCell, sync::Arc}; |
6 | 6 | ||
7 | use hir_def::{expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId}; | 7 | use hir_def::{ |
8 | expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule, | ||
9 | }; | ||
8 | use hir_expand::name; | 10 | use hir_expand::name; |
9 | use rustc_hash::FxHashSet; | 11 | use rustc_hash::FxHashSet; |
10 | use syntax::{ast, AstPtr}; | 12 | use syntax::{ast, AstPtr}; |
@@ -12,7 +14,10 @@ use syntax::{ast, AstPtr}; | |||
12 | use crate::{ | 14 | use 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) { |