Update the rest of the tests

This commit is contained in:
Aleksey Kladov 2020-06-29 17:22:47 +02:00
parent e805e8c1d5
commit bbc4dc9956
9 changed files with 311 additions and 446 deletions

View File

@ -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)
}

View File

@ -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#"

View File

@ -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));
}

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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() {

View File

@ -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: