mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 04:23:25 +00:00
Update the rest of the tests
This commit is contained in:
parent
e805e8c1d5
commit
bbc4dc9956
9 changed files with 311 additions and 446 deletions
|
@ -17,11 +17,11 @@ use hir_def::{
|
|||
item_scope::ItemScope,
|
||||
keys,
|
||||
nameres::CrateDefMap,
|
||||
AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, ModuleId,
|
||||
AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
|
||||
};
|
||||
use hir_expand::{db::AstDatabase, InFile};
|
||||
use insta::assert_snapshot;
|
||||
use ra_db::{fixture::WithFixture, salsa::Database, FilePosition, SourceDatabase};
|
||||
use ra_db::{fixture::WithFixture, salsa::Database, FileRange, SourceDatabase};
|
||||
use ra_syntax::{
|
||||
algo,
|
||||
ast::{self, AstNode},
|
||||
|
@ -39,13 +39,27 @@ use crate::{
|
|||
// update the snapshots.
|
||||
|
||||
fn check_types(ra_fixture: &str) {
|
||||
check_types_impl(ra_fixture, false)
|
||||
}
|
||||
|
||||
fn check_types_source_code(ra_fixture: &str) {
|
||||
check_types_impl(ra_fixture, true)
|
||||
}
|
||||
|
||||
fn check_types_impl(ra_fixture: &str, display_source: bool) {
|
||||
let db = TestDB::with_files(ra_fixture);
|
||||
let mut checked_one = false;
|
||||
for file_id in db.all_files() {
|
||||
let text = db.parse(file_id).syntax_node().to_string();
|
||||
let annotations = extract_annotations(&text);
|
||||
for (offset, expected) in annotations {
|
||||
let actual = type_at_pos(&db, FilePosition { file_id, offset });
|
||||
for (range, expected) in annotations {
|
||||
let ty = type_at_range(&db, FileRange { file_id, range });
|
||||
let actual = if display_source {
|
||||
let module = db.module_for_file(file_id);
|
||||
ty.display_source_code(&db, module).unwrap()
|
||||
} else {
|
||||
ty.display(&db).to_string()
|
||||
};
|
||||
assert_eq!(expected, actual);
|
||||
checked_one = true;
|
||||
}
|
||||
|
@ -53,21 +67,9 @@ fn check_types(ra_fixture: &str) {
|
|||
assert!(checked_one, "no `//^` annotations found");
|
||||
}
|
||||
|
||||
fn type_at_pos(db: &TestDB, pos: FilePosition) -> String {
|
||||
type_at_pos_displayed(db, pos, |ty, _| ty.display(db).to_string())
|
||||
}
|
||||
|
||||
fn displayed_source_at_pos(db: &TestDB, pos: FilePosition) -> String {
|
||||
type_at_pos_displayed(db, pos, |ty, module_id| ty.display_source_code(db, module_id).unwrap())
|
||||
}
|
||||
|
||||
fn type_at_pos_displayed(
|
||||
db: &TestDB,
|
||||
pos: FilePosition,
|
||||
display_fn: impl FnOnce(&Ty, ModuleId) -> String,
|
||||
) -> String {
|
||||
fn type_at_range(db: &TestDB, pos: FileRange) -> Ty {
|
||||
let file = db.parse(pos.file_id).ok().unwrap();
|
||||
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
|
||||
let expr = algo::find_node_at_range::<ast::Expr>(file.syntax(), pos.range).unwrap();
|
||||
let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
|
||||
let module = db.module_for_file(pos.file_id);
|
||||
let func = *module.child_by_source(db)[keys::FUNCTION]
|
||||
|
@ -77,17 +79,11 @@ fn type_at_pos_displayed(
|
|||
let (_body, source_map) = db.body_with_source_map(func.into());
|
||||
if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) {
|
||||
let infer = db.infer(func.into());
|
||||
let ty = &infer[expr_id];
|
||||
return display_fn(ty, module);
|
||||
return infer[expr_id].clone();
|
||||
}
|
||||
panic!("Can't find expression")
|
||||
}
|
||||
|
||||
fn type_at(ra_fixture: &str) -> String {
|
||||
let (db, file_pos) = TestDB::with_position(ra_fixture);
|
||||
type_at_pos(&db, file_pos)
|
||||
}
|
||||
|
||||
fn infer(ra_fixture: &str) -> String {
|
||||
infer_with_mismatches(ra_fixture, false)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use super::infer_with_mismatches;
|
||||
use insta::assert_snapshot;
|
||||
use test_utils::mark;
|
||||
|
||||
use super::infer_with_mismatches;
|
||||
|
||||
// Infer with some common definitions and impls.
|
||||
fn infer(source: &str) -> String {
|
||||
let defs = r#"
|
||||
|
|
|
@ -1,50 +1,41 @@
|
|||
use super::displayed_source_at_pos;
|
||||
use crate::test_db::TestDB;
|
||||
use ra_db::fixture::WithFixture;
|
||||
use super::check_types_source_code;
|
||||
|
||||
#[test]
|
||||
fn qualify_path_to_submodule() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types_source_code(
|
||||
r#"
|
||||
//- /main.rs
|
||||
|
||||
mod foo {
|
||||
pub struct Foo;
|
||||
}
|
||||
|
||||
fn bar() {
|
||||
let foo: foo::Foo = foo::Foo;
|
||||
foo<|>
|
||||
}
|
||||
foo
|
||||
} //^ foo::Foo
|
||||
|
||||
"#,
|
||||
);
|
||||
assert_eq!("foo::Foo", displayed_source_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn omit_default_type_parameters() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
r"
|
||||
//- /main.rs
|
||||
struct Foo<T = u8> { t: T }
|
||||
fn main() {
|
||||
let foo = Foo { t: 5u8 };
|
||||
foo<|>;
|
||||
}
|
||||
",
|
||||
check_types_source_code(
|
||||
r#"
|
||||
struct Foo<T = u8> { t: T }
|
||||
fn main() {
|
||||
let foo = Foo { t: 5u8 };
|
||||
foo;
|
||||
} //^ Foo
|
||||
"#,
|
||||
);
|
||||
assert_eq!("Foo", displayed_source_at_pos(&db, pos));
|
||||
|
||||
let (db, pos) = TestDB::with_position(
|
||||
r"
|
||||
//- /main.rs
|
||||
struct Foo<K, T = u8> { k: K, t: T }
|
||||
fn main() {
|
||||
let foo = Foo { k: 400, t: 5u8 };
|
||||
foo<|>;
|
||||
}
|
||||
",
|
||||
check_types_source_code(
|
||||
r#"
|
||||
struct Foo<K, T = u8> { k: K, t: T }
|
||||
fn main() {
|
||||
let foo = Foo { k: 400, t: 5u8 };
|
||||
foo;
|
||||
} //^ Foo<i32>
|
||||
"#,
|
||||
);
|
||||
assert_eq!("Foo<i32>", displayed_source_at_pos(&db, pos));
|
||||
}
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
use std::fs;
|
||||
|
||||
use insta::assert_snapshot;
|
||||
use ra_db::fixture::WithFixture;
|
||||
use test_utils::project_dir;
|
||||
|
||||
use crate::test_db::TestDB;
|
||||
|
||||
use super::{infer, type_at, type_at_pos};
|
||||
use super::{check_types, infer};
|
||||
|
||||
#[test]
|
||||
fn cfg_impl_def() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:foo cfg:test
|
||||
use foo::S as T;
|
||||
|
@ -28,8 +25,8 @@ impl S {
|
|||
|
||||
fn test() {
|
||||
let t = (S.foo1(), S.foo2(), T.foo3(), T.foo4());
|
||||
t<|>;
|
||||
}
|
||||
t;
|
||||
} //^ (i32, {unknown}, i32, {unknown})
|
||||
|
||||
//- /foo.rs crate:foo
|
||||
struct S;
|
||||
|
@ -45,7 +42,6 @@ impl S {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("(i32, {unknown}, i32, {unknown})", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -253,26 +249,24 @@ fn foo() {
|
|||
|
||||
#[test]
|
||||
fn processes_impls_generated_by_macros() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
macro_rules! m {
|
||||
($ident:ident) => (impl Trait for $ident {})
|
||||
}
|
||||
trait Trait { fn foo(self) -> u128 {} }
|
||||
struct S;
|
||||
m!(S);
|
||||
fn test() { S.foo()<|>; }
|
||||
fn test() { S.foo(); }
|
||||
//^ u128
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_assoc_items_generated_by_macros() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
macro_rules! m {
|
||||
() => (fn foo(&self) -> u128 {0})
|
||||
}
|
||||
|
@ -281,17 +275,16 @@ impl S {
|
|||
m!();
|
||||
}
|
||||
|
||||
fn test() { S.foo()<|>; }
|
||||
fn test() { S.foo(); }
|
||||
//^ u128
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_assoc_items_generated_by_macros_chain() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
macro_rules! m_inner {
|
||||
() => {fn foo(&self) -> u128 {0}}
|
||||
}
|
||||
|
@ -304,21 +297,21 @@ impl S {
|
|||
m!();
|
||||
}
|
||||
|
||||
fn test() { S.foo()<|>; }
|
||||
fn test() { S.foo(); }
|
||||
//^ u128
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_macro_with_dollar_crate_is_correct_in_expr() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:foo
|
||||
fn test() {
|
||||
let x = (foo::foo!(1), foo::foo!(2));
|
||||
x<|>;
|
||||
}
|
||||
x;
|
||||
} //^ (i32, usize)
|
||||
|
||||
//- /lib.rs crate:foo
|
||||
#[macro_export]
|
||||
|
@ -335,12 +328,11 @@ macro_rules! bar {
|
|||
pub fn baz() -> usize { 31usize }
|
||||
"#,
|
||||
);
|
||||
assert_eq!("(i32, usize)", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_macro_with_dollar_crate_is_correct_in_trait_associate_type() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:foo
|
||||
use foo::Trait;
|
||||
|
@ -348,7 +340,8 @@ use foo::Trait;
|
|||
fn test() {
|
||||
let msg = foo::Message(foo::MessageRef);
|
||||
let r = msg.deref();
|
||||
r<|>;
|
||||
r;
|
||||
//^ &MessageRef
|
||||
}
|
||||
|
||||
//- /lib.rs crate:foo
|
||||
|
@ -375,7 +368,6 @@ macro_rules! expand {
|
|||
expand!();
|
||||
"#,
|
||||
);
|
||||
assert_eq!("&MessageRef", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -429,13 +421,13 @@ fn main() {
|
|||
|
||||
#[test]
|
||||
fn infer_local_inner_macros() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:foo
|
||||
fn test() {
|
||||
let x = foo::foo!(1);
|
||||
x<|>;
|
||||
}
|
||||
x;
|
||||
} //^ i32
|
||||
|
||||
//- /lib.rs crate:foo
|
||||
#[macro_export(local_inner_macros)]
|
||||
|
@ -450,7 +442,6 @@ macro_rules! bar {
|
|||
|
||||
"#,
|
||||
);
|
||||
assert_eq!("i32", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -531,7 +522,7 @@ fn main() {
|
|||
|
||||
#[test]
|
||||
fn infer_builtin_macros_include() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
#[rustc_builtin_macro]
|
||||
|
@ -540,14 +531,13 @@ macro_rules! include {() => {}}
|
|||
include!("foo.rs");
|
||||
|
||||
fn main() {
|
||||
bar()<|>;
|
||||
}
|
||||
bar();
|
||||
} //^ u32
|
||||
|
||||
//- /foo.rs
|
||||
fn bar() -> u32 {0}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("u32", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -565,18 +555,17 @@ macro_rules! include {() => {}}
|
|||
include!("foo.rs");
|
||||
|
||||
fn main() {
|
||||
RegisterBlock { }<|>;
|
||||
RegisterBlock { };
|
||||
//^ RegisterBlock
|
||||
}
|
||||
"#;
|
||||
let fixture = format!("{}\n//- /foo.rs\n{}", fixture, big_file);
|
||||
|
||||
let (db, pos) = TestDB::with_position(&fixture);
|
||||
assert_eq!("RegisterBlock", type_at_pos(&db, pos));
|
||||
check_types(&fixture);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_builtin_macros_include_concat() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
#[rustc_builtin_macro]
|
||||
|
@ -588,19 +577,18 @@ macro_rules! concat {() => {}}
|
|||
include!(concat!("f", "oo.rs"));
|
||||
|
||||
fn main() {
|
||||
bar()<|>;
|
||||
}
|
||||
bar();
|
||||
} //^ u32
|
||||
|
||||
//- /foo.rs
|
||||
fn bar() -> u32 {0}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("u32", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_builtin_macros_include_concat_with_bad_env_should_failed() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
#[rustc_builtin_macro]
|
||||
|
@ -615,32 +603,29 @@ macro_rules! env {() => {}}
|
|||
include!(concat!(env!("OUT_DIR"), "/foo.rs"));
|
||||
|
||||
fn main() {
|
||||
bar()<|>;
|
||||
}
|
||||
bar();
|
||||
} //^ {unknown}
|
||||
|
||||
//- /foo.rs
|
||||
fn bar() -> u32 {0}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("{unknown}", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_builtin_macros_include_itself_should_failed() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! include {() => {}}
|
||||
|
||||
include!("main.rs");
|
||||
|
||||
fn main() {
|
||||
0<|>
|
||||
}
|
||||
0
|
||||
} //^ i32
|
||||
"#,
|
||||
);
|
||||
assert_eq!("i32", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -686,14 +671,14 @@ fn main() {
|
|||
|
||||
#[test]
|
||||
fn infer_derive_clone_simple() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
#[derive(Clone)]
|
||||
struct S;
|
||||
fn test() {
|
||||
S.clone()<|>;
|
||||
}
|
||||
S.clone();
|
||||
} //^ S
|
||||
|
||||
//- /lib.rs crate:core
|
||||
#[prelude_import]
|
||||
|
@ -705,12 +690,11 @@ mod clone {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("S", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_derive_clone_in_core() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /lib.rs crate:core
|
||||
#[prelude_import]
|
||||
|
@ -726,16 +710,15 @@ pub struct S;
|
|||
//- /main.rs crate:main deps:core
|
||||
use core::S;
|
||||
fn test() {
|
||||
S.clone()<|>;
|
||||
}
|
||||
S.clone();
|
||||
} //^ S
|
||||
"#,
|
||||
);
|
||||
assert_eq!("S", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_derive_clone_with_params() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
#[derive(Clone)]
|
||||
|
@ -744,7 +727,8 @@ struct S;
|
|||
struct Wrapper<T>(T);
|
||||
struct NonClone;
|
||||
fn test() {
|
||||
(Wrapper(S).clone(), Wrapper(NonClone).clone())<|>;
|
||||
(Wrapper(S).clone(), Wrapper(NonClone).clone());
|
||||
//^ (Wrapper<S>, {unknown})
|
||||
}
|
||||
|
||||
//- /lib.rs crate:core
|
||||
|
@ -757,13 +741,12 @@ mod clone {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("(Wrapper<S>, {unknown})", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_custom_derive_simple() {
|
||||
// FIXME: this test current now do nothing
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main
|
||||
use foo::Foo;
|
||||
|
@ -772,11 +755,10 @@ use foo::Foo;
|
|||
struct S{}
|
||||
|
||||
fn test() {
|
||||
S{}<|>;
|
||||
}
|
||||
S{};
|
||||
} //^ S
|
||||
"#,
|
||||
);
|
||||
assert_eq!("S", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use super::{infer, type_at, type_at_pos};
|
||||
use crate::test_db::TestDB;
|
||||
use insta::assert_snapshot;
|
||||
use ra_db::fixture::WithFixture;
|
||||
|
||||
use super::{check_types, infer};
|
||||
|
||||
#[test]
|
||||
fn infer_slice_method() {
|
||||
|
@ -246,13 +245,13 @@ fn test() {
|
|||
|
||||
#[test]
|
||||
fn cross_crate_associated_method_call() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:other_crate
|
||||
fn test() {
|
||||
let x = other_crate::foo::S::thing();
|
||||
x<|>;
|
||||
}
|
||||
x;
|
||||
} //^ i128
|
||||
|
||||
//- /lib.rs crate:other_crate
|
||||
mod foo {
|
||||
|
@ -263,7 +262,6 @@ mod foo {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("i128", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -684,135 +682,127 @@ fn test() {
|
|||
|
||||
#[test]
|
||||
fn method_resolution_unify_impl_self_type() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
struct S<T>;
|
||||
impl S<u32> { fn foo(&self) -> u8 {} }
|
||||
impl S<i32> { fn foo(&self) -> i8 {} }
|
||||
fn test() { (S::<u32>.foo(), S::<i32>.foo())<|>; }
|
||||
fn test() { (S::<u32>.foo(), S::<i32>.foo()); }
|
||||
//^ (u8, i8)
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "(u8, i8)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_trait_before_autoref() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait { fn foo(self) -> u128; }
|
||||
struct S;
|
||||
impl S { fn foo(&self) -> i8 { 0 } }
|
||||
impl Trait for S { fn foo(self) -> u128 { 0 } }
|
||||
fn test() { S.foo()<|>; }
|
||||
fn test() { S.foo(); }
|
||||
//^ u128
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_by_value_before_autoref() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Clone { fn clone(&self) -> Self; }
|
||||
struct S;
|
||||
impl Clone for S {}
|
||||
impl Clone for &S {}
|
||||
fn test() { (S.clone(), (&S).clone(), (&&S).clone())<|>; }
|
||||
fn test() { (S.clone(), (&S).clone(), (&&S).clone()); }
|
||||
//^ (S, S, &S)
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "(S, S, &S)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_trait_before_autoderef() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait { fn foo(self) -> u128; }
|
||||
struct S;
|
||||
impl S { fn foo(self) -> i8 { 0 } }
|
||||
impl Trait for &S { fn foo(self) -> u128 { 0 } }
|
||||
fn test() { (&S).foo()<|>; }
|
||||
fn test() { (&S).foo(); }
|
||||
//^ u128
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_impl_before_trait() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait { fn foo(self) -> u128; }
|
||||
struct S;
|
||||
impl S { fn foo(self) -> i8 { 0 } }
|
||||
impl Trait for S { fn foo(self) -> u128 { 0 } }
|
||||
fn test() { S.foo()<|>; }
|
||||
fn test() { S.foo(); }
|
||||
//^ i8
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "i8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_impl_ref_before_trait() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait { fn foo(self) -> u128; }
|
||||
struct S;
|
||||
impl S { fn foo(&self) -> i8 { 0 } }
|
||||
impl Trait for &S { fn foo(self) -> u128 { 0 } }
|
||||
fn test() { S.foo()<|>; }
|
||||
fn test() { S.foo(); }
|
||||
//^ i8
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "i8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_trait_autoderef() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait { fn foo(self) -> u128; }
|
||||
struct S;
|
||||
impl Trait for S { fn foo(self) -> u128 { 0 } }
|
||||
fn test() { (&S).foo()<|>; }
|
||||
fn test() { (&S).foo(); }
|
||||
//^ u128
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_unsize_array() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
#[lang = "slice"]
|
||||
impl<T> [T] {
|
||||
fn len(&self) -> usize { loop {} }
|
||||
}
|
||||
fn test() {
|
||||
let a = [1, 2, 3];
|
||||
a.len()<|>;
|
||||
}
|
||||
a.len();
|
||||
} //^ usize
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "usize");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_trait_from_prelude() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:other_crate
|
||||
struct S;
|
||||
impl Clone for S {}
|
||||
|
||||
fn test() {
|
||||
S.clone()<|>;
|
||||
S.clone();
|
||||
//^ S
|
||||
}
|
||||
|
||||
//- /lib.rs crate:other_crate
|
||||
|
@ -825,115 +815,107 @@ mod foo {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("S", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_where_clause_for_unknown_trait() {
|
||||
// The blanket impl currently applies because we ignore the unresolved where clause
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait { fn foo(self) -> u128; }
|
||||
struct S;
|
||||
impl<T> Trait for T where T: UnknownTrait {}
|
||||
fn test() { (&S).foo()<|>; }
|
||||
fn test() { (&S).foo(); }
|
||||
//^ u128
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_where_clause_not_met() {
|
||||
// The blanket impl shouldn't apply because we can't prove S: Clone
|
||||
let t = type_at(
|
||||
// This is also to make sure that we don't resolve to the foo method just
|
||||
// because that's the only method named foo we can find, which would make
|
||||
// the below tests not work
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Clone {}
|
||||
trait Trait { fn foo(self) -> u128; }
|
||||
struct S;
|
||||
impl<T> Trait for T where T: Clone {}
|
||||
fn test() { (&S).foo()<|>; }
|
||||
fn test() { (&S).foo(); }
|
||||
//^ {unknown}
|
||||
"#,
|
||||
);
|
||||
// This is also to make sure that we don't resolve to the foo method just
|
||||
// because that's the only method named foo we can find, which would make
|
||||
// the below tests not work
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_where_clause_inline_not_met() {
|
||||
// The blanket impl shouldn't apply because we can't prove S: Clone
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Clone {}
|
||||
trait Trait { fn foo(self) -> u128; }
|
||||
struct S;
|
||||
impl<T: Clone> Trait for T {}
|
||||
fn test() { (&S).foo()<|>; }
|
||||
fn test() { (&S).foo(); }
|
||||
//^ {unknown}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_where_clause_1() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Clone {}
|
||||
trait Trait { fn foo(self) -> u128; }
|
||||
struct S;
|
||||
impl Clone for S {}
|
||||
impl<T> Trait for T where T: Clone {}
|
||||
fn test() { S.foo()<|>; }
|
||||
fn test() { S.foo(); }
|
||||
//^ u128
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_where_clause_2() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Into<T> { fn into(self) -> T; }
|
||||
trait From<T> { fn from(other: T) -> Self; }
|
||||
struct S1;
|
||||
struct S2;
|
||||
impl From<S2> for S1 {}
|
||||
impl<T, U> Into<U> for T where U: From<T> {}
|
||||
fn test() { S2.into()<|>; }
|
||||
fn test() { S2.into(); }
|
||||
//^ {unknown}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_where_clause_inline() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Into<T> { fn into(self) -> T; }
|
||||
trait From<T> { fn from(other: T) -> Self; }
|
||||
struct S1;
|
||||
struct S2;
|
||||
impl From<S2> for S1 {}
|
||||
impl<T, U: From<T>> Into<U> for T {}
|
||||
fn test() { S2.into()<|>; }
|
||||
fn test() { S2.into(); }
|
||||
//^ {unknown}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_overloaded_method() {
|
||||
test_utils::mark::check!(impl_self_type_match_without_receiver);
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
struct Wrapper<T>(T);
|
||||
struct Foo<T>(T);
|
||||
struct Bar<T>(T);
|
||||
|
@ -953,30 +935,30 @@ impl<T> Wrapper<Bar<T>> {
|
|||
fn main() {
|
||||
let a = Wrapper::<Foo<f32>>::new(1.0);
|
||||
let b = Wrapper::<Bar<f32>>::new(1.0);
|
||||
(a, b)<|>;
|
||||
(a, b);
|
||||
//^ (Wrapper<Foo<f32>>, Wrapper<Bar<f32>>)
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "(Wrapper<Foo<f32>>, Wrapper<Bar<f32>>)")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_encountering_fn_type() {
|
||||
type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
fn foo() {}
|
||||
trait FnOnce { fn call(self); }
|
||||
fn test() { foo.call()<|>; }
|
||||
fn test() { foo.call(); }
|
||||
//^ {unknown}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_non_parameter_type() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
mod a {
|
||||
pub trait Foo {
|
||||
fn foo(&self);
|
||||
|
@ -988,18 +970,16 @@ fn foo<T>(t: Wrapper<T>)
|
|||
where
|
||||
Wrapper<T>: a::Foo,
|
||||
{
|
||||
t.foo()<|>;
|
||||
}
|
||||
t.foo();
|
||||
} //^ {unknown}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_3373() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
struct A<T>(T);
|
||||
|
||||
impl A<i32> {
|
||||
|
@ -1007,19 +987,17 @@ impl A<i32> {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
A::from(3)<|>;
|
||||
}
|
||||
A::from(3);
|
||||
} //^ A<i32>
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "A<i32>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_resolution_slow() {
|
||||
// this can get quite slow if we set the solver size limit too high
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait SendX {}
|
||||
|
||||
struct S1; impl SendX for S1 {}
|
||||
|
@ -1037,10 +1015,10 @@ trait FnX {}
|
|||
|
||||
impl<B, C> Trait for S<B, C> where C: FnX, B: SendX {}
|
||||
|
||||
fn test() { (S {}).method()<|>; }
|
||||
fn test() { (S {}).method(); }
|
||||
//^ ()
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
use insta::assert_snapshot;
|
||||
use ra_db::fixture::WithFixture;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::test_db::TestDB;
|
||||
|
||||
use super::infer;
|
||||
use super::{check_types, infer};
|
||||
|
||||
#[test]
|
||||
fn bug_484() {
|
||||
|
@ -404,13 +401,13 @@ fn test() {
|
|||
|
||||
#[test]
|
||||
fn issue_2683_chars_impl() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
fn test() {
|
||||
let chars: std::str::Chars<'_>;
|
||||
(chars.next(), chars.nth(1))<|>;
|
||||
}
|
||||
(chars.next(), chars.nth(1));
|
||||
} //^ (Option<char>, Option<char>)
|
||||
|
||||
//- /std.rs crate:std
|
||||
#[prelude_import]
|
||||
|
@ -449,15 +446,12 @@ pub mod str {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_eq!("(Option<char>, Option<char>)", super::type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_3642_bad_macro_stackover() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
#[macro_export]
|
||||
macro_rules! match_ast {
|
||||
(match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
|
||||
|
@ -472,7 +466,8 @@ macro_rules! match_ast {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let anchor<|> = match_ast! {
|
||||
let anchor = match_ast! {
|
||||
//^ ()
|
||||
match parent {
|
||||
as => {},
|
||||
_ => return None
|
||||
|
@ -480,8 +475,6 @@ fn main() {
|
|||
};
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_eq!("()", super::type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
use insta::assert_snapshot;
|
||||
use ra_db::fixture::WithFixture;
|
||||
use test_utils::mark;
|
||||
|
||||
use crate::test_db::TestDB;
|
||||
|
||||
use super::{infer, infer_with_mismatches, type_at, type_at_pos};
|
||||
use super::{check_types, infer, infer_with_mismatches};
|
||||
|
||||
#[test]
|
||||
fn infer_await() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
|
||||
struct IntFuture;
|
||||
|
||||
impl Future for IntFuture {
|
||||
|
@ -21,8 +17,8 @@ impl Future for IntFuture {
|
|||
fn test() {
|
||||
let r = IntFuture;
|
||||
let v = r.await;
|
||||
v<|>;
|
||||
}
|
||||
v;
|
||||
} //^ u64
|
||||
|
||||
//- /core.rs crate:core
|
||||
#[prelude_import] use future::*;
|
||||
|
@ -32,18 +28,15 @@ mod future {
|
|||
type Output;
|
||||
}
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
assert_eq!("u64", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_async() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
|
||||
async fn foo() -> u64 {
|
||||
128
|
||||
}
|
||||
|
@ -51,8 +44,8 @@ async fn foo() -> u64 {
|
|||
fn test() {
|
||||
let r = foo();
|
||||
let v = r.await;
|
||||
v<|>;
|
||||
}
|
||||
v;
|
||||
} //^ u64
|
||||
|
||||
//- /core.rs crate:core
|
||||
#[prelude_import] use future::*;
|
||||
|
@ -62,26 +55,23 @@ mod future {
|
|||
type Output;
|
||||
}
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
assert_eq!("u64", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_desugar_async() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
|
||||
async fn foo() -> u64 {
|
||||
128
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let r = foo();
|
||||
r<|>;
|
||||
}
|
||||
r;
|
||||
} //^ impl Future<Output = u64>
|
||||
|
||||
//- /core.rs crate:core
|
||||
#[prelude_import] use future::*;
|
||||
|
@ -93,23 +83,20 @@ mod future {
|
|||
|
||||
"#,
|
||||
);
|
||||
assert_eq!("impl Future<Output = u64>", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_try() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
|
||||
fn test() {
|
||||
let r: Result<i32, u64> = Result::Ok(1);
|
||||
let v = r?;
|
||||
v<|>;
|
||||
}
|
||||
v;
|
||||
} //^ i32
|
||||
|
||||
//- /core.rs crate:core
|
||||
|
||||
#[prelude_import] use ops::*;
|
||||
mod ops {
|
||||
trait Try {
|
||||
|
@ -130,30 +117,26 @@ mod result {
|
|||
type Error = E;
|
||||
}
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
assert_eq!("i32", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_for_loop() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core,alloc
|
||||
|
||||
use alloc::collections::Vec;
|
||||
|
||||
fn test() {
|
||||
let v = Vec::new();
|
||||
v.push("foo");
|
||||
for x in v {
|
||||
x<|>;
|
||||
}
|
||||
x;
|
||||
} //^ &str
|
||||
}
|
||||
|
||||
//- /core.rs crate:core
|
||||
|
||||
#[prelude_import] use iter::*;
|
||||
mod iter {
|
||||
trait IntoIterator {
|
||||
|
@ -162,7 +145,6 @@ mod iter {
|
|||
}
|
||||
|
||||
//- /alloc.rs crate:alloc deps:core
|
||||
|
||||
mod collections {
|
||||
struct Vec<T> {}
|
||||
impl<T> Vec<T> {
|
||||
|
@ -176,15 +158,13 @@ mod collections {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("&str", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_ops_neg() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
|
||||
struct Bar;
|
||||
struct Foo;
|
||||
|
||||
|
@ -195,11 +175,10 @@ impl std::ops::Neg for Bar {
|
|||
fn test() {
|
||||
let a = Bar;
|
||||
let b = -a;
|
||||
b<|>;
|
||||
}
|
||||
b;
|
||||
} //^ Foo
|
||||
|
||||
//- /std.rs crate:std
|
||||
|
||||
#[prelude_import] use ops::*;
|
||||
mod ops {
|
||||
#[lang = "neg"]
|
||||
|
@ -209,15 +188,13 @@ mod ops {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("Foo", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_ops_not() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
|
||||
struct Bar;
|
||||
struct Foo;
|
||||
|
||||
|
@ -228,11 +205,10 @@ impl std::ops::Not for Bar {
|
|||
fn test() {
|
||||
let a = Bar;
|
||||
let b = !a;
|
||||
b<|>;
|
||||
}
|
||||
b;
|
||||
} //^ Foo
|
||||
|
||||
//- /std.rs crate:std
|
||||
|
||||
#[prelude_import] use ops::*;
|
||||
mod ops {
|
||||
#[lang = "not"]
|
||||
|
@ -242,7 +218,6 @@ mod ops {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("Foo", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -537,10 +512,9 @@ fn indexing_arrays() {
|
|||
|
||||
#[test]
|
||||
fn infer_ops_index() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
|
||||
struct Bar;
|
||||
struct Foo;
|
||||
|
||||
|
@ -551,11 +525,10 @@ impl std::ops::Index<u32> for Bar {
|
|||
fn test() {
|
||||
let a = Bar;
|
||||
let b = a[1u32];
|
||||
b<|>;
|
||||
}
|
||||
b;
|
||||
} //^ Foo
|
||||
|
||||
//- /std.rs crate:std
|
||||
|
||||
#[prelude_import] use ops::*;
|
||||
mod ops {
|
||||
#[lang = "index"]
|
||||
|
@ -565,19 +538,18 @@ mod ops {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("Foo", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_ops_index_autoderef() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
fn test() {
|
||||
let a = &[1u32, 2, 3];
|
||||
let b = a[1u32];
|
||||
b<|>;
|
||||
}
|
||||
b;
|
||||
} //^ u32
|
||||
|
||||
//- /std.rs crate:std
|
||||
impl<T> ops::Index<u32> for [T] {
|
||||
|
@ -593,14 +565,12 @@ mod ops {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("u32", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deref_trait() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
#[lang = "deref"]
|
||||
trait Deref {
|
||||
type Target;
|
||||
|
@ -618,16 +588,15 @@ impl S {
|
|||
}
|
||||
|
||||
fn test(s: Arc<S>) {
|
||||
(*s, s.foo())<|>;
|
||||
}
|
||||
(*s, s.foo());
|
||||
} //^ (S, u128)
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "(S, u128)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deref_trait_with_inference_var() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
#[lang = "deref"]
|
||||
|
@ -647,19 +616,18 @@ fn foo(a: Arc<S>) {}
|
|||
|
||||
fn test() {
|
||||
let a = new_arc();
|
||||
let b = (*a)<|>;
|
||||
let b = (*a);
|
||||
//^ S
|
||||
foo(a);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "S");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deref_trait_infinite_recursion() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
#[lang = "deref"]
|
||||
trait Deref {
|
||||
type Target;
|
||||
|
@ -673,18 +641,16 @@ impl Deref for S {
|
|||
}
|
||||
|
||||
fn test(s: S) {
|
||||
s.foo()<|>;
|
||||
}
|
||||
s.foo();
|
||||
} //^ {unknown}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deref_trait_with_question_mark_size() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
#[lang = "deref"]
|
||||
trait Deref {
|
||||
type Target;
|
||||
|
@ -702,18 +668,16 @@ impl S {
|
|||
}
|
||||
|
||||
fn test(s: Arc<S>) {
|
||||
(*s, s.foo())<|>;
|
||||
}
|
||||
(*s, s.foo());
|
||||
} //^ (S, u128)
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "(S, u128)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn obligation_from_function_clause() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
struct S;
|
||||
|
||||
trait Trait<T> {}
|
||||
|
@ -722,16 +686,15 @@ impl Trait<u32> for S {}
|
|||
fn foo<T: Trait<U>, U>(t: T) -> U {}
|
||||
|
||||
fn test(s: S) {
|
||||
foo(s)<|>;
|
||||
}
|
||||
(foo(s));
|
||||
} //^ u32
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn obligation_from_method_clause() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
struct S;
|
||||
|
@ -745,18 +708,16 @@ impl O {
|
|||
}
|
||||
|
||||
fn test() {
|
||||
O.foo(S)<|>;
|
||||
}
|
||||
O.foo(S);
|
||||
} //^ isize
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "isize");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn obligation_from_self_method_clause() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
struct S;
|
||||
|
||||
trait Trait<T> {}
|
||||
|
@ -767,18 +728,16 @@ impl S {
|
|||
}
|
||||
|
||||
fn test() {
|
||||
S.foo()<|>;
|
||||
}
|
||||
S.foo();
|
||||
} //^ i64
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "i64");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn obligation_from_impl_clause() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
struct S;
|
||||
|
||||
trait Trait<T> {}
|
||||
|
@ -790,32 +749,30 @@ impl<U, T: Trait<U>> O<T> {
|
|||
}
|
||||
|
||||
fn test(o: O<S>) {
|
||||
o.foo()<|>;
|
||||
}
|
||||
o.foo();
|
||||
} //^ &str
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "&str");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_param_env_1() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Clone {}
|
||||
trait Trait { fn foo(self) -> u128; }
|
||||
struct S;
|
||||
impl Clone for S {}
|
||||
impl<T> Trait for T where T: Clone {}
|
||||
fn test<T: Clone>(t: T) { t.foo()<|>; }
|
||||
fn test<T: Clone>(t: T) { t.foo(); }
|
||||
//^ u128
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_param_env_1_not_met() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Clone {}
|
||||
|
@ -823,45 +780,42 @@ trait Trait { fn foo(self) -> u128; }
|
|||
struct S;
|
||||
impl Clone for S {}
|
||||
impl<T> Trait for T where T: Clone {}
|
||||
fn test<T>(t: T) { t.foo()<|>; }
|
||||
fn test<T>(t: T) { t.foo(); }
|
||||
//^ {unknown}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_param_env_2() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait { fn foo(self) -> u128; }
|
||||
struct S;
|
||||
impl Trait for S {}
|
||||
fn test<T: Trait>(t: T) { t.foo()<|>; }
|
||||
fn test<T: Trait>(t: T) { t.foo(); }
|
||||
//^ u128
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_param_env_2_not_met() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait { fn foo(self) -> u128; }
|
||||
struct S;
|
||||
impl Trait for S {}
|
||||
fn test<T>(t: T) { t.foo()<|>; }
|
||||
fn test<T>(t: T) { t.foo(); }
|
||||
//^ {unknown}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_param_env_deref() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
#[lang = "deref"]
|
||||
trait Deref {
|
||||
type Target;
|
||||
|
@ -870,17 +824,17 @@ trait Trait {}
|
|||
impl<T> Deref for T where T: Trait {
|
||||
type Target = i128;
|
||||
}
|
||||
fn test<T: Trait>(t: T) { (*t)<|>; }
|
||||
fn test<T: Trait>(t: T) { (*t); }
|
||||
//^ i128
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "i128");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn associated_type_placeholder() {
|
||||
let t = type_at(
|
||||
// inside the generic function, the associated type gets normalized to a placeholder `ApplL::Out<T>` [https://rust-lang.github.io/rustc-guide/traits/associated-types.html#placeholder-associated-types].
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
pub trait ApplyL {
|
||||
type Out;
|
||||
}
|
||||
|
@ -893,19 +847,16 @@ impl<T> ApplyL for RefMutL<T> {
|
|||
|
||||
fn test<T: ApplyL>() {
|
||||
let y: <RefMutL<T> as ApplyL>::Out = no_matter;
|
||||
y<|>;
|
||||
}
|
||||
y;
|
||||
} //^ ApplyL::Out<T>
|
||||
"#,
|
||||
);
|
||||
// inside the generic function, the associated type gets normalized to a placeholder `ApplL::Out<T>` [https://rust-lang.github.io/rustc-guide/traits/associated-types.html#placeholder-associated-types].
|
||||
assert_eq!(t, "ApplyL::Out<T>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn associated_type_placeholder_2() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
pub trait ApplyL {
|
||||
type Out;
|
||||
}
|
||||
|
@ -913,11 +864,10 @@ fn foo<T: ApplyL>(t: T) -> <T as ApplyL>::Out;
|
|||
|
||||
fn test<T: ApplyL>(t: T) {
|
||||
let y = foo(t);
|
||||
y<|>;
|
||||
}
|
||||
y;
|
||||
} //^ ApplyL::Out<T>
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "ApplyL::Out<T>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1398,19 +1348,17 @@ fn test(a: impl Trait + 'lifetime, b: impl 'lifetime, c: impl (Trait), d: impl (
|
|||
#[test]
|
||||
#[ignore]
|
||||
fn error_bound_chalk() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait {
|
||||
fn foo(&self) -> u32 {}
|
||||
}
|
||||
|
||||
fn test(x: (impl Trait + UnknownTrait)) {
|
||||
x.foo()<|>;
|
||||
}
|
||||
x.foo();
|
||||
} //^ u32
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1480,7 +1428,7 @@ fn test<T: Trait<Type = u32>>(x: T, y: impl Trait<Type = i64>) {
|
|||
|
||||
#[test]
|
||||
fn impl_trait_assoc_binding_projection_bug() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std
|
||||
pub trait Language {
|
||||
|
@ -1499,8 +1447,8 @@ trait Clone {
|
|||
|
||||
fn api_walkthrough() {
|
||||
for node in foo() {
|
||||
node.clone()<|>;
|
||||
}
|
||||
node.clone();
|
||||
} //^ {unknown}
|
||||
}
|
||||
|
||||
//- /std.rs crate:std
|
||||
|
@ -1518,7 +1466,6 @@ mod iter {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!("{unknown}", type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1549,9 +1496,8 @@ fn test<T: Trait1<Type = u32>>(x: T) {
|
|||
|
||||
#[test]
|
||||
fn where_clause_trait_in_scope_for_method_resolution() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
mod foo {
|
||||
trait Trait {
|
||||
fn foo(&self) -> u32 {}
|
||||
|
@ -1559,11 +1505,10 @@ mod foo {
|
|||
}
|
||||
|
||||
fn test<T: foo::Trait>(x: T) {
|
||||
x.foo()<|>;
|
||||
}
|
||||
x.foo();
|
||||
} //^ u32
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2012,7 +1957,7 @@ fn test() {
|
|||
|
||||
#[test]
|
||||
fn unselected_projection_in_trait_env_1() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait {
|
||||
|
@ -2025,18 +1970,16 @@ trait Trait2 {
|
|||
|
||||
fn test<T: Trait>() where T::Item: Trait2 {
|
||||
let x: T::Item = no_matter;
|
||||
x.foo()<|>;
|
||||
}
|
||||
x.foo();
|
||||
} //^ u32
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unselected_projection_in_trait_env_2() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait<T> {
|
||||
type Item;
|
||||
}
|
||||
|
@ -2047,11 +1990,10 @@ trait Trait2 {
|
|||
|
||||
fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> {
|
||||
let x: T::Item = no_matter;
|
||||
x.foo()<|>;
|
||||
}
|
||||
x.foo();
|
||||
} //^ u32
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2097,9 +2039,8 @@ impl Trait for S2 {
|
|||
|
||||
#[test]
|
||||
fn unselected_projection_on_trait_self() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait {
|
||||
type Item;
|
||||
|
||||
|
@ -2112,18 +2053,16 @@ impl Trait for S {
|
|||
}
|
||||
|
||||
fn test() {
|
||||
S.f()<|>;
|
||||
}
|
||||
S.f();
|
||||
} //^ u32
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unselected_projection_chalk_fold() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Interner {}
|
||||
trait Fold<I: Interner, TI = I> {
|
||||
type Result;
|
||||
|
@ -2142,18 +2081,16 @@ where
|
|||
}
|
||||
|
||||
fn foo<I: Interner>(interner: &I, t: Ty<I>) {
|
||||
fold(interner, t)<|>;
|
||||
}
|
||||
fold(interner, t);
|
||||
} //^ Ty<I>
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "Ty<I>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trait_impl_self_ty() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait<T> {
|
||||
fn foo(&self);
|
||||
}
|
||||
|
@ -2163,18 +2100,16 @@ struct S;
|
|||
impl Trait<Self> for S {}
|
||||
|
||||
fn test() {
|
||||
S.foo()<|>;
|
||||
}
|
||||
S.foo();
|
||||
} //^ ()
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trait_impl_self_ty_cycle() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait {
|
||||
fn foo(&self);
|
||||
}
|
||||
|
@ -2184,18 +2119,17 @@ struct S<T>;
|
|||
impl Trait for S<Self> {}
|
||||
|
||||
fn test() {
|
||||
S.foo()<|>;
|
||||
}
|
||||
S.foo();
|
||||
} //^ {unknown}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unselected_projection_in_trait_env_cycle_1() {
|
||||
let t = type_at(
|
||||
// this is a legitimate cycle
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait {
|
||||
type Item;
|
||||
}
|
||||
|
@ -2203,17 +2137,16 @@ trait Trait {
|
|||
trait Trait2<T> {}
|
||||
|
||||
fn test<T: Trait>() where T: Trait2<T::Item> {
|
||||
let x: T::Item = no_matter<|>;
|
||||
}
|
||||
let x: T::Item = no_matter;
|
||||
} //^ {unknown}
|
||||
"#,
|
||||
);
|
||||
// this is a legitimate cycle
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unselected_projection_in_trait_env_cycle_2() {
|
||||
let t = type_at(
|
||||
// this is a legitimate cycle
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Trait<T> {
|
||||
|
@ -2221,19 +2154,16 @@ trait Trait<T> {
|
|||
}
|
||||
|
||||
fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> {
|
||||
let x: T::Item = no_matter<|>;
|
||||
}
|
||||
let x: T::Item = no_matter;
|
||||
} //^ {unknown}
|
||||
"#,
|
||||
);
|
||||
// this is a legitimate cycle
|
||||
assert_eq!(t, "{unknown}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline_assoc_type_bounds_1() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Iterator {
|
||||
type Item;
|
||||
}
|
||||
|
@ -2249,29 +2179,26 @@ impl<T: Iterator> Iterator for S<T> {
|
|||
|
||||
fn test<I: Iterator<Item: OtherTrait<u32>>>() {
|
||||
let x: <S<I> as Iterator>::Item;
|
||||
x.foo()<|>;
|
||||
}
|
||||
x.foo();
|
||||
} //^ u32
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline_assoc_type_bounds_2() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
trait Iterator {
|
||||
type Item;
|
||||
}
|
||||
|
||||
fn test<I: Iterator<Item: Iterator<Item = u32>>>() {
|
||||
let x: <<I as Iterator>::Item as Iterator>::Item;
|
||||
x<|>;
|
||||
}
|
||||
x;
|
||||
} //^ u32
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2445,9 +2372,8 @@ fn main() {
|
|||
|
||||
#[test]
|
||||
fn associated_type_bound() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
pub trait Trait {
|
||||
type Item: OtherTrait<u32>;
|
||||
}
|
||||
|
@ -2463,18 +2389,16 @@ impl<T: Trait> Trait for S<T> {
|
|||
|
||||
fn test<T: Trait>() {
|
||||
let y: <S<T> as Trait>::Item = no_matter;
|
||||
y.foo()<|>;
|
||||
}
|
||||
y.foo();
|
||||
} //^ u32
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "u32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dyn_trait_through_chalk() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
struct Box<T> {}
|
||||
#[lang = "deref"]
|
||||
trait Deref {
|
||||
|
@ -2488,18 +2412,16 @@ trait Trait {
|
|||
}
|
||||
|
||||
fn test(x: Box<dyn Trait>) {
|
||||
x.foo()<|>;
|
||||
}
|
||||
x.foo();
|
||||
} //^ ()
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_to_owned() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
struct String {}
|
||||
pub trait ToOwned {
|
||||
type Owned;
|
||||
|
@ -2509,11 +2431,10 @@ impl ToOwned for str {
|
|||
type Owned = String;
|
||||
}
|
||||
fn test() {
|
||||
"foo".to_owned()<|>;
|
||||
}
|
||||
"foo".to_owned();
|
||||
} //^ String
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "String");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2637,9 +2558,8 @@ fn main() {
|
|||
|
||||
#[test]
|
||||
fn nested_assoc() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs
|
||||
struct Bar;
|
||||
struct Foo;
|
||||
|
||||
|
@ -2662,11 +2582,10 @@ impl<T:A> B for T {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
Bar::foo()<|>;
|
||||
}
|
||||
Bar::foo();
|
||||
} //^ Foo
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "Foo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2846,12 +2765,12 @@ fn test() {
|
|||
|
||||
#[test]
|
||||
fn integer_range_iterate() {
|
||||
let t = type_at(
|
||||
check_types(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:core
|
||||
fn test() {
|
||||
for x in 0..100 { x<|>; }
|
||||
}
|
||||
for x in 0..100 { x; }
|
||||
} //^ i32
|
||||
|
||||
//- /core.rs crate:core
|
||||
pub mod ops {
|
||||
|
@ -2886,7 +2805,6 @@ impl<A: Step> iter::Iterator for ops::Range<A> {
|
|||
}
|
||||
"#,
|
||||
);
|
||||
assert_eq!(t, "i32");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -41,6 +41,10 @@ pub fn find_node_at_offset<N: AstNode>(syntax: &SyntaxNode, offset: TextSize) ->
|
|||
ancestors_at_offset(syntax, offset).find_map(N::cast)
|
||||
}
|
||||
|
||||
pub fn find_node_at_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
|
||||
find_covering_element(syntax, range).ancestors().find_map(N::cast)
|
||||
}
|
||||
|
||||
/// Skip to next non `trivia` token
|
||||
pub fn skip_trivia_token(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> {
|
||||
while token.kind().is_trivia() {
|
||||
|
|
|
@ -161,7 +161,7 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String {
|
|||
}
|
||||
|
||||
/// Extracts `//^ some text` annotations
|
||||
pub fn extract_annotations(text: &str) -> Vec<(TextSize, String)> {
|
||||
pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
|
||||
let mut res = Vec::new();
|
||||
let mut prev_line_start: Option<TextSize> = None;
|
||||
let mut line_start: TextSize = 0.into();
|
||||
|
@ -169,7 +169,7 @@ pub fn extract_annotations(text: &str) -> Vec<(TextSize, String)> {
|
|||
if let Some(idx) = line.find("//^") {
|
||||
let offset = prev_line_start.unwrap() + TextSize::of(&line[..idx + "//".len()]);
|
||||
let data = line[idx + "//^".len()..].trim().to_string();
|
||||
res.push((offset, data))
|
||||
res.push((TextRange::at(offset, 1.into()), data))
|
||||
}
|
||||
prev_line_start = Some(line_start);
|
||||
line_start += TextSize::of(line);
|
||||
|
@ -179,18 +179,20 @@ pub fn extract_annotations(text: &str) -> Vec<(TextSize, String)> {
|
|||
|
||||
#[test]
|
||||
fn test_extract_annotations() {
|
||||
let res = extract_annotations(&trim_indent(
|
||||
let text = stdx::trim_indent(
|
||||
r#"
|
||||
fn main() {
|
||||
let x = 92;
|
||||
//^ def
|
||||
|
||||
x + 1
|
||||
z + 1
|
||||
} //^ i32
|
||||
"#,
|
||||
));
|
||||
|
||||
assert_eq!(res, vec![(20.into(), "def".into()), (47.into(), "i32".into())]);
|
||||
);
|
||||
let res = extract_annotations(&text)
|
||||
.into_iter()
|
||||
.map(|(range, ann)| (&text[range], ann))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(res, vec![("x", "def".into()), ("z", "i32".into()),]);
|
||||
}
|
||||
|
||||
// Comparison functionality borrowed from cargo:
|
||||
|
|
Loading…
Reference in a new issue