From e805e8c1d5bf26e9716fb855f97d950395129c20 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 29 Jun 2020 14:21:57 +0200 Subject: (T): make typification tests more data driven --- crates/ra_hir_ty/src/test_db.rs | 13 ++++ crates/ra_hir_ty/src/tests.rs | 16 +++++ crates/ra_hir_ty/src/tests/never_type.rs | 118 ++++++++++++------------------- crates/ra_hir_ty/src/tests/simple.rs | 89 ++++++++--------------- crates/test_utils/src/lib.rs | 34 +++++++++ 5 files changed, 137 insertions(+), 133 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index 9c2c6959d..0481a7b12 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs @@ -154,6 +154,19 @@ impl TestDB { }); (buf, count) } + + pub fn all_files(&self) -> Vec { + 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 { diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index 2a85ce85d..4d0dc3011 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs @@ -28,6 +28,7 @@ use ra_syntax::{ SyntaxNode, }; use stdx::format_to; +use test_utils::extract_annotations; use crate::{ db::HirDatabase, display::HirDisplay, infer::TypeMismatch, test_db::TestDB, InferenceResult, Ty, @@ -37,6 +38,21 @@ use crate::{ // 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()) } diff --git a/crates/ra_hir_ty/src/tests/never_type.rs b/crates/ra_hir_ty/src/tests/never_type.rs index ab9a990f5..64d421d40 100644 --- a/crates/ra_hir_ty/src/tests/never_type.rs +++ b/crates/ra_hir_ty/src/tests/never_type.rs @@ -1,99 +1,91 @@ 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 { 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 { 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 { 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 { 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 { None, Some(T) } fn test() { let a = if true { Option::None } else { Option::Some(return) }; - a<|>; + a; + //^ Option match 42 { 42 => a, _ => Option::Some(42), @@ -101,19 +93,18 @@ fn test() { } "#, ); - assert_eq!(t, "Option"); } #[test] fn never_type_can_be_reinferred3() { - let t = type_at( + check_types( r#" -//- /main.rs enum Option { 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"), @@ -121,82 +112,72 @@ fn test() { } "#, ); - 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, @@ -204,18 +185,16 @@ fn test(a: i32) { 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, @@ -223,45 +202,40 @@ fn test(a: i32) { 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] diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 5e3f2bd3c..de63f4cce 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -1,19 +1,17 @@ -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, Box>, Box<&i32>, Box<[i32; _]>) //- /std.rs crate:std #[prelude_import] use prelude::*; @@ -25,29 +23,24 @@ mod boxed { inner: *mut T, } } - "#, ); - assert_eq!("(Box, Box>, 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] @@ -93,7 +86,7 @@ fn foo() { #[test] fn infer_ranges() { - let (db, pos) = TestDB::with_position( + check_types( r#" //- /main.rs crate:main deps:core fn test() { @@ -105,8 +98,8 @@ fn test() { let f = 'a'..='z'; let t = (a, b, c, d, e, f); - t<|>; -} + t; +} //^ (RangeFull, RangeFrom, RangeTo, Range, RangeToInclusive, RangeInclusive) //- /core.rs crate:core #[prelude_import] use prelude::*; @@ -135,29 +128,22 @@ pub mod ops { } "#, ); - assert_eq!( - "(RangeFull, RangeFrom, RangeTo, Range, RangeToInclusive, RangeInclusive)", - type_at_pos(&db, pos), - ); } #[test] fn infer_while_let() { - let (db, pos) = TestDB::with_position( + check_types( r#" -//- /main.rs enum Option { Some(T), None } fn test() { let foo: Option = None; while let Option::Some(x) = foo { - <|>x - } + x + } //^ f32 } - "#, ); - assert_eq!("f32", type_at_pos(&db, pos)); } #[test] @@ -1687,9 +1673,8 @@ fn test() { #[test] fn shadowing_primitive() { - let t = type_at( + check_types( r#" -//- /main.rs struct i32; struct Foo; @@ -1697,15 +1682,15 @@ impl i32 { fn foo(&self) -> Foo { 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() {} @@ -1715,15 +1700,15 @@ mod str; 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} @@ -1733,10 +1718,10 @@ mod str; 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 @@ -1744,27 +1729,7 @@ fn main() { // 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} @@ -1779,10 +1744,12 @@ mod d { } fn main() { - d::foo{a:0<|>}; + d::foo(); + //^ u8 + d::foo{a:0}; + //^ u8 }"#, ); - assert_eq!(t, "u8"); } #[test] diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index eaeeeb97b..e74f3b263 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -16,6 +16,7 @@ use std::{ }; use serde_json::Value; +use stdx::lines_with_ends; use text_size::{TextRange, TextSize}; pub use difference::Changeset as __Changeset; @@ -159,6 +160,39 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String { 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 = 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. -- cgit v1.2.3