diff options
author | Dawer <[email protected]> | 2021-05-11 13:18:16 +0100 |
---|---|---|
committer | Dawer <[email protected]> | 2021-05-31 20:23:09 +0100 |
commit | e84efc4a4656e54a4f08b99592d5d98ac5726449 (patch) | |
tree | 7f21f05a7eb6cbef32ca9c081e19f9f0c2392567 /crates/hir_ty/src/diagnostics/expr.rs | |
parent | 894b4c64ffdb280a38c1ea2e9be145ca308965fd (diff) |
Replace the old match checking algorithm
Diffstat (limited to 'crates/hir_ty/src/diagnostics/expr.rs')
-rw-r--r-- | crates/hir_ty/src/diagnostics/expr.rs | 119 |
1 files changed, 17 insertions, 102 deletions
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index b321004ac..c6015d236 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -4,7 +4,9 @@ | |||
4 | 4 | ||
5 | use std::{cell::RefCell, 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 | }, |
@@ -26,13 +31,7 @@ pub(crate) use hir_def::{ | |||
26 | LocalFieldId, VariantId, | 31 | LocalFieldId, VariantId, |
27 | }; | 32 | }; |
28 | 33 | ||
29 | use super::{ | 34 | use super::ReplaceFilterMapNextWithFindMap; |
30 | pattern::{ | ||
31 | self, | ||
32 | usefulness::{expand_pattern, PatternArena}, | ||
33 | }, | ||
34 | ReplaceFilterMapNextWithFindMap, | ||
35 | }; | ||
36 | 35 | ||
37 | pub(super) struct ExprValidator<'a, 'b: 'a> { | 36 | pub(super) struct ExprValidator<'a, 'b: 'a> { |
38 | owner: DefWithBodyId, | 37 | owner: DefWithBodyId, |
@@ -68,7 +67,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
68 | 67 | ||
69 | match expr { | 68 | match expr { |
70 | Expr::Match { expr, arms } => { | 69 | Expr::Match { expr, arms } => { |
71 | self.validate_match2(id, *expr, arms, db, self.infer.clone()); | 70 | self.validate_match(id, *expr, arms, db, self.infer.clone()); |
72 | } | 71 | } |
73 | Expr::Call { .. } | Expr::MethodCall { .. } => { | 72 | Expr::Call { .. } | Expr::MethodCall { .. } => { |
74 | self.validate_call(db, id, expr); | 73 | self.validate_call(db, id, expr); |
@@ -283,7 +282,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
283 | } | 282 | } |
284 | } | 283 | } |
285 | 284 | ||
286 | #[allow(dead_code)] | ||
287 | fn validate_match( | 285 | fn validate_match( |
288 | &mut self, | 286 | &mut self, |
289 | id: ExprId, | 287 | id: ExprId, |
@@ -301,90 +299,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
301 | &infer.type_of_expr[match_expr] | 299 | &infer.type_of_expr[match_expr] |
302 | }; | 300 | }; |
303 | 301 | ||
304 | let cx = MatchCheckCtx { match_expr, body, infer: infer.clone(), db }; | ||
305 | let pats = arms.iter().map(|arm| arm.pat); | ||
306 | |||
307 | let mut seen = Matrix::empty(); | ||
308 | for pat in pats { | ||
309 | if let Some(pat_ty) = infer.type_of_pat.get(pat) { | ||
310 | // We only include patterns whose type matches the type | ||
311 | // of the match expression. If we had a InvalidMatchArmPattern | ||
312 | // diagnostic or similar we could raise that in an else | ||
313 | // block here. | ||
314 | // | ||
315 | // When comparing the types, we also have to consider that rustc | ||
316 | // will automatically de-reference the match expression type if | ||
317 | // necessary. | ||
318 | // | ||
319 | // FIXME we should use the type checker for this. | ||
320 | if (pat_ty == match_expr_ty | ||
321 | || match_expr_ty | ||
322 | .as_reference() | ||
323 | .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) | ||
324 | .unwrap_or(false)) | ||
325 | && types_of_subpatterns_do_match(pat, &cx.body, &infer) | ||
326 | { | ||
327 | // If we had a NotUsefulMatchArm diagnostic, we could | ||
328 | // check the usefulness of each pattern as we added it | ||
329 | // to the matrix here. | ||
330 | let v = PatStack::from_pattern(pat); | ||
331 | seen.push(&cx, v); | ||
332 | continue; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | // If we can't resolve the type of a pattern, or the pattern type doesn't | ||
337 | // fit the match expression, we skip this diagnostic. Skipping the entire | ||
338 | // diagnostic rather than just not including this match arm is preferred | ||
339 | // to avoid the chance of false positives. | ||
340 | return; | ||
341 | } | ||
342 | |||
343 | match is_useful(&cx, &seen, &PatStack::from_wild()) { | ||
344 | Ok(Usefulness::Useful) => (), | ||
345 | // if a wildcard pattern is not useful, then all patterns are covered | ||
346 | Ok(Usefulness::NotUseful) => return, | ||
347 | // this path is for unimplemented checks, so we err on the side of not | ||
348 | // reporting any errors | ||
349 | _ => return, | ||
350 | } | ||
351 | |||
352 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | ||
353 | let root = source_ptr.file_syntax(db.upcast()); | ||
354 | if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) { | ||
355 | if let (Some(match_expr), Some(arms)) = | ||
356 | (match_expr.expr(), match_expr.match_arm_list()) | ||
357 | { | ||
358 | self.sink.push(MissingMatchArms { | ||
359 | file: source_ptr.file_id, | ||
360 | match_expr: AstPtr::new(&match_expr), | ||
361 | arms: AstPtr::new(&arms), | ||
362 | }) | ||
363 | } | ||
364 | } | ||
365 | } | ||
366 | } | ||
367 | |||
368 | fn validate_match2( | ||
369 | &mut self, | ||
370 | id: ExprId, | ||
371 | match_expr: ExprId, | ||
372 | arms: &[MatchArm], | ||
373 | db: &dyn HirDatabase, | ||
374 | infer: Arc<InferenceResult>, | ||
375 | ) { | ||
376 | use crate::diagnostics::pattern::usefulness; | ||
377 | use hir_def::HasModule; | ||
378 | |||
379 | let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) = | ||
380 | db.body_with_source_map(self.owner); | ||
381 | |||
382 | let match_expr_ty = if infer.type_of_expr[match_expr].is_unknown() { | ||
383 | return; | ||
384 | } else { | ||
385 | &infer.type_of_expr[match_expr] | ||
386 | }; | ||
387 | |||
388 | let pattern_arena = RefCell::new(PatternArena::new()); | 302 | let pattern_arena = RefCell::new(PatternArena::new()); |
389 | 303 | ||
390 | let mut m_arms = Vec::new(); | 304 | let mut m_arms = Vec::new(); |
@@ -401,16 +315,17 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
401 | // necessary. | 315 | // necessary. |
402 | // | 316 | // |
403 | // FIXME we should use the type checker for this. | 317 | // FIXME we should use the type checker for this. |
404 | if pat_ty == match_expr_ty | 318 | if (pat_ty == match_expr_ty |
405 | || match_expr_ty | 319 | || match_expr_ty |
406 | .as_reference() | 320 | .as_reference() |
407 | .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) | 321 | .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty) |
408 | .unwrap_or(false) | 322 | .unwrap_or(false)) |
323 | && types_of_subpatterns_do_match(arm.pat, &body, &infer) | ||
409 | { | 324 | { |
410 | // If we had a NotUsefulMatchArm diagnostic, we could | 325 | // If we had a NotUsefulMatchArm diagnostic, we could |
411 | // check the usefulness of each pattern as we added it | 326 | // check the usefulness of each pattern as we added it |
412 | // to the matrix here. | 327 | // to the matrix here. |
413 | let m_arm = usefulness::MatchArm { | 328 | let m_arm = match_check::MatchArm { |
414 | pat: self.lower_pattern( | 329 | pat: self.lower_pattern( |
415 | arm.pat, | 330 | arm.pat, |
416 | &mut pattern_arena.borrow_mut(), | 331 | &mut pattern_arena.borrow_mut(), |
@@ -434,14 +349,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
434 | return; | 349 | return; |
435 | } | 350 | } |
436 | 351 | ||
437 | let cx = usefulness::MatchCheckCtx { | 352 | let cx = MatchCheckCtx { |
438 | module: self.owner.module(db.upcast()), | 353 | module: self.owner.module(db.upcast()), |
439 | match_expr, | 354 | match_expr, |
440 | infer: &infer, | 355 | infer: &infer, |
441 | db, | 356 | db, |
442 | pattern_arena: &pattern_arena, | 357 | pattern_arena: &pattern_arena, |
443 | }; | 358 | }; |
444 | let report = usefulness::compute_match_usefulness(&cx, &m_arms); | 359 | let report = compute_match_usefulness(&cx, &m_arms); |
445 | 360 | ||
446 | // FIXME Report unreacheble arms | 361 | // FIXME Report unreacheble arms |
447 | // https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200-L201 | 362 | // https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200-L201 |
@@ -473,8 +388,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
473 | db: &dyn HirDatabase, | 388 | db: &dyn HirDatabase, |
474 | body: &Body, | 389 | body: &Body, |
475 | have_errors: &mut bool, | 390 | have_errors: &mut bool, |
476 | ) -> pattern::PatId { | 391 | ) -> match_check::PatId { |
477 | let mut patcx = pattern::PatCtxt::new(db, &self.infer, body); | 392 | let mut patcx = match_check::PatCtxt::new(db, &self.infer, body); |
478 | let pattern = patcx.lower_pattern(pat); | 393 | let pattern = patcx.lower_pattern(pat); |
479 | let pattern = pattern_arena.alloc(expand_pattern(pattern)); | 394 | let pattern = pattern_arena.alloc(expand_pattern(pattern)); |
480 | if !patcx.errors.is_empty() { | 395 | if !patcx.errors.is_empty() { |