From e4267967a8ee3b35d902931cecf06bb4e19f86c5 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Fri, 1 May 2020 11:23:03 +0800 Subject: Support local_inner_macros --- crates/ra_hir_ty/src/tests/macros.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 5ddecbdc6..70e17bc94 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs @@ -387,6 +387,32 @@ fn main() { ); } +#[test] +fn infer_local_inner_macros() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:foo +fn test() { + let x = foo::foo!(1); + x<|>; +} + +//- /lib.rs crate:foo +#[macro_export(local_inner_macros)] +macro_rules! foo { + (1) => { bar!() }; +} + +#[macro_export] +macro_rules! bar { + () => { 42 } +} + +"#, + ); + assert_eq!("i32", type_at_pos(&db, pos)); +} + #[test] fn infer_builtin_macros_line() { assert_snapshot!( -- cgit v1.2.3 From fd030f9450ed6910677e30f8fa65b06e71fcffa2 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 2 May 2020 01:12:37 +0200 Subject: Revert "Merge #4233" This reverts commit a5f2b16366f027ad60c58266a66eb7fbdcbda9f9, reversing changes made to c96b2180c1c4206a0a98c280b4d30897eb116336. --- crates/ra_hir_ty/src/infer/expr.rs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index efc60986b..83f946eee 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -73,6 +73,11 @@ impl<'a> InferenceContext<'a> { self.coerce_merge_branch(&then_ty, &else_ty) } Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected), + Expr::TryBlock { body } => { + let _inner = self.infer_expr(*body, expected); + // FIXME should be std::result::Result<{inner}, _> + Ty::Unknown + } Expr::Loop { body } => { self.infer_expr(*body, &Expectation::has_type(Ty::unit())); // FIXME handle break with value -- cgit v1.2.3 From b58dfd24f1bf1f30128fa9a78368b4d430e10e97 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 2 May 2020 11:27:28 +0200 Subject: Add smoke test for decorated blocks --- crates/ra_hir_ty/src/tests/simple.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 56abc65b8..3d3088965 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -1755,3 +1755,35 @@ fn main() { "### ); } + +#[test] +fn effects_smoke_test() { + assert_snapshot!( + infer(r#" +fn main() { + let x = unsafe { 92 }; + let y = async { async { () }.await }; + let z = try { () }; + let t = 'a: { 92 }; +} +"#), + @r###" + 11..131 '{ ...2 }; }': () + 21..22 'x': i32 + 32..38 '{ 92 }': i32 + 34..36 '92': i32 + 48..49 'y': {unknown} + 58..80 '{ asyn...wait }': {unknown} + 60..78 'async ....await': {unknown} + 66..72 '{ () }': () + 68..70 '()': () + 90..91 'z': {unknown} + 94..104 'try { () }': {unknown} + 98..104 '{ () }': () + 100..102 '()': () + 114..115 't': i32 + 122..128 '{ 92 }': i32 + 124..126 '92': i32 + "### + ) +} -- cgit v1.2.3 From 8b6216df05a1d17e58f789bbd323ce2679f9ab4a Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 3 May 2020 22:08:39 +0800 Subject: Support macro for trait items --- crates/ra_hir_ty/src/tests/traits.rs | 40 +++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 19 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index e555c879a..23f682eff 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -2055,7 +2055,7 @@ fn test>>() { #[test] fn proc_macro_server_types() { assert_snapshot!( - infer_with_mismatches(r#" + infer(r#" macro_rules! with_api { ($S:ident, $self:ident, $m:ident) => { $m! { @@ -2069,9 +2069,9 @@ macro_rules! with_api { } macro_rules! associated_item { (type TokenStream) => - (type TokenStream: 'static + Clone;); + (type TokenStream: 'static;); (type Group) => - (type Group: 'static + Clone;); + (type Group: 'static;); ($($item:tt)*) => ($($item)*;) } macro_rules! declare_server_traits { @@ -2083,39 +2083,41 @@ macro_rules! declare_server_traits { } $(pub trait $name: Types { - $(associated_item!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)* + $(associated_item!(fn $method($($arg: $arg_ty),*) $(-> $ret_ty)?);)* })* pub trait Server: Types $(+ $name)* {} impl Server for S {} } } + with_api!(Self, self_, declare_server_traits); -struct Group {} -struct TokenStream {} +struct G {} +struct T {} struct Rustc; impl Types for Rustc { - type TokenStream = TokenStream; - type Group = Group; + type TokenStream = T; + type Group = G; } + fn make() -> T { loop {} } impl TokenStream for Rustc { fn new() -> Self::TokenStream { let group: Self::Group = make(); make() } -} -"#, true), +} +"#), @r###" - 1115..1126 '{ loop {} }': T - 1117..1124 'loop {}': ! - 1122..1124 '{}': () - 1190..1253 '{ ... }': {unknown} - 1204..1209 'group': {unknown} - 1225..1229 'make': fn make<{unknown}>() -> {unknown} - 1225..1231 'make()': {unknown} - 1241..1245 'make': fn make<{unknown}>() -> {unknown} - 1241..1247 'make()': {unknown} + 1062..1073 '{ loop {} }': T + 1064..1071 'loop {}': ! + 1069..1071 '{}': () + 1137..1200 '{ ... }': T + 1151..1156 'group': G + 1172..1176 'make': fn make() -> G + 1172..1178 'make()': G + 1188..1192 'make': fn make() -> T + 1188..1194 'make()': T "### ); } -- cgit v1.2.3 From 73ed2f2e9063fbf16f92d9d2edc87bed1259b883 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 3 May 2020 22:15:07 +0800 Subject: Remove whitespaces --- crates/ra_hir_ty/src/tests/traits.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 23f682eff..9d32cbc7a 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -2106,7 +2106,7 @@ impl TokenStream for Rustc { let group: Self::Group = make(); make() } -} +} "#), @r###" 1062..1073 '{ loop {} }': T -- cgit v1.2.3 From bed115d6e1a634d79e175e543a7936cd03200ebc Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Sun, 3 May 2020 17:56:45 +0200 Subject: add support of cfg attributes on enum variants #4279 Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_hir_ty/src/tests.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index 588d81282..d60732e19 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs @@ -360,6 +360,33 @@ fn no_such_field_with_feature_flag_diagnostics() { assert_snapshot!(diagnostics, @r###""###); } +#[test] +fn no_such_field_enum_with_feature_flag_diagnostics() { + let diagnostics = TestDB::with_files( + r#" + //- /lib.rs crate:foo cfg:feature=foo + enum Foo { + #[cfg(not(feature = "foo"))] + Buz, + #[cfg(feature = "foo")] + Bar, + Baz + } + + fn test_fn(f: Foo) { + match f { + Foo::Bar => {}, + Foo::Baz => {}, + } + } + "#, + ) + .diagnostics() + .0; + + assert_snapshot!(diagnostics, @r###""###); +} + #[test] fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { let diagnostics = TestDB::with_files( -- cgit v1.2.3 From 204c8de3bfc5a8c21e0104a68356bf90f7762527 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Tue, 5 May 2020 00:29:47 +0800 Subject: Add test for issue --- crates/ra_hir_ty/src/tests/regression.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs index 8a1292c7a..115ad8328 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs @@ -534,6 +534,35 @@ fn foo(b: Bar) { ); } +#[test] +fn issue_4235_name_conflicts() { + assert_snapshot!( + infer(r#" +struct FOO {} +static FOO:FOO = FOO {}; + +impl FOO { + fn foo(&self) {} +} + +fn main() { + let a = &FOO; + a.foo(); +} +"#), @r###" + 32..38 'FOO {}': FOO + 64..68 'self': &FOO + 70..72 '{}': () + 86..120 '{ ...o(); }': () + 96..97 'a': &FOO + 100..104 '&FOO': &FOO + 101..104 'FOO': FOO + 110..111 'a': &FOO + 110..117 'a.foo()': () +"### + ); +} + #[test] fn issue_4053_diesel_where_clauses() { assert_snapshot!( -- cgit v1.2.3 From 92665358cd98913e3fef8294e1889cc0bb919e3f Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Tue, 5 May 2020 23:56:10 +0800 Subject: Rename ImplItem to AssocItem --- crates/ra_hir_ty/src/tests/macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 29e38a06c..07398ddcc 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs @@ -269,7 +269,7 @@ fn test() { S.foo()<|>; } } #[test] -fn infer_impl_items_generated_by_macros() { +fn infer_assoc_items_generated_by_macros() { let t = type_at( r#" //- /main.rs @@ -288,7 +288,7 @@ fn test() { S.foo()<|>; } } #[test] -fn infer_impl_items_generated_by_macros_chain() { +fn infer_assoc_items_generated_by_macros_chain() { let t = type_at( r#" //- /main.rs -- cgit v1.2.3 From 7c94fa7d01f264e5268ab5f57519f071c00a6579 Mon Sep 17 00:00:00 2001 From: Mikail Bagishov Date: Wed, 6 May 2020 17:45:47 +0300 Subject: Fix usefulness check for never type --- crates/ra_hir_ty/src/_match.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs index 779e78574..149f65042 100644 --- a/crates/ra_hir_ty/src/_match.rs +++ b/crates/ra_hir_ty/src/_match.rs @@ -573,14 +573,20 @@ pub(crate) fn is_useful( matrix: &Matrix, v: &PatStack, ) -> MatchCheckResult { - // Handle the special case of enums with no variants. In that case, no match - // arm is useful. - if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(AdtId::EnumId(enum_id)), .. }) = - cx.infer[cx.match_expr].strip_references() - { - if cx.db.enum_data(*enum_id).variants.is_empty() { + // Handle two special cases: + // - enum with no variants + // - `!` type + // In those cases, no match arm is useful. + match cx.infer[cx.match_expr].strip_references() { + Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(AdtId::EnumId(enum_id)), .. }) => { + if cx.db.enum_data(*enum_id).variants.is_empty() { + return Ok(Usefulness::NotUseful); + } + } + Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) => { return Ok(Usefulness::NotUseful); } + _ => (), } if v.is_empty() { @@ -1917,6 +1923,17 @@ mod tests { check_no_diagnostic(content); } + #[test] + fn type_never() { + let content = r" + fn test_fn(never: !) { + match never {} + } + "; + + check_no_diagnostic(content); + } + #[test] fn enum_never_ref() { let content = r" -- cgit v1.2.3 From f5177f91ae61e1d812efd6b55a1ec7ab07c7c7e1 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 8 May 2020 15:17:35 +0200 Subject: Fix type of byte literals They're `&[u8; N]`, not `&[u8]` (see #4374). --- crates/ra_hir_ty/src/infer/expr.rs | 4 ++-- crates/ra_hir_ty/src/tests/method_resolution.rs | 13 +++++++------ crates/ra_hir_ty/src/tests/simple.rs | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 83f946eee..614c352a0 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -501,8 +501,8 @@ impl<'a> InferenceContext<'a> { } Literal::ByteString(..) => { let byte_type = Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::u8()))); - let slice_type = Ty::apply_one(TypeCtor::Slice, byte_type); - Ty::apply_one(TypeCtor::Ref(Mutability::Shared), slice_type) + let array_type = Ty::apply_one(TypeCtor::Array, byte_type); + Ty::apply_one(TypeCtor::Ref(Mutability::Shared), array_type) } Literal::Char(..) => Ty::simple(TypeCtor::Char), Literal::Int(_v, ty) => Ty::simple(TypeCtor::Int((*ty).into())), diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index ab87f598a..67f964ab5 100644 --- a/crates/ra_hir_ty/src/tests/method_resolution.rs +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs @@ -17,8 +17,8 @@ impl [T] { #[lang = "slice_alloc"] impl [T] {} -fn test() { - <[_]>::foo(b"foo"); +fn test(x: &[u8]) { + <[_]>::foo(x); } "#), @r###" @@ -26,10 +26,11 @@ fn test() { 56..79 '{ ... }': T 66..73 'loop {}': ! 71..73 '{}': () - 133..160 '{ ...o"); }': () - 139..149 '<[_]>::foo': fn foo(&[u8]) -> u8 - 139..157 '<[_]>:..."foo")': u8 - 150..156 'b"foo"': &[u8] + 131..132 'x': &[u8] + 141..163 '{ ...(x); }': () + 147..157 '<[_]>::foo': fn foo(&[u8]) -> u8 + 147..160 '<[_]>::foo(x)': u8 + 158..159 'x': &[u8] "### ); } diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 3d3088965..e17a17900 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -414,7 +414,7 @@ fn test() { 27..31 '5f32': f32 37..41 '5f64': f64 47..54 '"hello"': &str - 60..68 'b"bytes"': &[u8] + 60..68 'b"bytes"': &[u8; _] 74..77 ''c'': char 83..87 'b'b'': u8 93..97 '3.14': f64 @@ -422,7 +422,7 @@ fn test() { 113..118 'false': bool 124..128 'true': bool 134..202 'r#" ... "#': &str - 208..218 'br#"yolo"#': &[u8] + 208..218 'br#"yolo"#': &[u8; _] "### ); } -- cgit v1.2.3 From fe93675e8ac2b55d051156151489dbe0496efec3 Mon Sep 17 00:00:00 2001 From: Timo Freiberg Date: Sat, 25 Apr 2020 16:57:59 +0200 Subject: New HirDisplay method for displaying sourcecode --- crates/ra_hir_ty/src/display.rs | 176 +++++++++++++++++----- crates/ra_hir_ty/src/tests.rs | 17 ++- crates/ra_hir_ty/src/tests/display_source_code.rs | 23 +++ 3 files changed, 178 insertions(+), 38 deletions(-) create mode 100644 crates/ra_hir_ty/src/tests/display_source_code.rs (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index d03bbd5a7..f5edaea8c 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs @@ -6,28 +6,42 @@ use crate::{ db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, }; -use hir_def::{generics::TypeParamProvenance, AdtId, AssocContainerId, Lookup}; +use hir_def::{ + find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, + Lookup, ModuleId, +}; use hir_expand::name::Name; -pub struct HirFormatter<'a, 'b> { +pub struct HirFormatter<'a> { pub db: &'a dyn HirDatabase, - fmt: &'a mut fmt::Formatter<'b>, + fmt: &'a mut dyn fmt::Write, buf: String, curr_size: usize, pub(crate) max_size: Option, omit_verbose_types: bool, + display_target: DisplayTarget, } pub trait HirDisplay { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result; + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError>; + /// Returns a `Display`able type that is human-readable. + /// Use this for showing types to the user (e.g. diagnostics) fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self> where Self: Sized, { - HirDisplayWrapper(db, self, None, false) + HirDisplayWrapper { + db, + t: self, + max_size: None, + omit_verbose_types: false, + display_target: DisplayTarget::Diagnostics, + } } + /// Returns a `Display`able type that is human-readable and tries to be succinct. + /// Use this for showing types to the user where space is constrained (e.g. doc popups) fn display_truncated<'a>( &'a self, db: &'a dyn HirDatabase, @@ -36,16 +50,46 @@ pub trait HirDisplay { where Self: Sized, { - HirDisplayWrapper(db, self, max_size, true) + HirDisplayWrapper { + db, + t: self, + max_size, + omit_verbose_types: true, + display_target: DisplayTarget::Diagnostics, + } + } + + /// Returns a String representation of `self` that can be inserted into the given module. + /// Use this when generating code (e.g. assists) + fn display_source_code<'a>( + &'a self, + db: &'a dyn HirDatabase, + module_id: ModuleId, + ) -> Result { + let mut result = String::new(); + match self.hir_fmt(&mut HirFormatter { + db, + fmt: &mut result, + buf: String::with_capacity(20), + curr_size: 0, + max_size: None, + omit_verbose_types: false, + display_target: DisplayTarget::SourceCode { module_id }, + }) { + Ok(()) => {} + Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"), + Err(HirDisplayError::DisplaySourceCodeError(e)) => return Err(e), + }; + Ok(result) } } -impl<'a, 'b> HirFormatter<'a, 'b> { +impl<'a> HirFormatter<'a> { pub fn write_joined( &mut self, iter: impl IntoIterator, sep: &str, - ) -> fmt::Result { + ) -> Result<(), HirDisplayError> { let mut first = true; for e in iter { if !first { @@ -58,14 +102,14 @@ impl<'a, 'b> HirFormatter<'a, 'b> { } /// This allows using the `write!` macro directly with a `HirFormatter`. - pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result { + pub fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), HirDisplayError> { // We write to a buffer first to track output size self.buf.clear(); fmt::write(&mut self.buf, args)?; self.curr_size += self.buf.len(); // Then we write to the internal formatter from the buffer - self.fmt.write_str(&self.buf) + self.fmt.write_str(&self.buf).map_err(HirDisplayError::from) } pub fn should_truncate(&self) -> bool { @@ -81,34 +125,76 @@ impl<'a, 'b> HirFormatter<'a, 'b> { } } -pub struct HirDisplayWrapper<'a, T>(&'a dyn HirDatabase, &'a T, Option, bool); +#[derive(Clone, Copy)] +enum DisplayTarget { + /// Display types for inlays, doc popups, autocompletion, etc... + /// Showing `{unknown}` or not qualifying paths is fine here. + /// There's no reason for this to fail. + Diagnostics, + /// Display types for inserting them in source files. + /// The generated code should compile, so paths need to be qualified. + SourceCode { module_id: ModuleId }, +} + +#[derive(Debug)] +pub enum DisplaySourceCodeError { + PathNotFound, +} + +pub enum HirDisplayError { + /// Errors that can occur when generating source code + DisplaySourceCodeError(DisplaySourceCodeError), + /// `FmtError` is required to be compatible with std::fmt::Display + FmtError, +} +impl From for HirDisplayError { + fn from(_: fmt::Error) -> Self { + Self::FmtError + } +} + +pub struct HirDisplayWrapper<'a, T> { + db: &'a dyn HirDatabase, + t: &'a T, + max_size: Option, + omit_verbose_types: bool, + display_target: DisplayTarget, +} impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> where T: HirDisplay, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.1.hir_fmt(&mut HirFormatter { - db: self.0, + match self.t.hir_fmt(&mut HirFormatter { + db: self.db, fmt: f, buf: String::with_capacity(20), curr_size: 0, - max_size: self.2, - omit_verbose_types: self.3, - }) + max_size: self.max_size, + omit_verbose_types: self.omit_verbose_types, + display_target: self.display_target, + }) { + Ok(()) => Ok(()), + Err(HirDisplayError::FmtError) => Err(fmt::Error), + Err(HirDisplayError::DisplaySourceCodeError(_)) => { + // This should never happen + panic!("HirDisplay failed when calling Display::fmt!") + } + } } } const TYPE_HINT_TRUNCATION: &str = "…"; impl HirDisplay for &Ty { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { HirDisplay::hir_fmt(*self, f) } } impl HirDisplay for ApplicationTy { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { if f.should_truncate() { return write!(f, "{}", TYPE_HINT_TRUNCATION); } @@ -191,12 +277,30 @@ impl HirDisplay for ApplicationTy { } } TypeCtor::Adt(def_id) => { - let name = match def_id { - AdtId::StructId(it) => f.db.struct_data(it).name.clone(), - AdtId::UnionId(it) => f.db.union_data(it).name.clone(), - AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), - }; - write!(f, "{}", name)?; + match f.display_target { + DisplayTarget::Diagnostics => { + let name = match def_id { + AdtId::StructId(it) => f.db.struct_data(it).name.clone(), + AdtId::UnionId(it) => f.db.union_data(it).name.clone(), + AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), + }; + write!(f, "{}", name)?; + } + DisplayTarget::SourceCode { module_id } => { + if let Some(path) = find_path::find_path( + f.db.upcast(), + ItemInNs::Types(def_id.into()), + module_id, + ) { + write!(f, "{}", path)?; + } else { + return Err(HirDisplayError::DisplaySourceCodeError( + DisplaySourceCodeError::PathNotFound, + )); + } + } + } + if self.parameters.len() > 0 { let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); let parameters_to_write = if f.omit_verbose_types() { @@ -269,7 +373,7 @@ impl HirDisplay for ApplicationTy { } impl HirDisplay for ProjectionTy { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { if f.should_truncate() { return write!(f, "{}", TYPE_HINT_TRUNCATION); } @@ -287,7 +391,7 @@ impl HirDisplay for ProjectionTy { } impl HirDisplay for Ty { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { if f.should_truncate() { return write!(f, "{}", TYPE_HINT_TRUNCATION); } @@ -332,7 +436,7 @@ impl HirDisplay for Ty { fn write_bounds_like_dyn_trait( predicates: &[GenericPredicate], f: &mut HirFormatter, -) -> fmt::Result { +) -> Result<(), HirDisplayError> { // Note: This code is written to produce nice results (i.e. // corresponding to surface Rust) for types that can occur in // actual Rust. It will have weird results if the predicates @@ -394,7 +498,7 @@ fn write_bounds_like_dyn_trait( } impl TraitRef { - fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> fmt::Result { + fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> { if f.should_truncate() { return write!(f, "{}", TYPE_HINT_TRUNCATION); } @@ -416,19 +520,19 @@ impl TraitRef { } impl HirDisplay for TraitRef { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { self.hir_fmt_ext(f, false) } } impl HirDisplay for &GenericPredicate { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { HirDisplay::hir_fmt(*self, f) } } impl HirDisplay for GenericPredicate { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { if f.should_truncate() { return write!(f, "{}", TYPE_HINT_TRUNCATION); } @@ -452,15 +556,15 @@ impl HirDisplay for GenericPredicate { } impl HirDisplay for Obligation { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - match self { - Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { + Ok(match self { + Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db))?, Obligation::Projection(proj) => write!( f, "Normalize({} => {})", proj.projection_ty.display(f.db), proj.ty.display(f.db) - ), - } + )?, + }) } } diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index d60732e19..623d00010 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs @@ -6,6 +6,7 @@ mod patterns; mod traits; mod method_resolution; mod macros; +mod display_source_code; use std::sync::Arc; @@ -16,7 +17,7 @@ use hir_def::{ item_scope::ItemScope, keys, nameres::CrateDefMap, - AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, + AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, ModuleId, }; use hir_expand::{db::AstDatabase, InFile}; use insta::assert_snapshot; @@ -37,6 +38,18 @@ use crate::{ // update the snapshots. 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 { let file = db.parse(pos.file_id).ok().unwrap(); let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); @@ -49,7 +62,7 @@ fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { 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 ty.display(db).to_string(); + return display_fn(ty, module); } panic!("Can't find expression") } diff --git a/crates/ra_hir_ty/src/tests/display_source_code.rs b/crates/ra_hir_ty/src/tests/display_source_code.rs new file mode 100644 index 000000000..ca1748615 --- /dev/null +++ b/crates/ra_hir_ty/src/tests/display_source_code.rs @@ -0,0 +1,23 @@ +use super::displayed_source_at_pos; +use crate::test_db::TestDB; +use ra_db::fixture::WithFixture; + +#[test] +fn qualify_path_to_submodule() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs + +mod foo { + pub struct Foo; +} + +fn bar() { + let foo: foo::Foo = foo::Foo; + foo<|> +} + +"#, + ); + assert_eq!("foo::Foo", displayed_source_at_pos(&db, pos)); +} -- cgit v1.2.3 From fe7bf993aa8d64668707e348f2ea69918cfda9a4 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 8 May 2020 17:36:11 +0200 Subject: Implement better handling of divergence Divergence here means that for some reason, the end of a block will not be reached. We tried to model this just using the never type, but that doesn't work fully (e.g. in `let x = { loop {}; "foo" };` x should still have type `&str`); so this introduces a `diverges` flag that the type checker keeps track of, like rustc does. --- crates/ra_hir_ty/src/infer.rs | 40 ++++++++++++ crates/ra_hir_ty/src/infer/expr.rs | 53 +++++++++++---- crates/ra_hir_ty/src/lib.rs | 7 ++ crates/ra_hir_ty/src/tests/coercion.rs | 4 +- crates/ra_hir_ty/src/tests/macros.rs | 2 +- crates/ra_hir_ty/src/tests/never_type.rs | 107 ++++++++++++++++++++++++++++++- crates/ra_hir_ty/src/tests/simple.rs | 10 +-- 7 files changed, 200 insertions(+), 23 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index bd4ef69a0..d3a066268 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -210,6 +210,7 @@ struct InferenceContext<'a> { /// closures, but currently this is the only field that will change there, /// so it doesn't make sense. return_ty: Ty, + diverges: Diverges, } impl<'a> InferenceContext<'a> { @@ -224,6 +225,7 @@ impl<'a> InferenceContext<'a> { owner, body: db.body(owner), resolver, + diverges: Diverges::Maybe, } } @@ -666,6 +668,44 @@ impl Expectation { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +enum Diverges { + Maybe, + Always, +} + +impl Diverges { + fn is_always(self) -> bool { + self == Diverges::Always + } +} + +impl std::ops::BitAnd for Diverges { + type Output = Self; + fn bitand(self, other: Self) -> Self { + std::cmp::min(self, other) + } +} + +impl std::ops::BitOr for Diverges { + type Output = Self; + fn bitor(self, other: Self) -> Self { + std::cmp::max(self, other) + } +} + +impl std::ops::BitAndAssign for Diverges { + fn bitand_assign(&mut self, other: Self) { + *self = *self & other; + } +} + +impl std::ops::BitOrAssign for Diverges { + fn bitor_assign(&mut self, other: Self) { + *self = *self | other; + } +} + mod diagnostics { use hir_def::{expr::ExprId, FunctionId}; use hir_expand::diagnostics::DiagnosticSink; diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 614c352a0..f2f9883b2 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -1,7 +1,7 @@ //! Type inference for expressions. use std::iter::{repeat, repeat_with}; -use std::sync::Arc; +use std::{mem, sync::Arc}; use hir_def::{ builtin_type::Signedness, @@ -21,11 +21,15 @@ use crate::{ Ty, TypeCtor, Uncertain, }; -use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch}; +use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, Diverges}; impl<'a> InferenceContext<'a> { pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { let ty = self.infer_expr_inner(tgt_expr, expected); + if ty.is_never() { + // Any expression that produces a value of type `!` must have diverged + self.diverges = Diverges::Always; + } let could_unify = self.unify(&ty, &expected.ty); if !could_unify { self.result.type_mismatches.insert( @@ -64,11 +68,18 @@ impl<'a> InferenceContext<'a> { // if let is desugared to match, so this is always simple if self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); + let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); + let mut both_arms_diverge = Diverges::Always; + let then_ty = self.infer_expr_inner(*then_branch, &expected); + both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); let else_ty = match else_branch { Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), None => Ty::unit(), }; + both_arms_diverge &= self.diverges; + + self.diverges = condition_diverges | both_arms_diverge; self.coerce_merge_branch(&then_ty, &else_ty) } @@ -132,10 +143,12 @@ impl<'a> InferenceContext<'a> { // infer the body. self.coerce(&closure_ty, &expected.ty); - let prev_ret_ty = std::mem::replace(&mut self.return_ty, ret_ty.clone()); + let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); + let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); + self.diverges = prev_diverges; self.return_ty = prev_ret_ty; closure_ty @@ -165,7 +178,11 @@ impl<'a> InferenceContext<'a> { self.table.new_type_var() }; + let matchee_diverges = self.diverges; + let mut all_arms_diverge = Diverges::Always; + for arm in arms { + self.diverges = Diverges::Maybe; let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); if let Some(guard_expr) = arm.guard { self.infer_expr( @@ -175,9 +192,12 @@ impl<'a> InferenceContext<'a> { } let arm_ty = self.infer_expr_inner(arm.expr, &expected); + all_arms_diverge &= self.diverges; result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); } + self.diverges = matchee_diverges | all_arms_diverge; + result_ty } Expr::Path(p) => { @@ -522,7 +542,6 @@ impl<'a> InferenceContext<'a> { tail: Option, expected: &Expectation, ) -> Ty { - let mut diverges = false; for stmt in statements { match stmt { Statement::Let { pat, type_ref, initializer } => { @@ -544,9 +563,7 @@ impl<'a> InferenceContext<'a> { self.infer_pat(*pat, &ty, BindingMode::default()); } Statement::Expr(expr) => { - if let ty_app!(TypeCtor::Never) = self.infer_expr(*expr, &Expectation::none()) { - diverges = true; - } + self.infer_expr(*expr, &Expectation::none()); } } } @@ -554,14 +571,22 @@ impl<'a> InferenceContext<'a> { let ty = if let Some(expr) = tail { self.infer_expr_coerce(expr, expected) } else { - self.coerce(&Ty::unit(), expected.coercion_target()); - Ty::unit() + // Citing rustc: if there is no explicit tail expression, + // that is typically equivalent to a tail expression + // of `()` -- except if the block diverges. In that + // case, there is no value supplied from the tail + // expression (assuming there are no other breaks, + // this implies that the type of the block will be + // `!`). + if self.diverges.is_always() { + // we don't even make an attempt at coercion + self.table.new_maybe_never_type_var() + } else { + self.coerce(&Ty::unit(), expected.coercion_target()); + Ty::unit() + } }; - if diverges { - Ty::simple(TypeCtor::Never) - } else { - ty - } + ty } fn infer_method_call( diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index a6f56c661..ac0ef1bfe 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -730,6 +730,13 @@ impl Ty { } } + pub fn is_never(&self) -> bool { + match self { + Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) => true, + _ => false, + } + } + /// If this is a `dyn Trait` type, this returns the `Trait` part. pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { match self { diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index e6fb3e123..0c3a833bd 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs @@ -384,7 +384,7 @@ fn foo() -> u32 { } "#, true), @r###" - 17..40 '{ ...own; }': ! + 17..40 '{ ...own; }': u32 23..37 'return unknown': ! 30..37 'unknown': u32 "### @@ -514,7 +514,7 @@ fn foo() { 27..103 '{ ... }': &u32 37..82 'if tru... }': () 40..44 'true': bool - 45..82 '{ ... }': ! + 45..82 '{ ... }': () 59..71 'return &1u32': ! 66..71 '&1u32': &u32 67..71 '1u32': u32 diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 07398ddcc..4c6099aa2 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs @@ -197,7 +197,7 @@ fn spam() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 54..457 '{ ...!(); }': ! + 54..457 '{ ...!(); }': () 88..109 'spam!(...am!())': {unknown} 115..134 'for _ ...!() {}': () 119..120 '_': {unknown} diff --git a/crates/ra_hir_ty/src/tests/never_type.rs b/crates/ra_hir_ty/src/tests/never_type.rs index a77209480..1721f97c5 100644 --- a/crates/ra_hir_ty/src/tests/never_type.rs +++ b/crates/ra_hir_ty/src/tests/never_type.rs @@ -1,4 +1,6 @@ -use super::type_at; +use insta::assert_snapshot; + +use super::{infer_with_mismatches, type_at}; #[test] fn infer_never1() { @@ -261,3 +263,106 @@ fn test(a: i32) { ); assert_eq!(t, "f64"); } + +#[test] +fn diverging_expression_1() { + let t = infer_with_mismatches( + r#" +//- /main.rs +fn test1() { + let x: u32 = return; +} +fn test2() { + let x: u32 = { return; }; +} +fn test3() { + let x: u32 = loop {}; +} +fn test4() { + let x: u32 = { loop {} }; +} +fn test5() { + let x: u32 = { if true { loop {}; } else { loop {}; } }; +} +fn test6() { + let x: u32 = { let y: u32 = { loop {}; }; }; +} +"#, + true, + ); + assert_snapshot!(t, @r###" + 25..53 '{ ...urn; }': () + 35..36 'x': u32 + 44..50 'return': ! + 65..98 '{ ...; }; }': () + 75..76 'x': u32 + 84..95 '{ return; }': u32 + 86..92 'return': ! + 110..139 '{ ... {}; }': () + 120..121 'x': u32 + 129..136 'loop {}': ! + 134..136 '{}': () + 151..184 '{ ...} }; }': () + 161..162 'x': u32 + 170..181 '{ loop {} }': u32 + 172..179 'loop {}': ! + 177..179 '{}': () + 196..260 '{ ...} }; }': () + 206..207 'x': u32 + 215..257 '{ if t...}; } }': u32 + 217..255 'if tru... {}; }': u32 + 220..224 'true': bool + 225..237 '{ loop {}; }': u32 + 227..234 'loop {}': ! + 232..234 '{}': () + 243..255 '{ loop {}; }': u32 + 245..252 'loop {}': ! + 250..252 '{}': () + 272..324 '{ ...; }; }': () + 282..283 'x': u32 + 291..321 '{ let ...; }; }': u32 + 297..298 'y': u32 + 306..318 '{ loop {}; }': u32 + 308..315 'loop {}': ! + 313..315 '{}': () + "###); +} + +#[test] +fn diverging_expression_2() { + let t = infer_with_mismatches( + r#" +//- /main.rs +fn test1() { + // should give type mismatch + let x: u32 = { loop {}; "foo" }; +} +"#, + true, + ); + assert_snapshot!(t, @r###" + 25..98 '{ ..." }; }': () + 68..69 'x': u32 + 77..95 '{ loop...foo" }': &str + 79..86 'loop {}': ! + 84..86 '{}': () + 88..93 '"foo"': &str + 77..95: expected u32, got &str + 88..93: expected u32, got &str + "###); +} + +#[test] +fn diverging_expression_3_break() { + let t = infer_with_mismatches( + r#" +//- /main.rs +fn test1() { + // should give type mismatch + let x: u32 = { loop { break; } }; +} +"#, + true, + ); + assert_snapshot!(t, @r###""###); +} diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index e17a17900..3820175f6 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -179,7 +179,7 @@ fn test(a: u32, b: isize, c: !, d: &str) { 17..18 'b': isize 27..28 'c': ! 33..34 'd': &str - 42..121 '{ ...f32; }': ! + 42..121 '{ ...f32; }': () 48..49 'a': u32 55..56 'b': isize 62..63 'c': ! @@ -935,7 +935,7 @@ fn foo() { 29..33 'true': bool 34..51 '{ ... }': i32 44..45 '1': i32 - 57..80 '{ ... }': ! + 57..80 '{ ... }': i32 67..73 'return': ! 90..93 '_x2': i32 96..149 'if tru... }': i32 @@ -951,7 +951,7 @@ fn foo() { 186..190 'true': bool 194..195 '3': i32 205..206 '_': bool - 210..241 '{ ... }': ! + 210..241 '{ ... }': i32 224..230 'return': ! 257..260 '_x4': i32 263..320 'match ... }': i32 @@ -1687,7 +1687,7 @@ fn foo() -> u32 { 17..59 '{ ...; }; }': () 27..28 'x': || -> usize 31..56 '|| -> ...n 1; }': || -> usize - 43..56 '{ return 1; }': ! + 43..56 '{ return 1; }': usize 45..53 'return 1': ! 52..53 '1': usize "### @@ -1706,7 +1706,7 @@ fn foo() -> u32 { 17..48 '{ ...; }; }': () 27..28 'x': || -> () 31..45 '|| { return; }': || -> () - 34..45 '{ return; }': ! + 34..45 '{ return; }': () 36..42 'return': ! "### ); -- cgit v1.2.3 From b60970fd2050f844e3e52fcfd1724a8c527a11af Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 8 May 2020 17:59:58 +0200 Subject: Handle break somewhat better Still no break-with-value or labels, but at least we know that `loop { break; }` doesn't diverge. --- crates/ra_hir_ty/src/infer.rs | 7 ++++ crates/ra_hir_ty/src/infer/expr.rs | 29 ++++++++++++- crates/ra_hir_ty/src/tests/never_type.rs | 72 +++++++++++++++++++++++++++++++- 3 files changed, 105 insertions(+), 3 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index d3a066268..413904518 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -211,6 +211,12 @@ struct InferenceContext<'a> { /// so it doesn't make sense. return_ty: Ty, diverges: Diverges, + breakables: Vec, +} + +#[derive(Clone, Debug)] +struct BreakableContext { + pub may_break: bool, } impl<'a> InferenceContext<'a> { @@ -226,6 +232,7 @@ impl<'a> InferenceContext<'a> { body: db.body(owner), resolver, diverges: Diverges::Maybe, + breakables: Vec::new(), } } diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index f2f9883b2..9cac0c787 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -21,7 +21,10 @@ use crate::{ Ty, TypeCtor, Uncertain, }; -use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, Diverges}; +use super::{ + BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, + TypeMismatch, +}; impl<'a> InferenceContext<'a> { pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { @@ -90,24 +93,43 @@ impl<'a> InferenceContext<'a> { Ty::Unknown } Expr::Loop { body } => { + self.breakables.push(BreakableContext { may_break: false }); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); + + let ctxt = self.breakables.pop().expect("breakable stack broken"); + if ctxt.may_break { + self.diverges = Diverges::Maybe; + } // FIXME handle break with value - Ty::simple(TypeCtor::Never) + if ctxt.may_break { + Ty::unit() + } else { + Ty::simple(TypeCtor::Never) + } } Expr::While { condition, body } => { + self.breakables.push(BreakableContext { may_break: false }); // while let is desugared to a match loop, so this is always simple while self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); + let _ctxt = self.breakables.pop().expect("breakable stack broken"); + // the body may not run, so it diverging doesn't mean we diverge + self.diverges = Diverges::Maybe; Ty::unit() } Expr::For { iterable, body, pat } => { let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); + self.breakables.push(BreakableContext { may_break: false }); let pat_ty = self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); self.infer_pat(*pat, &pat_ty, BindingMode::default()); + self.infer_expr(*body, &Expectation::has_type(Ty::unit())); + let _ctxt = self.breakables.pop().expect("breakable stack broken"); + // the body may not run, so it diverging doesn't mean we diverge + self.diverges = Diverges::Maybe; Ty::unit() } Expr::Lambda { body, args, ret_type, arg_types } => { @@ -211,6 +233,9 @@ impl<'a> InferenceContext<'a> { // FIXME handle break with value self.infer_expr(*expr, &Expectation::none()); } + if let Some(ctxt) = self.breakables.last_mut() { + ctxt.may_break = true; + } Ty::simple(TypeCtor::Never) } Expr::Return { expr } => { diff --git a/crates/ra_hir_ty/src/tests/never_type.rs b/crates/ra_hir_ty/src/tests/never_type.rs index 1721f97c5..082c47208 100644 --- a/crates/ra_hir_ty/src/tests/never_type.rs +++ b/crates/ra_hir_ty/src/tests/never_type.rs @@ -361,8 +361,78 @@ fn test1() { // should give type mismatch let x: u32 = { loop { break; } }; } +fn test2() { + // should give type mismatch + let x: u32 = { for a in b { break; }; }; + // should give type mismatch as well + let x: u32 = { for a in b {}; }; + // should give type mismatch as well + let x: u32 = { for a in b { return; }; }; +} +fn test3() { + // should give type mismatch + let x: u32 = { while true { break; }; }; + // should give type mismatch as well -- there's an implicit break, even if it's never hit + let x: u32 = { while true {}; }; + // should give type mismatch as well + let x: u32 = { while true { return; }; }; +} "#, true, ); - assert_snapshot!(t, @r###""###); + assert_snapshot!(t, @r###" + 25..99 '{ ...} }; }': () + 68..69 'x': u32 + 77..96 '{ loop...k; } }': () + 79..94 'loop { break; }': () + 84..94 '{ break; }': () + 86..91 'break': ! + 77..96: expected u32, got () + 79..94: expected u32, got () + 111..357 '{ ...; }; }': () + 154..155 'x': u32 + 163..189 '{ for ...; }; }': () + 165..186 'for a ...eak; }': () + 169..170 'a': {unknown} + 174..175 'b': {unknown} + 176..186 '{ break; }': () + 178..183 'break': ! + 240..241 'x': u32 + 249..267 '{ for ... {}; }': () + 251..264 'for a in b {}': () + 255..256 'a': {unknown} + 260..261 'b': {unknown} + 262..264 '{}': () + 318..319 'x': u32 + 327..354 '{ for ...; }; }': () + 329..351 'for a ...urn; }': () + 333..334 'a': {unknown} + 338..339 'b': {unknown} + 340..351 '{ return; }': () + 342..348 'return': ! + 163..189: expected u32, got () + 249..267: expected u32, got () + 327..354: expected u32, got () + 369..668 '{ ...; }; }': () + 412..413 'x': u32 + 421..447 '{ whil...; }; }': () + 423..444 'while ...eak; }': () + 429..433 'true': bool + 434..444 '{ break; }': () + 436..441 'break': ! + 551..552 'x': u32 + 560..578 '{ whil... {}; }': () + 562..575 'while true {}': () + 568..572 'true': bool + 573..575 '{}': () + 629..630 'x': u32 + 638..665 '{ whil...; }; }': () + 640..662 'while ...urn; }': () + 646..650 'true': bool + 651..662 '{ return; }': () + 653..659 'return': ! + 421..447: expected u32, got () + 560..578: expected u32, got () + 638..665: expected u32, got () + "###); } -- cgit v1.2.3 From f8bf94a4b94074eb344e495dfb4dab4bec6bc20e Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 8 May 2020 19:30:02 +0200 Subject: Use matches! --- crates/ra_hir_ty/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index ac0ef1bfe..3e5f38d0d 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -731,10 +731,7 @@ impl Ty { } pub fn is_never(&self) -> bool { - match self { - Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) => true, - _ => false, - } + matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. })) } /// If this is a `dyn Trait` type, this returns the `Trait` part. -- cgit v1.2.3 From d0129c4ddba3b72e7b26e94e9c25546d37dbf166 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 8 May 2020 19:48:03 +0200 Subject: Add diagnostic for break outside of loop --- crates/ra_hir_ty/src/diagnostics.rs | 28 ++++++++++++++++++++++++++++ crates/ra_hir_ty/src/infer.rs | 13 ++++++++++++- crates/ra_hir_ty/src/infer/expr.rs | 4 ++++ crates/ra_hir_ty/src/tests.rs | 18 ++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index c8fd54861..41ac70272 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs @@ -131,3 +131,31 @@ impl AstDiagnostic for MissingOkInTailExpr { ast::Expr::cast(node).unwrap() } } + +#[derive(Debug)] +pub struct BreakOutsideOfLoop { + pub file: HirFileId, + pub expr: AstPtr, +} + +impl Diagnostic for BreakOutsideOfLoop { + fn message(&self) -> String { + "break outside of loop".to_string() + } + fn source(&self) -> InFile { + InFile { file_id: self.file, value: self.expr.clone().into() } + } + fn as_any(&self) -> &(dyn Any + Send + 'static) { + self + } +} + +impl AstDiagnostic for BreakOutsideOfLoop { + type AST = ast::Expr; + + fn ast(&self, db: &impl AstDatabase) -> Self::AST { + let root = db.parse_or_expand(self.file).unwrap(); + let node = self.source().value.to_node(&root); + ast::Expr::cast(node).unwrap() + } +} diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 413904518..a21ad8d86 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -717,11 +717,15 @@ mod diagnostics { use hir_def::{expr::ExprId, FunctionId}; use hir_expand::diagnostics::DiagnosticSink; - use crate::{db::HirDatabase, diagnostics::NoSuchField}; + use crate::{ + db::HirDatabase, + diagnostics::{BreakOutsideOfLoop, NoSuchField}, + }; #[derive(Debug, PartialEq, Eq, Clone)] pub(super) enum InferenceDiagnostic { NoSuchField { expr: ExprId, field: usize }, + BreakOutsideOfLoop { expr: ExprId }, } impl InferenceDiagnostic { @@ -737,6 +741,13 @@ mod diagnostics { let field = source_map.field_syntax(*expr, *field); sink.push(NoSuchField { file: field.file_id, field: field.value }) } + InferenceDiagnostic::BreakOutsideOfLoop { expr } => { + let (_, source_map) = db.body_with_source_map(owner.into()); + let ptr = source_map + .expr_syntax(*expr) + .expect("break outside of loop in synthetic syntax"); + sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) + } } } } diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 9cac0c787..0b67d216a 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -235,6 +235,10 @@ impl<'a> InferenceContext<'a> { } if let Some(ctxt) = self.breakables.last_mut() { ctxt.may_break = true; + } else { + self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { + expr: tgt_expr, + }); } Ty::simple(TypeCtor::Never) } diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index d60732e19..5af88b368 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs @@ -518,3 +518,21 @@ fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { assert_snapshot!(diagnostics, @""); } + +#[test] +fn break_outside_of_loop() { + let diagnostics = TestDB::with_files( + r" + //- /lib.rs + fn foo() { + break; + } + ", + ) + .diagnostics() + .0; + + assert_snapshot!(diagnostics, @r###""break": break outside of loop + "### + ); +} -- cgit v1.2.3 From a3d866e776f43c1ae717740bf0c507f4d9fe47cb Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 8 May 2020 22:12:16 +0200 Subject: Handle coercing function types to function pointers in match E.g. in ```rust match x { 1 => function1, 2 => function2, } ``` we need to try coercing both to pointers. Turns out this is a special case in rustc as well (see the link in the comment). --- crates/ra_hir_ty/src/infer/coerce.rs | 34 ++++++++++++++++++--------- crates/ra_hir_ty/src/lib.rs | 6 +++++ crates/ra_hir_ty/src/marks.rs | 1 + crates/ra_hir_ty/src/tests/coercion.rs | 42 ++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 11 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 89200255a..173ec59ed 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs @@ -20,21 +20,35 @@ impl<'a> InferenceContext<'a> { self.coerce_inner(from_ty, &to_ty) } - /// Merge two types from different branches, with possible implicit coerce. + /// Merge two types from different branches, with possible coercion. /// - /// Note that it is only possible that one type are coerced to another. - /// Coercing both types to another least upper bound type is not possible in rustc, - /// which will simply result in "incompatible types" error. + /// Mostly this means trying to coerce one to the other, but + /// - if we have two function types for different functions, we need to + /// coerce both to function pointers; + /// - if we were concerned with lifetime subtyping, we'd need to look for a + /// least upper bound. pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { if self.coerce(ty1, ty2) { ty2.clone() } else if self.coerce(ty2, ty1) { ty1.clone() } else { - tested_by!(coerce_merge_fail_fallback); - // For incompatible types, we use the latter one as result - // to be better recovery for `if` without `else`. - ty2.clone() + if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) { + tested_by!(coerce_fn_reification); + // Special case: two function types. Try to coerce both to + // pointers to have a chance at getting a match. See + // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 + let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig"); + let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig"); + let ptr_ty1 = Ty::fn_ptr(sig1); + let ptr_ty2 = Ty::fn_ptr(sig2); + self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) + } else { + tested_by!(coerce_merge_fail_fallback); + // For incompatible types, we use the latter one as result + // to be better recovery for `if` without `else`. + ty2.clone() + } } } @@ -84,9 +98,7 @@ impl<'a> InferenceContext<'a> { match from_ty.callable_sig(self.db) { None => return false, Some(sig) => { - let num_args = sig.params_and_return.len() as u16 - 1; - from_ty = - Ty::apply(TypeCtor::FnPtr { num_args }, Substs(sig.params_and_return)); + from_ty = Ty::fn_ptr(sig); } } } diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 3e5f38d0d..e8f3482fe 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -683,6 +683,12 @@ impl Ty { pub fn unit() -> Self { Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty()) } + pub fn fn_ptr(sig: FnSig) -> Self { + Ty::apply( + TypeCtor::FnPtr { num_args: sig.params().len() as u16 }, + Substs(sig.params_and_return), + ) + } pub fn as_reference(&self) -> Option<(&Ty, Mutability)> { match self { diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs index de5cb1d6b..a39740143 100644 --- a/crates/ra_hir_ty/src/marks.rs +++ b/crates/ra_hir_ty/src/marks.rs @@ -7,5 +7,6 @@ test_utils::marks!( impl_self_type_match_without_receiver match_ergonomics_ref coerce_merge_fail_fallback + coerce_fn_reification trait_self_implements_self ); diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index 0c3a833bd..6dc4b2cd1 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs @@ -545,6 +545,48 @@ fn test() { ); } +#[test] +fn coerce_fn_items_in_match_arms() { + covers!(coerce_fn_reification); + assert_snapshot!( + infer_with_mismatches(r#" +fn foo1(x: u32) -> isize { 1 } +fn foo2(x: u32) -> isize { 2 } +fn foo3(x: u32) -> isize { 3 } +fn test() { + let x = match 1 { + 1 => foo1, + 2 => foo2, + _ => foo3, + }; +} +"#, true), + @r###" + 9..10 'x': u32 + 26..31 '{ 1 }': isize + 28..29 '1': isize + 40..41 'x': u32 + 57..62 '{ 2 }': isize + 59..60 '2': isize + 71..72 'x': u32 + 88..93 '{ 3 }': isize + 90..91 '3': isize + 104..193 '{ ... }; }': () + 114..115 'x': fn(u32) -> isize + 118..190 'match ... }': fn(u32) -> isize + 124..125 '1': i32 + 136..137 '1': i32 + 136..137 '1': i32 + 141..145 'foo1': fn foo1(u32) -> isize + 155..156 '2': i32 + 155..156 '2': i32 + 160..164 'foo2': fn foo2(u32) -> isize + 174..175 '_': i32 + 179..183 'foo3': fn foo3(u32) -> isize + "### + ); +} + #[test] fn coerce_closure_to_fn_ptr() { assert_snapshot!( -- cgit v1.2.3 From 85d44cad45761a55741ff406e23f2e40b0f24b88 Mon Sep 17 00:00:00 2001 From: Emil Lauridsen Date: Sun, 10 May 2020 16:20:13 +0200 Subject: infer: Make expected rhs type for plain assign the lhs type This fixes an issue where the following code sample would fail to infer the type contained in the option: ```rust fn main() { let mut end = None; // TODO: Fix inference for this in RA loop { end = Some(true); } } ``` --- crates/ra_hir_ty/src/op.rs | 3 ++- crates/ra_hir_ty/src/tests/simple.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/op.rs b/crates/ra_hir_ty/src/op.rs index 54e2bd05a..0870874fc 100644 --- a/crates/ra_hir_ty/src/op.rs +++ b/crates/ra_hir_ty/src/op.rs @@ -30,7 +30,8 @@ pub(super) fn binary_op_return_ty(op: BinaryOp, lhs_ty: Ty, rhs_ty: Ty) -> Ty { pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { match op { BinaryOp::LogicOp(..) => Ty::simple(TypeCtor::Bool), - BinaryOp::Assignment { op: None } | BinaryOp::CmpOp(CmpOp::Eq { .. }) => match lhs_ty { + BinaryOp::Assignment { op: None } => lhs_ty, + BinaryOp::CmpOp(CmpOp::Eq { .. }) => match lhs_ty { Ty::Apply(ApplicationTy { ctor, .. }) => match ctor { TypeCtor::Int(..) | TypeCtor::Float(..) diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 3820175f6..322838f02 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -1787,3 +1787,32 @@ fn main() { "### ) } + +#[test] +fn infer_generic_from_later_assignment() { + assert_snapshot!( + infer(r#" +enum Option { Some(T), None } +use Option::*; + +fn test() { + let mut end = None; + loop { + end = Some(true); + } +} +"#), + @r###" + 60..130 '{ ... } }': () + 70..77 'mut end': Option + 80..84 'None': Option + 90..128 'loop {... }': ! + 95..128 '{ ... }': () + 105..108 'end': Option + 105..121 'end = ...(true)': () + 111..115 'Some': Some(bool) -> Option + 111..121 'Some(true)': Option + 116..120 'true': bool + "### + ); +} -- cgit v1.2.3 From 11c0a5bb60f9377526a588c11c68d5471ae46aa3 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 10 May 2020 16:08:28 +0100 Subject: Highlight mutable statics as mutable --- crates/ra_hir_ty/src/infer.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index a21ad8d86..fb7c6cd8c 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -22,7 +22,7 @@ use rustc_hash::FxHashMap; use hir_def::{ body::Body, - data::{ConstData, FunctionData}, + data::{ConstData, FunctionData, StaticData}, expr::{BindingAnnotation, ExprId, PatId}, lang_item::LangItemTarget, path::{path, Path}, @@ -71,7 +71,7 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc ctx.collect_const(&db.const_data(c)), DefWithBodyId::FunctionId(f) => ctx.collect_fn(&db.function_data(f)), - DefWithBodyId::StaticId(s) => ctx.collect_const(&db.static_data(s)), + DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)), } ctx.infer_body(); @@ -485,6 +485,10 @@ impl<'a> InferenceContext<'a> { self.return_ty = self.make_ty(&data.type_ref); } + fn collect_static(&mut self, data: &StaticData) { + self.return_ty = self.make_ty(&data.type_ref); + } + fn collect_fn(&mut self, data: &FunctionData) { let body = Arc::clone(&self.body); // avoid borrow checker problem let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) -- cgit v1.2.3 From cbbbd4832582256820695bb73fdb4ca125f1b1f9 Mon Sep 17 00:00:00 2001 From: Timo Freiberg Date: Sun, 10 May 2020 18:09:22 +0200 Subject: Omit default types in HirDisplay SourceCode mode --- crates/ra_hir_ty/src/display.rs | 63 +++++++++++++---------- crates/ra_hir_ty/src/tests/display_source_code.rs | 27 ++++++++++ 2 files changed, 63 insertions(+), 27 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index f5edaea8c..b9c4d2e89 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs @@ -136,6 +136,12 @@ enum DisplayTarget { SourceCode { module_id: ModuleId }, } +impl DisplayTarget { + fn is_source_code(&self) -> bool { + matches!(self, Self::SourceCode {..}) + } +} + #[derive(Debug)] pub enum DisplaySourceCodeError { PathNotFound, @@ -303,37 +309,40 @@ impl HirDisplay for ApplicationTy { if self.parameters.len() > 0 { let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); - let parameters_to_write = if f.omit_verbose_types() { - match self - .ctor - .as_generic_def() - .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) - .filter(|defaults| !defaults.is_empty()) - { - None => self.parameters.0.as_ref(), - Some(default_parameters) => { - for (i, parameter) in self.parameters.iter().enumerate() { - match (parameter, default_parameters.get(i)) { - (&Ty::Unknown, _) | (_, None) => { - non_default_parameters.push(parameter.clone()) - } - (_, Some(default_parameter)) - if parameter != default_parameter => - { - non_default_parameters.push(parameter.clone()) + let parameters_to_write = + if f.display_target.is_source_code() || f.omit_verbose_types() { + match self + .ctor + .as_generic_def() + .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) + .filter(|defaults| !defaults.is_empty()) + { + None => self.parameters.0.as_ref(), + Some(default_parameters) => { + for (i, parameter) in self.parameters.iter().enumerate() { + match (parameter, default_parameters.get(i)) { + (&Ty::Unknown, _) | (_, None) => { + non_default_parameters.push(parameter.clone()) + } + (_, Some(default_parameter)) + if parameter != default_parameter => + { + non_default_parameters.push(parameter.clone()) + } + _ => (), } - _ => (), } + &non_default_parameters } - &non_default_parameters } - } - } else { - self.parameters.0.as_ref() - }; - write!(f, "<")?; - f.write_joined(parameters_to_write, ", ")?; - write!(f, ">")?; + } else { + self.parameters.0.as_ref() + }; + if !parameters_to_write.is_empty() { + write!(f, "<")?; + f.write_joined(parameters_to_write, ", ")?; + write!(f, ">")?; + } } } TypeCtor::AssociatedType(type_alias) => { diff --git a/crates/ra_hir_ty/src/tests/display_source_code.rs b/crates/ra_hir_ty/src/tests/display_source_code.rs index ca1748615..4088b1d22 100644 --- a/crates/ra_hir_ty/src/tests/display_source_code.rs +++ b/crates/ra_hir_ty/src/tests/display_source_code.rs @@ -21,3 +21,30 @@ fn bar() { ); 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: T } + fn main() { + let foo = Foo { t: 5 }; + foo<|>; + } + ", + ); + assert_eq!("Foo", displayed_source_at_pos(&db, pos)); + + let (db, pos) = TestDB::with_position( + r" + //- /main.rs + struct Foo { k: K, t: T } + fn main() { + let foo = Foo { k: 400, t: 5 }; + foo<|>; + } + ", + ); + assert_eq!("Foo", displayed_source_at_pos(&db, pos)); +} -- cgit v1.2.3 From 12bf008ab12f87b2e6d4805681f5a389d9e2f425 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Thu, 14 May 2020 13:47:36 +0300 Subject: Adds a param_idx helper --- crates/ra_hir_ty/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index e8f3482fe..ccc4348f4 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -427,6 +427,11 @@ impl Substs { } } +/// Return an index of a parameter in the generic type parameter list by it's id. +pub fn param_idx(db: &dyn HirDatabase, id: TypeParamId) -> Option { + generics(db.upcast(), id.parent).param_idx(id) +} + #[derive(Debug, Clone)] pub struct SubstsBuilder { vec: Vec, -- cgit v1.2.3 From 3f42b2e837c4672a0fbe953e14ae2fd3fe6fc3b6 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 15 May 2020 17:15:40 +0200 Subject: Handle `Self` in values and patterns I.e. - `Self(x)` or `Self` in tuple/unit struct impls - `Self::Variant(x)` or `Self::Variant` in enum impls - the same in patterns Fixes #4454. --- crates/ra_hir_ty/src/infer.rs | 79 ++++++++++++++++++++++++++++------ crates/ra_hir_ty/src/infer/path.rs | 35 ++++++++++++++- crates/ra_hir_ty/src/tests/patterns.rs | 39 +++++++++++++++++ crates/ra_hir_ty/src/tests/simple.rs | 44 +++++++++++++++++++ 4 files changed, 183 insertions(+), 14 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index fb7c6cd8c..2876cb141 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -28,7 +28,8 @@ use hir_def::{ path::{path, Path}, resolver::{HasResolver, Resolver, TypeNs}, type_ref::{Mutability, TypeRef}, - AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, TraitId, TypeAliasId, VariantId, + AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, TraitId, TypeAliasId, + VariantId, }; use hir_expand::{diagnostics::DiagnosticSink, name::name}; use ra_arena::map::ArenaMap; @@ -438,43 +439,95 @@ impl<'a> InferenceContext<'a> { let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); // FIXME: this should resolve assoc items as well, see this example: // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 - return match resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path()) { - Some(TypeNs::AdtId(AdtId::StructId(strukt))) => { + let (resolution, unresolved) = + match resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { + Some(it) => it, + None => return (Ty::Unknown, None), + }; + return match resolution { + TypeNs::AdtId(AdtId::StructId(strukt)) => { let substs = Ty::substs_from_path(&ctx, path, strukt.into()); let ty = self.db.ty(strukt.into()); let ty = self.insert_type_vars(ty.subst(&substs)); - (ty, Some(strukt.into())) + forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) } - Some(TypeNs::EnumVariantId(var)) => { + TypeNs::EnumVariantId(var) => { let substs = Ty::substs_from_path(&ctx, path, var.into()); let ty = self.db.ty(var.parent.into()); let ty = self.insert_type_vars(ty.subst(&substs)); - (ty, Some(var.into())) + forbid_unresolved_segments((ty, Some(var.into())), unresolved) } - Some(TypeNs::SelfType(impl_id)) => { + TypeNs::SelfType(impl_id) => { let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); let substs = Substs::type_params_for_generics(&generics); let ty = self.db.impl_self_ty(impl_id).subst(&substs); - let variant = ty_variant(&ty); - (ty, variant) + match unresolved { + None => { + let variant = ty_variant(&ty); + (ty, variant) + } + Some(1) => { + let segment = path.mod_path().segments.last().unwrap(); + // this could be an enum variant or associated type + if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { + let enum_data = self.db.enum_data(enum_id); + if let Some(local_id) = enum_data.variant(segment) { + let variant = EnumVariantId { parent: enum_id, local_id }; + return (ty, Some(variant.into())); + } + } + // FIXME potentially resolve assoc type + (Ty::Unknown, None) + } + Some(_) => { + // FIXME diagnostic + (Ty::Unknown, None) + } + } } - Some(TypeNs::TypeAliasId(it)) => { + TypeNs::TypeAliasId(it) => { let substs = Substs::build_for_def(self.db, it) .fill(std::iter::repeat_with(|| self.table.new_type_var())) .build(); let ty = self.db.ty(it.into()).subst(&substs); let variant = ty_variant(&ty); - (ty, variant) + forbid_unresolved_segments((ty, variant), unresolved) + } + TypeNs::AdtSelfType(_) => { + // FIXME this could happen in array size expressions, once we're checking them + (Ty::Unknown, None) + } + TypeNs::GenericParam(_) => { + // FIXME potentially resolve assoc type + (Ty::Unknown, None) + } + TypeNs::AdtId(AdtId::EnumId(_)) + | TypeNs::AdtId(AdtId::UnionId(_)) + | TypeNs::BuiltinType(_) + | TypeNs::TraitId(_) => { + // FIXME diagnostic + (Ty::Unknown, None) } - Some(_) | None => (Ty::Unknown, None), }; + fn forbid_unresolved_segments( + result: (Ty, Option), + unresolved: Option, + ) -> (Ty, Option) { + if unresolved.is_none() { + result + } else { + // FIXME diagnostic + (Ty::Unknown, None) + } + } + fn ty_variant(ty: &Ty) -> Option { ty.as_adt().and_then(|(adt_id, _)| match adt_id { AdtId::StructId(s) => Some(VariantId::StructId(s)), AdtId::UnionId(u) => Some(VariantId::UnionId(u)), AdtId::EnumId(_) => { - // Error E0071, expected struct, variant or union type, found enum `Foo` + // FIXME Error E0071, expected struct, variant or union type, found enum `Foo` None } }) diff --git a/crates/ra_hir_ty/src/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs index 2b6bc0f79..1c2e56fb0 100644 --- a/crates/ra_hir_ty/src/infer/path.rs +++ b/crates/ra_hir_ty/src/infer/path.rs @@ -5,7 +5,7 @@ use std::iter; use hir_def::{ path::{Path, PathSegment}, resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs}, - AssocContainerId, AssocItemId, Lookup, + AdtId, AssocContainerId, AssocItemId, EnumVariantId, Lookup, }; use hir_expand::name::Name; @@ -77,6 +77,18 @@ impl<'a> InferenceContext<'a> { it.into() } + ValueNs::ImplSelf(impl_id) => { + let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); + let substs = Substs::type_params_for_generics(&generics); + let ty = self.db.impl_self_ty(impl_id).subst(&substs); + if let Some((AdtId::StructId(struct_id), _)) = ty.as_adt() { + let ty = self.db.value_ty(struct_id.into()).subst(&substs); + return Some(ty); + } else { + // FIXME: diagnostic, invalid Self reference + return None; + } + } }; let ty = self.db.value_ty(typable); @@ -199,6 +211,10 @@ impl<'a> InferenceContext<'a> { return None; } + if let Some(result) = self.resolve_enum_variant_on_ty(&ty, name, id) { + return Some(result); + } + let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); let krate = self.resolver.krate()?; let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); @@ -250,4 +266,21 @@ impl<'a> InferenceContext<'a> { }, ) } + + fn resolve_enum_variant_on_ty( + &mut self, + ty: &Ty, + name: &Name, + id: ExprOrPatId, + ) -> Option<(ValueNs, Option)> { + let (enum_id, subst) = match ty.as_adt() { + Some((AdtId::EnumId(e), subst)) => (e, subst), + _ => return None, + }; + let enum_data = self.db.enum_data(enum_id); + let local_id = enum_data.variant(name)?; + let variant = EnumVariantId { parent: enum_id, local_id }; + self.write_variant_resolution(id, variant.into()); + Some((ValueNs::EnumVariantId(variant), Some(subst.clone()))) + } } diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs index af291092d..d83ff5e0e 100644 --- a/crates/ra_hir_ty/src/tests/patterns.rs +++ b/crates/ra_hir_ty/src/tests/patterns.rs @@ -368,6 +368,45 @@ fn test() { ); } +#[test] +fn enum_variant_through_self_in_pattern() { + assert_snapshot!( + infer(r#" +enum E { + A { x: usize }, + B(usize), + C +} + +impl E { + fn test() { + match (loop {}) { + Self::A { x } => { x; }, + Self::B(x) => { x; }, + Self::C => {}, + }; + } +} +"#), + @r###" + 76..218 '{ ... }': () + 86..211 'match ... }': () + 93..100 'loop {}': ! + 98..100 '{}': () + 116..129 'Self::A { x }': E + 126..127 'x': usize + 133..139 '{ x; }': () + 135..136 'x': usize + 153..163 'Self::B(x)': E + 161..162 'x': usize + 167..173 '{ x; }': () + 169..170 'x': usize + 187..194 'Self::C': E + 198..200 '{}': () + "### + ); +} + #[test] fn infer_generics_in_patterns() { assert_snapshot!( diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 322838f02..72122c070 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -575,6 +575,50 @@ impl S { ); } +#[test] +fn infer_self_as_path() { + assert_snapshot!( + infer(r#" +struct S1; +struct S2(isize); +enum E { + V1, + V2(u32), +} + +impl S1 { + fn test() { + Self; + } +} +impl S2 { + fn test() { + Self(1); + } +} +impl E { + fn test() { + Self::V1; + Self::V2(1); + } +} +"#), + @r###" + 87..108 '{ ... }': () + 97..101 'Self': S1 + 135..159 '{ ... }': () + 145..149 'Self': S2(isize) -> S2 + 145..152 'Self(1)': S2 + 150..151 '1': isize + 185..231 '{ ... }': () + 195..203 'Self::V1': E + 213..221 'Self::V2': V2(u32) -> E + 213..224 'Self::V2(1)': E + 222..223 '1': u32 + "### + ); +} + #[test] fn infer_binary_op() { assert_snapshot!( -- cgit v1.2.3 From 050601ef7bda598e3ba289987afad7ac7ee46f7d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 16 May 2020 10:49:43 +0200 Subject: Chalk upgrade --- crates/ra_hir_ty/Cargo.toml | 6 ++-- crates/ra_hir_ty/src/traits/chalk.rs | 57 ++++++++++++++++++++++++------------ 2 files changed, 41 insertions(+), 22 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 65db6d1b0..5fc0ec5e3 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml @@ -27,9 +27,9 @@ test_utils = { path = "../test_utils" } scoped-tls = "1" -chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } -chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } -chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } +chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" } +chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" } +chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "3e9c2503ae9c5277c2acb74624dc267876dd89b3" } [dev-dependencies] insta = "0.16.0" diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs index 1ccb7c3b4..5870618a0 100644 --- a/crates/ra_hir_ty/src/traits/chalk.rs +++ b/crates/ra_hir_ty/src/traits/chalk.rs @@ -182,7 +182,10 @@ impl chalk_ir::interner::Interner for Interner { Arc::new(goal) } - fn intern_goals(&self, data: impl IntoIterator>) -> Self::InternedGoals { + fn intern_goals( + &self, + data: impl IntoIterator, E>>, + ) -> Result { data.into_iter().collect() } @@ -222,10 +225,10 @@ impl chalk_ir::interner::Interner for Interner { clause } - fn intern_program_clauses( + fn intern_program_clauses( &self, - data: impl IntoIterator>, - ) -> Arc<[chalk_ir::ProgramClause]> { + data: impl IntoIterator, E>>, + ) -> Result]>, E> { data.into_iter().collect() } @@ -236,10 +239,10 @@ impl chalk_ir::interner::Interner for Interner { &clauses } - fn intern_quantified_where_clauses( + fn intern_quantified_where_clauses( &self, - data: impl IntoIterator>, - ) -> Self::InternedQuantifiedWhereClauses { + data: impl IntoIterator, E>>, + ) -> Result { data.into_iter().collect() } @@ -250,10 +253,10 @@ impl chalk_ir::interner::Interner for Interner { clauses } - fn intern_parameter_kinds( + fn intern_parameter_kinds( &self, - data: impl IntoIterator>, - ) -> Self::InternedParameterKinds { + data: impl IntoIterator, E>>, + ) -> Result { data.into_iter().collect() } @@ -264,10 +267,10 @@ impl chalk_ir::interner::Interner for Interner { ¶meter_kinds } - fn intern_canonical_var_kinds( + fn intern_canonical_var_kinds( &self, - data: impl IntoIterator>, - ) -> Self::InternedCanonicalVarKinds { + data: impl IntoIterator, E>>, + ) -> Result { data.into_iter().collect() } @@ -460,6 +463,14 @@ impl ToChalk for TypeCtor { TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()), TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)), TypeName::OpaqueType(_) => unreachable!(), + + TypeName::Scalar(_) => unreachable!(), + TypeName::Tuple(_) => unreachable!(), + TypeName::Raw(_) => unreachable!(), + TypeName::Slice => unreachable!(), + TypeName::Ref(_) => unreachable!(), + TypeName::Str => unreachable!(), + TypeName::Error => { // this should not be reached, since we don't represent TypeName::Error with TypeCtor unreachable!() @@ -862,12 +873,6 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { // We don't do coherence checking (yet) unimplemented!() } - fn as_struct_id(&self, id: &TypeName) -> Option { - match id { - TypeName::Struct(struct_id) => Some(*struct_id), - _ => None, - } - } fn interner(&self) -> &Interner { &Interner } @@ -892,6 +897,20 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> { ) -> Arc> { unimplemented!() } + + fn force_impl_for( + &self, + _well_known: chalk_rust_ir::WellKnownTrait, + _ty: &chalk_ir::TyData, + ) -> Option { + // this method is mostly for rustc + None + } + + fn is_object_safe(&self, _trait_id: chalk_ir::TraitId) -> bool { + // FIXME: implement actual object safety + true + } } pub(crate) fn program_clauses_for_chalk_env_query( -- cgit v1.2.3 From 811d25b7237cfb74a312369a7820bf6fbb81483e Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sat, 16 May 2020 18:32:15 +0200 Subject: Allow calling dyn trait super trait methods without the super trait in scope This also removes some vestiges of the old impl trait support which I think aren't currently in use. --- crates/ra_hir_ty/src/lib.rs | 14 +++++------ crates/ra_hir_ty/src/method_resolution.rs | 10 +++----- crates/ra_hir_ty/src/tests/method_resolution.rs | 31 +++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 15 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index ccc4348f4..daea02f88 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -808,15 +808,13 @@ impl Ty { } } - /// If this is an `impl Trait` or `dyn Trait`, returns that trait. - pub fn inherent_trait(&self) -> Option { + /// If this is a `dyn Trait`, returns that trait. + pub fn dyn_trait(&self) -> Option { match self { - Ty::Dyn(predicates) | Ty::Opaque(predicates) => { - predicates.iter().find_map(|pred| match pred { - GenericPredicate::Implemented(tr) => Some(tr.trait_), - _ => None, - }) - } + Ty::Dyn(predicates) => predicates.iter().find_map(|pred| match pred { + GenericPredicate::Implemented(tr) => Some(tr.trait_), + _ => None, + }), _ => None, } } diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index 657284fd0..0851e16a8 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs @@ -408,8 +408,9 @@ fn iterate_trait_method_candidates( receiver_ty: Option<&Canonical>, mut callback: impl FnMut(&Ty, AssocItemId) -> Option, ) -> Option { - // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope - let inherent_trait = self_ty.value.inherent_trait().into_iter(); + // if ty is `dyn Trait`, the trait doesn't need to be in scope + let inherent_trait = + self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); let env_traits = if let Ty::Placeholder(_) = self_ty.value { // if we have `T: Trait` in the param env, the trait doesn't need to be in scope env.trait_predicates_for_self_ty(&self_ty.value) @@ -601,11 +602,6 @@ pub fn implements_trait( krate: CrateId, trait_: TraitId, ) -> bool { - if ty.value.inherent_trait() == Some(trait_) { - // FIXME this is a bit of a hack, since Chalk should say the same thing - // anyway, but currently Chalk doesn't implement `dyn/impl Trait` yet - return true; - } let goal = generic_implements_goal(db, env, trait_, ty.clone()); let solution = db.trait_solve(krate, goal); diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index 67f964ab5..9c2c9e1d2 100644 --- a/crates/ra_hir_ty/src/tests/method_resolution.rs +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs @@ -1096,3 +1096,34 @@ fn test() { (S {}).method()<|>; } ); assert_eq!(t, "()"); } + +#[test] +fn dyn_trait_super_trait_not_in_scope() { + assert_snapshot!( + infer(r#" +mod m { + pub trait SuperTrait { + fn foo(&self) -> u32 { 0 } + } +} +trait Trait: m::SuperTrait {} + +struct S; +impl m::SuperTrait for S {} +impl Trait for S {} + +fn test(d: &dyn Trait) { + d.foo(); +} +"#), + @r###" + 52..56 'self': &Self + 65..70 '{ 0 }': u32 + 67..68 '0': u32 + 177..178 'd': &dyn Trait + 192..208 '{ ...o(); }': () + 198..199 'd': &dyn Trait + 198..205 'd.foo()': u32 + "### + ); +} -- cgit v1.2.3 From 12a3bf3c31d4c9a6d9ee110db174604f688ca0f0 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 17 May 2020 23:37:30 +0800 Subject: Create LowerCtx on the fly --- crates/ra_hir_ty/src/tests/regression.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs index 115ad8328..c2168222e 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs @@ -563,6 +563,37 @@ fn main() { ); } +#[test] +fn issue_4465_dollar_crate_at_type() { + assert_snapshot!( + infer(r#" +pub struct Foo {} +pub fn anything() -> T { + loop {} +} +macro_rules! foo { + () => {{ + let r: $crate::Foo = anything(); + r + }}; +} +fn main() { + let _a = foo!(); +} +"#), @r###" + 45..60 '{ loop {} }': T + 51..58 'loop {}': ! + 56..58 '{}': () + !0..31 '{letr:...g();r}': Foo + !4..5 'r': Foo + !18..26 'anything': fn anything() -> Foo + !18..28 'anything()': Foo + !29..30 'r': Foo + 164..188 '{ ...!(); }': () + 174..176 '_a': Foo +"###); +} + #[test] fn issue_4053_diesel_where_clauses() { assert_snapshot!( -- cgit v1.2.3 From 68db49c8534fa99768c7f600455ea76176f61994 Mon Sep 17 00:00:00 2001 From: Hrvoje Ban Date: Mon, 18 May 2020 08:07:31 +0200 Subject: Add more tests for Fn traits --- crates/ra_hir_ty/src/tests/traits.rs | 132 +++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index 9d32cbc7a..c49aacf98 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1616,6 +1616,138 @@ fn test u128>(f: F) { ); } +#[test] +fn fn_ptr_and_item() { + assert_snapshot!( + infer(r#" +#[lang="fn_once"] +trait FnOnce { + type Output; + + fn call_once(self, args: Args) -> Self::Output; +} + +trait Foo { + fn foo(&self) -> T; +} + +struct Bar(T); + +impl R> Foo<(A1, R)> for Bar { + fn foo(&self) -> (A1, R) {} +} + +enum Opt { None, Some(T) } +impl Opt { + fn map U>(self, f: F) -> Opt {} +} + +fn test() { + let bar: Bar u32>; + bar.foo(); + + let opt: Opt; + let f: fn(u8) -> u32; + opt.map(f); +} +"#), + @r###" +75..79 'self': Self +81..85 'args': Args +140..144 'self': &Self +244..248 'self': &Bar +261..263 '{}': () +347..351 'self': Opt +353..354 'f': F +369..371 '{}': () +385..501 '{ ...(f); }': () +395..398 'bar': Bar u32> +424..427 'bar': Bar u32> +424..433 'bar.foo()': {unknown} +444..447 'opt': Opt +466..467 'f': fn(u8) -> u32 +488..491 'opt': Opt +488..498 'opt.map(f)': Opt u32, (u8,)>> +496..497 'f': fn(u8) -> u32 +"### + ); +} + +#[test] +fn fn_trait_deref_with_ty_default() { + assert_snapshot!( + infer(r#" +#[lang = "deref"] +trait Deref { + type Target; + + fn deref(&self) -> &Self::Target; +} + +#[lang="fn_once"] +trait FnOnce { + type Output; + + fn call_once(self, args: Args) -> Self::Output; +} + +struct Foo; + +impl Foo { + fn foo(&self) -> usize {} +} + +struct Lazy T>(F); + +impl Lazy { + pub fn new(f: F) -> Lazy {} +} + +impl T> Deref for Lazy { + type Target = T; +} + +fn test() { + let lazy1: Lazy = Lazy::new(|| Foo); + let r1 = lazy1.foo(); + + fn make_foo_fn() -> Foo {} + let make_foo_fn_ptr: fn() -> Foo = make_foo_fn; + let lazy2: Lazy = Lazy::new(make_foo_fn_ptr); + let r2 = lazy2.foo(); +} +"#), + @r###" +65..69 'self': &Self +166..170 'self': Self +172..176 'args': Args +240..244 'self': &Foo +255..257 '{}': () +335..336 'f': F +355..357 '{}': () +444..690 '{ ...o(); }': () +454..459 'lazy1': Lazy T> +476..485 'Lazy::new': fn new T>(fn() -> T) -> Lazy T> +476..493 'Lazy::...| Foo)': Lazy T> +486..492 '|| Foo': || -> T +489..492 'Foo': Foo +503..505 'r1': {unknown} +508..513 'lazy1': Lazy T> +508..519 'lazy1.foo()': {unknown} +561..576 'make_foo_fn_ptr': fn() -> Foo +592..603 'make_foo_fn': fn make_foo_fn() -> Foo +613..618 'lazy2': Lazy T> +635..644 'Lazy::new': fn new T>(fn() -> T) -> Lazy T> +635..661 'Lazy::...n_ptr)': Lazy T> +645..660 'make_foo_fn_ptr': fn() -> Foo +671..673 'r2': {unknown} +676..681 'lazy2': Lazy T> +676..687 'lazy2.foo()': {unknown} +550..552 '{}': () +"### + ); +} + #[test] fn closure_1() { assert_snapshot!( -- cgit v1.2.3 From 0fe876925e59aad4765b415d9caaf262a6d43c4c Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Mon, 18 May 2020 23:39:10 +0200 Subject: Infer return type of loops with value breaks. --- crates/ra_hir_ty/src/infer.rs | 1 + crates/ra_hir_ty/src/infer/expr.rs | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 2876cb141..957d6e0b5 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -218,6 +218,7 @@ struct InferenceContext<'a> { #[derive(Clone, Debug)] struct BreakableContext { pub may_break: bool, + pub break_ty: Ty, } impl<'a> InferenceContext<'a> { diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 0b67d216a..c7aa67fbe 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -93,7 +93,7 @@ impl<'a> InferenceContext<'a> { Ty::Unknown } Expr::Loop { body } => { - self.breakables.push(BreakableContext { may_break: false }); + self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); let ctxt = self.breakables.pop().expect("breakable stack broken"); @@ -102,13 +102,13 @@ impl<'a> InferenceContext<'a> { } // FIXME handle break with value if ctxt.may_break { - Ty::unit() + ctxt.break_ty } else { Ty::simple(TypeCtor::Never) } } Expr::While { condition, body } => { - self.breakables.push(BreakableContext { may_break: false }); + self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); // while let is desugared to a match loop, so this is always simple while self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); @@ -120,7 +120,7 @@ impl<'a> InferenceContext<'a> { Expr::For { iterable, body, pat } => { let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); - self.breakables.push(BreakableContext { may_break: false }); + self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); let pat_ty = self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); @@ -229,12 +229,21 @@ impl<'a> InferenceContext<'a> { } Expr::Continue => Ty::simple(TypeCtor::Never), Expr::Break { expr } => { + let mut has_val_ty = None; + if let Some(expr) = expr { - // FIXME handle break with value - self.infer_expr(*expr, &Expectation::none()); + has_val_ty = Some(self.infer_expr(*expr, &Expectation::none())); } + if let Some(ctxt) = self.breakables.last_mut() { ctxt.may_break = true; + if let Some(val_ty) = has_val_ty { + if ctxt.break_ty == Ty::Unknown { + ctxt.break_ty = val_ty; + } else if ctxt.break_ty != val_ty { + // TODO: Unify partially matching type information (Option<{unknown}> + Option => Option) + } + } } else { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, -- cgit v1.2.3 From 6eaa669da0c7b3730a309db5e320126653b88997 Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 19 May 2020 21:03:59 +0200 Subject: loop return value inference: coerce_merge branches --- crates/ra_hir_ty/src/infer/expr.rs | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index c7aa67fbe..83702ada0 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -93,14 +93,17 @@ impl<'a> InferenceContext<'a> { Ty::Unknown } Expr::Loop { body } => { - self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown }); + self.breakables.push(BreakableContext { + may_break: false, + break_ty: self.table.new_type_var(), + }); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); let ctxt = self.breakables.pop().expect("breakable stack broken"); if ctxt.may_break { self.diverges = Diverges::Maybe; } - // FIXME handle break with value + if ctxt.may_break { ctxt.break_ty } else { @@ -229,26 +232,31 @@ impl<'a> InferenceContext<'a> { } Expr::Continue => Ty::simple(TypeCtor::Never), Expr::Break { expr } => { - let mut has_val_ty = None; + let val_ty = if let Some(expr) = expr { + self.infer_expr(*expr, &Expectation::none()) + } else { + Ty::unit() + }; - if let Some(expr) = expr { - has_val_ty = Some(self.infer_expr(*expr, &Expectation::none())); - } + let mut has_brkctx = false; - if let Some(ctxt) = self.breakables.last_mut() { - ctxt.may_break = true; - if let Some(val_ty) = has_val_ty { - if ctxt.break_ty == Ty::Unknown { - ctxt.break_ty = val_ty; - } else if ctxt.break_ty != val_ty { - // TODO: Unify partially matching type information (Option<{unknown}> + Option => Option) - } - } + if self.breakables.last().is_some() { + has_brkctx = true; } else { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, }); } + + if has_brkctx { + let last_ty = self.breakables.last().expect("This is a bug").break_ty.clone(); + let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); + + let ctxt = self.breakables.last_mut().expect("This is a bug"); + ctxt.may_break = true; + ctxt.break_ty = merged_type; + } + Ty::simple(TypeCtor::Never) } Expr::Return { expr } => { -- cgit v1.2.3 From 6e36ad3d910ebec5af5f4f208b0f98c613687c41 Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 19 May 2020 21:18:43 +0200 Subject: Move false negative expr_diverges_missing_arm() to working tests --- crates/ra_hir_ty/src/_match.rs | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs index 149f65042..3e6e1e333 100644 --- a/crates/ra_hir_ty/src/_match.rs +++ b/crates/ra_hir_ty/src/_match.rs @@ -1946,6 +1946,23 @@ mod tests { check_no_diagnostic(content); } + + #[test] + fn expr_diverges_missing_arm() { + let content = r" + enum Either { + A, + B, + } + fn test_fn() { + match loop {} { + Either::A => (), + } + } + "; + + check_no_diagnostic(content); + } } #[cfg(test)] @@ -1997,26 +2014,6 @@ mod false_negatives { check_no_diagnostic(content); } - #[test] - fn expr_diverges_missing_arm() { - let content = r" - enum Either { - A, - B, - } - fn test_fn() { - match loop {} { - Either::A => (), - } - } - "; - - // This is a false negative. - // Even though the match expression diverges, rustc fails - // to compile here since `Either::B` is missing. - check_no_diagnostic(content); - } - #[test] fn expr_loop_missing_arm() { let content = r" @@ -2035,7 +2032,7 @@ mod false_negatives { // We currently infer the type of `loop { break Foo::A }` to `!`, which // causes us to skip the diagnostic since `Either::A` doesn't type check // with `!`. - check_no_diagnostic(content); + check_diagnostic(content); } #[test] -- cgit v1.2.3 From da09f967469127576d9a87a7c143f754777a4f6b Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 19 May 2020 21:49:45 +0200 Subject: loop return value inference: add tests --- crates/ra_hir_ty/src/tests/simple.rs | 63 ++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 72122c070..fd2208af2 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -1860,3 +1860,66 @@ fn test() { "### ); } + +#[test] +fn infer_loop_break_with_val() { + assert_snapshot!( + infer(r#" +enum Option { Some(T), None } +use Option::*; + +fn test() { + let x = loop { + if false { + break None; + } + + break Some(true); + }; +} +"#), + @r###" + 60..169 '{ ... }; }': () + 70..71 'x': Option + 74..166 'loop {... }': Option + 79..166 '{ ... }': () + 89..133 'if fal... }': () + 92..97 'false': bool + 98..133 '{ ... }': () + 112..122 'break None': ! + 118..122 'None': Option + 143..159 'break ...(true)': ! + 149..153 'Some': Some(bool) -> Option + 149..159 'Some(true)': Option + 154..158 'true': bool + "### + ); +} + +#[test] +fn infer_loop_break_without_val() { + assert_snapshot!( + infer(r#" +enum Option { Some(T), None } +use Option::*; + +fn test() { + let x = loop { + if false { + break; + } + }; +} +"#), + @r###" + 60..137 '{ ... }; }': () + 70..71 'x': () + 74..134 'loop {... }': () + 79..134 '{ ... }': () + 89..128 'if fal... }': () + 92..97 'false': bool + 98..128 '{ ... }': () + 112..117 'break': ! + "### + ); +} -- cgit v1.2.3 From 45021cae551826727c32c7499c68ca48d046890f Mon Sep 17 00:00:00 2001 From: Roland Ruckerbauer Date: Tue, 19 May 2020 22:52:15 +0200 Subject: Apply suggestion of @flodiebold: Get rid of multiple unwraps --- crates/ra_hir_ty/src/infer/expr.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 83702ada0..b28724f0e 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -238,25 +238,23 @@ impl<'a> InferenceContext<'a> { Ty::unit() }; - let mut has_brkctx = false; + let last_ty = if let Some(ctxt) = self.breakables.last() { + ctxt.break_ty.clone() + } else { + Ty::Unknown + }; - if self.breakables.last().is_some() { - has_brkctx = true; + let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); + + if let Some(ctxt) = self.breakables.last_mut() { + ctxt.break_ty = merged_type; + ctxt.may_break = true; } else { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, }); } - if has_brkctx { - let last_ty = self.breakables.last().expect("This is a bug").break_ty.clone(); - let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); - - let ctxt = self.breakables.last_mut().expect("This is a bug"); - ctxt.may_break = true; - ctxt.break_ty = merged_type; - } - Ty::simple(TypeCtor::Never) } Expr::Return { expr } => { -- cgit v1.2.3 From ecac5d7de2192873c24b7b06d4964d188d8abe6a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 12:59:20 +0200 Subject: Switch to new magic marks --- crates/ra_hir_ty/src/infer/coerce.rs | 6 +++--- crates/ra_hir_ty/src/infer/pat.rs | 4 ++-- crates/ra_hir_ty/src/infer/unify.rs | 8 ++++---- crates/ra_hir_ty/src/lib.rs | 1 - crates/ra_hir_ty/src/lower.rs | 2 +- crates/ra_hir_ty/src/marks.rs | 12 ------------ crates/ra_hir_ty/src/method_resolution.rs | 2 +- crates/ra_hir_ty/src/tests/coercion.rs | 6 +++--- crates/ra_hir_ty/src/tests/method_resolution.rs | 2 +- crates/ra_hir_ty/src/tests/patterns.rs | 4 ++-- crates/ra_hir_ty/src/tests/regression.rs | 15 +++++++-------- crates/ra_hir_ty/src/tests/traits.rs | 8 ++++---- 12 files changed, 28 insertions(+), 42 deletions(-) delete mode 100644 crates/ra_hir_ty/src/marks.rs (limited to 'crates/ra_hir_ty') diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 173ec59ed..2ee9adb16 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs @@ -5,7 +5,7 @@ //! See: https://doc.rust-lang.org/nomicon/coercions.html use hir_def::{lang_item::LangItemTarget, type_ref::Mutability}; -use test_utils::tested_by; +use test_utils::mark; use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor}; @@ -34,7 +34,7 @@ impl<'a> InferenceContext<'a> { ty1.clone() } else { if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) { - tested_by!(coerce_fn_reification); + mark::hit!(coerce_fn_reification); // Special case: two function types. Try to coerce both to // pointers to have a chance at getting a match. See // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 @@ -44,7 +44,7 @@ impl<'a> InferenceContext<'a> { let ptr_ty2 = Ty::fn_ptr(sig2); self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) } else { - tested_by!(coerce_merge_fail_fallback); + mark::hit!(coerce_merge_fail_fallback); // For incompatible types, we use the latter one as result // to be better recovery for `if` without `else`. ty2.clone() diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs index 54ec870df..4006f595d 100644 --- a/crates/ra_hir_ty/src/infer/pat.rs +++ b/crates/ra_hir_ty/src/infer/pat.rs @@ -10,7 +10,7 @@ use hir_def::{ FieldId, }; use hir_expand::name::Name; -use test_utils::tested_by; +use test_utils::mark; use super::{BindingMode, Expectation, InferenceContext}; use crate::{utils::variant_data, Substs, Ty, TypeCtor}; @@ -111,7 +111,7 @@ impl<'a> InferenceContext<'a> { } } } else if let Pat::Ref { .. } = &body[pat] { - tested_by!(match_ergonomics_ref); + mark::hit!(match_ergonomics_ref); // When you encounter a `&pat` pattern, reset to Move. // This is so that `w` is by value: `let (_, &w) = &(1, &2);` default_bm = BindingMode::Move; diff --git a/crates/ra_hir_ty/src/infer/unify.rs b/crates/ra_hir_ty/src/infer/unify.rs index ab0bc8b70..269495ca0 100644 --- a/crates/ra_hir_ty/src/infer/unify.rs +++ b/crates/ra_hir_ty/src/infer/unify.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; -use test_utils::tested_by; +use test_utils::mark; use super::{InferenceContext, Obligation}; use crate::{ @@ -313,7 +313,7 @@ impl InferenceTable { // more than once for i in 0..3 { if i > 0 { - tested_by!(type_var_resolves_to_int_var); + mark::hit!(type_var_resolves_to_int_var); } match &*ty { Ty::Infer(tv) => { @@ -342,7 +342,7 @@ impl InferenceTable { Ty::Infer(tv) => { let inner = tv.to_inner(); if tv_stack.contains(&inner) { - tested_by!(type_var_cycles_resolve_as_possible); + mark::hit!(type_var_cycles_resolve_as_possible); // recursive type return tv.fallback_value(); } @@ -369,7 +369,7 @@ impl InferenceTable { Ty::Infer(tv) => { let inner = tv.to_inner(); if tv_stack.contains(&inner) { - tested_by!(type_var_cycles_resolve_completely); + mark::hit!(type_var_cycles_resolve_completely); // recursive type return tv.fallback_value(); } diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index daea02f88..c87ee06ce 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -42,7 +42,6 @@ pub mod expr; mod tests; #[cfg(test)] mod test_db; -mod marks; mod _match; use std::ops::Deref; diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 9ad6dbe07..35ac86a46 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -812,7 +812,7 @@ impl TraitEnvironment { // add `Self: Trait` to the environment in trait // function default implementations (and hypothetical code // inside consts or type aliases) - test_utils::tested_by!(trait_self_implements_self); + test_utils::mark::hit!(trait_self_implements_self); let substs = Substs::type_params(db, trait_id); let trait_ref = TraitRef { trait_: trait_id, substs }; let pred = GenericPredicate::Implemented(trait_ref); diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs deleted file mode 100644 index a39740143..000000000 --- a/crates/ra_hir_ty/src/marks.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! See test_utils/src/marks.rs - -test_utils::marks!( - type_var_cycles_resolve_completely - type_var_cycles_resolve_as_possible - type_var_resolves_to_int_var - impl_self_type_match_without_receiver - match_ergonomics_ref - coerce_merge_fail_fallback - coerce_fn_reification - trait_self_implements_self -); diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index 0851e16a8..e19628fdf 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs @@ -469,7 +469,7 @@ fn iterate_inherent_methods( // already happens in `is_valid_candidate` above; if not, we // check it here if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { - test_utils::tested_by!(impl_self_type_match_without_receiver); + test_utils::mark::hit!(impl_self_type_match_without_receiver); continue; } if let Some(result) = callback(&self_ty.value, item) { diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index 6dc4b2cd1..2cc4f4bf9 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs @@ -1,6 +1,6 @@ use super::infer_with_mismatches; use insta::assert_snapshot; -use test_utils::covers; +use test_utils::mark; // Infer with some common definitions and impls. fn infer(source: &str) -> String { @@ -339,7 +339,7 @@ fn test(i: i32) { #[test] fn coerce_merge_one_by_one1() { - covers!(coerce_merge_fail_fallback); + mark::check!(coerce_merge_fail_fallback); assert_snapshot!( infer(r#" @@ -547,7 +547,7 @@ fn test() { #[test] fn coerce_fn_items_in_match_arms() { - covers!(coerce_fn_reification); + mark::check!(coerce_fn_reification); assert_snapshot!( infer_with_mismatches(r#" fn foo1(x: u32) -> isize { 1 } diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index 9c2c9e1d2..558a70022 100644 --- a/crates/ra_hir_ty/src/tests/method_resolution.rs +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs @@ -984,7 +984,7 @@ fn test() { S2.into()<|>; } #[test] fn method_resolution_overloaded_method() { - test_utils::covers!(impl_self_type_match_without_receiver); + test_utils::mark::check!(impl_self_type_match_without_receiver); let t = type_at( r#" //- main.rs diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs index d83ff5e0e..0c5f972a2 100644 --- a/crates/ra_hir_ty/src/tests/patterns.rs +++ b/crates/ra_hir_ty/src/tests/patterns.rs @@ -1,5 +1,5 @@ use insta::assert_snapshot; -use test_utils::covers; +use test_utils::mark; use super::{infer, infer_with_mismatches}; @@ -197,7 +197,7 @@ fn test() { #[test] fn infer_pattern_match_ergonomics_ref() { - covers!(match_ergonomics_ref); + mark::check!(match_ergonomics_ref); assert_snapshot!( infer(r#" fn test() { diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs index c2168222e..1f004bd63 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs @@ -1,9 +1,10 @@ use insta::assert_snapshot; -use test_utils::covers; +use ra_db::fixture::WithFixture; +use test_utils::mark; -use super::infer; use crate::test_db::TestDB; -use ra_db::fixture::WithFixture; + +use super::infer; #[test] fn bug_484() { @@ -89,8 +90,8 @@ fn quux() { #[test] fn recursive_vars() { - covers!(type_var_cycles_resolve_completely); - covers!(type_var_cycles_resolve_as_possible); + mark::check!(type_var_cycles_resolve_completely); + mark::check!(type_var_cycles_resolve_as_possible); assert_snapshot!( infer(r#" fn test() { @@ -112,8 +113,6 @@ fn test() { #[test] fn recursive_vars_2() { - covers!(type_var_cycles_resolve_completely); - covers!(type_var_cycles_resolve_as_possible); assert_snapshot!( infer(r#" fn test() { @@ -170,7 +169,7 @@ fn write() { #[test] fn infer_std_crash_2() { - covers!(type_var_resolves_to_int_var); + mark::check!(type_var_resolves_to_int_var); // caused "equating two type variables, ...", taken from std assert_snapshot!( infer(r#" diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index c49aacf98..34f4b9039 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs @@ -1,10 +1,11 @@ use insta::assert_snapshot; - use ra_db::fixture::WithFixture; +use test_utils::mark; -use super::{infer, infer_with_mismatches, type_at, type_at_pos}; use crate::test_db::TestDB; +use super::{infer, infer_with_mismatches, type_at, type_at_pos}; + #[test] fn infer_await() { let (db, pos) = TestDB::with_position( @@ -301,7 +302,7 @@ fn test() { #[test] fn trait_default_method_self_bound_implements_trait() { - test_utils::covers!(trait_self_implements_self); + mark::check!(trait_self_implements_self); assert_snapshot!( infer(r#" trait Trait { @@ -324,7 +325,6 @@ trait Trait { #[test] fn trait_default_method_self_bound_implements_super_trait() { - test_utils::covers!(trait_self_implements_self); assert_snapshot!( infer(r#" trait SuperTrait { -- cgit v1.2.3