mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 15:15:24 +00:00
944 lines
17 KiB
Rust
944 lines
17 KiB
Rust
use super::{check, check_no_mismatches, check_types};
|
|
|
|
#[test]
|
|
fn block_expr_type_mismatch() {
|
|
check(
|
|
r"
|
|
fn test() {
|
|
let a: i32 = { 1i64 };
|
|
// ^^^^ expected i32, got i64
|
|
}
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_places() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
struct S<T> { a: T }
|
|
|
|
fn f<T>(_: &[T]) -> T { loop {} }
|
|
fn g<T>(_: S<&[T]>) -> T { loop {} }
|
|
|
|
fn gen<T>() -> *mut [T; 2] { loop {} }
|
|
fn test1<U>() -> *mut [U] {
|
|
gen()
|
|
}
|
|
|
|
fn test2() {
|
|
let arr: &[u8; 1] = &[1];
|
|
|
|
let a: &[_] = arr;
|
|
let b = f(arr);
|
|
let c: &[_] = { arr };
|
|
let d = g(S { a: arr });
|
|
let e: [&[_]; 1] = [arr];
|
|
let f: [&[_]; 2] = [arr; 2];
|
|
let g: (&[_], &[_]) = (arr, arr);
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn let_stmt_coerce() {
|
|
check(
|
|
r"
|
|
//- minicore: coerce_unsized
|
|
fn test() {
|
|
let x: &[isize] = &[1];
|
|
// ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
|
|
let x: *const [isize] = &[1];
|
|
// ^^^^ adjustments: Deref(None), Borrow(RawPtr(Not)), Pointer(Unsize)
|
|
}
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn custom_coerce_unsized() {
|
|
check(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
use core::{marker::Unsize, ops::CoerceUnsized};
|
|
|
|
struct A<T: ?Sized>(*const T);
|
|
struct B<T: ?Sized>(*const T);
|
|
struct C<T: ?Sized> { inner: *const T }
|
|
|
|
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {}
|
|
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<C<U>> for C<T> {}
|
|
|
|
fn foo1<T>(x: A<[T]>) -> A<[T]> { x }
|
|
fn foo2<T>(x: B<[T]>) -> B<[T]> { x }
|
|
fn foo3<T>(x: C<[T]>) -> C<[T]> { x }
|
|
|
|
fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) {
|
|
let d = foo1(a);
|
|
// ^ expected A<[{unknown}]>, got A<[u8; 2]>
|
|
let e = foo2(b);
|
|
// ^ type: B<[u8]>
|
|
let f = foo3(c);
|
|
// ^ type: C<[u8]>
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn if_coerce() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
fn foo<T>(x: &[T]) -> &[T] { x }
|
|
fn test() {
|
|
let x = if true {
|
|
foo(&[1])
|
|
// ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
|
|
} else {
|
|
&[1]
|
|
};
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn if_else_coerce() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
fn foo<T>(x: &[T]) -> &[T] { x }
|
|
fn test() {
|
|
let x = if true {
|
|
&[1]
|
|
} else {
|
|
foo(&[1])
|
|
};
|
|
}
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn if_else_adjust_for_branches_discard_type_var() {
|
|
check_no_mismatches(
|
|
r#"
|
|
fn test() {
|
|
let f = || {
|
|
if true {
|
|
&""
|
|
} else {
|
|
""
|
|
}
|
|
};
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn match_first_coerce() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
fn foo<T>(x: &[T]) -> &[T] { x }
|
|
fn test(i: i32) {
|
|
let x = match i {
|
|
2 => foo(&[2]),
|
|
// ^^^^ adjustments: Deref(None), Borrow(Ref(Not)), Pointer(Unsize)
|
|
1 => &[1],
|
|
_ => &[3],
|
|
};
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn match_second_coerce() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
fn foo<T>(x: &[T]) -> &[T] { loop {} }
|
|
// ^^^^^^^ adjustments: NeverToAny
|
|
fn test(i: i32) {
|
|
let x = match i {
|
|
1 => &[1],
|
|
2 => foo(&[2]),
|
|
_ => &[3],
|
|
};
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_merge_one_by_one1() {
|
|
cov_mark::check!(coerce_merge_fail_fallback);
|
|
|
|
check(
|
|
r"
|
|
fn test() {
|
|
let t = &mut 1;
|
|
let x = match 1 {
|
|
1 => t as *mut i32,
|
|
2 => t as &i32,
|
|
//^^^^^^^^^ expected *mut i32, got &'? i32
|
|
_ => t as *const i32,
|
|
// ^^^^^^^^^^^^^^^ adjustments: Pointer(MutToConstPointer)
|
|
|
|
};
|
|
x;
|
|
//^ type: *const i32
|
|
|
|
}
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn match_adjust_for_branches_discard_type_var() {
|
|
check_no_mismatches(
|
|
r#"
|
|
fn test() {
|
|
let f = || {
|
|
match 0i32 {
|
|
0i32 => &"",
|
|
_ => "",
|
|
}
|
|
};
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn return_coerce_unknown() {
|
|
check_types(
|
|
r"
|
|
fn foo() -> u32 {
|
|
return unknown;
|
|
//^^^^^^^ u32
|
|
}
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_autoderef() {
|
|
check_no_mismatches(
|
|
r"
|
|
struct Foo;
|
|
fn takes_ref_foo(x: &Foo) {}
|
|
fn test() {
|
|
takes_ref_foo(&Foo);
|
|
takes_ref_foo(&&Foo);
|
|
takes_ref_foo(&&&Foo);
|
|
}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_autoderef_generic() {
|
|
check_no_mismatches(
|
|
r#"
|
|
struct Foo;
|
|
fn takes_ref<T>(x: &T) -> T { *x }
|
|
fn test() {
|
|
takes_ref(&Foo);
|
|
takes_ref(&&Foo);
|
|
takes_ref(&&&Foo);
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_autoderef_block() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: deref
|
|
struct String {}
|
|
impl core::ops::Deref for String { type Target = str; }
|
|
fn takes_ref_str(x: &str) {}
|
|
fn returns_string() -> String { loop {} }
|
|
fn test() {
|
|
takes_ref_str(&{ returns_string() });
|
|
// ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_autoderef_implication_1() {
|
|
check_no_mismatches(
|
|
r"
|
|
//- minicore: deref
|
|
struct Foo<T>;
|
|
impl core::ops::Deref for Foo<u32> { type Target = (); }
|
|
|
|
fn takes_ref_foo<T>(x: &Foo<T>) {}
|
|
fn test() {
|
|
let foo = Foo;
|
|
//^^^ type: Foo<{unknown}>
|
|
takes_ref_foo(&foo);
|
|
|
|
let foo = Foo;
|
|
//^^^ type: Foo<u32>
|
|
let _: &() = &foo;
|
|
}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_autoderef_implication_2() {
|
|
check(
|
|
r"
|
|
//- minicore: deref
|
|
struct Foo<T>;
|
|
impl core::ops::Deref for Foo<u32> { type Target = (); }
|
|
|
|
fn takes_ref_foo<T>(x: &Foo<T>) {}
|
|
fn test() {
|
|
let foo = Foo;
|
|
//^^^ type: Foo<{unknown}>
|
|
let _: &u32 = &Foo;
|
|
//^^^^ expected &'? u32, got &'? Foo<{unknown}>
|
|
}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn closure_return_coerce() {
|
|
check_no_mismatches(
|
|
r"
|
|
fn foo() {
|
|
let x = || {
|
|
if true {
|
|
return &1u32;
|
|
}
|
|
&&1u32
|
|
};
|
|
}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coroutine_yield_return_coerce() {
|
|
check_no_mismatches(
|
|
r#"
|
|
fn test() {
|
|
let g = || {
|
|
yield &1u32;
|
|
yield &&1u32;
|
|
if true {
|
|
return &1u32;
|
|
}
|
|
&&1u32
|
|
};
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn assign_coerce() {
|
|
check_no_mismatches(
|
|
r"
|
|
//- minicore: deref
|
|
struct String;
|
|
impl core::ops::Deref for String { type Target = str; }
|
|
fn g(_text: &str) {}
|
|
fn f(text: &str) {
|
|
let mut text = text;
|
|
let tmp = String;
|
|
text = &tmp;
|
|
g(text);
|
|
}
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn destructuring_assign_coerce() {
|
|
check_no_mismatches(
|
|
r"
|
|
//- minicore: deref
|
|
struct String;
|
|
impl core::ops::Deref for String { type Target = str; }
|
|
fn g(_text: &str) {}
|
|
fn f(text: &str) {
|
|
let mut text = text;
|
|
let tmp = String;
|
|
[text, _] = [&tmp, &tmp];
|
|
g(text);
|
|
}
|
|
",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_fn_item_to_fn_ptr() {
|
|
check_no_mismatches(
|
|
r"
|
|
fn foo(x: u32) -> isize { 1 }
|
|
fn test() {
|
|
let f: fn(u32) -> isize = foo;
|
|
// ^^^ adjustments: Pointer(ReifyFnPointer)
|
|
let f: unsafe fn(u32) -> isize = foo;
|
|
// ^^^ adjustments: Pointer(ReifyFnPointer), Pointer(UnsafeFnPointer)
|
|
}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_fn_item_to_fn_ptr_in_array() {
|
|
check_no_mismatches(
|
|
r"
|
|
fn foo(x: u32) -> isize { 1 }
|
|
fn bar(x: u32) -> isize { 1 }
|
|
fn test() {
|
|
let f = [foo, bar];
|
|
// ^^^ adjustments: Pointer(ReifyFnPointer)
|
|
}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_fn_items_in_match_arms() {
|
|
cov_mark::check!(coerce_fn_reification);
|
|
|
|
check_no_mismatches(
|
|
r"
|
|
fn foo1(x: u32) -> isize { 1 }
|
|
fn foo2(x: u32) -> isize { 2 }
|
|
fn foo3(x: u32) -> isize { 3 }
|
|
fn test() {
|
|
let x = match 1 {
|
|
1 => foo1,
|
|
// ^^^^ adjustments: Pointer(ReifyFnPointer)
|
|
2 => foo2,
|
|
// ^^^^ adjustments: Pointer(ReifyFnPointer)
|
|
_ => foo3,
|
|
// ^^^^ adjustments: Pointer(ReifyFnPointer)
|
|
};
|
|
x;
|
|
}",
|
|
);
|
|
check_types(
|
|
r"
|
|
fn foo1(x: u32) -> isize { 1 }
|
|
fn foo2(x: u32) -> isize { 2 }
|
|
fn foo3(x: u32) -> isize { 3 }
|
|
fn test() {
|
|
let x = match 1 {
|
|
1 => foo1,
|
|
2 => foo2,
|
|
_ => foo3,
|
|
};
|
|
x;
|
|
//^ fn(u32) -> isize
|
|
}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_closure_to_fn_ptr() {
|
|
check_no_mismatches(
|
|
r"
|
|
fn test() {
|
|
let f: fn(u32) -> u32 = |x| x;
|
|
// ^^^^^ adjustments: Pointer(ClosureFnPointer(Safe))
|
|
let f: unsafe fn(u32) -> u32 = |x| x;
|
|
// ^^^^^ adjustments: Pointer(ClosureFnPointer(Unsafe))
|
|
}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_placeholder_ref() {
|
|
// placeholders should unify, even behind references
|
|
check_no_mismatches(
|
|
r"
|
|
struct S<T> { t: T }
|
|
impl<TT> S<TT> {
|
|
fn get(&self) -> &TT {
|
|
&self.t
|
|
}
|
|
}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_unsize_array() {
|
|
check_types(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
fn test() {
|
|
let f: &[usize] = &[1, 2, 3];
|
|
//^ usize
|
|
}"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_unsize_trait_object_simple() {
|
|
check_types(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
trait Foo<T, U> {}
|
|
trait Bar<U, T, X>: Foo<T, U> {}
|
|
trait Baz<T, X>: Bar<usize, T, X> {}
|
|
|
|
struct S<T, X>;
|
|
impl<T, X> Foo<T, usize> for S<T, X> {}
|
|
impl<T, X> Bar<usize, T, X> for S<T, X> {}
|
|
impl<T, X> Baz<T, X> for S<T, X> {}
|
|
|
|
fn test() {
|
|
let obj: &dyn Baz<i8, i16> = &S;
|
|
//^ S<i8, i16>
|
|
let obj: &dyn Bar<_, i8, i16> = &S;
|
|
//^ S<i8, i16>
|
|
let obj: &dyn Foo<i8, _> = &S;
|
|
//^ S<i8, {unknown}>
|
|
}"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_unsize_super_trait_cycle() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
trait A {}
|
|
trait B: C + A {}
|
|
trait C: B {}
|
|
trait D: C
|
|
|
|
struct S;
|
|
impl A for S {}
|
|
impl B for S {}
|
|
impl C for S {}
|
|
impl D for S {}
|
|
|
|
fn test() {
|
|
let obj: &dyn D = &S;
|
|
let obj: &dyn A = &S;
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_unsize_generic() {
|
|
check(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
struct Foo<T> { t: T };
|
|
struct Bar<T>(Foo<T>);
|
|
|
|
fn test() {
|
|
let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] };
|
|
//^^^^^^^^^^^^^^^^^^^^^ expected &'? Foo<[usize]>, got &'? Foo<[i32; 3]>
|
|
let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] });
|
|
//^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &'? Bar<[usize]>, got &'? Bar<[i32; 3]>
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_unsize_apit() {
|
|
check(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
trait Foo {}
|
|
|
|
fn test(f: impl Foo, g: &(impl Foo + ?Sized)) {
|
|
let _: &dyn Foo = &f;
|
|
let _: &dyn Foo = g;
|
|
//^ expected &'? dyn Foo, got &'? impl Foo + ?Sized
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn two_closures_lub() {
|
|
check_types(
|
|
r#"
|
|
fn foo(c: i32) {
|
|
let add = |a: i32, b: i32| a + b;
|
|
//^^^^^^^^^^^^^^^^^^^^^^ impl Fn(i32, i32) -> i32
|
|
let sub = |a, b| a - b;
|
|
//^^^^^^^^^^^^ impl Fn(i32, i32) -> i32
|
|
if c > 42 { add } else { sub };
|
|
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fn(i32, i32) -> i32
|
|
}
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn match_diverging_branch_1() {
|
|
check_types(
|
|
r#"
|
|
enum Result<T> { Ok(T), Err }
|
|
fn parse<T>() -> T { loop {} }
|
|
|
|
fn test() -> i32 {
|
|
let a = match parse() {
|
|
Ok(val) => val,
|
|
Err => return 0,
|
|
};
|
|
a
|
|
//^ i32
|
|
}
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn match_diverging_branch_2() {
|
|
// same as 1 except for order of branches
|
|
check_types(
|
|
r#"
|
|
enum Result<T> { Ok(T), Err }
|
|
fn parse<T>() -> T { loop {} }
|
|
|
|
fn test() -> i32 {
|
|
let a = match parse() {
|
|
Err => return 0,
|
|
Ok(val) => val,
|
|
};
|
|
a
|
|
//^ i32
|
|
}
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn panic_macro() {
|
|
check_no_mismatches(
|
|
r#"
|
|
mod panic {
|
|
#[macro_export]
|
|
pub macro panic_2015 {
|
|
() => (
|
|
$crate::panicking::panic()
|
|
),
|
|
}
|
|
}
|
|
|
|
mod panicking {
|
|
pub fn panic() -> ! { loop {} }
|
|
}
|
|
|
|
#[rustc_builtin_macro = "core_panic"]
|
|
macro_rules! panic {
|
|
// Expands to either `$crate::panic::panic_2015` or `$crate::panic::panic_2021`
|
|
// depending on the edition of the caller.
|
|
($($arg:tt)*) => {
|
|
/* compiler built-in */
|
|
};
|
|
}
|
|
|
|
fn main() {
|
|
panic!()
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_unsize_expected_type_1() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
fn main() {
|
|
let foo: &[u32] = &[1, 2];
|
|
let foo: &[u32] = match true {
|
|
true => &[1, 2],
|
|
false => &[1, 2, 3],
|
|
};
|
|
let foo: &[u32] = if true {
|
|
&[1, 2]
|
|
} else {
|
|
&[1, 2, 3]
|
|
};
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_unsize_expected_type_2() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
struct InFile<T>;
|
|
impl<T> InFile<T> {
|
|
fn with_value<U>(self, value: U) -> InFile<U> { InFile }
|
|
}
|
|
struct RecordField;
|
|
trait AstNode {}
|
|
impl AstNode for RecordField {}
|
|
|
|
fn takes_dyn(it: InFile<&dyn AstNode>) {}
|
|
|
|
fn test() {
|
|
let x: InFile<()> = InFile;
|
|
let n = &RecordField;
|
|
takes_dyn(x.with_value(n));
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_unsize_expected_type_3() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
enum Option<T> { Some(T), None }
|
|
struct RecordField;
|
|
trait AstNode {}
|
|
impl AstNode for RecordField {}
|
|
|
|
fn takes_dyn(it: Option<&dyn AstNode>) {}
|
|
|
|
fn test() {
|
|
let x: InFile<()> = InFile;
|
|
let n = &RecordField;
|
|
takes_dyn(Option::Some(n));
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_unsize_expected_type_4() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
use core::{marker::Unsize, ops::CoerceUnsized};
|
|
|
|
struct B<T: ?Sized>(*const T);
|
|
impl<T: ?Sized> B<T> {
|
|
fn new(t: T) -> Self { B(&t) }
|
|
}
|
|
|
|
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {}
|
|
|
|
fn test() {
|
|
let _: B<[isize]> = B::new({ [1, 2, 3] });
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_array_elems_lub() {
|
|
check_no_mismatches(
|
|
r#"
|
|
fn f() {}
|
|
fn g() {}
|
|
|
|
fn test() {
|
|
[f, g];
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_type_var() {
|
|
check_types(
|
|
r#"
|
|
//- minicore: from, coerce_unsized
|
|
fn test() {
|
|
let x = ();
|
|
let _: &() = &x.into();
|
|
} //^^^^^^^^ ()
|
|
"#,
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn coerce_overloaded_binary_op_rhs() {
|
|
check_types(
|
|
r#"
|
|
//- minicore: deref, add
|
|
|
|
struct String {}
|
|
impl core::ops::Deref for String { type Target = str; }
|
|
|
|
impl core::ops::Add<&str> for String {
|
|
type Output = String;
|
|
}
|
|
|
|
fn test() {
|
|
let s1 = String {};
|
|
let s2 = String {};
|
|
s1 + &s2;
|
|
//^^^^^^^^ String
|
|
}
|
|
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn assign_coerce_struct_fields() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
struct S;
|
|
trait Tr {}
|
|
impl Tr for S {}
|
|
struct V<T> { t: T }
|
|
|
|
fn main() {
|
|
let a: V<&dyn Tr>;
|
|
a = V { t: &S };
|
|
|
|
let mut a: V<&dyn Tr> = V { t: &S };
|
|
a = V { t: &S };
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn destructuring_assign_coerce_struct_fields() {
|
|
check(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
struct S;
|
|
trait Tr {}
|
|
impl Tr for S {}
|
|
struct V<T> { t: T }
|
|
|
|
fn main() {
|
|
let a: V<&dyn Tr>;
|
|
(a,) = V { t: &S };
|
|
//^^^^expected V<&'? S>, got (V<&'? dyn Tr>,)
|
|
|
|
let mut a: V<&dyn Tr> = V { t: &S };
|
|
(a,) = V { t: &S };
|
|
//^^^^expected V<&'? S>, got (V<&'? dyn Tr>,)
|
|
}
|
|
"#,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn adjust_comparison_arguments() {
|
|
check_no_mismatches(
|
|
r"
|
|
//- minicore: eq
|
|
struct Struct;
|
|
impl core::cmp::PartialEq for Struct {
|
|
fn eq(&self, other: &Self) -> bool { true }
|
|
}
|
|
fn test() {
|
|
Struct == Struct;
|
|
// ^^^^^^ adjustments: Borrow(Ref(Not))
|
|
// ^^^^^^ adjustments: Borrow(Ref(Not))
|
|
}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn adjust_assign_lhs() {
|
|
check_no_mismatches(
|
|
r"
|
|
//- minicore: add
|
|
struct Struct;
|
|
impl core::ops::AddAssign for Struct {
|
|
fn add_assign(&mut self, other: Self) {}
|
|
}
|
|
fn test() {
|
|
Struct += Struct;
|
|
// ^^^^^^ adjustments: Borrow(Ref(Mut))
|
|
// ^^^^^^ adjustments:
|
|
}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn adjust_index() {
|
|
check_no_mismatches(
|
|
r"
|
|
//- minicore: index, slice, coerce_unsized
|
|
fn test() {
|
|
let x = [1, 2, 3];
|
|
x[2] = 6;
|
|
// ^ adjustments: Borrow(Ref(Mut))
|
|
}
|
|
",
|
|
);
|
|
check_no_mismatches(
|
|
r"
|
|
//- minicore: index
|
|
struct Struct;
|
|
impl core::ops::Index<usize> for Struct {
|
|
type Output = ();
|
|
|
|
fn index(&self, index: usize) -> &Self::Output { &() }
|
|
}
|
|
struct StructMut;
|
|
|
|
impl core::ops::Index<usize> for StructMut {
|
|
type Output = ();
|
|
|
|
fn index(&self, index: usize) -> &Self::Output { &() }
|
|
}
|
|
impl core::ops::IndexMut for StructMut {
|
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut () }
|
|
}
|
|
fn test() {
|
|
Struct[0];
|
|
// ^^^^^^ adjustments: Borrow(Ref(Not))
|
|
StructMut[0];
|
|
// ^^^^^^^^^ adjustments: Borrow(Ref(Not))
|
|
&mut StructMut[0];
|
|
// ^^^^^^^^^ adjustments: Borrow(Ref(Mut))
|
|
}",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn regression_14443_dyn_coercion_block_impls() {
|
|
check_no_mismatches(
|
|
r#"
|
|
//- minicore: coerce_unsized
|
|
trait T {}
|
|
|
|
fn dyn_t(d: &dyn T) {}
|
|
|
|
fn main() {
|
|
struct A;
|
|
impl T for A {}
|
|
|
|
let a = A;
|
|
|
|
let b = {
|
|
struct B;
|
|
impl T for B {}
|
|
|
|
B
|
|
};
|
|
|
|
dyn_t(&a);
|
|
dyn_t(&b);
|
|
}
|
|
"#,
|
|
)
|
|
}
|