Repository: rust-analyzer/rust-analyzer
Pull request #485 (2019-01-10 17:13:28)

Add type inference for a bunch of primitives
This PR adds inference for `&str`, `&[u8]`, `char`, `bool`, floats and integers. For floats and integers it uses type variables to infer the exact type, i.e. `u32`, from context when it's not annotated explicitly.

I'm not quite happy with the implementation yet, but I think it mostly works now.


Patch:
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<u8>),
+    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<ExprId>,
     },
+    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<T>(x: Cancelable<Option<T>>) -> Option<Cancelable<T>> {
@@ -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<Self> {
         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<UncertainIntTy> {
+        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<UncertainFloatTy> {
+        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_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<SmolStr> },
+    FloatNumber { suffix: Option<SmolStr> },
+    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<RaTypes>;
+}
+
+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<FalseKw> { 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<RaTypes>;
+}
+
+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<FloatNumber> { 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<RaTypes>;
+}
+
+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<IntNumber> { 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<RaTypes>;
+}
+
+#[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<LiteralExpr> { 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<RaTypes>;
+}
+
+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<RawByteString> { 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<RaTypes>;
+}
+
+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<RawString> { 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<RaTypes>;
+}
+
+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<TrueKw> { 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<SourceFile> {
         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<SourceFile> {
         self.incremental_reparse(edit)
             .unwrap_or_else(|| self.full_reparse(edit))
     }
+
     pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<TreeArc<SourceFile>> {
         reparsing::incremental_reparse(self.syntax(), edit, self.errors())
             .map(|(green_node, errors)| SourceFile::new(green_node, errors))
     }
+
     fn full_reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> {
         let text = edit.apply(self.syntax().text().to_string());
         SourceFile::parse(&text)
     }
+
     pub fn errors(&self) -> Vec<SyntaxError> {
         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<SyntaxError> {
         self.0.root_data()
     }
+
     pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode {
         self.0.replace_self(replacement)
     }
+
     pub fn to_owned(&self) -> TreeArc<SyntaxNode> {
         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<Item = &'a str> {
         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<TextUnit> {
         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<char> {
+
+    pub fn char_at(&self, offset: impl Into<TextUnit>) -> Option<char> {
         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 {


Test patch:
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