diff options
Diffstat (limited to 'crates/hir_ty')
29 files changed, 1459 insertions, 4185 deletions
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index 4b714c6d8..74129eb21 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -10,7 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = { version = "1.1", features = ["thread-local"] } | 13 | cov-mark = "2.0.0-pre.1" |
14 | itertools = "0.10.0" | 14 | itertools = "0.10.0" |
15 | arrayvec = "0.7" | 15 | arrayvec = "0.7" |
16 | smallvec = "1.2.0" | 16 | smallvec = "1.2.0" |
@@ -20,7 +20,7 @@ rustc-hash = "1.1.0" | |||
20 | scoped-tls = "1" | 20 | scoped-tls = "1" |
21 | chalk-solve = { version = "0.68", default-features = false } | 21 | chalk-solve = { version = "0.68", default-features = false } |
22 | chalk-ir = "0.68" | 22 | chalk-ir = "0.68" |
23 | chalk-recursive = "0.68" | 23 | chalk-recursive = { version = "0.68", default-features = false } |
24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
25 | once_cell = { version = "1.5.0" } | 25 | once_cell = { version = "1.5.0" } |
26 | 26 | ||
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs index 893e727c2..bb9d84246 100644 --- a/crates/hir_ty/src/builder.rs +++ b/crates/hir_ty/src/builder.rs | |||
@@ -202,7 +202,7 @@ impl<T: HasInterner<Interner = Interner> + Fold<Interner>> TyBuilder<Binders<T>> | |||
202 | 202 | ||
203 | impl TyBuilder<Binders<Ty>> { | 203 | impl TyBuilder<Binders<Ty>> { |
204 | pub fn def_ty(db: &dyn HirDatabase, def: TyDefId) -> TyBuilder<Binders<Ty>> { | 204 | pub fn def_ty(db: &dyn HirDatabase, def: TyDefId) -> TyBuilder<Binders<Ty>> { |
205 | TyBuilder::subst_binders(db.ty(def.into())) | 205 | TyBuilder::subst_binders(db.ty(def)) |
206 | } | 206 | } |
207 | 207 | ||
208 | pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> { | 208 | pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> { |
diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs index 4e042bf42..a4c09c742 100644 --- a/crates/hir_ty/src/chalk_db.rs +++ b/crates/hir_ty/src/chalk_db.rs | |||
@@ -10,16 +10,16 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; | |||
10 | use base_db::CrateId; | 10 | use base_db::CrateId; |
11 | use hir_def::{ | 11 | use hir_def::{ |
12 | lang_item::{lang_attr, LangItemTarget}, | 12 | lang_item::{lang_attr, LangItemTarget}, |
13 | AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId, | 13 | AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, ModuleId, TypeAliasId, |
14 | }; | 14 | }; |
15 | use hir_expand::name::name; | 15 | use hir_expand::name::name; |
16 | 16 | ||
17 | use crate::{ | 17 | use crate::{ |
18 | db::HirDatabase, | 18 | db::HirDatabase, |
19 | display::HirDisplay, | 19 | display::HirDisplay, |
20 | from_assoc_type_id, from_chalk_trait_id, make_only_type_binders, | 20 | from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_only_type_binders, |
21 | mapping::{from_chalk, ToChalk, TypeAliasAsValue}, | 21 | mapping::{from_chalk, ToChalk, TypeAliasAsValue}, |
22 | method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, | 22 | method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS}, |
23 | to_assoc_type_id, to_chalk_trait_id, | 23 | to_assoc_type_id, to_chalk_trait_id, |
24 | traits::ChalkContext, | 24 | traits::ChalkContext, |
25 | utils::generics, | 25 | utils::generics, |
@@ -105,12 +105,30 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
105 | _ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]), | 105 | _ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]), |
106 | }; | 106 | }; |
107 | 107 | ||
108 | fn local_impls(db: &dyn HirDatabase, module: ModuleId) -> Option<Arc<TraitImpls>> { | ||
109 | db.trait_impls_in_block(module.containing_block()?) | ||
110 | } | ||
111 | |||
108 | // Note: Since we're using impls_for_trait, only impls where the trait | 112 | // Note: Since we're using impls_for_trait, only impls where the trait |
109 | // can be resolved should ever reach Chalk. Symbol’s value as variable is void: impl_datum relies on that | 113 | // can be resolved should ever reach Chalk. impl_datum relies on that |
110 | // and will panic if the trait can't be resolved. | 114 | // and will panic if the trait can't be resolved. |
111 | let in_deps = self.db.trait_impls_in_deps(self.krate); | 115 | let in_deps = self.db.trait_impls_in_deps(self.krate); |
112 | let in_self = self.db.trait_impls_in_crate(self.krate); | 116 | let in_self = self.db.trait_impls_in_crate(self.krate); |
113 | let impl_maps = [in_deps, in_self]; | 117 | let trait_module = trait_.module(self.db.upcast()); |
118 | let type_module = match self_ty_fp { | ||
119 | Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())), | ||
120 | Some(TyFingerprint::ForeignType(type_id)) => { | ||
121 | Some(from_foreign_def_id(type_id).module(self.db.upcast())) | ||
122 | } | ||
123 | Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())), | ||
124 | _ => None, | ||
125 | }; | ||
126 | let impl_maps = [ | ||
127 | Some(in_deps), | ||
128 | Some(in_self), | ||
129 | local_impls(self.db, trait_module), | ||
130 | type_module.and_then(|m| local_impls(self.db, m)), | ||
131 | ]; | ||
114 | 132 | ||
115 | let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); | 133 | let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); |
116 | 134 | ||
@@ -118,14 +136,16 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
118 | debug!("Unrestricted search for {:?} impls...", trait_); | 136 | debug!("Unrestricted search for {:?} impls...", trait_); |
119 | impl_maps | 137 | impl_maps |
120 | .iter() | 138 | .iter() |
121 | .flat_map(|crate_impl_defs| crate_impl_defs.for_trait(trait_).map(id_to_chalk)) | 139 | .filter_map(|o| o.as_ref()) |
140 | .flat_map(|impls| impls.for_trait(trait_).map(id_to_chalk)) | ||
122 | .collect() | 141 | .collect() |
123 | } else { | 142 | } else { |
124 | impl_maps | 143 | impl_maps |
125 | .iter() | 144 | .iter() |
126 | .flat_map(|crate_impl_defs| { | 145 | .filter_map(|o| o.as_ref()) |
146 | .flat_map(|impls| { | ||
127 | fps.iter().flat_map(move |fp| { | 147 | fps.iter().flat_map(move |fp| { |
128 | crate_impl_defs.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) | 148 | impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) |
129 | }) | 149 | }) |
130 | }) | 150 | }) |
131 | .collect() | 151 | .collect() |
@@ -430,8 +450,7 @@ pub(crate) fn trait_datum_query( | |||
430 | fundamental: false, | 450 | fundamental: false, |
431 | }; | 451 | }; |
432 | let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); | 452 | let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); |
433 | let associated_ty_ids = | 453 | let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect(); |
434 | trait_data.associated_types().map(|type_alias| to_assoc_type_id(type_alias)).collect(); | ||
435 | let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; | 454 | let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; |
436 | let well_known = | 455 | let well_known = |
437 | lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); | 456 | lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name)); |
diff --git a/crates/hir_ty/src/consteval.rs b/crates/hir_ty/src/consteval.rs index e3ceb3d62..6f0bf8f8c 100644 --- a/crates/hir_ty/src/consteval.rs +++ b/crates/hir_ty/src/consteval.rs | |||
@@ -49,7 +49,7 @@ pub fn usize_const(value: Option<u64>) -> Const { | |||
49 | ConstData { | 49 | ConstData { |
50 | ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner), | 50 | ty: TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)).intern(&Interner), |
51 | value: ConstValue::Concrete(chalk_ir::ConcreteConst { | 51 | value: ConstValue::Concrete(chalk_ir::ConcreteConst { |
52 | interned: value.map(|value| ConstScalar::Usize(value)).unwrap_or(ConstScalar::Unknown), | 52 | interned: value.map(ConstScalar::Usize).unwrap_or(ConstScalar::Unknown), |
53 | }), | 53 | }), |
54 | } | 54 | } |
55 | .intern(&Interner) | 55 | .intern(&Interner) |
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs index be5b9110e..b9003c413 100644 --- a/crates/hir_ty/src/db.rs +++ b/crates/hir_ty/src/db.rs | |||
@@ -5,8 +5,8 @@ use std::sync::Arc; | |||
5 | 5 | ||
6 | use base_db::{impl_intern_key, salsa, CrateId, Upcast}; | 6 | use base_db::{impl_intern_key, salsa, CrateId, Upcast}; |
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId, | 8 | db::DefDatabase, expr::ExprId, BlockId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, |
9 | LifetimeParamId, LocalFieldId, TypeParamId, VariantId, | 9 | ImplId, LifetimeParamId, LocalFieldId, TypeParamId, VariantId, |
10 | }; | 10 | }; |
11 | use la_arena::ArenaMap; | 11 | use la_arena::ArenaMap; |
12 | 12 | ||
@@ -79,6 +79,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
79 | #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)] | 79 | #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)] |
80 | fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>; | 80 | fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>; |
81 | 81 | ||
82 | #[salsa::invoke(TraitImpls::trait_impls_in_block_query)] | ||
83 | fn trait_impls_in_block(&self, krate: BlockId) -> Option<Arc<TraitImpls>>; | ||
84 | |||
82 | #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)] | 85 | #[salsa::invoke(TraitImpls::trait_impls_in_deps_query)] |
83 | fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>; | 86 | fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>; |
84 | 87 | ||
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs index 283894704..6339c9687 100644 --- a/crates/hir_ty/src/diagnostics.rs +++ b/crates/hir_ty/src/diagnostics.rs | |||
@@ -4,325 +4,31 @@ mod match_check; | |||
4 | mod unsafe_check; | 4 | mod unsafe_check; |
5 | mod decl_check; | 5 | mod decl_check; |
6 | 6 | ||
7 | use std::{any::Any, fmt}; | 7 | use std::fmt; |
8 | 8 | ||
9 | use base_db::CrateId; | 9 | use base_db::CrateId; |
10 | use hir_def::{DefWithBodyId, ModuleDefId}; | 10 | use hir_def::ModuleDefId; |
11 | use hir_expand::{name::Name, HirFileId, InFile}; | 11 | use hir_expand::HirFileId; |
12 | use stdx::format_to; | 12 | use syntax::{ast, AstPtr}; |
13 | use syntax::{ast, AstPtr, SyntaxNodePtr}; | ||
14 | 13 | ||
15 | use crate::{ | 14 | use crate::db::HirDatabase; |
16 | db::HirDatabase, | ||
17 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink}, | ||
18 | }; | ||
19 | 15 | ||
20 | pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; | 16 | pub use crate::diagnostics::{ |
17 | expr::{ | ||
18 | record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic, | ||
19 | }, | ||
20 | unsafe_check::missing_unsafe, | ||
21 | }; | ||
21 | 22 | ||
22 | pub fn validate_module_item( | 23 | pub fn validate_module_item( |
23 | db: &dyn HirDatabase, | 24 | db: &dyn HirDatabase, |
24 | krate: CrateId, | 25 | krate: CrateId, |
25 | owner: ModuleDefId, | 26 | owner: ModuleDefId, |
26 | sink: &mut DiagnosticSink<'_>, | 27 | ) -> Vec<IncorrectCase> { |
27 | ) { | ||
28 | let _p = profile::span("validate_module_item"); | 28 | let _p = profile::span("validate_module_item"); |
29 | let mut validator = decl_check::DeclValidator::new(db, krate, sink); | 29 | let mut validator = decl_check::DeclValidator::new(db, krate); |
30 | validator.validate_item(owner); | 30 | validator.validate_item(owner); |
31 | } | 31 | validator.sink |
32 | |||
33 | pub fn validate_body(db: &dyn HirDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { | ||
34 | let _p = profile::span("validate_body"); | ||
35 | let infer = db.infer(owner); | ||
36 | infer.add_diagnostics(db, owner, sink); | ||
37 | let mut validator = expr::ExprValidator::new(owner, infer.clone(), sink); | ||
38 | validator.validate_body(db); | ||
39 | let mut validator = unsafe_check::UnsafeValidator::new(owner, infer, sink); | ||
40 | validator.validate_body(db); | ||
41 | } | ||
42 | |||
43 | // Diagnostic: no-such-field | ||
44 | // | ||
45 | // This diagnostic is triggered if created structure does not have field provided in record. | ||
46 | #[derive(Debug)] | ||
47 | pub struct NoSuchField { | ||
48 | pub file: HirFileId, | ||
49 | pub field: AstPtr<ast::RecordExprField>, | ||
50 | } | ||
51 | |||
52 | impl Diagnostic for NoSuchField { | ||
53 | fn code(&self) -> DiagnosticCode { | ||
54 | DiagnosticCode("no-such-field") | ||
55 | } | ||
56 | |||
57 | fn message(&self) -> String { | ||
58 | "no such field".to_string() | ||
59 | } | ||
60 | |||
61 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
62 | InFile::new(self.file, self.field.clone().into()) | ||
63 | } | ||
64 | |||
65 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
66 | self | ||
67 | } | ||
68 | } | ||
69 | |||
70 | // Diagnostic: missing-structure-fields | ||
71 | // | ||
72 | // This diagnostic is triggered if record lacks some fields that exist in the corresponding structure. | ||
73 | // | ||
74 | // Example: | ||
75 | // | ||
76 | // ```rust | ||
77 | // struct A { a: u8, b: u8 } | ||
78 | // | ||
79 | // let a = A { a: 10 }; | ||
80 | // ``` | ||
81 | #[derive(Debug)] | ||
82 | pub struct MissingFields { | ||
83 | pub file: HirFileId, | ||
84 | pub field_list_parent: AstPtr<ast::RecordExpr>, | ||
85 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, | ||
86 | pub missed_fields: Vec<Name>, | ||
87 | } | ||
88 | |||
89 | impl Diagnostic for MissingFields { | ||
90 | fn code(&self) -> DiagnosticCode { | ||
91 | DiagnosticCode("missing-structure-fields") | ||
92 | } | ||
93 | fn message(&self) -> String { | ||
94 | let mut buf = String::from("Missing structure fields:\n"); | ||
95 | for field in &self.missed_fields { | ||
96 | format_to!(buf, "- {}\n", field); | ||
97 | } | ||
98 | buf | ||
99 | } | ||
100 | |||
101 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
102 | InFile { | ||
103 | file_id: self.file, | ||
104 | value: self | ||
105 | .field_list_parent_path | ||
106 | .clone() | ||
107 | .map(SyntaxNodePtr::from) | ||
108 | .unwrap_or_else(|| self.field_list_parent.clone().into()), | ||
109 | } | ||
110 | } | ||
111 | |||
112 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
113 | self | ||
114 | } | ||
115 | } | ||
116 | |||
117 | // Diagnostic: missing-pat-fields | ||
118 | // | ||
119 | // This diagnostic is triggered if pattern lacks some fields that exist in the corresponding structure. | ||
120 | // | ||
121 | // Example: | ||
122 | // | ||
123 | // ```rust | ||
124 | // struct A { a: u8, b: u8 } | ||
125 | // | ||
126 | // let a = A { a: 10, b: 20 }; | ||
127 | // | ||
128 | // if let A { a } = a { | ||
129 | // // ... | ||
130 | // } | ||
131 | // ``` | ||
132 | #[derive(Debug)] | ||
133 | pub struct MissingPatFields { | ||
134 | pub file: HirFileId, | ||
135 | pub field_list_parent: AstPtr<ast::RecordPat>, | ||
136 | pub field_list_parent_path: Option<AstPtr<ast::Path>>, | ||
137 | pub missed_fields: Vec<Name>, | ||
138 | } | ||
139 | |||
140 | impl Diagnostic for MissingPatFields { | ||
141 | fn code(&self) -> DiagnosticCode { | ||
142 | DiagnosticCode("missing-pat-fields") | ||
143 | } | ||
144 | fn message(&self) -> String { | ||
145 | let mut buf = String::from("Missing structure fields:\n"); | ||
146 | for field in &self.missed_fields { | ||
147 | format_to!(buf, "- {}\n", field); | ||
148 | } | ||
149 | buf | ||
150 | } | ||
151 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
152 | InFile { | ||
153 | file_id: self.file, | ||
154 | value: self | ||
155 | .field_list_parent_path | ||
156 | .clone() | ||
157 | .map(SyntaxNodePtr::from) | ||
158 | .unwrap_or_else(|| self.field_list_parent.clone().into()), | ||
159 | } | ||
160 | } | ||
161 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
162 | self | ||
163 | } | ||
164 | } | ||
165 | |||
166 | // Diagnostic: missing-match-arm | ||
167 | // | ||
168 | // This diagnostic is triggered if `match` block is missing one or more match arms. | ||
169 | #[derive(Debug)] | ||
170 | pub struct MissingMatchArms { | ||
171 | pub file: HirFileId, | ||
172 | pub match_expr: AstPtr<ast::Expr>, | ||
173 | pub arms: AstPtr<ast::MatchArmList>, | ||
174 | } | ||
175 | |||
176 | impl Diagnostic for MissingMatchArms { | ||
177 | fn code(&self) -> DiagnosticCode { | ||
178 | DiagnosticCode("missing-match-arm") | ||
179 | } | ||
180 | fn message(&self) -> String { | ||
181 | String::from("Missing match arm") | ||
182 | } | ||
183 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
184 | InFile { file_id: self.file, value: self.match_expr.clone().into() } | ||
185 | } | ||
186 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
187 | self | ||
188 | } | ||
189 | } | ||
190 | |||
191 | // Diagnostic: missing-ok-or-some-in-tail-expr | ||
192 | // | ||
193 | // This diagnostic is triggered if a block that should return `Result` returns a value not wrapped in `Ok`, | ||
194 | // or if a block that should return `Option` returns a value not wrapped in `Some`. | ||
195 | // | ||
196 | // Example: | ||
197 | // | ||
198 | // ```rust | ||
199 | // fn foo() -> Result<u8, ()> { | ||
200 | // 10 | ||
201 | // } | ||
202 | // ``` | ||
203 | #[derive(Debug)] | ||
204 | pub struct MissingOkOrSomeInTailExpr { | ||
205 | pub file: HirFileId, | ||
206 | pub expr: AstPtr<ast::Expr>, | ||
207 | // `Some` or `Ok` depending on whether the return type is Result or Option | ||
208 | pub required: String, | ||
209 | } | ||
210 | |||
211 | impl Diagnostic for MissingOkOrSomeInTailExpr { | ||
212 | fn code(&self) -> DiagnosticCode { | ||
213 | DiagnosticCode("missing-ok-or-some-in-tail-expr") | ||
214 | } | ||
215 | fn message(&self) -> String { | ||
216 | format!("wrap return expression in {}", self.required) | ||
217 | } | ||
218 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
219 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
220 | } | ||
221 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
222 | self | ||
223 | } | ||
224 | } | ||
225 | |||
226 | #[derive(Debug)] | ||
227 | pub struct RemoveThisSemicolon { | ||
228 | pub file: HirFileId, | ||
229 | pub expr: AstPtr<ast::Expr>, | ||
230 | } | ||
231 | |||
232 | impl Diagnostic for RemoveThisSemicolon { | ||
233 | fn code(&self) -> DiagnosticCode { | ||
234 | DiagnosticCode("remove-this-semicolon") | ||
235 | } | ||
236 | |||
237 | fn message(&self) -> String { | ||
238 | "Remove this semicolon".to_string() | ||
239 | } | ||
240 | |||
241 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
242 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
243 | } | ||
244 | |||
245 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
246 | self | ||
247 | } | ||
248 | } | ||
249 | |||
250 | // Diagnostic: break-outside-of-loop | ||
251 | // | ||
252 | // This diagnostic is triggered if the `break` keyword is used outside of a loop. | ||
253 | #[derive(Debug)] | ||
254 | pub struct BreakOutsideOfLoop { | ||
255 | pub file: HirFileId, | ||
256 | pub expr: AstPtr<ast::Expr>, | ||
257 | } | ||
258 | |||
259 | impl Diagnostic for BreakOutsideOfLoop { | ||
260 | fn code(&self) -> DiagnosticCode { | ||
261 | DiagnosticCode("break-outside-of-loop") | ||
262 | } | ||
263 | fn message(&self) -> String { | ||
264 | "break outside of loop".to_string() | ||
265 | } | ||
266 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
267 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
268 | } | ||
269 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
270 | self | ||
271 | } | ||
272 | } | ||
273 | |||
274 | // Diagnostic: missing-unsafe | ||
275 | // | ||
276 | // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. | ||
277 | #[derive(Debug)] | ||
278 | pub struct MissingUnsafe { | ||
279 | pub file: HirFileId, | ||
280 | pub expr: AstPtr<ast::Expr>, | ||
281 | } | ||
282 | |||
283 | impl Diagnostic for MissingUnsafe { | ||
284 | fn code(&self) -> DiagnosticCode { | ||
285 | DiagnosticCode("missing-unsafe") | ||
286 | } | ||
287 | fn message(&self) -> String { | ||
288 | format!("This operation is unsafe and requires an unsafe function or block") | ||
289 | } | ||
290 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
291 | InFile { file_id: self.file, value: self.expr.clone().into() } | ||
292 | } | ||
293 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
294 | self | ||
295 | } | ||
296 | } | ||
297 | |||
298 | // Diagnostic: mismatched-arg-count | ||
299 | // | ||
300 | // This diagnostic is triggered if a function is invoked with an incorrect amount of arguments. | ||
301 | #[derive(Debug)] | ||
302 | pub struct MismatchedArgCount { | ||
303 | pub file: HirFileId, | ||
304 | pub call_expr: AstPtr<ast::Expr>, | ||
305 | pub expected: usize, | ||
306 | pub found: usize, | ||
307 | } | ||
308 | |||
309 | impl Diagnostic for MismatchedArgCount { | ||
310 | fn code(&self) -> DiagnosticCode { | ||
311 | DiagnosticCode("mismatched-arg-count") | ||
312 | } | ||
313 | fn message(&self) -> String { | ||
314 | let s = if self.expected == 1 { "" } else { "s" }; | ||
315 | format!("Expected {} argument{}, found {}", self.expected, s, self.found) | ||
316 | } | ||
317 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
318 | InFile { file_id: self.file, value: self.call_expr.clone().into() } | ||
319 | } | ||
320 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
321 | self | ||
322 | } | ||
323 | fn is_experimental(&self) -> bool { | ||
324 | true | ||
325 | } | ||
326 | } | 32 | } |
327 | 33 | ||
328 | #[derive(Debug)] | 34 | #[derive(Debug)] |
@@ -378,9 +84,6 @@ impl fmt::Display for IdentType { | |||
378 | } | 84 | } |
379 | } | 85 | } |
380 | 86 | ||
381 | // Diagnostic: incorrect-ident-case | ||
382 | // | ||
383 | // This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. | ||
384 | #[derive(Debug)] | 87 | #[derive(Debug)] |
385 | pub struct IncorrectCase { | 88 | pub struct IncorrectCase { |
386 | pub file: HirFileId, | 89 | pub file: HirFileId, |
@@ -390,450 +93,3 @@ pub struct IncorrectCase { | |||
390 | pub ident_text: String, | 93 | pub ident_text: String, |
391 | pub suggested_text: String, | 94 | pub suggested_text: String, |
392 | } | 95 | } |
393 | |||
394 | impl Diagnostic for IncorrectCase { | ||
395 | fn code(&self) -> DiagnosticCode { | ||
396 | DiagnosticCode("incorrect-ident-case") | ||
397 | } | ||
398 | |||
399 | fn message(&self) -> String { | ||
400 | format!( | ||
401 | "{} `{}` should have {} name, e.g. `{}`", | ||
402 | self.ident_type, | ||
403 | self.ident_text, | ||
404 | self.expected_case.to_string(), | ||
405 | self.suggested_text | ||
406 | ) | ||
407 | } | ||
408 | |||
409 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
410 | InFile::new(self.file, self.ident.clone().into()) | ||
411 | } | ||
412 | |||
413 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
414 | self | ||
415 | } | ||
416 | |||
417 | fn is_experimental(&self) -> bool { | ||
418 | true | ||
419 | } | ||
420 | } | ||
421 | |||
422 | // Diagnostic: replace-filter-map-next-with-find-map | ||
423 | // | ||
424 | // This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`. | ||
425 | #[derive(Debug)] | ||
426 | pub struct ReplaceFilterMapNextWithFindMap { | ||
427 | pub file: HirFileId, | ||
428 | /// This expression is the whole method chain up to and including `.filter_map(..).next()`. | ||
429 | pub next_expr: AstPtr<ast::Expr>, | ||
430 | } | ||
431 | |||
432 | impl Diagnostic for ReplaceFilterMapNextWithFindMap { | ||
433 | fn code(&self) -> DiagnosticCode { | ||
434 | DiagnosticCode("replace-filter-map-next-with-find-map") | ||
435 | } | ||
436 | fn message(&self) -> String { | ||
437 | "replace filter_map(..).next() with find_map(..)".to_string() | ||
438 | } | ||
439 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
440 | InFile { file_id: self.file, value: self.next_expr.clone().into() } | ||
441 | } | ||
442 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
443 | self | ||
444 | } | ||
445 | } | ||
446 | |||
447 | #[cfg(test)] | ||
448 | mod tests { | ||
449 | use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; | ||
450 | use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; | ||
451 | use hir_expand::db::AstDatabase; | ||
452 | use rustc_hash::FxHashMap; | ||
453 | use syntax::{TextRange, TextSize}; | ||
454 | |||
455 | use crate::{ | ||
456 | diagnostics::{validate_body, validate_module_item}, | ||
457 | diagnostics_sink::{Diagnostic, DiagnosticSinkBuilder}, | ||
458 | test_db::TestDB, | ||
459 | }; | ||
460 | |||
461 | impl TestDB { | ||
462 | fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) { | ||
463 | let crate_graph = self.crate_graph(); | ||
464 | for krate in crate_graph.iter() { | ||
465 | let crate_def_map = self.crate_def_map(krate); | ||
466 | |||
467 | let mut fns = Vec::new(); | ||
468 | for (module_id, _) in crate_def_map.modules() { | ||
469 | for decl in crate_def_map[module_id].scope.declarations() { | ||
470 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); | ||
471 | validate_module_item(self, krate, decl, &mut sink); | ||
472 | |||
473 | if let ModuleDefId::FunctionId(f) = decl { | ||
474 | fns.push(f) | ||
475 | } | ||
476 | } | ||
477 | |||
478 | for impl_id in crate_def_map[module_id].scope.impls() { | ||
479 | let impl_data = self.impl_data(impl_id); | ||
480 | for item in impl_data.items.iter() { | ||
481 | if let AssocItemId::FunctionId(f) = item { | ||
482 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); | ||
483 | validate_module_item( | ||
484 | self, | ||
485 | krate, | ||
486 | ModuleDefId::FunctionId(*f), | ||
487 | &mut sink, | ||
488 | ); | ||
489 | fns.push(*f) | ||
490 | } | ||
491 | } | ||
492 | } | ||
493 | } | ||
494 | |||
495 | for f in fns { | ||
496 | let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); | ||
497 | validate_body(self, f.into(), &mut sink); | ||
498 | } | ||
499 | } | ||
500 | } | ||
501 | } | ||
502 | |||
503 | pub(crate) fn check_diagnostics(ra_fixture: &str) { | ||
504 | let db = TestDB::with_files(ra_fixture); | ||
505 | let annotations = db.extract_annotations(); | ||
506 | |||
507 | let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default(); | ||
508 | db.diagnostics(|d| { | ||
509 | let src = d.display_source(); | ||
510 | let root = db.parse_or_expand(src.file_id).unwrap(); | ||
511 | // FIXME: macros... | ||
512 | let file_id = src.file_id.original_file(&db); | ||
513 | let range = src.value.to_node(&root).text_range(); | ||
514 | let message = d.message(); | ||
515 | actual.entry(file_id).or_default().push((range, message)); | ||
516 | }); | ||
517 | |||
518 | for (file_id, diags) in actual.iter_mut() { | ||
519 | diags.sort_by_key(|it| it.0.start()); | ||
520 | let text = db.file_text(*file_id); | ||
521 | // For multiline spans, place them on line start | ||
522 | for (range, content) in diags { | ||
523 | if text[*range].contains('\n') { | ||
524 | *range = TextRange::new(range.start(), range.start() + TextSize::from(1)); | ||
525 | *content = format!("... {}", content); | ||
526 | } | ||
527 | } | ||
528 | } | ||
529 | |||
530 | assert_eq!(annotations, actual); | ||
531 | } | ||
532 | |||
533 | #[test] | ||
534 | fn no_such_field_diagnostics() { | ||
535 | check_diagnostics( | ||
536 | r#" | ||
537 | struct S { foo: i32, bar: () } | ||
538 | impl S { | ||
539 | fn new() -> S { | ||
540 | S { | ||
541 | //^ Missing structure fields: | ||
542 | //| - bar | ||
543 | foo: 92, | ||
544 | baz: 62, | ||
545 | //^^^^^^^ no such field | ||
546 | } | ||
547 | } | ||
548 | } | ||
549 | "#, | ||
550 | ); | ||
551 | } | ||
552 | #[test] | ||
553 | fn no_such_field_with_feature_flag_diagnostics() { | ||
554 | check_diagnostics( | ||
555 | r#" | ||
556 | //- /lib.rs crate:foo cfg:feature=foo | ||
557 | struct MyStruct { | ||
558 | my_val: usize, | ||
559 | #[cfg(feature = "foo")] | ||
560 | bar: bool, | ||
561 | } | ||
562 | |||
563 | impl MyStruct { | ||
564 | #[cfg(feature = "foo")] | ||
565 | pub(crate) fn new(my_val: usize, bar: bool) -> Self { | ||
566 | Self { my_val, bar } | ||
567 | } | ||
568 | #[cfg(not(feature = "foo"))] | ||
569 | pub(crate) fn new(my_val: usize, _bar: bool) -> Self { | ||
570 | Self { my_val } | ||
571 | } | ||
572 | } | ||
573 | "#, | ||
574 | ); | ||
575 | } | ||
576 | |||
577 | #[test] | ||
578 | fn no_such_field_enum_with_feature_flag_diagnostics() { | ||
579 | check_diagnostics( | ||
580 | r#" | ||
581 | //- /lib.rs crate:foo cfg:feature=foo | ||
582 | enum Foo { | ||
583 | #[cfg(not(feature = "foo"))] | ||
584 | Buz, | ||
585 | #[cfg(feature = "foo")] | ||
586 | Bar, | ||
587 | Baz | ||
588 | } | ||
589 | |||
590 | fn test_fn(f: Foo) { | ||
591 | match f { | ||
592 | Foo::Bar => {}, | ||
593 | Foo::Baz => {}, | ||
594 | } | ||
595 | } | ||
596 | "#, | ||
597 | ); | ||
598 | } | ||
599 | |||
600 | #[test] | ||
601 | fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { | ||
602 | check_diagnostics( | ||
603 | r#" | ||
604 | //- /lib.rs crate:foo cfg:feature=foo | ||
605 | struct S { | ||
606 | #[cfg(feature = "foo")] | ||
607 | foo: u32, | ||
608 | #[cfg(not(feature = "foo"))] | ||
609 | bar: u32, | ||
610 | } | ||
611 | |||
612 | impl S { | ||
613 | #[cfg(feature = "foo")] | ||
614 | fn new(foo: u32) -> Self { | ||
615 | Self { foo } | ||
616 | } | ||
617 | #[cfg(not(feature = "foo"))] | ||
618 | fn new(bar: u32) -> Self { | ||
619 | Self { bar } | ||
620 | } | ||
621 | fn new2(bar: u32) -> Self { | ||
622 | #[cfg(feature = "foo")] | ||
623 | { Self { foo: bar } } | ||
624 | #[cfg(not(feature = "foo"))] | ||
625 | { Self { bar } } | ||
626 | } | ||
627 | fn new2(val: u32) -> Self { | ||
628 | Self { | ||
629 | #[cfg(feature = "foo")] | ||
630 | foo: val, | ||
631 | #[cfg(not(feature = "foo"))] | ||
632 | bar: val, | ||
633 | } | ||
634 | } | ||
635 | } | ||
636 | "#, | ||
637 | ); | ||
638 | } | ||
639 | |||
640 | #[test] | ||
641 | fn no_such_field_with_type_macro() { | ||
642 | check_diagnostics( | ||
643 | r#" | ||
644 | macro_rules! Type { () => { u32 }; } | ||
645 | struct Foo { bar: Type![] } | ||
646 | |||
647 | impl Foo { | ||
648 | fn new() -> Self { | ||
649 | Foo { bar: 0 } | ||
650 | } | ||
651 | } | ||
652 | "#, | ||
653 | ); | ||
654 | } | ||
655 | |||
656 | #[test] | ||
657 | fn missing_record_pat_field_diagnostic() { | ||
658 | check_diagnostics( | ||
659 | r#" | ||
660 | struct S { foo: i32, bar: () } | ||
661 | fn baz(s: S) { | ||
662 | let S { foo: _ } = s; | ||
663 | //^ Missing structure fields: | ||
664 | //| - bar | ||
665 | } | ||
666 | "#, | ||
667 | ); | ||
668 | } | ||
669 | |||
670 | #[test] | ||
671 | fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { | ||
672 | check_diagnostics( | ||
673 | r" | ||
674 | struct S { foo: i32, bar: () } | ||
675 | fn baz(s: S) -> i32 { | ||
676 | match s { | ||
677 | S { foo, .. } => foo, | ||
678 | } | ||
679 | } | ||
680 | ", | ||
681 | ) | ||
682 | } | ||
683 | |||
684 | #[test] | ||
685 | fn missing_record_pat_field_box() { | ||
686 | check_diagnostics( | ||
687 | r" | ||
688 | struct S { s: Box<u32> } | ||
689 | fn x(a: S) { | ||
690 | let S { box s } = a; | ||
691 | } | ||
692 | ", | ||
693 | ) | ||
694 | } | ||
695 | |||
696 | #[test] | ||
697 | fn missing_record_pat_field_ref() { | ||
698 | check_diagnostics( | ||
699 | r" | ||
700 | struct S { s: u32 } | ||
701 | fn x(a: S) { | ||
702 | let S { ref s } = a; | ||
703 | } | ||
704 | ", | ||
705 | ) | ||
706 | } | ||
707 | |||
708 | #[test] | ||
709 | fn import_extern_crate_clash_with_inner_item() { | ||
710 | // This is more of a resolver test, but doesn't really work with the hir_def testsuite. | ||
711 | |||
712 | check_diagnostics( | ||
713 | r#" | ||
714 | //- /lib.rs crate:lib deps:jwt | ||
715 | mod permissions; | ||
716 | |||
717 | use permissions::jwt; | ||
718 | |||
719 | fn f() { | ||
720 | fn inner() {} | ||
721 | jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic | ||
722 | } | ||
723 | |||
724 | //- /permissions.rs | ||
725 | pub mod jwt { | ||
726 | pub struct Claims {} | ||
727 | } | ||
728 | |||
729 | //- /jwt/lib.rs crate:jwt | ||
730 | pub struct Claims { | ||
731 | field: u8, | ||
732 | } | ||
733 | "#, | ||
734 | ); | ||
735 | } | ||
736 | |||
737 | #[test] | ||
738 | fn break_outside_of_loop() { | ||
739 | check_diagnostics( | ||
740 | r#" | ||
741 | fn foo() { break; } | ||
742 | //^^^^^ break outside of loop | ||
743 | "#, | ||
744 | ); | ||
745 | } | ||
746 | |||
747 | #[test] | ||
748 | fn missing_semicolon() { | ||
749 | check_diagnostics( | ||
750 | r#" | ||
751 | fn test() -> i32 { 123; } | ||
752 | //^^^ Remove this semicolon | ||
753 | "#, | ||
754 | ); | ||
755 | } | ||
756 | |||
757 | // Register the required standard library types to make the tests work | ||
758 | fn add_filter_map_with_find_next_boilerplate(body: &str) -> String { | ||
759 | let prefix = r#" | ||
760 | //- /main.rs crate:main deps:core | ||
761 | use core::iter::Iterator; | ||
762 | use core::option::Option::{self, Some, None}; | ||
763 | "#; | ||
764 | let suffix = r#" | ||
765 | //- /core/lib.rs crate:core | ||
766 | pub mod option { | ||
767 | pub enum Option<T> { Some(T), None } | ||
768 | } | ||
769 | pub mod iter { | ||
770 | pub trait Iterator { | ||
771 | type Item; | ||
772 | fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap } | ||
773 | fn next(&mut self) -> Option<Self::Item>; | ||
774 | } | ||
775 | pub struct FilterMap {} | ||
776 | impl Iterator for FilterMap { | ||
777 | type Item = i32; | ||
778 | fn next(&mut self) -> i32 { 7 } | ||
779 | } | ||
780 | } | ||
781 | "#; | ||
782 | format!("{}{}{}", prefix, body, suffix) | ||
783 | } | ||
784 | |||
785 | #[test] | ||
786 | fn replace_filter_map_next_with_find_map2() { | ||
787 | check_diagnostics(&add_filter_map_with_find_next_boilerplate( | ||
788 | r#" | ||
789 | fn foo() { | ||
790 | let m = [1, 2, 3].iter().filter_map(|x| if *x == 2 { Some (4) } else { None }).next(); | ||
791 | //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ replace filter_map(..).next() with find_map(..) | ||
792 | } | ||
793 | "#, | ||
794 | )); | ||
795 | } | ||
796 | |||
797 | #[test] | ||
798 | fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() { | ||
799 | check_diagnostics(&add_filter_map_with_find_next_boilerplate( | ||
800 | r#" | ||
801 | fn foo() { | ||
802 | let m = [1, 2, 3] | ||
803 | .iter() | ||
804 | .filter_map(|x| if *x == 2 { Some (4) } else { None }) | ||
805 | .len(); | ||
806 | } | ||
807 | "#, | ||
808 | )); | ||
809 | } | ||
810 | |||
811 | #[test] | ||
812 | fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() { | ||
813 | check_diagnostics(&add_filter_map_with_find_next_boilerplate( | ||
814 | r#" | ||
815 | fn foo() { | ||
816 | let m = [1, 2, 3] | ||
817 | .iter() | ||
818 | .filter_map(|x| if *x == 2 { Some (4) } else { None }) | ||
819 | .map(|x| x + 2) | ||
820 | .len(); | ||
821 | } | ||
822 | "#, | ||
823 | )); | ||
824 | } | ||
825 | |||
826 | #[test] | ||
827 | fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() { | ||
828 | check_diagnostics(&add_filter_map_with_find_next_boilerplate( | ||
829 | r#" | ||
830 | fn foo() { | ||
831 | let m = [1, 2, 3] | ||
832 | .iter() | ||
833 | .filter_map(|x| if *x == 2 { Some (4) } else { None }); | ||
834 | let n = m.next(); | ||
835 | } | ||
836 | "#, | ||
837 | )); | ||
838 | } | ||
839 | } | ||
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index cfb5d7320..f26150b77 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -29,7 +29,6 @@ use syntax::{ | |||
29 | use crate::{ | 29 | use crate::{ |
30 | db::HirDatabase, | 30 | db::HirDatabase, |
31 | diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase}, | 31 | diagnostics::{decl_check::case_conv::*, CaseType, IdentType, IncorrectCase}, |
32 | diagnostics_sink::DiagnosticSink, | ||
33 | }; | 32 | }; |
34 | 33 | ||
35 | mod allow { | 34 | mod allow { |
@@ -40,10 +39,10 @@ mod allow { | |||
40 | pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; | 39 | pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; |
41 | } | 40 | } |
42 | 41 | ||
43 | pub(super) struct DeclValidator<'a, 'b> { | 42 | pub(super) struct DeclValidator<'a> { |
44 | db: &'a dyn HirDatabase, | 43 | db: &'a dyn HirDatabase, |
45 | krate: CrateId, | 44 | krate: CrateId, |
46 | sink: &'a mut DiagnosticSink<'b>, | 45 | pub(super) sink: Vec<IncorrectCase>, |
47 | } | 46 | } |
48 | 47 | ||
49 | #[derive(Debug)] | 48 | #[derive(Debug)] |
@@ -53,13 +52,9 @@ struct Replacement { | |||
53 | expected_case: CaseType, | 52 | expected_case: CaseType, |
54 | } | 53 | } |
55 | 54 | ||
56 | impl<'a, 'b> DeclValidator<'a, 'b> { | 55 | impl<'a> DeclValidator<'a> { |
57 | pub(super) fn new( | 56 | pub(super) fn new(db: &'a dyn HirDatabase, krate: CrateId) -> DeclValidator<'a> { |
58 | db: &'a dyn HirDatabase, | 57 | DeclValidator { db, krate, sink: Vec::new() } |
59 | krate: CrateId, | ||
60 | sink: &'a mut DiagnosticSink<'b>, | ||
61 | ) -> DeclValidator<'a, 'b> { | ||
62 | DeclValidator { db, krate, sink } | ||
63 | } | 58 | } |
64 | 59 | ||
65 | pub(super) fn validate_item(&mut self, item: ModuleDefId) { | 60 | pub(super) fn validate_item(&mut self, item: ModuleDefId) { |
@@ -131,7 +126,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
131 | for (_, block_def_map) in body.blocks(self.db.upcast()) { | 126 | for (_, block_def_map) in body.blocks(self.db.upcast()) { |
132 | for (_, module) in block_def_map.modules() { | 127 | for (_, module) in block_def_map.modules() { |
133 | for def_id in module.scope.declarations() { | 128 | for def_id in module.scope.declarations() { |
134 | let mut validator = DeclValidator::new(self.db, self.krate, self.sink); | 129 | let mut validator = DeclValidator::new(self.db, self.krate); |
135 | validator.validate_item(def_id); | 130 | validator.validate_item(def_id); |
136 | } | 131 | } |
137 | } | 132 | } |
@@ -623,343 +618,3 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
623 | self.sink.push(diagnostic); | 618 | self.sink.push(diagnostic); |
624 | } | 619 | } |
625 | } | 620 | } |
626 | |||
627 | #[cfg(test)] | ||
628 | mod tests { | ||
629 | use crate::diagnostics::tests::check_diagnostics; | ||
630 | |||
631 | #[test] | ||
632 | fn incorrect_function_name() { | ||
633 | check_diagnostics( | ||
634 | r#" | ||
635 | fn NonSnakeCaseName() {} | ||
636 | // ^^^^^^^^^^^^^^^^ Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` | ||
637 | "#, | ||
638 | ); | ||
639 | } | ||
640 | |||
641 | #[test] | ||
642 | fn incorrect_function_params() { | ||
643 | check_diagnostics( | ||
644 | r#" | ||
645 | fn foo(SomeParam: u8) {} | ||
646 | // ^^^^^^^^^ Parameter `SomeParam` should have snake_case name, e.g. `some_param` | ||
647 | |||
648 | fn foo2(ok_param: &str, CAPS_PARAM: u8) {} | ||
649 | // ^^^^^^^^^^ Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param` | ||
650 | "#, | ||
651 | ); | ||
652 | } | ||
653 | |||
654 | #[test] | ||
655 | fn incorrect_variable_names() { | ||
656 | check_diagnostics( | ||
657 | r#" | ||
658 | fn foo() { | ||
659 | let SOME_VALUE = 10; | ||
660 | // ^^^^^^^^^^ Variable `SOME_VALUE` should have snake_case name, e.g. `some_value` | ||
661 | let AnotherValue = 20; | ||
662 | // ^^^^^^^^^^^^ Variable `AnotherValue` should have snake_case name, e.g. `another_value` | ||
663 | } | ||
664 | "#, | ||
665 | ); | ||
666 | } | ||
667 | |||
668 | #[test] | ||
669 | fn incorrect_struct_names() { | ||
670 | check_diagnostics( | ||
671 | r#" | ||
672 | struct non_camel_case_name {} | ||
673 | // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` | ||
674 | |||
675 | struct SCREAMING_CASE {} | ||
676 | // ^^^^^^^^^^^^^^ Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase` | ||
677 | "#, | ||
678 | ); | ||
679 | } | ||
680 | |||
681 | #[test] | ||
682 | fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() { | ||
683 | check_diagnostics( | ||
684 | r#" | ||
685 | struct AABB {} | ||
686 | "#, | ||
687 | ); | ||
688 | } | ||
689 | |||
690 | #[test] | ||
691 | fn incorrect_struct_field() { | ||
692 | check_diagnostics( | ||
693 | r#" | ||
694 | struct SomeStruct { SomeField: u8 } | ||
695 | // ^^^^^^^^^ Field `SomeField` should have snake_case name, e.g. `some_field` | ||
696 | "#, | ||
697 | ); | ||
698 | } | ||
699 | |||
700 | #[test] | ||
701 | fn incorrect_enum_names() { | ||
702 | check_diagnostics( | ||
703 | r#" | ||
704 | enum some_enum { Val(u8) } | ||
705 | // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` | ||
706 | |||
707 | enum SOME_ENUM | ||
708 | // ^^^^^^^^^ Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum` | ||
709 | "#, | ||
710 | ); | ||
711 | } | ||
712 | |||
713 | #[test] | ||
714 | fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() { | ||
715 | check_diagnostics( | ||
716 | r#" | ||
717 | enum AABB {} | ||
718 | "#, | ||
719 | ); | ||
720 | } | ||
721 | |||
722 | #[test] | ||
723 | fn incorrect_enum_variant_name() { | ||
724 | check_diagnostics( | ||
725 | r#" | ||
726 | enum SomeEnum { SOME_VARIANT(u8) } | ||
727 | // ^^^^^^^^^^^^ Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant` | ||
728 | "#, | ||
729 | ); | ||
730 | } | ||
731 | |||
732 | #[test] | ||
733 | fn incorrect_const_name() { | ||
734 | check_diagnostics( | ||
735 | r#" | ||
736 | const some_weird_const: u8 = 10; | ||
737 | // ^^^^^^^^^^^^^^^^ Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` | ||
738 | |||
739 | fn func() { | ||
740 | const someConstInFunc: &str = "hi there"; | ||
741 | // ^^^^^^^^^^^^^^^ Constant `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC` | ||
742 | |||
743 | } | ||
744 | "#, | ||
745 | ); | ||
746 | } | ||
747 | |||
748 | #[test] | ||
749 | fn incorrect_static_name() { | ||
750 | check_diagnostics( | ||
751 | r#" | ||
752 | static some_weird_const: u8 = 10; | ||
753 | // ^^^^^^^^^^^^^^^^ Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` | ||
754 | |||
755 | fn func() { | ||
756 | static someConstInFunc: &str = "hi there"; | ||
757 | // ^^^^^^^^^^^^^^^ Static variable `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC` | ||
758 | } | ||
759 | "#, | ||
760 | ); | ||
761 | } | ||
762 | |||
763 | #[test] | ||
764 | fn fn_inside_impl_struct() { | ||
765 | check_diagnostics( | ||
766 | r#" | ||
767 | struct someStruct; | ||
768 | // ^^^^^^^^^^ Structure `someStruct` should have CamelCase name, e.g. `SomeStruct` | ||
769 | |||
770 | impl someStruct { | ||
771 | fn SomeFunc(&self) { | ||
772 | // ^^^^^^^^ Function `SomeFunc` should have snake_case name, e.g. `some_func` | ||
773 | static someConstInFunc: &str = "hi there"; | ||
774 | // ^^^^^^^^^^^^^^^ Static variable `someConstInFunc` should have UPPER_SNAKE_CASE name, e.g. `SOME_CONST_IN_FUNC` | ||
775 | let WHY_VAR_IS_CAPS = 10; | ||
776 | // ^^^^^^^^^^^^^^^ Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps` | ||
777 | } | ||
778 | } | ||
779 | "#, | ||
780 | ); | ||
781 | } | ||
782 | |||
783 | #[test] | ||
784 | fn no_diagnostic_for_enum_varinats() { | ||
785 | check_diagnostics( | ||
786 | r#" | ||
787 | enum Option { Some, None } | ||
788 | |||
789 | fn main() { | ||
790 | match Option::None { | ||
791 | None => (), | ||
792 | Some => (), | ||
793 | } | ||
794 | } | ||
795 | "#, | ||
796 | ); | ||
797 | } | ||
798 | |||
799 | #[test] | ||
800 | fn non_let_bind() { | ||
801 | check_diagnostics( | ||
802 | r#" | ||
803 | enum Option { Some, None } | ||
804 | |||
805 | fn main() { | ||
806 | match Option::None { | ||
807 | SOME_VAR @ None => (), | ||
808 | // ^^^^^^^^ Variable `SOME_VAR` should have snake_case name, e.g. `some_var` | ||
809 | Some => (), | ||
810 | } | ||
811 | } | ||
812 | "#, | ||
813 | ); | ||
814 | } | ||
815 | |||
816 | #[test] | ||
817 | fn allow_attributes() { | ||
818 | check_diagnostics( | ||
819 | r#" | ||
820 | #[allow(non_snake_case)] | ||
821 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8{ | ||
822 | // cov_flags generated output from elsewhere in this file | ||
823 | extern "C" { | ||
824 | #[no_mangle] | ||
825 | static lower_case: u8; | ||
826 | } | ||
827 | |||
828 | let OtherVar = SOME_VAR + 1; | ||
829 | OtherVar | ||
830 | } | ||
831 | |||
832 | #[allow(nonstandard_style)] | ||
833 | mod CheckNonstandardStyle { | ||
834 | fn HiImABadFnName() {} | ||
835 | } | ||
836 | |||
837 | #[allow(bad_style)] | ||
838 | mod CheckBadStyle { | ||
839 | fn HiImABadFnName() {} | ||
840 | } | ||
841 | |||
842 | mod F { | ||
843 | #![allow(non_snake_case)] | ||
844 | fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {} | ||
845 | } | ||
846 | |||
847 | #[allow(non_snake_case, non_camel_case_types)] | ||
848 | pub struct some_type { | ||
849 | SOME_FIELD: u8, | ||
850 | SomeField: u16, | ||
851 | } | ||
852 | |||
853 | #[allow(non_upper_case_globals)] | ||
854 | pub const some_const: u8 = 10; | ||
855 | |||
856 | #[allow(non_upper_case_globals)] | ||
857 | pub static SomeStatic: u8 = 10; | ||
858 | "#, | ||
859 | ); | ||
860 | } | ||
861 | |||
862 | #[test] | ||
863 | fn allow_attributes_crate_attr() { | ||
864 | check_diagnostics( | ||
865 | r#" | ||
866 | #![allow(non_snake_case)] | ||
867 | |||
868 | mod F { | ||
869 | fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {} | ||
870 | } | ||
871 | "#, | ||
872 | ); | ||
873 | } | ||
874 | |||
875 | #[test] | ||
876 | #[ignore] | ||
877 | fn bug_trait_inside_fn() { | ||
878 | // FIXME: | ||
879 | // This is broken, and in fact, should not even be looked at by this | ||
880 | // lint in the first place. There's weird stuff going on in the | ||
881 | // collection phase. | ||
882 | // It's currently being brought in by: | ||
883 | // * validate_func on `a` recursing into modules | ||
884 | // * then it finds the trait and then the function while iterating | ||
885 | // through modules | ||
886 | // * then validate_func is called on Dirty | ||
887 | // * ... which then proceeds to look at some unknown module taking no | ||
888 | // attrs from either the impl or the fn a, and then finally to the root | ||
889 | // module | ||
890 | // | ||
891 | // It should find the attribute on the trait, but it *doesn't even see | ||
892 | // the trait* as far as I can tell. | ||
893 | |||
894 | check_diagnostics( | ||
895 | r#" | ||
896 | trait T { fn a(); } | ||
897 | struct U {} | ||
898 | impl T for U { | ||
899 | fn a() { | ||
900 | // this comes out of bitflags, mostly | ||
901 | #[allow(non_snake_case)] | ||
902 | trait __BitFlags { | ||
903 | const HiImAlsoBad: u8 = 2; | ||
904 | #[inline] | ||
905 | fn Dirty(&self) -> bool { | ||
906 | false | ||
907 | } | ||
908 | } | ||
909 | |||
910 | } | ||
911 | } | ||
912 | "#, | ||
913 | ); | ||
914 | } | ||
915 | |||
916 | #[test] | ||
917 | #[ignore] | ||
918 | fn bug_traits_arent_checked() { | ||
919 | // FIXME: Traits and functions in traits aren't currently checked by | ||
920 | // r-a, even though rustc will complain about them. | ||
921 | check_diagnostics( | ||
922 | r#" | ||
923 | trait BAD_TRAIT { | ||
924 | // ^^^^^^^^^ Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait` | ||
925 | fn BAD_FUNCTION(); | ||
926 | // ^^^^^^^^^^^^ Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function` | ||
927 | fn BadFunction(); | ||
928 | // ^^^^^^^^^^^^ Function `BadFunction` should have snake_case name, e.g. `bad_function` | ||
929 | } | ||
930 | "#, | ||
931 | ); | ||
932 | } | ||
933 | |||
934 | #[test] | ||
935 | fn ignores_extern_items() { | ||
936 | cov_mark::check!(extern_func_incorrect_case_ignored); | ||
937 | cov_mark::check!(extern_static_incorrect_case_ignored); | ||
938 | check_diagnostics( | ||
939 | r#" | ||
940 | extern { | ||
941 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8; | ||
942 | pub static SomeStatic: u8 = 10; | ||
943 | } | ||
944 | "#, | ||
945 | ); | ||
946 | } | ||
947 | |||
948 | #[test] | ||
949 | fn infinite_loop_inner_items() { | ||
950 | check_diagnostics( | ||
951 | r#" | ||
952 | fn qualify() { | ||
953 | mod foo { | ||
954 | use super::*; | ||
955 | } | ||
956 | } | ||
957 | "#, | ||
958 | ) | ||
959 | } | ||
960 | |||
961 | #[test] // Issue #8809. | ||
962 | fn parenthesized_parameter() { | ||
963 | check_diagnostics(r#"fn f((O): _) {}"#) | ||
964 | } | ||
965 | } | ||
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index a2a4d61db..b809b96a0 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -8,20 +8,15 @@ use hir_def::{ | |||
8 | expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule, | 8 | expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId, HasModule, |
9 | }; | 9 | }; |
10 | use hir_expand::name; | 10 | use hir_expand::name; |
11 | use itertools::Either; | ||
11 | use rustc_hash::FxHashSet; | 12 | use rustc_hash::FxHashSet; |
12 | use syntax::{ast, AstPtr}; | ||
13 | 13 | ||
14 | use crate::{ | 14 | use crate::{ |
15 | db::HirDatabase, | 15 | db::HirDatabase, |
16 | diagnostics::{ | 16 | diagnostics::match_check::{ |
17 | match_check::{ | 17 | self, |
18 | self, | 18 | usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena}, |
19 | usefulness::{compute_match_usefulness, expand_pattern, MatchCheckCtx, PatternArena}, | ||
20 | }, | ||
21 | MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkOrSomeInTailExpr, | ||
22 | MissingPatFields, RemoveThisSemicolon, | ||
23 | }, | 19 | }, |
24 | diagnostics_sink::DiagnosticSink, | ||
25 | AdtId, InferenceResult, Interner, TyExt, TyKind, | 20 | AdtId, InferenceResult, Interner, TyExt, TyKind, |
26 | }; | 21 | }; |
27 | 22 | ||
@@ -31,38 +26,67 @@ pub(crate) use hir_def::{ | |||
31 | LocalFieldId, VariantId, | 26 | LocalFieldId, VariantId, |
32 | }; | 27 | }; |
33 | 28 | ||
34 | use super::ReplaceFilterMapNextWithFindMap; | 29 | pub enum BodyValidationDiagnostic { |
30 | RecordMissingFields { | ||
31 | record: Either<ExprId, PatId>, | ||
32 | variant: VariantId, | ||
33 | missed_fields: Vec<LocalFieldId>, | ||
34 | }, | ||
35 | ReplaceFilterMapNextWithFindMap { | ||
36 | method_call_expr: ExprId, | ||
37 | }, | ||
38 | MismatchedArgCount { | ||
39 | call_expr: ExprId, | ||
40 | expected: usize, | ||
41 | found: usize, | ||
42 | }, | ||
43 | RemoveThisSemicolon { | ||
44 | expr: ExprId, | ||
45 | }, | ||
46 | MissingOkOrSomeInTailExpr { | ||
47 | expr: ExprId, | ||
48 | required: String, | ||
49 | }, | ||
50 | MissingMatchArms { | ||
51 | match_expr: ExprId, | ||
52 | }, | ||
53 | } | ||
54 | |||
55 | impl BodyValidationDiagnostic { | ||
56 | pub fn collect(db: &dyn HirDatabase, owner: DefWithBodyId) -> Vec<BodyValidationDiagnostic> { | ||
57 | let _p = profile::span("BodyValidationDiagnostic::collect"); | ||
58 | let infer = db.infer(owner); | ||
59 | let mut validator = ExprValidator::new(owner, infer.clone()); | ||
60 | validator.validate_body(db); | ||
61 | validator.diagnostics | ||
62 | } | ||
63 | } | ||
35 | 64 | ||
36 | pub(super) struct ExprValidator<'a, 'b: 'a> { | 65 | struct ExprValidator { |
37 | owner: DefWithBodyId, | 66 | owner: DefWithBodyId, |
38 | infer: Arc<InferenceResult>, | 67 | infer: Arc<InferenceResult>, |
39 | sink: &'a mut DiagnosticSink<'b>, | 68 | pub(super) diagnostics: Vec<BodyValidationDiagnostic>, |
40 | } | 69 | } |
41 | 70 | ||
42 | impl<'a, 'b> ExprValidator<'a, 'b> { | 71 | impl ExprValidator { |
43 | pub(super) fn new( | 72 | fn new(owner: DefWithBodyId, infer: Arc<InferenceResult>) -> ExprValidator { |
44 | owner: DefWithBodyId, | 73 | ExprValidator { owner, infer, diagnostics: Vec::new() } |
45 | infer: Arc<InferenceResult>, | ||
46 | sink: &'a mut DiagnosticSink<'b>, | ||
47 | ) -> ExprValidator<'a, 'b> { | ||
48 | ExprValidator { owner, infer, sink } | ||
49 | } | 74 | } |
50 | 75 | ||
51 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { | 76 | fn validate_body(&mut self, db: &dyn HirDatabase) { |
52 | self.check_for_filter_map_next(db); | 77 | self.check_for_filter_map_next(db); |
53 | 78 | ||
54 | let body = db.body(self.owner); | 79 | let body = db.body(self.owner); |
55 | 80 | ||
56 | for (id, expr) in body.exprs.iter() { | 81 | for (id, expr) in body.exprs.iter() { |
57 | if let Some((variant_def, missed_fields, true)) = | 82 | if let Some((variant, missed_fields, true)) = |
58 | record_literal_missing_fields(db, &self.infer, id, expr) | 83 | record_literal_missing_fields(db, &self.infer, id, expr) |
59 | { | 84 | { |
60 | self.create_record_literal_missing_fields_diagnostic( | 85 | self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields { |
61 | id, | 86 | record: Either::Left(id), |
62 | db, | 87 | variant, |
63 | variant_def, | ||
64 | missed_fields, | 88 | missed_fields, |
65 | ); | 89 | }); |
66 | } | 90 | } |
67 | 91 | ||
68 | match expr { | 92 | match expr { |
@@ -76,15 +100,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
76 | } | 100 | } |
77 | } | 101 | } |
78 | for (id, pat) in body.pats.iter() { | 102 | for (id, pat) in body.pats.iter() { |
79 | if let Some((variant_def, missed_fields, true)) = | 103 | if let Some((variant, missed_fields, true)) = |
80 | record_pattern_missing_fields(db, &self.infer, id, pat) | 104 | record_pattern_missing_fields(db, &self.infer, id, pat) |
81 | { | 105 | { |
82 | self.create_record_pattern_missing_fields_diagnostic( | 106 | self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields { |
83 | id, | 107 | record: Either::Right(id), |
84 | db, | 108 | variant, |
85 | variant_def, | ||
86 | missed_fields, | 109 | missed_fields, |
87 | ); | 110 | }); |
88 | } | 111 | } |
89 | } | 112 | } |
90 | let body_expr = &body[body.body_expr]; | 113 | let body_expr = &body[body.body_expr]; |
@@ -92,71 +115,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
92 | if let Some(t) = tail { | 115 | if let Some(t) = tail { |
93 | self.validate_results_in_tail_expr(body.body_expr, *t, db); | 116 | self.validate_results_in_tail_expr(body.body_expr, *t, db); |
94 | } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() { | 117 | } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() { |
95 | self.validate_missing_tail_expr(body.body_expr, *id, db); | 118 | self.validate_missing_tail_expr(body.body_expr, *id); |
96 | } | ||
97 | } | ||
98 | } | ||
99 | |||
100 | fn create_record_literal_missing_fields_diagnostic( | ||
101 | &mut self, | ||
102 | id: ExprId, | ||
103 | db: &dyn HirDatabase, | ||
104 | variant_def: VariantId, | ||
105 | missed_fields: Vec<LocalFieldId>, | ||
106 | ) { | ||
107 | // XXX: only look at source_map if we do have missing fields | ||
108 | let (_, source_map) = db.body_with_source_map(self.owner); | ||
109 | |||
110 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | ||
111 | let root = source_ptr.file_syntax(db.upcast()); | ||
112 | if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) { | ||
113 | if let Some(_) = record_expr.record_expr_field_list() { | ||
114 | let variant_data = variant_def.variant_data(db.upcast()); | ||
115 | let missed_fields = missed_fields | ||
116 | .into_iter() | ||
117 | .map(|idx| variant_data.fields()[idx].name.clone()) | ||
118 | .collect(); | ||
119 | self.sink.push(MissingFields { | ||
120 | file: source_ptr.file_id, | ||
121 | field_list_parent: AstPtr::new(&record_expr), | ||
122 | field_list_parent_path: record_expr.path().map(|path| AstPtr::new(&path)), | ||
123 | missed_fields, | ||
124 | }) | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | |||
130 | fn create_record_pattern_missing_fields_diagnostic( | ||
131 | &mut self, | ||
132 | id: PatId, | ||
133 | db: &dyn HirDatabase, | ||
134 | variant_def: VariantId, | ||
135 | missed_fields: Vec<LocalFieldId>, | ||
136 | ) { | ||
137 | // XXX: only look at source_map if we do have missing fields | ||
138 | let (_, source_map) = db.body_with_source_map(self.owner); | ||
139 | |||
140 | if let Ok(source_ptr) = source_map.pat_syntax(id) { | ||
141 | if let Some(expr) = source_ptr.value.as_ref().left() { | ||
142 | let root = source_ptr.file_syntax(db.upcast()); | ||
143 | if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { | ||
144 | if let Some(_) = record_pat.record_pat_field_list() { | ||
145 | let variant_data = variant_def.variant_data(db.upcast()); | ||
146 | let missed_fields = missed_fields | ||
147 | .into_iter() | ||
148 | .map(|idx| variant_data.fields()[idx].name.clone()) | ||
149 | .collect(); | ||
150 | self.sink.push(MissingPatFields { | ||
151 | file: source_ptr.file_id, | ||
152 | field_list_parent: AstPtr::new(&record_pat), | ||
153 | field_list_parent_path: record_pat | ||
154 | .path() | ||
155 | .map(|path| AstPtr::new(&path)), | ||
156 | missed_fields, | ||
157 | }) | ||
158 | } | ||
159 | } | ||
160 | } | 119 | } |
161 | } | 120 | } |
162 | } | 121 | } |
@@ -199,13 +158,11 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
199 | if function_id == *next_function_id { | 158 | if function_id == *next_function_id { |
200 | if let Some(filter_map_id) = prev { | 159 | if let Some(filter_map_id) = prev { |
201 | if *receiver == filter_map_id { | 160 | if *receiver == filter_map_id { |
202 | let (_, source_map) = db.body_with_source_map(self.owner); | 161 | self.diagnostics.push( |
203 | if let Ok(next_source_ptr) = source_map.expr_syntax(id) { | 162 | BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap { |
204 | self.sink.push(ReplaceFilterMapNextWithFindMap { | 163 | method_call_expr: id, |
205 | file: next_source_ptr.file_id, | 164 | }, |
206 | next_expr: next_source_ptr.value, | 165 | ); |
207 | }); | ||
208 | } | ||
209 | } | 166 | } |
210 | } | 167 | } |
211 | } | 168 | } |
@@ -266,19 +223,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
266 | let mut arg_count = args.len(); | 223 | let mut arg_count = args.len(); |
267 | 224 | ||
268 | if arg_count != param_count { | 225 | if arg_count != param_count { |
269 | let (_, source_map) = db.body_with_source_map(self.owner); | 226 | if is_method_call { |
270 | if let Ok(source_ptr) = source_map.expr_syntax(call_id) { | 227 | param_count -= 1; |
271 | if is_method_call { | 228 | arg_count -= 1; |
272 | param_count -= 1; | ||
273 | arg_count -= 1; | ||
274 | } | ||
275 | self.sink.push(MismatchedArgCount { | ||
276 | file: source_ptr.file_id, | ||
277 | call_expr: source_ptr.value, | ||
278 | expected: param_count, | ||
279 | found: arg_count, | ||
280 | }); | ||
281 | } | 229 | } |
230 | self.diagnostics.push(BodyValidationDiagnostic::MismatchedArgCount { | ||
231 | call_expr: call_id, | ||
232 | expected: param_count, | ||
233 | found: arg_count, | ||
234 | }); | ||
282 | } | 235 | } |
283 | } | 236 | } |
284 | 237 | ||
@@ -346,8 +299,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
346 | // fit the match expression, we skip this diagnostic. Skipping the entire | 299 | // fit the match expression, we skip this diagnostic. Skipping the entire |
347 | // diagnostic rather than just not including this match arm is preferred | 300 | // diagnostic rather than just not including this match arm is preferred |
348 | // to avoid the chance of false positives. | 301 | // to avoid the chance of false positives. |
349 | #[cfg(test)] | 302 | cov_mark::hit!(validate_match_bailed_out); |
350 | match_check::tests::report_bail_out(db, self.owner, arm.pat, self.sink); | ||
351 | return; | 303 | return; |
352 | } | 304 | } |
353 | 305 | ||
@@ -382,20 +334,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
382 | // FIXME Report witnesses | 334 | // FIXME Report witnesses |
383 | // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); | 335 | // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); |
384 | if !witnesses.is_empty() { | 336 | if !witnesses.is_empty() { |
385 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | 337 | self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms { match_expr: id }); |
386 | let root = source_ptr.file_syntax(db.upcast()); | ||
387 | if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) { | ||
388 | if let (Some(match_expr), Some(arms)) = | ||
389 | (match_expr.expr(), match_expr.match_arm_list()) | ||
390 | { | ||
391 | self.sink.push(MissingMatchArms { | ||
392 | file: source_ptr.file_id, | ||
393 | match_expr: AstPtr::new(&match_expr), | ||
394 | arms: AstPtr::new(&arms), | ||
395 | }) | ||
396 | } | ||
397 | } | ||
398 | } | ||
399 | } | 338 | } |
400 | } | 339 | } |
401 | 340 | ||
@@ -453,24 +392,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
453 | if params.len(&Interner) > 0 | 392 | if params.len(&Interner) > 0 |
454 | && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual) | 393 | && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual) |
455 | { | 394 | { |
456 | let (_, source_map) = db.body_with_source_map(self.owner); | 395 | self.diagnostics |
457 | 396 | .push(BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr: id, required }); | |
458 | if let Ok(source_ptr) = source_map.expr_syntax(id) { | ||
459 | self.sink.push(MissingOkOrSomeInTailExpr { | ||
460 | file: source_ptr.file_id, | ||
461 | expr: source_ptr.value, | ||
462 | required, | ||
463 | }); | ||
464 | } | ||
465 | } | 397 | } |
466 | } | 398 | } |
467 | 399 | ||
468 | fn validate_missing_tail_expr( | 400 | fn validate_missing_tail_expr(&mut self, body_id: ExprId, possible_tail_id: ExprId) { |
469 | &mut self, | ||
470 | body_id: ExprId, | ||
471 | possible_tail_id: ExprId, | ||
472 | db: &dyn HirDatabase, | ||
473 | ) { | ||
474 | let mismatch = match self.infer.type_mismatch_for_expr(body_id) { | 401 | let mismatch = match self.infer.type_mismatch_for_expr(body_id) { |
475 | Some(m) => m, | 402 | Some(m) => m, |
476 | None => return, | 403 | None => return, |
@@ -485,12 +412,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
485 | return; | 412 | return; |
486 | } | 413 | } |
487 | 414 | ||
488 | let (_, source_map) = db.body_with_source_map(self.owner); | 415 | self.diagnostics |
489 | 416 | .push(BodyValidationDiagnostic::RemoveThisSemicolon { expr: possible_tail_id }); | |
490 | if let Ok(source_ptr) = source_map.expr_syntax(possible_tail_id) { | ||
491 | self.sink | ||
492 | .push(RemoveThisSemicolon { file: source_ptr.file_id, expr: source_ptr.value }); | ||
493 | } | ||
494 | } | 417 | } |
495 | } | 418 | } |
496 | 419 | ||
@@ -568,258 +491,3 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul | |||
568 | walk(pat, body, infer, &mut has_type_mismatches); | 491 | walk(pat, body, infer, &mut has_type_mismatches); |
569 | !has_type_mismatches | 492 | !has_type_mismatches |
570 | } | 493 | } |
571 | |||
572 | #[cfg(test)] | ||
573 | mod tests { | ||
574 | use crate::diagnostics::tests::check_diagnostics; | ||
575 | |||
576 | #[test] | ||
577 | fn simple_free_fn_zero() { | ||
578 | check_diagnostics( | ||
579 | r#" | ||
580 | fn zero() {} | ||
581 | fn f() { zero(1); } | ||
582 | //^^^^^^^ Expected 0 arguments, found 1 | ||
583 | "#, | ||
584 | ); | ||
585 | |||
586 | check_diagnostics( | ||
587 | r#" | ||
588 | fn zero() {} | ||
589 | fn f() { zero(); } | ||
590 | "#, | ||
591 | ); | ||
592 | } | ||
593 | |||
594 | #[test] | ||
595 | fn simple_free_fn_one() { | ||
596 | check_diagnostics( | ||
597 | r#" | ||
598 | fn one(arg: u8) {} | ||
599 | fn f() { one(); } | ||
600 | //^^^^^ Expected 1 argument, found 0 | ||
601 | "#, | ||
602 | ); | ||
603 | |||
604 | check_diagnostics( | ||
605 | r#" | ||
606 | fn one(arg: u8) {} | ||
607 | fn f() { one(1); } | ||
608 | "#, | ||
609 | ); | ||
610 | } | ||
611 | |||
612 | #[test] | ||
613 | fn method_as_fn() { | ||
614 | check_diagnostics( | ||
615 | r#" | ||
616 | struct S; | ||
617 | impl S { fn method(&self) {} } | ||
618 | |||
619 | fn f() { | ||
620 | S::method(); | ||
621 | } //^^^^^^^^^^^ Expected 1 argument, found 0 | ||
622 | "#, | ||
623 | ); | ||
624 | |||
625 | check_diagnostics( | ||
626 | r#" | ||
627 | struct S; | ||
628 | impl S { fn method(&self) {} } | ||
629 | |||
630 | fn f() { | ||
631 | S::method(&S); | ||
632 | S.method(); | ||
633 | } | ||
634 | "#, | ||
635 | ); | ||
636 | } | ||
637 | |||
638 | #[test] | ||
639 | fn method_with_arg() { | ||
640 | check_diagnostics( | ||
641 | r#" | ||
642 | struct S; | ||
643 | impl S { fn method(&self, arg: u8) {} } | ||
644 | |||
645 | fn f() { | ||
646 | S.method(); | ||
647 | } //^^^^^^^^^^ Expected 1 argument, found 0 | ||
648 | "#, | ||
649 | ); | ||
650 | |||
651 | check_diagnostics( | ||
652 | r#" | ||
653 | struct S; | ||
654 | impl S { fn method(&self, arg: u8) {} } | ||
655 | |||
656 | fn f() { | ||
657 | S::method(&S, 0); | ||
658 | S.method(1); | ||
659 | } | ||
660 | "#, | ||
661 | ); | ||
662 | } | ||
663 | |||
664 | #[test] | ||
665 | fn method_unknown_receiver() { | ||
666 | // note: this is incorrect code, so there might be errors on this in the | ||
667 | // future, but we shouldn't emit an argument count diagnostic here | ||
668 | check_diagnostics( | ||
669 | r#" | ||
670 | trait Foo { fn method(&self, arg: usize) {} } | ||
671 | |||
672 | fn f() { | ||
673 | let x; | ||
674 | x.method(); | ||
675 | } | ||
676 | "#, | ||
677 | ); | ||
678 | } | ||
679 | |||
680 | #[test] | ||
681 | fn tuple_struct() { | ||
682 | check_diagnostics( | ||
683 | r#" | ||
684 | struct Tup(u8, u16); | ||
685 | fn f() { | ||
686 | Tup(0); | ||
687 | } //^^^^^^ Expected 2 arguments, found 1 | ||
688 | "#, | ||
689 | ) | ||
690 | } | ||
691 | |||
692 | #[test] | ||
693 | fn enum_variant() { | ||
694 | check_diagnostics( | ||
695 | r#" | ||
696 | enum En { Variant(u8, u16), } | ||
697 | fn f() { | ||
698 | En::Variant(0); | ||
699 | } //^^^^^^^^^^^^^^ Expected 2 arguments, found 1 | ||
700 | "#, | ||
701 | ) | ||
702 | } | ||
703 | |||
704 | #[test] | ||
705 | fn enum_variant_type_macro() { | ||
706 | check_diagnostics( | ||
707 | r#" | ||
708 | macro_rules! Type { | ||
709 | () => { u32 }; | ||
710 | } | ||
711 | enum Foo { | ||
712 | Bar(Type![]) | ||
713 | } | ||
714 | impl Foo { | ||
715 | fn new() { | ||
716 | Foo::Bar(0); | ||
717 | Foo::Bar(0, 1); | ||
718 | //^^^^^^^^^^^^^^ Expected 1 argument, found 2 | ||
719 | Foo::Bar(); | ||
720 | //^^^^^^^^^^ Expected 1 argument, found 0 | ||
721 | } | ||
722 | } | ||
723 | "#, | ||
724 | ); | ||
725 | } | ||
726 | |||
727 | #[test] | ||
728 | fn varargs() { | ||
729 | check_diagnostics( | ||
730 | r#" | ||
731 | extern "C" { | ||
732 | fn fixed(fixed: u8); | ||
733 | fn varargs(fixed: u8, ...); | ||
734 | fn varargs2(...); | ||
735 | } | ||
736 | |||
737 | fn f() { | ||
738 | unsafe { | ||
739 | fixed(0); | ||
740 | fixed(0, 1); | ||
741 | //^^^^^^^^^^^ Expected 1 argument, found 2 | ||
742 | varargs(0); | ||
743 | varargs(0, 1); | ||
744 | varargs2(); | ||
745 | varargs2(0); | ||
746 | varargs2(0, 1); | ||
747 | } | ||
748 | } | ||
749 | "#, | ||
750 | ) | ||
751 | } | ||
752 | |||
753 | #[test] | ||
754 | fn arg_count_lambda() { | ||
755 | check_diagnostics( | ||
756 | r#" | ||
757 | fn main() { | ||
758 | let f = |()| (); | ||
759 | f(); | ||
760 | //^^^ Expected 1 argument, found 0 | ||
761 | f(()); | ||
762 | f((), ()); | ||
763 | //^^^^^^^^^ Expected 1 argument, found 2 | ||
764 | } | ||
765 | "#, | ||
766 | ) | ||
767 | } | ||
768 | |||
769 | #[test] | ||
770 | fn cfgd_out_call_arguments() { | ||
771 | check_diagnostics( | ||
772 | r#" | ||
773 | struct C(#[cfg(FALSE)] ()); | ||
774 | impl C { | ||
775 | fn new() -> Self { | ||
776 | Self( | ||
777 | #[cfg(FALSE)] | ||
778 | (), | ||
779 | ) | ||
780 | } | ||
781 | |||
782 | fn method(&self) {} | ||
783 | } | ||
784 | |||
785 | fn main() { | ||
786 | C::new().method(#[cfg(FALSE)] 0); | ||
787 | } | ||
788 | "#, | ||
789 | ); | ||
790 | } | ||
791 | |||
792 | #[test] | ||
793 | fn cfgd_out_fn_params() { | ||
794 | check_diagnostics( | ||
795 | r#" | ||
796 | fn foo(#[cfg(NEVER)] x: ()) {} | ||
797 | |||
798 | struct S; | ||
799 | |||
800 | impl S { | ||
801 | fn method(#[cfg(NEVER)] self) {} | ||
802 | fn method2(#[cfg(NEVER)] self, arg: u8) {} | ||
803 | fn method3(self, #[cfg(NEVER)] arg: u8) {} | ||
804 | } | ||
805 | |||
806 | extern "C" { | ||
807 | fn fixed(fixed: u8, #[cfg(NEVER)] ...); | ||
808 | fn varargs(#[cfg(not(NEVER))] ...); | ||
809 | } | ||
810 | |||
811 | fn main() { | ||
812 | foo(); | ||
813 | S::method(); | ||
814 | S::method2(0); | ||
815 | S::method3(S); | ||
816 | S.method3(); | ||
817 | unsafe { | ||
818 | fixed(0); | ||
819 | varargs(1, 2, 3); | ||
820 | } | ||
821 | } | ||
822 | "#, | ||
823 | ) | ||
824 | } | ||
825 | } | ||
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index c8e1b23de..a30e42699 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -364,960 +364,3 @@ impl PatternFoldable for PatKind { | |||
364 | } | 364 | } |
365 | } | 365 | } |
366 | } | 366 | } |
367 | |||
368 | #[cfg(test)] | ||
369 | pub(super) mod tests { | ||
370 | mod report { | ||
371 | use std::any::Any; | ||
372 | |||
373 | use hir_def::{expr::PatId, DefWithBodyId}; | ||
374 | use hir_expand::{HirFileId, InFile}; | ||
375 | use syntax::SyntaxNodePtr; | ||
376 | |||
377 | use crate::{ | ||
378 | db::HirDatabase, | ||
379 | diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink}, | ||
380 | }; | ||
381 | |||
382 | /// In tests, match check bails out loudly. | ||
383 | /// This helps to catch incorrect tests that pass due to false negatives. | ||
384 | pub(crate) fn report_bail_out( | ||
385 | db: &dyn HirDatabase, | ||
386 | def: DefWithBodyId, | ||
387 | pat: PatId, | ||
388 | sink: &mut DiagnosticSink, | ||
389 | ) { | ||
390 | let (_, source_map) = db.body_with_source_map(def); | ||
391 | if let Ok(source_ptr) = source_map.pat_syntax(pat) { | ||
392 | let pat_syntax_ptr = source_ptr.value.either(Into::into, Into::into); | ||
393 | sink.push(BailedOut { file: source_ptr.file_id, pat_syntax_ptr }); | ||
394 | } | ||
395 | } | ||
396 | |||
397 | #[derive(Debug)] | ||
398 | struct BailedOut { | ||
399 | file: HirFileId, | ||
400 | pat_syntax_ptr: SyntaxNodePtr, | ||
401 | } | ||
402 | |||
403 | impl Diagnostic for BailedOut { | ||
404 | fn code(&self) -> DiagnosticCode { | ||
405 | DiagnosticCode("internal:match-check-bailed-out") | ||
406 | } | ||
407 | fn message(&self) -> String { | ||
408 | format!("Internal: match check bailed out") | ||
409 | } | ||
410 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | ||
411 | InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() } | ||
412 | } | ||
413 | fn as_any(&self) -> &(dyn Any + Send + 'static) { | ||
414 | self | ||
415 | } | ||
416 | } | ||
417 | } | ||
418 | |||
419 | use crate::diagnostics::tests::check_diagnostics; | ||
420 | |||
421 | pub(crate) use self::report::report_bail_out; | ||
422 | |||
423 | #[test] | ||
424 | fn empty_tuple() { | ||
425 | check_diagnostics( | ||
426 | r#" | ||
427 | fn main() { | ||
428 | match () { } | ||
429 | //^^ Missing match arm | ||
430 | match (()) { } | ||
431 | //^^^^ Missing match arm | ||
432 | |||
433 | match () { _ => (), } | ||
434 | match () { () => (), } | ||
435 | match (()) { (()) => (), } | ||
436 | } | ||
437 | "#, | ||
438 | ); | ||
439 | } | ||
440 | |||
441 | #[test] | ||
442 | fn tuple_of_two_empty_tuple() { | ||
443 | check_diagnostics( | ||
444 | r#" | ||
445 | fn main() { | ||
446 | match ((), ()) { } | ||
447 | //^^^^^^^^ Missing match arm | ||
448 | |||
449 | match ((), ()) { ((), ()) => (), } | ||
450 | } | ||
451 | "#, | ||
452 | ); | ||
453 | } | ||
454 | |||
455 | #[test] | ||
456 | fn boolean() { | ||
457 | check_diagnostics( | ||
458 | r#" | ||
459 | fn test_main() { | ||
460 | match false { } | ||
461 | //^^^^^ Missing match arm | ||
462 | match false { true => (), } | ||
463 | //^^^^^ Missing match arm | ||
464 | match (false, true) {} | ||
465 | //^^^^^^^^^^^^^ Missing match arm | ||
466 | match (false, true) { (true, true) => (), } | ||
467 | //^^^^^^^^^^^^^ Missing match arm | ||
468 | match (false, true) { | ||
469 | //^^^^^^^^^^^^^ Missing match arm | ||
470 | (false, true) => (), | ||
471 | (false, false) => (), | ||
472 | (true, false) => (), | ||
473 | } | ||
474 | match (false, true) { (true, _x) => (), } | ||
475 | //^^^^^^^^^^^^^ Missing match arm | ||
476 | |||
477 | match false { true => (), false => (), } | ||
478 | match (false, true) { | ||
479 | (false, _) => (), | ||
480 | (true, false) => (), | ||
481 | (_, true) => (), | ||
482 | } | ||
483 | match (false, true) { | ||
484 | (true, true) => (), | ||
485 | (true, false) => (), | ||
486 | (false, true) => (), | ||
487 | (false, false) => (), | ||
488 | } | ||
489 | match (false, true) { | ||
490 | (true, _x) => (), | ||
491 | (false, true) => (), | ||
492 | (false, false) => (), | ||
493 | } | ||
494 | match (false, true, false) { | ||
495 | (false, ..) => (), | ||
496 | (true, ..) => (), | ||
497 | } | ||
498 | match (false, true, false) { | ||
499 | (.., false) => (), | ||
500 | (.., true) => (), | ||
501 | } | ||
502 | match (false, true, false) { (..) => (), } | ||
503 | } | ||
504 | "#, | ||
505 | ); | ||
506 | } | ||
507 | |||
508 | #[test] | ||
509 | fn tuple_of_tuple_and_bools() { | ||
510 | check_diagnostics( | ||
511 | r#" | ||
512 | fn main() { | ||
513 | match (false, ((), false)) {} | ||
514 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
515 | match (false, ((), false)) { (true, ((), true)) => (), } | ||
516 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
517 | match (false, ((), false)) { (true, _) => (), } | ||
518 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
519 | |||
520 | match (false, ((), false)) { | ||
521 | (true, ((), true)) => (), | ||
522 | (true, ((), false)) => (), | ||
523 | (false, ((), true)) => (), | ||
524 | (false, ((), false)) => (), | ||
525 | } | ||
526 | match (false, ((), false)) { | ||
527 | (true, ((), true)) => (), | ||
528 | (true, ((), false)) => (), | ||
529 | (false, _) => (), | ||
530 | } | ||
531 | } | ||
532 | "#, | ||
533 | ); | ||
534 | } | ||
535 | |||
536 | #[test] | ||
537 | fn enums() { | ||
538 | check_diagnostics( | ||
539 | r#" | ||
540 | enum Either { A, B, } | ||
541 | |||
542 | fn main() { | ||
543 | match Either::A { } | ||
544 | //^^^^^^^^^ Missing match arm | ||
545 | match Either::B { Either::A => (), } | ||
546 | //^^^^^^^^^ Missing match arm | ||
547 | |||
548 | match &Either::B { | ||
549 | //^^^^^^^^^^ Missing match arm | ||
550 | Either::A => (), | ||
551 | } | ||
552 | |||
553 | match Either::B { | ||
554 | Either::A => (), Either::B => (), | ||
555 | } | ||
556 | match &Either::B { | ||
557 | Either::A => (), Either::B => (), | ||
558 | } | ||
559 | } | ||
560 | "#, | ||
561 | ); | ||
562 | } | ||
563 | |||
564 | #[test] | ||
565 | fn enum_containing_bool() { | ||
566 | check_diagnostics( | ||
567 | r#" | ||
568 | enum Either { A(bool), B } | ||
569 | |||
570 | fn main() { | ||
571 | match Either::B { } | ||
572 | //^^^^^^^^^ Missing match arm | ||
573 | match Either::B { | ||
574 | //^^^^^^^^^ Missing match arm | ||
575 | Either::A(true) => (), Either::B => () | ||
576 | } | ||
577 | |||
578 | match Either::B { | ||
579 | Either::A(true) => (), | ||
580 | Either::A(false) => (), | ||
581 | Either::B => (), | ||
582 | } | ||
583 | match Either::B { | ||
584 | Either::B => (), | ||
585 | _ => (), | ||
586 | } | ||
587 | match Either::B { | ||
588 | Either::A(_) => (), | ||
589 | Either::B => (), | ||
590 | } | ||
591 | |||
592 | } | ||
593 | "#, | ||
594 | ); | ||
595 | } | ||
596 | |||
597 | #[test] | ||
598 | fn enum_different_sizes() { | ||
599 | check_diagnostics( | ||
600 | r#" | ||
601 | enum Either { A(bool), B(bool, bool) } | ||
602 | |||
603 | fn main() { | ||
604 | match Either::A(false) { | ||
605 | //^^^^^^^^^^^^^^^^ Missing match arm | ||
606 | Either::A(_) => (), | ||
607 | Either::B(false, _) => (), | ||
608 | } | ||
609 | |||
610 | match Either::A(false) { | ||
611 | Either::A(_) => (), | ||
612 | Either::B(true, _) => (), | ||
613 | Either::B(false, _) => (), | ||
614 | } | ||
615 | match Either::A(false) { | ||
616 | Either::A(true) | Either::A(false) => (), | ||
617 | Either::B(true, _) => (), | ||
618 | Either::B(false, _) => (), | ||
619 | } | ||
620 | } | ||
621 | "#, | ||
622 | ); | ||
623 | } | ||
624 | |||
625 | #[test] | ||
626 | fn tuple_of_enum_no_diagnostic() { | ||
627 | check_diagnostics( | ||
628 | r#" | ||
629 | enum Either { A(bool), B(bool, bool) } | ||
630 | enum Either2 { C, D } | ||
631 | |||
632 | fn main() { | ||
633 | match (Either::A(false), Either2::C) { | ||
634 | (Either::A(true), _) | (Either::A(false), _) => (), | ||
635 | (Either::B(true, _), Either2::C) => (), | ||
636 | (Either::B(false, _), Either2::C) => (), | ||
637 | (Either::B(_, _), Either2::D) => (), | ||
638 | } | ||
639 | } | ||
640 | "#, | ||
641 | ); | ||
642 | } | ||
643 | |||
644 | #[test] | ||
645 | fn or_pattern_no_diagnostic() { | ||
646 | check_diagnostics( | ||
647 | r#" | ||
648 | enum Either {A, B} | ||
649 | |||
650 | fn main() { | ||
651 | match (Either::A, Either::B) { | ||
652 | (Either::A | Either::B, _) => (), | ||
653 | } | ||
654 | }"#, | ||
655 | ) | ||
656 | } | ||
657 | |||
658 | #[test] | ||
659 | fn mismatched_types() { | ||
660 | // Match statements with arms that don't match the | ||
661 | // expression pattern do not fire this diagnostic. | ||
662 | check_diagnostics( | ||
663 | r#" | ||
664 | enum Either { A, B } | ||
665 | enum Either2 { C, D } | ||
666 | |||
667 | fn main() { | ||
668 | match Either::A { | ||
669 | Either2::C => (), | ||
670 | // ^^^^^^^^^^ Internal: match check bailed out | ||
671 | Either2::D => (), | ||
672 | } | ||
673 | match (true, false) { | ||
674 | (true, false, true) => (), | ||
675 | // ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out | ||
676 | (true) => (), | ||
677 | } | ||
678 | match (true, false) { (true,) => {} } | ||
679 | // ^^^^^^^ Internal: match check bailed out | ||
680 | match (0) { () => () } | ||
681 | // ^^ Internal: match check bailed out | ||
682 | match Unresolved::Bar { Unresolved::Baz => () } | ||
683 | } | ||
684 | "#, | ||
685 | ); | ||
686 | } | ||
687 | |||
688 | #[test] | ||
689 | fn mismatched_types_in_or_patterns() { | ||
690 | check_diagnostics( | ||
691 | r#" | ||
692 | fn main() { | ||
693 | match false { true | () => {} } | ||
694 | // ^^^^^^^^^ Internal: match check bailed out | ||
695 | match (false,) { (true | (),) => {} } | ||
696 | // ^^^^^^^^^^^^ Internal: match check bailed out | ||
697 | } | ||
698 | "#, | ||
699 | ); | ||
700 | } | ||
701 | |||
702 | #[test] | ||
703 | fn malformed_match_arm_tuple_enum_missing_pattern() { | ||
704 | // We are testing to be sure we don't panic here when the match | ||
705 | // arm `Either::B` is missing its pattern. | ||
706 | check_diagnostics( | ||
707 | r#" | ||
708 | enum Either { A, B(u32) } | ||
709 | |||
710 | fn main() { | ||
711 | match Either::A { | ||
712 | Either::A => (), | ||
713 | Either::B() => (), | ||
714 | } | ||
715 | } | ||
716 | "#, | ||
717 | ); | ||
718 | } | ||
719 | |||
720 | #[test] | ||
721 | fn malformed_match_arm_extra_fields() { | ||
722 | check_diagnostics( | ||
723 | r#" | ||
724 | enum A { B(isize, isize), C } | ||
725 | fn main() { | ||
726 | match A::B(1, 2) { | ||
727 | A::B(_, _, _) => (), | ||
728 | // ^^^^^^^^^^^^^ Internal: match check bailed out | ||
729 | } | ||
730 | match A::B(1, 2) { | ||
731 | A::C(_) => (), | ||
732 | // ^^^^^^^ Internal: match check bailed out | ||
733 | } | ||
734 | } | ||
735 | "#, | ||
736 | ); | ||
737 | } | ||
738 | |||
739 | #[test] | ||
740 | fn expr_diverges() { | ||
741 | check_diagnostics( | ||
742 | r#" | ||
743 | enum Either { A, B } | ||
744 | |||
745 | fn main() { | ||
746 | match loop {} { | ||
747 | Either::A => (), | ||
748 | // ^^^^^^^^^ Internal: match check bailed out | ||
749 | Either::B => (), | ||
750 | } | ||
751 | match loop {} { | ||
752 | Either::A => (), | ||
753 | // ^^^^^^^^^ Internal: match check bailed out | ||
754 | } | ||
755 | match loop { break Foo::A } { | ||
756 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
757 | Either::A => (), | ||
758 | } | ||
759 | match loop { break Foo::A } { | ||
760 | Either::A => (), | ||
761 | Either::B => (), | ||
762 | } | ||
763 | } | ||
764 | "#, | ||
765 | ); | ||
766 | } | ||
767 | |||
768 | #[test] | ||
769 | fn expr_partially_diverges() { | ||
770 | check_diagnostics( | ||
771 | r#" | ||
772 | enum Either<T> { A(T), B } | ||
773 | |||
774 | fn foo() -> Either<!> { Either::B } | ||
775 | fn main() -> u32 { | ||
776 | match foo() { | ||
777 | Either::A(val) => val, | ||
778 | Either::B => 0, | ||
779 | } | ||
780 | } | ||
781 | "#, | ||
782 | ); | ||
783 | } | ||
784 | |||
785 | #[test] | ||
786 | fn enum_record() { | ||
787 | check_diagnostics( | ||
788 | r#" | ||
789 | enum Either { A { foo: bool }, B } | ||
790 | |||
791 | fn main() { | ||
792 | let a = Either::A { foo: true }; | ||
793 | match a { } | ||
794 | //^ Missing match arm | ||
795 | match a { Either::A { foo: true } => () } | ||
796 | //^ Missing match arm | ||
797 | match a { | ||
798 | Either::A { } => (), | ||
799 | //^^^^^^^^^ Missing structure fields: | ||
800 | // | - foo | ||
801 | Either::B => (), | ||
802 | } | ||
803 | match a { | ||
804 | //^ Missing match arm | ||
805 | Either::A { } => (), | ||
806 | } //^^^^^^^^^ Missing structure fields: | ||
807 | // | - foo | ||
808 | |||
809 | match a { | ||
810 | Either::A { foo: true } => (), | ||
811 | Either::A { foo: false } => (), | ||
812 | Either::B => (), | ||
813 | } | ||
814 | match a { | ||
815 | Either::A { foo: _ } => (), | ||
816 | Either::B => (), | ||
817 | } | ||
818 | } | ||
819 | "#, | ||
820 | ); | ||
821 | } | ||
822 | |||
823 | #[test] | ||
824 | fn enum_record_fields_out_of_order() { | ||
825 | check_diagnostics( | ||
826 | r#" | ||
827 | enum Either { | ||
828 | A { foo: bool, bar: () }, | ||
829 | B, | ||
830 | } | ||
831 | |||
832 | fn main() { | ||
833 | let a = Either::A { foo: true, bar: () }; | ||
834 | match a { | ||
835 | //^ Missing match arm | ||
836 | Either::A { bar: (), foo: false } => (), | ||
837 | Either::A { foo: true, bar: () } => (), | ||
838 | } | ||
839 | |||
840 | match a { | ||
841 | Either::A { bar: (), foo: false } => (), | ||
842 | Either::A { foo: true, bar: () } => (), | ||
843 | Either::B => (), | ||
844 | } | ||
845 | } | ||
846 | "#, | ||
847 | ); | ||
848 | } | ||
849 | |||
850 | #[test] | ||
851 | fn enum_record_ellipsis() { | ||
852 | check_diagnostics( | ||
853 | r#" | ||
854 | enum Either { | ||
855 | A { foo: bool, bar: bool }, | ||
856 | B, | ||
857 | } | ||
858 | |||
859 | fn main() { | ||
860 | let a = Either::B; | ||
861 | match a { | ||
862 | //^ Missing match arm | ||
863 | Either::A { foo: true, .. } => (), | ||
864 | Either::B => (), | ||
865 | } | ||
866 | match a { | ||
867 | //^ Missing match arm | ||
868 | Either::A { .. } => (), | ||
869 | } | ||
870 | |||
871 | match a { | ||
872 | Either::A { foo: true, .. } => (), | ||
873 | Either::A { foo: false, .. } => (), | ||
874 | Either::B => (), | ||
875 | } | ||
876 | |||
877 | match a { | ||
878 | Either::A { .. } => (), | ||
879 | Either::B => (), | ||
880 | } | ||
881 | } | ||
882 | "#, | ||
883 | ); | ||
884 | } | ||
885 | |||
886 | #[test] | ||
887 | fn enum_tuple_partial_ellipsis() { | ||
888 | check_diagnostics( | ||
889 | r#" | ||
890 | enum Either { | ||
891 | A(bool, bool, bool, bool), | ||
892 | B, | ||
893 | } | ||
894 | |||
895 | fn main() { | ||
896 | match Either::B { | ||
897 | //^^^^^^^^^ Missing match arm | ||
898 | Either::A(true, .., true) => (), | ||
899 | Either::A(true, .., false) => (), | ||
900 | Either::A(false, .., false) => (), | ||
901 | Either::B => (), | ||
902 | } | ||
903 | match Either::B { | ||
904 | //^^^^^^^^^ Missing match arm | ||
905 | Either::A(true, .., true) => (), | ||
906 | Either::A(true, .., false) => (), | ||
907 | Either::A(.., true) => (), | ||
908 | Either::B => (), | ||
909 | } | ||
910 | |||
911 | match Either::B { | ||
912 | Either::A(true, .., true) => (), | ||
913 | Either::A(true, .., false) => (), | ||
914 | Either::A(false, .., true) => (), | ||
915 | Either::A(false, .., false) => (), | ||
916 | Either::B => (), | ||
917 | } | ||
918 | match Either::B { | ||
919 | Either::A(true, .., true) => (), | ||
920 | Either::A(true, .., false) => (), | ||
921 | Either::A(.., true) => (), | ||
922 | Either::A(.., false) => (), | ||
923 | Either::B => (), | ||
924 | } | ||
925 | } | ||
926 | "#, | ||
927 | ); | ||
928 | } | ||
929 | |||
930 | #[test] | ||
931 | fn never() { | ||
932 | check_diagnostics( | ||
933 | r#" | ||
934 | enum Never {} | ||
935 | |||
936 | fn enum_(never: Never) { | ||
937 | match never {} | ||
938 | } | ||
939 | fn enum_ref(never: &Never) { | ||
940 | match never {} | ||
941 | //^^^^^ Missing match arm | ||
942 | } | ||
943 | fn bang(never: !) { | ||
944 | match never {} | ||
945 | } | ||
946 | "#, | ||
947 | ); | ||
948 | } | ||
949 | |||
950 | #[test] | ||
951 | fn unknown_type() { | ||
952 | check_diagnostics( | ||
953 | r#" | ||
954 | enum Option<T> { Some(T), None } | ||
955 | |||
956 | fn main() { | ||
957 | // `Never` is deliberately not defined so that it's an uninferred type. | ||
958 | match Option::<Never>::None { | ||
959 | None => (), | ||
960 | Some(never) => match never {}, | ||
961 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
962 | } | ||
963 | match Option::<Never>::None { | ||
964 | //^^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
965 | Option::Some(_never) => {}, | ||
966 | } | ||
967 | } | ||
968 | "#, | ||
969 | ); | ||
970 | } | ||
971 | |||
972 | #[test] | ||
973 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
974 | check_diagnostics( | ||
975 | r#" | ||
976 | fn main() { | ||
977 | match (false, true, false) { | ||
978 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
979 | (false, ..) => (), | ||
980 | } | ||
981 | }"#, | ||
982 | ); | ||
983 | } | ||
984 | |||
985 | #[test] | ||
986 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
987 | check_diagnostics( | ||
988 | r#" | ||
989 | fn main() { | ||
990 | match (false, true, false) { | ||
991 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
992 | (.., false) => (), | ||
993 | } | ||
994 | }"#, | ||
995 | ); | ||
996 | } | ||
997 | |||
998 | #[test] | ||
999 | fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() { | ||
1000 | check_diagnostics( | ||
1001 | r#" | ||
1002 | fn main() { | ||
1003 | match (false, true, false) { | ||
1004 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1005 | (true, .., false) => (), | ||
1006 | } | ||
1007 | }"#, | ||
1008 | ); | ||
1009 | } | ||
1010 | |||
1011 | #[test] | ||
1012 | fn record_struct() { | ||
1013 | check_diagnostics( | ||
1014 | r#"struct Foo { a: bool } | ||
1015 | fn main(f: Foo) { | ||
1016 | match f {} | ||
1017 | //^ Missing match arm | ||
1018 | match f { Foo { a: true } => () } | ||
1019 | //^ Missing match arm | ||
1020 | match &f { Foo { a: true } => () } | ||
1021 | //^^ Missing match arm | ||
1022 | match f { Foo { a: _ } => () } | ||
1023 | match f { | ||
1024 | Foo { a: true } => (), | ||
1025 | Foo { a: false } => (), | ||
1026 | } | ||
1027 | match &f { | ||
1028 | Foo { a: true } => (), | ||
1029 | Foo { a: false } => (), | ||
1030 | } | ||
1031 | } | ||
1032 | "#, | ||
1033 | ); | ||
1034 | } | ||
1035 | |||
1036 | #[test] | ||
1037 | fn tuple_struct() { | ||
1038 | check_diagnostics( | ||
1039 | r#"struct Foo(bool); | ||
1040 | fn main(f: Foo) { | ||
1041 | match f {} | ||
1042 | //^ Missing match arm | ||
1043 | match f { Foo(true) => () } | ||
1044 | //^ Missing match arm | ||
1045 | match f { | ||
1046 | Foo(true) => (), | ||
1047 | Foo(false) => (), | ||
1048 | } | ||
1049 | } | ||
1050 | "#, | ||
1051 | ); | ||
1052 | } | ||
1053 | |||
1054 | #[test] | ||
1055 | fn unit_struct() { | ||
1056 | check_diagnostics( | ||
1057 | r#"struct Foo; | ||
1058 | fn main(f: Foo) { | ||
1059 | match f {} | ||
1060 | //^ Missing match arm | ||
1061 | match f { Foo => () } | ||
1062 | } | ||
1063 | "#, | ||
1064 | ); | ||
1065 | } | ||
1066 | |||
1067 | #[test] | ||
1068 | fn record_struct_ellipsis() { | ||
1069 | check_diagnostics( | ||
1070 | r#"struct Foo { foo: bool, bar: bool } | ||
1071 | fn main(f: Foo) { | ||
1072 | match f { Foo { foo: true, .. } => () } | ||
1073 | //^ Missing match arm | ||
1074 | match f { | ||
1075 | //^ Missing match arm | ||
1076 | Foo { foo: true, .. } => (), | ||
1077 | Foo { bar: false, .. } => () | ||
1078 | } | ||
1079 | match f { Foo { .. } => () } | ||
1080 | match f { | ||
1081 | Foo { foo: true, .. } => (), | ||
1082 | Foo { foo: false, .. } => () | ||
1083 | } | ||
1084 | } | ||
1085 | "#, | ||
1086 | ); | ||
1087 | } | ||
1088 | |||
1089 | #[test] | ||
1090 | fn internal_or() { | ||
1091 | check_diagnostics( | ||
1092 | r#" | ||
1093 | fn main() { | ||
1094 | enum Either { A(bool), B } | ||
1095 | match Either::B { | ||
1096 | //^^^^^^^^^ Missing match arm | ||
1097 | Either::A(true | false) => (), | ||
1098 | } | ||
1099 | } | ||
1100 | "#, | ||
1101 | ); | ||
1102 | } | ||
1103 | |||
1104 | #[test] | ||
1105 | fn no_panic_at_unimplemented_subpattern_type() { | ||
1106 | check_diagnostics( | ||
1107 | r#" | ||
1108 | struct S { a: char} | ||
1109 | fn main(v: S) { | ||
1110 | match v { S{ a } => {} } | ||
1111 | match v { S{ a: _x } => {} } | ||
1112 | match v { S{ a: 'a' } => {} } | ||
1113 | //^^^^^^^^^^^ Internal: match check bailed out | ||
1114 | match v { S{..} => {} } | ||
1115 | match v { _ => {} } | ||
1116 | match v { } | ||
1117 | //^ Missing match arm | ||
1118 | } | ||
1119 | "#, | ||
1120 | ); | ||
1121 | } | ||
1122 | |||
1123 | #[test] | ||
1124 | fn binding() { | ||
1125 | check_diagnostics( | ||
1126 | r#" | ||
1127 | fn main() { | ||
1128 | match true { | ||
1129 | _x @ true => {} | ||
1130 | false => {} | ||
1131 | } | ||
1132 | match true { _x @ true => {} } | ||
1133 | //^^^^ Missing match arm | ||
1134 | } | ||
1135 | "#, | ||
1136 | ); | ||
1137 | } | ||
1138 | |||
1139 | #[test] | ||
1140 | fn binding_ref_has_correct_type() { | ||
1141 | // Asserts `PatKind::Binding(ref _x): bool`, not &bool. | ||
1142 | // If that's not true match checking will panic with "incompatible constructors" | ||
1143 | // FIXME: make facilities to test this directly like `tests::check_infer(..)` | ||
1144 | check_diagnostics( | ||
1145 | r#" | ||
1146 | enum Foo { A } | ||
1147 | fn main() { | ||
1148 | // FIXME: this should not bail out but current behavior is such as the old algorithm. | ||
1149 | // ExprValidator::validate_match(..) checks types of top level patterns incorrecly. | ||
1150 | match Foo::A { | ||
1151 | ref _x => {} | ||
1152 | // ^^^^^^ Internal: match check bailed out | ||
1153 | Foo::A => {} | ||
1154 | } | ||
1155 | match (true,) { | ||
1156 | (ref _x,) => {} | ||
1157 | (true,) => {} | ||
1158 | } | ||
1159 | } | ||
1160 | "#, | ||
1161 | ); | ||
1162 | } | ||
1163 | |||
1164 | #[test] | ||
1165 | fn enum_non_exhaustive() { | ||
1166 | check_diagnostics( | ||
1167 | r#" | ||
1168 | //- /lib.rs crate:lib | ||
1169 | #[non_exhaustive] | ||
1170 | pub enum E { A, B } | ||
1171 | fn _local() { | ||
1172 | match E::A { _ => {} } | ||
1173 | match E::A { | ||
1174 | E::A => {} | ||
1175 | E::B => {} | ||
1176 | } | ||
1177 | match E::A { | ||
1178 | E::A | E::B => {} | ||
1179 | } | ||
1180 | } | ||
1181 | |||
1182 | //- /main.rs crate:main deps:lib | ||
1183 | use lib::E; | ||
1184 | fn main() { | ||
1185 | match E::A { _ => {} } | ||
1186 | match E::A { | ||
1187 | //^^^^ Missing match arm | ||
1188 | E::A => {} | ||
1189 | E::B => {} | ||
1190 | } | ||
1191 | match E::A { | ||
1192 | //^^^^ Missing match arm | ||
1193 | E::A | E::B => {} | ||
1194 | } | ||
1195 | } | ||
1196 | "#, | ||
1197 | ); | ||
1198 | } | ||
1199 | |||
1200 | #[test] | ||
1201 | fn match_guard() { | ||
1202 | check_diagnostics( | ||
1203 | r#" | ||
1204 | fn main() { | ||
1205 | match true { | ||
1206 | true if false => {} | ||
1207 | true => {} | ||
1208 | false => {} | ||
1209 | } | ||
1210 | match true { | ||
1211 | //^^^^ Missing match arm | ||
1212 | true if false => {} | ||
1213 | false => {} | ||
1214 | } | ||
1215 | "#, | ||
1216 | ); | ||
1217 | } | ||
1218 | |||
1219 | #[test] | ||
1220 | fn pattern_type_is_of_substitution() { | ||
1221 | cov_mark::check!(match_check_wildcard_expanded_to_substitutions); | ||
1222 | check_diagnostics( | ||
1223 | r#" | ||
1224 | struct Foo<T>(T); | ||
1225 | struct Bar; | ||
1226 | fn main() { | ||
1227 | match Foo(Bar) { | ||
1228 | _ | Foo(Bar) => {} | ||
1229 | } | ||
1230 | } | ||
1231 | "#, | ||
1232 | ); | ||
1233 | } | ||
1234 | |||
1235 | #[test] | ||
1236 | fn record_struct_no_such_field() { | ||
1237 | check_diagnostics( | ||
1238 | r#" | ||
1239 | struct Foo { } | ||
1240 | fn main(f: Foo) { | ||
1241 | match f { Foo { bar } => () } | ||
1242 | // ^^^^^^^^^^^ Internal: match check bailed out | ||
1243 | } | ||
1244 | "#, | ||
1245 | ); | ||
1246 | } | ||
1247 | |||
1248 | #[test] | ||
1249 | fn match_ergonomics_issue_9095() { | ||
1250 | check_diagnostics( | ||
1251 | r#" | ||
1252 | enum Foo<T> { A(T) } | ||
1253 | fn main() { | ||
1254 | match &Foo::A(true) { | ||
1255 | _ => {} | ||
1256 | Foo::A(_) => {} | ||
1257 | } | ||
1258 | } | ||
1259 | "#, | ||
1260 | ); | ||
1261 | } | ||
1262 | |||
1263 | mod false_negatives { | ||
1264 | //! The implementation of match checking here is a work in progress. As we roll this out, we | ||
1265 | //! prefer false negatives to false positives (ideally there would be no false positives). This | ||
1266 | //! test module should document known false negatives. Eventually we will have a complete | ||
1267 | //! implementation of match checking and this module will be empty. | ||
1268 | //! | ||
1269 | //! The reasons for documenting known false negatives: | ||
1270 | //! | ||
1271 | //! 1. It acts as a backlog of work that can be done to improve the behavior of the system. | ||
1272 | //! 2. It ensures the code doesn't panic when handling these cases. | ||
1273 | use super::*; | ||
1274 | |||
1275 | #[test] | ||
1276 | fn integers() { | ||
1277 | // We don't currently check integer exhaustiveness. | ||
1278 | check_diagnostics( | ||
1279 | r#" | ||
1280 | fn main() { | ||
1281 | match 5 { | ||
1282 | 10 => (), | ||
1283 | // ^^ Internal: match check bailed out | ||
1284 | 11..20 => (), | ||
1285 | } | ||
1286 | } | ||
1287 | "#, | ||
1288 | ); | ||
1289 | } | ||
1290 | |||
1291 | #[test] | ||
1292 | fn reference_patterns_at_top_level() { | ||
1293 | check_diagnostics( | ||
1294 | r#" | ||
1295 | fn main() { | ||
1296 | match &false { | ||
1297 | &true => {} | ||
1298 | // ^^^^^ Internal: match check bailed out | ||
1299 | } | ||
1300 | } | ||
1301 | "#, | ||
1302 | ); | ||
1303 | } | ||
1304 | |||
1305 | #[test] | ||
1306 | fn reference_patterns_in_fields() { | ||
1307 | check_diagnostics( | ||
1308 | r#" | ||
1309 | fn main() { | ||
1310 | match (&false,) { | ||
1311 | (true,) => {} | ||
1312 | // ^^^^^^^ Internal: match check bailed out | ||
1313 | } | ||
1314 | match (&false,) { | ||
1315 | (&true,) => {} | ||
1316 | // ^^^^^^^^ Internal: match check bailed out | ||
1317 | } | ||
1318 | } | ||
1319 | "#, | ||
1320 | ); | ||
1321 | } | ||
1322 | } | ||
1323 | } | ||
diff --git a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs index 1f4219b42..471cd4921 100644 --- a/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs | |||
@@ -528,7 +528,7 @@ impl SplitWildcard { | |||
528 | smallvec![NonExhaustive] | 528 | smallvec![NonExhaustive] |
529 | } | 529 | } |
530 | TyKind::Never => SmallVec::new(), | 530 | TyKind::Never => SmallVec::new(), |
531 | _ if cx.is_uninhabited(&pcx.ty) => SmallVec::new(), | 531 | _ if cx.is_uninhabited(pcx.ty) => SmallVec::new(), |
532 | TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single], | 532 | TyKind::Adt(..) | TyKind::Tuple(..) | TyKind::Ref(..) => smallvec![Single], |
533 | // This type is one for which we cannot list constructors, like `str` or `f64`. | 533 | // This type is one for which we cannot list constructors, like `str` or `f64`. |
534 | _ => smallvec![NonExhaustive], | 534 | _ => smallvec![NonExhaustive], |
diff --git a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs index bd76a606c..8451f9df5 100644 --- a/crates/hir_ty/src/diagnostics/match_check/usefulness.rs +++ b/crates/hir_ty/src/diagnostics/match_check/usefulness.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | //! Based on rust-lang/rust 1.52.0-nightly (25c15cdbe 2021-04-22) | 1 | //! Based on rust-lang/rust 1.52.0-nightly (25c15cdbe 2021-04-22) |
2 | //! https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs | 2 | //! <https://github.com/rust-lang/rust/blob/25c15cdbe/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs> |
3 | //! | 3 | //! |
4 | //! ----- | 4 | //! ----- |
5 | //! | 5 | //! |
@@ -645,7 +645,7 @@ impl SubPatSet { | |||
645 | (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => { | 645 | (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => { |
646 | s_set.retain(|i, s_sub_set| { | 646 | s_set.retain(|i, s_sub_set| { |
647 | // Missing entries count as full. | 647 | // Missing entries count as full. |
648 | let o_sub_set = o_set.remove(&i).unwrap_or(Full); | 648 | let o_sub_set = o_set.remove(i).unwrap_or(Full); |
649 | s_sub_set.union(o_sub_set); | 649 | s_sub_set.union(o_sub_set); |
650 | // We drop full entries. | 650 | // We drop full entries. |
651 | !s_sub_set.is_full() | 651 | !s_sub_set.is_full() |
@@ -656,7 +656,7 @@ impl SubPatSet { | |||
656 | (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => { | 656 | (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => { |
657 | s_set.retain(|i, s_sub_set| { | 657 | s_set.retain(|i, s_sub_set| { |
658 | // Missing entries count as empty. | 658 | // Missing entries count as empty. |
659 | let o_sub_set = o_set.remove(&i).unwrap_or(Empty); | 659 | let o_sub_set = o_set.remove(i).unwrap_or(Empty); |
660 | s_sub_set.union(o_sub_set); | 660 | s_sub_set.union(o_sub_set); |
661 | // We drop empty entries. | 661 | // We drop empty entries. |
662 | !s_sub_set.is_empty() | 662 | !s_sub_set.is_empty() |
@@ -898,7 +898,7 @@ impl Usefulness { | |||
898 | } else { | 898 | } else { |
899 | witnesses | 899 | witnesses |
900 | .into_iter() | 900 | .into_iter() |
901 | .map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns)) | 901 | .map(|witness| witness.apply_constructor(pcx, ctor, ctor_wild_subpatterns)) |
902 | .collect() | 902 | .collect() |
903 | }; | 903 | }; |
904 | WithWitnesses(new_witnesses) | 904 | WithWitnesses(new_witnesses) |
diff --git a/crates/hir_ty/src/diagnostics/unsafe_check.rs b/crates/hir_ty/src/diagnostics/unsafe_check.rs index c3c483425..777f347b8 100644 --- a/crates/hir_ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir_ty/src/diagnostics/unsafe_check.rs | |||
@@ -1,8 +1,6 @@ | |||
1 | //! Provides validations for unsafe code. Currently checks if unsafe functions are missing | 1 | //! Provides validations for unsafe code. Currently checks if unsafe functions are missing |
2 | //! unsafe blocks. | 2 | //! unsafe blocks. |
3 | 3 | ||
4 | use std::sync::Arc; | ||
5 | |||
6 | use hir_def::{ | 4 | use hir_def::{ |
7 | body::Body, | 5 | body::Body, |
8 | expr::{Expr, ExprId, UnaryOp}, | 6 | expr::{Expr, ExprId, UnaryOp}, |
@@ -10,60 +8,32 @@ use hir_def::{ | |||
10 | DefWithBodyId, | 8 | DefWithBodyId, |
11 | }; | 9 | }; |
12 | 10 | ||
13 | use crate::{ | 11 | use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind}; |
14 | db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult, | ||
15 | Interner, TyExt, TyKind, | ||
16 | }; | ||
17 | 12 | ||
18 | pub(super) struct UnsafeValidator<'a, 'b: 'a> { | 13 | pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> { |
19 | owner: DefWithBodyId, | 14 | let infer = db.infer(def); |
20 | infer: Arc<InferenceResult>, | ||
21 | sink: &'a mut DiagnosticSink<'b>, | ||
22 | } | ||
23 | 15 | ||
24 | impl<'a, 'b> UnsafeValidator<'a, 'b> { | 16 | let is_unsafe = match def { |
25 | pub(super) fn new( | 17 | DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), |
26 | owner: DefWithBodyId, | 18 | DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false, |
27 | infer: Arc<InferenceResult>, | 19 | }; |
28 | sink: &'a mut DiagnosticSink<'b>, | 20 | if is_unsafe { |
29 | ) -> UnsafeValidator<'a, 'b> { | 21 | return Vec::new(); |
30 | UnsafeValidator { owner, infer, sink } | ||
31 | } | 22 | } |
32 | 23 | ||
33 | pub(super) fn validate_body(&mut self, db: &dyn HirDatabase) { | 24 | unsafe_expressions(db, &infer, def) |
34 | let def = self.owner; | 25 | .into_iter() |
35 | let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def); | 26 | .filter(|it| !it.inside_unsafe_block) |
36 | let is_unsafe = match self.owner { | 27 | .map(|it| it.expr) |
37 | DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), | 28 | .collect() |
38 | DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false, | ||
39 | }; | ||
40 | if is_unsafe | ||
41 | || unsafe_expressions | ||
42 | .iter() | ||
43 | .filter(|unsafe_expr| !unsafe_expr.inside_unsafe_block) | ||
44 | .count() | ||
45 | == 0 | ||
46 | { | ||
47 | return; | ||
48 | } | ||
49 | |||
50 | let (_, body_source) = db.body_with_source_map(def); | ||
51 | for unsafe_expr in unsafe_expressions { | ||
52 | if !unsafe_expr.inside_unsafe_block { | ||
53 | if let Ok(in_file) = body_source.as_ref().expr_syntax(unsafe_expr.expr) { | ||
54 | self.sink.push(MissingUnsafe { file: in_file.file_id, expr: in_file.value }) | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | } | 29 | } |
60 | 30 | ||
61 | pub(crate) struct UnsafeExpr { | 31 | struct UnsafeExpr { |
62 | pub(crate) expr: ExprId, | 32 | pub(crate) expr: ExprId, |
63 | pub(crate) inside_unsafe_block: bool, | 33 | pub(crate) inside_unsafe_block: bool, |
64 | } | 34 | } |
65 | 35 | ||
66 | pub(crate) fn unsafe_expressions( | 36 | fn unsafe_expressions( |
67 | db: &dyn HirDatabase, | 37 | db: &dyn HirDatabase, |
68 | infer: &InferenceResult, | 38 | infer: &InferenceResult, |
69 | def: DefWithBodyId, | 39 | def: DefWithBodyId, |
@@ -126,92 +96,3 @@ fn walk_unsafe( | |||
126 | walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); | 96 | walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); |
127 | }); | 97 | }); |
128 | } | 98 | } |
129 | |||
130 | #[cfg(test)] | ||
131 | mod tests { | ||
132 | use crate::diagnostics::tests::check_diagnostics; | ||
133 | |||
134 | #[test] | ||
135 | fn missing_unsafe_diagnostic_with_raw_ptr() { | ||
136 | check_diagnostics( | ||
137 | r#" | ||
138 | fn main() { | ||
139 | let x = &5 as *const usize; | ||
140 | unsafe { let y = *x; } | ||
141 | let z = *x; | ||
142 | } //^^ This operation is unsafe and requires an unsafe function or block | ||
143 | "#, | ||
144 | ) | ||
145 | } | ||
146 | |||
147 | #[test] | ||
148 | fn missing_unsafe_diagnostic_with_unsafe_call() { | ||
149 | check_diagnostics( | ||
150 | r#" | ||
151 | struct HasUnsafe; | ||
152 | |||
153 | impl HasUnsafe { | ||
154 | unsafe fn unsafe_fn(&self) { | ||
155 | let x = &5 as *const usize; | ||
156 | let y = *x; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | unsafe fn unsafe_fn() { | ||
161 | let x = &5 as *const usize; | ||
162 | let y = *x; | ||
163 | } | ||
164 | |||
165 | fn main() { | ||
166 | unsafe_fn(); | ||
167 | //^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
168 | HasUnsafe.unsafe_fn(); | ||
169 | //^^^^^^^^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
170 | unsafe { | ||
171 | unsafe_fn(); | ||
172 | HasUnsafe.unsafe_fn(); | ||
173 | } | ||
174 | } | ||
175 | "#, | ||
176 | ); | ||
177 | } | ||
178 | |||
179 | #[test] | ||
180 | fn missing_unsafe_diagnostic_with_static_mut() { | ||
181 | check_diagnostics( | ||
182 | r#" | ||
183 | struct Ty { | ||
184 | a: u8, | ||
185 | } | ||
186 | |||
187 | static mut STATIC_MUT: Ty = Ty { a: 0 }; | ||
188 | |||
189 | fn main() { | ||
190 | let x = STATIC_MUT.a; | ||
191 | //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
192 | unsafe { | ||
193 | let x = STATIC_MUT.a; | ||
194 | } | ||
195 | } | ||
196 | "#, | ||
197 | ); | ||
198 | } | ||
199 | |||
200 | #[test] | ||
201 | fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { | ||
202 | check_diagnostics( | ||
203 | r#" | ||
204 | extern "rust-intrinsic" { | ||
205 | pub fn bitreverse(x: u32) -> u32; // Safe intrinsic | ||
206 | pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic | ||
207 | } | ||
208 | |||
209 | fn main() { | ||
210 | let _ = bitreverse(12); | ||
211 | let _ = floorf32(12.0); | ||
212 | //^^^^^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block | ||
213 | } | ||
214 | "#, | ||
215 | ); | ||
216 | } | ||
217 | } | ||
diff --git a/crates/hir_ty/src/diagnostics_sink.rs b/crates/hir_ty/src/diagnostics_sink.rs deleted file mode 100644 index 084fa8b06..000000000 --- a/crates/hir_ty/src/diagnostics_sink.rs +++ /dev/null | |||
@@ -1,109 +0,0 @@ | |||
1 | //! Semantic errors and warnings. | ||
2 | //! | ||
3 | //! The `Diagnostic` trait defines a trait object which can represent any | ||
4 | //! diagnostic. | ||
5 | //! | ||
6 | //! `DiagnosticSink` struct is used as an emitter for diagnostic. When creating | ||
7 | //! a `DiagnosticSink`, you supply a callback which can react to a `dyn | ||
8 | //! Diagnostic` or to any concrete diagnostic (downcasting is used internally). | ||
9 | //! | ||
10 | //! Because diagnostics store file offsets, it's a bad idea to store them | ||
11 | //! directly in salsa. For this reason, every hir subsytem defines it's own | ||
12 | //! strongly-typed closed set of diagnostics which use hir ids internally, are | ||
13 | //! stored in salsa and do *not* implement the `Diagnostic` trait. Instead, a | ||
14 | //! subsystem provides a separate, non-query-based API which can walk all stored | ||
15 | //! values and transform them into instances of `Diagnostic`. | ||
16 | |||
17 | use std::{any::Any, fmt}; | ||
18 | |||
19 | use hir_expand::InFile; | ||
20 | use syntax::SyntaxNodePtr; | ||
21 | |||
22 | #[derive(Copy, Clone, Debug, PartialEq)] | ||
23 | pub struct DiagnosticCode(pub &'static str); | ||
24 | |||
25 | impl DiagnosticCode { | ||
26 | pub fn as_str(&self) -> &str { | ||
27 | self.0 | ||
28 | } | ||
29 | } | ||
30 | |||
31 | pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { | ||
32 | fn code(&self) -> DiagnosticCode; | ||
33 | fn message(&self) -> String; | ||
34 | /// Source element that triggered the diagnostics. | ||
35 | /// | ||
36 | /// Note that this should reflect "semantics", rather than specific span we | ||
37 | /// want to highlight. When rendering the diagnostics into an error message, | ||
38 | /// the IDE will fetch the `SyntaxNode` and will narrow the span | ||
39 | /// appropriately. | ||
40 | fn display_source(&self) -> InFile<SyntaxNodePtr>; | ||
41 | fn as_any(&self) -> &(dyn Any + Send + 'static); | ||
42 | fn is_experimental(&self) -> bool { | ||
43 | false | ||
44 | } | ||
45 | } | ||
46 | |||
47 | pub struct DiagnosticSink<'a> { | ||
48 | callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>, | ||
49 | filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>, | ||
50 | default_callback: Box<dyn FnMut(&dyn Diagnostic) + 'a>, | ||
51 | } | ||
52 | |||
53 | impl<'a> DiagnosticSink<'a> { | ||
54 | pub fn push(&mut self, d: impl Diagnostic) { | ||
55 | let d: &dyn Diagnostic = &d; | ||
56 | self._push(d); | ||
57 | } | ||
58 | |||
59 | fn _push(&mut self, d: &dyn Diagnostic) { | ||
60 | for filter in &mut self.filters { | ||
61 | if !filter(d) { | ||
62 | return; | ||
63 | } | ||
64 | } | ||
65 | for cb in &mut self.callbacks { | ||
66 | match cb(d) { | ||
67 | Ok(()) => return, | ||
68 | Err(()) => (), | ||
69 | } | ||
70 | } | ||
71 | (self.default_callback)(d) | ||
72 | } | ||
73 | } | ||
74 | |||
75 | pub struct DiagnosticSinkBuilder<'a> { | ||
76 | callbacks: Vec<Box<dyn FnMut(&dyn Diagnostic) -> Result<(), ()> + 'a>>, | ||
77 | filters: Vec<Box<dyn FnMut(&dyn Diagnostic) -> bool + 'a>>, | ||
78 | } | ||
79 | |||
80 | impl<'a> DiagnosticSinkBuilder<'a> { | ||
81 | pub fn new() -> Self { | ||
82 | Self { callbacks: Vec::new(), filters: Vec::new() } | ||
83 | } | ||
84 | |||
85 | pub fn filter<F: FnMut(&dyn Diagnostic) -> bool + 'a>(mut self, cb: F) -> Self { | ||
86 | self.filters.push(Box::new(cb)); | ||
87 | self | ||
88 | } | ||
89 | |||
90 | pub fn on<D: Diagnostic, F: FnMut(&D) + 'a>(mut self, mut cb: F) -> Self { | ||
91 | let cb = move |diag: &dyn Diagnostic| match diag.as_any().downcast_ref::<D>() { | ||
92 | Some(d) => { | ||
93 | cb(d); | ||
94 | Ok(()) | ||
95 | } | ||
96 | None => Err(()), | ||
97 | }; | ||
98 | self.callbacks.push(Box::new(cb)); | ||
99 | self | ||
100 | } | ||
101 | |||
102 | pub fn build<F: FnMut(&dyn Diagnostic) + 'a>(self, default_callback: F) -> DiagnosticSink<'a> { | ||
103 | DiagnosticSink { | ||
104 | callbacks: self.callbacks, | ||
105 | filters: self.filters, | ||
106 | default_callback: Box::new(default_callback), | ||
107 | } | ||
108 | } | ||
109 | } | ||
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 0e9f777da..63f37c0ab 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs | |||
@@ -35,11 +35,9 @@ use stdx::impl_from; | |||
35 | use syntax::SmolStr; | 35 | use syntax::SmolStr; |
36 | 36 | ||
37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; | 37 | use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; |
38 | use crate::diagnostics_sink::DiagnosticSink; | ||
39 | use crate::{ | 38 | use crate::{ |
40 | db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic, | 39 | db::HirDatabase, fold_tys, lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, |
41 | lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution, | 40 | Goal, Interner, Substitution, TyBuilder, TyExt, TyKind, |
42 | TyBuilder, TyExt, TyKind, | ||
43 | }; | 41 | }; |
44 | 42 | ||
45 | // This lint has a false positive here. See the link below for details. | 43 | // This lint has a false positive here. See the link below for details. |
@@ -80,7 +78,7 @@ enum ExprOrPatId { | |||
80 | impl_from!(ExprId, PatId for ExprOrPatId); | 78 | impl_from!(ExprId, PatId for ExprOrPatId); |
81 | 79 | ||
82 | /// Binding modes inferred for patterns. | 80 | /// Binding modes inferred for patterns. |
83 | /// https://doc.rust-lang.org/reference/patterns.html#binding-modes | 81 | /// <https://doc.rust-lang.org/reference/patterns.html#binding-modes> |
84 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | 82 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
85 | enum BindingMode { | 83 | enum BindingMode { |
86 | Move, | 84 | Move, |
@@ -111,6 +109,12 @@ pub(crate) struct InferOk { | |||
111 | pub(crate) struct TypeError; | 109 | pub(crate) struct TypeError; |
112 | pub(crate) type InferResult = Result<InferOk, TypeError>; | 110 | pub(crate) type InferResult = Result<InferOk, TypeError>; |
113 | 111 | ||
112 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
113 | pub enum InferenceDiagnostic { | ||
114 | NoSuchField { expr: ExprId }, | ||
115 | BreakOutsideOfLoop { expr: ExprId }, | ||
116 | } | ||
117 | |||
114 | /// A mismatch between an expected and an inferred type. | 118 | /// A mismatch between an expected and an inferred type. |
115 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 119 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
116 | pub struct TypeMismatch { | 120 | pub struct TypeMismatch { |
@@ -140,7 +144,7 @@ pub struct InferenceResult { | |||
140 | variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, | 144 | variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, |
141 | /// For each associated item record what it resolves to | 145 | /// For each associated item record what it resolves to |
142 | assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, | 146 | assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, |
143 | diagnostics: Vec<InferenceDiagnostic>, | 147 | pub diagnostics: Vec<InferenceDiagnostic>, |
144 | pub type_of_expr: ArenaMap<ExprId, Ty>, | 148 | pub type_of_expr: ArenaMap<ExprId, Ty>, |
145 | /// For each pattern record the type it resolves to. | 149 | /// For each pattern record the type it resolves to. |
146 | /// | 150 | /// |
@@ -191,14 +195,6 @@ impl InferenceResult { | |||
191 | _ => None, | 195 | _ => None, |
192 | }) | 196 | }) |
193 | } | 197 | } |
194 | pub fn add_diagnostics( | ||
195 | &self, | ||
196 | db: &dyn HirDatabase, | ||
197 | owner: DefWithBodyId, | ||
198 | sink: &mut DiagnosticSink, | ||
199 | ) { | ||
200 | self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink)) | ||
201 | } | ||
202 | } | 198 | } |
203 | 199 | ||
204 | impl Index<ExprId> for InferenceResult { | 200 | impl Index<ExprId> for InferenceResult { |
@@ -765,6 +761,38 @@ impl Expectation { | |||
765 | Expectation::RValueLikeUnsized(_) | Expectation::None => None, | 761 | Expectation::RValueLikeUnsized(_) | Expectation::None => None, |
766 | } | 762 | } |
767 | } | 763 | } |
764 | |||
765 | /// Comment copied from rustc: | ||
766 | /// Disregard "castable to" expectations because they | ||
767 | /// can lead us astray. Consider for example `if cond | ||
768 | /// {22} else {c} as u8` -- if we propagate the | ||
769 | /// "castable to u8" constraint to 22, it will pick the | ||
770 | /// type 22u8, which is overly constrained (c might not | ||
771 | /// be a u8). In effect, the problem is that the | ||
772 | /// "castable to" expectation is not the tightest thing | ||
773 | /// we can say, so we want to drop it in this case. | ||
774 | /// The tightest thing we can say is "must unify with | ||
775 | /// else branch". Note that in the case of a "has type" | ||
776 | /// constraint, this limitation does not hold. | ||
777 | /// | ||
778 | /// If the expected type is just a type variable, then don't use | ||
779 | /// an expected type. Otherwise, we might write parts of the type | ||
780 | /// when checking the 'then' block which are incompatible with the | ||
781 | /// 'else' branch. | ||
782 | fn adjust_for_branches(&self, table: &mut unify::InferenceTable) -> Expectation { | ||
783 | match self { | ||
784 | Expectation::HasType(ety) => { | ||
785 | let ety = table.resolve_ty_shallow(ety); | ||
786 | if !ety.is_ty_var() { | ||
787 | Expectation::HasType(ety) | ||
788 | } else { | ||
789 | Expectation::None | ||
790 | } | ||
791 | } | ||
792 | Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety.clone()), | ||
793 | _ => Expectation::None, | ||
794 | } | ||
795 | } | ||
768 | } | 796 | } |
769 | 797 | ||
770 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] | 798 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] |
@@ -804,43 +832,3 @@ impl std::ops::BitOrAssign for Diverges { | |||
804 | *self = *self | other; | 832 | *self = *self | other; |
805 | } | 833 | } |
806 | } | 834 | } |
807 | |||
808 | mod diagnostics { | ||
809 | use hir_def::{expr::ExprId, DefWithBodyId}; | ||
810 | |||
811 | use crate::{ | ||
812 | db::HirDatabase, | ||
813 | diagnostics::{BreakOutsideOfLoop, NoSuchField}, | ||
814 | diagnostics_sink::DiagnosticSink, | ||
815 | }; | ||
816 | |||
817 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
818 | pub(super) enum InferenceDiagnostic { | ||
819 | NoSuchField { expr: ExprId }, | ||
820 | BreakOutsideOfLoop { expr: ExprId }, | ||
821 | } | ||
822 | |||
823 | impl InferenceDiagnostic { | ||
824 | pub(super) fn add_to( | ||
825 | &self, | ||
826 | db: &dyn HirDatabase, | ||
827 | owner: DefWithBodyId, | ||
828 | sink: &mut DiagnosticSink, | ||
829 | ) { | ||
830 | match self { | ||
831 | InferenceDiagnostic::NoSuchField { expr } => { | ||
832 | let (_, source_map) = db.body_with_source_map(owner); | ||
833 | let field = source_map.field_syntax(*expr); | ||
834 | sink.push(NoSuchField { file: field.file_id, field: field.value }) | ||
835 | } | ||
836 | InferenceDiagnostic::BreakOutsideOfLoop { expr } => { | ||
837 | let (_, source_map) = db.body_with_source_map(owner); | ||
838 | let ptr = source_map | ||
839 | .expr_syntax(*expr) | ||
840 | .expect("break outside of loop in synthetic syntax"); | ||
841 | sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) | ||
842 | } | ||
843 | } | ||
844 | } | ||
845 | } | ||
846 | } | ||
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 03b97e7db..4b7f31521 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs | |||
@@ -2,8 +2,8 @@ | |||
2 | //! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions | 2 | //! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions |
3 | //! like going from `&Vec<T>` to `&[T]`. | 3 | //! like going from `&Vec<T>` to `&[T]`. |
4 | //! | 4 | //! |
5 | //! See https://doc.rust-lang.org/nomicon/coercions.html and | 5 | //! See <https://doc.rust-lang.org/nomicon/coercions.html> and |
6 | //! librustc_typeck/check/coercion.rs. | 6 | //! `librustc_typeck/check/coercion.rs`. |
7 | 7 | ||
8 | use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; | 8 | use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; |
9 | use hir_def::{expr::ExprId, lang_item::LangItemTarget}; | 9 | use hir_def::{expr::ExprId, lang_item::LangItemTarget}; |
@@ -109,7 +109,7 @@ impl<'a> InferenceContext<'a> { | |||
109 | } | 109 | } |
110 | 110 | ||
111 | // Consider coercing the subtype to a DST | 111 | // Consider coercing the subtype to a DST |
112 | if let Ok(ret) = self.try_coerce_unsized(&from_ty, &to_ty) { | 112 | if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) { |
113 | return Ok(ret); | 113 | return Ok(ret); |
114 | } | 114 | } |
115 | 115 | ||
@@ -331,7 +331,7 @@ impl<'a> InferenceContext<'a> { | |||
331 | 331 | ||
332 | /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>` | 332 | /// Coerce a type using `from_ty: CoerceUnsized<ty_ty>` |
333 | /// | 333 | /// |
334 | /// See: https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html | 334 | /// See: <https://doc.rust-lang.org/nightly/std/marker/trait.CoerceUnsized.html> |
335 | fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult { | 335 | fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> InferResult { |
336 | // These 'if' statements require some explanation. | 336 | // These 'if' statements require some explanation. |
337 | // The `CoerceUnsized` trait is special - it is only | 337 | // The `CoerceUnsized` trait is special - it is only |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index f73bf43b2..5ea2e5934 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -54,7 +54,7 @@ impl<'a> InferenceContext<'a> { | |||
54 | /// Infer type of expression with possibly implicit coerce to the expected type. | 54 | /// Infer type of expression with possibly implicit coerce to the expected type. |
55 | /// Return the type after possible coercion. | 55 | /// Return the type after possible coercion. |
56 | pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { | 56 | pub(super) fn infer_expr_coerce(&mut self, expr: ExprId, expected: &Expectation) -> Ty { |
57 | let ty = self.infer_expr_inner(expr, &expected); | 57 | let ty = self.infer_expr_inner(expr, expected); |
58 | let ty = if let Some(target) = expected.only_has_type(&mut self.table) { | 58 | let ty = if let Some(target) = expected.only_has_type(&mut self.table) { |
59 | if !self.coerce(&ty, &target) { | 59 | if !self.coerce(&ty, &target) { |
60 | self.result | 60 | self.result |
@@ -135,11 +135,11 @@ impl<'a> InferenceContext<'a> { | |||
135 | let mut both_arms_diverge = Diverges::Always; | 135 | let mut both_arms_diverge = Diverges::Always; |
136 | 136 | ||
137 | let mut result_ty = self.table.new_type_var(); | 137 | let mut result_ty = self.table.new_type_var(); |
138 | let then_ty = self.infer_expr_inner(*then_branch, &expected); | 138 | let then_ty = self.infer_expr_inner(*then_branch, expected); |
139 | both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); | 139 | both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); |
140 | result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty); | 140 | result_ty = self.coerce_merge_branch(Some(*then_branch), &result_ty, &then_ty); |
141 | let else_ty = match else_branch { | 141 | let else_ty = match else_branch { |
142 | Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), | 142 | Some(else_branch) => self.infer_expr_inner(*else_branch, expected), |
143 | None => TyBuilder::unit(), | 143 | None => TyBuilder::unit(), |
144 | }; | 144 | }; |
145 | both_arms_diverge &= self.diverges; | 145 | both_arms_diverge &= self.diverges; |
@@ -327,20 +327,19 @@ impl<'a> InferenceContext<'a> { | |||
327 | self.normalize_associated_types_in(ret_ty) | 327 | self.normalize_associated_types_in(ret_ty) |
328 | } | 328 | } |
329 | Expr::MethodCall { receiver, args, method_name, generic_args } => self | 329 | Expr::MethodCall { receiver, args, method_name, generic_args } => self |
330 | .infer_method_call( | 330 | .infer_method_call(tgt_expr, *receiver, args, method_name, generic_args.as_deref()), |
331 | tgt_expr, | ||
332 | *receiver, | ||
333 | &args, | ||
334 | &method_name, | ||
335 | generic_args.as_deref(), | ||
336 | ), | ||
337 | Expr::Match { expr, arms } => { | 331 | Expr::Match { expr, arms } => { |
338 | let input_ty = self.infer_expr(*expr, &Expectation::none()); | 332 | let input_ty = self.infer_expr(*expr, &Expectation::none()); |
339 | 333 | ||
334 | let expected = expected.adjust_for_branches(&mut self.table); | ||
335 | |||
340 | let mut result_ty = if arms.is_empty() { | 336 | let mut result_ty = if arms.is_empty() { |
341 | TyKind::Never.intern(&Interner) | 337 | TyKind::Never.intern(&Interner) |
342 | } else { | 338 | } else { |
343 | self.table.new_type_var() | 339 | match &expected { |
340 | Expectation::HasType(ty) => ty.clone(), | ||
341 | _ => self.table.new_type_var(), | ||
342 | } | ||
344 | }; | 343 | }; |
345 | 344 | ||
346 | let matchee_diverges = self.diverges; | 345 | let matchee_diverges = self.diverges; |
@@ -988,7 +987,7 @@ impl<'a> InferenceContext<'a> { | |||
988 | } | 987 | } |
989 | 988 | ||
990 | fn register_obligations_for_call(&mut self, callable_ty: &Ty) { | 989 | fn register_obligations_for_call(&mut self, callable_ty: &Ty) { |
991 | let callable_ty = self.resolve_ty_shallow(&callable_ty); | 990 | let callable_ty = self.resolve_ty_shallow(callable_ty); |
992 | if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) { | 991 | if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(&Interner) { |
993 | let def: CallableDefId = from_chalk(self.db, *fn_def); | 992 | let def: CallableDefId = from_chalk(self.db, *fn_def); |
994 | let generic_predicates = self.db.generic_predicates(def.into()); | 993 | let generic_predicates = self.db.generic_predicates(def.into()); |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index 25dff7e49..035f4ded6 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -192,7 +192,7 @@ impl<'a> InferenceContext<'a> { | |||
192 | Pat::Path(path) => { | 192 | Pat::Path(path) => { |
193 | // FIXME use correct resolver for the surrounding expression | 193 | // FIXME use correct resolver for the surrounding expression |
194 | let resolver = self.resolver.clone(); | 194 | let resolver = self.resolver.clone(); |
195 | self.infer_path(&resolver, &path, pat.into()).unwrap_or(self.err_ty()) | 195 | self.infer_path(&resolver, path, pat.into()).unwrap_or(self.err_ty()) |
196 | } | 196 | } |
197 | Pat::Bind { mode, name: _, subpat } => { | 197 | Pat::Bind { mode, name: _, subpat } => { |
198 | let mode = if mode == &BindingAnnotation::Unannotated { | 198 | let mode = if mode == &BindingAnnotation::Unannotated { |
@@ -275,7 +275,7 @@ impl<'a> InferenceContext<'a> { | |||
275 | if !self.unify(&ty, &expected) { | 275 | if !self.unify(&ty, &expected) { |
276 | self.result | 276 | self.result |
277 | .type_mismatches | 277 | .type_mismatches |
278 | .insert(pat.into(), TypeMismatch { expected: expected, actual: ty.clone() }); | 278 | .insert(pat.into(), TypeMismatch { expected, actual: ty.clone() }); |
279 | } | 279 | } |
280 | self.write_pat_ty(pat, ty.clone()); | 280 | self.write_pat_ty(pat, ty.clone()); |
281 | ty | 281 | ty |
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index 14c99eafd..056cdb5d5 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs | |||
@@ -43,11 +43,11 @@ impl<'a> InferenceContext<'a> { | |||
43 | } | 43 | } |
44 | let ty = self.make_ty(type_ref); | 44 | let ty = self.make_ty(type_ref); |
45 | let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); | 45 | let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); |
46 | let ctx = crate::lower::TyLoweringContext::new(self.db, &resolver); | 46 | let ctx = crate::lower::TyLoweringContext::new(self.db, resolver); |
47 | let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty); | 47 | let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty); |
48 | self.resolve_ty_assoc_item( | 48 | self.resolve_ty_assoc_item( |
49 | ty, | 49 | ty, |
50 | &path.segments().last().expect("path had at least one segment").name, | 50 | path.segments().last().expect("path had at least one segment").name, |
51 | id, | 51 | id, |
52 | )? | 52 | )? |
53 | } else { | 53 | } else { |
@@ -154,7 +154,7 @@ impl<'a> InferenceContext<'a> { | |||
154 | let segment = | 154 | let segment = |
155 | remaining_segments.last().expect("there should be at least one segment here"); | 155 | remaining_segments.last().expect("there should be at least one segment here"); |
156 | 156 | ||
157 | self.resolve_ty_assoc_item(ty, &segment.name, id) | 157 | self.resolve_ty_assoc_item(ty, segment.name, id) |
158 | } | 158 | } |
159 | } | 159 | } |
160 | } | 160 | } |
diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs index 29ffdd9b7..5fef878e8 100644 --- a/crates/hir_ty/src/interner.rs +++ b/crates/hir_ty/src/interner.rs | |||
@@ -331,7 +331,7 @@ impl chalk_ir::interner::Interner for Interner { | |||
331 | &self, | 331 | &self, |
332 | clauses: &'a Self::InternedProgramClauses, | 332 | clauses: &'a Self::InternedProgramClauses, |
333 | ) -> &'a [chalk_ir::ProgramClause<Self>] { | 333 | ) -> &'a [chalk_ir::ProgramClause<Self>] { |
334 | &clauses | 334 | clauses |
335 | } | 335 | } |
336 | 336 | ||
337 | fn intern_quantified_where_clauses<E>( | 337 | fn intern_quantified_where_clauses<E>( |
@@ -373,7 +373,7 @@ impl chalk_ir::interner::Interner for Interner { | |||
373 | &self, | 373 | &self, |
374 | canonical_var_kinds: &'a Self::InternedCanonicalVarKinds, | 374 | canonical_var_kinds: &'a Self::InternedCanonicalVarKinds, |
375 | ) -> &'a [chalk_ir::CanonicalVarKind<Self>] { | 375 | ) -> &'a [chalk_ir::CanonicalVarKind<Self>] { |
376 | &canonical_var_kinds | 376 | canonical_var_kinds |
377 | } | 377 | } |
378 | 378 | ||
379 | fn intern_constraints<E>( | 379 | fn intern_constraints<E>( |
@@ -413,7 +413,7 @@ impl chalk_ir::interner::Interner for Interner { | |||
413 | &self, | 413 | &self, |
414 | variances: &'a Self::InternedVariances, | 414 | variances: &'a Self::InternedVariances, |
415 | ) -> &'a [chalk_ir::Variance] { | 415 | ) -> &'a [chalk_ir::Variance] { |
416 | &variances | 416 | variances |
417 | } | 417 | } |
418 | } | 418 | } |
419 | 419 | ||
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 50e0d6333..128cae830 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -21,7 +21,6 @@ mod utils; | |||
21 | mod walk; | 21 | mod walk; |
22 | pub mod db; | 22 | pub mod db; |
23 | pub mod diagnostics; | 23 | pub mod diagnostics; |
24 | pub mod diagnostics_sink; | ||
25 | pub mod display; | 24 | pub mod display; |
26 | pub mod method_resolution; | 25 | pub mod method_resolution; |
27 | pub mod primitive; | 26 | pub mod primitive; |
@@ -50,7 +49,7 @@ use crate::{db::HirDatabase, utils::generics}; | |||
50 | pub use autoderef::autoderef; | 49 | pub use autoderef::autoderef; |
51 | pub use builder::TyBuilder; | 50 | pub use builder::TyBuilder; |
52 | pub use chalk_ext::*; | 51 | pub use chalk_ext::*; |
53 | pub use infer::{could_unify, InferenceResult}; | 52 | pub use infer::{could_unify, InferenceDiagnostic, InferenceResult}; |
54 | pub use interner::Interner; | 53 | pub use interner::Interner; |
55 | pub use lower::{ | 54 | pub use lower::{ |
56 | associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, | 55 | associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode, |
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index c83933c73..817a65c20 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -238,7 +238,7 @@ impl<'a> TyLoweringContext<'a> { | |||
238 | // away instead of two. | 238 | // away instead of two. |
239 | let actual_opaque_type_data = self | 239 | let actual_opaque_type_data = self |
240 | .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { | 240 | .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { |
241 | ctx.lower_impl_trait(&bounds) | 241 | ctx.lower_impl_trait(bounds) |
242 | }); | 242 | }); |
243 | self.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data; | 243 | self.opaque_type_data.borrow_mut()[idx as usize] = actual_opaque_type_data; |
244 | 244 | ||
@@ -421,7 +421,7 @@ impl<'a> TyLoweringContext<'a> { | |||
421 | let found = self | 421 | let found = self |
422 | .db | 422 | .db |
423 | .trait_data(trait_ref.hir_trait_id()) | 423 | .trait_data(trait_ref.hir_trait_id()) |
424 | .associated_type_by_name(&segment.name); | 424 | .associated_type_by_name(segment.name); |
425 | match found { | 425 | match found { |
426 | Some(associated_ty) => { | 426 | Some(associated_ty) => { |
427 | // FIXME handle type parameters on the segment | 427 | // FIXME handle type parameters on the segment |
@@ -505,7 +505,7 @@ impl<'a> TyLoweringContext<'a> { | |||
505 | pub(crate) fn lower_path(&self, path: &Path) -> (Ty, Option<TypeNs>) { | 505 | pub(crate) fn lower_path(&self, path: &Path) -> (Ty, Option<TypeNs>) { |
506 | // Resolve the path (in type namespace) | 506 | // Resolve the path (in type namespace) |
507 | if let Some(type_ref) = path.type_anchor() { | 507 | if let Some(type_ref) = path.type_anchor() { |
508 | let (ty, res) = self.lower_ty_ext(&type_ref); | 508 | let (ty, res) = self.lower_ty_ext(type_ref); |
509 | return self.lower_ty_relative_path(ty, res, path.segments()); | 509 | return self.lower_ty_relative_path(ty, res, path.segments()); |
510 | } | 510 | } |
511 | let (resolution, remaining_index) = | 511 | let (resolution, remaining_index) = |
@@ -784,7 +784,7 @@ impl<'a> TyLoweringContext<'a> { | |||
784 | let trait_ref = match bound { | 784 | let trait_ref = match bound { |
785 | TypeBound::Path(path) => { | 785 | TypeBound::Path(path) => { |
786 | bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); | 786 | bindings = self.lower_trait_ref_from_path(path, Some(self_ty)); |
787 | bindings.clone().map(WhereClause::Implemented).map(|b| crate::wrap_empty_binders(b)) | 787 | bindings.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) |
788 | } | 788 | } |
789 | TypeBound::Lifetime(_) => None, | 789 | TypeBound::Lifetime(_) => None, |
790 | TypeBound::Error => None, | 790 | TypeBound::Error => None, |
@@ -957,7 +957,7 @@ pub(crate) fn field_types_query( | |||
957 | /// like `T::Item`. | 957 | /// like `T::Item`. |
958 | /// | 958 | /// |
959 | /// See the analogous query in rustc and its comment: | 959 | /// See the analogous query in rustc and its comment: |
960 | /// https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46 | 960 | /// <https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46> |
961 | /// This is a query mostly to handle cycles somewhat gracefully; e.g. the | 961 | /// This is a query mostly to handle cycles somewhat gracefully; e.g. the |
962 | /// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but | 962 | /// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but |
963 | /// these are fine: `T: Foo<U::Item>, U: Foo<()>`. | 963 | /// these are fine: `T: Foo<U::Item>, U: Foo<()>`. |
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index a23527f7d..3d233b1e2 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -8,7 +8,7 @@ use arrayvec::ArrayVec; | |||
8 | use base_db::{CrateId, Edition}; | 8 | use base_db::{CrateId, Edition}; |
9 | use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; | 9 | use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; |
10 | use hir_def::{ | 10 | use hir_def::{ |
11 | lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId, | 11 | lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, BlockId, FunctionId, |
12 | GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId, | 12 | GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId, |
13 | }; | 13 | }; |
14 | use hir_expand::name::Name; | 14 | use hir_expand::name::Name; |
@@ -60,7 +60,7 @@ impl TyFingerprint { | |||
60 | TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), | 60 | TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), |
61 | TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), | 61 | TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), |
62 | TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), | 62 | TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), |
63 | TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?, | 63 | TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?, |
64 | _ => return None, | 64 | _ => return None, |
65 | }; | 65 | }; |
66 | Some(fp) | 66 | Some(fp) |
@@ -77,7 +77,7 @@ impl TyFingerprint { | |||
77 | TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), | 77 | TyKind::Adt(AdtId(adt), _) => TyFingerprint::Adt(*adt), |
78 | TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), | 78 | TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), |
79 | TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), | 79 | TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), |
80 | TyKind::Dyn(_) => ty.dyn_trait().map(|trait_| TyFingerprint::Dyn(trait_))?, | 80 | TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?, |
81 | TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty), | 81 | TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty), |
82 | TyKind::Tuple(_, subst) => { | 82 | TyKind::Tuple(_, subst) => { |
83 | let first_ty = subst.interned().get(0).map(|arg| arg.assert_ty_ref(&Interner)); | 83 | let first_ty = subst.interned().get(0).map(|arg| arg.assert_ty_ref(&Interner)); |
@@ -139,35 +139,47 @@ impl TraitImpls { | |||
139 | let mut impls = Self { map: FxHashMap::default() }; | 139 | let mut impls = Self { map: FxHashMap::default() }; |
140 | 140 | ||
141 | let crate_def_map = db.crate_def_map(krate); | 141 | let crate_def_map = db.crate_def_map(krate); |
142 | collect_def_map(db, &crate_def_map, &mut impls); | 142 | impls.collect_def_map(db, &crate_def_map); |
143 | 143 | ||
144 | return Arc::new(impls); | 144 | return Arc::new(impls); |
145 | } | ||
145 | 146 | ||
146 | fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut TraitImpls) { | 147 | pub(crate) fn trait_impls_in_block_query( |
147 | for (_module_id, module_data) in def_map.modules() { | 148 | db: &dyn HirDatabase, |
148 | for impl_id in module_data.scope.impls() { | 149 | block: BlockId, |
149 | let target_trait = match db.impl_trait(impl_id) { | 150 | ) -> Option<Arc<Self>> { |
150 | Some(tr) => tr.skip_binders().hir_trait_id(), | 151 | let _p = profile::span("trait_impls_in_block_query"); |
151 | None => continue, | 152 | let mut impls = Self { map: FxHashMap::default() }; |
152 | }; | ||
153 | let self_ty = db.impl_self_ty(impl_id); | ||
154 | let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders()); | ||
155 | impls | ||
156 | .map | ||
157 | .entry(target_trait) | ||
158 | .or_default() | ||
159 | .entry(self_ty_fp) | ||
160 | .or_default() | ||
161 | .push(impl_id); | ||
162 | } | ||
163 | 153 | ||
164 | // To better support custom derives, collect impls in all unnamed const items. | 154 | let block_def_map = db.block_def_map(block)?; |
165 | // const _: () = { ... }; | 155 | impls.collect_def_map(db, &block_def_map); |
166 | for konst in module_data.scope.unnamed_consts() { | 156 | |
167 | let body = db.body(konst.into()); | 157 | return Some(Arc::new(impls)); |
168 | for (_, block_def_map) in body.blocks(db.upcast()) { | 158 | } |
169 | collect_def_map(db, &block_def_map, impls); | 159 | |
170 | } | 160 | fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) { |
161 | for (_module_id, module_data) in def_map.modules() { | ||
162 | for impl_id in module_data.scope.impls() { | ||
163 | let target_trait = match db.impl_trait(impl_id) { | ||
164 | Some(tr) => tr.skip_binders().hir_trait_id(), | ||
165 | None => continue, | ||
166 | }; | ||
167 | let self_ty = db.impl_self_ty(impl_id); | ||
168 | let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders()); | ||
169 | self.map | ||
170 | .entry(target_trait) | ||
171 | .or_default() | ||
172 | .entry(self_ty_fp) | ||
173 | .or_default() | ||
174 | .push(impl_id); | ||
175 | } | ||
176 | |||
177 | // To better support custom derives, collect impls in all unnamed const items. | ||
178 | // const _: () = { ... }; | ||
179 | for konst in module_data.scope.unnamed_consts() { | ||
180 | let body = db.body(konst.into()); | ||
181 | for (_, block_def_map) in body.blocks(db.upcast()) { | ||
182 | self.collect_def_map(db, &block_def_map); | ||
171 | } | 183 | } |
172 | } | 184 | } |
173 | } | 185 | } |
@@ -372,7 +384,7 @@ pub(crate) fn lookup_method( | |||
372 | db, | 384 | db, |
373 | env, | 385 | env, |
374 | krate, | 386 | krate, |
375 | &traits_in_scope, | 387 | traits_in_scope, |
376 | visible_from_module, | 388 | visible_from_module, |
377 | Some(name), | 389 | Some(name), |
378 | LookupMode::MethodCall, | 390 | LookupMode::MethodCall, |
@@ -484,7 +496,7 @@ fn iterate_method_candidates_impl( | |||
484 | LookupMode::Path => { | 496 | LookupMode::Path => { |
485 | // No autoderef for path lookups | 497 | // No autoderef for path lookups |
486 | iterate_method_candidates_for_self_ty( | 498 | iterate_method_candidates_for_self_ty( |
487 | &ty, | 499 | ty, |
488 | db, | 500 | db, |
489 | env, | 501 | env, |
490 | krate, | 502 | krate, |
@@ -513,7 +525,7 @@ fn iterate_method_candidates_with_autoref( | |||
513 | db, | 525 | db, |
514 | env.clone(), | 526 | env.clone(), |
515 | krate, | 527 | krate, |
516 | &traits_in_scope, | 528 | traits_in_scope, |
517 | visible_from_module, | 529 | visible_from_module, |
518 | name, | 530 | name, |
519 | &mut callback, | 531 | &mut callback, |
@@ -531,7 +543,7 @@ fn iterate_method_candidates_with_autoref( | |||
531 | db, | 543 | db, |
532 | env.clone(), | 544 | env.clone(), |
533 | krate, | 545 | krate, |
534 | &traits_in_scope, | 546 | traits_in_scope, |
535 | visible_from_module, | 547 | visible_from_module, |
536 | name, | 548 | name, |
537 | &mut callback, | 549 | &mut callback, |
@@ -549,7 +561,7 @@ fn iterate_method_candidates_with_autoref( | |||
549 | db, | 561 | db, |
550 | env, | 562 | env, |
551 | krate, | 563 | krate, |
552 | &traits_in_scope, | 564 | traits_in_scope, |
553 | visible_from_module, | 565 | visible_from_module, |
554 | name, | 566 | name, |
555 | &mut callback, | 567 | &mut callback, |
@@ -593,7 +605,7 @@ fn iterate_method_candidates_by_receiver( | |||
593 | db, | 605 | db, |
594 | env.clone(), | 606 | env.clone(), |
595 | krate, | 607 | krate, |
596 | &traits_in_scope, | 608 | traits_in_scope, |
597 | name, | 609 | name, |
598 | Some(receiver_ty), | 610 | Some(receiver_ty), |
599 | &mut callback, | 611 | &mut callback, |
@@ -870,7 +882,7 @@ fn transform_receiver_ty( | |||
870 | .fill_with_unknown() | 882 | .fill_with_unknown() |
871 | .build(), | 883 | .build(), |
872 | AssocContainerId::ImplId(impl_id) => { | 884 | AssocContainerId::ImplId(impl_id) => { |
873 | let impl_substs = inherent_impl_substs(db, env, impl_id, &self_ty)?; | 885 | let impl_substs = inherent_impl_substs(db, env, impl_id, self_ty)?; |
874 | TyBuilder::subst_for_def(db, function_id) | 886 | TyBuilder::subst_for_def(db, function_id) |
875 | .use_parent_substs(&impl_substs) | 887 | .use_parent_substs(&impl_substs) |
876 | .fill_with_unknown() | 888 | .fill_with_unknown() |
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs index 9d726b024..b873585c4 100644 --- a/crates/hir_ty/src/tests.rs +++ b/crates/hir_ty/src/tests.rs | |||
@@ -9,7 +9,7 @@ mod macros; | |||
9 | mod display_source_code; | 9 | mod display_source_code; |
10 | mod incremental; | 10 | mod incremental; |
11 | 11 | ||
12 | use std::{env, sync::Arc}; | 12 | use std::{collections::HashMap, env, sync::Arc}; |
13 | 13 | ||
14 | use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; | 14 | use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; |
15 | use expect_test::Expect; | 15 | use expect_test::Expect; |
@@ -83,9 +83,105 @@ fn check_types_impl(ra_fixture: &str, display_source: bool) { | |||
83 | checked_one = true; | 83 | checked_one = true; |
84 | } | 84 | } |
85 | } | 85 | } |
86 | |||
86 | assert!(checked_one, "no `//^` annotations found"); | 87 | assert!(checked_one, "no `//^` annotations found"); |
87 | } | 88 | } |
88 | 89 | ||
90 | fn check_no_mismatches(ra_fixture: &str) { | ||
91 | check_mismatches_impl(ra_fixture, true) | ||
92 | } | ||
93 | |||
94 | #[allow(unused)] | ||
95 | fn check_mismatches(ra_fixture: &str) { | ||
96 | check_mismatches_impl(ra_fixture, false) | ||
97 | } | ||
98 | |||
99 | fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) { | ||
100 | let _tracing = setup_tracing(); | ||
101 | let (db, file_id) = TestDB::with_single_file(ra_fixture); | ||
102 | let module = db.module_for_file(file_id); | ||
103 | let def_map = module.def_map(&db); | ||
104 | |||
105 | let mut defs: Vec<DefWithBodyId> = Vec::new(); | ||
106 | visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it)); | ||
107 | defs.sort_by_key(|def| match def { | ||
108 | DefWithBodyId::FunctionId(it) => { | ||
109 | let loc = it.lookup(&db); | ||
110 | loc.source(&db).value.syntax().text_range().start() | ||
111 | } | ||
112 | DefWithBodyId::ConstId(it) => { | ||
113 | let loc = it.lookup(&db); | ||
114 | loc.source(&db).value.syntax().text_range().start() | ||
115 | } | ||
116 | DefWithBodyId::StaticId(it) => { | ||
117 | let loc = it.lookup(&db); | ||
118 | loc.source(&db).value.syntax().text_range().start() | ||
119 | } | ||
120 | }); | ||
121 | let mut mismatches = HashMap::new(); | ||
122 | let mut push_mismatch = |src_ptr: InFile<SyntaxNode>, mismatch: TypeMismatch| { | ||
123 | let range = src_ptr.value.text_range(); | ||
124 | if src_ptr.file_id.call_node(&db).is_some() { | ||
125 | panic!("type mismatch in macro expansion"); | ||
126 | } | ||
127 | let file_range = FileRange { file_id: src_ptr.file_id.original_file(&db), range }; | ||
128 | let actual = format!( | ||
129 | "expected {}, got {}", | ||
130 | mismatch.expected.display_test(&db), | ||
131 | mismatch.actual.display_test(&db) | ||
132 | ); | ||
133 | mismatches.insert(file_range, actual); | ||
134 | }; | ||
135 | for def in defs { | ||
136 | let (_body, body_source_map) = db.body_with_source_map(def); | ||
137 | let inference_result = db.infer(def); | ||
138 | for (pat, mismatch) in inference_result.pat_type_mismatches() { | ||
139 | let syntax_ptr = match body_source_map.pat_syntax(pat) { | ||
140 | Ok(sp) => { | ||
141 | let root = db.parse_or_expand(sp.file_id).unwrap(); | ||
142 | sp.map(|ptr| { | ||
143 | ptr.either( | ||
144 | |it| it.to_node(&root).syntax().clone(), | ||
145 | |it| it.to_node(&root).syntax().clone(), | ||
146 | ) | ||
147 | }) | ||
148 | } | ||
149 | Err(SyntheticSyntax) => continue, | ||
150 | }; | ||
151 | push_mismatch(syntax_ptr, mismatch.clone()); | ||
152 | } | ||
153 | for (expr, mismatch) in inference_result.expr_type_mismatches() { | ||
154 | let node = match body_source_map.expr_syntax(expr) { | ||
155 | Ok(sp) => { | ||
156 | let root = db.parse_or_expand(sp.file_id).unwrap(); | ||
157 | sp.map(|ptr| ptr.to_node(&root).syntax().clone()) | ||
158 | } | ||
159 | Err(SyntheticSyntax) => continue, | ||
160 | }; | ||
161 | push_mismatch(node, mismatch.clone()); | ||
162 | } | ||
163 | } | ||
164 | let mut checked_one = false; | ||
165 | for (file_id, annotations) in db.extract_annotations() { | ||
166 | for (range, expected) in annotations { | ||
167 | let file_range = FileRange { file_id, range }; | ||
168 | if let Some(mismatch) = mismatches.remove(&file_range) { | ||
169 | assert_eq!(mismatch, expected); | ||
170 | } else { | ||
171 | assert!(false, "Expected mismatch not encountered: {}\n", expected); | ||
172 | } | ||
173 | checked_one = true; | ||
174 | } | ||
175 | } | ||
176 | let mut buf = String::new(); | ||
177 | for (range, mismatch) in mismatches { | ||
178 | format_to!(buf, "{:?}: {}\n", range.range, mismatch,); | ||
179 | } | ||
180 | assert!(buf.is_empty(), "Unexpected type mismatches:\n{}", buf); | ||
181 | |||
182 | assert!(checked_one || allow_none, "no `//^` annotations found"); | ||
183 | } | ||
184 | |||
89 | fn type_at_range(db: &TestDB, pos: FileRange) -> Ty { | 185 | fn type_at_range(db: &TestDB, pos: FileRange) -> Ty { |
90 | let file = db.parse(pos.file_id).ok().unwrap(); | 186 | let file = db.parse(pos.file_id).ok().unwrap(); |
91 | let expr = algo::find_node_at_range::<ast::Expr>(file.syntax(), pos.range).unwrap(); | 187 | let expr = algo::find_node_at_range::<ast::Expr>(file.syntax(), pos.range).unwrap(); |
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index 6dac7e103..713b74165 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | 2 | ||
3 | use super::{check_infer, check_infer_with_mismatches, check_types}; | 3 | use super::{check_infer, check_infer_with_mismatches, check_no_mismatches, check_types}; |
4 | 4 | ||
5 | #[test] | 5 | #[test] |
6 | fn infer_block_expr_type_mismatch() { | 6 | fn infer_block_expr_type_mismatch() { |
@@ -23,38 +23,29 @@ fn infer_block_expr_type_mismatch() { | |||
23 | fn coerce_places() { | 23 | fn coerce_places() { |
24 | check_infer( | 24 | check_infer( |
25 | r#" | 25 | r#" |
26 | struct S<T> { a: T } | 26 | //- minicore: coerce_unsized |
27 | struct S<T> { a: T } | ||
27 | 28 | ||
28 | fn f<T>(_: &[T]) -> T { loop {} } | 29 | fn f<T>(_: &[T]) -> T { loop {} } |
29 | fn g<T>(_: S<&[T]>) -> T { loop {} } | 30 | fn g<T>(_: S<&[T]>) -> T { loop {} } |
30 | 31 | ||
31 | fn gen<T>() -> *mut [T; 2] { loop {} } | 32 | fn gen<T>() -> *mut [T; 2] { loop {} } |
32 | fn test1<U>() -> *mut [U] { | 33 | fn test1<U>() -> *mut [U] { |
33 | gen() | 34 | gen() |
34 | } | 35 | } |
35 | |||
36 | fn test2() { | ||
37 | let arr: &[u8; 1] = &[1]; | ||
38 | |||
39 | let a: &[_] = arr; | ||
40 | let b = f(arr); | ||
41 | let c: &[_] = { arr }; | ||
42 | let d = g(S { a: arr }); | ||
43 | let e: [&[_]; 1] = [arr]; | ||
44 | let f: [&[_]; 2] = [arr; 2]; | ||
45 | let g: (&[_], &[_]) = (arr, arr); | ||
46 | } | ||
47 | 36 | ||
48 | #[lang = "sized"] | 37 | fn test2() { |
49 | pub trait Sized {} | 38 | let arr: &[u8; 1] = &[1]; |
50 | #[lang = "unsize"] | ||
51 | pub trait Unsize<T: ?Sized> {} | ||
52 | #[lang = "coerce_unsized"] | ||
53 | pub trait CoerceUnsized<T> {} | ||
54 | 39 | ||
55 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} | 40 | let a: &[_] = arr; |
56 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} | 41 | let b = f(arr); |
57 | "#, | 42 | let c: &[_] = { arr }; |
43 | let d = g(S { a: arr }); | ||
44 | let e: [&[_]; 1] = [arr]; | ||
45 | let f: [&[_]; 2] = [arr; 2]; | ||
46 | let g: (&[_], &[_]) = (arr, arr); | ||
47 | } | ||
48 | "#, | ||
58 | expect![[r#" | 49 | expect![[r#" |
59 | 30..31 '_': &[T] | 50 | 30..31 '_': &[T] |
60 | 44..55 '{ loop {} }': T | 51 | 44..55 '{ loop {} }': T |
@@ -131,60 +122,52 @@ fn infer_let_stmt_coerce() { | |||
131 | fn infer_custom_coerce_unsized() { | 122 | fn infer_custom_coerce_unsized() { |
132 | check_infer( | 123 | check_infer( |
133 | r#" | 124 | r#" |
134 | struct A<T: ?Sized>(*const T); | 125 | //- minicore: coerce_unsized |
135 | struct B<T: ?Sized>(*const T); | 126 | use core::{marker::Unsize, ops::CoerceUnsized}; |
136 | struct C<T: ?Sized> { inner: *const T } | ||
137 | 127 | ||
138 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {} | 128 | struct A<T: ?Sized>(*const T); |
139 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<C<U>> for C<T> {} | 129 | struct B<T: ?Sized>(*const T); |
130 | struct C<T: ?Sized> { inner: *const T } | ||
140 | 131 | ||
141 | fn foo1<T>(x: A<[T]>) -> A<[T]> { x } | 132 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<B<U>> for B<T> {} |
142 | fn foo2<T>(x: B<[T]>) -> B<[T]> { x } | 133 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<C<U>> for C<T> {} |
143 | fn foo3<T>(x: C<[T]>) -> C<[T]> { x } | ||
144 | |||
145 | fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) { | ||
146 | let d = foo1(a); | ||
147 | let e = foo2(b); | ||
148 | let f = foo3(c); | ||
149 | } | ||
150 | 134 | ||
135 | fn foo1<T>(x: A<[T]>) -> A<[T]> { x } | ||
136 | fn foo2<T>(x: B<[T]>) -> B<[T]> { x } | ||
137 | fn foo3<T>(x: C<[T]>) -> C<[T]> { x } | ||
151 | 138 | ||
152 | #[lang = "sized"] | 139 | fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) { |
153 | pub trait Sized {} | 140 | let d = foo1(a); |
154 | #[lang = "unsize"] | 141 | let e = foo2(b); |
155 | pub trait Unsize<T: ?Sized> {} | 142 | let f = foo3(c); |
156 | #[lang = "coerce_unsized"] | 143 | } |
157 | pub trait CoerceUnsized<T> {} | 144 | "#, |
158 | |||
159 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} | ||
160 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} | ||
161 | "#, | ||
162 | expect![[r#" | 145 | expect![[r#" |
163 | 257..258 'x': A<[T]> | 146 | 306..307 'x': A<[T]> |
164 | 278..283 '{ x }': A<[T]> | 147 | 327..332 '{ x }': A<[T]> |
165 | 280..281 'x': A<[T]> | 148 | 329..330 'x': A<[T]> |
166 | 295..296 'x': B<[T]> | 149 | 344..345 'x': B<[T]> |
167 | 316..321 '{ x }': B<[T]> | 150 | 365..370 '{ x }': B<[T]> |
168 | 318..319 'x': B<[T]> | 151 | 367..368 'x': B<[T]> |
169 | 333..334 'x': C<[T]> | 152 | 382..383 'x': C<[T]> |
170 | 354..359 '{ x }': C<[T]> | 153 | 403..408 '{ x }': C<[T]> |
171 | 356..357 'x': C<[T]> | 154 | 405..406 'x': C<[T]> |
172 | 369..370 'a': A<[u8; 2]> | 155 | 418..419 'a': A<[u8; 2]> |
173 | 384..385 'b': B<[u8; 2]> | 156 | 433..434 'b': B<[u8; 2]> |
174 | 399..400 'c': C<[u8; 2]> | 157 | 448..449 'c': C<[u8; 2]> |
175 | 414..480 '{ ...(c); }': () | 158 | 463..529 '{ ...(c); }': () |
176 | 424..425 'd': A<[{unknown}]> | 159 | 473..474 'd': A<[{unknown}]> |
177 | 428..432 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]> | 160 | 477..481 'foo1': fn foo1<{unknown}>(A<[{unknown}]>) -> A<[{unknown}]> |
178 | 428..435 'foo1(a)': A<[{unknown}]> | 161 | 477..484 'foo1(a)': A<[{unknown}]> |
179 | 433..434 'a': A<[u8; 2]> | 162 | 482..483 'a': A<[u8; 2]> |
180 | 445..446 'e': B<[u8]> | 163 | 494..495 'e': B<[u8]> |
181 | 449..453 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]> | 164 | 498..502 'foo2': fn foo2<u8>(B<[u8]>) -> B<[u8]> |
182 | 449..456 'foo2(b)': B<[u8]> | 165 | 498..505 'foo2(b)': B<[u8]> |
183 | 454..455 'b': B<[u8; 2]> | 166 | 503..504 'b': B<[u8; 2]> |
184 | 466..467 'f': C<[u8]> | 167 | 515..516 'f': C<[u8]> |
185 | 470..474 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]> | 168 | 519..523 'foo3': fn foo3<u8>(C<[u8]>) -> C<[u8]> |
186 | 470..477 'foo3(c)': C<[u8]> | 169 | 519..526 'foo3(c)': C<[u8]> |
187 | 475..476 'c': C<[u8; 2]> | 170 | 524..525 'c': C<[u8; 2]> |
188 | "#]], | 171 | "#]], |
189 | ); | 172 | ); |
190 | } | 173 | } |
@@ -193,21 +176,16 @@ fn infer_custom_coerce_unsized() { | |||
193 | fn infer_if_coerce() { | 176 | fn infer_if_coerce() { |
194 | check_infer( | 177 | check_infer( |
195 | r#" | 178 | r#" |
196 | fn foo<T>(x: &[T]) -> &[T] { loop {} } | 179 | //- minicore: unsize |
197 | fn test() { | 180 | fn foo<T>(x: &[T]) -> &[T] { loop {} } |
198 | let x = if true { | 181 | fn test() { |
199 | foo(&[1]) | 182 | let x = if true { |
200 | } else { | 183 | foo(&[1]) |
201 | &[1] | 184 | } else { |
202 | }; | 185 | &[1] |
203 | } | 186 | }; |
204 | 187 | } | |
205 | 188 | "#, | |
206 | #[lang = "sized"] | ||
207 | pub trait Sized {} | ||
208 | #[lang = "unsize"] | ||
209 | pub trait Unsize<T: ?Sized> {} | ||
210 | "#, | ||
211 | expect![[r#" | 189 | expect![[r#" |
212 | 10..11 'x': &[T] | 190 | 10..11 'x': &[T] |
213 | 27..38 '{ loop {} }': &[T] | 191 | 27..38 '{ loop {} }': &[T] |
@@ -235,25 +213,16 @@ fn infer_if_coerce() { | |||
235 | fn infer_if_else_coerce() { | 213 | fn infer_if_else_coerce() { |
236 | check_infer( | 214 | check_infer( |
237 | r#" | 215 | r#" |
238 | fn foo<T>(x: &[T]) -> &[T] { loop {} } | 216 | //- minicore: coerce_unsized |
239 | fn test() { | 217 | fn foo<T>(x: &[T]) -> &[T] { loop {} } |
240 | let x = if true { | 218 | fn test() { |
241 | &[1] | 219 | let x = if true { |
242 | } else { | 220 | &[1] |
243 | foo(&[1]) | 221 | } else { |
244 | }; | 222 | foo(&[1]) |
245 | } | 223 | }; |
246 | 224 | } | |
247 | #[lang = "sized"] | 225 | "#, |
248 | pub trait Sized {} | ||
249 | #[lang = "unsize"] | ||
250 | pub trait Unsize<T: ?Sized> {} | ||
251 | #[lang = "coerce_unsized"] | ||
252 | pub trait CoerceUnsized<T> {} | ||
253 | |||
254 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} | ||
255 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} | ||
256 | "#, | ||
257 | expect![[r#" | 226 | expect![[r#" |
258 | 10..11 'x': &[T] | 227 | 10..11 'x': &[T] |
259 | 27..38 '{ loop {} }': &[T] | 228 | 27..38 '{ loop {} }': &[T] |
@@ -281,20 +250,16 @@ fn infer_if_else_coerce() { | |||
281 | fn infer_match_first_coerce() { | 250 | fn infer_match_first_coerce() { |
282 | check_infer( | 251 | check_infer( |
283 | r#" | 252 | r#" |
284 | fn foo<T>(x: &[T]) -> &[T] { loop {} } | 253 | //- minicore: unsize |
285 | fn test(i: i32) { | 254 | fn foo<T>(x: &[T]) -> &[T] { loop {} } |
286 | let x = match i { | 255 | fn test(i: i32) { |
287 | 2 => foo(&[2]), | 256 | let x = match i { |
288 | 1 => &[1], | 257 | 2 => foo(&[2]), |
289 | _ => &[3], | 258 | 1 => &[1], |
290 | }; | 259 | _ => &[3], |
291 | } | 260 | }; |
292 | 261 | } | |
293 | #[lang = "sized"] | 262 | "#, |
294 | pub trait Sized {} | ||
295 | #[lang = "unsize"] | ||
296 | pub trait Unsize<T: ?Sized> {} | ||
297 | "#, | ||
298 | expect![[r#" | 263 | expect![[r#" |
299 | 10..11 'x': &[T] | 264 | 10..11 'x': &[T] |
300 | 27..38 '{ loop {} }': &[T] | 265 | 27..38 '{ loop {} }': &[T] |
@@ -329,25 +294,16 @@ fn infer_match_first_coerce() { | |||
329 | fn infer_match_second_coerce() { | 294 | fn infer_match_second_coerce() { |
330 | check_infer( | 295 | check_infer( |
331 | r#" | 296 | r#" |
332 | fn foo<T>(x: &[T]) -> &[T] { loop {} } | 297 | //- minicore: coerce_unsized |
333 | fn test(i: i32) { | 298 | fn foo<T>(x: &[T]) -> &[T] { loop {} } |
334 | let x = match i { | 299 | fn test(i: i32) { |
335 | 1 => &[1], | 300 | let x = match i { |
336 | 2 => foo(&[2]), | 301 | 1 => &[1], |
337 | _ => &[3], | 302 | 2 => foo(&[2]), |
338 | }; | 303 | _ => &[3], |
339 | } | 304 | }; |
340 | 305 | } | |
341 | #[lang = "sized"] | 306 | "#, |
342 | pub trait Sized {} | ||
343 | #[lang = "unsize"] | ||
344 | pub trait Unsize<T: ?Sized> {} | ||
345 | #[lang = "coerce_unsized"] | ||
346 | pub trait CoerceUnsized<T> {} | ||
347 | |||
348 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} | ||
349 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} | ||
350 | "#, | ||
351 | expect![[r#" | 307 | expect![[r#" |
352 | 10..11 'x': &[T] | 308 | 10..11 'x': &[T] |
353 | 27..38 '{ loop {} }': &[T] | 309 | 27..38 '{ loop {} }': &[T] |
@@ -470,15 +426,15 @@ fn coerce_autoderef() { | |||
470 | #[test] | 426 | #[test] |
471 | fn coerce_autoderef_generic() { | 427 | fn coerce_autoderef_generic() { |
472 | check_infer_with_mismatches( | 428 | check_infer_with_mismatches( |
473 | r" | 429 | r#" |
474 | struct Foo; | 430 | struct Foo; |
475 | fn takes_ref<T>(x: &T) -> T { *x } | 431 | fn takes_ref<T>(x: &T) -> T { *x } |
476 | fn test() { | 432 | fn test() { |
477 | takes_ref(&Foo); | 433 | takes_ref(&Foo); |
478 | takes_ref(&&Foo); | 434 | takes_ref(&&Foo); |
479 | takes_ref(&&&Foo); | 435 | takes_ref(&&&Foo); |
480 | } | 436 | } |
481 | ", | 437 | "#, |
482 | expect![[r" | 438 | expect![[r" |
483 | 28..29 'x': &T | 439 | 28..29 'x': &T |
484 | 40..46 '{ *x }': T | 440 | 40..46 '{ *x }': T |
@@ -508,30 +464,29 @@ fn coerce_autoderef_generic() { | |||
508 | fn coerce_autoderef_block() { | 464 | fn coerce_autoderef_block() { |
509 | check_infer_with_mismatches( | 465 | check_infer_with_mismatches( |
510 | r#" | 466 | r#" |
511 | struct String {} | 467 | //- minicore: deref |
512 | #[lang = "deref"] | 468 | struct String {} |
513 | trait Deref { type Target; } | 469 | impl core::ops::Deref for String { type Target = str; } |
514 | impl Deref for String { type Target = str; } | 470 | fn takes_ref_str(x: &str) {} |
515 | fn takes_ref_str(x: &str) {} | 471 | fn returns_string() -> String { loop {} } |
516 | fn returns_string() -> String { loop {} } | 472 | fn test() { |
517 | fn test() { | 473 | takes_ref_str(&{ returns_string() }); |
518 | takes_ref_str(&{ returns_string() }); | 474 | } |
519 | } | 475 | "#, |
520 | "#, | 476 | expect![[r#" |
521 | expect![[r" | 477 | 90..91 'x': &str |
522 | 126..127 'x': &str | 478 | 99..101 '{}': () |
523 | 135..137 '{}': () | 479 | 132..143 '{ loop {} }': String |
524 | 168..179 '{ loop {} }': String | 480 | 134..141 'loop {}': ! |
525 | 170..177 'loop {}': ! | 481 | 139..141 '{}': () |
526 | 175..177 '{}': () | 482 | 154..199 '{ ... }); }': () |
527 | 190..235 '{ ... }); }': () | 483 | 160..173 'takes_ref_str': fn takes_ref_str(&str) |
528 | 196..209 'takes_ref_str': fn takes_ref_str(&str) | 484 | 160..196 'takes_...g() })': () |
529 | 196..232 'takes_...g() })': () | 485 | 174..195 '&{ ret...ng() }': &String |
530 | 210..231 '&{ ret...ng() }': &String | 486 | 175..195 '{ retu...ng() }': String |
531 | 211..231 '{ retu...ng() }': String | 487 | 177..191 'returns_string': fn returns_string() -> String |
532 | 213..227 'returns_string': fn returns_string() -> String | 488 | 177..193 'return...ring()': String |
533 | 213..229 'return...ring()': String | 489 | "#]], |
534 | "]], | ||
535 | ); | 490 | ); |
536 | } | 491 | } |
537 | 492 | ||
@@ -674,25 +629,19 @@ fn coerce_placeholder_ref() { | |||
674 | fn coerce_unsize_array() { | 629 | fn coerce_unsize_array() { |
675 | check_infer_with_mismatches( | 630 | check_infer_with_mismatches( |
676 | r#" | 631 | r#" |
677 | #[lang = "unsize"] | 632 | //- minicore: coerce_unsized |
678 | pub trait Unsize<T> {} | 633 | fn test() { |
679 | #[lang = "coerce_unsized"] | 634 | let f: &[usize] = &[1, 2, 3]; |
680 | pub trait CoerceUnsized<T> {} | 635 | } |
681 | |||
682 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | ||
683 | |||
684 | fn test() { | ||
685 | let f: &[usize] = &[1, 2, 3]; | ||
686 | } | ||
687 | "#, | 636 | "#, |
688 | expect![[r#" | 637 | expect![[r#" |
689 | 161..198 '{ ... 3]; }': () | 638 | 10..47 '{ ... 3]; }': () |
690 | 171..172 'f': &[usize] | 639 | 20..21 'f': &[usize] |
691 | 185..195 '&[1, 2, 3]': &[usize; 3] | 640 | 34..44 '&[1, 2, 3]': &[usize; 3] |
692 | 186..195 '[1, 2, 3]': [usize; 3] | 641 | 35..44 '[1, 2, 3]': [usize; 3] |
693 | 187..188 '1': usize | 642 | 36..37 '1': usize |
694 | 190..191 '2': usize | 643 | 39..40 '2': usize |
695 | 193..194 '3': usize | 644 | 42..43 '3': usize |
696 | "#]], | 645 | "#]], |
697 | ); | 646 | ); |
698 | } | 647 | } |
@@ -701,93 +650,94 @@ fn coerce_unsize_array() { | |||
701 | fn coerce_unsize_trait_object_simple() { | 650 | fn coerce_unsize_trait_object_simple() { |
702 | check_infer_with_mismatches( | 651 | check_infer_with_mismatches( |
703 | r#" | 652 | r#" |
704 | #[lang = "sized"] | 653 | //- minicore: coerce_unsized |
705 | pub trait Sized {} | 654 | trait Foo<T, U> {} |
706 | #[lang = "unsize"] | 655 | trait Bar<U, T, X>: Foo<T, U> {} |
707 | pub trait Unsize<T> {} | 656 | trait Baz<T, X>: Bar<usize, T, X> {} |
708 | #[lang = "coerce_unsized"] | 657 | |
709 | pub trait CoerceUnsized<T> {} | 658 | struct S<T, X>; |
710 | 659 | impl<T, X> Foo<T, usize> for S<T, X> {} | |
711 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | 660 | impl<T, X> Bar<usize, T, X> for S<T, X> {} |
712 | 661 | impl<T, X> Baz<T, X> for S<T, X> {} | |
713 | trait Foo<T, U> {} | 662 | |
714 | trait Bar<U, T, X>: Foo<T, U> {} | 663 | fn test() { |
715 | trait Baz<T, X>: Bar<usize, T, X> {} | 664 | let obj: &dyn Baz<i8, i16> = &S; |
716 | 665 | let obj: &dyn Bar<_, i8, i16> = &S; | |
717 | struct S<T, X>; | 666 | let obj: &dyn Foo<i8, _> = &S; |
718 | impl<T, X> Foo<T, usize> for S<T, X> {} | 667 | } |
719 | impl<T, X> Bar<usize, T, X> for S<T, X> {} | 668 | "#, |
720 | impl<T, X> Baz<T, X> for S<T, X> {} | 669 | expect![[r#" |
721 | 670 | 236..351 '{ ... &S; }': () | |
722 | fn test() { | 671 | 246..249 'obj': &dyn Baz<i8, i16> |
723 | let obj: &dyn Baz<i8, i16> = &S; | 672 | 271..273 '&S': &S<i8, i16> |
724 | let obj: &dyn Bar<_, i8, i16> = &S; | 673 | 272..273 'S': S<i8, i16> |
725 | let obj: &dyn Foo<i8, _> = &S; | 674 | 283..286 'obj': &dyn Bar<usize, i8, i16> |
726 | } | 675 | 311..313 '&S': &S<i8, i16> |
727 | "#, | 676 | 312..313 'S': S<i8, i16> |
728 | expect![[r" | 677 | 323..326 'obj': &dyn Foo<i8, usize> |
729 | 424..539 '{ ... &S; }': () | 678 | 346..348 '&S': &S<i8, {unknown}> |
730 | 434..437 'obj': &dyn Baz<i8, i16> | 679 | 347..348 'S': S<i8, {unknown}> |
731 | 459..461 '&S': &S<i8, i16> | 680 | "#]], |
732 | 460..461 'S': S<i8, i16> | ||
733 | 471..474 'obj': &dyn Bar<usize, i8, i16> | ||
734 | 499..501 '&S': &S<i8, i16> | ||
735 | 500..501 'S': S<i8, i16> | ||
736 | 511..514 'obj': &dyn Foo<i8, usize> | ||
737 | 534..536 '&S': &S<i8, {unknown}> | ||
738 | 535..536 'S': S<i8, {unknown}> | ||
739 | "]], | ||
740 | ); | 681 | ); |
741 | } | 682 | } |
742 | 683 | ||
743 | #[test] | 684 | #[test] |
744 | // The rust reference says this should be possible, but rustc doesn't implement | ||
745 | // it. We used to support it, but Chalk doesn't. | ||
746 | #[ignore] | ||
747 | fn coerce_unsize_trait_object_to_trait_object() { | 685 | fn coerce_unsize_trait_object_to_trait_object() { |
686 | // FIXME: The rust reference says this should be possible, but rustc doesn't | ||
687 | // implement it. We used to support it, but Chalk doesn't. Here's the | ||
688 | // correct expect: | ||
689 | // | ||
690 | // 424..609 '{ ...bj2; }': () | ||
691 | // 434..437 'obj': &dyn Baz<i8, i16> | ||
692 | // 459..461 '&S': &S<i8, i16> | ||
693 | // 460..461 'S': S<i8, i16> | ||
694 | // 471..474 'obj': &dyn Bar<usize, i8, i16> | ||
695 | // 496..499 'obj': &dyn Baz<i8, i16> | ||
696 | // 509..512 'obj': &dyn Foo<i8, usize> | ||
697 | // 531..534 'obj': &dyn Bar<usize, i8, i16> | ||
698 | // 544..548 'obj2': &dyn Baz<i8, i16> | ||
699 | // 570..572 '&S': &S<i8, i16> | ||
700 | // 571..572 'S': S<i8, i16> | ||
701 | // 582..583 '_': &dyn Foo<i8, usize> | ||
702 | // 602..606 'obj2': &dyn Baz<i8, i16> | ||
748 | check_infer_with_mismatches( | 703 | check_infer_with_mismatches( |
749 | r#" | 704 | r#" |
750 | #[lang = "sized"] | 705 | //- minicore: coerce_unsized |
751 | pub trait Sized {} | 706 | trait Foo<T, U> {} |
752 | #[lang = "unsize"] | 707 | trait Bar<U, T, X>: Foo<T, U> {} |
753 | pub trait Unsize<T> {} | 708 | trait Baz<T, X>: Bar<usize, T, X> {} |
754 | #[lang = "coerce_unsized"] | 709 | |
755 | pub trait CoerceUnsized<T> {} | 710 | struct S<T, X>; |
756 | 711 | impl<T, X> Foo<T, usize> for S<T, X> {} | |
757 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | 712 | impl<T, X> Bar<usize, T, X> for S<T, X> {} |
758 | 713 | impl<T, X> Baz<T, X> for S<T, X> {} | |
759 | trait Foo<T, U> {} | 714 | |
760 | trait Bar<U, T, X>: Foo<T, U> {} | 715 | fn test() { |
761 | trait Baz<T, X>: Bar<usize, T, X> {} | 716 | let obj: &dyn Baz<i8, i16> = &S; |
762 | 717 | let obj: &dyn Bar<_, _, _> = obj; | |
763 | struct S<T, X>; | 718 | let obj: &dyn Foo<_, _> = obj; |
764 | impl<T, X> Foo<T, usize> for S<T, X> {} | 719 | let obj2: &dyn Baz<i8, i16> = &S; |
765 | impl<T, X> Bar<usize, T, X> for S<T, X> {} | 720 | let _: &dyn Foo<_, _> = obj2; |
766 | impl<T, X> Baz<T, X> for S<T, X> {} | 721 | } |
767 | 722 | "#, | |
768 | fn test() { | 723 | expect![[r#" |
769 | let obj: &dyn Baz<i8, i16> = &S; | 724 | 236..421 '{ ...bj2; }': () |
770 | let obj: &dyn Bar<_, _, _> = obj; | 725 | 246..249 'obj': &dyn Baz<i8, i16> |
771 | let obj: &dyn Foo<_, _> = obj; | 726 | 271..273 '&S': &S<i8, i16> |
772 | let obj2: &dyn Baz<i8, i16> = &S; | 727 | 272..273 'S': S<i8, i16> |
773 | let _: &dyn Foo<_, _> = obj2; | 728 | 283..286 'obj': &dyn Bar<{unknown}, {unknown}, {unknown}> |
774 | } | 729 | 308..311 'obj': &dyn Baz<i8, i16> |
775 | "#, | 730 | 321..324 'obj': &dyn Foo<{unknown}, {unknown}> |
776 | expect![[r" | 731 | 343..346 'obj': &dyn Bar<{unknown}, {unknown}, {unknown}> |
777 | 424..609 '{ ...bj2; }': () | 732 | 356..360 'obj2': &dyn Baz<i8, i16> |
778 | 434..437 'obj': &dyn Baz<i8, i16> | 733 | 382..384 '&S': &S<i8, i16> |
779 | 459..461 '&S': &S<i8, i16> | 734 | 383..384 'S': S<i8, i16> |
780 | 460..461 'S': S<i8, i16> | 735 | 394..395 '_': &dyn Foo<{unknown}, {unknown}> |
781 | 471..474 'obj': &dyn Bar<usize, i8, i16> | 736 | 414..418 'obj2': &dyn Baz<i8, i16> |
782 | 496..499 'obj': &dyn Baz<i8, i16> | 737 | 308..311: expected &dyn Bar<{unknown}, {unknown}, {unknown}>, got &dyn Baz<i8, i16> |
783 | 509..512 'obj': &dyn Foo<i8, usize> | 738 | 343..346: expected &dyn Foo<{unknown}, {unknown}>, got &dyn Bar<{unknown}, {unknown}, {unknown}> |
784 | 531..534 'obj': &dyn Bar<usize, i8, i16> | 739 | 414..418: expected &dyn Foo<{unknown}, {unknown}>, got &dyn Baz<i8, i16> |
785 | 544..548 'obj2': &dyn Baz<i8, i16> | 740 | "#]], |
786 | 570..572 '&S': &S<i8, i16> | ||
787 | 571..572 'S': S<i8, i16> | ||
788 | 582..583 '_': &dyn Foo<i8, usize> | ||
789 | 602..606 'obj2': &dyn Baz<i8, i16> | ||
790 | "]], | ||
791 | ); | 741 | ); |
792 | } | 742 | } |
793 | 743 | ||
@@ -795,40 +745,32 @@ fn coerce_unsize_trait_object_to_trait_object() { | |||
795 | fn coerce_unsize_super_trait_cycle() { | 745 | fn coerce_unsize_super_trait_cycle() { |
796 | check_infer_with_mismatches( | 746 | check_infer_with_mismatches( |
797 | r#" | 747 | r#" |
798 | #[lang = "sized"] | 748 | //- minicore: coerce_unsized |
799 | pub trait Sized {} | 749 | trait A {} |
800 | #[lang = "unsize"] | 750 | trait B: C + A {} |
801 | pub trait Unsize<T> {} | 751 | trait C: B {} |
802 | #[lang = "coerce_unsized"] | 752 | trait D: C |
803 | pub trait CoerceUnsized<T> {} | 753 | |
804 | 754 | struct S; | |
805 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | 755 | impl A for S {} |
806 | 756 | impl B for S {} | |
807 | trait A {} | 757 | impl C for S {} |
808 | trait B: C + A {} | 758 | impl D for S {} |
809 | trait C: B {} | 759 | |
810 | trait D: C | 760 | fn test() { |
811 | 761 | let obj: &dyn D = &S; | |
812 | struct S; | 762 | let obj: &dyn A = &S; |
813 | impl A for S {} | 763 | } |
814 | impl B for S {} | 764 | "#, |
815 | impl C for S {} | 765 | expect![[r#" |
816 | impl D for S {} | 766 | 140..195 '{ ... &S; }': () |
817 | 767 | 150..153 'obj': &dyn D | |
818 | fn test() { | 768 | 164..166 '&S': &S |
819 | let obj: &dyn D = &S; | 769 | 165..166 'S': S |
820 | let obj: &dyn A = &S; | 770 | 176..179 'obj': &dyn A |
821 | } | 771 | 190..192 '&S': &S |
822 | "#, | 772 | 191..192 'S': S |
823 | expect![[r" | 773 | "#]], |
824 | 328..383 '{ ... &S; }': () | ||
825 | 338..341 'obj': &dyn D | ||
826 | 352..354 '&S': &S | ||
827 | 353..354 'S': S | ||
828 | 364..367 'obj': &dyn A | ||
829 | 378..380 '&S': &S | ||
830 | 379..380 'S': S | ||
831 | "]], | ||
832 | ); | 774 | ); |
833 | } | 775 | } |
834 | 776 | ||
@@ -837,41 +779,35 @@ fn coerce_unsize_generic() { | |||
837 | // FIXME: fix the type mismatches here | 779 | // FIXME: fix the type mismatches here |
838 | check_infer_with_mismatches( | 780 | check_infer_with_mismatches( |
839 | r#" | 781 | r#" |
840 | #[lang = "unsize"] | 782 | //- minicore: coerce_unsized |
841 | pub trait Unsize<T> {} | 783 | struct Foo<T> { t: T }; |
842 | #[lang = "coerce_unsized"] | 784 | struct Bar<T>(Foo<T>); |
843 | pub trait CoerceUnsized<T> {} | ||
844 | |||
845 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | ||
846 | 785 | ||
847 | struct Foo<T> { t: T }; | 786 | fn test() { |
848 | struct Bar<T>(Foo<T>); | 787 | let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] }; |
849 | 788 | let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); | |
850 | fn test() { | 789 | } |
851 | let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] }; | 790 | "#, |
852 | let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); | ||
853 | } | ||
854 | "#, | ||
855 | expect![[r#" | 791 | expect![[r#" |
856 | 209..317 '{ ... }); }': () | 792 | 58..166 '{ ... }); }': () |
857 | 219..220 '_': &Foo<[usize]> | 793 | 68..69 '_': &Foo<[usize]> |
858 | 238..259 '&Foo {..., 3] }': &Foo<[usize]> | 794 | 87..108 '&Foo {..., 3] }': &Foo<[usize]> |
859 | 239..259 'Foo { ..., 3] }': Foo<[usize]> | 795 | 88..108 'Foo { ..., 3] }': Foo<[usize]> |
860 | 248..257 '[1, 2, 3]': [usize; 3] | 796 | 97..106 '[1, 2, 3]': [usize; 3] |
861 | 249..250 '1': usize | 797 | 98..99 '1': usize |
862 | 252..253 '2': usize | 798 | 101..102 '2': usize |
863 | 255..256 '3': usize | 799 | 104..105 '3': usize |
864 | 269..270 '_': &Bar<[usize]> | 800 | 118..119 '_': &Bar<[usize]> |
865 | 288..314 '&Bar(F... 3] })': &Bar<[i32; 3]> | 801 | 137..163 '&Bar(F... 3] })': &Bar<[i32; 3]> |
866 | 289..292 'Bar': Bar<[i32; 3]>(Foo<[i32; 3]>) -> Bar<[i32; 3]> | 802 | 138..141 'Bar': Bar<[i32; 3]>(Foo<[i32; 3]>) -> Bar<[i32; 3]> |
867 | 289..314 'Bar(Fo... 3] })': Bar<[i32; 3]> | 803 | 138..163 'Bar(Fo... 3] })': Bar<[i32; 3]> |
868 | 293..313 'Foo { ..., 3] }': Foo<[i32; 3]> | 804 | 142..162 'Foo { ..., 3] }': Foo<[i32; 3]> |
869 | 302..311 '[1, 2, 3]': [i32; 3] | 805 | 151..160 '[1, 2, 3]': [i32; 3] |
870 | 303..304 '1': i32 | 806 | 152..153 '1': i32 |
871 | 306..307 '2': i32 | 807 | 155..156 '2': i32 |
872 | 309..310 '3': i32 | 808 | 158..159 '3': i32 |
873 | 248..257: expected [usize], got [usize; 3] | 809 | 97..106: expected [usize], got [usize; 3] |
874 | 288..314: expected &Bar<[usize]>, got &Bar<[i32; 3]> | 810 | 137..163: expected &Bar<[usize]>, got &Bar<[i32; 3]> |
875 | "#]], | 811 | "#]], |
876 | ); | 812 | ); |
877 | } | 813 | } |
@@ -881,15 +817,7 @@ fn coerce_unsize_apit() { | |||
881 | // FIXME: #8984 | 817 | // FIXME: #8984 |
882 | check_infer_with_mismatches( | 818 | check_infer_with_mismatches( |
883 | r#" | 819 | r#" |
884 | #[lang = "sized"] | 820 | //- minicore: coerce_unsized |
885 | pub trait Sized {} | ||
886 | #[lang = "unsize"] | ||
887 | pub trait Unsize<T> {} | ||
888 | #[lang = "coerce_unsized"] | ||
889 | pub trait CoerceUnsized<T> {} | ||
890 | |||
891 | impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {} | ||
892 | |||
893 | trait Foo {} | 821 | trait Foo {} |
894 | 822 | ||
895 | fn test(f: impl Foo) { | 823 | fn test(f: impl Foo) { |
@@ -897,12 +825,12 @@ fn test(f: impl Foo) { | |||
897 | } | 825 | } |
898 | "#, | 826 | "#, |
899 | expect![[r#" | 827 | expect![[r#" |
900 | 210..211 'f': impl Foo | 828 | 22..23 'f': impl Foo |
901 | 223..252 '{ ... &f; }': () | 829 | 35..64 '{ ... &f; }': () |
902 | 233..234 '_': &dyn Foo | 830 | 45..46 '_': &dyn Foo |
903 | 247..249 '&f': &impl Foo | 831 | 59..61 '&f': &impl Foo |
904 | 248..249 'f': impl Foo | 832 | 60..61 'f': impl Foo |
905 | 247..249: expected &dyn Foo, got &impl Foo | 833 | 59..61: expected &dyn Foo, got &impl Foo |
906 | "#]], | 834 | "#]], |
907 | ); | 835 | ); |
908 | } | 836 | } |
@@ -963,7 +891,7 @@ fn test() -> i32 { | |||
963 | 891 | ||
964 | #[test] | 892 | #[test] |
965 | fn panic_macro() { | 893 | fn panic_macro() { |
966 | check_infer_with_mismatches( | 894 | check_no_mismatches( |
967 | r#" | 895 | r#" |
968 | mod panic { | 896 | mod panic { |
969 | #[macro_export] | 897 | #[macro_export] |
@@ -991,15 +919,26 @@ fn main() { | |||
991 | panic!() | 919 | panic!() |
992 | } | 920 | } |
993 | "#, | 921 | "#, |
994 | expect![[r#" | 922 | ); |
995 | 174..185 '{ loop {} }': ! | 923 | } |
996 | 176..183 'loop {}': ! | 924 | |
997 | 181..183 '{}': () | 925 | #[test] |
998 | !0..24 '$crate...:panic': fn panic() -> ! | 926 | fn coerce_unsize_expected_type() { |
999 | !0..26 '$crate...anic()': ! | 927 | check_no_mismatches( |
1000 | !0..26 '$crate...anic()': ! | 928 | r#" |
1001 | !0..28 '$crate...015!()': ! | 929 | //- minicore: coerce_unsized |
1002 | 454..470 '{ ...c!() }': () | 930 | fn main() { |
1003 | "#]], | 931 | let foo: &[u32] = &[1, 2]; |
932 | let foo: &[u32] = match true { | ||
933 | true => &[1, 2], | ||
934 | false => &[1, 2, 3], | ||
935 | }; | ||
936 | let foo: &[u32] = if true { | ||
937 | &[1, 2] | ||
938 | } else { | ||
939 | &[1, 2, 3] | ||
940 | }; | ||
941 | } | ||
942 | "#, | ||
1004 | ); | 943 | ); |
1005 | } | 944 | } |
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index f26b2c8a7..d9b5ee9cf 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs | |||
@@ -780,10 +780,7 @@ fn test() { (&S).foo(); } | |||
780 | fn method_resolution_unsize_array() { | 780 | fn method_resolution_unsize_array() { |
781 | check_types( | 781 | check_types( |
782 | r#" | 782 | r#" |
783 | #[lang = "slice"] | 783 | //- minicore: slice |
784 | impl<T> [T] { | ||
785 | fn len(&self) -> usize { loop {} } | ||
786 | } | ||
787 | fn test() { | 784 | fn test() { |
788 | let a = [1, 2, 3]; | 785 | let a = [1, 2, 3]; |
789 | a.len(); | 786 | a.len(); |
@@ -1178,11 +1175,7 @@ fn main() { | |||
1178 | fn autoderef_visibility_field() { | 1175 | fn autoderef_visibility_field() { |
1179 | check_infer( | 1176 | check_infer( |
1180 | r#" | 1177 | r#" |
1181 | #[lang = "deref"] | 1178 | //- minicore: deref |
1182 | pub trait Deref { | ||
1183 | type Target; | ||
1184 | fn deref(&self) -> &Self::Target; | ||
1185 | } | ||
1186 | mod a { | 1179 | mod a { |
1187 | pub struct Foo(pub char); | 1180 | pub struct Foo(pub char); |
1188 | pub struct Bar(i32); | 1181 | pub struct Bar(i32); |
@@ -1191,7 +1184,7 @@ mod a { | |||
1191 | Self(0) | 1184 | Self(0) |
1192 | } | 1185 | } |
1193 | } | 1186 | } |
1194 | impl super::Deref for Bar { | 1187 | impl core::ops::Deref for Bar { |
1195 | type Target = Foo; | 1188 | type Target = Foo; |
1196 | fn deref(&self) -> &Foo { | 1189 | fn deref(&self) -> &Foo { |
1197 | &Foo('z') | 1190 | &Foo('z') |
@@ -1205,22 +1198,21 @@ mod b { | |||
1205 | } | 1198 | } |
1206 | "#, | 1199 | "#, |
1207 | expect![[r#" | 1200 | expect![[r#" |
1208 | 67..71 'self': &Self | 1201 | 107..138 '{ ... }': Bar |
1209 | 200..231 '{ ... }': Bar | 1202 | 121..125 'Self': Bar(i32) -> Bar |
1210 | 214..218 'Self': Bar(i32) -> Bar | 1203 | 121..128 'Self(0)': Bar |
1211 | 214..221 'Self(0)': Bar | 1204 | 126..127 '0': i32 |
1212 | 219..220 '0': i32 | 1205 | 226..230 'self': &Bar |
1213 | 315..319 'self': &Bar | 1206 | 240..273 '{ ... }': &Foo |
1214 | 329..362 '{ ... }': &Foo | 1207 | 254..263 '&Foo('z')': &Foo |
1215 | 343..352 '&Foo('z')': &Foo | 1208 | 255..258 'Foo': Foo(char) -> Foo |
1216 | 344..347 'Foo': Foo(char) -> Foo | 1209 | 255..263 'Foo('z')': Foo |
1217 | 344..352 'Foo('z')': Foo | 1210 | 259..262 ''z'': char |
1218 | 348..351 ''z'': char | 1211 | 303..350 '{ ... }': () |
1219 | 392..439 '{ ... }': () | 1212 | 317..318 'x': char |
1220 | 406..407 'x': char | 1213 | 321..339 'super:...r::new': fn new() -> Bar |
1221 | 410..428 'super:...r::new': fn new() -> Bar | 1214 | 321..341 'super:...:new()': Bar |
1222 | 410..430 'super:...:new()': Bar | 1215 | 321..343 'super:...ew().0': char |
1223 | 410..432 'super:...ew().0': char | ||
1224 | "#]], | 1216 | "#]], |
1225 | ) | 1217 | ) |
1226 | } | 1218 | } |
@@ -1230,11 +1222,7 @@ fn autoderef_visibility_method() { | |||
1230 | cov_mark::check!(autoderef_candidate_not_visible); | 1222 | cov_mark::check!(autoderef_candidate_not_visible); |
1231 | check_infer( | 1223 | check_infer( |
1232 | r#" | 1224 | r#" |
1233 | #[lang = "deref"] | 1225 | //- minicore: deref |
1234 | pub trait Deref { | ||
1235 | type Target; | ||
1236 | fn deref(&self) -> &Self::Target; | ||
1237 | } | ||
1238 | mod a { | 1226 | mod a { |
1239 | pub struct Foo(pub char); | 1227 | pub struct Foo(pub char); |
1240 | impl Foo { | 1228 | impl Foo { |
@@ -1251,7 +1239,7 @@ mod a { | |||
1251 | self.0 | 1239 | self.0 |
1252 | } | 1240 | } |
1253 | } | 1241 | } |
1254 | impl super::Deref for Bar { | 1242 | impl core::ops::Deref for Bar { |
1255 | type Target = Foo; | 1243 | type Target = Foo; |
1256 | fn deref(&self) -> &Foo { | 1244 | fn deref(&self) -> &Foo { |
1257 | &Foo('z') | 1245 | &Foo('z') |
@@ -1265,30 +1253,29 @@ mod b { | |||
1265 | } | 1253 | } |
1266 | "#, | 1254 | "#, |
1267 | expect![[r#" | 1255 | expect![[r#" |
1268 | 67..71 'self': &Self | 1256 | 75..79 'self': &Foo |
1269 | 168..172 'self': &Foo | 1257 | 89..119 '{ ... }': char |
1270 | 182..212 '{ ... }': char | 1258 | 103..107 'self': &Foo |
1271 | 196..200 'self': &Foo | 1259 | 103..109 'self.0': char |
1272 | 196..202 'self.0': char | 1260 | 195..226 '{ ... }': Bar |
1273 | 288..319 '{ ... }': Bar | 1261 | 209..213 'Self': Bar(i32) -> Bar |
1274 | 302..306 'Self': Bar(i32) -> Bar | 1262 | 209..216 'Self(0)': Bar |
1275 | 302..309 'Self(0)': Bar | 1263 | 214..215 '0': i32 |
1276 | 307..308 '0': i32 | 1264 | 245..249 'self': &Bar |
1277 | 338..342 'self': &Bar | 1265 | 258..288 '{ ... }': i32 |
1278 | 351..381 '{ ... }': i32 | 1266 | 272..276 'self': &Bar |
1279 | 365..369 'self': &Bar | 1267 | 272..278 'self.0': i32 |
1280 | 365..371 'self.0': i32 | 1268 | 376..380 'self': &Bar |
1281 | 465..469 'self': &Bar | 1269 | 390..423 '{ ... }': &Foo |
1282 | 479..512 '{ ... }': &Foo | 1270 | 404..413 '&Foo('z')': &Foo |
1283 | 493..502 '&Foo('z')': &Foo | 1271 | 405..408 'Foo': Foo(char) -> Foo |
1284 | 494..497 'Foo': Foo(char) -> Foo | 1272 | 405..413 'Foo('z')': Foo |
1285 | 494..502 'Foo('z')': Foo | 1273 | 409..412 ''z'': char |
1286 | 498..501 ''z'': char | 1274 | 453..506 '{ ... }': () |
1287 | 542..595 '{ ... }': () | 1275 | 467..468 'x': char |
1288 | 556..557 'x': char | 1276 | 471..489 'super:...r::new': fn new() -> Bar |
1289 | 560..578 'super:...r::new': fn new() -> Bar | 1277 | 471..491 'super:...:new()': Bar |
1290 | 560..580 'super:...:new()': Bar | 1278 | 471..499 'super:...ango()': char |
1291 | 560..588 'super:...ango()': char | ||
1292 | "#]], | 1279 | "#]], |
1293 | ) | 1280 | ) |
1294 | } | 1281 | } |
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index 7d00cee9b..5adbe9c45 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | 2 | ||
3 | use super::{check_infer, check_infer_with_mismatches, check_types}; | 3 | use super::{check_infer, check_infer_with_mismatches, check_mismatches, check_types}; |
4 | 4 | ||
5 | #[test] | 5 | #[test] |
6 | fn infer_pattern() { | 6 | fn infer_pattern() { |
@@ -518,47 +518,24 @@ fn infer_generics_in_patterns() { | |||
518 | 518 | ||
519 | #[test] | 519 | #[test] |
520 | fn infer_const_pattern() { | 520 | fn infer_const_pattern() { |
521 | check_infer_with_mismatches( | 521 | check_mismatches( |
522 | r#" | 522 | r#" |
523 | enum Option<T> { None } | 523 | enum Option<T> { None } |
524 | use Option::None; | 524 | use Option::None; |
525 | struct Foo; | 525 | struct Foo; |
526 | const Bar: usize = 1; | 526 | const Bar: usize = 1; |
527 | 527 | ||
528 | fn test() { | 528 | fn test() { |
529 | let a: Option<u32> = None; | 529 | let a: Option<u32> = None; |
530 | let b: Option<i64> = match a { | 530 | let b: Option<i64> = match a { |
531 | None => None, | 531 | None => None, |
532 | }; | 532 | }; |
533 | let _: () = match () { Foo => Foo }; // Expected mismatch | 533 | let _: () = match () { Foo => () }; |
534 | let _: () = match () { Bar => Bar }; // Expected mismatch | 534 | // ^^^ expected (), got Foo |
535 | } | 535 | let _: () = match () { Bar => () }; |
536 | // ^^^ expected (), got usize | ||
537 | } | ||
536 | "#, | 538 | "#, |
537 | expect![[r#" | ||
538 | 73..74 '1': usize | ||
539 | 87..309 '{ ...atch }': () | ||
540 | 97..98 'a': Option<u32> | ||
541 | 114..118 'None': Option<u32> | ||
542 | 128..129 'b': Option<i64> | ||
543 | 145..182 'match ... }': Option<i64> | ||
544 | 151..152 'a': Option<u32> | ||
545 | 163..167 'None': Option<u32> | ||
546 | 171..175 'None': Option<i64> | ||
547 | 192..193 '_': () | ||
548 | 200..223 'match ... Foo }': Foo | ||
549 | 206..208 '()': () | ||
550 | 211..214 'Foo': Foo | ||
551 | 218..221 'Foo': Foo | ||
552 | 254..255 '_': () | ||
553 | 262..285 'match ... Bar }': usize | ||
554 | 268..270 '()': () | ||
555 | 273..276 'Bar': usize | ||
556 | 280..283 'Bar': usize | ||
557 | 200..223: expected (), got Foo | ||
558 | 211..214: expected (), got Foo | ||
559 | 262..285: expected (), got usize | ||
560 | 273..276: expected (), got usize | ||
561 | "#]], | ||
562 | ); | 539 | ); |
563 | } | 540 | } |
564 | 541 | ||
@@ -594,48 +571,44 @@ fn main() { | |||
594 | fn match_ergonomics_in_closure_params() { | 571 | fn match_ergonomics_in_closure_params() { |
595 | check_infer( | 572 | check_infer( |
596 | r#" | 573 | r#" |
597 | #[lang = "fn_once"] | 574 | //- minicore: fn |
598 | trait FnOnce<Args> { | 575 | fn foo<T, U, F: FnOnce(T) -> U>(t: T, f: F) -> U { loop {} } |
599 | type Output; | ||
600 | } | ||
601 | |||
602 | fn foo<T, U, F: FnOnce(T) -> U>(t: T, f: F) -> U { loop {} } | ||
603 | 576 | ||
604 | fn test() { | 577 | fn test() { |
605 | foo(&(1, "a"), |&(x, y)| x); // normal, no match ergonomics | 578 | foo(&(1, "a"), |&(x, y)| x); // normal, no match ergonomics |
606 | foo(&(1, "a"), |(x, y)| x); | 579 | foo(&(1, "a"), |(x, y)| x); |
607 | } | 580 | } |
608 | "#, | 581 | "#, |
609 | expect![[r#" | 582 | expect![[r#" |
610 | 93..94 't': T | 583 | 32..33 't': T |
611 | 99..100 'f': F | 584 | 38..39 'f': F |
612 | 110..121 '{ loop {} }': U | 585 | 49..60 '{ loop {} }': U |
613 | 112..119 'loop {}': ! | 586 | 51..58 'loop {}': ! |
614 | 117..119 '{}': () | 587 | 56..58 '{}': () |
615 | 133..232 '{ ... x); }': () | 588 | 72..171 '{ ... x); }': () |
616 | 139..142 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32 | 589 | 78..81 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32 |
617 | 139..166 'foo(&(...y)| x)': i32 | 590 | 78..105 'foo(&(...y)| x)': i32 |
618 | 143..152 '&(1, "a")': &(i32, &str) | 591 | 82..91 '&(1, "a")': &(i32, &str) |
619 | 144..152 '(1, "a")': (i32, &str) | 592 | 83..91 '(1, "a")': (i32, &str) |
620 | 145..146 '1': i32 | 593 | 84..85 '1': i32 |
621 | 148..151 '"a"': &str | 594 | 87..90 '"a"': &str |
622 | 154..165 '|&(x, y)| x': |&(i32, &str)| -> i32 | 595 | 93..104 '|&(x, y)| x': |&(i32, &str)| -> i32 |
623 | 155..162 '&(x, y)': &(i32, &str) | 596 | 94..101 '&(x, y)': &(i32, &str) |
624 | 156..162 '(x, y)': (i32, &str) | 597 | 95..101 '(x, y)': (i32, &str) |
625 | 157..158 'x': i32 | 598 | 96..97 'x': i32 |
626 | 160..161 'y': &str | 599 | 99..100 'y': &str |
627 | 164..165 'x': i32 | 600 | 103..104 'x': i32 |
628 | 203..206 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32 | 601 | 142..145 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32 |
629 | 203..229 'foo(&(...y)| x)': &i32 | 602 | 142..168 'foo(&(...y)| x)': &i32 |
630 | 207..216 '&(1, "a")': &(i32, &str) | 603 | 146..155 '&(1, "a")': &(i32, &str) |
631 | 208..216 '(1, "a")': (i32, &str) | 604 | 147..155 '(1, "a")': (i32, &str) |
632 | 209..210 '1': i32 | 605 | 148..149 '1': i32 |
633 | 212..215 '"a"': &str | 606 | 151..154 '"a"': &str |
634 | 218..228 '|(x, y)| x': |&(i32, &str)| -> &i32 | 607 | 157..167 '|(x, y)| x': |&(i32, &str)| -> &i32 |
635 | 219..225 '(x, y)': (i32, &str) | 608 | 158..164 '(x, y)': (i32, &str) |
636 | 220..221 'x': &i32 | 609 | 159..160 |