Skip to content

Commit a40e390

Browse files
theotherphilmatklad
authored andcommitted
Check type rather than just name in ok-wrapping diagnostic. Add test for handling generic functions (which currently fails)
1 parent 62c2002 commit a40e390

File tree

3 files changed

+77
-8
lines changed

3 files changed

+77
-8
lines changed

crates/ra_hir/src/expr/validation.rs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ use ra_syntax::ast::{AstNode, RecordLit};
66
use super::{Expr, ExprId, RecordLitField};
77
use crate::{
88
adt::AdtDef,
9+
code_model::Enum,
910
diagnostics::{DiagnosticSink, MissingFields, MissingOkInTailExpr},
1011
expr::AstPtr,
12+
name,
13+
path::{PathKind, PathSegment},
1114
ty::{InferenceResult, Ty, TypeCtor},
12-
Function, HasSource, HirDatabase, Name, Path,
15+
Function, HasSource, HirDatabase, ModuleDef, Name, Path, PerNs, Resolution
1316
};
1417
use ra_syntax::ast;
1518

@@ -106,18 +109,45 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
106109
Some(m) => m,
107110
None => return,
108111
};
109-
let ret = match &mismatch.expected {
110-
Ty::Apply(t) => t,
111-
_ => return,
112+
113+
let std_result_path = Path {
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+
]
112120
};
113-
let ret_enum = match ret.ctor {
114-
TypeCtor::Adt(AdtDef::Enum(e)) => e,
121+
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,
115125
_ => return,
116126
};
117-
let enum_name = ret_enum.name(db);
118-
if enum_name.is_none() || enum_name.unwrap().to_string() != "Result" {
127+
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) {
119143
return;
120144
}
145+
146+
let ret = match &mismatch.expected {
147+
Ty::Apply(t) => t,
148+
_ => return,
149+
};
150+
121151
let params = &ret.parameters;
122152
if params.len() == 2 && &params[0] == &mismatch.actual {
123153
let source_map = self.func.body_source_map(db);

crates/ra_hir/src/name.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ pub(crate) const TRY: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Try")
120120
pub(crate) const OK: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok"));
121121
pub(crate) const FUTURE_MOD: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"future"));
122122
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"));
123125
pub(crate) const OUTPUT: Name = Name::new(SmolStr::new_inline_from_ascii(6, b"Output"));
124126

125127
fn resolve_name(text: &SmolStr) -> SmolStr {

crates/ra_ide_api/src/diagnostics.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,43 @@ fn div(x: i32, y: i32) -> Result<i32, String> {
281281
check_apply_diagnostic_fix_for_target_file("/main.rs", before, after);
282282
}
283283

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+
284321
#[test]
285322
fn test_wrap_return_type_handles_type_aliases() {
286323
let before = r#"

0 commit comments

Comments
 (0)