aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir')
-rw-r--r--crates/ra_hir/src/diagnostics.rs31
-rw-r--r--crates/ra_hir/src/expr/validation.rs56
-rw-r--r--crates/ra_hir/src/ty.rs2
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)]
148pub struct MissingOkInTailExpr {
149 pub file: HirFileId,
150 pub expr: AstPtr<ast::Expr>,
151}
152
153impl 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
168impl 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};
6use super::{Expr, ExprId, RecordLitField}; 6use super::{Expr, ExprId, RecordLitField};
7use crate::{ 7use 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};
14use ra_syntax::ast;
14 15
15pub(crate) struct ExprValidator<'a, 'b: 'a> { 16pub(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 && &params[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)),