2019-04-02 05:02:23 -05:00
|
|
|
//! Various extension methods to ast Nodes, which are hard to code-generate.
|
|
|
|
//! Extensions for various expressions live in a sibling `expr_extensions` module.
|
|
|
|
|
2019-04-02 04:47:39 -05:00
|
|
|
use itertools::Itertools;
|
|
|
|
|
2019-05-20 17:04:20 -05:00
|
|
|
use crate::{
|
2019-07-18 11:23:05 -05:00
|
|
|
ast::{self, child_opt, children, AstNode, SyntaxNode},
|
2019-07-04 15:05:17 -05:00
|
|
|
SmolStr, SyntaxElement,
|
2019-05-20 17:04:20 -05:00
|
|
|
SyntaxKind::*,
|
2019-07-04 15:05:17 -05:00
|
|
|
SyntaxToken, T,
|
2019-05-20 17:04:20 -05:00
|
|
|
};
|
2019-04-05 15:34:45 -05:00
|
|
|
use ra_parser::SyntaxKind;
|
2019-04-02 04:47:39 -05:00
|
|
|
|
|
|
|
impl ast::Name {
|
|
|
|
pub fn text(&self) -> &SmolStr {
|
2019-07-18 11:23:05 -05:00
|
|
|
text_of_first_token(self.syntax())
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::NameRef {
|
|
|
|
pub fn text(&self) -> &SmolStr {
|
2019-07-18 11:23:05 -05:00
|
|
|
text_of_first_token(self.syntax())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn text_of_first_token(node: &SyntaxNode) -> &SmolStr {
|
|
|
|
match node.0.green().children().first() {
|
|
|
|
Some(rowan::GreenElement::Token(it)) => it.text(),
|
|
|
|
_ => panic!(),
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2019-05-15 07:35:47 -05:00
|
|
|
prev.kind() == T![!]
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-18 11:23:05 -05:00
|
|
|
pub fn as_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
|
2019-04-02 04:47:39 -05:00
|
|
|
let tt = self.value()?;
|
|
|
|
let (_bra, attr, args, _ket) = tt.syntax().children_with_tokens().collect_tuple()?;
|
2019-07-18 11:23:05 -05:00
|
|
|
let args = ast::TokenTree::cast(args.as_node()?.clone())?;
|
2019-04-02 04:47:39 -05:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2019-04-14 17:03:54 -05:00
|
|
|
|
|
|
|
pub fn as_key_value(&self) -> Option<(SmolStr, SmolStr)> {
|
|
|
|
let tt = self.value()?;
|
|
|
|
let tt_node = tt.syntax();
|
|
|
|
let attr = tt_node.children_with_tokens().nth(1)?;
|
|
|
|
if attr.kind() == IDENT {
|
|
|
|
let key = attr.as_token()?.text().clone();
|
|
|
|
let val_node = tt_node.children_with_tokens().find(|t| t.kind() == STRING)?;
|
2019-06-04 01:38:13 -05:00
|
|
|
let val = val_node.as_token()?.text().trim_start_matches('"').trim_end_matches('"');
|
2019-04-14 17:03:54 -05:00
|
|
|
Some((key, SmolStr::new(val)))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
|
2019-07-18 11:23:05 -05:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub enum PathSegmentKind {
|
|
|
|
Name(ast::NameRef),
|
2019-04-02 04:47:39 -05:00
|
|
|
SelfKw,
|
|
|
|
SuperKw,
|
|
|
|
CrateKw,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::PathSegment {
|
2019-07-18 11:23:05 -05:00
|
|
|
pub fn parent_path(&self) -> ast::Path {
|
2019-04-02 04:47:39 -05:00
|
|
|
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() {
|
2019-05-15 07:35:47 -05:00
|
|
|
T![self] => PathSegmentKind::SelfKw,
|
|
|
|
T![super] => PathSegmentKind::SuperKw,
|
|
|
|
T![crate] => PathSegmentKind::CrateKw,
|
2019-04-02 04:47:39 -05:00
|
|
|
_ => return None,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Some(res)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn has_colon_colon(&self) -> bool {
|
|
|
|
match self.syntax.first_child_or_token().map(|s| s.kind()) {
|
2019-05-15 07:35:47 -05:00
|
|
|
Some(T![::]) => true,
|
2019-04-02 04:47:39 -05:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::Path {
|
2019-07-18 11:23:05 -05:00
|
|
|
pub fn parent_path(&self) -> Option<ast::Path> {
|
2019-04-02 04:47:39 -05:00
|
|
|
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,
|
2019-05-15 07:35:47 -05:00
|
|
|
Some(node) => node.kind() == T![;],
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::UseTree {
|
|
|
|
pub fn has_star(&self) -> bool {
|
2019-05-15 07:35:47 -05:00
|
|
|
self.syntax().children_with_tokens().any(|it| it.kind() == T![*])
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::UseTreeList {
|
2019-07-18 11:23:05 -05:00
|
|
|
pub fn parent_use_tree(&self) -> ast::UseTree {
|
2019-04-02 04:47:39 -05:00
|
|
|
self.syntax()
|
|
|
|
.parent()
|
|
|
|
.and_then(ast::UseTree::cast)
|
|
|
|
.expect("UseTreeLists are always nested in UseTrees")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::ImplBlock {
|
2019-07-18 11:23:05 -05:00
|
|
|
pub fn target_type(&self) -> Option<ast::TypeRef> {
|
2019-04-02 04:47:39 -05:00
|
|
|
match self.target() {
|
|
|
|
(Some(t), None) | (_, Some(t)) => Some(t),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-18 11:23:05 -05:00
|
|
|
pub fn target_trait(&self) -> Option<ast::TypeRef> {
|
2019-04-02 04:47:39 -05:00
|
|
|
match self.target() {
|
|
|
|
(Some(t), Some(_)) => Some(t),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-18 11:23:05 -05:00
|
|
|
fn target(&self) -> (Option<ast::TypeRef>, Option<ast::TypeRef>) {
|
2019-04-02 04:47:39 -05:00
|
|
|
let mut types = children(self);
|
|
|
|
let first = types.next();
|
|
|
|
let second = types.next();
|
|
|
|
(first, second)
|
|
|
|
}
|
2019-05-07 11:53:16 -05:00
|
|
|
|
|
|
|
pub fn is_negative(&self) -> bool {
|
2019-05-15 07:35:47 -05:00
|
|
|
self.syntax().children_with_tokens().any(|t| t.kind() == T![!])
|
2019-05-07 11:53:16 -05:00
|
|
|
}
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
2019-07-18 11:23:05 -05:00
|
|
|
pub enum StructKind {
|
|
|
|
Tuple(ast::PosFieldDefList),
|
|
|
|
Named(ast::NamedFieldDefList),
|
2019-04-02 04:47:39 -05:00
|
|
|
Unit,
|
|
|
|
}
|
|
|
|
|
2019-07-18 11:23:05 -05:00
|
|
|
impl StructKind {
|
2019-04-02 04:48:14 -05:00
|
|
|
fn from_node<N: AstNode>(node: &N) -> StructKind {
|
2019-04-02 04:47:39 -05:00
|
|
|
if let Some(nfdl) = child_opt::<_, ast::NamedFieldDefList>(node) {
|
2019-04-02 04:48:14 -05:00
|
|
|
StructKind::Named(nfdl)
|
2019-04-02 04:47:39 -05:00
|
|
|
} else if let Some(pfl) = child_opt::<_, ast::PosFieldDefList>(node) {
|
2019-04-02 04:48:14 -05:00
|
|
|
StructKind::Tuple(pfl)
|
2019-04-02 04:47:39 -05:00
|
|
|
} else {
|
2019-04-02 04:48:14 -05:00
|
|
|
StructKind::Unit
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::StructDef {
|
2019-05-20 17:04:20 -05:00
|
|
|
pub fn is_union(&self) -> bool {
|
|
|
|
for child in self.syntax().children_with_tokens() {
|
|
|
|
match child.kind() {
|
|
|
|
T![struct] => return false,
|
|
|
|
T![union] => return true,
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2019-04-02 04:48:14 -05:00
|
|
|
pub fn kind(&self) -> StructKind {
|
|
|
|
StructKind::from_node(self)
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::EnumVariant {
|
2019-07-18 11:23:05 -05:00
|
|
|
pub fn parent_enum(&self) -> ast::EnumDef {
|
2019-04-02 04:47:39 -05:00
|
|
|
self.syntax()
|
|
|
|
.parent()
|
|
|
|
.and_then(|it| it.parent())
|
|
|
|
.and_then(ast::EnumDef::cast)
|
|
|
|
.expect("EnumVariants are always nested in Enums")
|
|
|
|
}
|
2019-04-02 04:48:14 -05:00
|
|
|
pub fn kind(&self) -> StructKind {
|
|
|
|
StructKind::from_node(self)
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-22 05:01:33 -05:00
|
|
|
impl ast::FnDef {
|
2019-07-18 11:23:05 -05:00
|
|
|
pub fn semicolon_token(&self) -> Option<SyntaxToken> {
|
2019-04-22 05:01:33 -05:00
|
|
|
self.syntax()
|
|
|
|
.last_child_or_token()
|
2019-07-18 11:23:05 -05:00
|
|
|
.and_then(|it| it.as_token().cloned())
|
2019-05-15 07:35:47 -05:00
|
|
|
.filter(|it| it.kind() == T![;])
|
2019-04-22 05:01:33 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-02 04:47:39 -05:00
|
|
|
impl ast::LetStmt {
|
|
|
|
pub fn has_semi(&self) -> bool {
|
|
|
|
match self.syntax().last_child_or_token() {
|
|
|
|
None => false,
|
2019-05-15 07:35:47 -05:00
|
|
|
Some(node) => node.kind() == T![;],
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::ExprStmt {
|
|
|
|
pub fn has_semi(&self) -> bool {
|
|
|
|
match self.syntax().last_child_or_token() {
|
|
|
|
None => false,
|
2019-05-15 07:35:47 -05:00
|
|
|
Some(node) => node.kind() == T![;],
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-05 15:34:45 -05:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
2019-07-18 11:23:05 -05:00
|
|
|
pub enum FieldKind {
|
|
|
|
Name(ast::NameRef),
|
|
|
|
Index(SyntaxToken),
|
2019-04-05 15:34:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::FieldExpr {
|
|
|
|
pub fn index_token(&self) -> Option<SyntaxToken> {
|
|
|
|
self.syntax
|
|
|
|
.children_with_tokens()
|
|
|
|
// FIXME: Accepting floats here to reject them in validation later
|
|
|
|
.find(|c| c.kind() == SyntaxKind::INT_NUMBER || c.kind() == SyntaxKind::FLOAT_NUMBER)
|
|
|
|
.as_ref()
|
|
|
|
.and_then(SyntaxElement::as_token)
|
2019-07-18 11:23:05 -05:00
|
|
|
.cloned()
|
2019-04-05 15:34:45 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn field_access(&self) -> Option<FieldKind> {
|
|
|
|
if let Some(nr) = self.name_ref() {
|
|
|
|
Some(FieldKind::Name(nr))
|
|
|
|
} else if let Some(tok) = self.index_token() {
|
|
|
|
Some(FieldKind::Index(tok))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-02 04:47:39 -05:00
|
|
|
impl ast::RefPat {
|
|
|
|
pub fn is_mut(&self) -> bool {
|
2019-05-15 07:35:47 -05:00
|
|
|
self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::BindPat {
|
|
|
|
pub fn is_mutable(&self) -> bool {
|
2019-05-15 07:35:47 -05:00
|
|
|
self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_ref(&self) -> bool {
|
2019-05-15 07:35:47 -05:00
|
|
|
self.syntax().children_with_tokens().any(|n| n.kind() == T![ref])
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::PointerType {
|
|
|
|
pub fn is_mut(&self) -> bool {
|
2019-05-15 07:35:47 -05:00
|
|
|
self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::ReferenceType {
|
|
|
|
pub fn is_mut(&self) -> bool {
|
2019-05-15 07:35:47 -05:00
|
|
|
self.syntax().children_with_tokens().any(|n| n.kind() == T![mut])
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
2019-04-02 04:48:14 -05:00
|
|
|
pub enum SelfParamKind {
|
2019-04-02 04:47:39 -05:00
|
|
|
/// self
|
|
|
|
Owned,
|
|
|
|
/// &self
|
|
|
|
Ref,
|
|
|
|
/// &mut self
|
|
|
|
MutRef,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::SelfParam {
|
|
|
|
pub fn self_kw_token(&self) -> SyntaxToken {
|
|
|
|
self.syntax()
|
|
|
|
.children_with_tokens()
|
2019-07-18 11:23:05 -05:00
|
|
|
.filter_map(|it| it.as_token().cloned())
|
2019-05-15 07:35:47 -05:00
|
|
|
.find(|it| it.kind() == T![self])
|
2019-04-02 04:47:39 -05:00
|
|
|
.expect("invalid tree: self param must have self")
|
|
|
|
}
|
|
|
|
|
2019-04-02 04:48:14 -05:00
|
|
|
pub fn kind(&self) -> SelfParamKind {
|
2019-05-15 07:35:47 -05:00
|
|
|
let borrowed = self.syntax().children_with_tokens().any(|n| n.kind() == T![&]);
|
2019-04-02 04:47:39 -05:00
|
|
|
if borrowed {
|
|
|
|
// check for a `mut` coming after the & -- `mut &self` != `&mut self`
|
|
|
|
if self
|
|
|
|
.syntax()
|
|
|
|
.children_with_tokens()
|
2019-05-15 07:35:47 -05:00
|
|
|
.skip_while(|n| n.kind() != T![&])
|
|
|
|
.any(|n| n.kind() == T![mut])
|
2019-04-02 04:47:39 -05:00
|
|
|
{
|
2019-04-02 04:48:14 -05:00
|
|
|
SelfParamKind::MutRef
|
2019-04-02 04:47:39 -05:00
|
|
|
} else {
|
2019-04-02 04:48:14 -05:00
|
|
|
SelfParamKind::Ref
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
} else {
|
2019-04-02 04:48:14 -05:00
|
|
|
SelfParamKind::Owned
|
2019-04-02 04:47:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::LifetimeParam {
|
|
|
|
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
|
|
|
|
self.syntax()
|
|
|
|
.children_with_tokens()
|
2019-07-18 11:23:05 -05:00
|
|
|
.filter_map(|it| it.as_token().cloned())
|
2019-04-02 04:47:39 -05:00
|
|
|
.find(|it| it.kind() == LIFETIME)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ast::WherePred {
|
|
|
|
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
|
|
|
|
self.syntax()
|
|
|
|
.children_with_tokens()
|
2019-07-18 11:23:05 -05:00
|
|
|
.filter_map(|it| it.as_token().cloned())
|
2019-04-02 04:47:39 -05:00
|
|
|
.find(|it| it.kind() == LIFETIME)
|
|
|
|
}
|
|
|
|
}
|
2019-05-07 11:53:16 -05:00
|
|
|
|
|
|
|
impl ast::TraitDef {
|
|
|
|
pub fn is_auto(&self) -> bool {
|
2019-05-15 07:35:47 -05:00
|
|
|
self.syntax().children_with_tokens().any(|t| t.kind() == T![auto])
|
2019-05-07 11:53:16 -05:00
|
|
|
}
|
|
|
|
}
|