1088: add minimal comments r=matklad a=matklad



Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2019-04-02 10:02:48 +00:00
commit b0d2447193
9 changed files with 612 additions and 582 deletions

View File

@ -11,8 +11,8 @@ pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) ->
let expr = cond.expr()?;
let then_block = if_expr.then_branch()?;
let else_block = match if_expr.else_branch()? {
ast::ElseBranchFlavor::Block(it) => it,
ast::ElseBranchFlavor::IfExpr(_) => return None,
ast::ElseBranch::Block(it) => it,
ast::ElseBranch::IfExpr(_) => return None,
};
ctx.add_action(AssistId("replace_if_let_with_match"), "replace with match", |edit| {

View File

@ -6,7 +6,7 @@ use std::sync::Arc;
use ra_arena::{RawId, Arena, impl_arena_id};
use ra_syntax::{
TreeArc,
ast::{self, NameOwner, StructFlavor, TypeAscriptionOwner}
ast::{self, NameOwner, StructKind, TypeAscriptionOwner}
};
use crate::{
@ -47,7 +47,7 @@ pub struct StructData {
impl StructData {
fn new(struct_def: &ast::StructDef) -> StructData {
let name = struct_def.name().map(|n| n.as_name());
let variant_data = VariantData::new(struct_def.flavor());
let variant_data = VariantData::new(struct_def.kind());
let variant_data = Arc::new(variant_data);
StructData { name, variant_data }
}
@ -94,7 +94,7 @@ impl EnumData {
let variants = variants(&*enum_def)
.map(|var| EnumVariantData {
name: var.name().map(|it| it.as_name()),
variant_data: Arc::new(VariantData::new(var.flavor())),
variant_data: Arc::new(VariantData::new(var.kind())),
})
.collect();
Arc::new(EnumData { name, variants })
@ -143,9 +143,9 @@ impl VariantData {
}
impl VariantData {
fn new(flavor: StructFlavor) -> Self {
fn new(flavor: StructKind) -> Self {
let inner = match flavor {
ast::StructFlavor::Tuple(fl) => {
ast::StructKind::Tuple(fl) => {
let fields = fl
.fields()
.enumerate()
@ -156,7 +156,7 @@ impl VariantData {
.collect();
VariantDataInner::Tuple(fields)
}
ast::StructFlavor::Named(fl) => {
ast::StructKind::Named(fl) => {
let fields = fl
.fields()
.map(|fd| StructFieldData {
@ -166,7 +166,7 @@ impl VariantData {
.collect();
VariantDataInner::Struct(fields)
}
ast::StructFlavor::Unit => VariantDataInner::Unit,
ast::StructKind::Unit => VariantDataInner::Unit,
};
VariantData(inner)
}
@ -200,27 +200,27 @@ impl StructField {
let fields = var_data.fields().unwrap();
let ss;
let es;
let (file_id, struct_flavor) = match self.parent {
let (file_id, struct_kind) = match self.parent {
VariantDef::Struct(s) => {
let (file_id, source) = s.source(db);
ss = source;
(file_id, ss.flavor())
(file_id, ss.kind())
}
VariantDef::EnumVariant(e) => {
let (file_id, source) = e.source(db);
es = source;
(file_id, es.flavor())
(file_id, es.kind())
}
};
let field_sources = match struct_flavor {
ast::StructFlavor::Tuple(fl) => {
let field_sources = match struct_kind {
ast::StructKind::Tuple(fl) => {
fl.fields().map(|it| FieldSource::Pos(it.to_owned())).collect()
}
ast::StructFlavor::Named(fl) => {
ast::StructKind::Named(fl) => {
fl.fields().map(|it| FieldSource::Named(it.to_owned())).collect()
}
ast::StructFlavor::Unit => Vec::new(),
ast::StructKind::Unit => Vec::new(),
};
let field = field_sources
.into_iter()

View File

@ -20,12 +20,12 @@ impl FnSignature {
TypeRef::from_ast(type_ref)
} else {
let self_type = TypeRef::Path(Name::self_type().into());
match self_param.flavor() {
ast::SelfParamFlavor::Owned => self_type,
ast::SelfParamFlavor::Ref => {
match self_param.kind() {
ast::SelfParamKind::Owned => self_type,
ast::SelfParamKind::Ref => {
TypeRef::Reference(Box::new(self_type), Mutability::Shared)
}
ast::SelfParamFlavor::MutRef => {
ast::SelfParamKind::MutRef => {
TypeRef::Reference(Box::new(self_type), Mutability::Mut)
}
}

View File

@ -6,7 +6,7 @@ use rustc_hash::FxHashMap;
use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
use ra_syntax::{
SyntaxNodePtr, AstPtr, AstNode,
ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralFlavor, TypeAscriptionOwner}
ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralKind, TypeAscriptionOwner}
};
use crate::{
@ -516,8 +516,8 @@ impl ExprCollector {
let else_branch = e
.else_branch()
.map(|b| match b {
ast::ElseBranchFlavor::Block(it) => self.collect_block(it),
ast::ElseBranchFlavor::IfExpr(elif) => {
ast::ElseBranch::Block(it) => self.collect_block(it),
ast::ElseBranch::IfExpr(elif) => {
let expr: &ast::Expr = ast::Expr::cast(elif.syntax()).unwrap();
self.collect_expr(expr)
}
@ -533,8 +533,8 @@ impl ExprCollector {
let condition = self.collect_expr_opt(e.condition().and_then(|c| c.expr()));
let then_branch = self.collect_block_opt(e.then_branch());
let else_branch = e.else_branch().map(|b| match b {
ast::ElseBranchFlavor::Block(it) => self.collect_block(it),
ast::ElseBranchFlavor::IfExpr(elif) => {
ast::ElseBranch::Block(it) => self.collect_block(it),
ast::ElseBranch::IfExpr(elif) => {
let expr: &ast::Expr = ast::Expr::cast(elif.syntax()).unwrap();
self.collect_expr(expr)
}
@ -726,8 +726,8 @@ impl ExprCollector {
self.alloc_expr(Expr::Array { exprs }, syntax_ptr)
}
ast::ExprKind::Literal(e) => {
let lit = match e.flavor() {
LiteralFlavor::IntNumber { suffix } => {
let lit = match e.kind() {
LiteralKind::IntNumber { suffix } => {
let known_name = suffix
.and_then(|it| IntTy::from_suffix(&it).map(UncertainIntTy::Known));
@ -736,7 +736,7 @@ impl ExprCollector {
known_name.unwrap_or(UncertainIntTy::Unknown),
)
}
LiteralFlavor::FloatNumber { suffix } => {
LiteralKind::FloatNumber { suffix } => {
let known_name = suffix
.and_then(|it| FloatTy::from_suffix(&it).map(UncertainFloatTy::Known));
@ -745,13 +745,13 @@ impl ExprCollector {
known_name.unwrap_or(UncertainFloatTy::Unknown),
)
}
LiteralFlavor::ByteString => Literal::ByteString(Default::default()),
LiteralFlavor::String => Literal::String(Default::default()),
LiteralFlavor::Byte => {
LiteralKind::ByteString => Literal::ByteString(Default::default()),
LiteralKind::String => Literal::String(Default::default()),
LiteralKind::Byte => {
Literal::Int(Default::default(), UncertainIntTy::Known(IntTy::u8()))
}
LiteralFlavor::Bool => Literal::Bool(Default::default()),
LiteralFlavor::Char => Literal::Char(Default::default()),
LiteralKind::Bool => Literal::Bool(Default::default()),
LiteralKind::Char => Literal::Char(Default::default()),
};
self.alloc_expr(Expr::Literal(lit), syntax_ptr)
}

View File

@ -1,22 +1,24 @@
//! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s
mod generated;
mod traits;
mod tokens;
mod extensions;
mod expr_extensions;
use std::marker::PhantomData;
use itertools::Itertools;
use crate::{
syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken, SyntaxElement},
syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken},
SmolStr,
SyntaxKind::*,
};
pub use self::{
generated::*,
traits::*,
tokens::*,
extensions::{PathSegmentKind, StructKind, SelfParamKind},
expr_extensions::{ElseBranch, PrefixOp, BinOp, LiteralKind},
};
/// The main trait to go from untyped `SyntaxNode` to a typed ast. The
@ -32,6 +34,18 @@ pub trait AstNode:
fn syntax(&self) -> &SyntaxNode;
}
/// Like `AstNode`, but wraps tokens rather than interior nodes.
pub trait AstToken<'a> {
fn cast(token: SyntaxToken<'a>) -> Option<Self>
where
Self: Sized;
fn syntax(&self) -> SyntaxToken<'a>;
fn text(&self) -> &'a SmolStr {
self.syntax().text()
}
}
/// An iterator over `SyntaxNode` children of a particular AST type.
#[derive(Debug)]
pub struct AstChildren<'a, N> {
inner: SyntaxNodeChildren<'a>,
@ -51,215 +65,6 @@ impl<'a, N: AstNode + 'a> Iterator for AstChildren<'a, N> {
}
}
pub trait AstToken<'a> {
fn cast(token: SyntaxToken<'a>) -> Option<Self>
where
Self: Sized;
fn syntax(&self) -> SyntaxToken<'a>;
fn text(&self) -> &'a SmolStr {
self.syntax().text()
}
}
impl Attr {
pub fn is_inner(&self) -> bool {
let tt = match self.value() {
None => return false,
Some(tt) => tt,
};
let prev = match tt.syntax().prev_sibling() {
None => return false,
Some(prev) => prev,
};
prev.kind() == EXCL
}
pub fn as_atom(&self) -> Option<SmolStr> {
let tt = self.value()?;
let (_bra, attr, _ket) = tt.syntax().children_with_tokens().collect_tuple()?;
if attr.kind() == IDENT {
Some(attr.as_token()?.text().clone())
} else {
None
}
}
pub fn as_call(&self) -> Option<(SmolStr, &TokenTree)> {
let tt = self.value()?;
let (_bra, attr, args, _ket) = tt.syntax().children_with_tokens().collect_tuple()?;
let args = TokenTree::cast(args.as_node()?)?;
if attr.kind() == IDENT {
Some((attr.as_token()?.text().clone(), args))
} else {
None
}
}
pub fn as_named(&self) -> Option<SmolStr> {
let tt = self.value()?;
let attr = tt.syntax().children_with_tokens().nth(1)?;
if attr.kind() == IDENT {
Some(attr.as_token()?.text().clone())
} else {
None
}
}
}
impl Name {
pub fn text(&self) -> &SmolStr {
let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap();
ident.text()
}
}
impl NameRef {
pub fn text(&self) -> &SmolStr {
let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap();
ident.text()
}
}
impl ImplBlock {
pub fn target_type(&self) -> Option<&TypeRef> {
match self.target() {
(Some(t), None) | (_, Some(t)) => Some(t),
_ => None,
}
}
pub fn target_trait(&self) -> Option<&TypeRef> {
match self.target() {
(Some(t), Some(_)) => Some(t),
_ => None,
}
}
fn target(&self) -> (Option<&TypeRef>, Option<&TypeRef>) {
let mut types = children(self);
let first = types.next();
let second = types.next();
(first, second)
}
}
impl Module {
pub fn has_semi(&self) -> bool {
match self.syntax().last_child_or_token() {
None => false,
Some(node) => node.kind() == SEMI,
}
}
}
impl LetStmt {
pub fn has_semi(&self) -> bool {
match self.syntax().last_child_or_token() {
None => false,
Some(node) => node.kind() == SEMI,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ElseBranchFlavor<'a> {
Block(&'a Block),
IfExpr(&'a IfExpr),
}
impl IfExpr {
pub fn then_branch(&self) -> Option<&Block> {
self.blocks().nth(0)
}
pub fn else_branch(&self) -> Option<ElseBranchFlavor> {
let res = match self.blocks().nth(1) {
Some(block) => ElseBranchFlavor::Block(block),
None => {
let elif: &IfExpr = child_opt(self)?;
ElseBranchFlavor::IfExpr(elif)
}
};
Some(res)
}
fn blocks(&self) -> AstChildren<Block> {
children(self)
}
}
impl ExprStmt {
pub fn has_semi(&self) -> bool {
match self.syntax().last_child_or_token() {
None => false,
Some(node) => node.kind() == SEMI,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PathSegmentKind<'a> {
Name(&'a NameRef),
SelfKw,
SuperKw,
CrateKw,
}
impl PathSegment {
pub fn parent_path(&self) -> &Path {
self.syntax().parent().and_then(Path::cast).expect("segments are always nested in paths")
}
pub fn kind(&self) -> Option<PathSegmentKind> {
let res = if let Some(name_ref) = self.name_ref() {
PathSegmentKind::Name(name_ref)
} else {
match self.syntax().first_child_or_token()?.kind() {
SELF_KW => PathSegmentKind::SelfKw,
SUPER_KW => PathSegmentKind::SuperKw,
CRATE_KW => PathSegmentKind::CrateKw,
_ => return None,
}
};
Some(res)
}
pub fn has_colon_colon(&self) -> bool {
match self.syntax.first_child_or_token().map(|s| s.kind()) {
Some(COLONCOLON) => true,
_ => false,
}
}
}
impl Path {
pub fn parent_path(&self) -> Option<&Path> {
self.syntax().parent().and_then(Path::cast)
}
}
impl UseTree {
pub fn has_star(&self) -> bool {
self.syntax().children_with_tokens().any(|it| it.kind() == STAR)
}
}
impl UseTreeList {
pub fn parent_use_tree(&self) -> &UseTree {
self.syntax()
.parent()
.and_then(UseTree::cast)
.expect("UseTreeLists are always nested in UseTrees")
}
}
impl RefPat {
pub fn is_mut(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
}
}
fn child_opt<P: AstNode, C: AstNode>(parent: &P) -> Option<&C> {
children(parent).next()
}
@ -268,342 +73,6 @@ fn children<P: AstNode, C: AstNode>(parent: &P) -> AstChildren<C> {
AstChildren::new(parent.syntax())
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StructFlavor<'a> {
Tuple(&'a PosFieldDefList),
Named(&'a NamedFieldDefList),
Unit,
}
impl StructFlavor<'_> {
fn from_node<N: AstNode>(node: &N) -> StructFlavor {
if let Some(nfdl) = child_opt::<_, NamedFieldDefList>(node) {
StructFlavor::Named(nfdl)
} else if let Some(pfl) = child_opt::<_, PosFieldDefList>(node) {
StructFlavor::Tuple(pfl)
} else {
StructFlavor::Unit
}
}
}
impl StructDef {
pub fn flavor(&self) -> StructFlavor {
StructFlavor::from_node(self)
}
}
impl EnumVariant {
pub fn parent_enum(&self) -> &EnumDef {
self.syntax()
.parent()
.and_then(|it| it.parent())
.and_then(EnumDef::cast)
.expect("EnumVariants are always nested in Enums")
}
pub fn flavor(&self) -> StructFlavor {
StructFlavor::from_node(self)
}
}
impl PointerType {
pub fn is_mut(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
}
}
impl ReferenceType {
pub fn is_mut(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
}
}
impl RefExpr {
pub fn is_mut(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum PrefixOp {
/// The `*` operator for dereferencing
Deref,
/// The `!` operator for logical inversion
Not,
/// The `-` operator for negation
Neg,
}
impl PrefixExpr {
pub fn op_kind(&self) -> Option<PrefixOp> {
match self.op_token()?.kind() {
STAR => Some(PrefixOp::Deref),
EXCL => Some(PrefixOp::Not),
MINUS => Some(PrefixOp::Neg),
_ => None,
}
}
pub fn op_token(&self) -> Option<SyntaxToken> {
self.syntax().first_child_or_token()?.as_token()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum BinOp {
/// The `||` operator for boolean OR
BooleanOr,
/// The `&&` operator for boolean AND
BooleanAnd,
/// The `==` operator for equality testing
EqualityTest,
/// The `!=` operator for equality testing
NegatedEqualityTest,
/// The `<=` operator for lesser-equal testing
LesserEqualTest,
/// The `>=` operator for greater-equal testing
GreaterEqualTest,
/// The `<` operator for comparison
LesserTest,
/// The `>` operator for comparison
GreaterTest,
/// The `+` operator for addition
Addition,
/// The `*` operator for multiplication
Multiplication,
/// The `-` operator for subtraction
Subtraction,
/// The `/` operator for division
Division,
/// The `%` operator for remainder after division
Remainder,
/// The `<<` operator for left shift
LeftShift,
/// The `>>` operator for right shift
RightShift,
/// The `^` operator for bitwise XOR
BitwiseXor,
/// The `|` operator for bitwise OR
BitwiseOr,
/// The `&` operator for bitwise AND
BitwiseAnd,
/// The `..` operator for right-open ranges
RangeRightOpen,
/// The `..=` operator for right-closed ranges
RangeRightClosed,
/// The `=` operator for assignment
Assignment,
/// The `+=` operator for assignment after addition
AddAssign,
/// The `/=` operator for assignment after division
DivAssign,
/// The `*=` operator for assignment after multiplication
MulAssign,
/// The `%=` operator for assignment after remainders
RemAssign,
/// The `>>=` operator for assignment after shifting right
ShrAssign,
/// The `<<=` operator for assignment after shifting left
ShlAssign,
/// The `-=` operator for assignment after subtraction
SubAssign,
/// The `|=` operator for assignment after bitwise OR
BitOrAssign,
/// The `&=` operator for assignment after bitwise AND
BitAndAssign,
/// The `^=` operator for assignment after bitwise XOR
BitXorAssign,
}
impl BinExpr {
fn op_details(&self) -> Option<(SyntaxToken, BinOp)> {
self.syntax().children_with_tokens().filter_map(|it| it.as_token()).find_map(|c| {
match c.kind() {
PIPEPIPE => Some((c, BinOp::BooleanOr)),
AMPAMP => Some((c, BinOp::BooleanAnd)),
EQEQ => Some((c, BinOp::EqualityTest)),
NEQ => Some((c, BinOp::NegatedEqualityTest)),
LTEQ => Some((c, BinOp::LesserEqualTest)),
GTEQ => Some((c, BinOp::GreaterEqualTest)),
L_ANGLE => Some((c, BinOp::LesserTest)),
R_ANGLE => Some((c, BinOp::GreaterTest)),
PLUS => Some((c, BinOp::Addition)),
STAR => Some((c, BinOp::Multiplication)),
MINUS => Some((c, BinOp::Subtraction)),
SLASH => Some((c, BinOp::Division)),
PERCENT => Some((c, BinOp::Remainder)),
SHL => Some((c, BinOp::LeftShift)),
SHR => Some((c, BinOp::RightShift)),
CARET => Some((c, BinOp::BitwiseXor)),
PIPE => Some((c, BinOp::BitwiseOr)),
AMP => Some((c, BinOp::BitwiseAnd)),
DOTDOT => Some((c, BinOp::RangeRightOpen)),
DOTDOTEQ => Some((c, BinOp::RangeRightClosed)),
EQ => Some((c, BinOp::Assignment)),
PLUSEQ => Some((c, BinOp::AddAssign)),
SLASHEQ => Some((c, BinOp::DivAssign)),
STAREQ => Some((c, BinOp::MulAssign)),
PERCENTEQ => Some((c, BinOp::RemAssign)),
SHREQ => Some((c, BinOp::ShrAssign)),
SHLEQ => Some((c, BinOp::ShlAssign)),
MINUSEQ => Some((c, BinOp::SubAssign)),
PIPEEQ => Some((c, BinOp::BitOrAssign)),
AMPEQ => Some((c, BinOp::BitAndAssign)),
CARETEQ => Some((c, BinOp::BitXorAssign)),
_ => None,
}
})
}
pub fn op_kind(&self) -> Option<BinOp> {
self.op_details().map(|t| t.1)
}
pub fn op_token(&self) -> Option<SyntaxToken> {
self.op_details().map(|t| t.0)
}
pub fn lhs(&self) -> Option<&Expr> {
children(self).nth(0)
}
pub fn rhs(&self) -> Option<&Expr> {
children(self).nth(1)
}
pub fn sub_exprs(&self) -> (Option<&Expr>, Option<&Expr>) {
let mut children = children(self);
let first = children.next();
let second = children.next();
(first, second)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SelfParamFlavor {
/// self
Owned,
/// &self
Ref,
/// &mut self
MutRef,
}
impl SelfParam {
pub fn self_kw_token(&self) -> SyntaxToken {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.as_token())
.find(|it| it.kind() == SELF_KW)
.expect("invalid tree: self param must have self")
}
pub fn flavor(&self) -> SelfParamFlavor {
let borrowed = self.syntax().children_with_tokens().any(|n| n.kind() == AMP);
if borrowed {
// check for a `mut` coming after the & -- `mut &self` != `&mut self`
if self
.syntax()
.children_with_tokens()
.skip_while(|n| n.kind() != AMP)
.any(|n| n.kind() == MUT_KW)
{
SelfParamFlavor::MutRef
} else {
SelfParamFlavor::Ref
}
} else {
SelfParamFlavor::Owned
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum LiteralFlavor {
String,
ByteString,
Char,
Byte,
IntNumber { suffix: Option<SmolStr> },
FloatNumber { suffix: Option<SmolStr> },
Bool,
}
impl Literal {
pub fn token(&self) -> SyntaxToken {
match self.syntax().first_child_or_token().unwrap() {
SyntaxElement::Token(token) => token,
_ => unreachable!(),
}
}
pub fn flavor(&self) -> LiteralFlavor {
match self.token().kind() {
INT_NUMBER => {
let allowed_suffix_list = [
"isize", "i128", "i64", "i32", "i16", "i8", "usize", "u128", "u64", "u32",
"u16", "u8",
];
let text = self.token().text().to_string();
let suffix = allowed_suffix_list
.iter()
.find(|&s| text.ends_with(s))
.map(|&suf| SmolStr::new(suf));
LiteralFlavor::IntNumber { suffix }
}
FLOAT_NUMBER => {
let allowed_suffix_list = ["f64", "f32"];
let text = self.token().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!(),
}
}
}
impl NamedField {
pub fn parent_struct_lit(&self) -> &StructLit {
self.syntax().ancestors().find_map(StructLit::cast).unwrap()
}
}
impl BindPat {
pub fn is_mutable(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
}
pub fn is_ref(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == REF_KW)
}
}
impl LifetimeParam {
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.as_token())
.find(|it| it.kind() == LIFETIME)
}
}
impl WherePred {
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.as_token())
.find(|it| it.kind() == LIFETIME)
}
}
#[test]
fn test_doc_comment_none() {
let file = SourceFile::parse(

View File

@ -0,0 +1,252 @@
//! Various extension methods to ast Expr Nodes, which are hard to code-generate.
use crate::{
SyntaxToken, SyntaxElement, SmolStr,
ast::{self, AstNode, AstChildren, children, child_opt},
SyntaxKind::*
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ElseBranch<'a> {
Block(&'a ast::Block),
IfExpr(&'a ast::IfExpr),
}
impl ast::IfExpr {
pub fn then_branch(&self) -> Option<&ast::Block> {
self.blocks().nth(0)
}
pub fn else_branch(&self) -> Option<ElseBranch> {
let res = match self.blocks().nth(1) {
Some(block) => ElseBranch::Block(block),
None => {
let elif: &ast::IfExpr = child_opt(self)?;
ElseBranch::IfExpr(elif)
}
};
Some(res)
}
fn blocks(&self) -> AstChildren<ast::Block> {
children(self)
}
}
impl ast::RefExpr {
pub fn is_mut(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum PrefixOp {
/// The `*` operator for dereferencing
Deref,
/// The `!` operator for logical inversion
Not,
/// The `-` operator for negation
Neg,
}
impl ast::PrefixExpr {
pub fn op_kind(&self) -> Option<PrefixOp> {
match self.op_token()?.kind() {
STAR => Some(PrefixOp::Deref),
EXCL => Some(PrefixOp::Not),
MINUS => Some(PrefixOp::Neg),
_ => None,
}
}
pub fn op_token(&self) -> Option<SyntaxToken> {
self.syntax().first_child_or_token()?.as_token()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum BinOp {
/// The `||` operator for boolean OR
BooleanOr,
/// The `&&` operator for boolean AND
BooleanAnd,
/// The `==` operator for equality testing
EqualityTest,
/// The `!=` operator for equality testing
NegatedEqualityTest,
/// The `<=` operator for lesser-equal testing
LesserEqualTest,
/// The `>=` operator for greater-equal testing
GreaterEqualTest,
/// The `<` operator for comparison
LesserTest,
/// The `>` operator for comparison
GreaterTest,
/// The `+` operator for addition
Addition,
/// The `*` operator for multiplication
Multiplication,
/// The `-` operator for subtraction
Subtraction,
/// The `/` operator for division
Division,
/// The `%` operator for remainder after division
Remainder,
/// The `<<` operator for left shift
LeftShift,
/// The `>>` operator for right shift
RightShift,
/// The `^` operator for bitwise XOR
BitwiseXor,
/// The `|` operator for bitwise OR
BitwiseOr,
/// The `&` operator for bitwise AND
BitwiseAnd,
/// The `..` operator for right-open ranges
RangeRightOpen,
/// The `..=` operator for right-closed ranges
RangeRightClosed,
/// The `=` operator for assignment
Assignment,
/// The `+=` operator for assignment after addition
AddAssign,
/// The `/=` operator for assignment after division
DivAssign,
/// The `*=` operator for assignment after multiplication
MulAssign,
/// The `%=` operator for assignment after remainders
RemAssign,
/// The `>>=` operator for assignment after shifting right
ShrAssign,
/// The `<<=` operator for assignment after shifting left
ShlAssign,
/// The `-=` operator for assignment after subtraction
SubAssign,
/// The `|=` operator for assignment after bitwise OR
BitOrAssign,
/// The `&=` operator for assignment after bitwise AND
BitAndAssign,
/// The `^=` operator for assignment after bitwise XOR
BitXorAssign,
}
impl ast::BinExpr {
fn op_details(&self) -> Option<(SyntaxToken, BinOp)> {
self.syntax().children_with_tokens().filter_map(|it| it.as_token()).find_map(|c| {
match c.kind() {
PIPEPIPE => Some((c, BinOp::BooleanOr)),
AMPAMP => Some((c, BinOp::BooleanAnd)),
EQEQ => Some((c, BinOp::EqualityTest)),
NEQ => Some((c, BinOp::NegatedEqualityTest)),
LTEQ => Some((c, BinOp::LesserEqualTest)),
GTEQ => Some((c, BinOp::GreaterEqualTest)),
L_ANGLE => Some((c, BinOp::LesserTest)),
R_ANGLE => Some((c, BinOp::GreaterTest)),
PLUS => Some((c, BinOp::Addition)),
STAR => Some((c, BinOp::Multiplication)),
MINUS => Some((c, BinOp::Subtraction)),
SLASH => Some((c, BinOp::Division)),
PERCENT => Some((c, BinOp::Remainder)),
SHL => Some((c, BinOp::LeftShift)),
SHR => Some((c, BinOp::RightShift)),
CARET => Some((c, BinOp::BitwiseXor)),
PIPE => Some((c, BinOp::BitwiseOr)),
AMP => Some((c, BinOp::BitwiseAnd)),
DOTDOT => Some((c, BinOp::RangeRightOpen)),
DOTDOTEQ => Some((c, BinOp::RangeRightClosed)),
EQ => Some((c, BinOp::Assignment)),
PLUSEQ => Some((c, BinOp::AddAssign)),
SLASHEQ => Some((c, BinOp::DivAssign)),
STAREQ => Some((c, BinOp::MulAssign)),
PERCENTEQ => Some((c, BinOp::RemAssign)),
SHREQ => Some((c, BinOp::ShrAssign)),
SHLEQ => Some((c, BinOp::ShlAssign)),
MINUSEQ => Some((c, BinOp::SubAssign)),
PIPEEQ => Some((c, BinOp::BitOrAssign)),
AMPEQ => Some((c, BinOp::BitAndAssign)),
CARETEQ => Some((c, BinOp::BitXorAssign)),
_ => None,
}
})
}
pub fn op_kind(&self) -> Option<BinOp> {
self.op_details().map(|t| t.1)
}
pub fn op_token(&self) -> Option<SyntaxToken> {
self.op_details().map(|t| t.0)
}
pub fn lhs(&self) -> Option<&ast::Expr> {
children(self).nth(0)
}
pub fn rhs(&self) -> Option<&ast::Expr> {
children(self).nth(1)
}
pub fn sub_exprs(&self) -> (Option<&ast::Expr>, Option<&ast::Expr>) {
let mut children = children(self);
let first = children.next();
let second = children.next();
(first, second)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum LiteralKind {
String,
ByteString,
Char,
Byte,
IntNumber { suffix: Option<SmolStr> },
FloatNumber { suffix: Option<SmolStr> },
Bool,
}
impl ast::Literal {
pub fn token(&self) -> SyntaxToken {
match self.syntax().first_child_or_token().unwrap() {
SyntaxElement::Token(token) => token,
_ => unreachable!(),
}
}
pub fn kind(&self) -> LiteralKind {
match self.token().kind() {
INT_NUMBER => {
let allowed_suffix_list = [
"isize", "i128", "i64", "i32", "i16", "i8", "usize", "u128", "u64", "u32",
"u16", "u8",
];
let text = self.token().text().to_string();
let suffix = allowed_suffix_list
.iter()
.find(|&s| text.ends_with(s))
.map(|&suf| SmolStr::new(suf));
LiteralKind::IntNumber { suffix }
}
FLOAT_NUMBER => {
let allowed_suffix_list = ["f64", "f32"];
let text = self.token().text().to_string();
let suffix = allowed_suffix_list
.iter()
.find(|&s| text.ends_with(s))
.map(|&suf| SmolStr::new(suf));
LiteralKind::FloatNumber { suffix: suffix }
}
STRING | RAW_STRING => LiteralKind::String,
TRUE_KW | FALSE_KW => LiteralKind::Bool,
BYTE_STRING | RAW_BYTE_STRING => LiteralKind::ByteString,
CHAR => LiteralKind::Char,
BYTE => LiteralKind::Byte,
_ => unreachable!(),
}
}
}
impl ast::NamedField {
pub fn parent_struct_lit(&self) -> &ast::StructLit {
self.syntax().ancestors().find_map(ast::StructLit::cast).unwrap()
}
}

View File

@ -0,0 +1,303 @@
//! Various extension methods to ast Nodes, which are hard to code-generate.
//! Extensions for various expressions live in a sibling `expr_extensions` module.
use itertools::Itertools;
use crate::{
SmolStr, SyntaxToken,
ast::{self, AstNode, children, child_opt},
SyntaxKind::*,
};
impl ast::Name {
pub fn text(&self) -> &SmolStr {
let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap();
ident.text()
}
}
impl ast::NameRef {
pub fn text(&self) -> &SmolStr {
let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap();
ident.text()
}
}
impl ast::Attr {
pub fn is_inner(&self) -> bool {
let tt = match self.value() {
None => return false,
Some(tt) => tt,
};
let prev = match tt.syntax().prev_sibling() {
None => return false,
Some(prev) => prev,
};
prev.kind() == EXCL
}
pub fn as_atom(&self) -> Option<SmolStr> {
let tt = self.value()?;
let (_bra, attr, _ket) = tt.syntax().children_with_tokens().collect_tuple()?;
if attr.kind() == IDENT {
Some(attr.as_token()?.text().clone())
} else {
None
}
}
pub fn as_call(&self) -> Option<(SmolStr, &ast::TokenTree)> {
let tt = self.value()?;
let (_bra, attr, args, _ket) = tt.syntax().children_with_tokens().collect_tuple()?;
let args = ast::TokenTree::cast(args.as_node()?)?;
if attr.kind() == IDENT {
Some((attr.as_token()?.text().clone(), args))
} else {
None
}
}
pub fn as_named(&self) -> Option<SmolStr> {
let tt = self.value()?;
let attr = tt.syntax().children_with_tokens().nth(1)?;
if attr.kind() == IDENT {
Some(attr.as_token()?.text().clone())
} else {
None
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PathSegmentKind<'a> {
Name(&'a ast::NameRef),
SelfKw,
SuperKw,
CrateKw,
}
impl ast::PathSegment {
pub fn parent_path(&self) -> &ast::Path {
self.syntax()
.parent()
.and_then(ast::Path::cast)
.expect("segments are always nested in paths")
}
pub fn kind(&self) -> Option<PathSegmentKind> {
let res = if let Some(name_ref) = self.name_ref() {
PathSegmentKind::Name(name_ref)
} else {
match self.syntax().first_child_or_token()?.kind() {
SELF_KW => PathSegmentKind::SelfKw,
SUPER_KW => PathSegmentKind::SuperKw,
CRATE_KW => PathSegmentKind::CrateKw,
_ => return None,
}
};
Some(res)
}
pub fn has_colon_colon(&self) -> bool {
match self.syntax.first_child_or_token().map(|s| s.kind()) {
Some(COLONCOLON) => true,
_ => false,
}
}
}
impl ast::Path {
pub fn parent_path(&self) -> Option<&ast::Path> {
self.syntax().parent().and_then(ast::Path::cast)
}
}
impl ast::Module {
pub fn has_semi(&self) -> bool {
match self.syntax().last_child_or_token() {
None => false,
Some(node) => node.kind() == SEMI,
}
}
}
impl ast::UseTree {
pub fn has_star(&self) -> bool {
self.syntax().children_with_tokens().any(|it| it.kind() == STAR)
}
}
impl ast::UseTreeList {
pub fn parent_use_tree(&self) -> &ast::UseTree {
self.syntax()
.parent()
.and_then(ast::UseTree::cast)
.expect("UseTreeLists are always nested in UseTrees")
}
}
impl ast::ImplBlock {
pub fn target_type(&self) -> Option<&ast::TypeRef> {
match self.target() {
(Some(t), None) | (_, Some(t)) => Some(t),
_ => None,
}
}
pub fn target_trait(&self) -> Option<&ast::TypeRef> {
match self.target() {
(Some(t), Some(_)) => Some(t),
_ => None,
}
}
fn target(&self) -> (Option<&ast::TypeRef>, Option<&ast::TypeRef>) {
let mut types = children(self);
let first = types.next();
let second = types.next();
(first, second)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StructKind<'a> {
Tuple(&'a ast::PosFieldDefList),
Named(&'a ast::NamedFieldDefList),
Unit,
}
impl StructKind<'_> {
fn from_node<N: AstNode>(node: &N) -> StructKind {
if let Some(nfdl) = child_opt::<_, ast::NamedFieldDefList>(node) {
StructKind::Named(nfdl)
} else if let Some(pfl) = child_opt::<_, ast::PosFieldDefList>(node) {
StructKind::Tuple(pfl)
} else {
StructKind::Unit
}
}
}
impl ast::StructDef {
pub fn kind(&self) -> StructKind {
StructKind::from_node(self)
}
}
impl ast::EnumVariant {
pub fn parent_enum(&self) -> &ast::EnumDef {
self.syntax()
.parent()
.and_then(|it| it.parent())
.and_then(ast::EnumDef::cast)
.expect("EnumVariants are always nested in Enums")
}
pub fn kind(&self) -> StructKind {
StructKind::from_node(self)
}
}
impl ast::LetStmt {
pub fn has_semi(&self) -> bool {
match self.syntax().last_child_or_token() {
None => false,
Some(node) => node.kind() == SEMI,
}
}
}
impl ast::ExprStmt {
pub fn has_semi(&self) -> bool {
match self.syntax().last_child_or_token() {
None => false,
Some(node) => node.kind() == SEMI,
}
}
}
impl ast::RefPat {
pub fn is_mut(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
}
}
impl ast::BindPat {
pub fn is_mutable(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
}
pub fn is_ref(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == REF_KW)
}
}
impl ast::PointerType {
pub fn is_mut(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
}
}
impl ast::ReferenceType {
pub fn is_mut(&self) -> bool {
self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SelfParamKind {
/// self
Owned,
/// &self
Ref,
/// &mut self
MutRef,
}
impl ast::SelfParam {
pub fn self_kw_token(&self) -> SyntaxToken {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.as_token())
.find(|it| it.kind() == SELF_KW)
.expect("invalid tree: self param must have self")
}
pub fn kind(&self) -> SelfParamKind {
let borrowed = self.syntax().children_with_tokens().any(|n| n.kind() == AMP);
if borrowed {
// check for a `mut` coming after the & -- `mut &self` != `&mut self`
if self
.syntax()
.children_with_tokens()
.skip_while(|n| n.kind() != AMP)
.any(|n| n.kind() == MUT_KW)
{
SelfParamKind::MutRef
} else {
SelfParamKind::Ref
}
} else {
SelfParamKind::Owned
}
}
}
impl ast::LifetimeParam {
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.as_token())
.find(|it| it.kind() == LIFETIME)
}
}
impl ast::WherePred {
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.as_token())
.find(|it| it.kind() == LIFETIME)
}
}

View File

@ -1,3 +1,5 @@
//! There are many AstNodes, but only a few tokens, so we hand-write them here.
use crate::{
SyntaxToken,
SyntaxKind::{COMMENT, WHITESPACE},

View File

@ -1,3 +1,7 @@
//! Various traits that are implemented by ast nodes.
//!
//! The implementations are usually trivial, and live in generated.rs
use itertools::Itertools;
use crate::{