From cb5001c0a5ddadccd18fe787d89de3d6c3c8147f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 2 Apr 2019 12:47:39 +0300 Subject: move extensions to submodules --- crates/ra_syntax/src/ast.rs | 565 +--------------------------- crates/ra_syntax/src/ast/expr_extensions.rs | 250 ++++++++++++ crates/ra_syntax/src/ast/extensions.rs | 300 +++++++++++++++ 3 files changed, 566 insertions(+), 549 deletions(-) create mode 100644 crates/ra_syntax/src/ast/expr_extensions.rs create mode 100644 crates/ra_syntax/src/ast/extensions.rs diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index b0e0f8bf9..74a415bdd 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -2,21 +2,22 @@ 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, StructFlavor, SelfParamFlavor}, + expr_extensions::{ElseBranch, PrefixOp, BinOp, LiteralFlavor}, }; /// The main trait to go from untyped `SyntaxNode` to a typed ast. The @@ -32,6 +33,17 @@ 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 + where + Self: Sized; + fn syntax(&self) -> SyntaxToken<'a>; + fn text(&self) -> &'a SmolStr { + self.syntax().text() + } +} + #[derive(Debug)] pub struct AstChildren<'a, N> { inner: SyntaxNodeChildren<'a>, @@ -51,215 +63,6 @@ impl<'a, N: AstNode + 'a> Iterator for AstChildren<'a, N> { } } -pub trait AstToken<'a> { - fn cast(token: SyntaxToken<'a>) -> Option - 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 { - 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 { - 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 ElseBranch<'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 { - let res = match self.blocks().nth(1) { - Some(block) => ElseBranch::Block(block), - None => { - let elif: &IfExpr = child_opt(self)?; - ElseBranch::IfExpr(elif) - } - }; - Some(res) - } - - fn blocks(&self) -> AstChildren { - 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 { - 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(parent: &P) -> Option<&C> { children(parent).next() } @@ -268,342 +71,6 @@ fn children(parent: &P) -> AstChildren { 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(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 { - match self.op_token()?.kind() { - STAR => Some(PrefixOp::Deref), - EXCL => Some(PrefixOp::Not), - MINUS => Some(PrefixOp::Neg), - _ => None, - } - } - - pub fn op_token(&self) -> Option { - 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 { - self.op_details().map(|t| t.1) - } - - pub fn op_token(&self) -> Option { - 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 }, - FloatNumber { suffix: Option }, - 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 { - self.syntax() - .children_with_tokens() - .filter_map(|it| it.as_token()) - .find(|it| it.kind() == LIFETIME) - } -} - -impl WherePred { - pub fn lifetime_token(&self) -> Option { - 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( diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs new file mode 100644 index 000000000..ddc26206f --- /dev/null +++ b/crates/ra_syntax/src/ast/expr_extensions.rs @@ -0,0 +1,250 @@ +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 { + 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 { + 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 { + match self.op_token()?.kind() { + STAR => Some(PrefixOp::Deref), + EXCL => Some(PrefixOp::Not), + MINUS => Some(PrefixOp::Neg), + _ => None, + } + } + + pub fn op_token(&self) -> Option { + 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 { + self.op_details().map(|t| t.1) + } + + pub fn op_token(&self) -> Option { + 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 LiteralFlavor { + String, + ByteString, + Char, + Byte, + IntNumber { suffix: Option }, + FloatNumber { suffix: Option }, + Bool, +} + +impl ast::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 ast::NamedField { + pub fn parent_struct_lit(&self) -> &ast::StructLit { + self.syntax().ancestors().find_map(ast::StructLit::cast).unwrap() + } +} diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs new file mode 100644 index 000000000..87592bfd8 --- /dev/null +++ b/crates/ra_syntax/src/ast/extensions.rs @@ -0,0 +1,300 @@ +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 { + 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 { + 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 { + 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 StructFlavor<'a> { + Tuple(&'a ast::PosFieldDefList), + Named(&'a ast::NamedFieldDefList), + Unit, +} + +impl StructFlavor<'_> { + fn from_node(node: &N) -> StructFlavor { + if let Some(nfdl) = child_opt::<_, ast::NamedFieldDefList>(node) { + StructFlavor::Named(nfdl) + } else if let Some(pfl) = child_opt::<_, ast::PosFieldDefList>(node) { + StructFlavor::Tuple(pfl) + } else { + StructFlavor::Unit + } + } +} + +impl ast::StructDef { + pub fn flavor(&self) -> StructFlavor { + StructFlavor::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 flavor(&self) -> StructFlavor { + StructFlavor::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 SelfParamFlavor { + /// 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 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 + } + } +} + +impl ast::LifetimeParam { + pub fn lifetime_token(&self) -> Option { + 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 { + self.syntax() + .children_with_tokens() + .filter_map(|it| it.as_token()) + .find(|it| it.kind() == LIFETIME) + } +} -- cgit v1.2.3