aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authoroxalica <[email protected]>2019-11-28 19:10:16 +0000
committeroxalica <[email protected]>2019-11-28 19:10:16 +0000
commit4992d2bf79e9da6db759eb8e1715f90f31ec7eb9 (patch)
tree9396eb224a6865aa76c64937bc88c02b6ade04b8 /crates
parent8b278b1ab660df0728508e45e88ac769a2e03a58 (diff)
Infer range types
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir_def/src/body/lower.rs22
-rw-r--r--crates/ra_hir_def/src/expr.rs28
-rw-r--r--crates/ra_hir_def/src/path.rs30
-rw-r--r--crates/ra_hir_expand/src/name.rs6
-rw-r--r--crates/ra_hir_ty/src/infer.rs36
-rw-r--r--crates/ra_hir_ty/src/infer/expr.rs41
-rw-r--r--crates/ra_hir_ty/src/tests.rs50
7 files changed, 209 insertions, 4 deletions
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 331736cb2..d18964d54 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -8,7 +8,7 @@ use hir_expand::{
8use ra_arena::Arena; 8use ra_arena::Arena;
9use ra_syntax::{ 9use ra_syntax::{
10 ast::{ 10 ast::{
11 self, ArgListOwner, ArrayExprKind, LiteralKind, LoopBodyOwner, NameOwner, 11 self, ArgListOwner, ArrayExprKind, LiteralKind, LoopBodyOwner, NameOwner, RangeOp,
12 TypeAscriptionOwner, 12 TypeAscriptionOwner,
13 }, 13 },
14 AstNode, AstPtr, 14 AstNode, AstPtr,
@@ -429,10 +429,28 @@ where
429 let index = self.collect_expr_opt(e.index()); 429 let index = self.collect_expr_opt(e.index());
430 self.alloc_expr(Expr::Index { base, index }, syntax_ptr) 430 self.alloc_expr(Expr::Index { base, index }, syntax_ptr)
431 } 431 }
432 ast::Expr::RangeExpr(e) => {
433 let lhs = e.start().map(|lhs| self.collect_expr(lhs));
434 let rhs = e.end().map(|rhs| self.collect_expr(rhs));
435 match (lhs, e.op_kind(), rhs) {
436 (None, _, None) => self.alloc_expr(Expr::RangeFull, syntax_ptr),
437 (Some(lhs), _, None) => self.alloc_expr(Expr::RangeFrom { lhs }, syntax_ptr),
438 (None, Some(RangeOp::Inclusive), Some(rhs)) => {
439 self.alloc_expr(Expr::RangeToInclusive { rhs }, syntax_ptr)
440 }
441 (Some(lhs), Some(RangeOp::Inclusive), Some(rhs)) => {
442 self.alloc_expr(Expr::RangeInclusive { lhs, rhs }, syntax_ptr)
443 }
444 // If RangeOp is missing, fallback to exclusive range.
445 (None, _, Some(rhs)) => self.alloc_expr(Expr::RangeTo { rhs }, syntax_ptr),
446 (Some(lhs), _, Some(rhs)) => {
447 self.alloc_expr(Expr::Range { lhs, rhs }, syntax_ptr)
448 }
449 }
450 }
432 451
433 // FIXME implement HIR for these: 452 // FIXME implement HIR for these:
434 ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), 453 ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
435 ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
436 ast::Expr::MacroCall(e) => match self.expander.enter_expand(self.db, e) { 454 ast::Expr::MacroCall(e) => match self.expander.enter_expand(self.db, e) {
437 Some((mark, expansion)) => { 455 Some((mark, expansion)) => {
438 let id = self.collect_expr(expansion); 456 let id = self.collect_expr(expansion);
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs
index 04c1d8f69..115090218 100644
--- a/crates/ra_hir_def/src/expr.rs
+++ b/crates/ra_hir_def/src/expr.rs
@@ -130,6 +130,24 @@ pub enum Expr {
130 rhs: ExprId, 130 rhs: ExprId,
131 op: Option<BinaryOp>, 131 op: Option<BinaryOp>,
132 }, 132 },
133 RangeFull,
134 RangeFrom {
135 lhs: ExprId,
136 },
137 RangeTo {
138 rhs: ExprId,
139 },
140 Range {
141 lhs: ExprId,
142 rhs: ExprId,
143 },
144 RangeToInclusive {
145 rhs: ExprId,
146 },
147 RangeInclusive {
148 lhs: ExprId,
149 rhs: ExprId,
150 },
133 Index { 151 Index {
134 base: ExprId, 152 base: ExprId,
135 index: ExprId, 153 index: ExprId,
@@ -284,7 +302,9 @@ impl Expr {
284 Expr::Lambda { body, .. } => { 302 Expr::Lambda { body, .. } => {
285 f(*body); 303 f(*body);
286 } 304 }
287 Expr::BinaryOp { lhs, rhs, .. } => { 305 Expr::BinaryOp { lhs, rhs, .. }
306 | Expr::Range { lhs, rhs }
307 | Expr::RangeInclusive { lhs, rhs } => {
288 f(*lhs); 308 f(*lhs);
289 f(*rhs); 309 f(*rhs);
290 } 310 }
@@ -292,7 +312,11 @@ impl Expr {
292 f(*base); 312 f(*base);
293 f(*index); 313 f(*index);
294 } 314 }
295 Expr::Field { expr, .. } 315 Expr::RangeFull => {}
316 Expr::RangeFrom { lhs: expr }
317 | Expr::RangeTo { rhs: expr }
318 | Expr::RangeToInclusive { rhs: expr }
319 | Expr::Field { expr, .. }
296 | Expr::Await { expr } 320 | Expr::Await { expr }
297 | Expr::Try { expr } 321 | Expr::Try { expr }
298 | Expr::Cast { expr, .. } 322 | Expr::Cast { expr, .. }
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index 10688df4d..ff252fe44 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -409,6 +409,36 @@ pub mod known {
409 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::TRY_TYPE]) 409 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::TRY_TYPE])
410 } 410 }
411 411
412 pub fn std_ops_range() -> Path {
413 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::RANGE_TYPE])
414 }
415
416 pub fn std_ops_range_from() -> Path {
417 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::RANGE_FROM_TYPE])
418 }
419
420 pub fn std_ops_range_full() -> Path {
421 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::RANGE_FULL_TYPE])
422 }
423
424 pub fn std_ops_range_inclusive() -> Path {
425 Path::from_simple_segments(
426 PathKind::Abs,
427 vec![name::STD, name::OPS, name::RANGE_INCLUSIVE_TYPE],
428 )
429 }
430
431 pub fn std_ops_range_to() -> Path {
432 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::RANGE_TO_TYPE])
433 }
434
435 pub fn std_ops_range_to_inclusive() -> Path {
436 Path::from_simple_segments(
437 PathKind::Abs,
438 vec![name::STD, name::OPS, name::RANGE_TO_INCLUSIVE_TYPE],
439 )
440 }
441
412 pub fn std_result_result() -> Path { 442 pub fn std_result_result() -> Path {
413 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE]) 443 Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE])
414 } 444 }
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index 7824489d7..05ba37070 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -140,6 +140,12 @@ pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result");
140pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output"); 140pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output");
141pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target"); 141pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
142pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box"); 142pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
143pub const RANGE_FROM_TYPE: Name = Name::new_inline_ascii(9, b"RangeFrom");
144pub const RANGE_FULL_TYPE: Name = Name::new_inline_ascii(9, b"RangeFull");
145pub const RANGE_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(14, b"RangeInclusive");
146pub const RANGE_TO_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(16, b"RangeToInclusive");
147pub const RANGE_TO_TYPE: Name = Name::new_inline_ascii(7, b"RangeTo");
148pub const RANGE_TYPE: Name = Name::new_inline_ascii(5, b"Range");
143 149
144// Builtin Macros 150// Builtin Macros
145pub const FILE_MACRO: Name = Name::new_inline_ascii(4, b"file"); 151pub const FILE_MACRO: Name = Name::new_inline_ascii(4, b"file");
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index f6283ab6d..fe259371f 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -577,6 +577,42 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
577 let struct_ = self.resolver.resolve_known_struct(self.db, &path)?; 577 let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
578 Some(struct_.into()) 578 Some(struct_.into())
579 } 579 }
580
581 fn resolve_range_full(&self) -> Option<AdtId> {
582 let path = known::std_ops_range_full();
583 let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
584 Some(struct_.into())
585 }
586
587 fn resolve_range(&self) -> Option<AdtId> {
588 let path = known::std_ops_range();
589 let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
590 Some(struct_.into())
591 }
592
593 fn resolve_range_inclusive(&self) -> Option<AdtId> {
594 let path = known::std_ops_range_inclusive();
595 let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
596 Some(struct_.into())
597 }
598
599 fn resolve_range_from(&self) -> Option<AdtId> {
600 let path = known::std_ops_range_from();
601 let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
602 Some(struct_.into())
603 }
604
605 fn resolve_range_to(&self) -> Option<AdtId> {
606 let path = known::std_ops_range_to();
607 let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
608 Some(struct_.into())
609 }
610
611 fn resolve_range_to_inclusive(&self) -> Option<AdtId> {
612 let path = known::std_ops_range_to_inclusive();
613 let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
614 Some(struct_.into())
615 }
580} 616}
581 617
582/// The ID of a type variable. 618/// The ID of a type variable.
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 2f9ca4bbb..a00aa426a 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -415,6 +415,47 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
415 } 415 }
416 _ => Ty::Unknown, 416 _ => Ty::Unknown,
417 }, 417 },
418 Expr::RangeFull => match self.resolve_range_full() {
419 Some(adt) => Ty::simple(TypeCtor::Adt(adt)),
420 None => Ty::Unknown,
421 },
422 Expr::Range { lhs, rhs } => {
423 let lhs_ty = self.infer_expr(*lhs, &Expectation::none());
424 let rhs_ty = self.infer_expr(*rhs, &Expectation::has_type(lhs_ty));
425 match self.resolve_range() {
426 Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), rhs_ty),
427 None => Ty::Unknown,
428 }
429 }
430 Expr::RangeInclusive { lhs, rhs } => {
431 let lhs_ty = self.infer_expr(*lhs, &Expectation::none());
432 let rhs_ty = self.infer_expr(*rhs, &Expectation::has_type(lhs_ty));
433 match self.resolve_range_inclusive() {
434 Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), rhs_ty),
435 None => Ty::Unknown,
436 }
437 }
438 Expr::RangeFrom { lhs } => {
439 let ty = self.infer_expr(*lhs, &Expectation::none());
440 match self.resolve_range_from() {
441 Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
442 None => Ty::Unknown,
443 }
444 }
445 Expr::RangeTo { rhs } => {
446 let ty = self.infer_expr(*rhs, &Expectation::none());
447 match self.resolve_range_to() {
448 Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
449 None => Ty::Unknown,
450 }
451 }
452 Expr::RangeToInclusive { rhs } => {
453 let ty = self.infer_expr(*rhs, &Expectation::none());
454 match self.resolve_range_to_inclusive() {
455 Some(adt) => Ty::apply_one(TypeCtor::Adt(adt), ty),
456 None => Ty::Unknown,
457 }
458 }
418 Expr::Index { base, index } => { 459 Expr::Index { base, index } => {
419 let _base_ty = self.infer_expr(*base, &Expectation::none()); 460 let _base_ty = self.infer_expr(*base, &Expectation::none());
420 let _index_ty = self.infer_expr(*index, &Expectation::none()); 461 let _index_ty = self.infer_expr(*index, &Expectation::none());
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index abbc1546c..4ba87e667 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -222,6 +222,56 @@ mod collections {
222} 222}
223 223
224#[test] 224#[test]
225fn infer_ranges() {
226 let (db, pos) = TestDB::with_position(
227 r#"
228//- /main.rs crate:main deps:std
229fn test() {
230 let a = ..;
231 let b = 1..;
232 let c = ..2u32;
233 let d = 1..2usize;
234 let e = ..=10;
235 let f = 'a'..='z';
236
237 let t = (a, b, c, d, e, f);
238 t<|>;
239}
240
241//- /std.rs crate:std
242#[prelude_import] use prelude::*;
243mod prelude {}
244
245pub mod ops {
246 pub struct Range<Idx> {
247 pub start: Idx,
248 pub end: Idx,
249 }
250 pub struct RangeFrom<Idx> {
251 pub start: Idx,
252 }
253 struct RangeFull;
254 pub struct RangeInclusive<Idx> {
255 start: Idx,
256 end: Idx,
257 is_empty: u8,
258 }
259 pub struct RangeTo<Idx> {
260 pub end: Idx,
261 }
262 pub struct RangeToInclusive<Idx> {
263 pub end: Idx,
264 }
265}
266"#,
267 );
268 assert_eq!(
269 "(RangeFull, RangeFrom<i32>, RangeTo<u32>, Range<usize>, RangeToInclusive<i32>, RangeInclusive<char>)",
270 type_at_pos(&db, pos),
271 );
272}
273
274#[test]
225fn infer_while_let() { 275fn infer_while_let() {
226 let (db, pos) = TestDB::with_position( 276 let (db, pos) = TestDB::with_position(
227 r#" 277 r#"