diff options
Diffstat (limited to 'crates/ra_hir/src/expr')
-rw-r--r-- | crates/ra_hir/src/expr/validation.rs | 69 |
1 files changed, 58 insertions, 11 deletions
diff --git a/crates/ra_hir/src/expr/validation.rs b/crates/ra_hir/src/expr/validation.rs index 82a06ca25..5d9d59ff8 100644 --- a/crates/ra_hir/src/expr/validation.rs +++ b/crates/ra_hir/src/expr/validation.rs | |||
@@ -1,16 +1,19 @@ | |||
1 | use rustc_hash::FxHashSet; | 1 | use rustc_hash::FxHashSet; |
2 | use std::sync::Arc; | 2 | use std::sync::Arc; |
3 | 3 | ||
4 | use ra_syntax::ast::{AstNode, StructLit}; | 4 | use ra_syntax::ast::{AstNode, RecordLit}; |
5 | 5 | ||
6 | use super::{Expr, ExprId, StructLitField}; | 6 | use super::{Expr, ExprId, RecordLitField}; |
7 | use crate::{ | 7 | use crate::{ |
8 | adt::AdtDef, | 8 | adt::AdtDef, |
9 | diagnostics::{DiagnosticSink, MissingFields}, | 9 | diagnostics::{DiagnosticSink, MissingFields, MissingOkInTailExpr}, |
10 | expr::AstPtr, | 10 | expr::AstPtr, |
11 | ty::InferenceResult, | 11 | name, |
12 | Function, HasSource, HirDatabase, Name, Path, | 12 | path::{PathKind, PathSegment}, |
13 | ty::{ApplicationTy, InferenceResult, Ty, TypeCtor}, | ||
14 | Function, HasSource, HirDatabase, ModuleDef, Name, Path, PerNs, Resolution, | ||
13 | }; | 15 | }; |
16 | use ra_syntax::ast; | ||
14 | 17 | ||
15 | pub(crate) struct ExprValidator<'a, 'b: 'a> { | 18 | pub(crate) struct ExprValidator<'a, 'b: 'a> { |
16 | func: Function, | 19 | func: Function, |
@@ -29,18 +32,24 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
29 | 32 | ||
30 | pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) { | 33 | pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) { |
31 | let body = self.func.body(db); | 34 | let body = self.func.body(db); |
35 | |||
32 | for e in body.exprs() { | 36 | for e in body.exprs() { |
33 | if let (id, Expr::StructLit { path, fields, spread }) = e { | 37 | if let (id, Expr::RecordLit { path, fields, spread }) = e { |
34 | self.validate_struct_literal(id, path, fields, *spread, db); | 38 | self.validate_record_literal(id, path, fields, *spread, db); |
35 | } | 39 | } |
36 | } | 40 | } |
41 | |||
42 | let body_expr = &body[body.body_expr()]; | ||
43 | if let Expr::Block { statements: _, tail: Some(t) } = body_expr { | ||
44 | self.validate_results_in_tail_expr(*t, db); | ||
45 | } | ||
37 | } | 46 | } |
38 | 47 | ||
39 | fn validate_struct_literal( | 48 | fn validate_record_literal( |
40 | &mut self, | 49 | &mut self, |
41 | id: ExprId, | 50 | id: ExprId, |
42 | _path: &Option<Path>, | 51 | _path: &Option<Path>, |
43 | fields: &[StructLitField], | 52 | fields: &[RecordLitField], |
44 | spread: Option<ExprId>, | 53 | spread: Option<ExprId>, |
45 | db: &impl HirDatabase, | 54 | db: &impl HirDatabase, |
46 | ) { | 55 | ) { |
@@ -76,8 +85,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
76 | if let Some(field_list_node) = source_map | 85 | if let Some(field_list_node) = source_map |
77 | .expr_syntax(id) | 86 | .expr_syntax(id) |
78 | .map(|ptr| ptr.to_node(source_file.syntax())) | 87 | .map(|ptr| ptr.to_node(source_file.syntax())) |
79 | .and_then(StructLit::cast) | 88 | .and_then(RecordLit::cast) |
80 | .and_then(|lit| lit.named_field_list()) | 89 | .and_then(|lit| lit.record_field_list()) |
81 | { | 90 | { |
82 | let field_list_ptr = AstPtr::new(&field_list_node); | 91 | let field_list_ptr = AstPtr::new(&field_list_node); |
83 | self.sink.push(MissingFields { | 92 | self.sink.push(MissingFields { |
@@ -87,4 +96,42 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
87 | }) | 96 | }) |
88 | } | 97 | } |
89 | } | 98 | } |
99 | |||
100 | fn validate_results_in_tail_expr(&mut self, id: ExprId, db: &impl HirDatabase) { | ||
101 | let mismatch = match self.infer.type_mismatch_for_expr(id) { | ||
102 | Some(m) => m, | ||
103 | None => return, | ||
104 | }; | ||
105 | |||
106 | let std_result_path = Path { | ||
107 | kind: PathKind::Abs, | ||
108 | segments: vec![ | ||
109 | PathSegment { name: name::STD, args_and_bindings: None }, | ||
110 | PathSegment { name: name::RESULT_MOD, args_and_bindings: None }, | ||
111 | PathSegment { name: name::RESULT_TYPE, args_and_bindings: None }, | ||
112 | ], | ||
113 | }; | ||
114 | |||
115 | let resolver = self.func.resolver(db); | ||
116 | let std_result_enum = | ||
117 | match resolver.resolve_path_segments(db, &std_result_path).into_fully_resolved() { | ||
118 | PerNs { types: Some(Resolution::Def(ModuleDef::Enum(e))), .. } => e, | ||
119 | _ => return, | ||
120 | }; | ||
121 | |||
122 | let std_result_ctor = TypeCtor::Adt(AdtDef::Enum(std_result_enum)); | ||
123 | let params = match &mismatch.expected { | ||
124 | Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters, | ||
125 | _ => return, | ||
126 | }; | ||
127 | |||
128 | if params.len() == 2 && ¶ms[0] == &mismatch.actual { | ||
129 | let source_map = self.func.body_source_map(db); | ||
130 | let file_id = self.func.source(db).file_id; | ||
131 | |||
132 | if let Some(expr) = source_map.expr_syntax(id).and_then(|n| n.cast::<ast::Expr>()) { | ||
133 | self.sink.push(MissingOkInTailExpr { file: file_id, expr }); | ||
134 | } | ||
135 | } | ||
136 | } | ||
90 | } | 137 | } |