diff options
author | Benjamin Coenen <[email protected]> | 2020-04-07 16:59:09 +0100 |
---|---|---|
committer | Benjamin Coenen <[email protected]> | 2020-04-07 16:59:09 +0100 |
commit | 18a5e164838e1dc2abcc6b79d4fc2f96ffd2507c (patch) | |
tree | bc80b5c49c3b7ba31c7fe967bb34fe14bac9d5ed /crates/ra_hir_ty/src/expr.rs | |
parent | ab864ed259c10ff51f7c9c3421d098eeea7b0245 (diff) | |
parent | 33c364b545350134b945fbca834194fd1a28fe08 (diff) |
Merge branch 'master' of github.com:rust-analyzer/rust-analyzer
Diffstat (limited to 'crates/ra_hir_ty/src/expr.rs')
-rw-r--r-- | crates/ra_hir_ty/src/expr.rs | 89 |
1 files changed, 87 insertions, 2 deletions
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs index eb1209d08..6547eedae 100644 --- a/crates/ra_hir_ty/src/expr.rs +++ b/crates/ra_hir_ty/src/expr.rs | |||
@@ -13,9 +13,10 @@ use rustc_hash::FxHashSet; | |||
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | db::HirDatabase, | 15 | db::HirDatabase, |
16 | diagnostics::{MissingFields, MissingOkInTailExpr}, | 16 | diagnostics::{MissingFields, MissingMatchArms, MissingOkInTailExpr}, |
17 | utils::variant_data, | 17 | utils::variant_data, |
18 | ApplicationTy, InferenceResult, Ty, TypeCtor, | 18 | ApplicationTy, InferenceResult, Ty, TypeCtor, |
19 | _match::{is_useful, MatchCheckCtx, Matrix, PatStack, Usefulness}, | ||
19 | }; | 20 | }; |
20 | 21 | ||
21 | pub use hir_def::{ | 22 | pub use hir_def::{ |
@@ -51,15 +52,99 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
51 | for e in body.exprs.iter() { | 52 | for e in body.exprs.iter() { |
52 | if let (id, Expr::RecordLit { path, fields, spread }) = e { | 53 | if let (id, Expr::RecordLit { path, fields, spread }) = e { |
53 | self.validate_record_literal(id, path, fields, *spread, db); | 54 | self.validate_record_literal(id, path, fields, *spread, db); |
55 | } else if let (id, Expr::Match { expr, arms }) = e { | ||
56 | self.validate_match(id, *expr, arms, db, self.infer.clone()); | ||
54 | } | 57 | } |
55 | } | 58 | } |
56 | 59 | ||
57 | let body_expr = &body[body.body_expr]; | 60 | let body_expr = &body[body.body_expr]; |
58 | if let Expr::Block { statements: _, tail: Some(t) } = body_expr { | 61 | if let Expr::Block { tail: Some(t), .. } = body_expr { |
59 | self.validate_results_in_tail_expr(body.body_expr, *t, db); | 62 | self.validate_results_in_tail_expr(body.body_expr, *t, db); |
60 | } | 63 | } |
61 | } | 64 | } |
62 | 65 | ||
66 | fn validate_match( | ||
67 | &mut self, | ||
68 | id: ExprId, | ||
69 | match_expr: ExprId, | ||
70 | arms: &[MatchArm], | ||
71 | db: &dyn HirDatabase, | ||
72 | infer: Arc<InferenceResult>, | ||
73 | ) { | ||
74 | let (body, source_map): (Arc<Body>, Arc<BodySourceMap>) = | ||
75 | db.body_with_source_map(self.func.into()); | ||
76 | |||
77 | let match_expr_ty = match infer.type_of_expr.get(match_expr) { | ||
78 | Some(ty) => ty, | ||
79 | // If we can't resolve the type of the match expression | ||
80 | // we cannot perform exhaustiveness checks. | ||
81 | None => return, | ||
82 | }; | ||
83 | |||
84 | let cx = MatchCheckCtx { body, infer: infer.clone(), db }; | ||
85 | let pats = arms.iter().map(|arm| arm.pat); | ||
86 | |||
87 | let mut seen = Matrix::empty(); | ||
88 | for pat in pats { | ||
89 | // We skip any patterns whose type we cannot resolve. | ||
90 | // | ||
91 | // This could lead to false positives in this diagnostic, so | ||
92 | // it might be better to skip the entire diagnostic if we either | ||
93 | // cannot resolve a match arm or determine that the match arm has | ||
94 | // the wrong type. | ||
95 | if let Some(pat_ty) = infer.type_of_pat.get(pat) { | ||
96 | // We only include patterns whose type matches the type | ||
97 | // of the match expression. If we had a InvalidMatchArmPattern | ||
98 | // diagnostic or similar we could raise that in an else | ||
99 | // block here. | ||
100 | // | ||
101 | // When comparing the types, we also have to consider that rustc | ||
102 | // will automatically de-reference the match expression type if | ||
103 | // necessary. | ||
104 | // | ||
105 | // FIXME we should use the type checker for this. | ||
106 | if pat_ty == match_expr_ty | ||
107 | || match_expr_ty | ||
108 | .as_reference() | ||
109 | .map(|(match_expr_ty, _)| match_expr_ty == pat_ty) | ||
110 | .unwrap_or(false) | ||
111 | { | ||
112 | // If we had a NotUsefulMatchArm diagnostic, we could | ||
113 | // check the usefulness of each pattern as we added it | ||
114 | // to the matrix here. | ||
115 | let v = PatStack::from_pattern(pat); | ||
116 | seen.push(&cx, v); | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | |||
121 | match is_useful(&cx, &seen, &PatStack::from_wild()) { | ||
122 | Ok(Usefulness::Useful) => (), | ||
123 | // if a wildcard pattern is not useful, then all patterns are covered | ||
124 | Ok(Usefulness::NotUseful) => return, | ||
125 | // this path is for unimplemented checks, so we err on the side of not | ||
126 | // reporting any errors | ||
127 | _ => return, | ||
128 | } | ||
129 | |||
130 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | ||
131 | if let Some(expr) = source_ptr.value.left() { | ||
132 | let root = source_ptr.file_syntax(db.upcast()); | ||
133 | if let ast::Expr::MatchExpr(match_expr) = expr.to_node(&root) { | ||
134 | if let (Some(match_expr), Some(arms)) = | ||
135 | (match_expr.expr(), match_expr.match_arm_list()) | ||
136 | { | ||
137 | self.sink.push(MissingMatchArms { | ||
138 | file: source_ptr.file_id, | ||
139 | match_expr: AstPtr::new(&match_expr), | ||
140 | arms: AstPtr::new(&arms), | ||
141 | }) | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | } | ||
147 | |||
63 | fn validate_record_literal( | 148 | fn validate_record_literal( |
64 | &mut self, | 149 | &mut self, |
65 | id: ExprId, | 150 | id: ExprId, |