});
(buf, count)
}
+
+ pub fn all_files(&self) -> Vec<FileId> {
+ let mut res = Vec::new();
+ let crate_graph = self.crate_graph();
+ for krate in crate_graph.iter() {
+ let crate_def_map = self.crate_def_map(krate);
+ for (module_id, _) in crate_def_map.modules.iter() {
+ let file_id = crate_def_map[module_id].origin.file_id();
+ res.extend(file_id)
+ }
+ }
+ res
+ }
}
impl TestDB {
SyntaxNode,
};
use stdx::format_to;
+use test_utils::extract_annotations;
use crate::{
db::HirDatabase, display::HirDisplay, infer::TypeMismatch, test_db::TestDB, InferenceResult, Ty,
// against snapshots of the expected results using insta. Use cargo-insta to
// update the snapshots.
+fn check_types(ra_fixture: &str) {
+ 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 });
+ assert_eq!(expected, actual);
+ checked_one = true;
+ }
+ }
+ 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())
}
use insta::assert_snapshot;
-use super::{infer_with_mismatches, type_at};
+use super::{check_types, infer_with_mismatches};
#[test]
fn infer_never1() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
fn test() {
let t = return;
- t<|>;
-}
+ t;
+} //^ !
"#,
);
- assert_eq!(t, "!");
}
#[test]
fn infer_never2() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
fn gen<T>() -> T { loop {} }
fn test() {
let a = gen();
if false { a } else { loop {} };
- a<|>;
-}
+ a;
+} //^ !
"#,
);
- assert_eq!(t, "!");
}
#[test]
fn infer_never3() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
fn gen<T>() -> T { loop {} }
fn test() {
let a = gen();
if false { loop {} } else { a };
- a<|>;
+ a;
+ //^ !
}
"#,
);
- assert_eq!(t, "!");
}
#[test]
fn never_type_in_generic_args() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
enum Option<T> { None, Some(T) }
fn test() {
let a = if true { Option::None } else { Option::Some(return) };
- a<|>;
-}
+ a;
+} //^ Option<!>
"#,
);
- assert_eq!(t, "Option<!>");
}
#[test]
fn never_type_can_be_reinferred1() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
fn gen<T>() -> T { loop {} }
fn test() {
let a = gen();
if false { loop {} } else { a };
- a<|>;
+ a;
+ //^ ()
if false { a };
}
"#,
);
- assert_eq!(t, "()");
}
#[test]
fn never_type_can_be_reinferred2() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
enum Option<T> { None, Some(T) }
fn test() {
let a = if true { Option::None } else { Option::Some(return) };
- a<|>;
+ a;
+ //^ Option<i32>
match 42 {
42 => a,
_ => Option::Some(42),
}
"#,
);
- assert_eq!(t, "Option<i32>");
}
#[test]
fn never_type_can_be_reinferred3() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
enum Option<T> { None, Some(T) }
fn test() {
let a = if true { Option::None } else { Option::Some(return) };
- a<|>;
+ a;
+ //^ Option<&str>
match 42 {
42 => a,
_ => Option::Some("str"),
}
"#,
);
- assert_eq!(t, "Option<&str>");
}
#[test]
fn match_no_arm() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
enum Void {}
fn test(a: Void) {
let t = match a {};
- t<|>;
-}
+ t;
+} //^ !
"#,
);
- assert_eq!(t, "!");
}
#[test]
fn match_unknown_arm() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
fn test(a: Option) {
let t = match 0 {
_ => unknown,
};
- t<|>;
-}
+ t;
+} //^ {unknown}
"#,
);
- assert_eq!(t, "{unknown}");
}
#[test]
fn if_never() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
fn test() {
let i = if true {
loop {}
} else {
3.0
};
- i<|>;
-}
+ i;
+} //^ f64
"#,
);
- assert_eq!(t, "f64");
}
#[test]
fn if_else_never() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
fn test(input: bool) {
let i = if input {
2.0
} else {
return
};
- i<|>;
-}
+ i;
+} //^ f64
"#,
);
- assert_eq!(t, "f64");
}
#[test]
fn match_first_arm_never() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
fn test(a: i32) {
let i = match a {
1 => return,
3 => loop {},
_ => 3.0,
};
- i<|>;
-}
+ i;
+} //^ f64
"#,
);
- assert_eq!(t, "f64");
}
#[test]
fn match_second_arm_never() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
fn test(a: i32) {
let i = match a {
1 => 3.0,
3 => 3.0,
_ => return,
};
- i<|>;
-}
+ i;
+} //^ f64
"#,
);
- assert_eq!(t, "f64");
}
#[test]
fn match_all_arms_never() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
fn test(a: i32) {
let i = match a {
2 => return,
_ => loop {},
};
- i<|>;
-}
+ i;
+} //^ !
"#,
);
- assert_eq!(t, "!");
}
#[test]
fn match_no_never_arms() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
fn test(a: i32) {
let i = match a {
2 => 2.0,
_ => 3.0,
};
- i<|>;
-}
+ i;
+} //^ f64
"#,
);
- assert_eq!(t, "f64");
}
#[test]
-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_box() {
- let (db, pos) = TestDB::with_position(
+ check_types(
r#"
//- /main.rs crate:main deps:std
-
fn test() {
let x = box 1;
let t = (x, box x, box &1, box [1]);
- t<|>;
-}
+ t;
+} //^ (Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; _]>)
//- /std.rs crate:std
#[prelude_import] use prelude::*;
inner: *mut T,
}
}
-
"#,
);
- assert_eq!("(Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; _]>)", type_at_pos(&db, pos));
}
#[test]
fn infer_adt_self() {
- let (db, pos) = TestDB::with_position(
+ check_types(
r#"
-//- /main.rs
enum Nat { Succ(Self), Demo(Nat), Zero }
fn test() {
let foo: Nat = Nat::Zero;
if let Nat::Succ(x) = foo {
- x<|>
- }
+ x
+ } //^ Nat
}
-
"#,
);
- assert_eq!("Nat", type_at_pos(&db, pos));
}
#[test]
#[test]
fn infer_ranges() {
- let (db, pos) = TestDB::with_position(
+ check_types(
r#"
//- /main.rs crate:main deps:core
fn test() {
let f = 'a'..='z';
let t = (a, b, c, d, e, f);
- t<|>;
-}
+ t;
+} //^ (RangeFull, RangeFrom<i32>, RangeTo<u32>, Range<usize>, RangeToInclusive<i32>, RangeInclusive<char>)
//- /core.rs crate:core
#[prelude_import] use prelude::*;
}
"#,
);
- assert_eq!(
- "(RangeFull, RangeFrom<i32>, RangeTo<u32>, Range<usize>, RangeToInclusive<i32>, RangeInclusive<char>)",
- type_at_pos(&db, pos),
- );
}
#[test]
fn infer_while_let() {
- let (db, pos) = TestDB::with_position(
+ check_types(
r#"
-//- /main.rs
enum Option<T> { Some(T), None }
fn test() {
let foo: Option<f32> = None;
while let Option::Some(x) = foo {
- <|>x
- }
+ x
+ } //^ f32
}
-
"#,
);
- assert_eq!("f32", type_at_pos(&db, pos));
}
#[test]
#[test]
fn shadowing_primitive() {
- let t = type_at(
+ check_types(
r#"
-//- /main.rs
struct i32;
struct Foo;
fn main() {
let x: i32 = i32;
- x.foo()<|>;
+ x.foo();
+ //^ Foo
}"#,
);
- assert_eq!(t, "Foo");
}
#[test]
fn not_shadowing_primitive_by_module() {
- let t = type_at(
+ check_types(
r#"
//- /str.rs
fn foo() {}
fn foo() -> &'static str { "" }
fn main() {
- foo()<|>;
+ foo();
+ //^ &str
}"#,
);
- assert_eq!(t, "&str");
}
#[test]
fn not_shadowing_module_by_primitive() {
- let t = type_at(
+ check_types(
r#"
//- /str.rs
fn foo() -> u32 {0}
fn foo() -> &'static str { "" }
fn main() {
- str::foo()<|>;
+ str::foo();
+ //^ u32
}"#,
);
- assert_eq!(t, "u32");
}
// This test is actually testing the shadowing behavior within ra_hir_def. It
// capable of asserting the necessary conditions.
#[test]
fn should_be_shadowing_imports() {
- let t = type_at(
- r#"
-mod a {
- pub fn foo() -> i8 {0}
- pub struct foo { a: i8 }
-}
-mod b { pub fn foo () -> u8 {0} }
-mod c { pub struct foo { a: u8 } }
-mod d {
- pub use super::a::*;
- pub use super::c::foo;
- pub use super::b::foo;
-}
-
-fn main() {
- d::foo()<|>;
-}"#,
- );
- assert_eq!(t, "u8");
-
- let t = type_at(
+ check_types(
r#"
mod a {
pub fn foo() -> i8 {0}
}
fn main() {
- d::foo{a:0<|>};
+ d::foo();
+ //^ u8
+ d::foo{a:0};
+ //^ u8
}"#,
);
- assert_eq!(t, "u8");
}
#[test]
};
use serde_json::Value;
+use stdx::lines_with_ends;
use text_size::{TextRange, TextSize};
pub use difference::Changeset as __Changeset;
res
}
+/// Extracts `//^ some text` annotations
+pub fn extract_annotations(text: &str) -> Vec<(TextSize, String)> {
+ let mut res = Vec::new();
+ let mut prev_line_start: Option<TextSize> = None;
+ let mut line_start: TextSize = 0.into();
+ for line in lines_with_ends(text) {
+ 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))
+ }
+ prev_line_start = Some(line_start);
+ line_start += TextSize::of(line);
+ }
+ res
+}
+
+#[test]
+fn test_extract_annotations() {
+ let res = extract_annotations(&trim_indent(
+ r#"
+fn main() {
+ let x = 92;
+ //^ def
+
+ x + 1
+} //^ i32
+ "#,
+ ));
+
+ assert_eq!(res, vec![(20.into(), "def".into()), (47.into(), "i32".into())]);
+}
+
// Comparison functionality borrowed from cargo:
/// Compare a line with an expected pattern.