aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_ty/src')
-rw-r--r--crates/hir_ty/src/builder.rs2
-rw-r--r--crates/hir_ty/src/chalk_db.rs39
-rw-r--r--crates/hir_ty/src/consteval.rs2
-rw-r--r--crates/hir_ty/src/db.rs7
-rw-r--r--crates/hir_ty/src/diagnostics.rs772
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs357
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs507
-rw-r--r--crates/hir_ty/src/diagnostics/match_check.rs955
-rw-r--r--crates/hir_ty/src/diagnostics/match_check/deconstruct_pat.rs2
-rw-r--r--crates/hir_ty/src/diagnostics/match_check/usefulness.rs13
-rw-r--r--crates/hir_ty/src/diagnostics/unsafe_check.rs151
-rw-r--r--crates/hir_ty/src/diagnostics_sink.rs109
-rw-r--r--crates/hir_ty/src/infer.rs98
-rw-r--r--crates/hir_ty/src/infer/coerce.rs8
-rw-r--r--crates/hir_ty/src/infer/expr.rs23
-rw-r--r--crates/hir_ty/src/infer/pat.rs15
-rw-r--r--crates/hir_ty/src/infer/path.rs6
-rw-r--r--crates/hir_ty/src/interner.rs6
-rw-r--r--crates/hir_ty/src/lib.rs3
-rw-r--r--crates/hir_ty/src/lower.rs10
-rw-r--r--crates/hir_ty/src/method_resolution.rs98
-rw-r--r--crates/hir_ty/src/test_db.rs10
-rw-r--r--crates/hir_ty/src/tests.rs98
-rw-r--r--crates/hir_ty/src/tests/coercion.rs43
-rw-r--r--crates/hir_ty/src/tests/method_resolution.rs49
-rw-r--r--crates/hir_ty/src/tests/patterns.rs104
-rw-r--r--crates/hir_ty/src/tests/traits.rs67
27 files changed, 623 insertions, 2931 deletions
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
203impl TyBuilder<Binders<Ty>> { 203impl 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};
10use base_db::CrateId; 10use base_db::CrateId;
11use hir_def::{ 11use 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};
15use hir_expand::name::name; 15use hir_expand::name::name;
16 16
17use crate::{ 17use 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
6use base_db::{impl_intern_key, salsa, CrateId, Upcast}; 6use base_db::{impl_intern_key, salsa, CrateId, Upcast};
7use hir_def::{ 7use 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};
11use la_arena::ArenaMap; 11use 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;
4mod unsafe_check; 4mod unsafe_check;
5mod decl_check; 5mod decl_check;
6 6
7use std::{any::Any, fmt}; 7use std::fmt;
8 8
9use base_db::CrateId; 9use base_db::CrateId;
10use hir_def::{DefWithBodyId, ModuleDefId}; 10use hir_def::ModuleDefId;
11use hir_expand::{name::Name, HirFileId, InFile}; 11use hir_expand::HirFileId;
12use stdx::format_to; 12use syntax::{ast, AstPtr};
13use syntax::{ast, AstPtr, SyntaxNodePtr};
14 13
15use crate::{ 14use crate::db::HirDatabase;
16 db::HirDatabase,
17 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
18};
19 15
20pub use crate::diagnostics::expr::{record_literal_missing_fields, record_pattern_missing_fields}; 16pub use crate::diagnostics::{
17 expr::{
18 record_literal_missing_fields, record_pattern_missing_fields, BodyValidationDiagnostic,
19 },
20 unsafe_check::missing_unsafe,
21};
21 22
22pub fn validate_module_item( 23pub 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
33pub 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)]
47pub struct NoSuchField {
48 pub file: HirFileId,
49 pub field: AstPtr<ast::RecordExprField>,
50}
51
52impl 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)]
82pub 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
89impl 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)]
133pub 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
140impl 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)]
170pub struct MissingMatchArms {
171 pub file: HirFileId,
172 pub match_expr: AstPtr<ast::Expr>,
173 pub arms: AstPtr<ast::MatchArmList>,
174}
175
176impl 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)]
204pub 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
211impl 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)]
227pub struct RemoveThisSemicolon {
228 pub file: HirFileId,
229 pub expr: AstPtr<ast::Expr>,
230}
231
232impl 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)]
254pub struct BreakOutsideOfLoop {
255 pub file: HirFileId,
256 pub expr: AstPtr<ast::Expr>,
257}
258
259impl 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)]
278pub struct MissingUnsafe {
279 pub file: HirFileId,
280 pub expr: AstPtr<ast::Expr>,
281}
282
283impl 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)]
302pub struct MismatchedArgCount {
303 pub file: HirFileId,
304 pub call_expr: AstPtr<ast::Expr>,
305 pub expected: usize,
306 pub found: usize,
307}
308
309impl 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)]
385pub struct IncorrectCase { 88pub 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
394impl 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)]
426pub 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
432impl 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)]
448mod 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#"
537struct S { foo: i32, bar: () }
538impl 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
557struct MyStruct {
558 my_val: usize,
559 #[cfg(feature = "foo")]
560 bar: bool,
561}
562
563impl 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
582enum Foo {
583 #[cfg(not(feature = "foo"))]
584 Buz,
585 #[cfg(feature = "foo")]
586 Bar,
587 Baz
588}
589
590fn 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
605struct S {
606 #[cfg(feature = "foo")]
607 foo: u32,
608 #[cfg(not(feature = "foo"))]
609 bar: u32,
610}
611
612impl 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#"
644macro_rules! Type { () => { u32 }; }
645struct Foo { bar: Type![] }
646
647impl 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#"
660struct S { foo: i32, bar: () }
661fn 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"
674struct S { foo: i32, bar: () }
675fn 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"
688struct S { s: Box<u32> }
689fn 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"
700struct S { s: u32 }
701fn 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
715mod permissions;
716
717use permissions::jwt;
718
719fn 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
725pub mod jwt {
726 pub struct Claims {}
727}
728
729//- /jwt/lib.rs crate:jwt
730pub struct Claims {
731 field: u8,
732}
733 "#,
734 );
735 }
736
737 #[test]
738 fn break_outside_of_loop() {
739 check_diagnostics(
740 r#"
741fn 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::{
29use crate::{ 29use 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
35mod allow { 34mod 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
43pub(super) struct DeclValidator<'a, 'b> { 42pub(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
56impl<'a, 'b> DeclValidator<'a, 'b> { 55impl<'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)]
628mod tests {
629 use crate::diagnostics::tests::check_diagnostics;
630
631 #[test]
632 fn incorrect_function_name() {
633 check_diagnostics(
634 r#"
635fn 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#"
645fn foo(SomeParam: u8) {}
646 // ^^^^^^^^^ Parameter `SomeParam` should have snake_case name, e.g. `some_param`
647
648fn 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#"
658fn 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#"
672struct non_camel_case_name {}
673 // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName`
674
675struct 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#"
685struct AABB {}
686"#,
687 );
688 }
689
690 #[test]
691 fn incorrect_struct_field() {
692 check_diagnostics(
693 r#"
694struct 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#"
704enum some_enum { Val(u8) }
705 // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum`
706
707enum 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#"
717enum AABB {}
718"#,
719 );
720 }
721
722 #[test]
723 fn incorrect_enum_variant_name() {
724 check_diagnostics(
725 r#"
726enum 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#"
736const some_weird_const: u8 = 10;
737 // ^^^^^^^^^^^^^^^^ Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
738
739fn 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#"
752static some_weird_const: u8 = 10;
753 // ^^^^^^^^^^^^^^^^ Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
754
755fn 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#"
767struct someStruct;
768 // ^^^^^^^^^^ Structure `someStruct` should have CamelCase name, e.g. `SomeStruct`
769
770impl 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#"
787enum Option { Some, None }
788
789fn 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#"
803enum Option { Some, None }
804
805fn 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)]
821fn 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)]
833mod CheckNonstandardStyle {
834 fn HiImABadFnName() {}
835}
836
837#[allow(bad_style)]
838mod CheckBadStyle {
839 fn HiImABadFnName() {}
840}
841
842mod F {
843 #![allow(non_snake_case)]
844 fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {}
845}
846
847#[allow(non_snake_case, non_camel_case_types)]
848pub struct some_type {
849 SOME_FIELD: u8,
850 SomeField: u16,
851}
852
853#[allow(non_upper_case_globals)]
854pub const some_const: u8 = 10;
855
856#[allow(non_upper_case_globals)]
857pub 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
868mod 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#"
896trait T { fn a(); }
897struct U {}
898impl 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#"
923trait 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#"
940extern {
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#"
952fn 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 3efbce773..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};
10use hir_expand::name; 10use hir_expand::name;
11use itertools::Either;
11use rustc_hash::FxHashSet; 12use rustc_hash::FxHashSet;
12use syntax::{ast, AstPtr};
13 13
14use crate::{ 14use 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
34use super::ReplaceFilterMapNextWithFindMap; 29pub 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
55impl 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
36pub(super) struct ExprValidator<'a, 'b: 'a> { 65struct 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
42impl<'a, 'b> ExprValidator<'a, 'b> { 71impl 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
@@ -357,17 +309,20 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
357 infer: &infer, 309 infer: &infer,
358 db, 310 db,
359 pattern_arena: &pattern_arena, 311 pattern_arena: &pattern_arena,
360 eprint_panic_context: &|| { 312 panic_context: &|| {
361 use syntax::AstNode; 313 use syntax::AstNode;
362 if let Ok(scrutinee_sptr) = source_map.expr_syntax(match_expr) { 314 let match_expr_text = source_map
363 let root = scrutinee_sptr.file_syntax(db.upcast()); 315 .expr_syntax(match_expr)
364 if let Some(match_ast) = scrutinee_sptr.value.to_node(&root).syntax().parent() { 316 .ok()
365 eprintln!( 317 .and_then(|scrutinee_sptr| {
366 "Match checking is about to panic on this expression:\n{}", 318 let root = scrutinee_sptr.file_syntax(db.upcast());
367 match_ast.to_string(), 319 scrutinee_sptr.value.to_node(&root).syntax().parent()
368 ); 320 })
369 } 321 .map(|node| node.to_string());
370 } 322 format!(
323 "expression:\n{}",
324 match_expr_text.as_deref().unwrap_or("<synthesized expr>")
325 )
371 }, 326 },
372 }; 327 };
373 let report = compute_match_usefulness(&cx, &m_arms); 328 let report = compute_match_usefulness(&cx, &m_arms);
@@ -379,20 +334,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
379 // FIXME Report witnesses 334 // FIXME Report witnesses
380 // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses); 335 // eprintln!("compute_match_usefulness(..) -> {:?}", &witnesses);
381 if !witnesses.is_empty() { 336 if !witnesses.is_empty() {
382 if let Ok(source_ptr) = source_map.expr_syntax(id) { 337 self.diagnostics.push(BodyValidationDiagnostic::MissingMatchArms { match_expr: id });
383 let root = source_ptr.file_syntax(db.upcast());
384 if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
385 if let (Some(match_expr), Some(arms)) =
386 (match_expr.expr(), match_expr.match_arm_list())
387 {
388 self.sink.push(MissingMatchArms {
389 file: source_ptr.file_id,
390 match_expr: AstPtr::new(&match_expr),
391 arms: AstPtr::new(&arms),
392 })
393 }
394 }
395 }
396 } 338 }
397 } 339 }
398 340
@@ -450,24 +392,12 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
450 if params.len(&Interner) > 0 392 if params.len(&Interner) > 0
451 && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual) 393 && params.at(&Interner, 0).ty(&Interner) == Some(&mismatch.actual)
452 { 394 {
453 let (_, source_map) = db.body_with_source_map(self.owner); 395 self.diagnostics
454 396 .push(BodyValidationDiagnostic::MissingOkOrSomeInTailExpr { expr: id, required });
455 if let Ok(source_ptr) = source_map.expr_syntax(id) {
456 self.sink.push(MissingOkOrSomeInTailExpr {
457 file: source_ptr.file_id,
458 expr: source_ptr.value,
459 required,
460 });
461 }
462 } 397 }
463 } 398 }
464 399
465 fn validate_missing_tail_expr( 400 fn validate_missing_tail_expr(&mut self, body_id: ExprId, possible_tail_id: ExprId) {
466 &mut self,
467 body_id: ExprId,
468 possible_tail_id: ExprId,
469 db: &dyn HirDatabase,
470 ) {
471 let mismatch = match self.infer.type_mismatch_for_expr(body_id) { 401 let mismatch = match self.infer.type_mismatch_for_expr(body_id) {
472 Some(m) => m, 402 Some(m) => m,
473 None => return, 403 None => return,
@@ -482,12 +412,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
482 return; 412 return;
483 } 413 }
484 414
485 let (_, source_map) = db.body_with_source_map(self.owner); 415 self.diagnostics
486 416 .push(BodyValidationDiagnostic::RemoveThisSemicolon { expr: possible_tail_id });
487 if let Ok(source_ptr) = source_map.expr_syntax(possible_tail_id) {
488 self.sink
489 .push(RemoveThisSemicolon { file: source_ptr.file_id, expr: source_ptr.value });
490 }
491 } 417 }
492} 418}
493 419
@@ -565,258 +491,3 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
565 walk(pat, body, infer, &mut has_type_mismatches); 491 walk(pat, body, infer, &mut has_type_mismatches);
566 !has_type_mismatches 492 !has_type_mismatches
567} 493}
568
569#[cfg(test)]
570mod tests {
571 use crate::diagnostics::tests::check_diagnostics;
572
573 #[test]
574 fn simple_free_fn_zero() {
575 check_diagnostics(
576 r#"
577fn zero() {}
578fn f() { zero(1); }
579 //^^^^^^^ Expected 0 arguments, found 1
580"#,
581 );
582
583 check_diagnostics(
584 r#"
585fn zero() {}
586fn f() { zero(); }
587"#,
588 );
589 }
590
591 #[test]
592 fn simple_free_fn_one() {
593 check_diagnostics(
594 r#"
595fn one(arg: u8) {}
596fn f() { one(); }
597 //^^^^^ Expected 1 argument, found 0
598"#,
599 );
600
601 check_diagnostics(
602 r#"
603fn one(arg: u8) {}
604fn f() { one(1); }
605"#,
606 );
607 }
608
609 #[test]
610 fn method_as_fn() {
611 check_diagnostics(
612 r#"
613struct S;
614impl S { fn method(&self) {} }
615
616fn f() {
617 S::method();
618} //^^^^^^^^^^^ Expected 1 argument, found 0
619"#,
620 );
621
622 check_diagnostics(
623 r#"
624struct S;
625impl S { fn method(&self) {} }
626
627fn f() {
628 S::method(&S);
629 S.method();
630}
631"#,
632 );
633 }
634
635 #[test]
636 fn method_with_arg() {
637 check_diagnostics(
638 r#"
639struct S;
640impl S { fn method(&self, arg: u8) {} }
641
642 fn f() {
643 S.method();
644 } //^^^^^^^^^^ Expected 1 argument, found 0
645 "#,
646 );
647
648 check_diagnostics(
649 r#"
650struct S;
651impl S { fn method(&self, arg: u8) {} }
652
653fn f() {
654 S::method(&S, 0);
655 S.method(1);
656}
657"#,
658 );
659 }
660
661 #[test]
662 fn method_unknown_receiver() {
663 // note: this is incorrect code, so there might be errors on this in the
664 // future, but we shouldn't emit an argument count diagnostic here
665 check_diagnostics(
666 r#"
667trait Foo { fn method(&self, arg: usize) {} }
668
669fn f() {
670 let x;
671 x.method();
672}
673"#,
674 );
675 }
676
677 #[test]
678 fn tuple_struct() {
679 check_diagnostics(
680 r#"
681struct Tup(u8, u16);
682fn f() {
683 Tup(0);
684} //^^^^^^ Expected 2 arguments, found 1
685"#,
686 )
687 }
688
689 #[test]
690 fn enum_variant() {
691 check_diagnostics(
692 r#"
693enum En { Variant(u8, u16), }
694fn f() {
695 En::Variant(0);
696} //^^^^^^^^^^^^^^ Expected 2 arguments, found 1
697"#,
698 )
699 }
700
701 #[test]
702 fn enum_variant_type_macro() {
703 check_diagnostics(
704 r#"
705macro_rules! Type {
706 () => { u32 };
707}
708enum Foo {
709 Bar(Type![])
710}
711impl Foo {
712 fn new() {
713 Foo::Bar(0);
714 Foo::Bar(0, 1);
715 //^^^^^^^^^^^^^^ Expected 1 argument, found 2
716 Foo::Bar();
717 //^^^^^^^^^^ Expected 1 argument, found 0
718 }
719}
720 "#,
721 );
722 }
723
724 #[test]
725 fn varargs() {
726 check_diagnostics(
727 r#"
728extern "C" {
729 fn fixed(fixed: u8);
730 fn varargs(fixed: u8, ...);
731 fn varargs2(...);
732}
733
734fn f() {
735 unsafe {
736 fixed(0);
737 fixed(0, 1);
738 //^^^^^^^^^^^ Expected 1 argument, found 2
739 varargs(0);
740 varargs(0, 1);
741 varargs2();
742 varargs2(0);
743 varargs2(0, 1);
744 }
745}
746 "#,
747 )
748 }
749
750 #[test]
751 fn arg_count_lambda() {
752 check_diagnostics(
753 r#"
754fn main() {
755 let f = |()| ();
756 f();
757 //^^^ Expected 1 argument, found 0
758 f(());
759 f((), ());
760 //^^^^^^^^^ Expected 1 argument, found 2
761}
762"#,
763 )
764 }
765
766 #[test]
767 fn cfgd_out_call_arguments() {
768 check_diagnostics(
769 r#"
770struct C(#[cfg(FALSE)] ());
771impl C {
772 fn new() -> Self {
773 Self(
774 #[cfg(FALSE)]
775 (),
776 )
777 }
778
779 fn method(&self) {}
780}
781
782fn main() {
783 C::new().method(#[cfg(FALSE)] 0);
784}
785 "#,
786 );
787 }
788
789 #[test]
790 fn cfgd_out_fn_params() {
791 check_diagnostics(
792 r#"
793fn foo(#[cfg(NEVER)] x: ()) {}
794
795struct S;
796
797impl S {
798 fn method(#[cfg(NEVER)] self) {}
799 fn method2(#[cfg(NEVER)] self, arg: u8) {}
800 fn method3(self, #[cfg(NEVER)] arg: u8) {}
801}
802
803extern "C" {
804 fn fixed(fixed: u8, #[cfg(NEVER)] ...);
805 fn varargs(#[cfg(not(NEVER))] ...);
806}
807
808fn main() {
809 foo();
810 S::method();
811 S::method2(0);
812 S::method3(S);
813 S.method3();
814 unsafe {
815 fixed(0);
816 varargs(1, 2, 3);
817 }
818}
819 "#,
820 )
821 }
822}
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs
index a9a99f57a..a30e42699 100644
--- a/crates/hir_ty/src/diagnostics/match_check.rs
+++ b/crates/hir_ty/src/diagnostics/match_check.rs
@@ -100,10 +100,19 @@ impl<'a> PatCtxt<'a> {
100 } 100 }
101 101
102 pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat { 102 pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat {
103 // FIXME: implement pattern adjustments (implicit pattern dereference; "RFC 2005-match-ergonomics") 103 // XXX(iDawer): Collecting pattern adjustments feels imprecise to me.
104 // When lowering of & and box patterns are implemented this should be tested
105 // in a manner of `match_ergonomics_issue_9095` test.
106 // Pattern adjustment is part of RFC 2005-match-ergonomics.
104 // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089 107 // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089
105 let unadjusted_pat = self.lower_pattern_unadjusted(pat); 108 let unadjusted_pat = self.lower_pattern_unadjusted(pat);
106 unadjusted_pat 109 self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold(
110 unadjusted_pat,
111 |subpattern, ref_ty| Pat {
112 ty: ref_ty.clone(),
113 kind: Box::new(PatKind::Deref { subpattern }),
114 },
115 )
107 } 116 }
108 117
109 fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat { 118 fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat {
@@ -355,945 +364,3 @@ impl PatternFoldable for PatKind {
355 } 364 }
356 } 365 }
357} 366}
358
359#[cfg(test)]
360pub(super) mod tests {
361 mod report {
362 use std::any::Any;
363
364 use hir_def::{expr::PatId, DefWithBodyId};
365 use hir_expand::{HirFileId, InFile};
366 use syntax::SyntaxNodePtr;
367
368 use crate::{
369 db::HirDatabase,
370 diagnostics_sink::{Diagnostic, DiagnosticCode, DiagnosticSink},
371 };
372
373 /// In tests, match check bails out loudly.
374 /// This helps to catch incorrect tests that pass due to false negatives.
375 pub(crate) fn report_bail_out(
376 db: &dyn HirDatabase,
377 def: DefWithBodyId,
378 pat: PatId,
379 sink: &mut DiagnosticSink,
380 ) {
381 let (_, source_map) = db.body_with_source_map(def);
382 if let Ok(source_ptr) = source_map.pat_syntax(pat) {
383 let pat_syntax_ptr = source_ptr.value.either(Into::into, Into::into);
384 sink.push(BailedOut { file: source_ptr.file_id, pat_syntax_ptr });
385 }
386 }
387
388 #[derive(Debug)]
389 struct BailedOut {
390 file: HirFileId,
391 pat_syntax_ptr: SyntaxNodePtr,
392 }
393
394 impl Diagnostic for BailedOut {
395 fn code(&self) -> DiagnosticCode {
396 DiagnosticCode("internal:match-check-bailed-out")
397 }
398 fn message(&self) -> String {
399 format!("Internal: match check bailed out")
400 }
401 fn display_source(&self) -> InFile<SyntaxNodePtr> {
402 InFile { file_id: self.file, value: self.pat_syntax_ptr.clone() }
403 }
404 fn as_any(&self) -> &(dyn Any + Send + 'static) {
405 self
406 }
407 }
408 }
409
410 use crate::diagnostics::tests::check_diagnostics;
411
412 pub(crate) use self::report::report_bail_out;
413
414 #[test]
415 fn empty_tuple() {
416 check_diagnostics(
417 r#"
418fn main() {
419 match () { }
420 //^^ Missing match arm
421 match (()) { }
422 //^^^^ Missing match arm
423
424 match () { _ => (), }
425 match () { () => (), }
426 match (()) { (()) => (), }
427}
428"#,
429 );
430 }
431
432 #[test]
433 fn tuple_of_two_empty_tuple() {
434 check_diagnostics(
435 r#"
436fn main() {
437 match ((), ()) { }
438 //^^^^^^^^ Missing match arm
439
440 match ((), ()) { ((), ()) => (), }
441}
442"#,
443 );
444 }
445
446 #[test]
447 fn boolean() {
448 check_diagnostics(
449 r#"
450fn test_main() {
451 match false { }
452 //^^^^^ Missing match arm
453 match false { true => (), }
454 //^^^^^ Missing match arm
455 match (false, true) {}
456 //^^^^^^^^^^^^^ Missing match arm
457 match (false, true) { (true, true) => (), }
458 //^^^^^^^^^^^^^ Missing match arm
459 match (false, true) {
460 //^^^^^^^^^^^^^ Missing match arm
461 (false, true) => (),
462 (false, false) => (),
463 (true, false) => (),
464 }
465 match (false, true) { (true, _x) => (), }
466 //^^^^^^^^^^^^^ Missing match arm
467
468 match false { true => (), false => (), }
469 match (false, true) {
470 (false, _) => (),
471 (true, false) => (),
472 (_, true) => (),
473 }
474 match (false, true) {
475 (true, true) => (),
476 (true, false) => (),
477 (false, true) => (),
478 (false, false) => (),
479 }
480 match (false, true) {
481 (true, _x) => (),
482 (false, true) => (),
483 (false, false) => (),
484 }
485 match (false, true, false) {
486 (false, ..) => (),
487 (true, ..) => (),
488 }
489 match (false, true, false) {
490 (.., false) => (),
491 (.., true) => (),
492 }
493 match (false, true, false) { (..) => (), }
494}
495"#,
496 );
497 }
498
499 #[test]
500 fn tuple_of_tuple_and_bools() {
501 check_diagnostics(
502 r#"
503fn main() {
504 match (false, ((), false)) {}
505 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
506 match (false, ((), false)) { (true, ((), true)) => (), }
507 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
508 match (false, ((), false)) { (true, _) => (), }
509 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
510
511 match (false, ((), false)) {
512 (true, ((), true)) => (),
513 (true, ((), false)) => (),
514 (false, ((), true)) => (),
515 (false, ((), false)) => (),
516 }
517 match (false, ((), false)) {
518 (true, ((), true)) => (),
519 (true, ((), false)) => (),
520 (false, _) => (),
521 }
522}
523"#,
524 );
525 }
526
527 #[test]
528 fn enums() {
529 check_diagnostics(
530 r#"
531enum Either { A, B, }
532
533fn main() {
534 match Either::A { }
535 //^^^^^^^^^ Missing match arm
536 match Either::B { Either::A => (), }
537 //^^^^^^^^^ Missing match arm
538
539 match &Either::B {
540 //^^^^^^^^^^ Missing match arm
541 Either::A => (),
542 }
543
544 match Either::B {
545 Either::A => (), Either::B => (),
546 }
547 match &Either::B {
548 Either::A => (), Either::B => (),
549 }
550}
551"#,
552 );
553 }
554
555 #[test]
556 fn enum_containing_bool() {
557 check_diagnostics(
558 r#"
559enum Either { A(bool), B }
560
561fn main() {
562 match Either::B { }
563 //^^^^^^^^^ Missing match arm
564 match Either::B {
565 //^^^^^^^^^ Missing match arm
566 Either::A(true) => (), Either::B => ()
567 }
568
569 match Either::B {
570 Either::A(true) => (),
571 Either::A(false) => (),
572 Either::B => (),
573 }
574 match Either::B {
575 Either::B => (),
576 _ => (),
577 }
578 match Either::B {
579 Either::A(_) => (),
580 Either::B => (),
581 }
582
583}
584 "#,
585 );
586 }
587
588 #[test]
589 fn enum_different_sizes() {
590 check_diagnostics(
591 r#"
592enum Either { A(bool), B(bool, bool) }
593
594fn main() {
595 match Either::A(false) {
596 //^^^^^^^^^^^^^^^^ Missing match arm
597 Either::A(_) => (),
598 Either::B(false, _) => (),
599 }
600
601 match Either::A(false) {
602 Either::A(_) => (),
603 Either::B(true, _) => (),
604 Either::B(false, _) => (),
605 }
606 match Either::A(false) {
607 Either::A(true) | Either::A(false) => (),
608 Either::B(true, _) => (),
609 Either::B(false, _) => (),
610 }
611}
612"#,
613 );
614 }
615
616 #[test]
617 fn tuple_of_enum_no_diagnostic() {
618 check_diagnostics(
619 r#"
620enum Either { A(bool), B(bool, bool) }
621enum Either2 { C, D }
622
623fn main() {
624 match (Either::A(false), Either2::C) {
625 (Either::A(true), _) | (Either::A(false), _) => (),
626 (Either::B(true, _), Either2::C) => (),
627 (Either::B(false, _), Either2::C) => (),
628 (Either::B(_, _), Either2::D) => (),
629 }
630}
631"#,
632 );
633 }
634
635 #[test]
636 fn or_pattern_no_diagnostic() {
637 check_diagnostics(
638 r#"
639enum Either {A, B}
640
641fn main() {
642 match (Either::A, Either::B) {
643 (Either::A | Either::B, _) => (),
644 }
645}"#,
646 )
647 }
648
649 #[test]
650 fn mismatched_types() {
651 // Match statements with arms that don't match the
652 // expression pattern do not fire this diagnostic.
653 check_diagnostics(
654 r#"
655enum Either { A, B }
656enum Either2 { C, D }
657
658fn main() {
659 match Either::A {
660 Either2::C => (),
661 // ^^^^^^^^^^ Internal: match check bailed out
662 Either2::D => (),
663 }
664 match (true, false) {
665 (true, false, true) => (),
666 // ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out
667 (true) => (),
668 }
669 match (true, false) { (true,) => {} }
670 // ^^^^^^^ Internal: match check bailed out
671 match (0) { () => () }
672 // ^^ Internal: match check bailed out
673 match Unresolved::Bar { Unresolved::Baz => () }
674}
675 "#,
676 );
677 }
678
679 #[test]
680 fn mismatched_types_in_or_patterns() {
681 check_diagnostics(
682 r#"
683fn main() {
684 match false { true | () => {} }
685 // ^^^^^^^^^ Internal: match check bailed out
686 match (false,) { (true | (),) => {} }
687 // ^^^^^^^^^^^^ Internal: match check bailed out
688}
689"#,
690 );
691 }
692
693 #[test]
694 fn malformed_match_arm_tuple_enum_missing_pattern() {
695 // We are testing to be sure we don't panic here when the match
696 // arm `Either::B` is missing its pattern.
697 check_diagnostics(
698 r#"
699enum Either { A, B(u32) }
700
701fn main() {
702 match Either::A {
703 Either::A => (),
704 Either::B() => (),
705 }
706}
707"#,
708 );
709 }
710
711 #[test]
712 fn malformed_match_arm_extra_fields() {
713 check_diagnostics(
714 r#"
715enum A { B(isize, isize), C }
716fn main() {
717 match A::B(1, 2) {
718 A::B(_, _, _) => (),
719 // ^^^^^^^^^^^^^ Internal: match check bailed out
720 }
721 match A::B(1, 2) {
722 A::C(_) => (),
723 // ^^^^^^^ Internal: match check bailed out
724 }
725}
726"#,
727 );
728 }
729
730 #[test]
731 fn expr_diverges() {
732 check_diagnostics(
733 r#"
734enum Either { A, B }
735
736fn main() {
737 match loop {} {
738 Either::A => (),
739 // ^^^^^^^^^ Internal: match check bailed out
740 Either::B => (),
741 }
742 match loop {} {
743 Either::A => (),
744 // ^^^^^^^^^ Internal: match check bailed out
745 }
746 match loop { break Foo::A } {
747 //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
748 Either::A => (),
749 }
750 match loop { break Foo::A } {
751 Either::A => (),
752 Either::B => (),
753 }
754}
755"#,
756 );
757 }
758
759 #[test]
760 fn expr_partially_diverges() {
761 check_diagnostics(
762 r#"
763enum Either<T> { A(T), B }
764
765fn foo() -> Either<!> { Either::B }
766fn main() -> u32 {
767 match foo() {
768 Either::A(val) => val,
769 Either::B => 0,
770 }
771}
772"#,
773 );
774 }
775
776 #[test]
777 fn enum_record() {
778 check_diagnostics(
779 r#"
780enum Either { A { foo: bool }, B }
781
782fn main() {
783 let a = Either::A { foo: true };
784 match a { }
785 //^ Missing match arm
786 match a { Either::A { foo: true } => () }
787 //^ Missing match arm
788 match a {
789 Either::A { } => (),
790 //^^^^^^^^^ Missing structure fields:
791 // | - foo
792 Either::B => (),
793 }
794 match a {
795 //^ Missing match arm
796 Either::A { } => (),
797 } //^^^^^^^^^ Missing structure fields:
798 // | - foo
799
800 match a {
801 Either::A { foo: true } => (),
802 Either::A { foo: false } => (),
803 Either::B => (),
804 }
805 match a {
806 Either::A { foo: _ } => (),
807 Either::B => (),
808 }
809}
810"#,
811 );
812 }
813
814 #[test]
815 fn enum_record_fields_out_of_order() {
816 check_diagnostics(
817 r#"
818enum Either {
819 A { foo: bool, bar: () },
820 B,
821}
822
823fn main() {
824 let a = Either::A { foo: true, bar: () };
825 match a {
826 //^ Missing match arm
827 Either::A { bar: (), foo: false } => (),
828 Either::A { foo: true, bar: () } => (),
829 }
830
831 match a {
832 Either::A { bar: (), foo: false } => (),
833 Either::A { foo: true, bar: () } => (),
834 Either::B => (),
835 }
836}
837"#,
838 );
839 }
840
841 #[test]
842 fn enum_record_ellipsis() {
843 check_diagnostics(
844 r#"
845enum Either {
846 A { foo: bool, bar: bool },
847 B,
848}
849
850fn main() {
851 let a = Either::B;
852 match a {
853 //^ Missing match arm
854 Either::A { foo: true, .. } => (),
855 Either::B => (),
856 }
857 match a {
858 //^ Missing match arm
859 Either::A { .. } => (),
860 }
861
862 match a {
863 Either::A { foo: true, .. } => (),
864 Either::A { foo: false, .. } => (),
865 Either::B => (),
866 }
867
868 match a {
869 Either::A { .. } => (),
870 Either::B => (),
871 }
872}
873"#,
874 );
875 }
876
877 #[test]
878 fn enum_tuple_partial_ellipsis() {
879 check_diagnostics(
880 r#"
881enum Either {
882 A(bool, bool, bool, bool),
883 B,
884}
885
886fn main() {
887 match Either::B {
888 //^^^^^^^^^ Missing match arm
889 Either::A(true, .., true) => (),
890 Either::A(true, .., false) => (),
891 Either::A(false, .., false) => (),
892 Either::B => (),
893 }
894 match Either::B {
895 //^^^^^^^^^ Missing match arm
896 Either::A(true, .., true) => (),
897 Either::A(true, .., false) => (),
898 Either::A(.., true) => (),
899 Either::B => (),
900 }
901
902 match Either::B {
903 Either::A(true, .., true) => (),
904 Either::A(true, .., false) => (),
905 Either::A(false, .., true) => (),
906 Either::A(false, .., false) => (),
907 Either::B => (),
908 }
909 match Either::B {
910 Either::A(true, .., true) => (),
911 Either::A(true, .., false) => (),
912 Either::A(.., true) => (),
913 Either::A(.., false) => (),
914 Either::B => (),
915 }
916}
917"#,
918 );
919 }
920
921 #[test]
922 fn never() {
923 check_diagnostics(
924 r#"
925enum Never {}
926
927fn enum_(never: Never) {
928 match never {}
929}
930fn enum_ref(never: &Never) {
931 match never {}
932 //^^^^^ Missing match arm
933}
934fn bang(never: !) {
935 match never {}
936}
937"#,
938 );
939 }
940
941 #[test]
942 fn unknown_type() {
943 check_diagnostics(
944 r#"
945enum Option<T> { Some(T), None }
946
947fn main() {
948 // `Never` is deliberately not defined so that it's an uninferred type.
949 match Option::<Never>::None {
950 None => (),
951 Some(never) => match never {},
952 // ^^^^^^^^^^^ Internal: match check bailed out
953 }
954 match Option::<Never>::None {
955 //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
956 Option::Some(_never) => {},
957 }
958}
959"#,
960 );
961 }
962
963 #[test]
964 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
965 check_diagnostics(
966 r#"
967fn main() {
968 match (false, true, false) {
969 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
970 (false, ..) => (),
971 }
972}"#,
973 );
974 }
975
976 #[test]
977 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
978 check_diagnostics(
979 r#"
980fn main() {
981 match (false, true, false) {
982 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
983 (.., false) => (),
984 }
985}"#,
986 );
987 }
988
989 #[test]
990 fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() {
991 check_diagnostics(
992 r#"
993fn main() {
994 match (false, true, false) {
995 //^^^^^^^^^^^^^^^^^^^^ Missing match arm
996 (true, .., false) => (),
997 }
998}"#,
999 );
1000 }
1001
1002 #[test]
1003 fn record_struct() {
1004 check_diagnostics(
1005 r#"struct Foo { a: bool }
1006fn main(f: Foo) {
1007 match f {}
1008 //^ Missing match arm
1009 match f { Foo { a: true } => () }
1010 //^ Missing match arm
1011 match &f { Foo { a: true } => () }
1012 //^^ Missing match arm
1013 match f { Foo { a: _ } => () }
1014 match f {
1015 Foo { a: true } => (),
1016 Foo { a: false } => (),
1017 }
1018 match &f {
1019 Foo { a: true } => (),
1020 Foo { a: false } => (),
1021 }
1022}
1023"#,
1024 );
1025 }
1026
1027 #[test]
1028 fn tuple_struct() {
1029 check_diagnostics(
1030 r#"struct Foo(bool);
1031fn main(f: Foo) {
1032 match f {}
1033 //^ Missing match arm
1034 match f { Foo(true) => () }
1035 //^ Missing match arm
1036 match f {
1037 Foo(true) => (),
1038 Foo(false) => (),
1039 }
1040}
1041"#,
1042 );
1043 }
1044
1045 #[test]
1046 fn unit_struct() {
1047 check_diagnostics(
1048 r#"struct Foo;
1049fn main(f: Foo) {
1050 match f {}
1051 //^ Missing match arm
1052 match f { Foo => () }
1053}
1054"#,
1055 );
1056 }
1057
1058 #[test]
1059 fn record_struct_ellipsis() {
1060 check_diagnostics(
1061 r#"struct Foo { foo: bool, bar: bool }
1062fn main(f: Foo) {
1063 match f { Foo { foo: true, .. } => () }
1064 //^ Missing match arm
1065 match f {
1066 //^ Missing match arm
1067 Foo { foo: true, .. } => (),
1068 Foo { bar: false, .. } => ()
1069 }
1070 match f { Foo { .. } => () }
1071 match f {
1072 Foo { foo: true, .. } => (),
1073 Foo { foo: false, .. } => ()
1074 }
1075}
1076"#,
1077 );
1078 }
1079
1080 #[test]
1081 fn internal_or() {
1082 check_diagnostics(
1083 r#"
1084fn main() {
1085 enum Either { A(bool), B }
1086 match Either::B {
1087 //^^^^^^^^^ Missing match arm
1088 Either::A(true | false) => (),
1089 }
1090}
1091"#,
1092 );
1093 }
1094
1095 #[test]
1096 fn no_panic_at_unimplemented_subpattern_type() {
1097 check_diagnostics(
1098 r#"
1099struct S { a: char}
1100fn main(v: S) {
1101 match v { S{ a } => {} }
1102 match v { S{ a: _x } => {} }
1103 match v { S{ a: 'a' } => {} }
1104 //^^^^^^^^^^^ Internal: match check bailed out
1105 match v { S{..} => {} }
1106 match v { _ => {} }
1107 match v { }
1108 //^ Missing match arm
1109}
1110"#,
1111 );
1112 }
1113
1114 #[test]
1115 fn binding() {
1116 check_diagnostics(
1117 r#"
1118fn main() {
1119 match true {
1120 _x @ true => {}
1121 false => {}
1122 }
1123 match true { _x @ true => {} }
1124 //^^^^ Missing match arm
1125}
1126"#,
1127 );
1128 }
1129
1130 #[test]
1131 fn binding_ref_has_correct_type() {
1132 // Asserts `PatKind::Binding(ref _x): bool`, not &bool.
1133 // If that's not true match checking will panic with "incompatible constructors"
1134 // FIXME: make facilities to test this directly like `tests::check_infer(..)`
1135 check_diagnostics(
1136 r#"
1137enum Foo { A }
1138fn main() {
1139 // FIXME: this should not bail out but current behavior is such as the old algorithm.
1140 // ExprValidator::validate_match(..) checks types of top level patterns incorrecly.
1141 match Foo::A {
1142 ref _x => {}
1143 // ^^^^^^ Internal: match check bailed out
1144 Foo::A => {}
1145 }
1146 match (true,) {
1147 (ref _x,) => {}
1148 (true,) => {}
1149 }
1150}
1151"#,
1152 );
1153 }
1154
1155 #[test]
1156 fn enum_non_exhaustive() {
1157 check_diagnostics(
1158 r#"
1159//- /lib.rs crate:lib
1160#[non_exhaustive]
1161pub enum E { A, B }
1162fn _local() {
1163 match E::A { _ => {} }
1164 match E::A {
1165 E::A => {}
1166 E::B => {}
1167 }
1168 match E::A {
1169 E::A | E::B => {}
1170 }
1171}
1172
1173//- /main.rs crate:main deps:lib
1174use lib::E;
1175fn main() {
1176 match E::A { _ => {} }
1177 match E::A {
1178 //^^^^ Missing match arm
1179 E::A => {}
1180 E::B => {}
1181 }
1182 match E::A {
1183 //^^^^ Missing match arm
1184 E::A | E::B => {}
1185 }
1186}
1187"#,
1188 );
1189 }
1190
1191 #[test]
1192 fn match_guard() {
1193 check_diagnostics(
1194 r#"
1195fn main() {
1196 match true {
1197 true if false => {}
1198 true => {}
1199 false => {}
1200 }
1201 match true {
1202 //^^^^ Missing match arm
1203 true if false => {}
1204 false => {}
1205}
1206"#,
1207 );
1208 }
1209
1210 #[test]
1211 fn pattern_type_is_of_substitution() {
1212 cov_mark::check!(match_check_wildcard_expanded_to_substitutions);
1213 check_diagnostics(
1214 r#"
1215struct Foo<T>(T);
1216struct Bar;
1217fn main() {
1218 match Foo(Bar) {
1219 _ | Foo(Bar) => {}
1220 }
1221}
1222"#,
1223 );
1224 }
1225
1226 #[test]
1227 fn record_struct_no_such_field() {
1228 check_diagnostics(
1229 r#"
1230struct Foo { }
1231fn main(f: Foo) {
1232 match f { Foo { bar } => () }
1233 // ^^^^^^^^^^^ Internal: match check bailed out
1234}
1235"#,
1236 );
1237 }
1238
1239 mod false_negatives {
1240 //! The implementation of match checking here is a work in progress. As we roll this out, we
1241 //! prefer false negatives to false positives (ideally there would be no false positives). This
1242 //! test module should document known false negatives. Eventually we will have a complete
1243 //! implementation of match checking and this module will be empty.
1244 //!
1245 //! The reasons for documenting known false negatives:
1246 //!
1247 //! 1. It acts as a backlog of work that can be done to improve the behavior of the system.
1248 //! 2. It ensures the code doesn't panic when handling these cases.
1249 use super::*;
1250
1251 #[test]
1252 fn integers() {
1253 // We don't currently check integer exhaustiveness.
1254 check_diagnostics(
1255 r#"
1256fn main() {
1257 match 5 {
1258 10 => (),
1259 // ^^ Internal: match check bailed out
1260 11..20 => (),
1261 }
1262}
1263"#,
1264 );
1265 }
1266
1267 #[test]
1268 fn reference_patterns_at_top_level() {
1269 check_diagnostics(
1270 r#"
1271fn main() {
1272 match &false {
1273 &true => {}
1274 // ^^^^^ Internal: match check bailed out
1275 }
1276}
1277 "#,
1278 );
1279 }
1280
1281 #[test]
1282 fn reference_patterns_in_fields() {
1283 check_diagnostics(
1284 r#"
1285fn main() {
1286 match (&false,) {
1287 (true,) => {}
1288 // ^^^^^^^ Internal: match check bailed out
1289 }
1290 match (&false,) {
1291 (&true,) => {}
1292 // ^^^^^^^^ Internal: match check bailed out
1293 }
1294}
1295 "#,
1296 );
1297 }
1298 }
1299}
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 83b094a89..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//!
@@ -295,7 +295,7 @@ pub(crate) struct MatchCheckCtx<'a> {
295 pub(crate) db: &'a dyn HirDatabase, 295 pub(crate) db: &'a dyn HirDatabase,
296 /// Lowered patterns from arms plus generated by the check. 296 /// Lowered patterns from arms plus generated by the check.
297 pub(crate) pattern_arena: &'a RefCell<PatternArena>, 297 pub(crate) pattern_arena: &'a RefCell<PatternArena>,
298 pub(crate) eprint_panic_context: &'a dyn Fn(), 298 pub(crate) panic_context: &'a dyn Fn() -> String,
299} 299}
300 300
301impl<'a> MatchCheckCtx<'a> { 301impl<'a> MatchCheckCtx<'a> {
@@ -331,8 +331,7 @@ impl<'a> MatchCheckCtx<'a> {
331 331
332 #[track_caller] 332 #[track_caller]
333 pub(super) fn bug(&self, info: &str) -> ! { 333 pub(super) fn bug(&self, info: &str) -> ! {
334 (self.eprint_panic_context)(); 334 panic!("bug: {}\n{}", info, (self.panic_context)());
335 panic!("bug: {}", info);
336 } 335 }
337} 336}
338 337
@@ -646,7 +645,7 @@ impl SubPatSet {
646 (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => { 645 (Seq { subpats: s_set }, Seq { subpats: mut o_set }) => {
647 s_set.retain(|i, s_sub_set| { 646 s_set.retain(|i, s_sub_set| {
648 // Missing entries count as full. 647 // Missing entries count as full.
649 let o_sub_set = o_set.remove(&i).unwrap_or(Full); 648 let o_sub_set = o_set.remove(i).unwrap_or(Full);
650 s_sub_set.union(o_sub_set); 649 s_sub_set.union(o_sub_set);
651 // We drop full entries. 650 // We drop full entries.
652 !s_sub_set.is_full() 651 !s_sub_set.is_full()
@@ -657,7 +656,7 @@ impl SubPatSet {
657 (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => { 656 (Alt { subpats: s_set, .. }, Alt { subpats: mut o_set, .. }) => {
658 s_set.retain(|i, s_sub_set| { 657 s_set.retain(|i, s_sub_set| {
659 // Missing entries count as empty. 658 // Missing entries count as empty.
660 let o_sub_set = o_set.remove(&i).unwrap_or(Empty); 659 let o_sub_set = o_set.remove(i).unwrap_or(Empty);
661 s_sub_set.union(o_sub_set); 660 s_sub_set.union(o_sub_set);
662 // We drop empty entries. 661 // We drop empty entries.
663 !s_sub_set.is_empty() 662 !s_sub_set.is_empty()
@@ -899,7 +898,7 @@ impl Usefulness {
899 } else { 898 } else {
900 witnesses 899 witnesses
901 .into_iter() 900 .into_iter()
902 .map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns)) 901 .map(|witness| witness.apply_constructor(pcx, ctor, ctor_wild_subpatterns))
903 .collect() 902 .collect()
904 }; 903 };
905 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
4use std::sync::Arc;
5
6use hir_def::{ 4use 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
13use crate::{ 11use crate::{db::HirDatabase, InferenceResult, Interner, TyExt, TyKind};
14 db::HirDatabase, diagnostics::MissingUnsafe, diagnostics_sink::DiagnosticSink, InferenceResult,
15 Interner, TyExt, TyKind,
16};
17 12
18pub(super) struct UnsafeValidator<'a, 'b: 'a> { 13pub 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
24impl<'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
61pub(crate) struct UnsafeExpr { 31struct 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
66pub(crate) fn unsafe_expressions( 36fn 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)]
131mod tests {
132 use crate::diagnostics::tests::check_diagnostics;
133
134 #[test]
135 fn missing_unsafe_diagnostic_with_raw_ptr() {
136 check_diagnostics(
137 r#"
138fn 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#"
151struct HasUnsafe;
152
153impl HasUnsafe {
154 unsafe fn unsafe_fn(&self) {
155 let x = &5 as *const usize;
156 let y = *x;
157 }
158}
159
160unsafe fn unsafe_fn() {
161 let x = &5 as *const usize;
162 let y = *x;
163}
164
165fn 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#"
183struct Ty {
184 a: u8,
185}
186
187static mut STATIC_MUT: Ty = Ty { a: 0 };
188
189fn 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#"
204extern "rust-intrinsic" {
205 pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
206 pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
207}
208
209fn 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
17use std::{any::Any, fmt};
18
19use hir_expand::InFile;
20use syntax::SyntaxNodePtr;
21
22#[derive(Copy, Clone, Debug, PartialEq)]
23pub struct DiagnosticCode(pub &'static str);
24
25impl DiagnosticCode {
26 pub fn as_str(&self) -> &str {
27 self.0
28 }
29}
30
31pub 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
47pub 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
53impl<'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
75pub 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
80impl<'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 7a4268819..63f37c0ab 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -35,11 +35,9 @@ use stdx::impl_from;
35use syntax::SmolStr; 35use syntax::SmolStr;
36 36
37use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty}; 37use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
38use crate::diagnostics_sink::DiagnosticSink;
39use crate::{ 38use 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 {
80impl_from!(ExprId, PatId for ExprOrPatId); 78impl_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)]
85enum BindingMode { 83enum BindingMode {
86 Move, 84 Move,
@@ -111,6 +109,12 @@ pub(crate) struct InferOk {
111pub(crate) struct TypeError; 109pub(crate) struct TypeError;
112pub(crate) type InferResult = Result<InferOk, TypeError>; 110pub(crate) type InferResult = Result<InferOk, TypeError>;
113 111
112#[derive(Debug, PartialEq, Eq, Clone)]
113pub 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)]
116pub struct TypeMismatch { 120pub 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 ///
@@ -150,6 +154,8 @@ pub struct InferenceResult {
150 type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, 154 type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
151 /// Interned Unknown to return references to. 155 /// Interned Unknown to return references to.
152 standard_types: InternedStandardTypes, 156 standard_types: InternedStandardTypes,
157 /// Stores the types which were implicitly dereferenced in pattern binding modes.
158 pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
153} 159}
154 160
155impl InferenceResult { 161impl InferenceResult {
@@ -189,14 +195,6 @@ impl InferenceResult {
189 _ => None, 195 _ => None,
190 }) 196 })
191 } 197 }
192 pub fn add_diagnostics(
193 &self,
194 db: &dyn HirDatabase,
195 owner: DefWithBodyId,
196 sink: &mut DiagnosticSink,
197 ) {
198 self.diagnostics.iter().for_each(|it| it.add_to(db, owner, sink))
199 }
200} 198}
201 199
202impl Index<ExprId> for InferenceResult { 200impl Index<ExprId> for InferenceResult {
@@ -763,6 +761,38 @@ impl Expectation {
763 Expectation::RValueLikeUnsized(_) | Expectation::None => None, 761 Expectation::RValueLikeUnsized(_) | Expectation::None => None,
764 } 762 }
765 } 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 }
766} 796}
767 797
768#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 798#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
@@ -802,43 +832,3 @@ impl std::ops::BitOrAssign for Diverges {
802 *self = *self | other; 832 *self = *self | other;
803 } 833 }
804} 834}
805
806mod diagnostics {
807 use hir_def::{expr::ExprId, DefWithBodyId};
808
809 use crate::{
810 db::HirDatabase,
811 diagnostics::{BreakOutsideOfLoop, NoSuchField},
812 diagnostics_sink::DiagnosticSink,
813 };
814
815 #[derive(Debug, PartialEq, Eq, Clone)]
816 pub(super) enum InferenceDiagnostic {
817 NoSuchField { expr: ExprId },
818 BreakOutsideOfLoop { expr: ExprId },
819 }
820
821 impl InferenceDiagnostic {
822 pub(super) fn add_to(
823 &self,
824 db: &dyn HirDatabase,
825 owner: DefWithBodyId,
826 sink: &mut DiagnosticSink,
827 ) {
828 match self {
829 InferenceDiagnostic::NoSuchField { expr } => {
830 let (_, source_map) = db.body_with_source_map(owner);
831 let field = source_map.field_syntax(*expr);
832 sink.push(NoSuchField { file: field.file_id, field: field.value })
833 }
834 InferenceDiagnostic::BreakOutsideOfLoop { expr } => {
835 let (_, source_map) = db.body_with_source_map(owner);
836 let ptr = source_map
837 .expr_syntax(*expr)
838 .expect("break outside of loop in synthetic syntax");
839 sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value })
840 }
841 }
842 }
843 }
844}
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
8use chalk_ir::{cast::Cast, Mutability, TyVariableKind}; 8use chalk_ir::{cast::Cast, Mutability, TyVariableKind};
9use hir_def::{expr::ExprId, lang_item::LangItemTarget}; 9use 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 83e0a7a9e..035f4ded6 100644
--- a/crates/hir_ty/src/infer/pat.rs
+++ b/crates/hir_ty/src/infer/pat.rs
@@ -101,7 +101,9 @@ impl<'a> InferenceContext<'a> {
101 let mut expected = self.resolve_ty_shallow(expected); 101 let mut expected = self.resolve_ty_shallow(expected);
102 102
103 if is_non_ref_pat(&body, pat) { 103 if is_non_ref_pat(&body, pat) {
104 let mut pat_adjustments = Vec::new();
104 while let Some((inner, _lifetime, mutability)) = expected.as_reference() { 105 while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
106 pat_adjustments.push(expected.clone());
105 expected = self.resolve_ty_shallow(inner); 107 expected = self.resolve_ty_shallow(inner);
106 default_bm = match default_bm { 108 default_bm = match default_bm {
107 BindingMode::Move => BindingMode::Ref(mutability), 109 BindingMode::Move => BindingMode::Ref(mutability),
@@ -109,6 +111,11 @@ impl<'a> InferenceContext<'a> {
109 BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability), 111 BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability),
110 } 112 }
111 } 113 }
114
115 if !pat_adjustments.is_empty() {
116 pat_adjustments.shrink_to_fit();
117 self.result.pat_adjustments.insert(pat, pat_adjustments);
118 }
112 } else if let Pat::Ref { .. } = &body[pat] { 119 } else if let Pat::Ref { .. } = &body[pat] {
113 cov_mark::hit!(match_ergonomics_ref); 120 cov_mark::hit!(match_ergonomics_ref);
114 // When you encounter a `&pat` pattern, reset to Move. 121 // When you encounter a `&pat` pattern, reset to Move.
@@ -185,7 +192,7 @@ impl<'a> InferenceContext<'a> {
185 Pat::Path(path) => { 192 Pat::Path(path) => {
186 // FIXME use correct resolver for the surrounding expression 193 // FIXME use correct resolver for the surrounding expression
187 let resolver = self.resolver.clone(); 194 let resolver = self.resolver.clone();
188 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())
189 } 196 }
190 Pat::Bind { mode, name: _, subpat } => { 197 Pat::Bind { mode, name: _, subpat } => {
191 let mode = if mode == &BindingAnnotation::Unannotated { 198 let mode = if mode == &BindingAnnotation::Unannotated {
@@ -268,7 +275,7 @@ impl<'a> InferenceContext<'a> {
268 if !self.unify(&ty, &expected) { 275 if !self.unify(&ty, &expected) {
269 self.result 276 self.result
270 .type_mismatches 277 .type_mismatches
271 .insert(pat.into(), TypeMismatch { expected: expected, actual: ty.clone() }); 278 .insert(pat.into(), TypeMismatch { expected, actual: ty.clone() });
272 } 279 }
273 self.write_pat_ty(pat, ty.clone()); 280 self.write_pat_ty(pat, ty.clone());
274 ty 281 ty
@@ -290,6 +297,10 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
290 Expr::Literal(Literal::String(..)) => false, 297 Expr::Literal(Literal::String(..)) => false,
291 _ => true, 298 _ => true,
292 }, 299 },
300 Pat::Bind { mode: BindingAnnotation::Mutable, subpat: Some(subpat), .. }
301 | Pat::Bind { mode: BindingAnnotation::Unannotated, subpat: Some(subpat), .. } => {
302 is_non_ref_pat(body, *subpat)
303 }
293 Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false, 304 Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false,
294 } 305 }
295} 306}
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;
21mod walk; 21mod walk;
22pub mod db; 22pub mod db;
23pub mod diagnostics; 23pub mod diagnostics;
24pub mod diagnostics_sink;
25pub mod display; 24pub mod display;
26pub mod method_resolution; 25pub mod method_resolution;
27pub mod primitive; 26pub mod primitive;
@@ -50,7 +49,7 @@ use crate::{db::HirDatabase, utils::generics};
50pub use autoderef::autoderef; 49pub use autoderef::autoderef;
51pub use builder::TyBuilder; 50pub use builder::TyBuilder;
52pub use chalk_ext::*; 51pub use chalk_ext::*;
53pub use infer::{could_unify, InferenceResult}; 52pub use infer::{could_unify, InferenceDiagnostic, InferenceResult};
54pub use interner::Interner; 53pub use interner::Interner;
55pub use lower::{ 54pub 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 af6b6cda7..3d233b1e2 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -5,10 +5,10 @@
5use std::{iter, sync::Arc}; 5use std::{iter, sync::Arc};
6 6
7use arrayvec::ArrayVec; 7use arrayvec::ArrayVec;
8use base_db::CrateId; 8use base_db::{CrateId, Edition};
9use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; 9use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
10use hir_def::{ 10use 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};
14use hir_expand::name::Name; 14use 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,
@@ -639,6 +651,7 @@ fn iterate_trait_method_candidates(
639 receiver_ty: Option<&Canonical<Ty>>, 651 receiver_ty: Option<&Canonical<Ty>>,
640 callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool, 652 callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
641) -> bool { 653) -> bool {
654 let receiver_is_array = matches!(self_ty.value.kind(&Interner), chalk_ir::TyKind::Array(..));
642 // if ty is `dyn Trait`, the trait doesn't need to be in scope 655 // if ty is `dyn Trait`, the trait doesn't need to be in scope
643 let inherent_trait = 656 let inherent_trait =
644 self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); 657 self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
@@ -655,6 +668,19 @@ fn iterate_trait_method_candidates(
655 'traits: for t in traits { 668 'traits: for t in traits {
656 let data = db.trait_data(t); 669 let data = db.trait_data(t);
657 670
671 // Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during
672 // method resolution, if the receiver is an array, and we're compiling for editions before
673 // 2021.
674 // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for
675 // arrays.
676 if data.skip_array_during_method_dispatch && receiver_is_array {
677 // FIXME: this should really be using the edition of the method name's span, in case it
678 // comes from a macro
679 if db.crate_graph()[krate].edition < Edition::Edition2021 {
680 continue;
681 }
682 }
683
658 // we'll be lazy about checking whether the type implements the 684 // we'll be lazy about checking whether the type implements the
659 // trait, but if we find out it doesn't, we'll skip the rest of the 685 // trait, but if we find out it doesn't, we'll skip the rest of the
660 // iteration 686 // iteration
@@ -856,7 +882,7 @@ fn transform_receiver_ty(
856 .fill_with_unknown() 882 .fill_with_unknown()
857 .build(), 883 .build(),
858 AssocContainerId::ImplId(impl_id) => { 884 AssocContainerId::ImplId(impl_id) => {
859 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)?;
860 TyBuilder::subst_for_def(db, function_id) 886 TyBuilder::subst_for_def(db, function_id)
861 .use_parent_substs(&impl_substs) 887 .use_parent_substs(&impl_substs)
862 .fill_with_unknown() 888 .fill_with_unknown()
diff --git a/crates/hir_ty/src/test_db.rs b/crates/hir_ty/src/test_db.rs
index 381b98ba8..4640ea821 100644
--- a/crates/hir_ty/src/test_db.rs
+++ b/crates/hir_ty/src/test_db.rs
@@ -22,11 +22,19 @@ use test_utils::extract_annotations;
22 hir_def::db::DefDatabaseStorage, 22 hir_def::db::DefDatabaseStorage,
23 crate::db::HirDatabaseStorage 23 crate::db::HirDatabaseStorage
24)] 24)]
25#[derive(Default)]
26pub(crate) struct TestDB { 25pub(crate) struct TestDB {
27 storage: salsa::Storage<TestDB>, 26 storage: salsa::Storage<TestDB>,
28 events: Mutex<Option<Vec<salsa::Event>>>, 27 events: Mutex<Option<Vec<salsa::Event>>>,
29} 28}
29
30impl Default for TestDB {
31 fn default() -> Self {
32 let mut this = Self { storage: Default::default(), events: Default::default() };
33 this.set_enable_proc_attr_macros(true);
34 this
35 }
36}
37
30impl fmt::Debug for TestDB { 38impl fmt::Debug for TestDB {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 f.debug_struct("TestDB").finish() 40 f.debug_struct("TestDB").finish()
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;
9mod display_source_code; 9mod display_source_code;
10mod incremental; 10mod incremental;
11 11
12use std::{env, sync::Arc}; 12use std::{collections::HashMap, env, sync::Arc};
13 13
14use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; 14use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt};
15use expect_test::Expect; 15use 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
90fn check_no_mismatches(ra_fixture: &str) {
91 check_mismatches_impl(ra_fixture, true)
92}
93
94#[allow(unused)]
95fn check_mismatches(ra_fixture: &str) {
96 check_mismatches_impl(ra_fixture, false)
97}
98
99fn 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
89fn type_at_range(db: &TestDB, pos: FileRange) -> Ty { 185fn 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..71047703d 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -1,6 +1,6 @@
1use expect_test::expect; 1use expect_test::expect;
2 2
3use super::{check_infer, check_infer_with_mismatches, check_types}; 3use super::{check_infer, check_infer_with_mismatches, check_no_mismatches, check_types};
4 4
5#[test] 5#[test]
6fn infer_block_expr_type_mismatch() { 6fn infer_block_expr_type_mismatch() {
@@ -963,7 +963,7 @@ fn test() -> i32 {
963 963
964#[test] 964#[test]
965fn panic_macro() { 965fn panic_macro() {
966 check_infer_with_mismatches( 966 check_no_mismatches(
967 r#" 967 r#"
968mod panic { 968mod panic {
969 #[macro_export] 969 #[macro_export]
@@ -991,15 +991,34 @@ fn main() {
991 panic!() 991 panic!()
992} 992}
993 "#, 993 "#,
994 expect![[r#" 994 );
995 174..185 '{ loop {} }': ! 995}
996 176..183 'loop {}': ! 996
997 181..183 '{}': () 997#[test]
998 !0..24 '$crate...:panic': fn panic() -> ! 998fn coerce_unsize_expected_type() {
999 !0..26 '$crate...anic()': ! 999 check_no_mismatches(
1000 !0..26 '$crate...anic()': ! 1000 r#"
1001 !0..28 '$crate...015!()': ! 1001#[lang = "sized"]
1002 454..470 '{ ...c!() }': () 1002pub trait Sized {}
1003 "#]], 1003#[lang = "unsize"]
1004pub trait Unsize<T> {}
1005#[lang = "coerce_unsized"]
1006pub trait CoerceUnsized<T> {}
1007
1008impl<T: Unsize<U>, U> CoerceUnsized<&U> for &T {}
1009
1010fn main() {
1011 let foo: &[u32] = &[1, 2];
1012 let foo: &[u32] = match true {
1013 true => &[1, 2],
1014 false => &[1, 2, 3],
1015 };
1016 let foo: &[u32] = if true {
1017 &[1, 2]
1018 } else {
1019 &[1, 2, 3]
1020 };
1021}
1022 "#,
1004 ); 1023 );
1005} 1024}
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index 058eb9129..f26b2c8a7 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1349,3 +1349,52 @@ fn f() {
1349 "#, 1349 "#,
1350 ); 1350 );
1351} 1351}
1352
1353#[test]
1354fn skip_array_during_method_dispatch() {
1355 check_types(
1356 r#"
1357//- /main2018.rs crate:main2018 deps:core
1358use core::IntoIterator;
1359
1360fn f() {
1361 let v = [4].into_iter();
1362 v;
1363 //^ &i32
1364
1365 let a = [0, 1].into_iter();
1366 a;
1367 //^ &i32
1368}
1369
1370//- /main2021.rs crate:main2021 deps:core edition:2021
1371use core::IntoIterator;
1372
1373fn f() {
1374 let v = [4].into_iter();
1375 v;
1376 //^ i32
1377
1378 let a = [0, 1].into_iter();
1379 a;
1380 //^ &i32
1381}
1382
1383//- /core.rs crate:core
1384#[rustc_skip_array_during_method_dispatch]
1385pub trait IntoIterator {
1386 type Out;
1387 fn into_iter(self) -> Self::Out;
1388}
1389
1390impl<T> IntoIterator for [T; 1] {
1391 type Out = T;
1392 fn into_iter(self) -> Self::Out {}
1393}
1394impl<'a, T> IntoIterator for &'a [T] {
1395 type Out = &'a T;
1396 fn into_iter(self) -> Self::Out {}
1397}
1398 "#,
1399 );
1400}
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index cd08b5c7a..aa513c56d 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -1,6 +1,6 @@
1use expect_test::expect; 1use expect_test::expect;
2 2
3use super::{check_infer, check_infer_with_mismatches, check_types}; 3use super::{check_infer, check_infer_with_mismatches, check_mismatches, check_types};
4 4
5#[test] 5#[test]
6fn infer_pattern() { 6fn infer_pattern() {
@@ -20,6 +20,8 @@ fn infer_pattern() {
20 let h = val; 20 let h = val;
21 } 21 }
22 22
23 if let x @ true = &true {}
24
23 let lambda = |a: u64, b, c: i32| { a + b; c }; 25 let lambda = |a: u64, b, c: i32| { a + b; c };
24 26
25 let ref ref_to_x = x; 27 let ref ref_to_x = x;
@@ -30,7 +32,7 @@ fn infer_pattern() {
30 "#, 32 "#,
31 expect![[r#" 33 expect![[r#"
32 8..9 'x': &i32 34 8..9 'x': &i32
33 17..368 '{ ...o_x; }': () 35 17..400 '{ ...o_x; }': ()
34 27..28 'y': &i32 36 27..28 'y': &i32
35 31..32 'x': &i32 37 31..32 'x': &i32
36 42..44 '&z': &i32 38 42..44 '&z': &i32
@@ -59,24 +61,31 @@ fn infer_pattern() {
59 176..204 '{ ... }': () 61 176..204 '{ ... }': ()
60 190..191 'h': {unknown} 62 190..191 'h': {unknown}
61 194..197 'val': {unknown} 63 194..197 'val': {unknown}
62 214..220 'lambda': |u64, u64, i32| -> i32 64 210..236 'if let...rue {}': ()
63 223..255 '|a: u6...b; c }': |u64, u64, i32| -> i32 65 217..225 'x @ true': &bool
64 224..225 'a': u64 66 221..225 'true': bool
65 232..233 'b': u64 67 221..225 'true': bool
66 235..236 'c': i32 68 228..233 '&true': &bool
67 243..255 '{ a + b; c }': i32 69 229..233 'true': bool
68 245..246 'a': u64 70 234..236 '{}': ()
69 245..250 'a + b': u64 71 246..252 'lambda': |u64, u64, i32| -> i32
70 249..250 'b': u64 72 255..287 '|a: u6...b; c }': |u64, u64, i32| -> i32
71 252..253 'c': i32 73 256..257 'a': u64
72 266..278 'ref ref_to_x': &&i32 74 264..265 'b': u64
73 281..282 'x': &i32 75 267..268 'c': i32
74 292..301 'mut mut_x': &i32 76 275..287 '{ a + b; c }': i32
75 304..305 'x': &i32 77 277..278 'a': u64
76 315..335 'ref mu...f_to_x': &mut &i32 78 277..282 'a + b': u64
77 338..339 'x': &i32 79 281..282 'b': u64
78 349..350 'k': &mut &i32 80 284..285 'c': i32
79 353..365 'mut_ref_to_x': &mut &i32 81 298..310 'ref ref_to_x': &&i32
82 313..314 'x': &i32
83 324..333 'mut mut_x': &i32
84 336..337 'x': &i32
85 347..367 'ref mu...f_to_x': &mut &i32
86 370..371 'x': &i32
87 381..382 'k': &mut &i32
88 385..397 'mut_ref_to_x': &mut &i32
80 "#]], 89 "#]],
81 ); 90 );
82} 91}
@@ -509,47 +518,24 @@ fn infer_generics_in_patterns() {
509 518
510#[test] 519#[test]
511fn infer_const_pattern() { 520fn infer_const_pattern() {
512 check_infer_with_mismatches( 521 check_mismatches(
513 r#" 522 r#"
514 enum Option<T> { None } 523enum Option<T> { None }
515 use Option::None; 524use Option::None;
516 struct Foo; 525struct Foo;
517 const Bar: usize = 1; 526const Bar: usize = 1;
518 527
519 fn test() { 528fn test() {
520 let a: Option<u32> = None; 529 let a: Option<u32> = None;
521 let b: Option<i64> = match a { 530 let b: Option<i64> = match a {
522 None => None, 531 None => None,
523 }; 532 };
524 let _: () = match () { Foo => Foo }; // Expected mismatch 533 let _: () = match () { Foo => () };
525 let _: () = match () { Bar => Bar }; // Expected mismatch 534 // ^^^ expected (), got Foo
526 } 535 let _: () = match () { Bar => () };
536 // ^^^ expected (), got usize
537}
527 "#, 538 "#,
528 expect![[r#"
529 73..74 '1': usize
530 87..309 '{ ...atch }': ()
531 97..98 'a': Option<u32>
532 114..118 'None': Option<u32>
533 128..129 'b': Option<i64>
534 145..182 'match ... }': Option<i64>
535 151..152 'a': Option<u32>
536 163..167 'None': Option<u32>
537 171..175 'None': Option<i64>
538 192..193 '_': ()
539 200..223 'match ... Foo }': Foo
540 206..208 '()': ()
541 211..214 'Foo': Foo
542 218..221 'Foo': Foo
543 254..255 '_': ()
544 262..285 'match ... Bar }': usize
545 268..270 '()': ()
546 273..276 'Bar': usize
547 280..283 'Bar': usize
548 200..223: expected (), got Foo
549 211..214: expected (), got Foo
550 262..285: expected (), got usize
551 273..276: expected (), got usize
552 "#]],
553 ); 539 );
554} 540}
555 541
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 588f0d1d4..6bcede4c4 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3740,3 +3740,70 @@ mod future {
3740"#, 3740"#,
3741 ); 3741 );
3742} 3742}
3743
3744#[test]
3745fn local_impl_1() {
3746 check_types(
3747 r#"
3748trait Trait<T> {
3749 fn foo(&self) -> T;
3750}
3751
3752fn test() {
3753 struct S;
3754 impl Trait<u32> for S {
3755 fn foo(&self) { 0 }
3756 }
3757
3758 S.foo();
3759 // ^^^^^^^ u32
3760}
3761"#,
3762 );
3763}
3764
3765#[test]
3766fn local_impl_2() {
3767 check_types(
3768 r#"
3769struct S;
3770
3771fn test() {
3772 trait Trait<T> {
3773 fn foo(&self) -> T;
3774 }
3775 impl Trait<u32> for S {
3776 fn foo(&self) { 0 }
3777 }
3778
3779 S.foo();
3780 // ^^^^^^^ u32
3781}
3782"#,
3783 );
3784}
3785
3786#[test]
3787fn local_impl_3() {
3788 check_types(
3789 r#"
3790trait Trait<T> {
3791 fn foo(&self) -> T;
3792}
3793
3794fn test() {
3795 struct S1;
3796 {
3797 struct S2;
3798
3799 impl Trait<S1> for S2 {
3800 fn foo(&self) { S1 }
3801 }
3802
3803 S2.foo();
3804 // ^^^^^^^^ S1
3805 }
3806}
3807"#,
3808 );
3809}