diff options
Diffstat (limited to 'crates/ra_hir_ty')
31 files changed, 3131 insertions, 1098 deletions
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 04d3cd6a2..4b8dcdc07 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml | |||
@@ -11,7 +11,7 @@ doctest = false | |||
11 | itertools = "0.9.0" | 11 | itertools = "0.9.0" |
12 | arrayvec = "0.5.1" | 12 | arrayvec = "0.5.1" |
13 | smallvec = "1.2.0" | 13 | smallvec = "1.2.0" |
14 | ena = "0.13.1" | 14 | ena = "0.14.0" |
15 | log = "0.4.8" | 15 | log = "0.4.8" |
16 | rustc-hash = "1.1.0" | 16 | rustc-hash = "1.1.0" |
17 | 17 | ||
@@ -27,9 +27,8 @@ test_utils = { path = "../test_utils" } | |||
27 | 27 | ||
28 | scoped-tls = "1" | 28 | scoped-tls = "1" |
29 | 29 | ||
30 | chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } | 30 | chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" } |
31 | chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } | 31 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" } |
32 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" } | ||
33 | 32 | ||
34 | [dev-dependencies] | 33 | [dev-dependencies] |
35 | insta = "0.16.0" | 34 | insta = "0.16.0" |
diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs index 779e78574..3e6e1e333 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( | |||
573 | matrix: &Matrix, | 573 | matrix: &Matrix, |
574 | v: &PatStack, | 574 | v: &PatStack, |
575 | ) -> MatchCheckResult<Usefulness> { | 575 | ) -> MatchCheckResult<Usefulness> { |
576 | // Handle the special case of enums with no variants. In that case, no match | 576 | // Handle two special cases: |
577 | // arm is useful. | 577 | // - enum with no variants |
578 | if let Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(AdtId::EnumId(enum_id)), .. }) = | 578 | // - `!` type |
579 | cx.infer[cx.match_expr].strip_references() | 579 | // In those cases, no match arm is useful. |
580 | { | 580 | match cx.infer[cx.match_expr].strip_references() { |
581 | if cx.db.enum_data(*enum_id).variants.is_empty() { | 581 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(AdtId::EnumId(enum_id)), .. }) => { |
582 | if cx.db.enum_data(*enum_id).variants.is_empty() { | ||
583 | return Ok(Usefulness::NotUseful); | ||
584 | } | ||
585 | } | ||
586 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) => { | ||
582 | return Ok(Usefulness::NotUseful); | 587 | return Ok(Usefulness::NotUseful); |
583 | } | 588 | } |
589 | _ => (), | ||
584 | } | 590 | } |
585 | 591 | ||
586 | if v.is_empty() { | 592 | if v.is_empty() { |
@@ -1918,6 +1924,17 @@ mod tests { | |||
1918 | } | 1924 | } |
1919 | 1925 | ||
1920 | #[test] | 1926 | #[test] |
1927 | fn type_never() { | ||
1928 | let content = r" | ||
1929 | fn test_fn(never: !) { | ||
1930 | match never {} | ||
1931 | } | ||
1932 | "; | ||
1933 | |||
1934 | check_no_diagnostic(content); | ||
1935 | } | ||
1936 | |||
1937 | #[test] | ||
1921 | fn enum_never_ref() { | 1938 | fn enum_never_ref() { |
1922 | let content = r" | 1939 | let content = r" |
1923 | enum Never {} | 1940 | enum Never {} |
@@ -1929,6 +1946,23 @@ mod tests { | |||
1929 | 1946 | ||
1930 | check_no_diagnostic(content); | 1947 | check_no_diagnostic(content); |
1931 | } | 1948 | } |
1949 | |||
1950 | #[test] | ||
1951 | fn expr_diverges_missing_arm() { | ||
1952 | let content = r" | ||
1953 | enum Either { | ||
1954 | A, | ||
1955 | B, | ||
1956 | } | ||
1957 | fn test_fn() { | ||
1958 | match loop {} { | ||
1959 | Either::A => (), | ||
1960 | } | ||
1961 | } | ||
1962 | "; | ||
1963 | |||
1964 | check_no_diagnostic(content); | ||
1965 | } | ||
1932 | } | 1966 | } |
1933 | 1967 | ||
1934 | #[cfg(test)] | 1968 | #[cfg(test)] |
@@ -1981,26 +2015,6 @@ mod false_negatives { | |||
1981 | } | 2015 | } |
1982 | 2016 | ||
1983 | #[test] | 2017 | #[test] |
1984 | fn expr_diverges_missing_arm() { | ||
1985 | let content = r" | ||
1986 | enum Either { | ||
1987 | A, | ||
1988 | B, | ||
1989 | } | ||
1990 | fn test_fn() { | ||
1991 | match loop {} { | ||
1992 | Either::A => (), | ||
1993 | } | ||
1994 | } | ||
1995 | "; | ||
1996 | |||
1997 | // This is a false negative. | ||
1998 | // Even though the match expression diverges, rustc fails | ||
1999 | // to compile here since `Either::B` is missing. | ||
2000 | check_no_diagnostic(content); | ||
2001 | } | ||
2002 | |||
2003 | #[test] | ||
2004 | fn expr_loop_missing_arm() { | 2018 | fn expr_loop_missing_arm() { |
2005 | let content = r" | 2019 | let content = r" |
2006 | enum Either { | 2020 | enum Either { |
@@ -2018,7 +2032,7 @@ mod false_negatives { | |||
2018 | // We currently infer the type of `loop { break Foo::A }` to `!`, which | 2032 | // We currently infer the type of `loop { break Foo::A }` to `!`, which |
2019 | // causes us to skip the diagnostic since `Either::A` doesn't type check | 2033 | // causes us to skip the diagnostic since `Either::A` doesn't type check |
2020 | // with `!`. | 2034 | // with `!`. |
2021 | check_no_diagnostic(content); | 2035 | check_diagnostic(content); |
2022 | } | 2036 | } |
2023 | 2037 | ||
2024 | #[test] | 2038 | #[test] |
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs index fdb49560b..0a8bb24ac 100644 --- a/crates/ra_hir_ty/src/db.rs +++ b/crates/ra_hir_ty/src/db.rs | |||
@@ -76,6 +76,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
76 | #[salsa::interned] | 76 | #[salsa::interned] |
77 | fn intern_type_ctor(&self, type_ctor: TypeCtor) -> crate::TypeCtorId; | 77 | fn intern_type_ctor(&self, type_ctor: TypeCtor) -> crate::TypeCtorId; |
78 | #[salsa::interned] | 78 | #[salsa::interned] |
79 | fn intern_callable_def(&self, callable_def: CallableDef) -> crate::CallableDefId; | ||
80 | #[salsa::interned] | ||
79 | fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; | 81 | fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId; |
80 | #[salsa::interned] | 82 | #[salsa::interned] |
81 | fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; | 83 | fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId; |
@@ -89,11 +91,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
89 | fn trait_datum(&self, krate: CrateId, trait_id: chalk::TraitId) -> Arc<chalk::TraitDatum>; | 91 | fn trait_datum(&self, krate: CrateId, trait_id: chalk::TraitId) -> Arc<chalk::TraitDatum>; |
90 | 92 | ||
91 | #[salsa::invoke(chalk::struct_datum_query)] | 93 | #[salsa::invoke(chalk::struct_datum_query)] |
92 | fn struct_datum(&self, krate: CrateId, struct_id: chalk::StructId) -> Arc<chalk::StructDatum>; | 94 | fn struct_datum(&self, krate: CrateId, struct_id: chalk::AdtId) -> Arc<chalk::StructDatum>; |
93 | 95 | ||
94 | #[salsa::invoke(crate::traits::chalk::impl_datum_query)] | 96 | #[salsa::invoke(crate::traits::chalk::impl_datum_query)] |
95 | fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>; | 97 | fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc<chalk::ImplDatum>; |
96 | 98 | ||
99 | #[salsa::invoke(crate::traits::chalk::fn_def_datum_query)] | ||
100 | fn fn_def_datum(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> Arc<chalk::FnDefDatum>; | ||
101 | |||
97 | #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)] | 102 | #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)] |
98 | fn associated_ty_value( | 103 | fn associated_ty_value( |
99 | &self, | 104 | &self, |
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index c8fd54861..2c7298714 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs | |||
@@ -40,7 +40,7 @@ impl Diagnostic for MissingFields { | |||
40 | fn message(&self) -> String { | 40 | fn message(&self) -> String { |
41 | let mut buf = String::from("Missing structure fields:\n"); | 41 | let mut buf = String::from("Missing structure fields:\n"); |
42 | for field in &self.missed_fields { | 42 | for field in &self.missed_fields { |
43 | format_to!(buf, "- {}", field); | 43 | format_to!(buf, "- {}\n", field); |
44 | } | 44 | } |
45 | buf | 45 | buf |
46 | } | 46 | } |
@@ -73,7 +73,7 @@ impl Diagnostic for MissingPatFields { | |||
73 | fn message(&self) -> String { | 73 | fn message(&self) -> String { |
74 | let mut buf = String::from("Missing structure fields:\n"); | 74 | let mut buf = String::from("Missing structure fields:\n"); |
75 | for field in &self.missed_fields { | 75 | for field in &self.missed_fields { |
76 | format_to!(buf, "- {}", field); | 76 | format_to!(buf, "- {}\n", field); |
77 | } | 77 | } |
78 | buf | 78 | buf |
79 | } | 79 | } |
@@ -131,3 +131,31 @@ impl AstDiagnostic for MissingOkInTailExpr { | |||
131 | ast::Expr::cast(node).unwrap() | 131 | ast::Expr::cast(node).unwrap() |
132 | } | 132 | } |
133 | } | 133 | } |
134 | |||
135 | #[derive(Debug)] | ||
136 | pub struct BreakOutsideOfLoop { | ||
137 | pub file: HirFileId, | ||
138 | pub expr: AstPtr<ast::Expr>, | ||
139 | } | ||
140 | |||
141 | impl Diagnostic for BreakOutsideOfLoop { | ||
142 | fn message(&self) -> String { | ||
143 | "break outside of loop".to_string() | ||
144 | } | ||
145 | fn source(&self) -> InFile<SyntaxNodePtr> { | ||
146 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
147 | } | ||
148 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
149 | self | ||
150 | } | ||
151 | } | ||
152 | |||
153 | impl AstDiagnostic for BreakOutsideOfLoop { | ||
154 | type AST = ast::Expr; | ||
155 | |||
156 | fn ast(&self, db: &impl AstDatabase) -> Self::AST { | ||
157 | let root = db.parse_or_expand(self.file).unwrap(); | ||
158 | let node = self.source().value.to_node(&root); | ||
159 | ast::Expr::cast(node).unwrap() | ||
160 | } | ||
161 | } | ||
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index d03bbd5a7..b9c4d2e89 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs | |||
@@ -6,28 +6,42 @@ use crate::{ | |||
6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, | 6 | db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, |
7 | Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, | 7 | Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, |
8 | }; | 8 | }; |
9 | use hir_def::{generics::TypeParamProvenance, AdtId, AssocContainerId, Lookup}; | 9 | use hir_def::{ |
10 | find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, | ||
11 | Lookup, ModuleId, | ||
12 | }; | ||
10 | use hir_expand::name::Name; | 13 | use hir_expand::name::Name; |
11 | 14 | ||
12 | pub struct HirFormatter<'a, 'b> { | 15 | pub struct HirFormatter<'a> { |
13 | pub db: &'a dyn HirDatabase, | 16 | pub db: &'a dyn HirDatabase, |
14 | fmt: &'a mut fmt::Formatter<'b>, | 17 | fmt: &'a mut dyn fmt::Write, |
15 | buf: String, | 18 | buf: String, |
16 | curr_size: usize, | 19 | curr_size: usize, |
17 | pub(crate) max_size: Option<usize>, | 20 | pub(crate) max_size: Option<usize>, |
18 | omit_verbose_types: bool, | 21 | omit_verbose_types: bool, |
22 | display_target: DisplayTarget, | ||
19 | } | 23 | } |
20 | 24 | ||
21 | pub trait HirDisplay { | 25 | pub trait HirDisplay { |
22 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result; | 26 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError>; |
23 | 27 | ||
28 | /// Returns a `Display`able type that is human-readable. | ||
29 | /// Use this for showing types to the user (e.g. diagnostics) | ||
24 | fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self> | 30 | fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self> |
25 | where | 31 | where |
26 | Self: Sized, | 32 | Self: Sized, |
27 | { | 33 | { |
28 | HirDisplayWrapper(db, self, None, false) | 34 | HirDisplayWrapper { |
35 | db, | ||
36 | t: self, | ||
37 | max_size: None, | ||
38 | omit_verbose_types: false, | ||
39 | display_target: DisplayTarget::Diagnostics, | ||
40 | } | ||
29 | } | 41 | } |
30 | 42 | ||
43 | /// Returns a `Display`able type that is human-readable and tries to be succinct. | ||
44 | /// Use this for showing types to the user where space is constrained (e.g. doc popups) | ||
31 | fn display_truncated<'a>( | 45 | fn display_truncated<'a>( |
32 | &'a self, | 46 | &'a self, |
33 | db: &'a dyn HirDatabase, | 47 | db: &'a dyn HirDatabase, |
@@ -36,16 +50,46 @@ pub trait HirDisplay { | |||
36 | where | 50 | where |
37 | Self: Sized, | 51 | Self: Sized, |
38 | { | 52 | { |
39 | HirDisplayWrapper(db, self, max_size, true) | 53 | HirDisplayWrapper { |
54 | db, | ||
55 | t: self, | ||
56 | max_size, | ||
57 | omit_verbose_types: true, | ||
58 | display_target: DisplayTarget::Diagnostics, | ||
59 | } | ||
60 | } | ||
61 | |||
62 | /// Returns a String representation of `self` that can be inserted into the given module. | ||
63 | /// Use this when generating code (e.g. assists) | ||
64 | fn display_source_code<'a>( | ||
65 | &'a self, | ||
66 | db: &'a dyn HirDatabase, | ||
67 | module_id: ModuleId, | ||
68 | ) -> Result<String, DisplaySourceCodeError> { | ||
69 | let mut result = String::new(); | ||
70 | match self.hir_fmt(&mut HirFormatter { | ||
71 | db, | ||
72 | fmt: &mut result, | ||
73 | buf: String::with_capacity(20), | ||
74 | curr_size: 0, | ||
75 | max_size: None, | ||
76 | omit_verbose_types: false, | ||
77 | display_target: DisplayTarget::SourceCode { module_id }, | ||
78 | }) { | ||
79 | Ok(()) => {} | ||
80 | Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"), | ||
81 | Err(HirDisplayError::DisplaySourceCodeError(e)) => return Err(e), | ||
82 | }; | ||
83 | Ok(result) | ||
40 | } | 84 | } |
41 | } | 85 | } |
42 | 86 | ||
43 | impl<'a, 'b> HirFormatter<'a, 'b> { | 87 | impl<'a> HirFormatter<'a> { |
44 | pub fn write_joined<T: HirDisplay>( | 88 | pub fn write_joined<T: HirDisplay>( |
45 | &mut self, | 89 | &mut self, |
46 | iter: impl IntoIterator<Item = T>, | 90 | iter: impl IntoIterator<Item = T>, |
47 | sep: &str, | 91 | sep: &str, |
48 | ) -> fmt::Result { | 92 | ) -> Result<(), HirDisplayError> { |
49 | let mut first = true; | 93 | let mut first = true; |
50 | for e in iter { | 94 | for e in iter { |
51 | if !first { | 95 | if !first { |
@@ -58,14 +102,14 @@ impl<'a, 'b> HirFormatter<'a, 'b> { | |||
58 | } | 102 | } |
59 | 103 | ||
60 | /// This allows using the `write!` macro directly with a `HirFormatter`. | 104 | /// This allows using the `write!` macro directly with a `HirFormatter`. |
61 | pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result { | 105 | pub fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), HirDisplayError> { |
62 | // We write to a buffer first to track output size | 106 | // We write to a buffer first to track output size |
63 | self.buf.clear(); | 107 | self.buf.clear(); |
64 | fmt::write(&mut self.buf, args)?; | 108 | fmt::write(&mut self.buf, args)?; |
65 | self.curr_size += self.buf.len(); | 109 | self.curr_size += self.buf.len(); |
66 | 110 | ||
67 | // Then we write to the internal formatter from the buffer | 111 | // Then we write to the internal formatter from the buffer |
68 | self.fmt.write_str(&self.buf) | 112 | self.fmt.write_str(&self.buf).map_err(HirDisplayError::from) |
69 | } | 113 | } |
70 | 114 | ||
71 | pub fn should_truncate(&self) -> bool { | 115 | pub fn should_truncate(&self) -> bool { |
@@ -81,34 +125,82 @@ impl<'a, 'b> HirFormatter<'a, 'b> { | |||
81 | } | 125 | } |
82 | } | 126 | } |
83 | 127 | ||
84 | pub struct HirDisplayWrapper<'a, T>(&'a dyn HirDatabase, &'a T, Option<usize>, bool); | 128 | #[derive(Clone, Copy)] |
129 | enum DisplayTarget { | ||
130 | /// Display types for inlays, doc popups, autocompletion, etc... | ||
131 | /// Showing `{unknown}` or not qualifying paths is fine here. | ||
132 | /// There's no reason for this to fail. | ||
133 | Diagnostics, | ||
134 | /// Display types for inserting them in source files. | ||
135 | /// The generated code should compile, so paths need to be qualified. | ||
136 | SourceCode { module_id: ModuleId }, | ||
137 | } | ||
138 | |||
139 | impl DisplayTarget { | ||
140 | fn is_source_code(&self) -> bool { | ||
141 | matches!(self, Self::SourceCode {..}) | ||
142 | } | ||
143 | } | ||
144 | |||
145 | #[derive(Debug)] | ||
146 | pub enum DisplaySourceCodeError { | ||
147 | PathNotFound, | ||
148 | } | ||
149 | |||
150 | pub enum HirDisplayError { | ||
151 | /// Errors that can occur when generating source code | ||
152 | DisplaySourceCodeError(DisplaySourceCodeError), | ||
153 | /// `FmtError` is required to be compatible with std::fmt::Display | ||
154 | FmtError, | ||
155 | } | ||
156 | impl From<fmt::Error> for HirDisplayError { | ||
157 | fn from(_: fmt::Error) -> Self { | ||
158 | Self::FmtError | ||
159 | } | ||
160 | } | ||
161 | |||
162 | pub struct HirDisplayWrapper<'a, T> { | ||
163 | db: &'a dyn HirDatabase, | ||
164 | t: &'a T, | ||
165 | max_size: Option<usize>, | ||
166 | omit_verbose_types: bool, | ||
167 | display_target: DisplayTarget, | ||
168 | } | ||
85 | 169 | ||
86 | impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> | 170 | impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> |
87 | where | 171 | where |
88 | T: HirDisplay, | 172 | T: HirDisplay, |
89 | { | 173 | { |
90 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 174 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
91 | self.1.hir_fmt(&mut HirFormatter { | 175 | match self.t.hir_fmt(&mut HirFormatter { |
92 | db: self.0, | 176 | db: self.db, |
93 | fmt: f, | 177 | fmt: f, |
94 | buf: String::with_capacity(20), | 178 | buf: String::with_capacity(20), |
95 | curr_size: 0, | 179 | curr_size: 0, |
96 | max_size: self.2, | 180 | max_size: self.max_size, |
97 | omit_verbose_types: self.3, | 181 | omit_verbose_types: self.omit_verbose_types, |
98 | }) | 182 | display_target: self.display_target, |
183 | }) { | ||
184 | Ok(()) => Ok(()), | ||
185 | Err(HirDisplayError::FmtError) => Err(fmt::Error), | ||
186 | Err(HirDisplayError::DisplaySourceCodeError(_)) => { | ||
187 | // This should never happen | ||
188 | panic!("HirDisplay failed when calling Display::fmt!") | ||
189 | } | ||
190 | } | ||
99 | } | 191 | } |
100 | } | 192 | } |
101 | 193 | ||
102 | const TYPE_HINT_TRUNCATION: &str = "…"; | 194 | const TYPE_HINT_TRUNCATION: &str = "…"; |
103 | 195 | ||
104 | impl HirDisplay for &Ty { | 196 | impl HirDisplay for &Ty { |
105 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 197 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
106 | HirDisplay::hir_fmt(*self, f) | 198 | HirDisplay::hir_fmt(*self, f) |
107 | } | 199 | } |
108 | } | 200 | } |
109 | 201 | ||
110 | impl HirDisplay for ApplicationTy { | 202 | impl HirDisplay for ApplicationTy { |
111 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 203 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
112 | if f.should_truncate() { | 204 | if f.should_truncate() { |
113 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 205 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
114 | } | 206 | } |
@@ -191,45 +283,66 @@ impl HirDisplay for ApplicationTy { | |||
191 | } | 283 | } |
192 | } | 284 | } |
193 | TypeCtor::Adt(def_id) => { | 285 | TypeCtor::Adt(def_id) => { |
194 | let name = match def_id { | 286 | match f.display_target { |
195 | AdtId::StructId(it) => f.db.struct_data(it).name.clone(), | 287 | DisplayTarget::Diagnostics => { |
196 | AdtId::UnionId(it) => f.db.union_data(it).name.clone(), | 288 | let name = match def_id { |
197 | AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), | 289 | AdtId::StructId(it) => f.db.struct_data(it).name.clone(), |
198 | }; | 290 | AdtId::UnionId(it) => f.db.union_data(it).name.clone(), |
199 | write!(f, "{}", name)?; | 291 | AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), |
292 | }; | ||
293 | write!(f, "{}", name)?; | ||
294 | } | ||
295 | DisplayTarget::SourceCode { module_id } => { | ||
296 | if let Some(path) = find_path::find_path( | ||
297 | f.db.upcast(), | ||
298 | ItemInNs::Types(def_id.into()), | ||
299 | module_id, | ||
300 | ) { | ||
301 | write!(f, "{}", path)?; | ||
302 | } else { | ||
303 | return Err(HirDisplayError::DisplaySourceCodeError( | ||
304 | DisplaySourceCodeError::PathNotFound, | ||
305 | )); | ||
306 | } | ||
307 | } | ||
308 | } | ||
309 | |||
200 | if self.parameters.len() > 0 { | 310 | if self.parameters.len() > 0 { |
201 | let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); | 311 | let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); |
202 | let parameters_to_write = if f.omit_verbose_types() { | 312 | let parameters_to_write = |
203 | match self | 313 | if f.display_target.is_source_code() || f.omit_verbose_types() { |
204 | .ctor | 314 | match self |
205 | .as_generic_def() | 315 | .ctor |
206 | .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) | 316 | .as_generic_def() |
207 | .filter(|defaults| !defaults.is_empty()) | 317 | .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) |
208 | { | 318 | .filter(|defaults| !defaults.is_empty()) |
209 | None => self.parameters.0.as_ref(), | 319 | { |
210 | Some(default_parameters) => { | 320 | None => self.parameters.0.as_ref(), |
211 | for (i, parameter) in self.parameters.iter().enumerate() { | 321 | Some(default_parameters) => { |
212 | match (parameter, default_parameters.get(i)) { | 322 | for (i, parameter) in self.parameters.iter().enumerate() { |
213 | (&Ty::Unknown, _) | (_, None) => { | 323 | match (parameter, default_parameters.get(i)) { |
214 | non_default_parameters.push(parameter.clone()) | 324 | (&Ty::Unknown, _) | (_, None) => { |
325 | non_default_parameters.push(parameter.clone()) | ||
326 | } | ||
327 | (_, Some(default_parameter)) | ||
328 | if parameter != default_parameter => | ||
329 | { | ||
330 | non_default_parameters.push(parameter.clone()) | ||
331 | } | ||
332 | _ => (), | ||
215 | } | 333 | } |
216 | (_, Some(default_parameter)) | ||
217 | if parameter != default_parameter => | ||
218 | { | ||
219 | non_default_parameters.push(parameter.clone()) | ||
220 | } | ||
221 | _ => (), | ||
222 | } | 334 | } |
335 | &non_default_parameters | ||
223 | } | 336 | } |
224 | &non_default_parameters | ||
225 | } | 337 | } |
226 | } | 338 | } else { |
227 | } else { | 339 | self.parameters.0.as_ref() |
228 | self.parameters.0.as_ref() | 340 | }; |
229 | }; | 341 | if !parameters_to_write.is_empty() { |
230 | write!(f, "<")?; | 342 | write!(f, "<")?; |
231 | f.write_joined(parameters_to_write, ", ")?; | 343 | f.write_joined(parameters_to_write, ", ")?; |
232 | write!(f, ">")?; | 344 | write!(f, ">")?; |
345 | } | ||
233 | } | 346 | } |
234 | } | 347 | } |
235 | TypeCtor::AssociatedType(type_alias) => { | 348 | TypeCtor::AssociatedType(type_alias) => { |
@@ -269,7 +382,7 @@ impl HirDisplay for ApplicationTy { | |||
269 | } | 382 | } |
270 | 383 | ||
271 | impl HirDisplay for ProjectionTy { | 384 | impl HirDisplay for ProjectionTy { |
272 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 385 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
273 | if f.should_truncate() { | 386 | if f.should_truncate() { |
274 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 387 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
275 | } | 388 | } |
@@ -287,7 +400,7 @@ impl HirDisplay for ProjectionTy { | |||
287 | } | 400 | } |
288 | 401 | ||
289 | impl HirDisplay for Ty { | 402 | impl HirDisplay for Ty { |
290 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 403 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
291 | if f.should_truncate() { | 404 | if f.should_truncate() { |
292 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 405 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
293 | } | 406 | } |
@@ -332,7 +445,7 @@ impl HirDisplay for Ty { | |||
332 | fn write_bounds_like_dyn_trait( | 445 | fn write_bounds_like_dyn_trait( |
333 | predicates: &[GenericPredicate], | 446 | predicates: &[GenericPredicate], |
334 | f: &mut HirFormatter, | 447 | f: &mut HirFormatter, |
335 | ) -> fmt::Result { | 448 | ) -> Result<(), HirDisplayError> { |
336 | // Note: This code is written to produce nice results (i.e. | 449 | // Note: This code is written to produce nice results (i.e. |
337 | // corresponding to surface Rust) for types that can occur in | 450 | // corresponding to surface Rust) for types that can occur in |
338 | // actual Rust. It will have weird results if the predicates | 451 | // actual Rust. It will have weird results if the predicates |
@@ -394,7 +507,7 @@ fn write_bounds_like_dyn_trait( | |||
394 | } | 507 | } |
395 | 508 | ||
396 | impl TraitRef { | 509 | impl TraitRef { |
397 | fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> fmt::Result { | 510 | fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> { |
398 | if f.should_truncate() { | 511 | if f.should_truncate() { |
399 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 512 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
400 | } | 513 | } |
@@ -416,19 +529,19 @@ impl TraitRef { | |||
416 | } | 529 | } |
417 | 530 | ||
418 | impl HirDisplay for TraitRef { | 531 | impl HirDisplay for TraitRef { |
419 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 532 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
420 | self.hir_fmt_ext(f, false) | 533 | self.hir_fmt_ext(f, false) |
421 | } | 534 | } |
422 | } | 535 | } |
423 | 536 | ||
424 | impl HirDisplay for &GenericPredicate { | 537 | impl HirDisplay for &GenericPredicate { |
425 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 538 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
426 | HirDisplay::hir_fmt(*self, f) | 539 | HirDisplay::hir_fmt(*self, f) |
427 | } | 540 | } |
428 | } | 541 | } |
429 | 542 | ||
430 | impl HirDisplay for GenericPredicate { | 543 | impl HirDisplay for GenericPredicate { |
431 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 544 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
432 | if f.should_truncate() { | 545 | if f.should_truncate() { |
433 | return write!(f, "{}", TYPE_HINT_TRUNCATION); | 546 | return write!(f, "{}", TYPE_HINT_TRUNCATION); |
434 | } | 547 | } |
@@ -452,15 +565,15 @@ impl HirDisplay for GenericPredicate { | |||
452 | } | 565 | } |
453 | 566 | ||
454 | impl HirDisplay for Obligation { | 567 | impl HirDisplay for Obligation { |
455 | fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { | 568 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { |
456 | match self { | 569 | Ok(match self { |
457 | Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), | 570 | Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db))?, |
458 | Obligation::Projection(proj) => write!( | 571 | Obligation::Projection(proj) => write!( |
459 | f, | 572 | f, |
460 | "Normalize({} => {})", | 573 | "Normalize({} => {})", |
461 | proj.projection_ty.display(f.db), | 574 | proj.projection_ty.display(f.db), |
462 | proj.ty.display(f.db) | 575 | proj.ty.display(f.db) |
463 | ), | 576 | )?, |
464 | } | 577 | }) |
465 | } | 578 | } |
466 | } | 579 | } |
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index bd4ef69a0..dc77e88e5 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs | |||
@@ -22,13 +22,14 @@ use rustc_hash::FxHashMap; | |||
22 | 22 | ||
23 | use hir_def::{ | 23 | use hir_def::{ |
24 | body::Body, | 24 | body::Body, |
25 | data::{ConstData, FunctionData}, | 25 | data::{ConstData, FunctionData, StaticData}, |
26 | expr::{BindingAnnotation, ExprId, PatId}, | 26 | expr::{BindingAnnotation, ExprId, PatId}, |
27 | lang_item::LangItemTarget, | 27 | lang_item::LangItemTarget, |
28 | path::{path, Path}, | 28 | path::{path, Path}, |
29 | resolver::{HasResolver, Resolver, TypeNs}, | 29 | resolver::{HasResolver, Resolver, TypeNs}, |
30 | type_ref::{Mutability, TypeRef}, | 30 | type_ref::{Mutability, TypeRef}, |
31 | AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, TraitId, TypeAliasId, VariantId, | 31 | AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, TraitId, TypeAliasId, |
32 | VariantId, | ||
32 | }; | 33 | }; |
33 | use hir_expand::{diagnostics::DiagnosticSink, name::name}; | 34 | use hir_expand::{diagnostics::DiagnosticSink, name::name}; |
34 | use ra_arena::map::ArenaMap; | 35 | use ra_arena::map::ArenaMap; |
@@ -71,7 +72,7 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer | |||
71 | match def { | 72 | match def { |
72 | DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)), | 73 | DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)), |
73 | DefWithBodyId::FunctionId(f) => ctx.collect_fn(&db.function_data(f)), | 74 | DefWithBodyId::FunctionId(f) => ctx.collect_fn(&db.function_data(f)), |
74 | DefWithBodyId::StaticId(s) => ctx.collect_const(&db.static_data(s)), | 75 | DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)), |
75 | } | 76 | } |
76 | 77 | ||
77 | ctx.infer_body(); | 78 | ctx.infer_body(); |
@@ -210,6 +211,25 @@ struct InferenceContext<'a> { | |||
210 | /// closures, but currently this is the only field that will change there, | 211 | /// closures, but currently this is the only field that will change there, |
211 | /// so it doesn't make sense. | 212 | /// so it doesn't make sense. |
212 | return_ty: Ty, | 213 | return_ty: Ty, |
214 | diverges: Diverges, | ||
215 | breakables: Vec<BreakableContext>, | ||
216 | } | ||
217 | |||
218 | #[derive(Clone, Debug)] | ||
219 | struct BreakableContext { | ||
220 | pub may_break: bool, | ||
221 | pub break_ty: Ty, | ||
222 | pub label: Option<name::Name>, | ||
223 | } | ||
224 | |||
225 | fn find_breakable<'c>( | ||
226 | ctxs: &'c mut [BreakableContext], | ||
227 | label: Option<&name::Name>, | ||
228 | ) -> Option<&'c mut BreakableContext> { | ||
229 | match label { | ||
230 | Some(_) => ctxs.iter_mut().rev().find(|ctx| ctx.label.as_ref() == label), | ||
231 | None => ctxs.last_mut(), | ||
232 | } | ||
213 | } | 233 | } |
214 | 234 | ||
215 | impl<'a> InferenceContext<'a> { | 235 | impl<'a> InferenceContext<'a> { |
@@ -224,6 +244,8 @@ impl<'a> InferenceContext<'a> { | |||
224 | owner, | 244 | owner, |
225 | body: db.body(owner), | 245 | body: db.body(owner), |
226 | resolver, | 246 | resolver, |
247 | diverges: Diverges::Maybe, | ||
248 | breakables: Vec::new(), | ||
227 | } | 249 | } |
228 | } | 250 | } |
229 | 251 | ||
@@ -429,43 +451,95 @@ impl<'a> InferenceContext<'a> { | |||
429 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); | 451 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); |
430 | // FIXME: this should resolve assoc items as well, see this example: | 452 | // FIXME: this should resolve assoc items as well, see this example: |
431 | // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 | 453 | // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 |
432 | return match resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path()) { | 454 | let (resolution, unresolved) = |
433 | Some(TypeNs::AdtId(AdtId::StructId(strukt))) => { | 455 | match resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { |
456 | Some(it) => it, | ||
457 | None => return (Ty::Unknown, None), | ||
458 | }; | ||
459 | return match resolution { | ||
460 | TypeNs::AdtId(AdtId::StructId(strukt)) => { | ||
434 | let substs = Ty::substs_from_path(&ctx, path, strukt.into()); | 461 | let substs = Ty::substs_from_path(&ctx, path, strukt.into()); |
435 | let ty = self.db.ty(strukt.into()); | 462 | let ty = self.db.ty(strukt.into()); |
436 | let ty = self.insert_type_vars(ty.subst(&substs)); | 463 | let ty = self.insert_type_vars(ty.subst(&substs)); |
437 | (ty, Some(strukt.into())) | 464 | forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) |
438 | } | 465 | } |
439 | Some(TypeNs::EnumVariantId(var)) => { | 466 | TypeNs::EnumVariantId(var) => { |
440 | let substs = Ty::substs_from_path(&ctx, path, var.into()); | 467 | let substs = Ty::substs_from_path(&ctx, path, var.into()); |
441 | let ty = self.db.ty(var.parent.into()); | 468 | let ty = self.db.ty(var.parent.into()); |
442 | let ty = self.insert_type_vars(ty.subst(&substs)); | 469 | let ty = self.insert_type_vars(ty.subst(&substs)); |
443 | (ty, Some(var.into())) | 470 | forbid_unresolved_segments((ty, Some(var.into())), unresolved) |
444 | } | 471 | } |
445 | Some(TypeNs::SelfType(impl_id)) => { | 472 | TypeNs::SelfType(impl_id) => { |
446 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); | 473 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); |
447 | let substs = Substs::type_params_for_generics(&generics); | 474 | let substs = Substs::type_params_for_generics(&generics); |
448 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); | 475 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); |
449 | let variant = ty_variant(&ty); | 476 | match unresolved { |
450 | (ty, variant) | 477 | None => { |
478 | let variant = ty_variant(&ty); | ||
479 | (ty, variant) | ||
480 | } | ||
481 | Some(1) => { | ||
482 | let segment = path.mod_path().segments.last().unwrap(); | ||
483 | // this could be an enum variant or associated type | ||
484 | if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() { | ||
485 | let enum_data = self.db.enum_data(enum_id); | ||
486 | if let Some(local_id) = enum_data.variant(segment) { | ||
487 | let variant = EnumVariantId { parent: enum_id, local_id }; | ||
488 | return (ty, Some(variant.into())); | ||
489 | } | ||
490 | } | ||
491 | // FIXME potentially resolve assoc type | ||
492 | (Ty::Unknown, None) | ||
493 | } | ||
494 | Some(_) => { | ||
495 | // FIXME diagnostic | ||
496 | (Ty::Unknown, None) | ||
497 | } | ||
498 | } | ||
451 | } | 499 | } |
452 | Some(TypeNs::TypeAliasId(it)) => { | 500 | TypeNs::TypeAliasId(it) => { |
453 | let substs = Substs::build_for_def(self.db, it) | 501 | let substs = Substs::build_for_def(self.db, it) |
454 | .fill(std::iter::repeat_with(|| self.table.new_type_var())) | 502 | .fill(std::iter::repeat_with(|| self.table.new_type_var())) |
455 | .build(); | 503 | .build(); |
456 | let ty = self.db.ty(it.into()).subst(&substs); | 504 | let ty = self.db.ty(it.into()).subst(&substs); |
457 | let variant = ty_variant(&ty); | 505 | let variant = ty_variant(&ty); |
458 | (ty, variant) | 506 | forbid_unresolved_segments((ty, variant), unresolved) |
507 | } | ||
508 | TypeNs::AdtSelfType(_) => { | ||
509 | // FIXME this could happen in array size expressions, once we're checking them | ||
510 | (Ty::Unknown, None) | ||
511 | } | ||
512 | TypeNs::GenericParam(_) => { | ||
513 | // FIXME potentially resolve assoc type | ||
514 | (Ty::Unknown, None) | ||
515 | } | ||
516 | TypeNs::AdtId(AdtId::EnumId(_)) | ||
517 | | TypeNs::AdtId(AdtId::UnionId(_)) | ||
518 | | TypeNs::BuiltinType(_) | ||
519 | | TypeNs::TraitId(_) => { | ||
520 | // FIXME diagnostic | ||
521 | (Ty::Unknown, None) | ||
459 | } | 522 | } |
460 | Some(_) | None => (Ty::Unknown, None), | ||
461 | }; | 523 | }; |
462 | 524 | ||
525 | fn forbid_unresolved_segments( | ||
526 | result: (Ty, Option<VariantId>), | ||
527 | unresolved: Option<usize>, | ||
528 | ) -> (Ty, Option<VariantId>) { | ||
529 | if unresolved.is_none() { | ||
530 | result | ||
531 | } else { | ||
532 | // FIXME diagnostic | ||
533 | (Ty::Unknown, None) | ||
534 | } | ||
535 | } | ||
536 | |||
463 | fn ty_variant(ty: &Ty) -> Option<VariantId> { | 537 | fn ty_variant(ty: &Ty) -> Option<VariantId> { |
464 | ty.as_adt().and_then(|(adt_id, _)| match adt_id { | 538 | ty.as_adt().and_then(|(adt_id, _)| match adt_id { |
465 | AdtId::StructId(s) => Some(VariantId::StructId(s)), | 539 | AdtId::StructId(s) => Some(VariantId::StructId(s)), |
466 | AdtId::UnionId(u) => Some(VariantId::UnionId(u)), | 540 | AdtId::UnionId(u) => Some(VariantId::UnionId(u)), |
467 | AdtId::EnumId(_) => { | 541 | AdtId::EnumId(_) => { |
468 | // Error E0071, expected struct, variant or union type, found enum `Foo` | 542 | // FIXME Error E0071, expected struct, variant or union type, found enum `Foo` |
469 | None | 543 | None |
470 | } | 544 | } |
471 | }) | 545 | }) |
@@ -476,6 +550,10 @@ impl<'a> InferenceContext<'a> { | |||
476 | self.return_ty = self.make_ty(&data.type_ref); | 550 | self.return_ty = self.make_ty(&data.type_ref); |
477 | } | 551 | } |
478 | 552 | ||
553 | fn collect_static(&mut self, data: &StaticData) { | ||
554 | self.return_ty = self.make_ty(&data.type_ref); | ||
555 | } | ||
556 | |||
479 | fn collect_fn(&mut self, data: &FunctionData) { | 557 | fn collect_fn(&mut self, data: &FunctionData) { |
480 | let body = Arc::clone(&self.body); // avoid borrow checker problem | 558 | let body = Arc::clone(&self.body); // avoid borrow checker problem |
481 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) | 559 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) |
@@ -666,15 +744,57 @@ impl Expectation { | |||
666 | } | 744 | } |
667 | } | 745 | } |
668 | 746 | ||
747 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] | ||
748 | enum Diverges { | ||
749 | Maybe, | ||
750 | Always, | ||
751 | } | ||
752 | |||
753 | impl Diverges { | ||
754 | fn is_always(self) -> bool { | ||
755 | self == Diverges::Always | ||
756 | } | ||
757 | } | ||
758 | |||
759 | impl std::ops::BitAnd for Diverges { | ||
760 | type Output = Self; | ||
761 | fn bitand(self, other: Self) -> Self { | ||
762 | std::cmp::min(self, other) | ||
763 | } | ||
764 | } | ||
765 | |||
766 | impl std::ops::BitOr for Diverges { | ||
767 | type Output = Self; | ||
768 | fn bitor(self, other: Self) -> Self { | ||
769 | std::cmp::max(self, other) | ||
770 | } | ||
771 | } | ||
772 | |||
773 | impl std::ops::BitAndAssign for Diverges { | ||
774 | fn bitand_assign(&mut self, other: Self) { | ||
775 | *self = *self & other; | ||
776 | } | ||
777 | } | ||
778 | |||
779 | impl std::ops::BitOrAssign for Diverges { | ||
780 | fn bitor_assign(&mut self, other: Self) { | ||
781 | *self = *self | other; | ||
782 | } | ||
783 | } | ||
784 | |||
669 | mod diagnostics { | 785 | mod diagnostics { |
670 | use hir_def::{expr::ExprId, FunctionId}; | 786 | use hir_def::{expr::ExprId, FunctionId}; |
671 | use hir_expand::diagnostics::DiagnosticSink; | 787 | use hir_expand::diagnostics::DiagnosticSink; |
672 | 788 | ||
673 | use crate::{db::HirDatabase, diagnostics::NoSuchField}; | 789 | use crate::{ |
790 | db::HirDatabase, | ||
791 | diagnostics::{BreakOutsideOfLoop, NoSuchField}, | ||
792 | }; | ||
674 | 793 | ||
675 | #[derive(Debug, PartialEq, Eq, Clone)] | 794 | #[derive(Debug, PartialEq, Eq, Clone)] |
676 | pub(super) enum InferenceDiagnostic { | 795 | pub(super) enum InferenceDiagnostic { |
677 | NoSuchField { expr: ExprId, field: usize }, | 796 | NoSuchField { expr: ExprId, field: usize }, |
797 | BreakOutsideOfLoop { expr: ExprId }, | ||
678 | } | 798 | } |
679 | 799 | ||
680 | impl InferenceDiagnostic { | 800 | impl InferenceDiagnostic { |
@@ -690,6 +810,13 @@ mod diagnostics { | |||
690 | let field = source_map.field_syntax(*expr, *field); | 810 | let field = source_map.field_syntax(*expr, *field); |
691 | sink.push(NoSuchField { file: field.file_id, field: field.value }) | 811 | sink.push(NoSuchField { file: field.file_id, field: field.value }) |
692 | } | 812 | } |
813 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { | ||
814 | let (_, source_map) = db.body_with_source_map(owner.into()); | ||
815 | let ptr = source_map | ||
816 | .expr_syntax(*expr) | ||
817 | .expect("break outside of loop in synthetic syntax"); | ||
818 | sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) | ||
819 | } | ||
693 | } | 820 | } |
694 | } | 821 | } |
695 | } | 822 | } |
diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 89200255a..32c7c57cd 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs | |||
@@ -5,7 +5,7 @@ | |||
5 | //! See: https://doc.rust-lang.org/nomicon/coercions.html | 5 | //! See: https://doc.rust-lang.org/nomicon/coercions.html |
6 | 6 | ||
7 | use hir_def::{lang_item::LangItemTarget, type_ref::Mutability}; | 7 | use hir_def::{lang_item::LangItemTarget, type_ref::Mutability}; |
8 | use test_utils::tested_by; | 8 | use test_utils::mark; |
9 | 9 | ||
10 | use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor}; | 10 | use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty, TypeCtor}; |
11 | 11 | ||
@@ -20,21 +20,33 @@ impl<'a> InferenceContext<'a> { | |||
20 | self.coerce_inner(from_ty, &to_ty) | 20 | self.coerce_inner(from_ty, &to_ty) |
21 | } | 21 | } |
22 | 22 | ||
23 | /// Merge two types from different branches, with possible implicit coerce. | 23 | /// Merge two types from different branches, with possible coercion. |
24 | /// | 24 | /// |
25 | /// Note that it is only possible that one type are coerced to another. | 25 | /// Mostly this means trying to coerce one to the other, but |
26 | /// Coercing both types to another least upper bound type is not possible in rustc, | 26 | /// - if we have two function types for different functions, we need to |
27 | /// which will simply result in "incompatible types" error. | 27 | /// coerce both to function pointers; |
28 | /// - if we were concerned with lifetime subtyping, we'd need to look for a | ||
29 | /// least upper bound. | ||
28 | pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { | 30 | pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { |
29 | if self.coerce(ty1, ty2) { | 31 | if self.coerce(ty1, ty2) { |
30 | ty2.clone() | 32 | ty2.clone() |
31 | } else if self.coerce(ty2, ty1) { | 33 | } else if self.coerce(ty2, ty1) { |
32 | ty1.clone() | 34 | ty1.clone() |
33 | } else { | 35 | } else { |
34 | tested_by!(coerce_merge_fail_fallback); | 36 | if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) { |
35 | // For incompatible types, we use the latter one as result | 37 | mark::hit!(coerce_fn_reification); |
36 | // to be better recovery for `if` without `else`. | 38 | // Special case: two function types. Try to coerce both to |
37 | ty2.clone() | 39 | // pointers to have a chance at getting a match. See |
40 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 | ||
41 | let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig"); | ||
42 | let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig"); | ||
43 | let ptr_ty1 = Ty::fn_ptr(sig1); | ||
44 | let ptr_ty2 = Ty::fn_ptr(sig2); | ||
45 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) | ||
46 | } else { | ||
47 | mark::hit!(coerce_merge_fail_fallback); | ||
48 | ty1.clone() | ||
49 | } | ||
38 | } | 50 | } |
39 | } | 51 | } |
40 | 52 | ||
@@ -84,9 +96,7 @@ impl<'a> InferenceContext<'a> { | |||
84 | match from_ty.callable_sig(self.db) { | 96 | match from_ty.callable_sig(self.db) { |
85 | None => return false, | 97 | None => return false, |
86 | Some(sig) => { | 98 | Some(sig) => { |
87 | let num_args = sig.params_and_return.len() as u16 - 1; | 99 | from_ty = Ty::fn_ptr(sig); |
88 | from_ty = | ||
89 | Ty::apply(TypeCtor::FnPtr { num_args }, Substs(sig.params_and_return)); | ||
90 | } | 100 | } |
91 | } | 101 | } |
92 | } | 102 | } |
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index efc60986b..4a98e2deb 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Type inference for expressions. | 1 | //! Type inference for expressions. |
2 | 2 | ||
3 | use std::iter::{repeat, repeat_with}; | 3 | use std::iter::{repeat, repeat_with}; |
4 | use std::sync::Arc; | 4 | use std::{mem, sync::Arc}; |
5 | 5 | ||
6 | use hir_def::{ | 6 | use hir_def::{ |
7 | builtin_type::Signedness, | 7 | builtin_type::Signedness, |
@@ -17,15 +17,22 @@ use crate::{ | |||
17 | autoderef, method_resolution, op, | 17 | autoderef, method_resolution, op, |
18 | traits::InEnvironment, | 18 | traits::InEnvironment, |
19 | utils::{generics, variant_data, Generics}, | 19 | utils::{generics, variant_data, Generics}, |
20 | ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Substs, TraitRef, | 20 | ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs, |
21 | Ty, TypeCtor, Uncertain, | 21 | TraitRef, Ty, TypeCtor, Uncertain, |
22 | }; | 22 | }; |
23 | 23 | ||
24 | use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch}; | 24 | use super::{ |
25 | find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, | ||
26 | InferenceDiagnostic, TypeMismatch, | ||
27 | }; | ||
25 | 28 | ||
26 | impl<'a> InferenceContext<'a> { | 29 | impl<'a> InferenceContext<'a> { |
27 | pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { | 30 | pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { |
28 | let ty = self.infer_expr_inner(tgt_expr, expected); | 31 | let ty = self.infer_expr_inner(tgt_expr, expected); |
32 | if ty.is_never() { | ||
33 | // Any expression that produces a value of type `!` must have diverged | ||
34 | self.diverges = Diverges::Always; | ||
35 | } | ||
29 | let could_unify = self.unify(&ty, &expected.ty); | 36 | let could_unify = self.unify(&ty, &expected.ty); |
30 | if !could_unify { | 37 | if !could_unify { |
31 | self.result.type_mismatches.insert( | 38 | self.result.type_mismatches.insert( |
@@ -64,34 +71,80 @@ impl<'a> InferenceContext<'a> { | |||
64 | // if let is desugared to match, so this is always simple if | 71 | // if let is desugared to match, so this is always simple if |
65 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); | 72 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); |
66 | 73 | ||
74 | let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); | ||
75 | let mut both_arms_diverge = Diverges::Always; | ||
76 | |||
67 | let then_ty = self.infer_expr_inner(*then_branch, &expected); | 77 | let then_ty = self.infer_expr_inner(*then_branch, &expected); |
78 | both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); | ||
68 | let else_ty = match else_branch { | 79 | let else_ty = match else_branch { |
69 | Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), | 80 | Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), |
70 | None => Ty::unit(), | 81 | None => Ty::unit(), |
71 | }; | 82 | }; |
83 | both_arms_diverge &= self.diverges; | ||
84 | |||
85 | self.diverges = condition_diverges | both_arms_diverge; | ||
72 | 86 | ||
73 | self.coerce_merge_branch(&then_ty, &else_ty) | 87 | self.coerce_merge_branch(&then_ty, &else_ty) |
74 | } | 88 | } |
75 | Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected), | 89 | Expr::Block { statements, tail, .. } => { |
76 | Expr::Loop { body } => { | 90 | // FIXME: Breakable block inference |
91 | self.infer_block(statements, *tail, expected) | ||
92 | } | ||
93 | Expr::TryBlock { body } => { | ||
94 | let _inner = self.infer_expr(*body, expected); | ||
95 | // FIXME should be std::result::Result<{inner}, _> | ||
96 | Ty::Unknown | ||
97 | } | ||
98 | Expr::Loop { body, label } => { | ||
99 | self.breakables.push(BreakableContext { | ||
100 | may_break: false, | ||
101 | break_ty: self.table.new_type_var(), | ||
102 | label: label.clone(), | ||
103 | }); | ||
77 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 104 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
78 | // FIXME handle break with value | 105 | |
79 | Ty::simple(TypeCtor::Never) | 106 | let ctxt = self.breakables.pop().expect("breakable stack broken"); |
107 | if ctxt.may_break { | ||
108 | self.diverges = Diverges::Maybe; | ||
109 | } | ||
110 | |||
111 | if ctxt.may_break { | ||
112 | ctxt.break_ty | ||
113 | } else { | ||
114 | Ty::simple(TypeCtor::Never) | ||
115 | } | ||
80 | } | 116 | } |
81 | Expr::While { condition, body } => { | 117 | Expr::While { condition, body, label } => { |
118 | self.breakables.push(BreakableContext { | ||
119 | may_break: false, | ||
120 | break_ty: Ty::Unknown, | ||
121 | label: label.clone(), | ||
122 | }); | ||
82 | // while let is desugared to a match loop, so this is always simple while | 123 | // while let is desugared to a match loop, so this is always simple while |
83 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); | 124 | self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); |
84 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 125 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
126 | let _ctxt = self.breakables.pop().expect("breakable stack broken"); | ||
127 | // the body may not run, so it diverging doesn't mean we diverge | ||
128 | self.diverges = Diverges::Maybe; | ||
85 | Ty::unit() | 129 | Ty::unit() |
86 | } | 130 | } |
87 | Expr::For { iterable, body, pat } => { | 131 | Expr::For { iterable, body, pat, label } => { |
88 | let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); | 132 | let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); |
89 | 133 | ||
134 | self.breakables.push(BreakableContext { | ||
135 | may_break: false, | ||
136 | break_ty: Ty::Unknown, | ||
137 | label: label.clone(), | ||
138 | }); | ||
90 | let pat_ty = | 139 | let pat_ty = |
91 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); | 140 | self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); |
92 | 141 | ||
93 | self.infer_pat(*pat, &pat_ty, BindingMode::default()); | 142 | self.infer_pat(*pat, &pat_ty, BindingMode::default()); |
143 | |||
94 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); | 144 | self.infer_expr(*body, &Expectation::has_type(Ty::unit())); |
145 | let _ctxt = self.breakables.pop().expect("breakable stack broken"); | ||
146 | // the body may not run, so it diverging doesn't mean we diverge | ||
147 | self.diverges = Diverges::Maybe; | ||
95 | Ty::unit() | 148 | Ty::unit() |
96 | } | 149 | } |
97 | Expr::Lambda { body, args, ret_type, arg_types } => { | 150 | Expr::Lambda { body, args, ret_type, arg_types } => { |
@@ -99,13 +152,13 @@ impl<'a> InferenceContext<'a> { | |||
99 | 152 | ||
100 | let mut sig_tys = Vec::new(); | 153 | let mut sig_tys = Vec::new(); |
101 | 154 | ||
102 | for (arg_pat, arg_type) in args.iter().zip(arg_types.iter()) { | 155 | // collect explicitly written argument types |
103 | let expected = if let Some(type_ref) = arg_type { | 156 | for arg_type in arg_types.iter() { |
157 | let arg_ty = if let Some(type_ref) = arg_type { | ||
104 | self.make_ty(type_ref) | 158 | self.make_ty(type_ref) |
105 | } else { | 159 | } else { |
106 | Ty::Unknown | 160 | self.table.new_type_var() |
107 | }; | 161 | }; |
108 | let arg_ty = self.infer_pat(*arg_pat, &expected, BindingMode::default()); | ||
109 | sig_tys.push(arg_ty); | 162 | sig_tys.push(arg_ty); |
110 | } | 163 | } |
111 | 164 | ||
@@ -117,7 +170,7 @@ impl<'a> InferenceContext<'a> { | |||
117 | sig_tys.push(ret_ty.clone()); | 170 | sig_tys.push(ret_ty.clone()); |
118 | let sig_ty = Ty::apply( | 171 | let sig_ty = Ty::apply( |
119 | TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 }, | 172 | TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 }, |
120 | Substs(sig_tys.into()), | 173 | Substs(sig_tys.clone().into()), |
121 | ); | 174 | ); |
122 | let closure_ty = | 175 | let closure_ty = |
123 | Ty::apply_one(TypeCtor::Closure { def: self.owner, expr: tgt_expr }, sig_ty); | 176 | Ty::apply_one(TypeCtor::Closure { def: self.owner, expr: tgt_expr }, sig_ty); |
@@ -127,10 +180,18 @@ impl<'a> InferenceContext<'a> { | |||
127 | // infer the body. | 180 | // infer the body. |
128 | self.coerce(&closure_ty, &expected.ty); | 181 | self.coerce(&closure_ty, &expected.ty); |
129 | 182 | ||
130 | let prev_ret_ty = std::mem::replace(&mut self.return_ty, ret_ty.clone()); | 183 | // Now go through the argument patterns |
184 | for (arg_pat, arg_ty) in args.iter().zip(sig_tys) { | ||
185 | let resolved = self.resolve_ty_as_possible(arg_ty); | ||
186 | self.infer_pat(*arg_pat, &resolved, BindingMode::default()); | ||
187 | } | ||
188 | |||
189 | let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); | ||
190 | let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); | ||
131 | 191 | ||
132 | self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); | 192 | self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); |
133 | 193 | ||
194 | self.diverges = prev_diverges; | ||
134 | self.return_ty = prev_ret_ty; | 195 | self.return_ty = prev_ret_ty; |
135 | 196 | ||
136 | closure_ty | 197 | closure_ty |
@@ -160,7 +221,11 @@ impl<'a> InferenceContext<'a> { | |||
160 | self.table.new_type_var() | 221 | self.table.new_type_var() |
161 | }; | 222 | }; |
162 | 223 | ||
224 | let matchee_diverges = self.diverges; | ||
225 | let mut all_arms_diverge = Diverges::Always; | ||
226 | |||
163 | for arm in arms { | 227 | for arm in arms { |
228 | self.diverges = Diverges::Maybe; | ||
164 | let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); | 229 | let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); |
165 | if let Some(guard_expr) = arm.guard { | 230 | if let Some(guard_expr) = arm.guard { |
166 | self.infer_expr( | 231 | self.infer_expr( |
@@ -170,9 +235,12 @@ impl<'a> InferenceContext<'a> { | |||
170 | } | 235 | } |
171 | 236 | ||
172 | let arm_ty = self.infer_expr_inner(arm.expr, &expected); | 237 | let arm_ty = self.infer_expr_inner(arm.expr, &expected); |
238 | all_arms_diverge &= self.diverges; | ||
173 | result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); | 239 | result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); |
174 | } | 240 | } |
175 | 241 | ||
242 | self.diverges = matchee_diverges | all_arms_diverge; | ||
243 | |||
176 | result_ty | 244 | result_ty |
177 | } | 245 | } |
178 | Expr::Path(p) => { | 246 | Expr::Path(p) => { |
@@ -180,12 +248,32 @@ impl<'a> InferenceContext<'a> { | |||
180 | let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr); | 248 | let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr); |
181 | self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown) | 249 | self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown) |
182 | } | 250 | } |
183 | Expr::Continue => Ty::simple(TypeCtor::Never), | 251 | Expr::Continue { .. } => Ty::simple(TypeCtor::Never), |
184 | Expr::Break { expr } => { | 252 | Expr::Break { expr, label } => { |
185 | if let Some(expr) = expr { | 253 | let val_ty = if let Some(expr) = expr { |
186 | // FIXME handle break with value | 254 | self.infer_expr(*expr, &Expectation::none()) |
187 | self.infer_expr(*expr, &Expectation::none()); | 255 | } else { |
256 | Ty::unit() | ||
257 | }; | ||
258 | |||
259 | let last_ty = | ||
260 | if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) { | ||
261 | ctxt.break_ty.clone() | ||
262 | } else { | ||
263 | Ty::Unknown | ||
264 | }; | ||
265 | |||
266 | let merged_type = self.coerce_merge_branch(&last_ty, &val_ty); | ||
267 | |||
268 | if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) { | ||
269 | ctxt.break_ty = merged_type; | ||
270 | ctxt.may_break = true; | ||
271 | } else { | ||
272 | self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { | ||
273 | expr: tgt_expr, | ||
274 | }); | ||
188 | } | 275 | } |
276 | |||
189 | Ty::simple(TypeCtor::Never) | 277 | Ty::simple(TypeCtor::Never) |
190 | } | 278 | } |
191 | Expr::Return { expr } => { | 279 | Expr::Return { expr } => { |
@@ -281,19 +369,28 @@ impl<'a> InferenceContext<'a> { | |||
281 | // FIXME check the cast... | 369 | // FIXME check the cast... |
282 | cast_ty | 370 | cast_ty |
283 | } | 371 | } |
284 | Expr::Ref { expr, mutability } => { | 372 | Expr::Ref { expr, rawness, mutability } => { |
285 | let expectation = | 373 | let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = |
286 | if let Some((exp_inner, exp_mutability)) = &expected.ty.as_reference() { | 374 | &expected.ty.as_reference_or_ptr() |
287 | if *exp_mutability == Mutability::Mut && *mutability == Mutability::Shared { | 375 | { |
288 | // FIXME: throw type error - expected mut reference but found shared ref, | 376 | if *exp_mutability == Mutability::Mut && *mutability == Mutability::Shared { |
289 | // which cannot be coerced | 377 | // FIXME: throw type error - expected mut reference but found shared ref, |
290 | } | 378 | // which cannot be coerced |
291 | Expectation::rvalue_hint(Ty::clone(exp_inner)) | 379 | } |
292 | } else { | 380 | if *exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr { |
293 | Expectation::none() | 381 | // FIXME: throw type error - expected reference but found ptr, |
294 | }; | 382 | // which cannot be coerced |
383 | } | ||
384 | Expectation::rvalue_hint(Ty::clone(exp_inner)) | ||
385 | } else { | ||
386 | Expectation::none() | ||
387 | }; | ||
295 | let inner_ty = self.infer_expr_inner(*expr, &expectation); | 388 | let inner_ty = self.infer_expr_inner(*expr, &expectation); |
296 | Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty) | 389 | let ty = match rawness { |
390 | Rawness::RawPtr => TypeCtor::RawPtr(*mutability), | ||
391 | Rawness::Ref => TypeCtor::Ref(*mutability), | ||
392 | }; | ||
393 | Ty::apply_one(ty, inner_ty) | ||
297 | } | 394 | } |
298 | Expr::Box { expr } => { | 395 | Expr::Box { expr } => { |
299 | let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); | 396 | let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); |
@@ -496,8 +593,8 @@ impl<'a> InferenceContext<'a> { | |||
496 | } | 593 | } |
497 | Literal::ByteString(..) => { | 594 | Literal::ByteString(..) => { |
498 | let byte_type = Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::u8()))); | 595 | let byte_type = Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::u8()))); |
499 | let slice_type = Ty::apply_one(TypeCtor::Slice, byte_type); | 596 | let array_type = Ty::apply_one(TypeCtor::Array, byte_type); |
500 | Ty::apply_one(TypeCtor::Ref(Mutability::Shared), slice_type) | 597 | Ty::apply_one(TypeCtor::Ref(Mutability::Shared), array_type) |
501 | } | 598 | } |
502 | Literal::Char(..) => Ty::simple(TypeCtor::Char), | 599 | Literal::Char(..) => Ty::simple(TypeCtor::Char), |
503 | Literal::Int(_v, ty) => Ty::simple(TypeCtor::Int((*ty).into())), | 600 | Literal::Int(_v, ty) => Ty::simple(TypeCtor::Int((*ty).into())), |
@@ -517,7 +614,6 @@ impl<'a> InferenceContext<'a> { | |||
517 | tail: Option<ExprId>, | 614 | tail: Option<ExprId>, |
518 | expected: &Expectation, | 615 | expected: &Expectation, |
519 | ) -> Ty { | 616 | ) -> Ty { |
520 | let mut diverges = false; | ||
521 | for stmt in statements { | 617 | for stmt in statements { |
522 | match stmt { | 618 | match stmt { |
523 | Statement::Let { pat, type_ref, initializer } => { | 619 | Statement::Let { pat, type_ref, initializer } => { |
@@ -539,9 +635,7 @@ impl<'a> InferenceContext<'a> { | |||
539 | self.infer_pat(*pat, &ty, BindingMode::default()); | 635 | self.infer_pat(*pat, &ty, BindingMode::default()); |
540 | } | 636 | } |
541 | Statement::Expr(expr) => { | 637 | Statement::Expr(expr) => { |
542 | if let ty_app!(TypeCtor::Never) = self.infer_expr(*expr, &Expectation::none()) { | 638 | self.infer_expr(*expr, &Expectation::none()); |
543 | diverges = true; | ||
544 | } | ||
545 | } | 639 | } |
546 | } | 640 | } |
547 | } | 641 | } |
@@ -549,14 +643,22 @@ impl<'a> InferenceContext<'a> { | |||
549 | let ty = if let Some(expr) = tail { | 643 | let ty = if let Some(expr) = tail { |
550 | self.infer_expr_coerce(expr, expected) | 644 | self.infer_expr_coerce(expr, expected) |
551 | } else { | 645 | } else { |
552 | self.coerce(&Ty::unit(), expected.coercion_target()); | 646 | // Citing rustc: if there is no explicit tail expression, |
553 | Ty::unit() | 647 | // that is typically equivalent to a tail expression |
648 | // of `()` -- except if the block diverges. In that | ||
649 | // case, there is no value supplied from the tail | ||
650 | // expression (assuming there are no other breaks, | ||
651 | // this implies that the type of the block will be | ||
652 | // `!`). | ||
653 | if self.diverges.is_always() { | ||
654 | // we don't even make an attempt at coercion | ||
655 | self.table.new_maybe_never_type_var() | ||
656 | } else { | ||
657 | self.coerce(&Ty::unit(), expected.coercion_target()); | ||
658 | Ty::unit() | ||
659 | } | ||
554 | }; | 660 | }; |
555 | if diverges { | 661 | ty |
556 | Ty::simple(TypeCtor::Never) | ||
557 | } else { | ||
558 | ty | ||
559 | } | ||
560 | } | 662 | } |
561 | 663 | ||
562 | fn infer_method_call( | 664 | fn infer_method_call( |
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::{ | |||
10 | FieldId, | 10 | FieldId, |
11 | }; | 11 | }; |
12 | use hir_expand::name::Name; | 12 | use hir_expand::name::Name; |
13 | use test_utils::tested_by; | 13 | use test_utils::mark; |
14 | 14 | ||
15 | use super::{BindingMode, Expectation, InferenceContext}; | 15 | use super::{BindingMode, Expectation, InferenceContext}; |
16 | use crate::{utils::variant_data, Substs, Ty, TypeCtor}; | 16 | use crate::{utils::variant_data, Substs, Ty, TypeCtor}; |
@@ -111,7 +111,7 @@ impl<'a> InferenceContext<'a> { | |||
111 | } | 111 | } |
112 | } | 112 | } |
113 | } else if let Pat::Ref { .. } = &body[pat] { | 113 | } else if let Pat::Ref { .. } = &body[pat] { |
114 | tested_by!(match_ergonomics_ref); | 114 | mark::hit!(match_ergonomics_ref); |
115 | // When you encounter a `&pat` pattern, reset to Move. | 115 | // When you encounter a `&pat` pattern, reset to Move. |
116 | // This is so that `w` is by value: `let (_, &w) = &(1, &2);` | 116 | // This is so that `w` is by value: `let (_, &w) = &(1, &2);` |
117 | default_bm = BindingMode::Move; | 117 | default_bm = BindingMode::Move; |
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; | |||
5 | use hir_def::{ | 5 | use hir_def::{ |
6 | path::{Path, PathSegment}, | 6 | path::{Path, PathSegment}, |
7 | resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs}, | 7 | resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs}, |
8 | AssocContainerId, AssocItemId, Lookup, | 8 | AdtId, AssocContainerId, AssocItemId, EnumVariantId, Lookup, |
9 | }; | 9 | }; |
10 | use hir_expand::name::Name; | 10 | use hir_expand::name::Name; |
11 | 11 | ||
@@ -77,6 +77,18 @@ impl<'a> InferenceContext<'a> { | |||
77 | 77 | ||
78 | it.into() | 78 | it.into() |
79 | } | 79 | } |
80 | ValueNs::ImplSelf(impl_id) => { | ||
81 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); | ||
82 | let substs = Substs::type_params_for_generics(&generics); | ||
83 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); | ||
84 | if let Some((AdtId::StructId(struct_id), _)) = ty.as_adt() { | ||
85 | let ty = self.db.value_ty(struct_id.into()).subst(&substs); | ||
86 | return Some(ty); | ||
87 | } else { | ||
88 | // FIXME: diagnostic, invalid Self reference | ||
89 | return None; | ||
90 | } | ||
91 | } | ||
80 | }; | 92 | }; |
81 | 93 | ||
82 | let ty = self.db.value_ty(typable); | 94 | let ty = self.db.value_ty(typable); |
@@ -199,6 +211,10 @@ impl<'a> InferenceContext<'a> { | |||
199 | return None; | 211 | return None; |
200 | } | 212 | } |
201 | 213 | ||
214 | if let Some(result) = self.resolve_enum_variant_on_ty(&ty, name, id) { | ||
215 | return Some(result); | ||
216 | } | ||
217 | |||
202 | let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); | 218 | let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); |
203 | let krate = self.resolver.krate()?; | 219 | let krate = self.resolver.krate()?; |
204 | let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); | 220 | let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); |
@@ -250,4 +266,21 @@ impl<'a> InferenceContext<'a> { | |||
250 | }, | 266 | }, |
251 | ) | 267 | ) |
252 | } | 268 | } |
269 | |||
270 | fn resolve_enum_variant_on_ty( | ||
271 | &mut self, | ||
272 | ty: &Ty, | ||
273 | name: &Name, | ||
274 | id: ExprOrPatId, | ||
275 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
276 | let (enum_id, subst) = match ty.as_adt() { | ||
277 | Some((AdtId::EnumId(e), subst)) => (e, subst), | ||
278 | _ => return None, | ||
279 | }; | ||
280 | let enum_data = self.db.enum_data(enum_id); | ||
281 | let local_id = enum_data.variant(name)?; | ||
282 | let variant = EnumVariantId { parent: enum_id, local_id }; | ||
283 | self.write_variant_resolution(id, variant.into()); | ||
284 | Some((ValueNs::EnumVariantId(variant), Some(subst.clone()))) | ||
285 | } | ||
253 | } | 286 | } |
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; | |||
4 | 4 | ||
5 | use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; | 5 | use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; |
6 | 6 | ||
7 | use test_utils::tested_by; | 7 | use test_utils::mark; |
8 | 8 | ||
9 | use super::{InferenceContext, Obligation}; | 9 | use super::{InferenceContext, Obligation}; |
10 | use crate::{ | 10 | use crate::{ |
@@ -313,7 +313,7 @@ impl InferenceTable { | |||
313 | // more than once | 313 | // more than once |
314 | for i in 0..3 { | 314 | for i in 0..3 { |
315 | if i > 0 { | 315 | if i > 0 { |
316 | tested_by!(type_var_resolves_to_int_var); | 316 | mark::hit!(type_var_resolves_to_int_var); |
317 | } | 317 | } |
318 | match &*ty { | 318 | match &*ty { |
319 | Ty::Infer(tv) => { | 319 | Ty::Infer(tv) => { |
@@ -342,7 +342,7 @@ impl InferenceTable { | |||
342 | Ty::Infer(tv) => { | 342 | Ty::Infer(tv) => { |
343 | let inner = tv.to_inner(); | 343 | let inner = tv.to_inner(); |
344 | if tv_stack.contains(&inner) { | 344 | if tv_stack.contains(&inner) { |
345 | tested_by!(type_var_cycles_resolve_as_possible); | 345 | mark::hit!(type_var_cycles_resolve_as_possible); |
346 | // recursive type | 346 | // recursive type |
347 | return tv.fallback_value(); | 347 | return tv.fallback_value(); |
348 | } | 348 | } |
@@ -369,7 +369,7 @@ impl InferenceTable { | |||
369 | Ty::Infer(tv) => { | 369 | Ty::Infer(tv) => { |
370 | let inner = tv.to_inner(); | 370 | let inner = tv.to_inner(); |
371 | if tv_stack.contains(&inner) { | 371 | if tv_stack.contains(&inner) { |
372 | tested_by!(type_var_cycles_resolve_completely); | 372 | mark::hit!(type_var_cycles_resolve_completely); |
373 | // recursive type | 373 | // recursive type |
374 | return tv.fallback_value(); | 374 | return tv.fallback_value(); |
375 | } | 375 | } |
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index a6f56c661..9fa8d3bdc 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs | |||
@@ -42,7 +42,6 @@ pub mod expr; | |||
42 | mod tests; | 42 | mod tests; |
43 | #[cfg(test)] | 43 | #[cfg(test)] |
44 | mod test_db; | 44 | mod test_db; |
45 | mod marks; | ||
46 | mod _match; | 45 | mod _match; |
47 | 46 | ||
48 | use std::ops::Deref; | 47 | use std::ops::Deref; |
@@ -50,8 +49,10 @@ use std::sync::Arc; | |||
50 | use std::{iter, mem}; | 49 | use std::{iter, mem}; |
51 | 50 | ||
52 | use hir_def::{ | 51 | use hir_def::{ |
53 | expr::ExprId, type_ref::Mutability, AdtId, AssocContainerId, DefWithBodyId, GenericDefId, | 52 | expr::ExprId, |
54 | HasModule, Lookup, TraitId, TypeAliasId, TypeParamId, | 53 | type_ref::{Mutability, Rawness}, |
54 | AdtId, AssocContainerId, DefWithBodyId, GenericDefId, HasModule, Lookup, TraitId, TypeAliasId, | ||
55 | TypeParamId, | ||
55 | }; | 56 | }; |
56 | use ra_db::{impl_intern_key, salsa, CrateId}; | 57 | use ra_db::{impl_intern_key, salsa, CrateId}; |
57 | 58 | ||
@@ -156,10 +157,16 @@ pub enum TypeCtor { | |||
156 | /// This exists just for Chalk, because Chalk just has a single `StructId` where | 157 | /// This exists just for Chalk, because Chalk just has a single `StructId` where |
157 | /// we have different kinds of ADTs, primitive types and special type | 158 | /// we have different kinds of ADTs, primitive types and special type |
158 | /// constructors like tuples and function pointers. | 159 | /// constructors like tuples and function pointers. |
159 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 160 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] |
160 | pub struct TypeCtorId(salsa::InternId); | 161 | pub struct TypeCtorId(salsa::InternId); |
161 | impl_intern_key!(TypeCtorId); | 162 | impl_intern_key!(TypeCtorId); |
162 | 163 | ||
164 | /// This exists just for Chalk, because Chalk just has a single `FnDefId` where | ||
165 | /// we have different IDs for struct and enum variant constructors. | ||
166 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] | ||
167 | pub struct CallableDefId(salsa::InternId); | ||
168 | impl_intern_key!(CallableDefId); | ||
169 | |||
163 | impl TypeCtor { | 170 | impl TypeCtor { |
164 | pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize { | 171 | pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize { |
165 | match self { | 172 | match self { |
@@ -427,6 +434,11 @@ impl Substs { | |||
427 | } | 434 | } |
428 | } | 435 | } |
429 | 436 | ||
437 | /// Return an index of a parameter in the generic type parameter list by it's id. | ||
438 | pub fn param_idx(db: &dyn HirDatabase, id: TypeParamId) -> Option<usize> { | ||
439 | generics(db.upcast(), id.parent).param_idx(id) | ||
440 | } | ||
441 | |||
430 | #[derive(Debug, Clone)] | 442 | #[derive(Debug, Clone)] |
431 | pub struct SubstsBuilder { | 443 | pub struct SubstsBuilder { |
432 | vec: Vec<Ty>, | 444 | vec: Vec<Ty>, |
@@ -683,6 +695,12 @@ impl Ty { | |||
683 | pub fn unit() -> Self { | 695 | pub fn unit() -> Self { |
684 | Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty()) | 696 | Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty()) |
685 | } | 697 | } |
698 | pub fn fn_ptr(sig: FnSig) -> Self { | ||
699 | Ty::apply( | ||
700 | TypeCtor::FnPtr { num_args: sig.params().len() as u16 }, | ||
701 | Substs(sig.params_and_return), | ||
702 | ) | ||
703 | } | ||
686 | 704 | ||
687 | pub fn as_reference(&self) -> Option<(&Ty, Mutability)> { | 705 | pub fn as_reference(&self) -> Option<(&Ty, Mutability)> { |
688 | match self { | 706 | match self { |
@@ -693,6 +711,18 @@ impl Ty { | |||
693 | } | 711 | } |
694 | } | 712 | } |
695 | 713 | ||
714 | pub fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> { | ||
715 | match self { | ||
716 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(mutability), parameters }) => { | ||
717 | Some((parameters.as_single(), Rawness::Ref, *mutability)) | ||
718 | } | ||
719 | Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(mutability), parameters }) => { | ||
720 | Some((parameters.as_single(), Rawness::RawPtr, *mutability)) | ||
721 | } | ||
722 | _ => None, | ||
723 | } | ||
724 | } | ||
725 | |||
696 | pub fn strip_references(&self) -> &Ty { | 726 | pub fn strip_references(&self) -> &Ty { |
697 | let mut t: &Ty = self; | 727 | let mut t: &Ty = self; |
698 | 728 | ||
@@ -730,6 +760,10 @@ impl Ty { | |||
730 | } | 760 | } |
731 | } | 761 | } |
732 | 762 | ||
763 | pub fn is_never(&self) -> bool { | ||
764 | matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. })) | ||
765 | } | ||
766 | |||
733 | /// If this is a `dyn Trait` type, this returns the `Trait` part. | 767 | /// If this is a `dyn Trait` type, this returns the `Trait` part. |
734 | pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { | 768 | pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { |
735 | match self { | 769 | match self { |
@@ -793,15 +827,13 @@ impl Ty { | |||
793 | } | 827 | } |
794 | } | 828 | } |
795 | 829 | ||
796 | /// If this is an `impl Trait` or `dyn Trait`, returns that trait. | 830 | /// If this is a `dyn Trait`, returns that trait. |
797 | pub fn inherent_trait(&self) -> Option<TraitId> { | 831 | pub fn dyn_trait(&self) -> Option<TraitId> { |
798 | match self { | 832 | match self { |
799 | Ty::Dyn(predicates) | Ty::Opaque(predicates) => { | 833 | Ty::Dyn(predicates) => predicates.iter().find_map(|pred| match pred { |
800 | predicates.iter().find_map(|pred| match pred { | 834 | GenericPredicate::Implemented(tr) => Some(tr.trait_), |
801 | GenericPredicate::Implemented(tr) => Some(tr.trait_), | 835 | _ => None, |
802 | _ => None, | 836 | }), |
803 | }) | ||
804 | } | ||
805 | _ => None, | 837 | _ => None, |
806 | } | 838 | } |
807 | } | 839 | } |
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 { | |||
812 | // add `Self: Trait<T1, T2, ...>` to the environment in trait | 812 | // add `Self: Trait<T1, T2, ...>` to the environment in trait |
813 | // function default implementations (and hypothetical code | 813 | // function default implementations (and hypothetical code |
814 | // inside consts or type aliases) | 814 | // inside consts or type aliases) |
815 | test_utils::tested_by!(trait_self_implements_self); | 815 | test_utils::mark::hit!(trait_self_implements_self); |
816 | let substs = Substs::type_params(db, trait_id); | 816 | let substs = Substs::type_params(db, trait_id); |
817 | let trait_ref = TraitRef { trait_: trait_id, substs }; | 817 | let trait_ref = TraitRef { trait_: trait_id, substs }; |
818 | let pred = GenericPredicate::Implemented(trait_ref); | 818 | 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 de5cb1d6b..000000000 --- a/crates/ra_hir_ty/src/marks.rs +++ /dev/null | |||
@@ -1,11 +0,0 @@ | |||
1 | //! See test_utils/src/marks.rs | ||
2 | |||
3 | test_utils::marks!( | ||
4 | type_var_cycles_resolve_completely | ||
5 | type_var_cycles_resolve_as_possible | ||
6 | type_var_resolves_to_int_var | ||
7 | impl_self_type_match_without_receiver | ||
8 | match_ergonomics_ref | ||
9 | coerce_merge_fail_fallback | ||
10 | trait_self_implements_self | ||
11 | ); | ||
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index 657284fd0..e19628fdf 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<T>( | |||
408 | receiver_ty: Option<&Canonical<Ty>>, | 408 | receiver_ty: Option<&Canonical<Ty>>, |
409 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | 409 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, |
410 | ) -> Option<T> { | 410 | ) -> Option<T> { |
411 | // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope | 411 | // if ty is `dyn Trait`, the trait doesn't need to be in scope |
412 | let inherent_trait = self_ty.value.inherent_trait().into_iter(); | 412 | let inherent_trait = |
413 | self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); | ||
413 | let env_traits = if let Ty::Placeholder(_) = self_ty.value { | 414 | let env_traits = if let Ty::Placeholder(_) = self_ty.value { |
414 | // if we have `T: Trait` in the param env, the trait doesn't need to be in scope | 415 | // if we have `T: Trait` in the param env, the trait doesn't need to be in scope |
415 | env.trait_predicates_for_self_ty(&self_ty.value) | 416 | env.trait_predicates_for_self_ty(&self_ty.value) |
@@ -468,7 +469,7 @@ fn iterate_inherent_methods<T>( | |||
468 | // already happens in `is_valid_candidate` above; if not, we | 469 | // already happens in `is_valid_candidate` above; if not, we |
469 | // check it here | 470 | // check it here |
470 | if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { | 471 | if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { |
471 | test_utils::tested_by!(impl_self_type_match_without_receiver); | 472 | test_utils::mark::hit!(impl_self_type_match_without_receiver); |
472 | continue; | 473 | continue; |
473 | } | 474 | } |
474 | if let Some(result) = callback(&self_ty.value, item) { | 475 | if let Some(result) = callback(&self_ty.value, item) { |
@@ -601,11 +602,6 @@ pub fn implements_trait( | |||
601 | krate: CrateId, | 602 | krate: CrateId, |
602 | trait_: TraitId, | 603 | trait_: TraitId, |
603 | ) -> bool { | 604 | ) -> bool { |
604 | if ty.value.inherent_trait() == Some(trait_) { | ||
605 | // FIXME this is a bit of a hack, since Chalk should say the same thing | ||
606 | // anyway, but currently Chalk doesn't implement `dyn/impl Trait` yet | ||
607 | return true; | ||
608 | } | ||
609 | let goal = generic_implements_goal(db, env, trait_, ty.clone()); | 605 | let goal = generic_implements_goal(db, env, trait_, ty.clone()); |
610 | let solution = db.trait_solve(krate, goal); | 606 | let solution = db.trait_solve(krate, goal); |
611 | 607 | ||
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 { | |||
30 | pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { | 30 | pub(super) fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { |
31 | match op { | 31 | match op { |
32 | BinaryOp::LogicOp(..) => Ty::simple(TypeCtor::Bool), | 32 | BinaryOp::LogicOp(..) => Ty::simple(TypeCtor::Bool), |
33 | BinaryOp::Assignment { op: None } | BinaryOp::CmpOp(CmpOp::Eq { .. }) => match lhs_ty { | 33 | BinaryOp::Assignment { op: None } => lhs_ty, |
34 | BinaryOp::CmpOp(CmpOp::Eq { .. }) => match lhs_ty { | ||
34 | Ty::Apply(ApplicationTy { ctor, .. }) => match ctor { | 35 | Ty::Apply(ApplicationTy { ctor, .. }) => match ctor { |
35 | TypeCtor::Int(..) | 36 | TypeCtor::Int(..) |
36 | | TypeCtor::Float(..) | 37 | | TypeCtor::Float(..) |
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index 588d81282..1fe05c70c 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs | |||
@@ -6,6 +6,7 @@ mod patterns; | |||
6 | mod traits; | 6 | mod traits; |
7 | mod method_resolution; | 7 | mod method_resolution; |
8 | mod macros; | 8 | mod macros; |
9 | mod display_source_code; | ||
9 | 10 | ||
10 | use std::sync::Arc; | 11 | use std::sync::Arc; |
11 | 12 | ||
@@ -16,7 +17,7 @@ use hir_def::{ | |||
16 | item_scope::ItemScope, | 17 | item_scope::ItemScope, |
17 | keys, | 18 | keys, |
18 | nameres::CrateDefMap, | 19 | nameres::CrateDefMap, |
19 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, | 20 | AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, ModuleId, |
20 | }; | 21 | }; |
21 | use hir_expand::{db::AstDatabase, InFile}; | 22 | use hir_expand::{db::AstDatabase, InFile}; |
22 | use insta::assert_snapshot; | 23 | use insta::assert_snapshot; |
@@ -37,6 +38,18 @@ use crate::{ | |||
37 | // update the snapshots. | 38 | // update the snapshots. |
38 | 39 | ||
39 | fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { | 40 | fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { |
41 | type_at_pos_displayed(db, pos, |ty, _| ty.display(db).to_string()) | ||
42 | } | ||
43 | |||
44 | fn displayed_source_at_pos(db: &TestDB, pos: FilePosition) -> String { | ||
45 | type_at_pos_displayed(db, pos, |ty, module_id| ty.display_source_code(db, module_id).unwrap()) | ||
46 | } | ||
47 | |||
48 | fn type_at_pos_displayed( | ||
49 | db: &TestDB, | ||
50 | pos: FilePosition, | ||
51 | display_fn: impl FnOnce(&Ty, ModuleId) -> String, | ||
52 | ) -> String { | ||
40 | let file = db.parse(pos.file_id).ok().unwrap(); | 53 | let file = db.parse(pos.file_id).ok().unwrap(); |
41 | let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); | 54 | let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); |
42 | let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | 55 | 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 { | |||
49 | if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { | 62 | if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { |
50 | let infer = db.infer(func.into()); | 63 | let infer = db.infer(func.into()); |
51 | let ty = &infer[expr_id]; | 64 | let ty = &infer[expr_id]; |
52 | return ty.display(db).to_string(); | 65 | return display_fn(ty, module); |
53 | } | 66 | } |
54 | panic!("Can't find expression") | 67 | panic!("Can't find expression") |
55 | } | 68 | } |
@@ -361,6 +374,33 @@ fn no_such_field_with_feature_flag_diagnostics() { | |||
361 | } | 374 | } |
362 | 375 | ||
363 | #[test] | 376 | #[test] |
377 | fn no_such_field_enum_with_feature_flag_diagnostics() { | ||
378 | let diagnostics = TestDB::with_files( | ||
379 | r#" | ||
380 | //- /lib.rs crate:foo cfg:feature=foo | ||
381 | enum Foo { | ||
382 | #[cfg(not(feature = "foo"))] | ||
383 | Buz, | ||
384 | #[cfg(feature = "foo")] | ||
385 | Bar, | ||
386 | Baz | ||
387 | } | ||
388 | |||
389 | fn test_fn(f: Foo) { | ||
390 | match f { | ||
391 | Foo::Bar => {}, | ||
392 | Foo::Baz => {}, | ||
393 | } | ||
394 | } | ||
395 | "#, | ||
396 | ) | ||
397 | .diagnostics() | ||
398 | .0; | ||
399 | |||
400 | assert_snapshot!(diagnostics, @r###""###); | ||
401 | } | ||
402 | |||
403 | #[test] | ||
364 | fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { | 404 | fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { |
365 | let diagnostics = TestDB::with_files( | 405 | let diagnostics = TestDB::with_files( |
366 | r#" | 406 | r#" |
@@ -491,3 +531,21 @@ fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { | |||
491 | 531 | ||
492 | assert_snapshot!(diagnostics, @""); | 532 | assert_snapshot!(diagnostics, @""); |
493 | } | 533 | } |
534 | |||
535 | #[test] | ||
536 | fn break_outside_of_loop() { | ||
537 | let diagnostics = TestDB::with_files( | ||
538 | r" | ||
539 | //- /lib.rs | ||
540 | fn foo() { | ||
541 | break; | ||
542 | } | ||
543 | ", | ||
544 | ) | ||
545 | .diagnostics() | ||
546 | .0; | ||
547 | |||
548 | assert_snapshot!(diagnostics, @r###""break": break outside of loop | ||
549 | "### | ||
550 | ); | ||
551 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index e6fb3e123..6f777ed8c 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use super::infer_with_mismatches; | 1 | use super::infer_with_mismatches; |
2 | use insta::assert_snapshot; | 2 | use insta::assert_snapshot; |
3 | use test_utils::covers; | 3 | use test_utils::mark; |
4 | 4 | ||
5 | // Infer with some common definitions and impls. | 5 | // Infer with some common definitions and impls. |
6 | fn infer(source: &str) -> String { | 6 | fn infer(source: &str) -> String { |
@@ -116,15 +116,20 @@ fn infer_let_stmt_coerce() { | |||
116 | assert_snapshot!( | 116 | assert_snapshot!( |
117 | infer(r#" | 117 | infer(r#" |
118 | fn test() { | 118 | fn test() { |
119 | let x: &[i32] = &[1]; | 119 | let x: &[isize] = &[1]; |
120 | let x: *const [isize] = &[1]; | ||
120 | } | 121 | } |
121 | "#), | 122 | "#), |
122 | @r###" | 123 | @r###" |
123 | 11..40 '{ ...[1]; }': () | 124 | 11..76 '{ ...[1]; }': () |
124 | 21..22 'x': &[i32] | 125 | 21..22 'x': &[isize] |
125 | 33..37 '&[1]': &[i32; _] | 126 | 35..39 '&[1]': &[isize; _] |
126 | 34..37 '[1]': [i32; _] | 127 | 36..39 '[1]': [isize; _] |
127 | 35..36 '1': i32 | 128 | 37..38 '1': isize |
129 | 49..50 'x': *const [isize] | ||
130 | 69..73 '&[1]': &[isize; _] | ||
131 | 70..73 '[1]': [isize; _] | ||
132 | 71..72 '1': isize | ||
128 | "###); | 133 | "###); |
129 | } | 134 | } |
130 | 135 | ||
@@ -339,7 +344,7 @@ fn test(i: i32) { | |||
339 | 344 | ||
340 | #[test] | 345 | #[test] |
341 | fn coerce_merge_one_by_one1() { | 346 | fn coerce_merge_one_by_one1() { |
342 | covers!(coerce_merge_fail_fallback); | 347 | mark::check!(coerce_merge_fail_fallback); |
343 | 348 | ||
344 | assert_snapshot!( | 349 | assert_snapshot!( |
345 | infer(r#" | 350 | infer(r#" |
@@ -384,7 +389,7 @@ fn foo() -> u32 { | |||
384 | } | 389 | } |
385 | "#, true), | 390 | "#, true), |
386 | @r###" | 391 | @r###" |
387 | 17..40 '{ ...own; }': ! | 392 | 17..40 '{ ...own; }': u32 |
388 | 23..37 'return unknown': ! | 393 | 23..37 'return unknown': ! |
389 | 30..37 'unknown': u32 | 394 | 30..37 'unknown': u32 |
390 | "### | 395 | "### |
@@ -514,7 +519,7 @@ fn foo() { | |||
514 | 27..103 '{ ... }': &u32 | 519 | 27..103 '{ ... }': &u32 |
515 | 37..82 'if tru... }': () | 520 | 37..82 'if tru... }': () |
516 | 40..44 'true': bool | 521 | 40..44 'true': bool |
517 | 45..82 '{ ... }': ! | 522 | 45..82 '{ ... }': () |
518 | 59..71 'return &1u32': ! | 523 | 59..71 'return &1u32': ! |
519 | 66..71 '&1u32': &u32 | 524 | 66..71 '&1u32': &u32 |
520 | 67..71 '1u32': u32 | 525 | 67..71 '1u32': u32 |
@@ -546,6 +551,48 @@ fn test() { | |||
546 | } | 551 | } |
547 | 552 | ||
548 | #[test] | 553 | #[test] |
554 | fn coerce_fn_items_in_match_arms() { | ||
555 | mark::check!(coerce_fn_reification); | ||
556 | assert_snapshot!( | ||
557 | infer_with_mismatches(r#" | ||
558 | fn foo1(x: u32) -> isize { 1 } | ||
559 | fn foo2(x: u32) -> isize { 2 } | ||
560 | fn foo3(x: u32) -> isize { 3 } | ||
561 | fn test() { | ||
562 | let x = match 1 { | ||
563 | 1 => foo1, | ||
564 | 2 => foo2, | ||
565 | _ => foo3, | ||
566 | }; | ||
567 | } | ||
568 | "#, true), | ||
569 | @r###" | ||
570 | 9..10 'x': u32 | ||
571 | 26..31 '{ 1 }': isize | ||
572 | 28..29 '1': isize | ||
573 | 40..41 'x': u32 | ||
574 | 57..62 '{ 2 }': isize | ||
575 | 59..60 '2': isize | ||
576 | 71..72 'x': u32 | ||
577 | 88..93 '{ 3 }': isize | ||
578 | 90..91 '3': isize | ||
579 | 104..193 '{ ... }; }': () | ||
580 | 114..115 'x': fn(u32) -> isize | ||
581 | 118..190 'match ... }': fn(u32) -> isize | ||
582 | 124..125 '1': i32 | ||
583 | 136..137 '1': i32 | ||
584 | 136..137 '1': i32 | ||
585 | 141..145 'foo1': fn foo1(u32) -> isize | ||
586 | 155..156 '2': i32 | ||
587 | 155..156 '2': i32 | ||
588 | 160..164 'foo2': fn foo2(u32) -> isize | ||
589 | 174..175 '_': i32 | ||
590 | 179..183 'foo3': fn foo3(u32) -> isize | ||
591 | "### | ||
592 | ); | ||
593 | } | ||
594 | |||
595 | #[test] | ||
549 | fn coerce_closure_to_fn_ptr() { | 596 | fn coerce_closure_to_fn_ptr() { |
550 | assert_snapshot!( | 597 | assert_snapshot!( |
551 | infer_with_mismatches(r#" | 598 | infer_with_mismatches(r#" |
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..4088b1d22 --- /dev/null +++ b/crates/ra_hir_ty/src/tests/display_source_code.rs | |||
@@ -0,0 +1,50 @@ | |||
1 | use super::displayed_source_at_pos; | ||
2 | use crate::test_db::TestDB; | ||
3 | use ra_db::fixture::WithFixture; | ||
4 | |||
5 | #[test] | ||
6 | fn qualify_path_to_submodule() { | ||
7 | let (db, pos) = TestDB::with_position( | ||
8 | r#" | ||
9 | //- /main.rs | ||
10 | |||
11 | mod foo { | ||
12 | pub struct Foo; | ||
13 | } | ||
14 | |||
15 | fn bar() { | ||
16 | let foo: foo::Foo = foo::Foo; | ||
17 | foo<|> | ||
18 | } | ||
19 | |||
20 | "#, | ||
21 | ); | ||
22 | assert_eq!("foo::Foo", displayed_source_at_pos(&db, pos)); | ||
23 | } | ||
24 | |||
25 | #[test] | ||
26 | fn omit_default_type_parameters() { | ||
27 | let (db, pos) = TestDB::with_position( | ||
28 | r" | ||
29 | //- /main.rs | ||
30 | struct Foo<T = u8> { t: T } | ||
31 | fn main() { | ||
32 | let foo = Foo { t: 5 }; | ||
33 | foo<|>; | ||
34 | } | ||
35 | ", | ||
36 | ); | ||
37 | assert_eq!("Foo", displayed_source_at_pos(&db, pos)); | ||
38 | |||
39 | let (db, pos) = TestDB::with_position( | ||
40 | r" | ||
41 | //- /main.rs | ||
42 | struct Foo<K, T = u8> { k: K, t: T } | ||
43 | fn main() { | ||
44 | let foo = Foo { k: 400, t: 5 }; | ||
45 | foo<|>; | ||
46 | } | ||
47 | ", | ||
48 | ); | ||
49 | assert_eq!("Foo<i32>", displayed_source_at_pos(&db, pos)); | ||
50 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 5ddecbdc6..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() { | |||
197 | !0..6 '1isize': isize | 197 | !0..6 '1isize': isize |
198 | !0..6 '1isize': isize | 198 | !0..6 '1isize': isize |
199 | !0..6 '1isize': isize | 199 | !0..6 '1isize': isize |
200 | 54..457 '{ ...!(); }': ! | 200 | 54..457 '{ ...!(); }': () |
201 | 88..109 'spam!(...am!())': {unknown} | 201 | 88..109 'spam!(...am!())': {unknown} |
202 | 115..134 'for _ ...!() {}': () | 202 | 115..134 'for _ ...!() {}': () |
203 | 119..120 '_': {unknown} | 203 | 119..120 '_': {unknown} |
@@ -269,7 +269,7 @@ fn test() { S.foo()<|>; } | |||
269 | } | 269 | } |
270 | 270 | ||
271 | #[test] | 271 | #[test] |
272 | fn infer_impl_items_generated_by_macros() { | 272 | fn infer_assoc_items_generated_by_macros() { |
273 | let t = type_at( | 273 | let t = type_at( |
274 | r#" | 274 | r#" |
275 | //- /main.rs | 275 | //- /main.rs |
@@ -288,7 +288,7 @@ fn test() { S.foo()<|>; } | |||
288 | } | 288 | } |
289 | 289 | ||
290 | #[test] | 290 | #[test] |
291 | fn infer_impl_items_generated_by_macros_chain() { | 291 | fn infer_assoc_items_generated_by_macros_chain() { |
292 | let t = type_at( | 292 | let t = type_at( |
293 | r#" | 293 | r#" |
294 | //- /main.rs | 294 | //- /main.rs |
@@ -339,6 +339,46 @@ pub fn baz() -> usize { 31usize } | |||
339 | } | 339 | } |
340 | 340 | ||
341 | #[test] | 341 | #[test] |
342 | fn infer_macro_with_dollar_crate_is_correct_in_trait_associate_type() { | ||
343 | let (db, pos) = TestDB::with_position( | ||
344 | r#" | ||
345 | //- /main.rs crate:main deps:foo | ||
346 | use foo::Trait; | ||
347 | |||
348 | fn test() { | ||
349 | let msg = foo::Message(foo::MessageRef); | ||
350 | let r = msg.deref(); | ||
351 | r<|>; | ||
352 | } | ||
353 | |||
354 | //- /lib.rs crate:foo | ||
355 | pub struct MessageRef; | ||
356 | pub struct Message(MessageRef); | ||
357 | |||
358 | pub trait Trait { | ||
359 | type Target; | ||
360 | fn deref(&self) -> &Self::Target; | ||
361 | } | ||
362 | |||
363 | #[macro_export] | ||
364 | macro_rules! expand { | ||
365 | () => { | ||
366 | impl Trait for Message { | ||
367 | type Target = $crate::MessageRef; | ||
368 | fn deref(&self) -> &Self::Target { | ||
369 | &self.0 | ||
370 | } | ||
371 | } | ||
372 | } | ||
373 | } | ||
374 | |||
375 | expand!(); | ||
376 | "#, | ||
377 | ); | ||
378 | assert_eq!("&MessageRef", type_at_pos(&db, pos)); | ||
379 | } | ||
380 | |||
381 | #[test] | ||
342 | fn infer_type_value_non_legacy_macro_use_as() { | 382 | fn infer_type_value_non_legacy_macro_use_as() { |
343 | assert_snapshot!( | 383 | assert_snapshot!( |
344 | infer(r#" | 384 | infer(r#" |
@@ -388,6 +428,32 @@ fn main() { | |||
388 | } | 428 | } |
389 | 429 | ||
390 | #[test] | 430 | #[test] |
431 | fn infer_local_inner_macros() { | ||
432 | let (db, pos) = TestDB::with_position( | ||
433 | r#" | ||
434 | //- /main.rs crate:main deps:foo | ||
435 | fn test() { | ||
436 | let x = foo::foo!(1); | ||
437 | x<|>; | ||
438 | } | ||
439 | |||
440 | //- /lib.rs crate:foo | ||
441 | #[macro_export(local_inner_macros)] | ||
442 | macro_rules! foo { | ||
443 | (1) => { bar!() }; | ||
444 | } | ||
445 | |||
446 | #[macro_export] | ||
447 | macro_rules! bar { | ||
448 | () => { 42 } | ||
449 | } | ||
450 | |||
451 | "#, | ||
452 | ); | ||
453 | assert_eq!("i32", type_at_pos(&db, pos)); | ||
454 | } | ||
455 | |||
456 | #[test] | ||
391 | fn infer_builtin_macros_line() { | 457 | fn infer_builtin_macros_line() { |
392 | assert_snapshot!( | 458 | assert_snapshot!( |
393 | infer(r#" | 459 | infer(r#" |
diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index ab87f598a..558a70022 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> [T] { | |||
17 | #[lang = "slice_alloc"] | 17 | #[lang = "slice_alloc"] |
18 | impl<T> [T] {} | 18 | impl<T> [T] {} |
19 | 19 | ||
20 | fn test() { | 20 | fn test(x: &[u8]) { |
21 | <[_]>::foo(b"foo"); | 21 | <[_]>::foo(x); |
22 | } | 22 | } |
23 | "#), | 23 | "#), |
24 | @r###" | 24 | @r###" |
@@ -26,10 +26,11 @@ fn test() { | |||
26 | 56..79 '{ ... }': T | 26 | 56..79 '{ ... }': T |
27 | 66..73 'loop {}': ! | 27 | 66..73 'loop {}': ! |
28 | 71..73 '{}': () | 28 | 71..73 '{}': () |
29 | 133..160 '{ ...o"); }': () | 29 | 131..132 'x': &[u8] |
30 | 139..149 '<[_]>::foo': fn foo<u8>(&[u8]) -> u8 | 30 | 141..163 '{ ...(x); }': () |
31 | 139..157 '<[_]>:..."foo")': u8 | 31 | 147..157 '<[_]>::foo': fn foo<u8>(&[u8]) -> u8 |
32 | 150..156 'b"foo"': &[u8] | 32 | 147..160 '<[_]>::foo(x)': u8 |
33 | 158..159 'x': &[u8] | ||
33 | "### | 34 | "### |
34 | ); | 35 | ); |
35 | } | 36 | } |
@@ -983,7 +984,7 @@ fn test() { S2.into()<|>; } | |||
983 | 984 | ||
984 | #[test] | 985 | #[test] |
985 | fn method_resolution_overloaded_method() { | 986 | fn method_resolution_overloaded_method() { |
986 | test_utils::covers!(impl_self_type_match_without_receiver); | 987 | test_utils::mark::check!(impl_self_type_match_without_receiver); |
987 | let t = type_at( | 988 | let t = type_at( |
988 | r#" | 989 | r#" |
989 | //- main.rs | 990 | //- main.rs |
@@ -1095,3 +1096,34 @@ fn test() { (S {}).method()<|>; } | |||
1095 | ); | 1096 | ); |
1096 | assert_eq!(t, "()"); | 1097 | assert_eq!(t, "()"); |
1097 | } | 1098 | } |
1099 | |||
1100 | #[test] | ||
1101 | fn dyn_trait_super_trait_not_in_scope() { | ||
1102 | assert_snapshot!( | ||
1103 | infer(r#" | ||
1104 | mod m { | ||
1105 | pub trait SuperTrait { | ||
1106 | fn foo(&self) -> u32 { 0 } | ||
1107 | } | ||
1108 | } | ||
1109 | trait Trait: m::SuperTrait {} | ||
1110 | |||
1111 | struct S; | ||
1112 | impl m::SuperTrait for S {} | ||
1113 | impl Trait for S {} | ||
1114 | |||
1115 | fn test(d: &dyn Trait) { | ||
1116 | d.foo(); | ||
1117 | } | ||
1118 | "#), | ||
1119 | @r###" | ||
1120 | 52..56 'self': &Self | ||
1121 | 65..70 '{ 0 }': u32 | ||
1122 | 67..68 '0': u32 | ||
1123 | 177..178 'd': &dyn Trait | ||
1124 | 192..208 '{ ...o(); }': () | ||
1125 | 198..199 'd': &dyn Trait | ||
1126 | 198..205 'd.foo()': u32 | ||
1127 | "### | ||
1128 | ); | ||
1129 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/never_type.rs b/crates/ra_hir_ty/src/tests/never_type.rs index a77209480..082c47208 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 @@ | |||
1 | use super::type_at; | 1 | use insta::assert_snapshot; |
2 | |||
3 | use super::{infer_with_mismatches, type_at}; | ||
2 | 4 | ||
3 | #[test] | 5 | #[test] |
4 | fn infer_never1() { | 6 | fn infer_never1() { |
@@ -261,3 +263,176 @@ fn test(a: i32) { | |||
261 | ); | 263 | ); |
262 | assert_eq!(t, "f64"); | 264 | assert_eq!(t, "f64"); |
263 | } | 265 | } |
266 | |||
267 | #[test] | ||
268 | fn diverging_expression_1() { | ||
269 | let t = infer_with_mismatches( | ||
270 | r#" | ||
271 | //- /main.rs | ||
272 | fn test1() { | ||
273 | let x: u32 = return; | ||
274 | } | ||
275 | fn test2() { | ||
276 | let x: u32 = { return; }; | ||
277 | } | ||
278 | fn test3() { | ||
279 | let x: u32 = loop {}; | ||
280 | } | ||
281 | fn test4() { | ||
282 | let x: u32 = { loop {} }; | ||
283 | } | ||
284 | fn test5() { | ||
285 | let x: u32 = { if true { loop {}; } else { loop {}; } }; | ||
286 | } | ||
287 | fn test6() { | ||
288 | let x: u32 = { let y: u32 = { loop {}; }; }; | ||
289 | } | ||
290 | "#, | ||
291 | true, | ||
292 | ); | ||
293 | assert_snapshot!(t, @r###" | ||
294 | 25..53 '{ ...urn; }': () | ||
295 | 35..36 'x': u32 | ||
296 | 44..50 'return': ! | ||
297 | 65..98 '{ ...; }; }': () | ||
298 | 75..76 'x': u32 | ||
299 | 84..95 '{ return; }': u32 | ||
300 | 86..92 'return': ! | ||
301 | 110..139 '{ ... {}; }': () | ||
302 | 120..121 'x': u32 | ||
303 | 129..136 'loop {}': ! | ||
304 | 134..136 '{}': () | ||
305 | 151..184 '{ ...} }; }': () | ||
306 | 161..162 'x': u32 | ||
307 | 170..181 '{ loop {} }': u32 | ||
308 | 172..179 'loop {}': ! | ||
309 | 177..179 '{}': () | ||
310 | 196..260 '{ ...} }; }': () | ||
311 | 206..207 'x': u32 | ||
312 | 215..257 '{ if t...}; } }': u32 | ||
313 | 217..255 'if tru... {}; }': u32 | ||
314 | 220..224 'true': bool | ||
315 | 225..237 '{ loop {}; }': u32 | ||
316 | 227..234 'loop {}': ! | ||
317 | 232..234 '{}': () | ||
318 | 243..255 '{ loop {}; }': u32 | ||
319 | 245..252 'loop {}': ! | ||
320 | 250..252 '{}': () | ||
321 | 272..324 '{ ...; }; }': () | ||
322 | 282..283 'x': u32 | ||
323 | 291..321 '{ let ...; }; }': u32 | ||
324 | 297..298 'y': u32 | ||
325 | 306..318 '{ loop {}; }': u32 | ||
326 | 308..315 'loop {}': ! | ||
327 | 313..315 '{}': () | ||
328 | "###); | ||
329 | } | ||
330 | |||
331 | #[test] | ||
332 | fn diverging_expression_2() { | ||
333 | let t = infer_with_mismatches( | ||
334 | r#" | ||
335 | //- /main.rs | ||
336 | fn test1() { | ||
337 | // should give type mismatch | ||
338 | let x: u32 = { loop {}; "foo" }; | ||
339 | } | ||
340 | "#, | ||
341 | true, | ||
342 | ); | ||
343 | assert_snapshot!(t, @r###" | ||
344 | 25..98 '{ ..." }; }': () | ||
345 | 68..69 'x': u32 | ||
346 | 77..95 '{ loop...foo" }': &str | ||
347 | 79..86 'loop {}': ! | ||
348 | 84..86 '{}': () | ||
349 | 88..93 '"foo"': &str | ||
350 | 77..95: expected u32, got &str | ||
351 | 88..93: expected u32, got &str | ||
352 | "###); | ||
353 | } | ||
354 | |||
355 | #[test] | ||
356 | fn diverging_expression_3_break() { | ||
357 | let t = infer_with_mismatches( | ||
358 | r#" | ||
359 | //- /main.rs | ||
360 | fn test1() { | ||
361 | // should give type mismatch | ||
362 | let x: u32 = { loop { break; } }; | ||
363 | } | ||
364 | fn test2() { | ||
365 | // should give type mismatch | ||
366 | let x: u32 = { for a in b { break; }; }; | ||
367 | // should give type mismatch as well | ||
368 | let x: u32 = { for a in b {}; }; | ||
369 | // should give type mismatch as well | ||
370 | let x: u32 = { for a in b { return; }; }; | ||
371 | } | ||
372 | fn test3() { | ||
373 | // should give type mismatch | ||
374 | let x: u32 = { while true { break; }; }; | ||
375 | // should give type mismatch as well -- there's an implicit break, even if it's never hit | ||
376 | let x: u32 = { while true {}; }; | ||
377 | // should give type mismatch as well | ||
378 | let x: u32 = { while true { return; }; }; | ||
379 | } | ||
380 | "#, | ||
381 | true, | ||
382 | ); | ||
383 | assert_snapshot!(t, @r###" | ||
384 | 25..99 '{ ...} }; }': () | ||
385 | 68..69 'x': u32 | ||
386 | 77..96 '{ loop...k; } }': () | ||
387 | 79..94 'loop { break; }': () | ||
388 | 84..94 '{ break; }': () | ||
389 | 86..91 'break': ! | ||
390 | 77..96: expected u32, got () | ||
391 | 79..94: expected u32, got () | ||
392 | 111..357 '{ ...; }; }': () | ||
393 | 154..155 'x': u32 | ||
394 | 163..189 '{ for ...; }; }': () | ||
395 | 165..186 'for a ...eak; }': () | ||
396 | 169..170 'a': {unknown} | ||
397 | 174..175 'b': {unknown} | ||
398 | 176..186 '{ break; }': () | ||
399 | 178..183 'break': ! | ||
400 | 240..241 'x': u32 | ||
401 | 249..267 '{ for ... {}; }': () | ||
402 | 251..264 'for a in b {}': () | ||
403 | 255..256 'a': {unknown} | ||
404 | 260..261 'b': {unknown} | ||
405 | 262..264 '{}': () | ||
406 | 318..319 'x': u32 | ||
407 | 327..354 '{ for ...; }; }': () | ||
408 | 329..351 'for a ...urn; }': () | ||
409 | 333..334 'a': {unknown} | ||
410 | 338..339 'b': {unknown} | ||
411 | 340..351 '{ return; }': () | ||
412 | 342..348 'return': ! | ||
413 | 163..189: expected u32, got () | ||
414 | 249..267: expected u32, got () | ||
415 | 327..354: expected u32, got () | ||
416 | 369..668 '{ ...; }; }': () | ||
417 | 412..413 'x': u32 | ||
418 | 421..447 '{ whil...; }; }': () | ||
419 | 423..444 'while ...eak; }': () | ||
420 | 429..433 'true': bool | ||
421 | 434..444 '{ break; }': () | ||
422 | 436..441 'break': ! | ||
423 | 551..552 'x': u32 | ||
424 | 560..578 '{ whil... {}; }': () | ||
425 | 562..575 'while true {}': () | ||
426 | 568..572 'true': bool | ||
427 | 573..575 '{}': () | ||
428 | 629..630 'x': u32 | ||
429 | 638..665 '{ whil...; }; }': () | ||
430 | 640..662 'while ...urn; }': () | ||
431 | 646..650 'true': bool | ||
432 | 651..662 '{ return; }': () | ||
433 | 653..659 'return': ! | ||
434 | 421..447: expected u32, got () | ||
435 | 560..578: expected u32, got () | ||
436 | 638..665: expected u32, got () | ||
437 | "###); | ||
438 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs index af291092d..fe62587c0 100644 --- a/crates/ra_hir_ty/src/tests/patterns.rs +++ b/crates/ra_hir_ty/src/tests/patterns.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use insta::assert_snapshot; | 1 | use insta::assert_snapshot; |
2 | use test_utils::covers; | 2 | use test_utils::mark; |
3 | 3 | ||
4 | use super::{infer, infer_with_mismatches}; | 4 | use super::{infer, infer_with_mismatches}; |
5 | 5 | ||
@@ -197,7 +197,7 @@ fn test() { | |||
197 | 197 | ||
198 | #[test] | 198 | #[test] |
199 | fn infer_pattern_match_ergonomics_ref() { | 199 | fn infer_pattern_match_ergonomics_ref() { |
200 | covers!(match_ergonomics_ref); | 200 | mark::check!(match_ergonomics_ref); |
201 | assert_snapshot!( | 201 | assert_snapshot!( |
202 | infer(r#" | 202 | infer(r#" |
203 | fn test() { | 203 | fn test() { |
@@ -369,6 +369,45 @@ fn test() { | |||
369 | } | 369 | } |
370 | 370 | ||
371 | #[test] | 371 | #[test] |
372 | fn enum_variant_through_self_in_pattern() { | ||
373 | assert_snapshot!( | ||
374 | infer(r#" | ||
375 | enum E { | ||
376 | A { x: usize }, | ||
377 | B(usize), | ||
378 | C | ||
379 | } | ||
380 | |||
381 | impl E { | ||
382 | fn test() { | ||
383 | match (loop {}) { | ||
384 | Self::A { x } => { x; }, | ||
385 | Self::B(x) => { x; }, | ||
386 | Self::C => {}, | ||
387 | }; | ||
388 | } | ||
389 | } | ||
390 | "#), | ||
391 | @r###" | ||
392 | 76..218 '{ ... }': () | ||
393 | 86..211 'match ... }': () | ||
394 | 93..100 'loop {}': ! | ||
395 | 98..100 '{}': () | ||
396 | 116..129 'Self::A { x }': E | ||
397 | 126..127 'x': usize | ||
398 | 133..139 '{ x; }': () | ||
399 | 135..136 'x': usize | ||
400 | 153..163 'Self::B(x)': E | ||
401 | 161..162 'x': usize | ||
402 | 167..173 '{ x; }': () | ||
403 | 169..170 'x': usize | ||
404 | 187..194 'Self::C': E | ||
405 | 198..200 '{}': () | ||
406 | "### | ||
407 | ); | ||
408 | } | ||
409 | |||
410 | #[test] | ||
372 | fn infer_generics_in_patterns() { | 411 | fn infer_generics_in_patterns() { |
373 | assert_snapshot!( | 412 | assert_snapshot!( |
374 | infer(r#" | 413 | infer(r#" |
@@ -481,3 +520,53 @@ fn main() { | |||
481 | 105..107 '()': () | 520 | 105..107 '()': () |
482 | ") | 521 | ") |
483 | } | 522 | } |
523 | |||
524 | #[test] | ||
525 | fn match_ergonomics_in_closure_params() { | ||
526 | assert_snapshot!( | ||
527 | infer(r#" | ||
528 | #[lang = "fn_once"] | ||
529 | trait FnOnce<Args> { | ||
530 | type Output; | ||
531 | } | ||
532 | |||
533 | fn foo<T, U, F: FnOnce(T) -> U>(t: T, f: F) -> U { loop {} } | ||
534 | |||
535 | fn test() { | ||
536 | foo(&(1, "a"), |&(x, y)| x); // normal, no match ergonomics | ||
537 | foo(&(1, "a"), |(x, y)| x); | ||
538 | } | ||
539 | "#), | ||
540 | @r###" | ||
541 | 94..95 't': T | ||
542 | 100..101 'f': F | ||
543 | 111..122 '{ loop {} }': U | ||
544 | 113..120 'loop {}': ! | ||
545 | 118..120 '{}': () | ||
546 | 134..233 '{ ... x); }': () | ||
547 | 140..143 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32 | ||
548 | 140..167 'foo(&(...y)| x)': i32 | ||
549 | 144..153 '&(1, "a")': &(i32, &str) | ||
550 | 145..153 '(1, "a")': (i32, &str) | ||
551 | 146..147 '1': i32 | ||
552 | 149..152 '"a"': &str | ||
553 | 155..166 '|&(x, y)| x': |&(i32, &str)| -> i32 | ||
554 | 156..163 '&(x, y)': &(i32, &str) | ||
555 | 157..163 '(x, y)': (i32, &str) | ||
556 | 158..159 'x': i32 | ||
557 | 161..162 'y': &str | ||
558 | 165..166 'x': i32 | ||
559 | 204..207 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32 | ||
560 | 204..230 'foo(&(...y)| x)': &i32 | ||
561 | 208..217 '&(1, "a")': &(i32, &str) | ||
562 | 209..217 '(1, "a")': (i32, &str) | ||
563 | 210..211 '1': i32 | ||
564 | 213..216 '"a"': &str | ||
565 | 219..229 '|(x, y)| x': |&(i32, &str)| -> &i32 | ||
566 | 220..226 '(x, y)': (i32, &str) | ||
567 | 221..222 'x': &i32 | ||
568 | 224..225 'y': &&str | ||
569 | 228..229 'x': &i32 | ||
570 | "### | ||
571 | ); | ||
572 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs index 8a1292c7a..1f004bd63 100644 --- a/crates/ra_hir_ty/src/tests/regression.rs +++ b/crates/ra_hir_ty/src/tests/regression.rs | |||
@@ -1,9 +1,10 @@ | |||
1 | use insta::assert_snapshot; | 1 | use insta::assert_snapshot; |
2 | use test_utils::covers; | 2 | use ra_db::fixture::WithFixture; |
3 | use test_utils::mark; | ||
3 | 4 | ||
4 | use super::infer; | ||
5 | use crate::test_db::TestDB; | 5 | use crate::test_db::TestDB; |
6 | use ra_db::fixture::WithFixture; | 6 | |
7 | use super::infer; | ||
7 | 8 | ||
8 | #[test] | 9 | #[test] |
9 | fn bug_484() { | 10 | fn bug_484() { |
@@ -89,8 +90,8 @@ fn quux() { | |||
89 | 90 | ||
90 | #[test] | 91 | #[test] |
91 | fn recursive_vars() { | 92 | fn recursive_vars() { |
92 | covers!(type_var_cycles_resolve_completely); | 93 | mark::check!(type_var_cycles_resolve_completely); |
93 | covers!(type_var_cycles_resolve_as_possible); | 94 | mark::check!(type_var_cycles_resolve_as_possible); |
94 | assert_snapshot!( | 95 | assert_snapshot!( |
95 | infer(r#" | 96 | infer(r#" |
96 | fn test() { | 97 | fn test() { |
@@ -112,8 +113,6 @@ fn test() { | |||
112 | 113 | ||
113 | #[test] | 114 | #[test] |
114 | fn recursive_vars_2() { | 115 | fn recursive_vars_2() { |
115 | covers!(type_var_cycles_resolve_completely); | ||
116 | covers!(type_var_cycles_resolve_as_possible); | ||
117 | assert_snapshot!( | 116 | assert_snapshot!( |
118 | infer(r#" | 117 | infer(r#" |
119 | fn test() { | 118 | fn test() { |
@@ -170,7 +169,7 @@ fn write() { | |||
170 | 169 | ||
171 | #[test] | 170 | #[test] |
172 | fn infer_std_crash_2() { | 171 | fn infer_std_crash_2() { |
173 | covers!(type_var_resolves_to_int_var); | 172 | mark::check!(type_var_resolves_to_int_var); |
174 | // caused "equating two type variables, ...", taken from std | 173 | // caused "equating two type variables, ...", taken from std |
175 | assert_snapshot!( | 174 | assert_snapshot!( |
176 | infer(r#" | 175 | infer(r#" |
@@ -535,6 +534,66 @@ fn foo(b: Bar) { | |||
535 | } | 534 | } |
536 | 535 | ||
537 | #[test] | 536 | #[test] |
537 | fn issue_4235_name_conflicts() { | ||
538 | assert_snapshot!( | ||
539 | infer(r#" | ||
540 | struct FOO {} | ||
541 | static FOO:FOO = FOO {}; | ||
542 | |||
543 | impl FOO { | ||
544 | fn foo(&self) {} | ||
545 | } | ||
546 | |||
547 | fn main() { | ||
548 | let a = &FOO; | ||
549 | a.foo(); | ||
550 | } | ||
551 | "#), @r###" | ||
552 | 32..38 'FOO {}': FOO | ||
553 | 64..68 'self': &FOO | ||
554 | 70..72 '{}': () | ||
555 | 86..120 '{ ...o(); }': () | ||
556 | 96..97 'a': &FOO | ||
557 | 100..104 '&FOO': &FOO | ||
558 | 101..104 'FOO': FOO | ||
559 | 110..111 'a': &FOO | ||
560 | 110..117 'a.foo()': () | ||
561 | "### | ||
562 | ); | ||
563 | } | ||
564 | |||
565 | #[test] | ||
566 | fn issue_4465_dollar_crate_at_type() { | ||
567 | assert_snapshot!( | ||
568 | infer(r#" | ||
569 | pub struct Foo {} | ||
570 | pub fn anything<T>() -> T { | ||
571 | loop {} | ||
572 | } | ||
573 | macro_rules! foo { | ||
574 | () => {{ | ||
575 | let r: $crate::Foo = anything(); | ||
576 | r | ||
577 | }}; | ||
578 | } | ||
579 | fn main() { | ||
580 | let _a = foo!(); | ||
581 | } | ||
582 | "#), @r###" | ||
583 | 45..60 '{ loop {} }': T | ||
584 | 51..58 'loop {}': ! | ||
585 | 56..58 '{}': () | ||
586 | !0..31 '{letr:...g();r}': Foo | ||
587 | !4..5 'r': Foo | ||
588 | !18..26 'anything': fn anything<Foo>() -> Foo | ||
589 | !18..28 'anything()': Foo | ||
590 | !29..30 'r': Foo | ||
591 | 164..188 '{ ...!(); }': () | ||
592 | 174..176 '_a': Foo | ||
593 | "###); | ||
594 | } | ||
595 | |||
596 | #[test] | ||
538 | fn issue_4053_diesel_where_clauses() { | 597 | fn issue_4053_diesel_where_clauses() { |
539 | assert_snapshot!( | 598 | assert_snapshot!( |
540 | infer(r#" | 599 | infer(r#" |
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 56abc65b8..88309157b 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) { | |||
179 | 17..18 'b': isize | 179 | 17..18 'b': isize |
180 | 27..28 'c': ! | 180 | 27..28 'c': ! |
181 | 33..34 'd': &str | 181 | 33..34 'd': &str |
182 | 42..121 '{ ...f32; }': ! | 182 | 42..121 '{ ...f32; }': () |
183 | 48..49 'a': u32 | 183 | 48..49 'a': u32 |
184 | 55..56 'b': isize | 184 | 55..56 'b': isize |
185 | 62..63 'c': ! | 185 | 62..63 'c': ! |
@@ -385,6 +385,26 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { | |||
385 | } | 385 | } |
386 | 386 | ||
387 | #[test] | 387 | #[test] |
388 | fn infer_raw_ref() { | ||
389 | assert_snapshot!( | ||
390 | infer(r#" | ||
391 | fn test(a: i32) { | ||
392 | &raw mut a; | ||
393 | &raw const a; | ||
394 | } | ||
395 | "#), | ||
396 | @r###" | ||
397 | 9..10 'a': i32 | ||
398 | 17..54 '{ ...t a; }': () | ||
399 | 23..33 '&raw mut a': *mut i32 | ||
400 | 32..33 'a': i32 | ||
401 | 39..51 '&raw const a': *const i32 | ||
402 | 50..51 'a': i32 | ||
403 | "### | ||
404 | ); | ||
405 | } | ||
406 | |||
407 | #[test] | ||
388 | fn infer_literals() { | 408 | fn infer_literals() { |
389 | assert_snapshot!( | 409 | assert_snapshot!( |
390 | infer(r##" | 410 | infer(r##" |
@@ -414,7 +434,7 @@ fn test() { | |||
414 | 27..31 '5f32': f32 | 434 | 27..31 '5f32': f32 |
415 | 37..41 '5f64': f64 | 435 | 37..41 '5f64': f64 |
416 | 47..54 '"hello"': &str | 436 | 47..54 '"hello"': &str |
417 | 60..68 'b"bytes"': &[u8] | 437 | 60..68 'b"bytes"': &[u8; _] |
418 | 74..77 ''c'': char | 438 | 74..77 ''c'': char |
419 | 83..87 'b'b'': u8 | 439 | 83..87 'b'b'': u8 |
420 | 93..97 '3.14': f64 | 440 | 93..97 '3.14': f64 |
@@ -422,7 +442,7 @@ fn test() { | |||
422 | 113..118 'false': bool | 442 | 113..118 'false': bool |
423 | 124..128 'true': bool | 443 | 124..128 'true': bool |
424 | 134..202 'r#" ... "#': &str | 444 | 134..202 'r#" ... "#': &str |
425 | 208..218 'br#"yolo"#': &[u8] | 445 | 208..218 'br#"yolo"#': &[u8; _] |
426 | "### | 446 | "### |
427 | ); | 447 | ); |
428 | } | 448 | } |
@@ -576,6 +596,50 @@ impl S { | |||
576 | } | 596 | } |
577 | 597 | ||
578 | #[test] | 598 | #[test] |
599 | fn infer_self_as_path() { | ||
600 | assert_snapshot!( | ||
601 | infer(r#" | ||
602 | struct S1; | ||
603 | struct S2(isize); | ||
604 | enum E { | ||
605 | V1, | ||
606 | V2(u32), | ||
607 | } | ||
608 | |||
609 | impl S1 { | ||
610 | fn test() { | ||
611 | Self; | ||
612 | } | ||
613 | } | ||
614 | impl S2 { | ||
615 | fn test() { | ||
616 | Self(1); | ||
617 | } | ||
618 | } | ||
619 | impl E { | ||
620 | fn test() { | ||
621 | Self::V1; | ||
622 | Self::V2(1); | ||
623 | } | ||
624 | } | ||
625 | "#), | ||
626 | @r###" | ||
627 | 87..108 '{ ... }': () | ||
628 | 97..101 'Self': S1 | ||
629 | 135..159 '{ ... }': () | ||
630 | 145..149 'Self': S2(isize) -> S2 | ||
631 | 145..152 'Self(1)': S2 | ||
632 | 150..151 '1': isize | ||
633 | 185..231 '{ ... }': () | ||
634 | 195..203 'Self::V1': E | ||
635 | 213..221 'Self::V2': V2(u32) -> E | ||
636 | 213..224 'Self::V2(1)': E | ||
637 | 222..223 '1': u32 | ||
638 | "### | ||
639 | ); | ||
640 | } | ||
641 | |||
642 | #[test] | ||
579 | fn infer_binary_op() { | 643 | fn infer_binary_op() { |
580 | assert_snapshot!( | 644 | assert_snapshot!( |
581 | infer(r#" | 645 | infer(r#" |
@@ -893,7 +957,7 @@ fn main(foo: Foo) { | |||
893 | 51..107 'if tru... }': () | 957 | 51..107 'if tru... }': () |
894 | 54..58 'true': bool | 958 | 54..58 'true': bool |
895 | 59..67 '{ }': () | 959 | 59..67 '{ }': () |
896 | 73..107 'if fal... }': () | 960 | 73..107 'if fal... }': i32 |
897 | 76..81 'false': bool | 961 | 76..81 'false': bool |
898 | 82..107 '{ ... }': i32 | 962 | 82..107 '{ ... }': i32 |
899 | 92..95 'foo': Foo | 963 | 92..95 'foo': Foo |
@@ -935,7 +999,7 @@ fn foo() { | |||
935 | 29..33 'true': bool | 999 | 29..33 'true': bool |
936 | 34..51 '{ ... }': i32 | 1000 | 34..51 '{ ... }': i32 |
937 | 44..45 '1': i32 | 1001 | 44..45 '1': i32 |
938 | 57..80 '{ ... }': ! | 1002 | 57..80 '{ ... }': i32 |
939 | 67..73 'return': ! | 1003 | 67..73 'return': ! |
940 | 90..93 '_x2': i32 | 1004 | 90..93 '_x2': i32 |
941 | 96..149 'if tru... }': i32 | 1005 | 96..149 'if tru... }': i32 |
@@ -951,7 +1015,7 @@ fn foo() { | |||
951 | 186..190 'true': bool | 1015 | 186..190 'true': bool |
952 | 194..195 '3': i32 | 1016 | 194..195 '3': i32 |
953 | 205..206 '_': bool | 1017 | 205..206 '_': bool |
954 | 210..241 '{ ... }': ! | 1018 | 210..241 '{ ... }': i32 |
955 | 224..230 'return': ! | 1019 | 224..230 'return': ! |
956 | 257..260 '_x4': i32 | 1020 | 257..260 '_x4': i32 |
957 | 263..320 'match ... }': i32 | 1021 | 263..320 'match ... }': i32 |
@@ -1687,7 +1751,7 @@ fn foo() -> u32 { | |||
1687 | 17..59 '{ ...; }; }': () | 1751 | 17..59 '{ ...; }; }': () |
1688 | 27..28 'x': || -> usize | 1752 | 27..28 'x': || -> usize |
1689 | 31..56 '|| -> ...n 1; }': || -> usize | 1753 | 31..56 '|| -> ...n 1; }': || -> usize |
1690 | 43..56 '{ return 1; }': ! | 1754 | 43..56 '{ return 1; }': usize |
1691 | 45..53 'return 1': ! | 1755 | 45..53 'return 1': ! |
1692 | 52..53 '1': usize | 1756 | 52..53 '1': usize |
1693 | "### | 1757 | "### |
@@ -1706,7 +1770,7 @@ fn foo() -> u32 { | |||
1706 | 17..48 '{ ...; }; }': () | 1770 | 17..48 '{ ...; }; }': () |
1707 | 27..28 'x': || -> () | 1771 | 27..28 'x': || -> () |
1708 | 31..45 '|| { return; }': || -> () | 1772 | 31..45 '|| { return; }': || -> () |
1709 | 34..45 '{ return; }': ! | 1773 | 34..45 '{ return; }': () |
1710 | 36..42 'return': ! | 1774 | 36..42 'return': ! |
1711 | "### | 1775 | "### |
1712 | ); | 1776 | ); |
@@ -1755,3 +1819,181 @@ fn main() { | |||
1755 | "### | 1819 | "### |
1756 | ); | 1820 | ); |
1757 | } | 1821 | } |
1822 | |||
1823 | #[test] | ||
1824 | fn effects_smoke_test() { | ||
1825 | assert_snapshot!( | ||
1826 | infer(r#" | ||
1827 | fn main() { | ||
1828 | let x = unsafe { 92 }; | ||
1829 | let y = async { async { () }.await }; | ||
1830 | let z = try { () }; | ||
1831 | let t = 'a: { 92 }; | ||
1832 | } | ||
1833 | "#), | ||
1834 | @r###" | ||
1835 | 11..131 '{ ...2 }; }': () | ||
1836 | 21..22 'x': i32 | ||
1837 | 32..38 '{ 92 }': i32 | ||
1838 | 34..36 '92': i32 | ||
1839 | 48..49 'y': {unknown} | ||
1840 | 58..80 '{ asyn...wait }': {unknown} | ||
1841 | 60..78 'async ....await': {unknown} | ||
1842 | 66..72 '{ () }': () | ||
1843 | 68..70 '()': () | ||
1844 | 90..91 'z': {unknown} | ||
1845 | 94..104 'try { () }': {unknown} | ||
1846 | 98..104 '{ () }': () | ||
1847 | 100..102 '()': () | ||
1848 | 114..115 't': i32 | ||
1849 | 122..128 '{ 92 }': i32 | ||
1850 | 124..126 '92': i32 | ||
1851 | "### | ||
1852 | ) | ||
1853 | } | ||
1854 | |||
1855 | #[test] | ||
1856 | fn infer_generic_from_later_assignment() { | ||
1857 | assert_snapshot!( | ||
1858 | infer(r#" | ||
1859 | enum Option<T> { Some(T), None } | ||
1860 | use Option::*; | ||
1861 | |||
1862 | fn test() { | ||
1863 | let mut end = None; | ||
1864 | loop { | ||
1865 | end = Some(true); | ||
1866 | } | ||
1867 | } | ||
1868 | "#), | ||
1869 | @r###" | ||
1870 | 60..130 '{ ... } }': () | ||
1871 | 70..77 'mut end': Option<bool> | ||
1872 | 80..84 'None': Option<bool> | ||
1873 | 90..128 'loop {... }': ! | ||
1874 | 95..128 '{ ... }': () | ||
1875 | 105..108 'end': Option<bool> | ||
1876 | 105..121 'end = ...(true)': () | ||
1877 | 111..115 'Some': Some<bool>(bool) -> Option<bool> | ||
1878 | 111..121 'Some(true)': Option<bool> | ||
1879 | 116..120 'true': bool | ||
1880 | "### | ||
1881 | ); | ||
1882 | } | ||
1883 | |||
1884 | #[test] | ||
1885 | fn infer_loop_break_with_val() { | ||
1886 | assert_snapshot!( | ||
1887 | infer(r#" | ||
1888 | enum Option<T> { Some(T), None } | ||
1889 | use Option::*; | ||
1890 | |||
1891 | fn test() { | ||
1892 | let x = loop { | ||
1893 | if false { | ||
1894 | break None; | ||
1895 | } | ||
1896 | |||
1897 | break Some(true); | ||
1898 | }; | ||
1899 | } | ||
1900 | "#), | ||
1901 | @r###" | ||
1902 | 60..169 '{ ... }; }': () | ||
1903 | 70..71 'x': Option<bool> | ||
1904 | 74..166 'loop {... }': Option<bool> | ||
1905 | 79..166 '{ ... }': () | ||
1906 | 89..133 'if fal... }': () | ||
1907 | 92..97 'false': bool | ||
1908 | 98..133 '{ ... }': () | ||
1909 | 112..122 'break None': ! | ||
1910 | 118..122 'None': Option<bool> | ||
1911 | 143..159 'break ...(true)': ! | ||
1912 | 149..153 'Some': Some<bool>(bool) -> Option<bool> | ||
1913 | 149..159 'Some(true)': Option<bool> | ||
1914 | 154..158 'true': bool | ||
1915 | "### | ||
1916 | ); | ||
1917 | } | ||
1918 | |||
1919 | #[test] | ||
1920 | fn infer_loop_break_without_val() { | ||
1921 | assert_snapshot!( | ||
1922 | infer(r#" | ||
1923 | enum Option<T> { Some(T), None } | ||
1924 | use Option::*; | ||
1925 | |||
1926 | fn test() { | ||
1927 | let x = loop { | ||
1928 | if false { | ||
1929 | break; | ||
1930 | } | ||
1931 | }; | ||
1932 | } | ||
1933 | "#), | ||
1934 | @r###" | ||
1935 | 60..137 '{ ... }; }': () | ||
1936 | 70..71 'x': () | ||
1937 | 74..134 'loop {... }': () | ||
1938 | 79..134 '{ ... }': () | ||
1939 | 89..128 'if fal... }': () | ||
1940 | 92..97 'false': bool | ||
1941 | 98..128 '{ ... }': () | ||
1942 | 112..117 'break': ! | ||
1943 | "### | ||
1944 | ); | ||
1945 | } | ||
1946 | |||
1947 | #[test] | ||
1948 | fn infer_labelled_break_with_val() { | ||
1949 | assert_snapshot!( | ||
1950 | infer(r#" | ||
1951 | fn foo() { | ||
1952 | let _x = || 'outer: loop { | ||
1953 | let inner = 'inner: loop { | ||
1954 | let i = Default::default(); | ||
1955 | if (break 'outer i) { | ||
1956 | loop { break 'inner 5i8; }; | ||
1957 | } else if true { | ||
1958 | break 'inner 6; | ||
1959 | } | ||
1960 | break 7; | ||
1961 | }; | ||
1962 | break inner < 8; | ||
1963 | }; | ||
1964 | } | ||
1965 | "#), | ||
1966 | @r###" | ||
1967 | 10..336 '{ ... }; }': () | ||
1968 | 20..22 '_x': || -> bool | ||
1969 | 25..333 '|| 'ou... }': || -> bool | ||
1970 | 28..333 ''outer... }': bool | ||
1971 | 41..333 '{ ... }': () | ||
1972 | 55..60 'inner': i8 | ||
1973 | 63..301 ''inner... }': i8 | ||
1974 | 76..301 '{ ... }': () | ||
1975 | 94..95 'i': bool | ||
1976 | 98..114 'Defaul...efault': {unknown} | ||
1977 | 98..116 'Defaul...ault()': bool | ||
1978 | 130..270 'if (br... }': () | ||
1979 | 134..148 'break 'outer i': ! | ||
1980 | 147..148 'i': bool | ||
1981 | 150..209 '{ ... }': () | ||
1982 | 168..194 'loop {...5i8; }': ! | ||
1983 | 173..194 '{ brea...5i8; }': () | ||
1984 | 175..191 'break ...er 5i8': ! | ||
1985 | 188..191 '5i8': i8 | ||
1986 | 215..270 'if tru... }': () | ||
1987 | 218..222 'true': bool | ||
1988 | 223..270 '{ ... }': () | ||
1989 | 241..255 'break 'inner 6': ! | ||
1990 | 254..255 '6': i8 | ||
1991 | 283..290 'break 7': ! | ||
1992 | 289..290 '7': i8 | ||
1993 | 311..326 'break inner < 8': ! | ||
1994 | 317..322 'inner': i8 | ||
1995 | 317..326 'inner < 8': bool | ||
1996 | 325..326 '8': i8 | ||
1997 | "### | ||
1998 | ); | ||
1999 | } | ||
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs index e555c879a..e8778d419 100644 --- a/crates/ra_hir_ty/src/tests/traits.rs +++ b/crates/ra_hir_ty/src/tests/traits.rs | |||
@@ -1,10 +1,11 @@ | |||
1 | use insta::assert_snapshot; | 1 | use insta::assert_snapshot; |
2 | |||
3 | use ra_db::fixture::WithFixture; | 2 | use ra_db::fixture::WithFixture; |
3 | use test_utils::mark; | ||
4 | 4 | ||
5 | use super::{infer, infer_with_mismatches, type_at, type_at_pos}; | ||
6 | use crate::test_db::TestDB; | 5 | use crate::test_db::TestDB; |
7 | 6 | ||
7 | use super::{infer, infer_with_mismatches, type_at, type_at_pos}; | ||
8 | |||
8 | #[test] | 9 | #[test] |
9 | fn infer_await() { | 10 | fn infer_await() { |
10 | let (db, pos) = TestDB::with_position( | 11 | let (db, pos) = TestDB::with_position( |
@@ -301,7 +302,7 @@ fn test() { | |||
301 | 302 | ||
302 | #[test] | 303 | #[test] |
303 | fn trait_default_method_self_bound_implements_trait() { | 304 | fn trait_default_method_self_bound_implements_trait() { |
304 | test_utils::covers!(trait_self_implements_self); | 305 | mark::check!(trait_self_implements_self); |
305 | assert_snapshot!( | 306 | assert_snapshot!( |
306 | infer(r#" | 307 | infer(r#" |
307 | trait Trait { | 308 | trait Trait { |
@@ -324,7 +325,6 @@ trait Trait { | |||
324 | 325 | ||
325 | #[test] | 326 | #[test] |
326 | fn trait_default_method_self_bound_implements_super_trait() { | 327 | fn trait_default_method_self_bound_implements_super_trait() { |
327 | test_utils::covers!(trait_self_implements_self); | ||
328 | assert_snapshot!( | 328 | assert_snapshot!( |
329 | infer(r#" | 329 | infer(r#" |
330 | trait SuperTrait { | 330 | trait SuperTrait { |
@@ -1617,6 +1617,138 @@ fn test<F: FnOnce(u32, u64) -> u128>(f: F) { | |||
1617 | } | 1617 | } |
1618 | 1618 | ||
1619 | #[test] | 1619 | #[test] |
1620 | fn fn_ptr_and_item() { | ||
1621 | assert_snapshot!( | ||
1622 | infer(r#" | ||
1623 | #[lang="fn_once"] | ||
1624 | trait FnOnce<Args> { | ||
1625 | type Output; | ||
1626 | |||
1627 | fn call_once(self, args: Args) -> Self::Output; | ||
1628 | } | ||
1629 | |||
1630 | trait Foo<T> { | ||
1631 | fn foo(&self) -> T; | ||
1632 | } | ||
1633 | |||
1634 | struct Bar<T>(T); | ||
1635 | |||
1636 | impl<A1, R, F: FnOnce(A1) -> R> Foo<(A1, R)> for Bar<F> { | ||
1637 | fn foo(&self) -> (A1, R) {} | ||
1638 | } | ||
1639 | |||
1640 | enum Opt<T> { None, Some(T) } | ||
1641 | impl<T> Opt<T> { | ||
1642 | fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Opt<U> {} | ||
1643 | } | ||
1644 | |||
1645 | fn test() { | ||
1646 | let bar: Bar<fn(u8) -> u32>; | ||
1647 | bar.foo(); | ||
1648 | |||
1649 | let opt: Opt<u8>; | ||
1650 | let f: fn(u8) -> u32; | ||
1651 | opt.map(f); | ||
1652 | } | ||
1653 | "#), | ||
1654 | @r###" | ||
1655 | 75..79 'self': Self | ||
1656 | 81..85 'args': Args | ||
1657 | 140..144 'self': &Self | ||
1658 | 244..248 'self': &Bar<F> | ||
1659 | 261..263 '{}': () | ||
1660 | 347..351 'self': Opt<T> | ||
1661 | 353..354 'f': F | ||
1662 | 369..371 '{}': () | ||
1663 | 385..501 '{ ...(f); }': () | ||
1664 | 395..398 'bar': Bar<fn(u8) -> u32> | ||
1665 | 424..427 'bar': Bar<fn(u8) -> u32> | ||
1666 | 424..433 'bar.foo()': {unknown} | ||
1667 | 444..447 'opt': Opt<u8> | ||
1668 | 466..467 'f': fn(u8) -> u32 | ||
1669 | 488..491 'opt': Opt<u8> | ||
1670 | 488..498 'opt.map(f)': Opt<FnOnce::Output<fn(u8) -> u32, (u8,)>> | ||
1671 | 496..497 'f': fn(u8) -> u32 | ||
1672 | "### | ||
1673 | ); | ||
1674 | } | ||
1675 | |||
1676 | #[test] | ||
1677 | fn fn_trait_deref_with_ty_default() { | ||
1678 | assert_snapshot!( | ||
1679 | infer(r#" | ||
1680 | #[lang = "deref"] | ||
1681 | trait Deref { | ||
1682 | type Target; | ||
1683 | |||
1684 | fn deref(&self) -> &Self::Target; | ||
1685 | } | ||
1686 | |||
1687 | #[lang="fn_once"] | ||
1688 | trait FnOnce<Args> { | ||
1689 | type Output; | ||
1690 | |||
1691 | fn call_once(self, args: Args) -> Self::Output; | ||
1692 | } | ||
1693 | |||
1694 | struct Foo; | ||
1695 | |||
1696 | impl Foo { | ||
1697 | fn foo(&self) -> usize {} | ||
1698 | } | ||
1699 | |||
1700 | struct Lazy<T, F = fn() -> T>(F); | ||
1701 | |||
1702 | impl<T, F> Lazy<T, F> { | ||
1703 | pub fn new(f: F) -> Lazy<T, F> {} | ||
1704 | } | ||
1705 | |||
1706 | impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> { | ||
1707 | type Target = T; | ||
1708 | } | ||
1709 | |||
1710 | fn test() { | ||
1711 | let lazy1: Lazy<Foo, _> = Lazy::new(|| Foo); | ||
1712 | let r1 = lazy1.foo(); | ||
1713 | |||
1714 | fn make_foo_fn() -> Foo {} | ||
1715 | let make_foo_fn_ptr: fn() -> Foo = make_foo_fn; | ||
1716 | let lazy2: Lazy<Foo, _> = Lazy::new(make_foo_fn_ptr); | ||
1717 | let r2 = lazy2.foo(); | ||
1718 | } | ||
1719 | "#), | ||
1720 | @r###" | ||
1721 | 65..69 'self': &Self | ||
1722 | 166..170 'self': Self | ||
1723 | 172..176 'args': Args | ||
1724 | 240..244 'self': &Foo | ||
1725 | 255..257 '{}': () | ||
1726 | 335..336 'f': F | ||
1727 | 355..357 '{}': () | ||
1728 | 444..690 '{ ...o(); }': () | ||
1729 | 454..459 'lazy1': Lazy<Foo, fn() -> T> | ||
1730 | 476..485 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T> | ||
1731 | 476..493 'Lazy::...| Foo)': Lazy<Foo, fn() -> T> | ||
1732 | 486..492 '|| Foo': || -> T | ||
1733 | 489..492 'Foo': Foo | ||
1734 | 503..505 'r1': {unknown} | ||
1735 | 508..513 'lazy1': Lazy<Foo, fn() -> T> | ||
1736 | 508..519 'lazy1.foo()': {unknown} | ||
1737 | 561..576 'make_foo_fn_ptr': fn() -> Foo | ||
1738 | 592..603 'make_foo_fn': fn make_foo_fn() -> Foo | ||
1739 | 613..618 'lazy2': Lazy<Foo, fn() -> T> | ||
1740 | 635..644 'Lazy::new': fn new<Foo, fn() -> T>(fn() -> T) -> Lazy<Foo, fn() -> T> | ||
1741 | 635..661 'Lazy::...n_ptr)': Lazy<Foo, fn() -> T> | ||
1742 | 645..660 'make_foo_fn_ptr': fn() -> Foo | ||
1743 | 671..673 'r2': {unknown} | ||
1744 | 676..681 'lazy2': Lazy<Foo, fn() -> T> | ||
1745 | 676..687 'lazy2.foo()': {unknown} | ||
1746 | 550..552 '{}': () | ||
1747 | "### | ||
1748 | ); | ||
1749 | } | ||
1750 | |||
1751 | #[test] | ||
1620 | fn closure_1() { | 1752 | fn closure_1() { |
1621 | assert_snapshot!( | 1753 | assert_snapshot!( |
1622 | infer(r#" | 1754 | infer(r#" |
@@ -2055,7 +2187,7 @@ fn test<I: Iterator<Item: Iterator<Item = u32>>>() { | |||
2055 | #[test] | 2187 | #[test] |
2056 | fn proc_macro_server_types() { | 2188 | fn proc_macro_server_types() { |
2057 | assert_snapshot!( | 2189 | assert_snapshot!( |
2058 | infer_with_mismatches(r#" | 2190 | infer(r#" |
2059 | macro_rules! with_api { | 2191 | macro_rules! with_api { |
2060 | ($S:ident, $self:ident, $m:ident) => { | 2192 | ($S:ident, $self:ident, $m:ident) => { |
2061 | $m! { | 2193 | $m! { |
@@ -2069,9 +2201,9 @@ macro_rules! with_api { | |||
2069 | } | 2201 | } |
2070 | macro_rules! associated_item { | 2202 | macro_rules! associated_item { |
2071 | (type TokenStream) => | 2203 | (type TokenStream) => |
2072 | (type TokenStream: 'static + Clone;); | 2204 | (type TokenStream: 'static;); |
2073 | (type Group) => | 2205 | (type Group) => |
2074 | (type Group: 'static + Clone;); | 2206 | (type Group: 'static;); |
2075 | ($($item:tt)*) => ($($item)*;) | 2207 | ($($item:tt)*) => ($($item)*;) |
2076 | } | 2208 | } |
2077 | macro_rules! declare_server_traits { | 2209 | macro_rules! declare_server_traits { |
@@ -2083,21 +2215,23 @@ macro_rules! declare_server_traits { | |||
2083 | } | 2215 | } |
2084 | 2216 | ||
2085 | $(pub trait $name: Types { | 2217 | $(pub trait $name: Types { |
2086 | $(associated_item!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)* | 2218 | $(associated_item!(fn $method($($arg: $arg_ty),*) $(-> $ret_ty)?);)* |
2087 | })* | 2219 | })* |
2088 | 2220 | ||
2089 | pub trait Server: Types $(+ $name)* {} | 2221 | pub trait Server: Types $(+ $name)* {} |
2090 | impl<S: Types $(+ $name)*> Server for S {} | 2222 | impl<S: Types $(+ $name)*> Server for S {} |
2091 | } | 2223 | } |
2092 | } | 2224 | } |
2225 | |||
2093 | with_api!(Self, self_, declare_server_traits); | 2226 | with_api!(Self, self_, declare_server_traits); |
2094 | struct Group {} | 2227 | struct G {} |
2095 | struct TokenStream {} | 2228 | struct T {} |
2096 | struct Rustc; | 2229 | struct Rustc; |
2097 | impl Types for Rustc { | 2230 | impl Types for Rustc { |
2098 | type TokenStream = TokenStream; | 2231 | type TokenStream = T; |
2099 | type Group = Group; | 2232 | type Group = G; |
2100 | } | 2233 | } |
2234 | |||
2101 | fn make<T>() -> T { loop {} } | 2235 | fn make<T>() -> T { loop {} } |
2102 | impl TokenStream for Rustc { | 2236 | impl TokenStream for Rustc { |
2103 | fn new() -> Self::TokenStream { | 2237 | fn new() -> Self::TokenStream { |
@@ -2105,17 +2239,17 @@ impl TokenStream for Rustc { | |||
2105 | make() | 2239 | make() |
2106 | } | 2240 | } |
2107 | } | 2241 | } |
2108 | "#, true), | 2242 | "#), |
2109 | @r###" | 2243 | @r###" |
2110 | 1115..1126 '{ loop {} }': T | 2244 | 1062..1073 '{ loop {} }': T |
2111 | 1117..1124 'loop {}': ! | 2245 | 1064..1071 'loop {}': ! |
2112 | 1122..1124 '{}': () | 2246 | 1069..1071 '{}': () |
2113 | 1190..1253 '{ ... }': {unknown} | 2247 | 1137..1200 '{ ... }': T |
2114 | 1204..1209 'group': {unknown} | 2248 | 1151..1156 'group': G |
2115 | 1225..1229 'make': fn make<{unknown}>() -> {unknown} | 2249 | 1172..1176 'make': fn make<G>() -> G |
2116 | 1225..1231 'make()': {unknown} | 2250 | 1172..1178 'make()': G |
2117 | 1241..1245 'make': fn make<{unknown}>() -> {unknown} | 2251 | 1188..1192 'make': fn make<T>() -> T |
2118 | 1241..1247 'make()': {unknown} | 2252 | 1188..1194 'make()': T |
2119 | "### | 2253 | "### |
2120 | ); | 2254 | ); |
2121 | } | 2255 | } |
@@ -2468,3 +2602,199 @@ fn test(x: &dyn Foo) { | |||
2468 | "### | 2602 | "### |
2469 | ); | 2603 | ); |
2470 | } | 2604 | } |
2605 | |||
2606 | #[test] | ||
2607 | fn builtin_copy() { | ||
2608 | assert_snapshot!( | ||
2609 | infer_with_mismatches(r#" | ||
2610 | #[lang = "copy"] | ||
2611 | trait Copy {} | ||
2612 | |||
2613 | struct IsCopy; | ||
2614 | impl Copy for IsCopy {} | ||