mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 20:43:21 +00:00
Merge #11842
11842: Fix duplicate type mismatches with blocks r=flodiebold a=flodiebold E.g. when there's a type mismatch on the return value of a function. To fix this, we have to return the expected type as the type of the block when there's a mismatch. That meant some IDE code that expected otherwise had to be adapted, in particular the "add return type" assist. For the "wrap in Ok/Some" quickfix, this sadly means it usually can't be applied in all branches of an if expression at the same time anymore, because there's a type mismatch for each branch that has the wrong type. Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
9eb7553d5a
12 changed files with 212 additions and 83 deletions
|
@ -69,12 +69,11 @@ impl<'a> InferenceContext<'a> {
|
||||||
match self.coerce(Some(expr), &ty, &target) {
|
match self.coerce(Some(expr), &ty, &target) {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
self.result
|
self.result.type_mismatches.insert(
|
||||||
.type_mismatches
|
expr.into(),
|
||||||
.insert(expr.into(), TypeMismatch { expected: target, actual: ty.clone() });
|
TypeMismatch { expected: target.clone(), actual: ty.clone() },
|
||||||
// Return actual type when type mismatch.
|
);
|
||||||
// This is needed for diagnostic when return type mismatch.
|
target
|
||||||
ty
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -914,9 +913,16 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.table.new_maybe_never_var()
|
self.table.new_maybe_never_var()
|
||||||
} else {
|
} else {
|
||||||
if let Some(t) = expected.only_has_type(&mut self.table) {
|
if let Some(t) = expected.only_has_type(&mut self.table) {
|
||||||
let _ = self.coerce(Some(expr), &TyBuilder::unit(), &t);
|
if self.coerce(Some(expr), &TyBuilder::unit(), &t).is_err() {
|
||||||
|
self.result.type_mismatches.insert(
|
||||||
|
expr.into(),
|
||||||
|
TypeMismatch { expected: t.clone(), actual: TyBuilder::unit() },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
t
|
||||||
|
} else {
|
||||||
|
TyBuilder::unit()
|
||||||
}
|
}
|
||||||
TyBuilder::unit()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ mod method_resolution;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod display_source_code;
|
mod display_source_code;
|
||||||
mod incremental;
|
mod incremental;
|
||||||
|
mod diagnostics;
|
||||||
|
|
||||||
use std::{collections::HashMap, env, sync::Arc};
|
use std::{collections::HashMap, env, sync::Arc};
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,10 @@ use super::{check, check_no_mismatches, check_types};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_expr_type_mismatch() {
|
fn block_expr_type_mismatch() {
|
||||||
// FIXME fix double type mismatch
|
|
||||||
check(
|
check(
|
||||||
r"
|
r"
|
||||||
fn test() {
|
fn test() {
|
||||||
let a: i32 = { 1i64 };
|
let a: i32 = { 1i64 };
|
||||||
// ^^^^^^^^ expected i32, got i64
|
|
||||||
// ^^^^ expected i32, got i64
|
// ^^^^ expected i32, got i64
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
|
|
75
crates/hir_ty/src/tests/diagnostics.rs
Normal file
75
crates/hir_ty/src/tests/diagnostics.rs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
use super::check;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_return_type_mismatch_1() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn test() -> &'static str {
|
||||||
|
5
|
||||||
|
//^ expected &str, got i32
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_return_type_mismatch_2() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn test(x: bool) -> &'static str {
|
||||||
|
if x {
|
||||||
|
return 1;
|
||||||
|
//^ expected &str, got i32
|
||||||
|
}
|
||||||
|
"ok"
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_return_type_mismatch_3() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn test(x: bool) -> &'static str {
|
||||||
|
if x {
|
||||||
|
return "ok";
|
||||||
|
}
|
||||||
|
1
|
||||||
|
//^ expected &str, got i32
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_return_type_mismatch_4() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn test(x: bool) -> &'static str {
|
||||||
|
if x {
|
||||||
|
"ok"
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
//^ expected &str, got i32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_return_type_mismatch_5() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn test(x: bool) -> &'static str {
|
||||||
|
if x {
|
||||||
|
1
|
||||||
|
//^ expected &str, got i32
|
||||||
|
} else {
|
||||||
|
"ok"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
|
@ -314,11 +314,10 @@ fn diverging_expression_2() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
11..84 '{ ..." }; }': ()
|
11..84 '{ ..." }; }': ()
|
||||||
54..55 'x': u32
|
54..55 'x': u32
|
||||||
63..81 '{ loop...foo" }': &str
|
63..81 '{ loop...foo" }': u32
|
||||||
65..72 'loop {}': !
|
65..72 'loop {}': !
|
||||||
70..72 '{}': ()
|
70..72 '{}': ()
|
||||||
74..79 '"foo"': &str
|
74..79 '"foo"': &str
|
||||||
63..81: expected u32, got &str
|
|
||||||
74..79: expected u32, got &str
|
74..79: expected u32, got &str
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
@ -350,31 +349,30 @@ fn diverging_expression_3_break() {
|
||||||
let x: u32 = { while true { return; }; };
|
let x: u32 = { while true { return; }; };
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
expect![[r"
|
expect![[r#"
|
||||||
11..85 '{ ...} }; }': ()
|
11..85 '{ ...} }; }': ()
|
||||||
54..55 'x': u32
|
54..55 'x': u32
|
||||||
63..82 '{ loop...k; } }': ()
|
63..82 '{ loop...k; } }': u32
|
||||||
65..80 'loop { break; }': ()
|
65..80 'loop { break; }': ()
|
||||||
70..80 '{ break; }': ()
|
70..80 '{ break; }': ()
|
||||||
72..77 'break': !
|
72..77 'break': !
|
||||||
63..82: expected u32, got ()
|
|
||||||
65..80: expected u32, got ()
|
65..80: expected u32, got ()
|
||||||
97..343 '{ ...; }; }': ()
|
97..343 '{ ...; }; }': ()
|
||||||
140..141 'x': u32
|
140..141 'x': u32
|
||||||
149..175 '{ for ...; }; }': ()
|
149..175 '{ for ...; }; }': u32
|
||||||
151..172 'for a ...eak; }': ()
|
151..172 'for a ...eak; }': ()
|
||||||
155..156 'a': {unknown}
|
155..156 'a': {unknown}
|
||||||
160..161 'b': {unknown}
|
160..161 'b': {unknown}
|
||||||
162..172 '{ break; }': ()
|
162..172 '{ break; }': ()
|
||||||
164..169 'break': !
|
164..169 'break': !
|
||||||
226..227 'x': u32
|
226..227 'x': u32
|
||||||
235..253 '{ for ... {}; }': ()
|
235..253 '{ for ... {}; }': u32
|
||||||
237..250 'for a in b {}': ()
|
237..250 'for a in b {}': ()
|
||||||
241..242 'a': {unknown}
|
241..242 'a': {unknown}
|
||||||
246..247 'b': {unknown}
|
246..247 'b': {unknown}
|
||||||
248..250 '{}': ()
|
248..250 '{}': ()
|
||||||
304..305 'x': u32
|
304..305 'x': u32
|
||||||
313..340 '{ for ...; }; }': ()
|
313..340 '{ for ...; }; }': u32
|
||||||
315..337 'for a ...urn; }': ()
|
315..337 'for a ...urn; }': ()
|
||||||
319..320 'a': {unknown}
|
319..320 'a': {unknown}
|
||||||
324..325 'b': {unknown}
|
324..325 'b': {unknown}
|
||||||
|
@ -385,18 +383,18 @@ fn diverging_expression_3_break() {
|
||||||
313..340: expected u32, got ()
|
313..340: expected u32, got ()
|
||||||
355..654 '{ ...; }; }': ()
|
355..654 '{ ...; }; }': ()
|
||||||
398..399 'x': u32
|
398..399 'x': u32
|
||||||
407..433 '{ whil...; }; }': ()
|
407..433 '{ whil...; }; }': u32
|
||||||
409..430 'while ...eak; }': ()
|
409..430 'while ...eak; }': ()
|
||||||
415..419 'true': bool
|
415..419 'true': bool
|
||||||
420..430 '{ break; }': ()
|
420..430 '{ break; }': ()
|
||||||
422..427 'break': !
|
422..427 'break': !
|
||||||
537..538 'x': u32
|
537..538 'x': u32
|
||||||
546..564 '{ whil... {}; }': ()
|
546..564 '{ whil... {}; }': u32
|
||||||
548..561 'while true {}': ()
|
548..561 'while true {}': ()
|
||||||
554..558 'true': bool
|
554..558 'true': bool
|
||||||
559..561 '{}': ()
|
559..561 '{}': ()
|
||||||
615..616 'x': u32
|
615..616 'x': u32
|
||||||
624..651 '{ whil...; }; }': ()
|
624..651 '{ whil...; }; }': u32
|
||||||
626..648 'while ...urn; }': ()
|
626..648 'while ...urn; }': ()
|
||||||
632..636 'true': bool
|
632..636 'true': bool
|
||||||
637..648 '{ return; }': ()
|
637..648 '{ return; }': ()
|
||||||
|
@ -404,7 +402,7 @@ fn diverging_expression_3_break() {
|
||||||
407..433: expected u32, got ()
|
407..433: expected u32, got ()
|
||||||
546..564: expected u32, got ()
|
546..564: expected u32, got ()
|
||||||
624..651: expected u32, got ()
|
624..651: expected u32, got ()
|
||||||
"]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,7 +436,7 @@ fn let_else_must_diverge() {
|
||||||
17..18 '1': i32
|
17..18 '1': i32
|
||||||
17..18 '1': i32
|
17..18 '1': i32
|
||||||
21..22 '2': i32
|
21..22 '2': i32
|
||||||
28..30 '{}': ()
|
28..30 '{}': !
|
||||||
28..30: expected !, got ()
|
28..30: expected !, got ()
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
|
|
@ -367,7 +367,7 @@ fn bug_1030() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
143..145 '{}': ()
|
143..145 '{}': HashSet<T, H>
|
||||||
168..197 '{ ...t(); }': ()
|
168..197 '{ ...t(); }': ()
|
||||||
174..192 'FxHash...efault': fn default<{unknown}, FxHasher>() -> HashSet<{unknown}, FxHasher>
|
174..192 'FxHash...efault': fn default<{unknown}, FxHasher>() -> HashSet<{unknown}, FxHasher>
|
||||||
174..194 'FxHash...ault()': HashSet<{unknown}, FxHasher>
|
174..194 'FxHash...ault()': HashSet<{unknown}, FxHasher>
|
||||||
|
@ -831,7 +831,7 @@ fn issue_4966() {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
225..229 'iter': T
|
225..229 'iter': T
|
||||||
244..246 '{}': ()
|
244..246 '{}': Vec<A>
|
||||||
258..402 '{ ...r(); }': ()
|
258..402 '{ ...r(); }': ()
|
||||||
268..273 'inner': Map<|&f64| -> f64>
|
268..273 'inner': Map<|&f64| -> f64>
|
||||||
276..300 'Map { ... 0.0 }': Map<|&f64| -> f64>
|
276..300 'Map { ... 0.0 }': Map<|&f64| -> f64>
|
||||||
|
@ -914,7 +914,7 @@ fn flush(&self) {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
123..127 'self': &Mutex<T>
|
123..127 'self': &Mutex<T>
|
||||||
150..152 '{}': ()
|
150..152 '{}': MutexGuard<T>
|
||||||
234..238 'self': &{unknown}
|
234..238 'self': &{unknown}
|
||||||
240..290 '{ ...()); }': ()
|
240..290 '{ ...()); }': ()
|
||||||
250..251 'w': &Mutex<BufWriter>
|
250..251 'w': &Mutex<BufWriter>
|
||||||
|
@ -1039,18 +1039,18 @@ fn cfg_tail() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
14..53 '{ ...)] 9 }': &str
|
14..53 '{ ...)] 9 }': ()
|
||||||
20..31 '{ "first" }': &str
|
20..31 '{ "first" }': ()
|
||||||
22..29 '"first"': &str
|
22..29 '"first"': &str
|
||||||
72..190 '{ ...] 13 }': &str
|
72..190 '{ ...] 13 }': ()
|
||||||
78..88 '{ "fake" }': &str
|
78..88 '{ "fake" }': &str
|
||||||
80..86 '"fake"': &str
|
80..86 '"fake"': &str
|
||||||
93..103 '{ "fake" }': &str
|
93..103 '{ "fake" }': &str
|
||||||
95..101 '"fake"': &str
|
95..101 '"fake"': &str
|
||||||
108..120 '{ "second" }': &str
|
108..120 '{ "second" }': ()
|
||||||
110..118 '"second"': &str
|
110..118 '"second"': &str
|
||||||
210..273 '{ ... 15; }': &str
|
210..273 '{ ... 15; }': ()
|
||||||
216..227 '{ "third" }': &str
|
216..227 '{ "third" }': ()
|
||||||
218..225 '"third"': &str
|
218..225 '"third"': &str
|
||||||
293..357 '{ ...] 15 }': ()
|
293..357 '{ ...] 15 }': ()
|
||||||
299..311 '{ "fourth" }': &str
|
299..311 '{ "fourth" }': &str
|
||||||
|
|
|
@ -996,9 +996,9 @@ fn main(foo: Foo) {
|
||||||
50..106 'if tru... }': ()
|
50..106 'if tru... }': ()
|
||||||
53..57 'true': bool
|
53..57 'true': bool
|
||||||
58..66 '{ }': ()
|
58..66 '{ }': ()
|
||||||
72..106 'if fal... }': i32
|
72..106 'if fal... }': ()
|
||||||
75..80 'false': bool
|
75..80 'false': bool
|
||||||
81..106 '{ ... }': i32
|
81..106 '{ ... }': ()
|
||||||
91..94 'foo': Foo
|
91..94 'foo': Foo
|
||||||
91..100 'foo.field': i32
|
91..100 'foo.field': i32
|
||||||
"#]],
|
"#]],
|
||||||
|
@ -1094,10 +1094,10 @@ fn infer_inherent_method() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
31..35 'self': A
|
31..35 'self': A
|
||||||
37..38 'x': u32
|
37..38 'x': u32
|
||||||
52..54 '{}': ()
|
52..54 '{}': i32
|
||||||
106..110 'self': &A
|
106..110 'self': &A
|
||||||
112..113 'x': u64
|
112..113 'x': u64
|
||||||
127..129 '{}': ()
|
127..129 '{}': i64
|
||||||
147..148 'a': A
|
147..148 'a': A
|
||||||
153..201 '{ ...(1); }': ()
|
153..201 '{ ...(1); }': ()
|
||||||
159..160 'a': A
|
159..160 'a': A
|
||||||
|
@ -1129,7 +1129,7 @@ fn infer_inherent_method_str() {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
39..43 'self': &str
|
39..43 'self': &str
|
||||||
52..54 '{}': ()
|
52..54 '{}': i32
|
||||||
68..88 '{ ...o(); }': ()
|
68..88 '{ ...o(); }': ()
|
||||||
74..79 '"foo"': &str
|
74..79 '"foo"': &str
|
||||||
74..85 '"foo".foo()': i32
|
74..85 '"foo".foo()': i32
|
||||||
|
@ -1419,7 +1419,7 @@ fn infer_impl_generics_basic() {
|
||||||
206..210 'self': A<X, Y>
|
206..210 'self': A<X, Y>
|
||||||
206..212 'self.y': Y
|
206..212 'self.y': Y
|
||||||
214..215 't': T
|
214..215 't': T
|
||||||
244..341 '{ ...(1); }': ()
|
244..341 '{ ...(1); }': i128
|
||||||
254..255 'a': A<u64, i64>
|
254..255 'a': A<u64, i64>
|
||||||
258..280 'A { x:...1i64 }': A<u64, i64>
|
258..280 'A { x:...1i64 }': A<u64, i64>
|
||||||
265..269 '1u64': u64
|
265..269 '1u64': u64
|
||||||
|
@ -1456,7 +1456,7 @@ fn infer_impl_generics_with_autoderef() {
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
77..81 'self': &Option<T>
|
77..81 'self': &Option<T>
|
||||||
97..99 '{}': ()
|
97..99 '{}': Option<&T>
|
||||||
110..111 'o': Option<u32>
|
110..111 'o': Option<u32>
|
||||||
126..164 '{ ...f(); }': ()
|
126..164 '{ ...f(); }': ()
|
||||||
132..145 '(&o).as_ref()': Option<&u32>
|
132..145 '(&o).as_ref()': Option<&u32>
|
||||||
|
@ -1852,7 +1852,7 @@ fn closure_return() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
16..58 '{ ...; }; }': ()
|
16..58 '{ ...; }; }': u32
|
||||||
26..27 'x': || -> usize
|
26..27 'x': || -> usize
|
||||||
30..55 '|| -> ...n 1; }': || -> usize
|
30..55 '|| -> ...n 1; }': || -> usize
|
||||||
42..55 '{ return 1; }': usize
|
42..55 '{ return 1; }': usize
|
||||||
|
@ -1871,7 +1871,7 @@ fn closure_return_unit() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
16..47 '{ ...; }; }': ()
|
16..47 '{ ...; }; }': u32
|
||||||
26..27 'x': || -> ()
|
26..27 'x': || -> ()
|
||||||
30..44 '|| { return; }': || -> ()
|
30..44 '|| { return; }': || -> ()
|
||||||
33..44 '{ return; }': ()
|
33..44 '{ return; }': ()
|
||||||
|
@ -1889,7 +1889,7 @@ fn closure_return_inferred() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
16..46 '{ ..." }; }': ()
|
16..46 '{ ..." }; }': u32
|
||||||
26..27 'x': || -> &str
|
26..27 'x': || -> &str
|
||||||
30..43 '|| { "test" }': || -> &str
|
30..43 '|| { "test" }': || -> &str
|
||||||
33..43 '{ "test" }': &str
|
33..43 '{ "test" }': &str
|
||||||
|
@ -2628,11 +2628,11 @@ fn main() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
104..108 'self': &Box<T>
|
104..108 'self': &Box<T>
|
||||||
188..192 'self': &Box<Foo<T>>
|
188..192 'self': &Box<Foo<T>>
|
||||||
218..220 '{}': ()
|
218..220 '{}': &T
|
||||||
242..246 'self': &Box<Foo<T>>
|
242..246 'self': &Box<Foo<T>>
|
||||||
275..277 '{}': ()
|
275..277 '{}': &Foo<T>
|
||||||
297..301 'self': Box<Foo<T>>
|
297..301 'self': Box<Foo<T>>
|
||||||
322..324 '{}': ()
|
322..324 '{}': Foo<T>
|
||||||
338..559 '{ ...r(); }': ()
|
338..559 '{ ...r(); }': ()
|
||||||
348..353 'boxed': Box<Foo<i32>>
|
348..353 'boxed': Box<Foo<i32>>
|
||||||
356..359 'Box': Box<Foo<i32>>(Foo<i32>) -> Box<Foo<i32>>
|
356..359 'Box': Box<Foo<i32>>(Foo<i32>) -> Box<Foo<i32>>
|
||||||
|
|
|
@ -1280,7 +1280,7 @@ fn test(x: dyn Trait<u64>, y: &dyn Trait<u64>) {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
29..33 'self': &Self
|
29..33 'self': &Self
|
||||||
54..58 'self': &Self
|
54..58 'self': &Self
|
||||||
97..99 '{}': ()
|
97..99 '{}': dyn Trait<u64>
|
||||||
109..110 'x': dyn Trait<u64>
|
109..110 'x': dyn Trait<u64>
|
||||||
128..129 'y': &dyn Trait<u64>
|
128..129 'y': &dyn Trait<u64>
|
||||||
148..265 '{ ...2(); }': ()
|
148..265 '{ ...2(); }': ()
|
||||||
|
@ -1361,10 +1361,10 @@ fn test(x: Trait, y: &Trait) -> u64 {
|
||||||
}"#,
|
}"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
26..30 'self': &Self
|
26..30 'self': &Self
|
||||||
60..62 '{}': ()
|
60..62 '{}': dyn Trait
|
||||||
72..73 'x': dyn Trait
|
72..73 'x': dyn Trait
|
||||||
82..83 'y': &dyn Trait
|
82..83 'y': &dyn Trait
|
||||||
100..175 '{ ...o(); }': ()
|
100..175 '{ ...o(); }': u64
|
||||||
106..107 'x': dyn Trait
|
106..107 'x': dyn Trait
|
||||||
113..114 'y': &dyn Trait
|
113..114 'y': &dyn Trait
|
||||||
124..125 'z': dyn Trait
|
124..125 'z': dyn Trait
|
||||||
|
@ -1449,9 +1449,9 @@ fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) {
|
||||||
}"#,
|
}"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
49..50 't': T
|
49..50 't': T
|
||||||
77..79 '{}': ()
|
77..79 '{}': Trait::Type<T>
|
||||||
111..112 't': T
|
111..112 't': T
|
||||||
122..124 '{}': ()
|
122..124 '{}': U
|
||||||
154..155 't': T
|
154..155 't': T
|
||||||
165..168 '{t}': T
|
165..168 '{t}': T
|
||||||
166..167 't': T
|
166..167 't': T
|
||||||
|
@ -1575,7 +1575,7 @@ fn test<T: Trait1, U: Trait2>(x: T, y: U) {
|
||||||
}"#,
|
}"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
49..53 'self': &Self
|
49..53 'self': &Self
|
||||||
62..64 '{}': ()
|
62..64 '{}': u32
|
||||||
181..182 'x': T
|
181..182 'x': T
|
||||||
187..188 'y': U
|
187..188 'y': U
|
||||||
193..222 '{ ...o(); }': ()
|
193..222 '{ ...o(); }': ()
|
||||||
|
@ -1604,7 +1604,7 @@ fn test(x: &impl Trait1) {
|
||||||
}"#,
|
}"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
49..53 'self': &Self
|
49..53 'self': &Self
|
||||||
62..64 '{}': ()
|
62..64 '{}': u32
|
||||||
115..116 'x': &impl Trait1
|
115..116 'x': &impl Trait1
|
||||||
132..148 '{ ...o(); }': ()
|
132..148 '{ ...o(); }': ()
|
||||||
138..139 'x': &impl Trait1
|
138..139 'x': &impl Trait1
|
||||||
|
@ -1653,7 +1653,7 @@ fn test() {
|
||||||
}"#,
|
}"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
102..103 't': T
|
102..103 't': T
|
||||||
113..115 '{}': ()
|
113..115 '{}': U
|
||||||
145..146 't': T
|
145..146 't': T
|
||||||
156..159 '{t}': T
|
156..159 '{t}': T
|
||||||
157..158 't': T
|
157..158 't': T
|
||||||
|
@ -1786,9 +1786,9 @@ fn test() {
|
||||||
}"#,
|
}"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
36..40 'self': &Foo
|
36..40 'self': &Foo
|
||||||
51..53 '{}': ()
|
51..53 '{}': usize
|
||||||
131..132 'f': F
|
131..132 'f': F
|
||||||
151..153 '{}': ()
|
151..153 '{}': Lazy<T, F>
|
||||||
251..497 '{ ...o(); }': ()
|
251..497 '{ ...o(); }': ()
|
||||||
261..266 'lazy1': Lazy<Foo, || -> Foo>
|
261..266 'lazy1': Lazy<Foo, || -> Foo>
|
||||||
283..292 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
|
283..292 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
|
||||||
|
@ -1807,7 +1807,7 @@ fn test() {
|
||||||
478..480 'r2': usize
|
478..480 'r2': usize
|
||||||
483..488 'lazy2': Lazy<Foo, fn() -> Foo>
|
483..488 'lazy2': Lazy<Foo, fn() -> Foo>
|
||||||
483..494 'lazy2.foo()': usize
|
483..494 'lazy2.foo()': usize
|
||||||
357..359 '{}': ()
|
357..359 '{}': Foo
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2738,7 +2738,7 @@ fn test() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
9..11 '{}': ()
|
9..11 '{}': ()
|
||||||
28..29 'T': {unknown}
|
28..29 'T': {unknown}
|
||||||
36..38 '{}': ()
|
36..38 '{}': T
|
||||||
36..38: expected T, got ()
|
36..38: expected T, got ()
|
||||||
113..117 'self': &Self
|
113..117 'self': &Self
|
||||||
169..249 '{ ...t(); }': ()
|
169..249 '{ ...t(); }': ()
|
||||||
|
@ -3167,7 +3167,7 @@ fn f() {
|
||||||
}"#,
|
}"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
17..73 '{ ... } }': ()
|
17..73 '{ ... } }': ()
|
||||||
39..71 '{ ... }': ()
|
39..71 '{ ... }': S
|
||||||
53..54 's': S
|
53..54 's': S
|
||||||
57..62 'inner': fn inner() -> S
|
57..62 'inner': fn inner() -> S
|
||||||
57..64 'inner()': S
|
57..64 'inner()': S
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use hir::HirDisplay;
|
use hir::HirDisplay;
|
||||||
use syntax::{ast, AstNode, SyntaxKind, SyntaxToken, TextRange, TextSize};
|
use syntax::{ast, match_ast, AstNode, SyntaxKind, SyntaxToken, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
|
||||||
pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?;
|
let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?;
|
||||||
let module = ctx.sema.scope(tail_expr.syntax()).module()?;
|
let module = ctx.sema.scope(tail_expr.syntax()).module()?;
|
||||||
let ty = ctx.sema.type_of_expr(&tail_expr)?.adjusted();
|
let ty = ctx.sema.type_of_expr(&peel_blocks(tail_expr.clone()))?.original();
|
||||||
if ty.is_unit() {
|
if ty.is_unit() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,45 @@ enum FnType {
|
||||||
Closure { wrap_expr: bool },
|
Closure { wrap_expr: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If we're looking at a block that is supposed to return `()`, type inference
|
||||||
|
/// will just tell us it has type `()`. We have to look at the tail expression
|
||||||
|
/// to see the mismatched actual type. This 'unpeels' the various blocks to
|
||||||
|
/// hopefully let us see the type the user intends. (This still doesn't handle
|
||||||
|
/// all situations fully correctly; the 'ideal' way to handle this would be to
|
||||||
|
/// run type inference on the function again, but with a variable as the return
|
||||||
|
/// type.)
|
||||||
|
fn peel_blocks(mut expr: ast::Expr) -> ast::Expr {
|
||||||
|
loop {
|
||||||
|
match_ast! {
|
||||||
|
match (expr.syntax()) {
|
||||||
|
ast::BlockExpr(it) => {
|
||||||
|
if let Some(tail) = it.tail_expr() {
|
||||||
|
expr = tail.clone();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ast::IfExpr(it) => {
|
||||||
|
if let Some(then_branch) = it.then_branch() {
|
||||||
|
expr = ast::Expr::BlockExpr(then_branch.clone());
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ast::MatchExpr(it) => {
|
||||||
|
if let Some(arm_expr) = it.match_arm_list().and_then(|l| l.arms().next()).and_then(|a| a.expr()) {
|
||||||
|
expr = arm_expr;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
|
||||||
fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrReplace)> {
|
fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrReplace)> {
|
||||||
let (fn_type, tail_expr, return_type_range, action) =
|
let (fn_type, tail_expr, return_type_range, action) =
|
||||||
if let Some(closure) = ctx.find_node_at_offset::<ast::ClosureExpr>() {
|
if let Some(closure) = ctx.find_node_at_offset::<ast::ClosureExpr>() {
|
||||||
|
@ -248,6 +287,25 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_return_type_nested_match() {
|
||||||
|
check_assist(
|
||||||
|
add_return_type,
|
||||||
|
r#"fn foo() {
|
||||||
|
match true {
|
||||||
|
true => { 3$0 },
|
||||||
|
false => { 5 },
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
r#"fn foo() -> i32 {
|
||||||
|
match true {
|
||||||
|
true => { 3 },
|
||||||
|
false => { 5 },
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn not_applicable_ret_type_specified() {
|
fn not_applicable_ret_type_specified() {
|
||||||
cov_mark::check!(existing_ret_type);
|
cov_mark::check!(existing_ret_type);
|
||||||
|
|
|
@ -4162,7 +4162,7 @@ fn main() {
|
||||||
match 6 {
|
match 6 {
|
||||||
100 => $0{ 100 }$0
|
100 => $0{ 100 }$0
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
|
@ -4170,7 +4170,7 @@ fn main() {
|
||||||
match 6 {
|
match 6 {
|
||||||
100 => fun_name(),
|
100 => fun_name(),
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn $0fun_name() -> i32 {
|
fn $0fun_name() -> i32 {
|
||||||
|
@ -4185,7 +4185,7 @@ fn main() {
|
||||||
match 6 {
|
match 6 {
|
||||||
100 => $0{ 100 }$0,
|
100 => $0{ 100 }$0,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
|
@ -4193,7 +4193,7 @@ fn main() {
|
||||||
match 6 {
|
match 6 {
|
||||||
100 => fun_name(),
|
100 => fun_name(),
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn $0fun_name() -> i32 {
|
fn $0fun_name() -> i32 {
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
use hir::{db::AstDatabase, HirDisplay, Type, TypeInfo};
|
use hir::{db::AstDatabase, HirDisplay, Type};
|
||||||
use ide_db::{
|
use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
|
||||||
famous_defs::FamousDefs, source_change::SourceChange,
|
|
||||||
syntax_helpers::node_ext::for_each_tail_expr,
|
|
||||||
};
|
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{BlockExpr, ExprStmt},
|
ast::{BlockExpr, ExprStmt},
|
||||||
AstNode,
|
AstNode,
|
||||||
|
@ -77,9 +74,9 @@ fn add_missing_ok_or_some(
|
||||||
acc: &mut Vec<Assist>,
|
acc: &mut Vec<Assist>,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
|
let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
|
||||||
let tail_expr = d.expr.value.to_node(&root);
|
let expr = d.expr.value.to_node(&root);
|
||||||
let tail_expr_range = tail_expr.syntax().text_range();
|
let expr_range = expr.syntax().text_range();
|
||||||
let scope = ctx.sema.scope(tail_expr.syntax());
|
let scope = ctx.sema.scope(expr.syntax());
|
||||||
|
|
||||||
let expected_adt = d.expected.as_adt()?;
|
let expected_adt = d.expected.as_adt()?;
|
||||||
let expected_enum = expected_adt.as_enum()?;
|
let expected_enum = expected_adt.as_enum()?;
|
||||||
|
@ -101,16 +98,12 @@ fn add_missing_ok_or_some(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut builder = TextEdit::builder();
|
let mut builder = TextEdit::builder();
|
||||||
for_each_tail_expr(&tail_expr, &mut |expr| {
|
builder.insert(expr.syntax().text_range().start(), format!("{}(", variant_name));
|
||||||
if ctx.sema.type_of_expr(expr).map(TypeInfo::adjusted).as_ref() != Some(&d.expected) {
|
builder.insert(expr.syntax().text_range().end(), ")".to_string());
|
||||||
builder.insert(expr.syntax().text_range().start(), format!("{}(", variant_name));
|
|
||||||
builder.insert(expr.syntax().text_range().end(), ")".to_string());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let source_change =
|
let source_change =
|
||||||
SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
|
SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
|
||||||
let name = format!("Wrap in {}", variant_name);
|
let name = format!("Wrap in {}", variant_name);
|
||||||
acc.push(fix("wrap_tail_expr", &name, source_change, tail_expr_range));
|
acc.push(fix("wrap_in_constructor", &name, source_change, expr_range));
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,12 +323,12 @@ fn div(x: i32, y: i32) -> Option<i32> {
|
||||||
//- minicore: option, result
|
//- minicore: option, result
|
||||||
fn div(x: i32, y: i32) -> Option<i32> {
|
fn div(x: i32, y: i32) -> Option<i32> {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
0
|
Some(0)
|
||||||
} else if true {
|
} else if true {
|
||||||
100
|
100$0
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}$0
|
}
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
|
|
|
@ -54,13 +54,13 @@ fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
|
||||||
actual
|
actual
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq_text!(&after, &actual);
|
|
||||||
assert!(
|
assert!(
|
||||||
fix.target.contains_inclusive(file_position.offset),
|
fix.target.contains_inclusive(file_position.offset),
|
||||||
"diagnostic fix range {:?} does not touch cursor position {:?}",
|
"diagnostic fix range {:?} does not touch cursor position {:?}",
|
||||||
fix.target,
|
fix.target,
|
||||||
file_position.offset
|
file_position.offset
|
||||||
);
|
);
|
||||||
|
assert_eq_text!(&after, &actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that there's a diagnostic *without* fix at `$0`.
|
/// Checks that there's a diagnostic *without* fix at `$0`.
|
||||||
|
|
Loading…
Reference in a new issue