diff options
author | Phil Ellison <[email protected]> | 2019-08-11 15:00:37 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2019-08-25 10:55:56 +0100 |
commit | a40e390860987a23f9b899abc5947f1525d3709c (patch) | |
tree | 39aa52a99ce47d439ab92d0519681d4524240820 | |
parent | 62c2002e2b21b3a74a4e2205ccc40fa93f722b34 (diff) |
Check type rather than just name in ok-wrapping diagnostic. Add test for handling generic functions (which currently fails)
-rw-r--r-- | crates/ra_hir/src/expr/validation.rs | 46 | ||||
-rw-r--r-- | crates/ra_hir/src/name.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide_api/src/diagnostics.rs | 37 |
3 files changed, 77 insertions, 8 deletions
diff --git a/crates/ra_hir/src/expr/validation.rs b/crates/ra_hir/src/expr/validation.rs index ca7db61bc..339a7b848 100644 --- a/crates/ra_hir/src/expr/validation.rs +++ b/crates/ra_hir/src/expr/validation.rs | |||
@@ -6,10 +6,13 @@ 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 | code_model::Enum, | ||
9 | diagnostics::{DiagnosticSink, MissingFields, MissingOkInTailExpr}, | 10 | diagnostics::{DiagnosticSink, MissingFields, MissingOkInTailExpr}, |
10 | expr::AstPtr, | 11 | expr::AstPtr, |
12 | name, | ||
13 | path::{PathKind, PathSegment}, | ||
11 | ty::{InferenceResult, Ty, TypeCtor}, | 14 | ty::{InferenceResult, Ty, TypeCtor}, |
12 | Function, HasSource, HirDatabase, Name, Path, | 15 | Function, HasSource, HirDatabase, ModuleDef, Name, Path, PerNs, Resolution |
13 | }; | 16 | }; |
14 | use ra_syntax::ast; | 17 | use ra_syntax::ast; |
15 | 18 | ||
@@ -106,18 +109,45 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
106 | Some(m) => m, | 109 | Some(m) => m, |
107 | None => return, | 110 | None => return, |
108 | }; | 111 | }; |
109 | let ret = match &mismatch.expected { | 112 | |
110 | Ty::Apply(t) => t, | 113 | let std_result_path = Path { |
111 | _ => return, | 114 | kind: PathKind::Abs, |
115 | segments: vec![ | ||
116 | PathSegment { name: name::STD, args_and_bindings: None }, | ||
117 | PathSegment { name: name::RESULT_MOD, args_and_bindings: None }, | ||
118 | PathSegment { name: name::RESULT_TYPE, args_and_bindings: None }, | ||
119 | ] | ||
112 | }; | 120 | }; |
113 | let ret_enum = match ret.ctor { | 121 | |
114 | TypeCtor::Adt(AdtDef::Enum(e)) => e, | 122 | let resolver = self.func.resolver(db); |
123 | let std_result_enum = match resolver.resolve_path_segments(db, &std_result_path).into_fully_resolved() { | ||
124 | PerNs { types: Some(Resolution::Def(ModuleDef::Enum(e))), .. } => e, | ||
115 | _ => return, | 125 | _ => return, |
116 | }; | 126 | }; |
117 | let enum_name = ret_enum.name(db); | 127 | |
118 | if enum_name.is_none() || enum_name.unwrap().to_string() != "Result" { | 128 | let std_result_type = std_result_enum.ty(db); |
129 | |||
130 | fn enum_from_type(ty: &Ty) -> Option<Enum> { | ||
131 | match ty { | ||
132 | Ty::Apply(t) => { | ||
133 | match t.ctor { | ||
134 | TypeCtor::Adt(AdtDef::Enum(e)) => Some(e), | ||
135 | _ => None, | ||
136 | } | ||
137 | } | ||
138 | _ => None | ||
139 | } | ||
140 | } | ||
141 | |||
142 | if enum_from_type(&mismatch.expected) != enum_from_type(&std_result_type) { | ||
119 | return; | 143 | return; |
120 | } | 144 | } |
145 | |||
146 | let ret = match &mismatch.expected { | ||
147 | Ty::Apply(t) => t, | ||
148 | _ => return, | ||
149 | }; | ||
150 | |||
121 | let params = &ret.parameters; | 151 | let params = &ret.parameters; |
122 | if params.len() == 2 && ¶ms[0] == &mismatch.actual { | 152 | if params.len() == 2 && ¶ms[0] == &mismatch.actual { |
123 | let source_map = self.func.body_source_map(db); | 153 | let source_map = self.func.body_source_map(db); |
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index 6d14eea8e..9c4822d91 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs | |||
@@ -120,6 +120,8 @@ pub(crate) const TRY: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Try") | |||
120 | pub(crate) const OK: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok")); | 120 | pub(crate) const OK: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok")); |
121 | pub(crate) const FUTURE_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"future")); | 121 | pub(crate) const FUTURE_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"future")); |
122 | pub(crate) const FUTURE_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Future")); | 122 | pub(crate) const FUTURE_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Future")); |
123 | pub(crate) const RESULT_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"result")); | ||
124 | pub(crate) const RESULT_TYPE: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Result")); | ||
123 | pub(crate) const OUTPUT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output")); | 125 | pub(crate) const OUTPUT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output")); |
124 | 126 | ||
125 | fn resolve_name(text: &SmolStr) -> SmolStr { | 127 | fn resolve_name(text: &SmolStr) -> SmolStr { |
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 5e25991c6..57454719c 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs | |||
@@ -282,6 +282,43 @@ fn div(x: i32, y: i32) -> Result<i32, String> { | |||
282 | } | 282 | } |
283 | 283 | ||
284 | #[test] | 284 | #[test] |
285 | fn test_wrap_return_type_handles_generic_functions() { | ||
286 | let before = r#" | ||
287 | //- /main.rs | ||
288 | use std::{default::Default, result::Result::{self, Ok, Err}}; | ||
289 | |||
290 | fn div<T: Default, i32>(x: i32) -> Result<T, i32> { | ||
291 | if x == 0 { | ||
292 | return Err(7); | ||
293 | } | ||
294 | T::default() | ||
295 | } | ||
296 | |||
297 | //- /std/lib.rs | ||
298 | pub mod result { | ||
299 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
300 | } | ||
301 | pub mod default { | ||
302 | pub trait Default { | ||
303 | fn default() -> Self; | ||
304 | } | ||
305 | } | ||
306 | "#; | ||
307 | // The formatting here is a bit odd due to how the parse_fixture function works in test_utils - | ||
308 | // it strips empty lines and leading whitespace. The important part of this test is that the final | ||
309 | // `x / y` expr is now wrapped in `Ok(..)` | ||
310 | let after = r#"use std::{default::Default, result::Result::{self, Ok, Err}}; | ||
311 | fn div<T: Default>(x: i32) -> Result<T, i32> { | ||
312 | if x == 0 { | ||
313 | return Err(7); | ||
314 | } | ||
315 | Ok(T::default()) | ||
316 | } | ||
317 | "#; | ||
318 | check_apply_diagnostic_fix_for_target_file("/main.rs", before, after); | ||
319 | } | ||
320 | |||
321 | #[test] | ||
285 | fn test_wrap_return_type_handles_type_aliases() { | 322 | fn test_wrap_return_type_handles_type_aliases() { |
286 | let before = r#" | 323 | let before = r#" |
287 | //- /main.rs | 324 | //- /main.rs |