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:
bors[bot] 2019-01-14 20:58:20 +00:00
commit e8e82ce032
19 changed files with 625 additions and 130 deletions

View file

@ -5,9 +5,12 @@ use rustc_hash::FxHashMap;
use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
use ra_db::{LocalSyntaxPtr, Cancelable}; 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::{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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ExprId(RawId); 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)] #[derive(Debug, Clone, Eq, PartialEq)]
pub enum Expr { pub enum Expr {
/// This is produced if syntax tree does not have a required expression piece. /// This is produced if syntax tree does not have a required expression piece.
@ -186,6 +199,7 @@ pub enum Expr {
Tuple { Tuple {
exprs: Vec<ExprId>, exprs: Vec<ExprId>,
}, },
Literal(Literal),
} }
pub use ra_syntax::ast::PrefixOp as UnaryOp; pub use ra_syntax::ast::PrefixOp as UnaryOp;
@ -305,6 +319,7 @@ impl Expr {
f(*expr); f(*expr);
} }
} }
Expr::Literal(_) => {}
} }
} }
} }
@ -633,13 +648,50 @@ impl ExprCollector {
let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect(); let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect();
self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr) 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: // TODO implement HIR for these:
ast::ExprKind::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::ExprKind::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
ast::ExprKind::IndexExpr(_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::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
ast::ExprKind::RangeExpr(_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),
} }
} }

View file

