diff options
Diffstat (limited to 'crates/ra_hir')
-rw-r--r-- | crates/ra_hir/src/diagnostics.rs | 31 | ||||
-rw-r--r-- | crates/ra_hir/src/expr/validation.rs | 56 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 2 |
3 files changed, 86 insertions, 3 deletions
diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index 301109cb8..718345d75 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs | |||
@@ -143,3 +143,34 @@ impl AstDiagnostic for MissingFields { | |||
143 | ast::RecordFieldList::cast(node).unwrap() | 143 | ast::RecordFieldList::cast(node).unwrap() |
144 | } | 144 | } |
145 | } | 145 | } |
146 | |||
147 | #[derive(Debug)] | ||
148 | pub struct MissingOkInTailExpr { | ||
149 | pub file: HirFileId, | ||
150 | pub expr: AstPtr<ast::Expr>, | ||
151 | } | ||
152 | |||
153 | impl Diagnostic for MissingOkInTailExpr { | ||
154 | fn message(&self) -> String { | ||
155 | "wrap return expression in Ok".to_string() | ||
156 | } | ||
157 | fn file(&self) -> HirFileId { | ||
158 | self.file | ||
159 | } | ||
160 | fn syntax_node_ptr(&self) -> SyntaxNodePtr { | ||
161 | self.expr.into() | ||
162 | } | ||
163 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
164 | self | ||
165 | } | ||
166 | } | ||
167 | |||
168 | impl AstDiagnostic for MissingOkInTailExpr { | ||
169 | type AST = ast::Expr; | ||
170 | |||
171 | fn ast(&self, db: &impl HirDatabase) -> Self::AST { | ||
172 | let root = db.parse_or_expand(self.file()).unwrap(); | ||
173 | let node = self.syntax_node_ptr().to_node(&root); | ||
174 | ast::Expr::cast(node).unwrap() | ||
175 | } | ||
176 | } | ||
diff --git a/crates/ra_hir/src/expr/validation.rs b/crates/ra_hir/src/expr/validation.rs index 62f7d41f5..f5e641557 100644 --- a/crates/ra_hir/src/expr/validation.rs +++ b/crates/ra_hir/src/expr/validation.rs | |||
@@ -6,11 +6,12 @@ use ra_syntax::ast::{AstNode, RecordLit}; | |||
6 | use super::{Expr, ExprId, RecordLitField}; | 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 | ty::{InferenceResult, Ty, TypeCtor}, |
12 | Function, HasSource, HirDatabase, Name, Path, | 12 | Function, HasSource, HirDatabase, Name, Path, |
13 | }; | 13 | }; |
14 | use ra_syntax::ast; | ||
14 | 15 | ||
15 | pub(crate) struct ExprValidator<'a, 'b: 'a> { | 16 | pub(crate) struct ExprValidator<'a, 'b: 'a> { |
16 | func: Function, | 17 | func: Function, |
@@ -29,11 +30,23 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
29 | 30 | ||
30 | pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) { | 31 | pub(crate) fn validate_body(&mut self, db: &impl HirDatabase) { |
31 | let body = self.func.body(db); | 32 | let body = self.func.body(db); |
33 | |||
34 | // The final expr in the function body is the whole body, | ||
35 | // so the expression being returned is the penultimate expr. | ||
36 | let mut penultimate_expr = None; | ||
37 | let mut final_expr = None; | ||
38 | |||
32 | for e in body.exprs() { | 39 | for e in body.exprs() { |
40 | penultimate_expr = final_expr; | ||
41 | final_expr = Some(e); | ||
42 | |||
33 | if let (id, Expr::RecordLit { path, fields, spread }) = e { | 43 | if let (id, Expr::RecordLit { path, fields, spread }) = e { |
34 | self.validate_record_literal(id, path, fields, *spread, db); | 44 | self.validate_record_literal(id, path, fields, *spread, db); |
35 | } | 45 | } |
36 | } | 46 | } |
47 | if let Some(e) = penultimate_expr { | ||
48 | self.validate_results_in_tail_expr(e.0, db); | ||
49 | } | ||
37 | } | 50 | } |
38 | 51 | ||
39 | fn validate_record_literal( | 52 | fn validate_record_literal( |
@@ -87,4 +100,43 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
87 | }) | 100 | }) |
88 | } | 101 | } |
89 | } | 102 | } |
103 | |||
104 | fn validate_results_in_tail_expr(&mut self, id: ExprId, db: &impl HirDatabase) { | ||
105 | let expr_ty = &self.infer[id]; | ||
106 | let func_ty = self.func.ty(db); | ||
107 | let func_sig = func_ty.callable_sig(db).unwrap(); | ||
108 | let ret = func_sig.ret(); | ||
109 | let ret = match ret { | ||
110 | Ty::Apply(t) => t, | ||
111 | _ => return, | ||
112 | }; | ||
113 | let ret_enum = match ret.ctor { | ||
114 | TypeCtor::Adt(AdtDef::Enum(e)) => e, | ||
115 | _ => return, | ||
116 | }; | ||
117 | let enum_name = ret_enum.name(db); | ||
118 | if enum_name.is_none() || enum_name.unwrap().to_string() != "Result" { | ||
119 | return; | ||
120 | } | ||
121 | let params = &ret.parameters; | ||
122 | if params.len() == 2 && ¶ms[0] == expr_ty { | ||
123 | let source_map = self.func.body_source_map(db); | ||
124 | let file_id = self.func.source(db).file_id; | ||
125 | let parse = db.parse(file_id.original_file(db)); | ||
126 | let source_file = parse.tree(); | ||
127 | let expr_syntax = source_map.expr_syntax(id); | ||
128 | if expr_syntax.is_none() { | ||
129 | return; | ||
130 | } | ||
131 | let expr_syntax = expr_syntax.unwrap(); | ||
132 | let node = expr_syntax.to_node(source_file.syntax()); | ||
133 | let ast = ast::Expr::cast(node); | ||
134 | if ast.is_none() { | ||
135 | return; | ||
136 | } | ||
137 | let ast = ast.unwrap(); | ||
138 | |||
139 | self.sink.push(MissingOkInTailExpr { file: file_id, expr: AstPtr::new(&ast) }); | ||
140 | } | ||
141 | } | ||
90 | } | 142 | } |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index b54c80318..0efd94cef 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -516,7 +516,7 @@ impl Ty { | |||
516 | } | 516 | } |
517 | } | 517 | } |
518 | 518 | ||
519 | fn callable_sig(&self, db: &impl HirDatabase) -> Option<FnSig> { | 519 | pub fn callable_sig(&self, db: &impl HirDatabase) -> Option<FnSig> { |
520 | match self { | 520 | match self { |
521 | Ty::Apply(a_ty) => match a_ty.ctor { | 521 | Ty::Apply(a_ty) => match a_ty.ctor { |
522 | TypeCtor::FnPtr { .. } => Some(FnSig::from_fn_ptr_substs(&a_ty.parameters)), | 522 | TypeCtor::FnPtr { .. } => Some(FnSig::from_fn_ptr_substs(&a_ty.parameters)), |