aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir')
-rw-r--r--crates/ra_hir/src/code_model.rs2
-rw-r--r--crates/ra_hir/src/expr.rs132
-rw-r--r--crates/ra_hir/src/expr/validation.rs137
3 files changed, 130 insertions, 141 deletions
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index dd43271f4..078bd8609 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -23,7 +23,7 @@ use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
23use crate::{ 23use crate::{
24 adt::VariantDef, 24 adt::VariantDef,
25 db::{AstDatabase, DefDatabase, HirDatabase}, 25 db::{AstDatabase, DefDatabase, HirDatabase},
26 expr::{validation::ExprValidator, BindingAnnotation, Body, BodySourceMap, Pat, PatId}, 26 expr::{BindingAnnotation, Body, BodySourceMap, ExprValidator, Pat, PatId},
27 generics::{GenericDef, HasGenericParams}, 27 generics::{GenericDef, HasGenericParams},
28 ids::{ 28 ids::{
29 AstItemDef, ConstId, EnumId, FunctionId, MacroDefId, StaticId, StructId, TraitId, 29 AstItemDef, ConstId, EnumId, FunctionId, MacroDefId, StaticId, StructId, TraitId,
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}
diff --git a/crates/ra_hir/src/expr/validation.rs b/crates/ra_hir/src/expr/validation.rs
deleted file mode 100644
index 3054f1dce..000000000
--- a/crates/ra_hir/src/expr/validation.rs
+++ /dev/null
@@ -1,137 +0,0 @@
1//! FIXME: write short doc here
2
3use std::sync::Arc;
4
5use hir_def::path::known;
6use hir_expand::diagnostics::DiagnosticSink;
7use ra_syntax::ast;
8use rustc_hash::FxHashSet;
9
10use crate::{
11 db::HirDatabase,
12 diagnostics::{MissingFields, MissingOkInTailExpr},
13 expr::AstPtr,
14 ty::{ApplicationTy, InferenceResult, Ty, TypeCtor},
15 Adt, Function, Name, Path,
16};
17
18use super::{Expr, ExprId, RecordLitField};
19
20pub(crate) struct ExprValidator<'a, 'b: 'a> {
21 func: Function,
22 infer: Arc<InferenceResult>,
23 sink: &'a mut DiagnosticSink<'b>,
24}
25
26impl<'a, 'b> ExprValidator<'a, 'b> {
27 pub(crate) fn new(
28 func: Function,
29 infer: Arc<InferenceResult>,
30 sink: &'a mut DiagnosticSink<'b>,
31 ) -> ExprValidator<'a, 'b> {
32 ExprValidator { func, infer, sink }
33 }
34
35 pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) {
36 let body = self.func.body(db);
37
38 for e in body.exprs() {
39 if let (id, Expr::RecordLit { path, fields, spread }) = e {
40 self.validate_record_literal(id, path, fields, *spread, db);
41 }
42 }
43
44 let body_expr = &body[body.body_expr()];
45 if let Expr::Block { statements: _, tail: Some(t) } = body_expr {
46 self.validate_results_in_tail_expr(body.body_expr(), *t, db);
47 }
48 }
49
50 fn validate_record_literal(
51 &mut self,
52 id: ExprId,
53 _path: &Option<Path>,
54 fields: &[RecordLitField],
55 spread: Option<ExprId>,
56 db: &impl HirDatabase,
57 ) {
58 if spread.is_some() {
59 return;
60 }
61
62 let struct_def = match self.infer[id].as_adt() {
63 Some((Adt::Struct(s), _)) => s,
64 _ => return,
65 };
66
67 let lit_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
68 let missed_fields: Vec<Name> = struct_def
69 .fields(db)
70 .iter()
71 .filter_map(|f| {
72 let name = f.name(db);
73 if lit_fields.contains(&name) {
74 None
75 } else {
76 Some(name)
77 }
78 })
79 .collect();
80 if missed_fields.is_empty() {
81 return;
82 }
83 let source_map = self.func.body_source_map(db);
84
85 if let Some(source_ptr) = source_map.expr_syntax(id) {
86 if let Some(expr) = source_ptr.ast.a() {
87 let root = source_ptr.file_syntax(db);
88 if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
89 if let Some(field_list) = record_lit.record_field_list() {
90 self.sink.push(MissingFields {
91 file: source_ptr.file_id,
92 field_list: AstPtr::new(&field_list),
93 missed_fields,
94 })
95 }
96 }
97 }
98 }
99 }
100
101 fn validate_results_in_tail_expr(
102 &mut self,
103 body_id: ExprId,
104 id: ExprId,
105 db: &impl HirDatabase,
106 ) {
107 // the mismatch will be on the whole block currently
108 let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
109 Some(m) => m,
110 None => return,
111 };
112
113 let std_result_path = known::std_result_result();
114
115 let resolver = self.func.resolver(db);
116 let std_result_enum = match resolver.resolve_known_enum(db, &std_result_path) {
117 Some(it) => it,
118 _ => return,
119 };
120
121 let std_result_ctor = TypeCtor::Adt(Adt::Enum(std_result_enum));
122 let params = match &mismatch.expected {
123 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters,
124 _ => return,
125 };
126
127 if params.len() == 2 && &params[0] == &mismatch.actual {
128 let source_map = self.func.body_source_map(db);
129
130 if let Some(source_ptr) = source_map.expr_syntax(id) {
131 if let Some(expr) = source_ptr.ast.a() {
132 self.sink.push(MissingOkInTailExpr { file: source_ptr.file_id, expr });
133 }
134 }
135 }
136 }
137}