aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs21
-rw-r--r--crates/ra_hir_ty/src/tests.rs49
-rw-r--r--crates/ra_hir_ty/src/tests/coercion.rs36
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs51
4 files changed, 133 insertions, 24 deletions
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 1e78f6efd..b8df27706 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -201,7 +201,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
201 } 201 }
202 Expr::Return { expr } => { 202 Expr::Return { expr } => {
203 if let Some(expr) = expr { 203 if let Some(expr) = expr {
204 self.infer_expr(*expr, &Expectation::has_type(self.return_ty.clone())); 204 self.infer_expr_coerce(*expr, &Expectation::has_type(self.return_ty.clone()));
205 } 205 }
206 Ty::simple(TypeCtor::Never) 206 Ty::simple(TypeCtor::Never)
207 } 207 }
@@ -245,7 +245,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
245 ty 245 ty
246 } 246 }
247 Expr::Field { expr, name } => { 247 Expr::Field { expr, name } => {
248 let receiver_ty = self.infer_expr(*expr, &Expectation::none()); 248 let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none());
249 let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty); 249 let canonicalized = self.canonicalizer().canonicalize_ty(receiver_ty);
250 let ty = autoderef::autoderef( 250 let ty = autoderef::autoderef(
251 self.db, 251 self.db,
@@ -280,7 +280,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
280 self.normalize_associated_types_in(ty) 280 self.normalize_associated_types_in(ty)
281 } 281 }
282 Expr::Await { expr } => { 282 Expr::Await { expr } => {
283 let inner_ty = self.infer_expr(*expr, &Expectation::none()); 283 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
284 let ty = match self.resolve_future_future_output() { 284 let ty = match self.resolve_future_future_output() {
285 Some(future_future_output_alias) => { 285 Some(future_future_output_alias) => {
286 let ty = self.table.new_type_var(); 286 let ty = self.table.new_type_var();
@@ -299,7 +299,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
299 ty 299 ty
300 } 300 }
301 Expr::Try { expr } => { 301 Expr::Try { expr } => {
302 let inner_ty = self.infer_expr(*expr, &Expectation::none()); 302 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
303 let ty = match self.resolve_ops_try_ok() { 303 let ty = match self.resolve_ops_try_ok() {
304 Some(ops_try_ok_alias) => { 304 Some(ops_try_ok_alias) => {
305 let ty = self.table.new_type_var(); 305 let ty = self.table.new_type_var();
@@ -318,7 +318,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
318 ty 318 ty
319 } 319 }
320 Expr::Cast { expr, type_ref } => { 320 Expr::Cast { expr, type_ref } => {
321 let _inner_ty = self.infer_expr(*expr, &Expectation::none()); 321 let _inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
322 let cast_ty = self.make_ty(type_ref); 322 let cast_ty = self.make_ty(type_ref);
323 // FIXME check the cast... 323 // FIXME check the cast...
324 cast_ty 324 cast_ty
@@ -334,12 +334,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
334 } else { 334 } else {
335 Expectation::none() 335 Expectation::none()
336 }; 336 };
337 // FIXME reference coercions etc. 337 let inner_ty = self.infer_expr_inner(*expr, &expectation);
338 let inner_ty = self.infer_expr(*expr, &expectation);
339 Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty) 338 Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty)
340 } 339 }
341 Expr::Box { expr } => { 340 Expr::Box { expr } => {
342 let inner_ty = self.infer_expr(*expr, &Expectation::none()); 341 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
343 if let Some(box_) = self.resolve_boxed_box() { 342 if let Some(box_) = self.resolve_boxed_box() {
344 Ty::apply_one(TypeCtor::Adt(box_), inner_ty) 343 Ty::apply_one(TypeCtor::Adt(box_), inner_ty)
345 } else { 344 } else {
@@ -347,7 +346,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
347 } 346 }
348 } 347 }
349 Expr::UnaryOp { expr, op } => { 348 Expr::UnaryOp { expr, op } => {
350 let inner_ty = self.infer_expr(*expr, &Expectation::none()); 349 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
351 match op { 350 match op {
352 UnaryOp::Deref => match self.resolver.krate() { 351 UnaryOp::Deref => match self.resolver.krate() {
353 Some(krate) => { 352 Some(krate) => {
@@ -417,7 +416,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
417 _ => Ty::Unknown, 416 _ => Ty::Unknown,
418 }, 417 },
419 Expr::Range { lhs, rhs, range_type } => { 418 Expr::Range { lhs, rhs, range_type } => {
420 let lhs_ty = lhs.map(|e| self.infer_expr(e, &Expectation::none())); 419 let lhs_ty = lhs.map(|e| self.infer_expr_inner(e, &Expectation::none()));
421 let rhs_expect = lhs_ty 420 let rhs_expect = lhs_ty
422 .as_ref() 421 .as_ref()
423 .map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone())); 422 .map_or_else(Expectation::none, |ty| Expectation::has_type(ty.clone()));
@@ -455,7 +454,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
455 } 454 }
456 } 455 }
457 Expr::Index { base, index } => { 456 Expr::Index { base, index } => {
458 let _base_ty = self.infer_expr(*base, &Expectation::none()); 457 let _base_ty = self.infer_expr_inner(*base, &Expectation::none());
459 let _index_ty = self.infer_expr(*index, &Expectation::none()); 458 let _index_ty = self.infer_expr(*index, &Expectation::none());
460 // FIXME: use `std::ops::Index::Output` to figure out the real return type 459 // FIXME: use `std::ops::Index::Output` to figure out the real return type
461 Ty::Unknown 460 Ty::Unknown
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index c520bb375..f1b67555f 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -11,8 +11,8 @@ use std::fmt::Write;
11use std::sync::Arc; 11use std::sync::Arc;
12 12
13use hir_def::{ 13use hir_def::{
14 body::BodySourceMap, db::DefDatabase, nameres::CrateDefMap, AssocItemId, DefWithBodyId, 14 body::BodySourceMap, child_from_source::ChildFromSource, db::DefDatabase, nameres::CrateDefMap,
15 LocalModuleId, Lookup, ModuleDefId, 15 AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
16}; 16};
17use hir_expand::InFile; 17use hir_expand::InFile;
18use insta::assert_snapshot; 18use insta::assert_snapshot;
@@ -31,18 +31,15 @@ use crate::{db::HirDatabase, display::HirDisplay, test_db::TestDB, InferenceResu
31fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { 31fn type_at_pos(db: &TestDB, pos: FilePosition) -> String {
32 let file = db.parse(pos.file_id).ok().unwrap(); 32 let file = db.parse(pos.file_id).ok().unwrap();
33 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); 33 let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
34 34 let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
35 let module = db.module_for_file(pos.file_id); 35 let module = db.module_for_file(pos.file_id);
36 let crate_def_map = db.crate_def_map(module.krate); 36 let func = module.child_from_source(db, InFile::new(pos.file_id.into(), fn_def)).unwrap();
37 for decl in crate_def_map[module.local_id].scope.declarations() { 37
38 if let ModuleDefId::FunctionId(func) = decl { 38 let (_body, source_map) = db.body_with_source_map(func.into());
39 let (_body, source_map) = db.body_with_source_map(func.into()); 39 if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) {
40 if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { 40 let infer = db.infer(func.into());
41 let infer = db.infer(func.into()); 41 let ty = &infer[expr_id];
42 let ty = &infer[expr_id]; 42 return ty.display(db).to_string();
43 return ty.display(db).to_string();
44 }
45 }
46 } 43 }
47 panic!("Can't find expression") 44 panic!("Can't find expression")
48} 45}
@@ -53,6 +50,10 @@ fn type_at(content: &str) -> String {
53} 50}
54 51
55fn infer(content: &str) -> String { 52fn infer(content: &str) -> String {
53 infer_with_mismatches(content, false)
54}
55
56fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
56 let (db, file_id) = TestDB::with_single_file(content); 57 let (db, file_id) = TestDB::with_single_file(content);
57 58
58 let mut acc = String::new(); 59 let mut acc = String::new();
@@ -60,6 +61,7 @@ fn infer(content: &str) -> String {
60 let mut infer_def = |inference_result: Arc<InferenceResult>, 61 let mut infer_def = |inference_result: Arc<InferenceResult>,
61 body_source_map: Arc<BodySourceMap>| { 62 body_source_map: Arc<BodySourceMap>| {
62 let mut types = Vec::new(); 63 let mut types = Vec::new();
64 let mut mismatches = Vec::new();
63 65
64 for (pat, ty) in inference_result.type_of_pat.iter() { 66 for (pat, ty) in inference_result.type_of_pat.iter() {
65 let syntax_ptr = match body_source_map.pat_syntax(pat) { 67 let syntax_ptr = match body_source_map.pat_syntax(pat) {
@@ -79,6 +81,9 @@ fn infer(content: &str) -> String {
79 None => continue, 81 None => continue,
80 }; 82 };
81 types.push((syntax_ptr, ty)); 83 types.push((syntax_ptr, ty));
84 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) {
85 mismatches.push((syntax_ptr, mismatch));
86 }
82 } 87 }
83 88
84 // sort ranges for consistency 89 // sort ranges for consistency
@@ -104,6 +109,24 @@ fn infer(content: &str) -> String {
104 ) 109 )
105 .unwrap(); 110 .unwrap();
106 } 111 }
112 if include_mismatches {
113 mismatches.sort_by_key(|(src_ptr, _)| {
114 (src_ptr.value.range().start(), src_ptr.value.range().end())
115 });
116 for (src_ptr, mismatch) in &mismatches {
117 let range = src_ptr.value.range();
118 let macro_prefix = if src_ptr.file_id != file_id.into() { "!" } else { "" };
119 write!(
120 acc,
121 "{}{}: expected {}, got {}\n",
122 macro_prefix,
123 range,
124 mismatch.expected.display(&db),
125 mismatch.actual.display(&db),
126 )
127 .unwrap();
128 }
129 }
107 }; 130 };
108 131
109 let module = db.module_for_file(file_id); 132 let module = db.module_for_file(file_id);
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs
index 1530fcc63..58b22396f 100644
--- a/crates/ra_hir_ty/src/tests/coercion.rs
+++ b/crates/ra_hir_ty/src/tests/coercion.rs
@@ -1,3 +1,4 @@
1use super::infer_with_mismatches;
1use insta::assert_snapshot; 2use insta::assert_snapshot;
2use test_utils::covers; 3use test_utils::covers;
3 4
@@ -367,3 +368,38 @@ fn test() {
367 "### 368 "###
368 ); 369 );
369} 370}
371
372#[test]
373fn coerce_autoderef() {
374 assert_snapshot!(
375 infer_with_mismatches(r#"
376struct Foo;
377fn takes_ref_foo(x: &Foo) {}
378fn test() {
379 takes_ref_foo(&Foo);
380 takes_ref_foo(&&Foo);
381 takes_ref_foo(&&&Foo);
382}
383"#, true),
384 @r###"
385 [30; 31) 'x': &Foo
386 [39; 41) '{}': ()
387 [52; 133) '{ ...oo); }': ()
388 [58; 71) 'takes_ref_foo': fn takes_ref_foo(&Foo) -> ()
389 [58; 77) 'takes_...(&Foo)': ()
390 [72; 76) '&Foo': &Foo
391 [73; 76) 'Foo': Foo
392 [83; 96) 'takes_ref_foo': fn takes_ref_foo(&Foo) -> ()
393 [83; 103) 'takes_...&&Foo)': ()
394 [97; 102) '&&Foo': &&Foo
395 [98; 102) '&Foo': &Foo
396 [99; 102) 'Foo': Foo
397 [109; 122) 'takes_ref_foo': fn takes_ref_foo(&Foo) -> ()
398 [109; 130) 'takes_...&&Foo)': ()
399 [123; 129) '&&&Foo': &&&Foo
400 [124; 129) '&&Foo': &&Foo
401 [125; 129) '&Foo': &Foo
402 [126; 129) 'Foo': Foo
403 "###
404 );
405}
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index 0d9a35ce0..9c29a054e 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -266,3 +266,54 @@ fn main() {
266 "### 266 "###
267 ); 267 );
268} 268}
269
270#[test]
271fn infer_derive_clone_simple() {
272 let (db, pos) = TestDB::with_position(
273 r#"
274//- /main.rs crate:main deps:std
275#[derive(Clone)]
276struct S;
277fn test() {
278 S.clone()<|>;
279}
280
281//- /lib.rs crate:std
282#[prelude_import]
283use clone::*;
284mod clone {
285 trait Clone {
286 fn clone(&self) -> Self;
287 }
288}
289"#,
290 );
291 assert_eq!("S", type_at_pos(&db, pos));
292}
293
294#[test]
295fn infer_derive_clone_with_params() {
296 let (db, pos) = TestDB::with_position(
297 r#"
298//- /main.rs crate:main deps:std
299#[derive(Clone)]
300struct S;
301#[derive(Clone)]
302struct Wrapper<T>(T);
303struct NonClone;
304fn test() {
305 (Wrapper(S).clone(), Wrapper(NonClone).clone())<|>;
306}
307
308//- /lib.rs crate:std
309#[prelude_import]
310use clone::*;
311mod clone {
312 trait Clone {
313 fn clone(&self) -> Self;
314 }
315}
316"#,
317 );
318 assert_eq!("(Wrapper<S>, {unknown})", type_at_pos(&db, pos));
319}