aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/expr.rs')
-rw-r--r--crates/ra_hir/src/expr.rs132
1 files changed, 129 insertions, 3 deletions
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index e4598eec0..e3733779e 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -1,12 +1,19 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3pub(crate) mod validation;
4
5use std::sync::Arc; 3use std::sync::Arc;
6 4
5use hir_def::path::known;
6use hir_expand::diagnostics::DiagnosticSink;
7use ra_syntax::ast;
7use ra_syntax::AstPtr; 8use ra_syntax::AstPtr;
9use rustc_hash::FxHashSet;
8 10
9use crate::{db::HirDatabase, DefWithBody, HasBody, Resolver}; 11use crate::{
12 db::HirDatabase,
13 diagnostics::{MissingFields, MissingOkInTailExpr},
14 ty::{ApplicationTy, InferenceResult, Ty, TypeCtor},
15 Adt, DefWithBody, Function, HasBody, Name, Path, Resolver,
16};
10 17
11pub use hir_def::{ 18pub use hir_def::{
12 body::{ 19 body::{
@@ -42,3 +49,122 @@ pub(crate) fn resolver_for_scope(
42 } 49 }
43 r 50 r
44} 51}
52
53pub(crate) struct ExprValidator<'a, 'b: 'a> {
54 func: Function,
55 infer: Arc<InferenceResult>,
56 sink: &'a mut DiagnosticSink<'b>,
57}
58
59impl<'a, 'b> ExprValidator<'a, 'b> {
60 pub(crate) fn new(
61 func: Function,
62 infer: Arc<InferenceResult>,
63 sink: &'a mut DiagnosticSink<'b>,
64 ) -> ExprValidator<'a, 'b> {
65 ExprValidator { func, infer, sink }
66 }
67
68 pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) {
69 let body = self.func.body(db);
70
71 for e in body.exprs() {
72 if let (id, Expr::RecordLit { path, fields, spread }) = e {
73 self.validate_record_literal(id, path, fields, *spread, db);
74 }
75 }
76
77 let body_expr = &body[body.body_expr()];
78 if let Expr::Block { statements: _, tail: Some(t) } = body_expr {
79 self.validate_results_in_tail_expr(body.body_expr(), *t, db);
80 }
81 }
82
83 fn validate_record_literal(
84 &mut self,
85 id: ExprId,
86 _path: &Option<Path>,
87 fields: &[RecordLitField],
88 spread: Option<ExprId>,
89 db: &impl HirDatabase,
90 ) {
91 if spread.is_some() {
92 return;
93 }
94
95 let struct_def = match self.infer[id].as_adt() {
96 Some((Adt::Struct(s), _)) => s,
97 _ => return,
98 };
99
100 let lit_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
101 let missed_fields: Vec<Name> = struct_def
102 .fields(db)
103 .iter()
104 .filter_map(|f| {
105 let name = f.name(db);
106 if lit_fields.contains(&name) {
107 None
108 } else {
109 Some(name)
110 }
111 })
112 .collect();
113 if missed_fields.is_empty() {
114 return;
115 }
116 let source_map = self.func.body_source_map(db);
117
118 if let Some(source_ptr) = source_map.expr_syntax(id) {
119 if let Some(expr) = source_ptr.ast.a() {
120 let root = source_ptr.file_syntax(db);
121 if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
122 if let Some(field_list) = record_lit.record_field_list() {
123 self.sink.push(MissingFields {
124 file: source_ptr.file_id,
125 field_list: AstPtr::new(&field_list),
126 missed_fields,
127 })
128 }
129 }
130 }
131 }
132 }
133
134 fn validate_results_in_tail_expr(
135 &mut self,
136 body_id: ExprId,
137 id: ExprId,
138 db: &impl HirDatabase,
139 ) {
140 // the mismatch will be on the whole block currently
141 let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
142 Some(m) => m,
143 None => return,
144 };
145
146 let std_result_path = known::std_result_result();
147
148 let resolver = self.func.resolver(db);
149 let std_result_enum = match resolver.resolve_known_enum(db, &std_result_path) {
150 Some(it) => it,
151 _ => return,
152 };
153
154 let std_result_ctor = TypeCtor::Adt(Adt::Enum(std_result_enum));
155 let params = match &mismatch.expected {
156 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters,
157 _ => return,
158 };
159
160 if params.len() == 2 && &params[0] == &mismatch.actual {
161 let source_map = self.func.body_source_map(db);
162
163 if let Some(source_ptr) = source_map.expr_syntax(id) {
164 if let Some(expr) = source_ptr.ast.a() {
165 self.sink.push(MissingOkInTailExpr { file: source_ptr.file_id, expr });
166 }
167 }
168 }
169 }
170}