mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 12:29:21 +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_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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
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]
|
[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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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: [
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue