]> git.lizzy.rs Git - rust.git/blob - crates/ra_hir_ty/src/expr.rs
Replace `ra_hir_expand::either` with crate
[rust.git] / crates / ra_hir_ty / src / expr.rs
1 //! FIXME: write short doc here
2
3 use std::sync::Arc;
4
5 use hir_def::{
6     path::{known, Path},
7     resolver::HasResolver,
8     AdtId, FunctionId,
9 };
10 use hir_expand::{diagnostics::DiagnosticSink, name::Name};
11 use ra_syntax::ast;
12 use ra_syntax::AstPtr;
13 use rustc_hash::FxHashSet;
14
15 use crate::{
16     db::HirDatabase,
17     diagnostics::{MissingFields, MissingOkInTailExpr},
18     ApplicationTy, InferenceResult, Ty, TypeCtor,
19 };
20
21 pub use hir_def::{
22     body::{
23         scope::{ExprScopes, ScopeEntry, ScopeId},
24         Body, BodySourceMap, ExprPtr, ExprSource, PatPtr, PatSource,
25     },
26     expr::{
27         ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Literal, LogicOp,
28         MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, UnaryOp,
29     },
30 };
31
32 pub struct ExprValidator<'a, 'b: 'a> {
33     func: FunctionId,
34     infer: Arc<InferenceResult>,
35     sink: &'a mut DiagnosticSink<'b>,
36 }
37
38 impl<'a, 'b> ExprValidator<'a, 'b> {
39     pub fn new(
40         func: FunctionId,
41         infer: Arc<InferenceResult>,
42         sink: &'a mut DiagnosticSink<'b>,
43     ) -> ExprValidator<'a, 'b> {
44         ExprValidator { func, infer, sink }
45     }
46
47     pub fn validate_body(&mut self, db: &impl HirDatabase) {
48         let body = db.body(self.func.into());
49
50         for e in body.exprs.iter() {
51             if let (id, Expr::RecordLit { path, fields, spread }) = e {
52                 self.validate_record_literal(id, path, fields, *spread, db);
53             }
54         }
55
56         let body_expr = &body[body.body_expr];
57         if let Expr::Block { statements: _, tail: Some(t) } = body_expr {
58             self.validate_results_in_tail_expr(body.body_expr, *t, db);
59         }
60     }
61
62     fn validate_record_literal(
63         &mut self,
64         id: ExprId,
65         _path: &Option<Path>,
66         fields: &[RecordLitField],
67         spread: Option<ExprId>,
68         db: &impl HirDatabase,
69     ) {
70         if spread.is_some() {
71             return;
72         }
73
74         let struct_def = match self.infer[id].as_adt() {
75             Some((AdtId::StructId(s), _)) => s,
76             _ => return,
77         };
78         let struct_data = db.struct_data(struct_def);
79
80         let lit_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
81         let missed_fields: Vec<Name> = struct_data
82             .variant_data
83             .fields()
84             .iter()
85             .filter_map(|(_f, d)| {
86                 let name = d.name.clone();
87                 if lit_fields.contains(&name) {
88                     None
89                 } else {
90                     Some(name)
91                 }
92             })
93             .collect();
94         if missed_fields.is_empty() {
95             return;
96         }
97         let (_, source_map) = db.body_with_source_map(self.func.into());
98
99         if let Some(source_ptr) = source_map.expr_syntax(id) {
100             if let Some(expr) = source_ptr.value.left() {
101                 let root = source_ptr.file_syntax(db);
102                 if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
103                     if let Some(field_list) = record_lit.record_field_list() {
104                         self.sink.push(MissingFields {
105                             file: source_ptr.file_id,
106                             field_list: AstPtr::new(&field_list),
107                             missed_fields,
108                         })
109                     }
110                 }
111             }
112         }
113     }
114
115     fn validate_results_in_tail_expr(
116         &mut self,
117         body_id: ExprId,
118         id: ExprId,
119         db: &impl HirDatabase,
120     ) {
121         // the mismatch will be on the whole block currently
122         let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
123             Some(m) => m,
124             None => return,
125         };
126
127         let std_result_path = known::std_result_result();
128
129         let resolver = self.func.resolver(db);
130         let std_result_enum = match resolver.resolve_known_enum(db, &std_result_path) {
131             Some(it) => it,
132             _ => return,
133         };
134
135         let std_result_ctor = TypeCtor::Adt(AdtId::EnumId(std_result_enum));
136         let params = match &mismatch.expected {
137             Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters,
138             _ => return,
139         };
140
141         if params.len() == 2 && &params[0] == &mismatch.actual {
142             let (_, source_map) = db.body_with_source_map(self.func.into());
143
144             if let Some(source_ptr) = source_map.expr_syntax(id) {
145                 if let Some(expr) = source_ptr.value.left() {
146                     self.sink.push(MissingOkInTailExpr { file: source_ptr.file_id, expr });
147                 }
148             }
149         }
150     }
151 }