Skip to content

Commit 4992d2b

Browse files
committed
Infer range types
1 parent 8b278b1 commit 4992d2b

File tree

7 files changed

+209
-4
lines changed

7 files changed

+209
-4
lines changed

crates/ra_hir_def/src/body/lower.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use hir_expand::{
88
use ra_arena::Arena;
99
use ra_syntax::{
1010
ast::{
11-
self, ArgListOwner, ArrayExprKind, LiteralKind, LoopBodyOwner, NameOwner,
11+
self, ArgListOwner, ArrayExprKind, LiteralKind, LoopBodyOwner, NameOwner, RangeOp,
1212
TypeAscriptionOwner,
1313
},
1414
AstNode, AstPtr,
@@ -429,10 +429,28 @@ where
429429
let index = self.collect_expr_opt(e.index());
430430
self.alloc_expr(Expr::Index { base, index }, syntax_ptr)
431431
}
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+
}
432451

433452
// FIXME implement HIR for these:
434453
ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
435-
ast::Expr::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
436454
ast::Expr::MacroCall(e) => match self.expander.enter_expand(self.db, e) {
437455
Some((mark, expansion)) => {
438456
let id = self.collect_expr(expansion);

crates/ra_hir_def/src/expr.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,24 @@ pub enum Expr {
130130
rhs: ExprId,
131131
op: Option<BinaryOp>,
132132
},
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+
},
133151
Index {
134152
base: ExprId,
135153
index: ExprId,
@@ -284,15 +302,21 @@ impl Expr {
284302
Expr::Lambda { body, .. } => {
285303
f(*body);
286304
}
287-
Expr::BinaryOp { lhs, rhs, .. } => {
305+
Expr::BinaryOp { lhs, rhs, .. }
306+
| Expr::Range { lhs, rhs }
307+
| Expr::RangeInclusive { lhs, rhs } => {
288308
f(*lhs);
289309
f(*rhs);
290310
}
291311
Expr::Index { base, index } => {
292312
f(*base);
293313
f(*index);
294314
}
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, .. }
296320
| Expr::Await { expr }
297321
| Expr::Try { expr }
298322
| Expr::Cast { expr, .. }

crates/ra_hir_def/src/path.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,36 @@ pub mod known {
409409
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::OPS, name::TRY_TYPE])
410410
}
411411

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+
412442
pub fn std_result_result() -> Path {
413443
Path::from_simple_segments(PathKind::Abs, vec![name::STD, name::RESULT, name::RESULT_TYPE])
414444
}

crates/ra_hir_expand/src/name.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result");
140140
pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output");
141141
pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
142142
pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
143+
pub const RANGE_FROM_TYPE: Name = Name::new_inline_ascii(9, b"RangeFrom");
144+
pub const RANGE_FULL_TYPE: Name = Name::new_inline_ascii(9, b"RangeFull");
145+
pub const RANGE_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(14, b"RangeInclusive");
146+
pub const RANGE_TO_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(16, b"RangeToInclusive");
147+
pub const RANGE_TO_TYPE: Name = Name::new_inline_ascii(7, b"RangeTo");
148+
pub const RANGE_TYPE: Name = Name::new_inline_ascii(5, b"Range");
143149

144150
// Builtin Macros
145151
pub const FILE_MACRO: Name = Name::new_inline_ascii(4, b"file");

crates/ra_hir_ty/src/infer.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,42 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
577577
let struct_ = self.resolver.resolve_known_struct(self.db, &path)?;
578578
Some(struct_.into())
579579
}
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+
}
580616
}
581617

582618
/// The ID of a type variable.

crates/ra_hir_ty/src/infer/expr.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,47 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
415415
}
416416
_ => Ty::Unknown,
417417
},
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+
}
418459
Expr::Index { base, index } => {
419460
let _base_ty = self.infer_expr(*base, &Expectation::none());
420461
let _index_ty = self.infer_expr(*index, &Expectation::none());

crates/ra_hir_ty/src/tests.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,56 @@ mod collections {
221221
assert_eq!("&str", type_at_pos(&db, pos));
222222
}
223223

224+
#[test]
225+
fn infer_ranges() {
226+
let (db, pos) = TestDB::with_position(
227+
r#"
228+
//- /main.rs crate:main deps:std
229+
fn 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::*;
243+
mod prelude {}
244+
245+
pub 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+
224274
#[test]
225275
fn infer_while_let() {
226276
let (db, pos) = TestDB::with_position(

0 commit comments

Comments
 (0)