mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 04:19:13 +00:00
Merge #485
485: Add type inference for a bunch of primitives r=flodiebold a=marcusklaas 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. Co-authored-by: Marcus Klaas de Vries <mail@marcusklaas.nl>
This commit is contained in:
commit
e8e82ce032
19 changed files with 625 additions and 130 deletions
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
12
crates/ra_hir/src/ty/tests/data/literals.txt
Normal file
12
crates/ra_hir/src/ty/tests/data/literals.txt
Normal file
|
@ -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]
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue