diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index f0936e9f33..5081466a2f 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -5,9 +5,12 @@ use rustc_hash::FxHashMap; use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; use ra_db::{LocalSyntaxPtr, Cancelable}; -use ra_syntax::ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner}; +use ra_syntax::{ + ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner, LiteralFlavor} +}; use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName}; +use crate::ty::primitive::{UintTy, UncertainIntTy, UncertainFloatTy}; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ExprId(RawId); @@ -103,6 +106,16 @@ impl BodySyntaxMapping { } } +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Literal { + String(String), + ByteString(Vec), + Char(char), + Bool(bool), + Int(u64, UncertainIntTy), + Float(u64, UncertainFloatTy), // FIXME: f64 is not Eq +} + #[derive(Debug, Clone, Eq, PartialEq)] pub enum Expr { /// This is produced if syntax tree does not have a required expression piece. @@ -186,6 +199,7 @@ pub enum Expr { Tuple { exprs: Vec, }, + Literal(Literal), } pub use ra_syntax::ast::PrefixOp as UnaryOp; @@ -305,6 +319,7 @@ impl Expr { f(*expr); } } + Expr::Literal(_) => {} } } } @@ -633,13 +648,50 @@ impl ExprCollector { let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect(); self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr) } + ast::ExprKind::Literal(e) => { + let child = if let Some(child) = e.literal_expr() { + child + } else { + return self.alloc_expr(Expr::Missing, syntax_ptr); + }; + + let lit = match child.flavor() { + LiteralFlavor::IntNumber { suffix } => { + let known_name = suffix + .map(|s| Name::new(s)) + .and_then(|name| UncertainIntTy::from_name(&name)); + + Literal::Int( + Default::default(), + known_name.unwrap_or(UncertainIntTy::Unknown), + ) + } + LiteralFlavor::FloatNumber { suffix } => { + let known_name = suffix + .map(|s| Name::new(s)) + .and_then(|name| UncertainFloatTy::from_name(&name)); + + Literal::Float( + Default::default(), + known_name.unwrap_or(UncertainFloatTy::Unknown), + ) + } + LiteralFlavor::ByteString => Literal::ByteString(Default::default()), + LiteralFlavor::String => Literal::String(Default::default()), + LiteralFlavor::Byte => { + Literal::Int(Default::default(), UncertainIntTy::Unsigned(UintTy::U8)) + } + LiteralFlavor::Bool => Literal::Bool(Default::default()), + LiteralFlavor::Char => Literal::Char(Default::default()), + }; + self.alloc_expr(Expr::Literal(lit), syntax_ptr) + } // TODO implement HIR for these: ast::ExprKind::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::ExprKind::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::ExprKind::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::ExprKind::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), - ast::ExprKind::Literal(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), } } diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index d9683549c6..8d786d2ac5 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs @@ -23,7 +23,7 @@ impl fmt::Debug for Name { } impl Name { - fn new(text: SmolStr) -> Name { + pub(crate) fn new(text: SmolStr) -> Name { Name { text } } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index fa46ddfe9e..5579db8fbc 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -14,7 +14,7 @@ //! rustc. mod autoderef; -mod primitive; +pub(crate) mod primitive; #[cfg(test)] mod tests; pub(crate) mod method_resolution; @@ -38,7 +38,7 @@ use crate::{ db::HirDatabase, type_ref::{TypeRef, Mutability}, name::KnownName, - expr::{Body, Expr, ExprId, PatId, UnaryOp, BinaryOp, Statement}, + expr::{Body, Expr, Literal, ExprId, PatId, UnaryOp, BinaryOp, Statement}, }; fn transpose(x: Cancelable>) -> Option> { @@ -107,13 +107,35 @@ impl UnifyValue for TypeVarValue { } } -/// The kinds of placeholders we need during type inference. Currently, we only -/// have type variables; in the future, we will probably also need int and float -/// variables, for inference of literal values (e.g. `100` could be one of +/// The kinds of placeholders we need during type inference. There's separate +/// values for general types, and for integer and float variables. The latter +/// two are used for inference of literal values (e.g. `100` could be one of /// several integer types). -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum InferTy { TypeVar(TypeVarId), + IntVar(TypeVarId), + FloatVar(TypeVarId), +} + +impl InferTy { + fn to_inner(self) -> TypeVarId { + match self { + InferTy::TypeVar(ty) | InferTy::IntVar(ty) | InferTy::FloatVar(ty) => ty, + } + } + + fn fallback_value(self) -> Ty { + match self { + InferTy::TypeVar(..) => Ty::Unknown, + InferTy::IntVar(..) => { + Ty::Int(primitive::UncertainIntTy::Signed(primitive::IntTy::I32)) + } + InferTy::FloatVar(..) => { + Ty::Float(primitive::UncertainFloatTy::Known(primitive::FloatTy::F64)) + } + } + } } /// When inferring an expression, we propagate downward whatever type hint we @@ -151,14 +173,11 @@ pub enum Ty { /// (a non-surrogate code point). Written as `char`. Char, - /// A primitive signed integer type. For example, `i32`. - Int(primitive::IntTy), - - /// A primitive unsigned integer type. For example, `u32`. - Uint(primitive::UintTy), + /// A primitive integer type. For example, `i32`. + Int(primitive::UncertainIntTy), /// A primitive floating-point type. For example, `f64`. - Float(primitive::FloatTy), + Float(primitive::UncertainFloatTy), /// Structures, enumerations and unions. Adt { @@ -198,8 +217,9 @@ pub enum Ty { // above function pointer type. Once we implement generics, we will probably // need this as well. - // A trait, defined with `dyn trait`. + // A trait, defined with `dyn Trait`. // Dynamic(), + // The anonymous type of a closure. Used to represent the type of // `|a| a`. // Closure(DefId, ClosureSubsts<'tcx>), @@ -312,20 +332,19 @@ impl Ty { path: &Path, ) -> Cancelable { if let Some(name) = path.as_ident() { - if let Some(KnownName::Bool) = name.as_known_name() { - return Ok(Ty::Bool); - } else if let Some(KnownName::Char) = name.as_known_name() { - return Ok(Ty::Char); - } else if let Some(KnownName::Str) = name.as_known_name() { - return Ok(Ty::Str); - } else if let Some(int_ty) = primitive::IntTy::from_name(name) { + if let Some(int_ty) = primitive::UncertainIntTy::from_name(name) { return Ok(Ty::Int(int_ty)); - } else if let Some(uint_ty) = primitive::UintTy::from_name(name) { - return Ok(Ty::Uint(uint_ty)); - } else if let Some(float_ty) = primitive::FloatTy::from_name(name) { + } else if let Some(float_ty) = primitive::UncertainFloatTy::from_name(name) { return Ok(Ty::Float(float_ty)); } else if name.as_known_name() == Some(KnownName::SelfType) { return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type())); + } else if let Some(known) = name.as_known_name() { + match known { + KnownName::Bool => return Ok(Ty::Bool), + KnownName::Char => return Ok(Ty::Char), + KnownName::Str => return Ok(Ty::Str), + _ => {} + } } } @@ -392,7 +411,6 @@ impl fmt::Display for Ty { Ty::Bool => write!(f, "bool"), Ty::Char => write!(f, "char"), Ty::Int(t) => write!(f, "{}", t.ty_to_string()), - Ty::Uint(t) => write!(f, "{}", t.ty_to_string()), Ty::Float(t) => write!(f, "{}", t.ty_to_string()), Ty::Str => write!(f, "str"), Ty::Slice(t) => write!(f, "[{}]", t), @@ -587,7 +605,7 @@ fn binary_op_return_ty(op: BinaryOp, rhs_ty: Ty) -> Ty { | BinaryOp::BitwiseAnd | BinaryOp::BitwiseOr | BinaryOp::BitwiseXor => match rhs_ty { - Ty::Uint(..) | Ty::Int(..) | Ty::Float(..) => rhs_ty, + Ty::Int(..) | Ty::Float(..) => rhs_ty, _ => Ty::Unknown, }, BinaryOp::RangeRightOpen | BinaryOp::RangeRightClosed => Ty::Unknown, @@ -598,7 +616,7 @@ fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { match op { BinaryOp::BooleanAnd | BinaryOp::BooleanOr => Ty::Bool, BinaryOp::Assignment | BinaryOp::EqualityTest => match lhs_ty { - Ty::Uint(..) | Ty::Int(..) | Ty::Float(..) | Ty::Str | Ty::Char | Ty::Bool => lhs_ty, + Ty::Int(..) | Ty::Float(..) | Ty::Str | Ty::Char | Ty::Bool => lhs_ty, _ => Ty::Unknown, }, BinaryOp::LesserEqualTest @@ -625,7 +643,7 @@ fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty { | BinaryOp::BitwiseAnd | BinaryOp::BitwiseOr | BinaryOp::BitwiseXor => match lhs_ty { - Ty::Uint(..) | Ty::Int(..) | Ty::Float(..) => lhs_ty, + Ty::Int(..) | Ty::Float(..) => lhs_ty, _ => Ty::Unknown, }, _ => Ty::Unknown, @@ -695,13 +713,17 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { match (&*ty1, &*ty2) { (Ty::Unknown, ..) => true, (.., Ty::Unknown) => true, - (Ty::Bool, _) - | (Ty::Str, _) - | (Ty::Never, _) - | (Ty::Char, _) - | (Ty::Int(..), Ty::Int(..)) - | (Ty::Uint(..), Ty::Uint(..)) - | (Ty::Float(..), Ty::Float(..)) => ty1 == ty2, + (Ty::Int(t1), Ty::Int(t2)) => match (t1, t2) { + (primitive::UncertainIntTy::Unknown, _) + | (_, primitive::UncertainIntTy::Unknown) => true, + _ => t1 == t2, + }, + (Ty::Float(t1), Ty::Float(t2)) => match (t1, t2) { + (primitive::UncertainFloatTy::Unknown, _) + | (_, primitive::UncertainFloatTy::Unknown) => true, + _ => t1 == t2, + }, + (Ty::Bool, _) | (Ty::Str, _) | (Ty::Never, _) | (Ty::Char, _) => ty1 == ty2, ( Ty::Adt { def_id: def_id1, .. @@ -718,12 +740,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { .iter() .zip(ts2.iter()) .all(|(t1, t2)| self.unify(t1, t2)), - (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) => { + (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) + | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2))) + | (Ty::Infer(InferTy::FloatVar(tv1)), Ty::Infer(InferTy::FloatVar(tv2))) => { // both type vars are unknown since we tried to resolve them self.var_unification_table.union(*tv1, *tv2); true } - (Ty::Infer(InferTy::TypeVar(tv)), other) | (other, Ty::Infer(InferTy::TypeVar(tv))) => { + (Ty::Infer(InferTy::TypeVar(tv)), other) + | (other, Ty::Infer(InferTy::TypeVar(tv))) + | (Ty::Infer(InferTy::IntVar(tv)), other) + | (other, Ty::Infer(InferTy::IntVar(tv))) + | (Ty::Infer(InferTy::FloatVar(tv)), other) + | (other, Ty::Infer(InferTy::FloatVar(tv))) => { // the type var is unknown since we tried to resolve it self.var_unification_table .union_value(*tv, TypeVarValue::Known(other.clone())); @@ -739,10 +768,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { )) } + fn new_integer_var(&mut self) -> Ty { + Ty::Infer(InferTy::IntVar( + self.var_unification_table.new_key(TypeVarValue::Unknown), + )) + } + + fn new_float_var(&mut self) -> Ty { + Ty::Infer(InferTy::FloatVar( + self.var_unification_table.new_key(TypeVarValue::Unknown), + )) + } + /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it. fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { match ty { Ty::Unknown => self.new_type_var(), + Ty::Int(primitive::UncertainIntTy::Unknown) => self.new_integer_var(), + Ty::Float(primitive::UncertainFloatTy::Unknown) => self.new_float_var(), _ => ty, } } @@ -757,12 +800,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { /// known type. fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty { ty.fold(&mut |ty| match ty { - Ty::Infer(InferTy::TypeVar(tv)) => { - if let Some(known_ty) = self.var_unification_table.probe_value(tv).known() { + Ty::Infer(tv) => { + let inner = tv.to_inner(); + if let Some(known_ty) = self.var_unification_table.probe_value(inner).known() { // known_ty may contain other variables that are known by now self.resolve_ty_as_possible(known_ty.clone()) } else { - Ty::Infer(InferTy::TypeVar(tv)) + ty } } _ => ty, @@ -773,8 +817,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { /// otherwise, return ty. fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { match ty { - Ty::Infer(InferTy::TypeVar(tv)) => { - match self.var_unification_table.probe_value(*tv).known() { + Ty::Infer(tv) => { + let inner = tv.to_inner(); + match self.var_unification_table.probe_value(inner).known() { Some(known_ty) => { // The known_ty can't be a type var itself Cow::Owned(known_ty.clone()) @@ -790,12 +835,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { /// replaced by Ty::Unknown. fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { ty.fold(&mut |ty| match ty { - Ty::Infer(InferTy::TypeVar(tv)) => { - if let Some(known_ty) = self.var_unification_table.probe_value(tv).known() { + Ty::Infer(tv) => { + let inner = tv.to_inner(); + if let Some(known_ty) = self.var_unification_table.probe_value(inner).known() { // known_ty may contain other variables that are known by now self.resolve_ty_completely(known_ty.clone()) } else { - Ty::Unknown + tv.fallback_value() } } _ => ty, @@ -1067,6 +1113,20 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { Ty::Tuple(Arc::from(ty_vec)) } + Expr::Literal(lit) => match lit { + Literal::Bool(..) => Ty::Bool, + Literal::String(..) => Ty::Ref(Arc::new(Ty::Str), Mutability::Shared), + Literal::ByteString(..) => { + let byte_type = Arc::new(Ty::Int(primitive::UncertainIntTy::Unsigned( + primitive::UintTy::U8, + ))); + let slice_type = Arc::new(Ty::Slice(byte_type)); + Ty::Ref(slice_type, Mutability::Shared) + } + Literal::Char(..) => Ty::Char, + Literal::Int(_v, ty) => Ty::Int(*ty), + Literal::Float(_v, ty) => Ty::Float(*ty), + }, }; // use a new type variable if we got Ty::Unknown here let ty = self.insert_type_vars_shallow(ty); diff --git a/crates/ra_hir/src/ty/primitive.rs b/crates/ra_hir/src/ty/primitive.rs index 498d42d52e..5741ca90d3 100644 --- a/crates/ra_hir/src/ty/primitive.rs +++ b/crates/ra_hir/src/ty/primitive.rs @@ -2,6 +2,56 @@ use std::fmt; use crate::{Name, KnownName}; +#[derive(Debug, Clone, Eq, PartialEq, Hash, Copy)] +pub enum UncertainIntTy { + Unknown, + Unsigned(UintTy), + Signed(IntTy), +} + +impl UncertainIntTy { + pub fn ty_to_string(&self) -> &'static str { + match *self { + UncertainIntTy::Unknown => "{integer}", + UncertainIntTy::Signed(ty) => ty.ty_to_string(), + UncertainIntTy::Unsigned(ty) => ty.ty_to_string(), + } + } + + pub fn from_name(name: &Name) -> Option { + if let Some(ty) = IntTy::from_name(name) { + Some(UncertainIntTy::Signed(ty)) + } else if let Some(ty) = UintTy::from_name(name) { + Some(UncertainIntTy::Unsigned(ty)) + } else { + None + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Copy)] +pub enum UncertainFloatTy { + Unknown, + Known(FloatTy), +} + +impl UncertainFloatTy { + pub fn ty_to_string(&self) -> &'static str { + match *self { + UncertainFloatTy::Unknown => "{float}", + UncertainFloatTy::Known(ty) => ty.ty_to_string(), + } + } + + pub fn from_name(name: &Name) -> Option { + if let Some(ty) = FloatTy::from_name(name) { + Some(UncertainFloatTy::Known(ty)) + } else { + None + } + } +} + #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] pub enum IntTy { Isize, diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 920188fc91..8aacb1a7fd 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -132,6 +132,32 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) { ); } +#[test] +fn infer_literals() { + check_inference( + r##" +fn test() { + 5i32; + "hello"; + b"bytes"; + 'c'; + b'b'; + 3.14; + 5000; + false; + true; + r#" + //! doc + // non-doc + mod foo {} + "#; + br#"yolo"#; +} +"##, + "literals.txt", + ); +} + #[test] fn infer_backwards() { check_inference( @@ -180,7 +206,7 @@ fn f(x: bool) -> i32 { 0i32 } -fn test() { +fn test() -> bool { let x = a && b; let y = true || false; let z = x == y; @@ -277,8 +303,6 @@ fn test(x: &str, y: isize) { let b = (a, x); let c = (y, x); let d = (c, x); - - // we have not infered these case yet. let e = (1, "e"); let f = (e, "d"); } diff --git a/crates/ra_hir/src/ty/tests/data/basics.txt b/crates/ra_hir/src/ty/tests/data/basics.txt index ac7faae0ac..e65fe07aa4 100644 --- a/crates/ra_hir/src/ty/tests/data/basics.txt +++ b/crates/ra_hir/src/ty/tests/data/basics.txt @@ -7,7 +7,7 @@ [55; 56) 'b': isize [62; 63) 'c': ! [69; 70) 'd': &str -[76; 82) '1usize': [unknown] -[88; 94) '1isize': [unknown] -[100; 106) '"test"': [unknown] -[112; 118) '1.0f32': [unknown] +[76; 82) '1usize': usize +[88; 94) '1isize': isize +[100; 106) '"test"': &str +[112; 118) '1.0f32': f32 diff --git a/crates/ra_hir/src/ty/tests/data/binary_op.txt b/crates/ra_hir/src/ty/tests/data/binary_op.txt index 8a515ac5e4..58a7276916 100644 --- a/crates/ra_hir/src/ty/tests/data/binary_op.txt +++ b/crates/ra_hir/src/ty/tests/data/binary_op.txt @@ -1,46 +1,46 @@ [6; 7) 'x': bool [22; 34) '{ 0i32 }': i32 [28; 32) '0i32': i32 -[46; 342) '{ ... < 3 }': bool -[56; 57) 'x': bool -[60; 61) 'a': bool -[60; 66) 'a && b': bool -[65; 66) 'b': bool -[76; 77) 'y': bool -[80; 84) 'true': bool -[80; 93) 'true || false': bool -[88; 93) 'false': bool -[103; 104) 'z': bool -[107; 108) 'x': bool -[107; 113) 'x == y': bool -[112; 113) 'y': bool -[123; 134) 'minus_forty': isize -[144; 152) '-40isize': isize -[145; 152) '40isize': [unknown] -[162; 163) 'h': bool -[166; 177) 'minus_forty': isize -[166; 188) 'minus_...ONST_2': bool -[181; 188) 'CONST_2': isize -[198; 199) 'c': i32 -[202; 203) 'f': fn(bool) -> i32 -[202; 211) 'f(z || y)': i32 -[202; 215) 'f(z || y) + 5': i32 -[204; 205) 'z': bool -[204; 210) 'z || y': bool -[209; 210) 'y': bool -[214; 215) '5': i32 -[225; 226) 'd': [unknown] -[229; 230) 'b': [unknown] -[240; 241) 'g': () -[244; 255) 'minus_forty': isize -[244; 260) 'minus_...y ^= i': () -[259; 260) 'i': isize -[270; 273) 'ten': usize -[283; 285) '10': usize -[295; 308) 'ten_is_eleven': bool -[311; 314) 'ten': usize -[311; 326) 'ten == some_num': bool -[318; 326) 'some_num': usize -[333; 336) 'ten': usize -[333; 340) 'ten < 3': bool -[339; 340) '3': usize +[54; 350) '{ ... < 3 }': bool +[64; 65) 'x': bool +[68; 69) 'a': bool +[68; 74) 'a && b': bool +[73; 74) 'b': bool +[84; 85) 'y': bool +[88; 92) 'true': bool +[88; 101) 'true || false': bool +[96; 101) 'false': bool +[111; 112) 'z': bool +[115; 116) 'x': bool +[115; 121) 'x == y': bool +[120; 121) 'y': bool +[131; 142) 'minus_forty': isize +[152; 160) '-40isize': isize +[153; 160) '40isize': isize +[170; 171) 'h': bool +[174; 185) 'minus_forty': isize +[174; 196) 'minus_...ONST_2': bool +[189; 196) 'CONST_2': isize +[206; 207) 'c': i32 +[210; 211) 'f': fn(bool) -> i32 +[210; 219) 'f(z || y)': i32 +[210; 223) 'f(z || y) + 5': i32 +[212; 213) 'z': bool +[212; 218) 'z || y': bool +[217; 218) 'y': bool +[222; 223) '5': i32 +[233; 234) 'd': [unknown] +[237; 238) 'b': [unknown] +[248; 249) 'g': () +[252; 263) 'minus_forty': isize +[252; 268) 'minus_...y ^= i': () +[267; 268) 'i': isize +[278; 281) 'ten': usize +[291; 293) '10': usize +[303; 316) 'ten_is_eleven': bool +[319; 322) 'ten': usize +[319; 334) 'ten == some_num': bool +[326; 334) 'some_num': usize +[341; 344) 'ten': usize +[341; 348) 'ten < 3': bool +[347; 348) '3': usize diff --git a/crates/ra_hir/src/ty/tests/data/let.txt b/crates/ra_hir/src/ty/tests/data/let.txt index 30f4a2cf52..8815dba411 100644 --- a/crates/ra_hir/src/ty/tests/data/let.txt +++ b/crates/ra_hir/src/ty/tests/data/let.txt @@ -1,6 +1,6 @@ [11; 71) '{ ...= b; }': () -[21; 22) 'a': [unknown] -[25; 31) '1isize': [unknown] +[21; 22) 'a': isize +[25; 31) '1isize': isize [41; 42) 'b': usize [52; 53) '1': usize [63; 64) 'c': usize diff --git a/crates/ra_hir/src/ty/tests/data/literals.txt b/crates/ra_hir/src/ty/tests/data/literals.txt new file mode 100644 index 0000000000..84ee2c11b9 --- /dev/null +++ b/crates/ra_hir/src/ty/tests/data/literals.txt @@ -0,0 +1,12 @@ +[11; 201) '{ ...o"#; }': () +[17; 21) '5i32': i32 +[27; 34) '"hello"': &str +[40; 48) 'b"bytes"': &[u8] +[54; 57) ''c'': char +[63; 67) 'b'b'': u8 +[73; 77) '3.14': f64 +[83; 87) '5000': i32 +[93; 98) 'false': bool +[104; 108) 'true': bool +[114; 182) 'r#" ... "#': &str +[188; 198) 'br#"yolo"#': &[u8] diff --git a/crates/ra_hir/src/ty/tests/data/struct.txt b/crates/ra_hir/src/ty/tests/data/struct.txt index 7b324c82fa..be9e12d024 100644 --- a/crates/ra_hir/src/ty/tests/data/struct.txt +++ b/crates/ra_hir/src/ty/tests/data/struct.txt @@ -2,14 +2,14 @@ [82; 83) 'c': [unknown] [86; 87) 'C': [unknown] [86; 90) 'C(1)': [unknown] -[88; 89) '1': [unknown] +[88; 89) '1': i32 [96; 97) 'B': [unknown] [107; 108) 'a': A [114; 133) 'A { b:...C(1) }': A [121; 122) 'B': B [127; 128) 'C': [unknown] [127; 131) 'C(1)': C -[129; 130) '1': [unknown] +[129; 130) '1': i32 [139; 140) 'a': A [139; 142) 'a.b': B [148; 149) 'a': A diff --git a/crates/ra_hir/src/ty/tests/data/tuple.txt b/crates/ra_hir/src/ty/tests/data/tuple.txt index 96169180d4..a95d3c286e 100644 --- a/crates/ra_hir/src/ty/tests/data/tuple.txt +++ b/crates/ra_hir/src/ty/tests/data/tuple.txt @@ -1,6 +1,6 @@ [9; 10) 'x': &str [18; 19) 'y': isize -[28; 214) '{ ...d"); }': () +[28; 170) '{ ...d"); }': () [38; 39) 'a': (u32, &str) [55; 63) '(1, "a")': (u32, &str) [56; 57) '1': u32 @@ -17,11 +17,11 @@ [117; 123) '(c, x)': ((isize, &str), &str) [118; 119) 'c': (isize, &str) [121; 122) 'x': &str -[177; 178) 'e': ([unknown], [unknown]) -[181; 189) '(1, "e")': ([unknown], [unknown]) -[182; 183) '1': [unknown] -[185; 188) '"e"': [unknown] -[199; 200) 'f': (([unknown], [unknown]), [unknown]) -[203; 211) '(e, "d")': (([unknown], [unknown]), [unknown]) -[204; 205) 'e': ([unknown], [unknown]) -[207; 210) '"d"': [unknown] +[133; 134) 'e': (i32, &str) +[137; 145) '(1, "e")': (i32, &str) +[138; 139) '1': i32 +[141; 144) '"e"': &str +[155; 156) 'f': ((i32, &str), &str) +[159; 167) '(e, "d")': ((i32, &str), &str) +[160; 161) 'e': (i32, &str) +[163; 166) '"d"': &str diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index d73c5bc314..107b238338 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs @@ -230,20 +230,19 @@ mod tests { assert_eq!("[unknown]", &type_name); } - // FIXME: improve type_of to make this work #[test] fn test_type_of_for_expr_2() { let (analysis, range) = single_file_with_range( " fn main() { let foo: usize = 1; - let bar = <|>1 + foo_test<|>; + let bar = <|>1 + foo<|>; } ", ); let type_name = analysis.type_of(range).unwrap().unwrap(); - assert_eq!("[unknown]", &type_name); + assert_eq!("usize", &type_name); } } diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 8bf439b60f..211ba31e57 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -609,6 +609,52 @@ impl SelfParam { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum LiteralFlavor { + String, + ByteString, + Char, + Byte, + IntNumber { suffix: Option }, + FloatNumber { suffix: Option }, + Bool, +} + +impl LiteralExpr { + pub fn flavor(&self) -> LiteralFlavor { + let syntax = self.syntax(); + match syntax.kind() { + INT_NUMBER => { + let allowed_suffix_list = [ + "isize", "i128", "i64", "i32", "i16", "i8", "usize", "u128", "u64", "u32", + "u16", "u8", + ]; + let text = syntax.text().to_string(); + let suffix = allowed_suffix_list + .iter() + .find(|&s| text.ends_with(s)) + .map(|&suf| SmolStr::new(suf)); + LiteralFlavor::IntNumber { suffix: suffix } + } + FLOAT_NUMBER => { + let allowed_suffix_list = ["f64", "f32"]; + let text = syntax.text().to_string(); + let suffix = allowed_suffix_list + .iter() + .find(|&s| text.ends_with(s)) + .map(|&suf| SmolStr::new(suf)); + LiteralFlavor::FloatNumber { suffix: suffix } + } + STRING | RAW_STRING => LiteralFlavor::String, + TRUE_KW | FALSE_KW => LiteralFlavor::Bool, + BYTE_STRING | RAW_BYTE_STRING => LiteralFlavor::ByteString, + CHAR => LiteralFlavor::Char, + BYTE => LiteralFlavor::Byte, + _ => unreachable!(), + } + } +} + #[test] fn test_doc_comment_of_items() { let file = SourceFile::parse( diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 94842a5143..3471d52264 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -793,6 +793,31 @@ impl AstNode for ExternCrateItem { impl ExternCrateItem {} +// FalseKw +#[derive(Debug, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct FalseKw { + pub(crate) syntax: SyntaxNode, +} +unsafe impl TransparentNewType for FalseKw { + type Repr = rowan::SyntaxNode; +} + +impl AstNode for FalseKw { + fn cast(syntax: &SyntaxNode) -> Option<&Self> { + match syntax.kind() { + FALSE_KW => Some(FalseKw::from_repr(syntax.into_repr())), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } + fn to_owned(&self) -> TreeArc { TreeArc::cast(self.syntax.to_owned()) } +} + + +impl ast::AstToken for FalseKw {} +impl FalseKw {} + // FieldExpr #[derive(Debug, PartialEq, Eq, Hash)] #[repr(transparent)] @@ -849,6 +874,31 @@ impl AstNode for FieldPatList { impl FieldPatList {} +// FloatNumber +#[derive(Debug, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct FloatNumber { + pub(crate) syntax: SyntaxNode, +} +unsafe impl TransparentNewType for FloatNumber { + type Repr = rowan::SyntaxNode; +} + +impl AstNode for FloatNumber { + fn cast(syntax: &SyntaxNode) -> Option<&Self> { + match syntax.kind() { + FLOAT_NUMBER => Some(FloatNumber::from_repr(syntax.into_repr())), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } + fn to_owned(&self) -> TreeArc { TreeArc::cast(self.syntax.to_owned()) } +} + + +impl ast::AstToken for FloatNumber {} +impl FloatNumber {} + // FnDef #[derive(Debug, PartialEq, Eq, Hash)] #[repr(transparent)] @@ -1130,6 +1180,31 @@ impl AstNode for IndexExpr { impl IndexExpr {} +// IntNumber +#[derive(Debug, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct IntNumber { + pub(crate) syntax: SyntaxNode, +} +unsafe impl TransparentNewType for IntNumber { + type Repr = rowan::SyntaxNode; +} + +impl AstNode for IntNumber { + fn cast(syntax: &SyntaxNode) -> Option<&Self> { + match syntax.kind() { + INT_NUMBER => Some(IntNumber::from_repr(syntax.into_repr())), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } + fn to_owned(&self) -> TreeArc { TreeArc::cast(self.syntax.to_owned()) } +} + + +impl ast::AstToken for IntNumber {} +impl IntNumber {} + // ItemList #[derive(Debug, PartialEq, Eq, Hash)] #[repr(transparent)] @@ -1327,7 +1402,75 @@ impl AstNode for Literal { } -impl Literal {} +impl Literal { + pub fn literal_expr(&self) -> Option<&LiteralExpr> { + super::child_opt(self) + } +} + +// LiteralExpr +#[derive(Debug, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct LiteralExpr { + pub(crate) syntax: SyntaxNode, +} +unsafe impl TransparentNewType for LiteralExpr { + type Repr = rowan::SyntaxNode; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LiteralExprKind<'a> { + String(&'a String), + ByteString(&'a ByteString), + RawString(&'a RawString), + RawByteString(&'a RawByteString), + Char(&'a Char), + Byte(&'a Byte), + IntNumber(&'a IntNumber), + FloatNumber(&'a FloatNumber), + TrueKw(&'a TrueKw), + FalseKw(&'a FalseKw), +} + +impl AstNode for LiteralExpr { + fn cast(syntax: &SyntaxNode) -> Option<&Self> { + match syntax.kind() { + | STRING + | BYTE_STRING + | RAW_STRING + | RAW_BYTE_STRING + | CHAR + | BYTE + | INT_NUMBER + | FLOAT_NUMBER + | TRUE_KW + | FALSE_KW => Some(LiteralExpr::from_repr(syntax.into_repr())), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } + fn to_owned(&self) -> TreeArc { TreeArc::cast(self.syntax.to_owned()) } +} + +impl LiteralExpr { + pub fn kind(&self) -> LiteralExprKind { + match self.syntax.kind() { + STRING => LiteralExprKind::String(String::cast(&self.syntax).unwrap()), + BYTE_STRING => LiteralExprKind::ByteString(ByteString::cast(&self.syntax).unwrap()), + RAW_STRING => LiteralExprKind::RawString(RawString::cast(&self.syntax).unwrap()), + RAW_BYTE_STRING => LiteralExprKind::RawByteString(RawByteString::cast(&self.syntax).unwrap()), + CHAR => LiteralExprKind::Char(Char::cast(&self.syntax).unwrap()), + BYTE => LiteralExprKind::Byte(Byte::cast(&self.syntax).unwrap()), + INT_NUMBER => LiteralExprKind::IntNumber(IntNumber::cast(&self.syntax).unwrap()), + FLOAT_NUMBER => LiteralExprKind::FloatNumber(FloatNumber::cast(&self.syntax).unwrap()), + TRUE_KW => LiteralExprKind::TrueKw(TrueKw::cast(&self.syntax).unwrap()), + FALSE_KW => LiteralExprKind::FalseKw(FalseKw::cast(&self.syntax).unwrap()), + _ => unreachable!(), + } + } +} + +impl LiteralExpr {} // LoopExpr #[derive(Debug, PartialEq, Eq, Hash)] @@ -2406,6 +2549,56 @@ impl AstNode for RangePat { impl RangePat {} +// RawByteString +#[derive(Debug, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct RawByteString { + pub(crate) syntax: SyntaxNode, +} +unsafe impl TransparentNewType for RawByteString { + type Repr = rowan::SyntaxNode; +} + +impl AstNode for RawByteString { + fn cast(syntax: &SyntaxNode) -> Option<&Self> { + match syntax.kind() { + RAW_BYTE_STRING => Some(RawByteString::from_repr(syntax.into_repr())), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } + fn to_owned(&self) -> TreeArc { TreeArc::cast(self.syntax.to_owned()) } +} + + +impl ast::AstToken for RawByteString {} +impl RawByteString {} + +// RawString +#[derive(Debug, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct RawString { + pub(crate) syntax: SyntaxNode, +} +unsafe impl TransparentNewType for RawString { + type Repr = rowan::SyntaxNode; +} + +impl AstNode for RawString { + fn cast(syntax: &SyntaxNode) -> Option<&Self> { + match syntax.kind() { + RAW_STRING => Some(RawString::from_repr(syntax.into_repr())), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } + fn to_owned(&self) -> TreeArc { TreeArc::cast(self.syntax.to_owned()) } +} + + +impl ast::AstToken for RawString {} +impl RawString {} + // RefExpr #[derive(Debug, PartialEq, Eq, Hash)] #[repr(transparent)] @@ -2919,6 +3112,31 @@ impl ast::AttrsOwner for TraitDef {} impl ast::DocCommentsOwner for TraitDef {} impl TraitDef {} +// TrueKw +#[derive(Debug, PartialEq, Eq, Hash)] +#[repr(transparent)] +pub struct TrueKw { + pub(crate) syntax: SyntaxNode, +} +unsafe impl TransparentNewType for TrueKw { + type Repr = rowan::SyntaxNode; +} + +impl AstNode for TrueKw { + fn cast(syntax: &SyntaxNode) -> Option<&Self> { + match syntax.kind() { + TRUE_KW => Some(TrueKw::from_repr(syntax.into_repr())), + _ => None, + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } + fn to_owned(&self) -> TreeArc { TreeArc::cast(self.syntax.to_owned()) } +} + + +impl ast::AstToken for TrueKw {} +impl TrueKw {} + // TryExpr #[derive(Debug, PartialEq, Eq, Hash)] #[repr(transparent)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index dfd88bd109..bd8c5b4110 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -426,11 +426,32 @@ Grammar( "PrefixExpr": (options: ["Expr"]), "RangeExpr": (), "BinExpr": (), + + "IntNumber": ( traits: ["AstToken"] ), + "FloatNumber": ( traits: ["AstToken"] ), "String": ( traits: ["AstToken"] ), + "RawString": ( traits: ["AstToken"] ), "Byte": ( traits: ["AstToken"] ), + "RawByteString": ( traits: ["AstToken"] ), "ByteString": ( traits: ["AstToken"] ), "Char": ( traits: ["AstToken"] ), - "Literal": (), + "TrueKw": ( traits: ["AstToken"] ), + "FalseKw": ( traits: ["AstToken"] ), + "LiteralExpr": ( + enum: [ + "String", + "ByteString", + "RawString", + "RawByteString", + "Char", + "Byte", + "IntNumber", + "FloatNumber", + "TrueKw", + "FalseKw", + ] + ), + "Literal": (options: ["LiteralExpr"]), "Expr": ( enum: [ diff --git a/crates/ra_syntax/src/lexer/strings.rs b/crates/ra_syntax/src/lexer/strings.rs index 5090feae64..0865b7f3b7 100644 --- a/crates/ra_syntax/src/lexer/strings.rs +++ b/crates/ra_syntax/src/lexer/strings.rs @@ -49,7 +49,7 @@ pub(crate) fn scan_byte_char_or_string(ptr: &mut Ptr) -> SyntaxKind { BYTE_STRING } 'r' => { - scan_raw_byte_string(ptr); + scan_raw_string(ptr); RAW_BYTE_STRING } _ => unreachable!(), @@ -108,16 +108,3 @@ fn scan_byte(ptr: &mut Ptr) { fn scan_byte_string(ptr: &mut Ptr) { scan_string(ptr) } - -fn scan_raw_byte_string(ptr: &mut Ptr) { - if !ptr.at('"') { - return; - } - ptr.bump(); - - while let Some(c) = ptr.bump() { - if c == '"' { - return; - } - } -} diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index 2a095817a3..bc311cbbcb 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs @@ -59,24 +59,29 @@ impl SourceFile { assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); TreeArc::cast(root) } + pub fn parse(text: &str) -> TreeArc { let tokens = tokenize(&text); let (green, errors) = parser_impl::parse_with(yellow::GreenBuilder::new(), text, &tokens, grammar::root); SourceFile::new(green, errors) } + pub fn reparse(&self, edit: &AtomTextEdit) -> TreeArc { self.incremental_reparse(edit) .unwrap_or_else(|| self.full_reparse(edit)) } + pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option> { reparsing::incremental_reparse(self.syntax(), edit, self.errors()) .map(|(green_node, errors)| SourceFile::new(green_node, errors)) } + fn full_reparse(&self, edit: &AtomTextEdit) -> TreeArc { let text = edit.apply(self.syntax().text().to_string()); SourceFile::parse(&text) } + pub fn errors(&self) -> Vec { let mut errors = self.syntax.root_data().clone(); errors.extend(validation::validate(self)); diff --git a/crates/ra_syntax/src/yellow.rs b/crates/ra_syntax/src/yellow.rs index 93621d08ab..9b93945ccd 100644 --- a/crates/ra_syntax/src/yellow.rs +++ b/crates/ra_syntax/src/yellow.rs @@ -128,40 +128,52 @@ impl SyntaxNode { pub(crate) fn root_data(&self) -> &Vec { self.0.root_data() } + pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode { self.0.replace_self(replacement) } + pub fn to_owned(&self) -> TreeArc { let ptr = TreeArc(self.0.to_owned()); TreeArc::cast(ptr) } + pub fn kind(&self) -> SyntaxKind { self.0.kind() } + pub fn range(&self) -> TextRange { self.0.range() } + pub fn text(&self) -> SyntaxText { SyntaxText::new(self) } + pub fn is_leaf(&self) -> bool { self.0.is_leaf() } + pub fn parent(&self) -> Option<&SyntaxNode> { self.0.parent().map(SyntaxNode::from_repr) } + pub fn first_child(&self) -> Option<&SyntaxNode> { self.0.first_child().map(SyntaxNode::from_repr) } + pub fn last_child(&self) -> Option<&SyntaxNode> { self.0.last_child().map(SyntaxNode::from_repr) } + pub fn next_sibling(&self) -> Option<&SyntaxNode> { self.0.next_sibling().map(SyntaxNode::from_repr) } + pub fn prev_sibling(&self) -> Option<&SyntaxNode> { self.0.prev_sibling().map(SyntaxNode::from_repr) } + pub fn children(&self) -> SyntaxNodeChildren { SyntaxNodeChildren(self.0.children()) } diff --git a/crates/ra_syntax/src/yellow/syntax_text.rs b/crates/ra_syntax/src/yellow/syntax_text.rs index 08dbe57a2b..378cd1b2e2 100644 --- a/crates/ra_syntax/src/yellow/syntax_text.rs +++ b/crates/ra_syntax/src/yellow/syntax_text.rs @@ -15,6 +15,7 @@ impl<'a> SyntaxText<'a> { range: node.range(), } } + pub fn chunks(&self) -> impl Iterator { let range = self.range; self.node.descendants().filter_map(move |node| { @@ -24,15 +25,19 @@ impl<'a> SyntaxText<'a> { Some(&text[range]) }) } + pub fn push_to(&self, buf: &mut String) { self.chunks().for_each(|it| buf.push_str(it)); } + pub fn to_string(&self) -> String { self.chunks().collect() } + pub fn contains(&self, c: char) -> bool { self.chunks().any(|it| it.contains(c)) } + pub fn find(&self, c: char) -> Option { let mut acc: TextUnit = 0.into(); for chunk in self.chunks() { @@ -44,9 +49,11 @@ impl<'a> SyntaxText<'a> { } None } + pub fn len(&self) -> TextUnit { self.range.len() } + pub fn slice(&self, range: impl SyntaxTextSlice) -> SyntaxText<'a> { let range = range.restrict(self.range).unwrap_or_else(|| { panic!("invalid slice, range: {:?}, slice: {:?}", self.range, range) @@ -56,8 +63,10 @@ impl<'a> SyntaxText<'a> { range, } } - pub fn char_at(&self, offset: TextUnit) -> Option { + + pub fn char_at(&self, offset: impl Into) -> Option { let mut start: TextUnit = 0.into(); + let offset = offset.into(); for chunk in self.chunks() { let end = start + TextUnit::of_str(chunk); if start <= offset && offset < end {