aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty/src/expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_ty/src/expr.rs')
-rw-r--r--crates/ra_hir_ty/src/expr.rs89
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
14use crate::{ 14use 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
21pub use hir_def::{ 22pub 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,