@ -23,7 +23,7 @@ impl fmt::Debug for Name {
} }
impl Name { impl Name {
fn new(text: SmolStr) -> Name { pub(crate) fn new(text: SmolStr) -> Name {
Name { text } Name { text }
} }

View file

@ -14,7 +14,7 @@
//! rustc. //! rustc.
mod autoderef; mod autoderef;
mod primitive; pub(crate) mod primitive;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub(crate) mod method_resolution; pub(crate) mod method_resolution;
@ -38,7 +38,7 @@ use crate::{
db::HirDatabase, db::HirDatabase,
type_ref::{TypeRef, Mutability}, type_ref::{TypeRef, Mutability},
name::KnownName, 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>> { 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 /// The kinds of placeholders we need during type inference. There's separate
/// have type variables; in the future, we will probably also need int and float /// values for general types, and for integer and float variables. The latter
/// variables, for inference of literal values (e.g. `100` could be one of /// two are used for inference of literal values (e.g. `100` could be one of
/// several integer types). /// several integer types).
#[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum InferTy { pub enum InferTy {
TypeVar(TypeVarId), 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 /// 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`. /// (a non-surrogate code point). Written as `char`.
Char, Char,
/// A primitive signed integer type. For example, `i32`. /// A primitive integer type. For example, `i32`.
Int(primitive::IntTy), Int(primitive::UncertainIntTy),
/// A primitive unsigned integer type. For example, `u32`.
Uint(primitive::UintTy),
/// A primitive floating-point type. For example, `f64`. /// A primitive floating-point type. For example, `f64`.
Float(primitive::FloatTy), Float(primitive::UncertainFloatTy),
/// Structures, enumerations and unions. /// Structures, enumerations and unions.
Adt { Adt {
@ -198,8 +217,9 @@ pub enum Ty {
// above function pointer type. Once we implement generics, we will probably // above function pointer type. Once we implement generics, we will probably
// need this as well. // need this as well.
// A trait, defined with `dyn trait`. // A trait, defined with `dyn Trait`.
// Dynamic(), // Dynamic(),
// The anonymous type of a closure. Used to represent the type of // The anonymous type of a closure. Used to represent the type of
// `|a| a`. // `|a| a`.
// Closure(DefId, ClosureSubsts<'tcx>), // Closure(DefId, ClosureSubsts<'tcx>),
@ -312,20 +332,19 @@ impl Ty {
path: &Path, path: &Path,
) -> Cancelable<Self> { ) -> Cancelable<Self> {
if let Some(name) = path.as_ident() { if let Some(name) = path.as_ident() {
if let Some(KnownName::Bool) = name.as_known_name() { if let Some(int_ty) = primitive::UncertainIntTy::from_name(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) {
return Ok(Ty::Int(int_ty)); return Ok(Ty::Int(int_ty));
} else if let Some(uint_ty) = primitive::UintTy::from_name(name) { } else if let Some(float_ty) = primitive::UncertainFloatTy::from_name(name) {
return Ok(Ty::Uint(uint_ty));
} else if let Some(float_ty) = primitive::FloatTy::from_name(name) {
return Ok(Ty::Float(float_ty)); return Ok(Ty::Float(float_ty));
} else if name.as_known_name() == Some(KnownName::SelfType) { } else if name.as_known_name() == Some(KnownName::SelfType) {
return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type())); 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::Bool => write!(f, "bool"),
Ty::Char => write!(f, "char"), Ty::Char => write!(f, "char"),
Ty::Int(t) => write!(f, "{}", t.ty_to_string()), 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::Float(t) => write!(f, "{}", t.ty_to_string()),
Ty::Str => write!(f, "str"), Ty::Str => write!(f, "str"),
Ty::Slice(t) => write!(f, "[{}]", t), Ty::Slice(t) => write!(f, "[{}]", t),
@ -587,7 +605,7 @@ fn binary_op_return_ty(op: BinaryOp, rhs_ty: Ty) -> Ty {
| BinaryOp::BitwiseAnd | BinaryOp::BitwiseAnd
| BinaryOp::BitwiseOr | BinaryOp::BitwiseOr
| BinaryOp::BitwiseXor => match rhs_ty { | BinaryOp::BitwiseXor => match rhs_ty {
Ty::Uint(..) | Ty::Int(..) | Ty::Float(..) => rhs_ty, Ty::Int(..) | Ty::Float(..) => rhs_ty,
_ => Ty::Unknown, _ => Ty::Unknown,
}, },
BinaryOp::RangeRightOpen | BinaryOp::RangeRightClosed => 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 { match op {
BinaryOp::BooleanAnd | BinaryOp::BooleanOr => Ty::Bool, BinaryOp::BooleanAnd | BinaryOp::BooleanOr => Ty::Bool,
BinaryOp::Assignment | BinaryOp::EqualityTest => match lhs_ty { 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, _ => Ty::Unknown,
}, },
BinaryOp::LesserEqualTest BinaryOp::LesserEqualTest
@ -625,7 +643,7 @@ fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty {
| BinaryOp::BitwiseAnd | BinaryOp::BitwiseAnd
| BinaryOp::BitwiseOr | BinaryOp::BitwiseOr
| BinaryOp::BitwiseXor => match lhs_ty { | BinaryOp::BitwiseXor => match lhs_ty {
Ty::Uint(..) | Ty::Int(..) | Ty::Float(..) => lhs_ty, Ty::Int(..) | Ty::Float(..) => lhs_ty,
_ => Ty::Unknown, _ => Ty::Unknown,
}, },
_ => Ty::Unknown, _ => Ty::Unknown,
@ -695,13 +713,17 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
match (&*ty1, &*ty2) { match (&*ty1, &*ty2) {
(Ty::Unknown, ..) => true, (Ty::Unknown, ..) => true,
(.., Ty::Unknown) => true, (.., Ty::Unknown) => true,
(Ty::Bool, _) (Ty::Int(t1), Ty::Int(t2)) => match (t1, t2) {
| (Ty::Str, _) (primitive::UncertainIntTy::Unknown, _)
| (Ty::Never, _) | (_, primitive::UncertainIntTy::Unknown) => true,
| (Ty::Char, _) _ => t1 == t2,
| (Ty::Int(..), Ty::Int(..)) },
| (Ty::Uint(..), Ty::Uint(..)) (Ty::Float(t1), Ty::Float(t2)) => match (t1, t2) {
| (Ty::Float(..), Ty::Float(..)) => ty1 == ty2, (primitive::UncertainFloatTy::Unknown, _)
| (_, primitive::UncertainFloatTy::Unknown) => true,
_ => t1 == t2,
},
(Ty::Bool, _) | (Ty::Str, _) | (Ty::Never, _) | (Ty::Char, _) => ty1 == ty2,
( (
Ty::Adt { Ty::Adt {
def_id: def_id1, .. def_id: def_id1, ..
@ -718,12 +740,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
.iter() .iter()
.zip(ts2.iter()) .zip(ts2.iter())
.all(|(t1, t2)| self.unify(t1, t2)), .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 // both type vars are unknown since we tried to resolve them
self.var_unification_table.union(*tv1, *tv2); self.var_unification_table.union(*tv1, *tv2);
true 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 // the type var is unknown since we tried to resolve it
self.var_unification_table self.var_unification_table
.union_value(*tv, TypeVarValue::Known(other.clone())); .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. /// 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 { fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
match ty { match ty {
Ty::Unknown => self.new_type_var(), 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, _ => ty,
} }
} }
@ -757,12 +800,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
/// known type. /// known type.
fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty { fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty {
ty.fold(&mut |ty| match ty { ty.fold(&mut |ty| match ty {
Ty::Infer(InferTy::TypeVar(tv)) => { Ty::Infer(tv) => {
if let Some(known_ty) = self.var_unification_table.probe_value(tv).known() { 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 // known_ty may contain other variables that are known by now
self.resolve_ty_as_possible(known_ty.clone()) self.resolve_ty_as_possible(known_ty.clone())
} else { } else {
Ty::Infer(InferTy::TypeVar(tv)) ty
} }
} }
_ => ty, _ => ty,
@ -773,8 +817,9 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
/// otherwise, return ty. /// otherwise, return ty.
fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
match ty { match ty {
Ty::Infer(InferTy::TypeVar(tv)) => { Ty::Infer(tv) => {
match self.var_unification_table.probe_value(*tv).known() { let inner = tv.to_inner();
match self.var_unification_table.probe_value(inner).known() {
Some(known_ty) => { Some(known_ty) => {
// The known_ty can't be a type var itself // The known_ty can't be a type var itself
Cow::Owned(known_ty.clone()) Cow::Owned(known_ty.clone())
@ -790,12 +835,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
/// replaced by Ty::Unknown. /// replaced by Ty::Unknown.
fn resolve_ty_completely(&mut self, ty: Ty) -> Ty { fn resolve_ty_completely(&mut self, ty: Ty) -> Ty {
ty.fold(&mut |ty| match ty { ty.fold(&mut |ty| match ty {
Ty::Infer(InferTy::TypeVar(tv)) => { Ty::Infer(tv) => {
if let Some(known_ty) = self.var_unification_table.probe_value(tv).known() { 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 // known_ty may contain other variables that are known by now
self.resolve_ty_completely(known_ty.clone()) self.resolve_ty_completely(known_ty.clone())
} else { } else {
Ty::Unknown tv.fallback_value()
} }
} }
_ => ty, _ => ty,
@ -1067,6 +1113,20 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
Ty::Tuple(Arc::from(ty_vec)) 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 // use a new type variable if we got Ty::Unknown here
let ty = self.insert_type_vars_shallow(ty); let ty = self.insert_type_vars_shallow(ty);

View file

@ -2,6 +2,56 @@ use std::fmt;
use crate::{Name, KnownName}; 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)] #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
pub enum IntTy { pub enum IntTy {
Isize, Isize,

View file

@ -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] #[test]
fn infer_backwards() { fn infer_backwards() {
check_inference( check_inference(
@ -180,7 +206,7 @@ fn f(x: bool) -> i32 {
0i32 0i32
} }
fn test() { fn test() -> bool {
let x = a && b; let x = a && b;
let y = true || false; let y = true || false;
let z = x == y; let z = x == y;
@ -277,8 +303,6 @@ fn test(x: &str, y: isize) {
let b = (a, x); let b = (a, x);
let c = (y, x); let c = (y, x);
let d = (c, x); let d = (c, x);
// we have not infered these case yet.
let e = (1, "e"); let e = (1, "e");
let f = (e, "d"); let f = (e, "d");
} }

View file

@ -7,7 +7,7 @@
[55; 56) 'b': isize [55; 56) 'b': isize
[62; 63) 'c': ! [62; 63) 'c': !
[69; 70) 'd': &str [69; 70) 'd': &str
[76; 82) '1usize': [unknown] [76; 82) '1usize': usize
[88; 94) '1isize': [unknown] [88; 94) '1isize': isize
[100; 106) '"test"': [unknown] [100; 106) '"test"': &str
[112; 118) '1.0f32': [unknown] [112; 118) '1.0f32': f32

View file

@ -1,46 +1,46 @@
[6; 7) 'x': bool [6; 7) 'x': bool
[22; 34) '{ 0i32 }': i32 [22; 34) '{ 0i32 }': i32
[28; 32) '0i32': i32 [28; 32) '0i32': i32
[46; 342) '{ ... < 3 }': bool [54; 350) '{ ... < 3 }': bool
[56; 57) 'x': bool [64; 65) 'x': bool
[60; 61) 'a': bool [68; 69) 'a': bool
[60; 66) 'a && b': bool [68; 74) 'a && b': bool
[65; 66) 'b': bool [73; 74) 'b': bool
[76; 77) 'y': bool [84; 85) 'y': bool
[80; 84) 'true': bool [88; 92) 'true': bool
[80; 93) 'true || false': bool [88; 101) 'true || false': bool
[88; 93) 'false': bool [96; 101) 'false': bool
[103; 104) 'z': bool [111; 112) 'z': bool
[107; 108) 'x': bool [115; 116) 'x': bool
[107; 113) 'x == y': bool [115; 121) 'x == y': bool
[112; 113) 'y': bool [120; 121) 'y': bool
[123; 134) 'minus_forty': isize [131; 142) 'minus_forty': isize
[144; 152) '-40isize': isize [152; 160) '-40isize': isize
[145; 152) '40isize': [unknown] [153; 160) '40isize': isize
[162; 163) 'h': bool [170; 171) 'h': bool
[166; 177) 'minus_forty': isize [174; 185) 'minus_forty': isize
[166; 188) 'minus_...ONST_2': bool [174; 196) 'minus_...ONST_2': bool
[181; 188) 'CONST_2': isize [189; 196) 'CONST_2': isize
[198; 199) 'c': i32 [206; 207) 'c': i32
[202; 203) 'f': fn(bool) -> i32 [210; 211) 'f': fn(bool) -> i32
[202; 211) 'f(z || y)': i32 [210; 219) 'f(z || y)': i32
[202; 215) 'f(z || y) + 5': i32 [210; 223) 'f(z || y) + 5': i32
[204; 205) 'z': bool [212; 213) 'z': bool
[204; 210) 'z || y': bool [212; 218) 'z || y': bool
[209; 210) 'y': bool [217; 218) 'y': bool
[214; 215) '5': i32 [222; 223) '5': i32
[225; 226) 'd': [unknown] [233; 234) 'd': [unknown]
[229; 230) 'b': [unknown] [237; 238) 'b': [unknown]
[240; 241) 'g': () [248; 249) 'g': ()
[244; 255) 'minus_forty': isize [252; 263) 'minus_forty': isize
[244; 260) 'minus_...y ^= i': () [252; 268) 'minus_...y ^= i': ()
[259; 260) 'i': isize [267; 268) 'i': isize
[270; 273) 'ten': usize [278; 281) 'ten': usize
[283; 285) '10': usize [291; 293) '10': usize
[295; 308) 'ten_is_eleven': bool [303; 316) 'ten_is_eleven': bool
[311; 314) 'ten': usize [319; 322) 'ten': usize
[311; 326) 'ten == some_num': bool [319; 334) 'ten == some_num': bool
[318; 326) 'some_num': usize [326; 334) 'some_num': usize
[333; 336) 'ten': usize [341; 344) 'ten': usize
[333; 340) 'ten < 3': bool [341; 348) 'ten < 3': bool
[339; 340) '3': usize [347; 348) '3': usize

View file

@ -1,6 +1,6 @@
[11; 71) '{ ...= b; }': () [11; 71) '{ ...= b; }': ()
[21; 22) 'a': [unknown] [21; 22) 'a': isize
[25; 31) '1isize': [unknown] [25; 31) '1isize': isize
[41; 42) 'b': usize [41; 42) 'b': usize
[52; 53) '1': usize [52; 53) '1': usize
[63; 64) 'c': usize [63; 64) 'c': usize

View 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]

View file

@ -2,14 +2,14 @@
[82; 83) 'c': [unknown] [82; 83) 'c': [unknown]
[86; 87) 'C': [unknown] [86; 87) 'C': [unknown]
[86; 90) 'C(1)': [unknown] [86; 90) 'C(1)': [unknown]
[88; 89) '1': [unknown] [88; 89) '1': i32
[96; 97) 'B': [unknown] [96; 97) 'B': [unknown]
[107; 108) 'a': A [107; 108) 'a': A
[114; 133) 'A { b:...C(1) }': A [114; 133) 'A { b:...C(1) }': A
[121; 122) 'B': B [121; 122) 'B': B
[127; 128) 'C': [unknown] [127; 128) 'C': [unknown]
[127; 131) 'C(1)': C [127; 131) 'C(1)': C
[129; 130) '1': [unknown] [129; 130) '1': i32
[139; 140) 'a': A [139; 140) 'a': A
[139; 142) 'a.b': B [139; 142) 'a.b': B
[148; 149) 'a': A [148; 149) 'a': A

View file

@ -1,6 +1,6 @@
[9; 10) 'x': &str [9; 10) 'x': &str
[18; 19) 'y': isize [18; 19) 'y': isize
[28; 214) '{ ...d"); }': () [28; 170) '{ ...d"); }': ()
[38; 39) 'a': (u32, &str) [38; 39) 'a': (u32, &str)
[55; 63) '(1, "a")': (u32, &str) [55; 63) '(1, "a")': (u32, &str)
[56; 57) '1': u32 [56; 57) '1': u32
@ -17,11 +17,11 @@
[117; 123) '(c, x)': ((isize, &str), &str) [117; 123) '(c, x)': ((isize, &str), &str)
[118; 119) 'c': (isize, &str) [118; 119) 'c': (isize, &str)
[121; 122) 'x': &str [121; 122) 'x': &str
[177; 178) 'e': ([unknown], [unknown]) [133; 134) 'e': (i32, &str)
[181; 189) '(1, "e")': ([unknown], [unknown]) [137; 145) '(1, "e")': (i32, &str)
[182; 183) '1': [unknown] [138; 139) '1': i32
[185; 188) '"e"': [unknown] [141; 144) '"e"': &str
[199; 200) 'f': (([unknown], [unknown]), [unknown]) [155; 156) 'f': ((i32, &str), &str)
[203; 211) '(e, "d")': (([unknown], [unknown]), [unknown]) [159; 167) '(e, "d")': ((i32, &str), &str)
[204; 205) 'e': ([unknown], [unknown]) [160; 161) 'e': (i32, &str)
[207; 210) '"d"': [unknown] [163; 166) '"d"': &str

View file

@ -230,20 +230,19 @@ mod tests {
assert_eq!("[unknown]", &type_name); assert_eq!("[unknown]", &type_name);
} }
// FIXME: improve type_of to make this work
#[test] #[test]
fn test_type_of_for_expr_2() { fn test_type_of_for_expr_2() {
let (analysis, range) = single_file_with_range( let (analysis, range) = single_file_with_range(
" "
fn main() { fn main() {
let foo: usize = 1; let foo: usize = 1;
let bar = <|>1 + foo_test<|>; let bar = <|>1 + foo<|>;
} }
", ",
); );
let type_name = analysis.type_of(range).unwrap().unwrap(); let type_name = analysis.type_of(range).unwrap().unwrap();
assert_eq!("[unknown]", &type_name); assert_eq!("usize", &type_name);
} }
} }

View file

@ -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] #[test]
fn test_doc_comment_of_items() { fn test_doc_comment_of_items() {
let file = SourceFile::parse( let file = SourceFile::parse(

View file

@ -793,6 +793,31 @@ impl AstNode for ExternCrateItem {
impl 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 // FieldExpr
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)] #[repr(transparent)]
@ -849,6 +874,31 @@ impl AstNode for FieldPatList {
impl 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 // FnDef
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)] #[repr(transparent)]
@ -1130,6 +1180,31 @@ impl AstNode for IndexExpr {
impl 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 // ItemList
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)] #[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 // LoopExpr
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
@ -2406,6 +2549,56 @@ impl AstNode for RangePat {
impl 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 // RefExpr
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)] #[repr(transparent)]
@ -2919,6 +3112,31 @@ impl ast::AttrsOwner for TraitDef {}
impl ast::DocCommentsOwner for TraitDef {} impl ast::DocCommentsOwner for TraitDef {}
impl 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 // TryExpr
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash)]
#[repr(transparent)] #[repr(transparent)]

View file

@ -426,11 +426,32 @@ Grammar(
"PrefixExpr": (options: ["Expr"]), "PrefixExpr": (options: ["Expr"]),
"RangeExpr": (), "RangeExpr": (),
"BinExpr": (), "BinExpr": (),
"IntNumber": ( traits: ["AstToken"] ),
"FloatNumber": ( traits: ["AstToken"] ),
"String": ( traits: ["AstToken"] ), "String": ( traits: ["AstToken"] ),
"RawString": ( traits: ["AstToken"] ),
"Byte": ( traits: ["AstToken"] ), "Byte": ( traits: ["AstToken"] ),
"RawByteString": ( traits: ["AstToken"] ),
"ByteString": ( traits: ["AstToken"] ), "ByteString": ( traits: ["AstToken"] ),
"Char": ( 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": ( "Expr": (
enum: [ enum: [

View file

@ -49,7 +49,7 @@ pub(crate) fn scan_byte_char_or_string(ptr: &mut Ptr) -> SyntaxKind {
BYTE_STRING BYTE_STRING
} }
'r' => { 'r' => {
scan_raw_byte_string(ptr); scan_raw_string(ptr);
RAW_BYTE_STRING RAW_BYTE_STRING
} }
_ => unreachable!(), _ => unreachable!(),
@ -108,16 +108,3 @@ fn scan_byte(ptr: &mut Ptr) {
fn scan_byte_string(ptr: &mut Ptr) { fn scan_byte_string(ptr: &mut Ptr) {
scan_string(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;
}
}
}

View file

@ -59,24 +59,29 @@ impl SourceFile {
assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
TreeArc::cast(root) TreeArc::cast(root)
} }
pub fn parse(text: &str) -> TreeArc<SourceFile> { pub fn parse(text: &str) -> TreeArc<SourceFile> {
let tokens = tokenize(&text); let tokens = tokenize(&text);
let (green, errors) = let (green, errors) =
parser_impl::parse_with(yellow::GreenBuilder::new(), text, &tokens, grammar::root); parser_impl::parse_with(yellow::GreenBuilder::new(), text, &tokens, grammar::root);
SourceFile::new(green, errors) SourceFile::new(green, errors)
} }
pub fn reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> { pub fn reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> {
self.incremental_reparse(edit) self.incremental_reparse(edit)
.unwrap_or_else(|| self.full_reparse(edit)) .unwrap_or_else(|| self.full_reparse(edit))
} }
pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<TreeArc<SourceFile>> { pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<TreeArc<SourceFile>> {
reparsing::incremental_reparse(self.syntax(), edit, self.errors()) reparsing::incremental_reparse(self.syntax(), edit, self.errors())
.map(|(green_node, errors)| SourceFile::new(green_node, errors)) .map(|(green_node, errors)| SourceFile::new(green_node, errors))
} }
fn full_reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> { fn full_reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> {
let text = edit.apply(self.syntax().text().to_string()); let text = edit.apply(self.syntax().text().to_string());
SourceFile::parse(&text) SourceFile::parse(&text)
} }
pub fn errors(&self) -> Vec<SyntaxError> { pub fn errors(&self) -> Vec<SyntaxError> {
let mut errors = self.syntax.root_data().clone(); let mut errors = self.syntax.root_data().clone();
errors.extend(validation::validate(self)); errors.extend(validation::validate(self));

View file

@ -128,40 +128,52 @@ impl SyntaxNode {
pub(crate) fn root_data(&self) -> &Vec<SyntaxError> { pub(crate) fn root_data(&self) -> &Vec<SyntaxError> {
self.0.root_data() self.0.root_data()
} }
pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode { pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode {
self.0.replace_self(replacement) self.0.replace_self(replacement)
} }
pub fn to_owned(&self) -> TreeArc<SyntaxNode> { pub fn to_owned(&self) -> TreeArc<SyntaxNode> {
let ptr = TreeArc(self.0.to_owned()); let ptr = TreeArc(self.0.to_owned());
TreeArc::cast(ptr) TreeArc::cast(ptr)
} }
pub fn kind(&self) -> SyntaxKind { pub fn kind(&self) -> SyntaxKind {
self.0.kind() self.0.kind()
} }
pub fn range(&self) -> TextRange { pub fn range(&self) -> TextRange {
self.0.range() self.0.range()
} }
pub fn text(&self) -> SyntaxText { pub fn text(&self) -> SyntaxText {
SyntaxText::new(self) SyntaxText::new(self)
} }
pub fn is_leaf(&self) -> bool { pub fn is_leaf(&self) -> bool {
self.0.is_leaf() self.0.is_leaf()
} }
pub fn parent(&self) -> Option<&SyntaxNode> { pub fn parent(&self) -> Option<&SyntaxNode> {
self.0.parent().map(SyntaxNode::from_repr) self.0.parent().map(SyntaxNode::from_repr)
} }
pub fn first_child(&self) -> Option<&SyntaxNode> { pub fn first_child(&self) -> Option<&SyntaxNode> {
self.0.first_child().map(SyntaxNode::from_repr) self.0.first_child().map(SyntaxNode::from_repr)
} }
pub fn last_child(&self) -> Option<&SyntaxNode> { pub fn last_child(&self) -> Option<&SyntaxNode> {
self.0.last_child().map(SyntaxNode::from_repr) self.0.last_child().map(SyntaxNode::from_repr)
} }
pub fn next_sibling(&self) -> Option<&SyntaxNode> { pub fn next_sibling(&self) -> Option<&SyntaxNode> {
self.0.next_sibling().map(SyntaxNode::from_repr) self.0.next_sibling().map(SyntaxNode::from_repr)
} }
pub fn prev_sibling(&self) -> Option<&SyntaxNode> { pub fn prev_sibling(&self) -> Option<&SyntaxNode> {
self.0.prev_sibling().map(SyntaxNode::from_repr) self.0.prev_sibling().map(SyntaxNode::from_repr)
} }
pub fn children(&self) -> SyntaxNodeChildren { pub fn children(&self) -> SyntaxNodeChildren {
SyntaxNodeChildren(self.0.children()) SyntaxNodeChildren(self.0.children())
} }

View file

@ -15,6 +15,7 @@ impl<'a> SyntaxText<'a> {
range: node.range(), range: node.range(),
} }
} }
pub fn chunks(&self) -> impl Iterator<Item = &'a str> { pub fn chunks(&self) -> impl Iterator<Item = &'a str> {
let range = self.range; let range = self.range;
self.node.descendants().filter_map(move |node| { self.node.descendants().filter_map(move |node| {
@ -24,15 +25,19 @@ impl<'a> SyntaxText<'a> {
Some(&text[range]) Some(&text[range])
}) })
} }
pub fn push_to(&self, buf: &mut String) { pub fn push_to(&self, buf: &mut String) {
self.chunks().for_each(|it| buf.push_str(it)); self.chunks().for_each(|it| buf.push_str(it));
} }
pub fn to_string(&self) -> String { pub fn to_string(&self) -> String {
self.chunks().collect() self.chunks().collect()
} }
pub fn contains(&self, c: char) -> bool { pub fn contains(&self, c: char) -> bool {
self.chunks().any(|it| it.contains(c)) self.chunks().any(|it| it.contains(c))
} }
pub fn find(&self, c: char) -> Option<TextUnit> { pub fn find(&self, c: char) -> Option<TextUnit> {
let mut acc: TextUnit = 0.into(); let mut acc: TextUnit = 0.into();
for chunk in self.chunks() { for chunk in self.chunks() {
@ -44,9 +49,11 @@ impl<'a> SyntaxText<'a> {
} }
None None
} }
pub fn len(&self) -> TextUnit { pub fn len(&self) -> TextUnit {
self.range.len() self.range.len()
} }
pub fn slice(&self, range: impl SyntaxTextSlice) -> SyntaxText<'a> { pub fn slice(&self, range: impl SyntaxTextSlice) -> SyntaxText<'a> {
let range = range.restrict(self.range).unwrap_or_else(|| { let range = range.restrict(self.range).unwrap_or_else(|| {
panic!("invalid slice, range: {:?}, slice: {:?}", self.range, range) panic!("invalid slice, range: {:?}, slice: {:?}", self.range, range)
@ -56,8 +63,10 @@ impl<'a> SyntaxText<'a> {
range, 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 mut start: TextUnit = 0.into();
let offset = offset.into();
for chunk in self.chunks() { for chunk in self.chunks() {
let end = start + TextUnit::of_str(chunk); let end = start + TextUnit::of_str(chunk);
if start <= offset && offset < end { if start <= offset && offset < end {