rust/crates/ra_syntax/src/ast.rs

403 lines
10 KiB
Rust
Raw Normal View History

2019-02-21 06:24:42 -06:00
//! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s
2019-04-02 05:02:23 -05:00
2018-08-09 09:43:39 -05:00
mod generated;
2019-04-02 02:03:19 -05:00
mod traits;
2019-04-02 02:23:18 -05:00
mod tokens;
2019-04-02 04:47:39 -05:00
mod extensions;
mod expr_extensions;
2019-09-28 11:50:16 -05:00
pub mod edit;
2019-09-26 04:18:26 -05:00
pub mod make;
2018-08-09 09:43:39 -05:00
2018-09-07 17:16:07 -05:00
use std::marker::PhantomData;
2018-10-15 11:55:32 -05:00
use crate::{
syntax_node::{
NodeOrToken, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren,
SyntaxToken,
},
2019-07-19 10:22:00 -05:00
SmolStr, SyntaxKind,
2018-08-09 08:03:21 -05:00
};
2018-07-30 13:58:49 -05:00
2019-04-02 02:03:19 -05:00
pub use self::{
2019-11-15 14:05:29 -06:00
expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp},
extensions::{
2020-02-12 08:44:52 -06:00
AttrKind, FieldKind, PathSegmentKind, SelfParamKind, SlicePatComponents, StructKind,
TypeBoundKind, VisibilityKind,
},
2019-04-02 02:03:19 -05:00
generated::*,
2019-04-02 02:23:18 -05:00
tokens::*,
traits::*,
2019-04-02 02:03:19 -05:00
};
2018-11-06 13:06:58 -06:00
/// The main trait to go from untyped `SyntaxNode` to a typed ast. The
/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
/// the same representation: a pointer to the tree root and a pointer to the
/// node itself.
pub trait AstNode: AstElement {
2019-08-22 07:20:07 -05:00
fn can_cast(kind: SyntaxKind) -> bool
where
Self: Sized;
2019-07-19 10:22:00 -05:00
fn cast_or_return(syntax: SyntaxNode) -> Result<Self, SyntaxNode>
where
Self: Sized;
2019-08-22 07:20:07 -05:00
fn cast(syntax: SyntaxNode) -> Option<Self>
where
Self: Sized,
{
<Self as AstNode>::cast_or_return(syntax).ok()
}
2019-01-07 07:15:47 -06:00
fn syntax(&self) -> &SyntaxNode;
fn into_syntax(self) -> SyntaxNode;
2018-08-09 08:03:21 -05:00
}
2019-08-22 07:20:07 -05:00
#[test]
fn assert_ast_is_object_safe() {
fn _f(_: &dyn AstNode, _: &dyn NameOwner) {}
}
2019-04-02 04:47:39 -05:00
/// Like `AstNode`, but wraps tokens rather than interior nodes.
pub trait AstToken: AstElement {
fn can_cast(token: SyntaxKind) -> bool
2019-04-02 04:47:39 -05:00
where
Self: Sized;
fn cast_or_return(syntax: SyntaxToken) -> Result<Self, SyntaxToken>
where
Self: Sized;
fn cast(syntax: SyntaxToken) -> Option<Self>
where
Self: Sized,
{
<Self as AstToken>::cast_or_return(syntax).ok()
}
2019-07-18 11:23:05 -05:00
fn syntax(&self) -> &SyntaxToken;
fn into_syntax(self) -> SyntaxToken;
2019-07-18 11:23:05 -05:00
fn text(&self) -> &SmolStr {
2019-04-02 04:47:39 -05:00
self.syntax().text()
}
}
/// Like `AstNode`, but wraps either nodes or tokens rather than interior nodes.
pub trait AstElement: std::fmt::Display {
fn can_cast_element(kind: SyntaxKind) -> bool
where
Self: Sized;
fn cast_or_return_element(syntax: SyntaxElement) -> Result<Self, SyntaxElement>
where
Self: Sized;
fn cast_element(syntax: SyntaxElement) -> Option<Self>
where
Self: Sized,
{
<Self as AstElement>::cast_or_return_element(syntax).ok()
}
fn syntax_element(&self) -> NodeOrToken<&SyntaxNode, &SyntaxToken>;
fn into_syntax_element(self) -> SyntaxElement;
}
2019-04-02 05:02:23 -05:00
/// An iterator over `SyntaxNode` children of a particular AST type.
2020-02-29 16:24:50 -06:00
#[derive(Debug, Clone)]
2019-07-18 11:23:05 -05:00
pub struct AstChildren<N> {
inner: SyntaxNodeChildren,
2019-04-02 02:09:52 -05:00
ph: PhantomData<N>,
}
2019-07-18 11:23:05 -05:00
impl<N> AstChildren<N> {
fn new(parent: &SyntaxNode) -> Self {
2019-04-02 02:09:52 -05:00
AstChildren { inner: parent.children(), ph: PhantomData }
}
}
2019-07-18 11:23:05 -05:00
impl<N: AstNode> Iterator for AstChildren<N> {
type Item = N;
fn next(&mut self) -> Option<N> {
2019-04-02 02:09:52 -05:00
self.inner.by_ref().find_map(N::cast)
}
}
2019-07-18 11:23:05 -05:00
fn child_opt<P: AstNode + ?Sized, C: AstNode>(parent: &P) -> Option<C> {
2018-08-22 09:01:51 -05:00
children(parent).next()
}
2019-07-18 11:23:05 -05:00
fn children<P: AstNode + ?Sized, C: AstNode>(parent: &P) -> AstChildren<C> {
2018-09-07 17:35:20 -05:00
AstChildren::new(parent.syntax())
2018-09-07 17:16:07 -05:00
}
/// An iterator over `SyntaxToken` children of a particular AST type.
#[derive(Debug, Clone)]
pub struct AstChildTokens<N> {
inner: SyntaxElementChildren,
ph: PhantomData<N>,
}
impl<N> AstChildTokens<N> {
fn new(parent: &SyntaxNode) -> Self {
AstChildTokens { inner: parent.children_with_tokens(), ph: PhantomData }
}
}
impl<N: AstToken> Iterator for AstChildTokens<N> {
type Item = N;
fn next(&mut self) -> Option<N> {
self.inner.by_ref().filter_map(|x| x.into_token()).find_map(N::cast)
}
}
fn child_token_opt<P: AstNode + ?Sized, C: AstToken>(parent: &P) -> Option<C> {
child_tokens(parent).next()
}
fn child_tokens<P: AstNode + ?Sized, C: AstToken>(parent: &P) -> AstChildTokens<C> {
AstChildTokens::new(parent.syntax())
}
/// An iterator over `SyntaxNode` children of a particular AST type.
#[derive(Debug, Clone)]
pub struct AstChildElements<N> {
inner: SyntaxElementChildren,
ph: PhantomData<N>,
}
impl<N> AstChildElements<N> {
fn new(parent: &SyntaxNode) -> Self {
AstChildElements { inner: parent.children_with_tokens(), ph: PhantomData }
}
}
impl<N: AstElement> Iterator for AstChildElements<N> {
type Item = N;
fn next(&mut self) -> Option<N> {
self.inner.by_ref().find_map(N::cast_element)
}
}
#[allow(dead_code)]
fn child_element_opt<P: AstNode + ?Sized, C: AstElement>(parent: &P) -> Option<C> {
child_elements(parent).next()
}
#[allow(dead_code)]
fn child_elements<P: AstNode + ?Sized, C: AstElement>(parent: &P) -> AstChildElements<C> {
AstChildElements::new(parent.syntax())
}
2019-01-26 09:35:23 -06:00
#[test]
fn test_doc_comment_none() {
let file = SourceFile::parse(
r#"
// non-doc
mod foo {}
"#,
2019-05-28 09:34:28 -05:00
)
.ok()
.unwrap();
2019-01-26 09:35:23 -06:00
let module = file.syntax().descendants().find_map(Module::cast).unwrap();
assert!(module.doc_comment_text().is_none());
}
2019-01-04 07:51:45 -06:00
#[test]
fn test_doc_comment_of_items() {
2019-01-07 07:15:47 -06:00
let file = SourceFile::parse(
2019-01-04 07:51:45 -06:00
r#"
//! doc
// non-doc
mod foo {}
"#,
2019-05-28 09:34:28 -05:00
)
.ok()
.unwrap();
2019-01-04 07:51:45 -06:00
let module = file.syntax().descendants().find_map(Module::cast).unwrap();
2019-01-26 09:35:23 -06:00
assert_eq!("doc", module.doc_comment_text().unwrap());
2019-01-04 07:51:45 -06:00
}
2019-01-25 20:31:31 -06:00
2019-10-31 15:21:46 -05:00
#[test]
fn test_doc_comment_of_statics() {
let file = SourceFile::parse(
r#"
/// Number of levels
static LEVELS: i32 = 0;
"#,
)
.ok()
.unwrap();
let st = file.syntax().descendants().find_map(StaticDef::cast).unwrap();
assert_eq!("Number of levels", st.doc_comment_text().unwrap());
}
2019-01-25 20:31:31 -06:00
#[test]
fn test_doc_comment_preserves_indents() {
let file = SourceFile::parse(
r#"
/// doc1
/// ```
/// fn foo() {
/// // ...
/// }
/// ```
mod foo {}
"#,
2019-05-28 09:34:28 -05:00
)
.ok()
.unwrap();
2019-01-25 20:31:31 -06:00
let module = file.syntax().descendants().find_map(Module::cast).unwrap();
2019-02-08 05:49:43 -06:00
assert_eq!("doc1\n```\nfn foo() {\n // ...\n}\n```", module.doc_comment_text().unwrap());
2019-01-04 07:51:45 -06:00
}
2019-07-31 09:46:15 -05:00
#[test]
fn test_doc_comment_preserves_newlines() {
let file = SourceFile::parse(
r#"
/// this
/// is
/// mod
/// foo
mod foo {}
"#,
)
.ok()
.unwrap();
let module = file.syntax().descendants().find_map(Module::cast).unwrap();
assert_eq!("this\nis\nmod\nfoo", module.doc_comment_text().unwrap());
}
2019-07-31 10:43:00 -05:00
#[test]
fn test_doc_comment_single_line_block_strips_suffix() {
let file = SourceFile::parse(
r#"
/** this is mod foo*/
mod foo {}
"#,
)
.ok()
.unwrap();
let module = file.syntax().descendants().find_map(Module::cast).unwrap();
assert_eq!("this is mod foo", module.doc_comment_text().unwrap());
}
#[test]
fn test_doc_comment_single_line_block_strips_suffix_whitespace() {
let file = SourceFile::parse(
r#"
/** this is mod foo */
mod foo {}
"#,
)
.ok()
.unwrap();
let module = file.syntax().descendants().find_map(Module::cast).unwrap();
assert_eq!("this is mod foo ", module.doc_comment_text().unwrap());
2019-07-31 10:43:00 -05:00
}
#[test]
fn test_doc_comment_multi_line_block_strips_suffix() {
let file = SourceFile::parse(
r#"
/**
this
is
mod foo
*/
mod foo {}
"#,
)
.ok()
.unwrap();
let module = file.syntax().descendants().find_map(Module::cast).unwrap();
assert_eq!(
" this\n is\n mod foo\n ",
module.doc_comment_text().unwrap()
);
}
#[test]
fn test_comments_preserve_trailing_whitespace() {
let file = SourceFile::parse(
2020-02-12 09:04:16 -06:00
"\n/// Representation of a Realm. \n/// In the specification these are called Realm Records.\nstruct Realm {}",
)
.ok()
.unwrap();
let def = file.syntax().descendants().find_map(StructDef::cast).unwrap();
assert_eq!(
"Representation of a Realm. \nIn the specification these are called Realm Records.",
def.doc_comment_text().unwrap()
);
2019-07-31 10:43:00 -05:00
}
#[test]
fn test_where_predicates() {
2019-07-18 11:23:05 -05:00
fn assert_bound(text: &str, bound: Option<TypeBound>) {
assert_eq!(text, bound.unwrap().syntax().text().to_string());
}
let file = SourceFile::parse(
r#"
fn foo()
where
T: Clone + Copy + Debug + 'static,
'a: 'b + 'c,
Iterator::Item: 'a + Debug,
Iterator::Item: Debug + 'a,
<T as Iterator>::Item: Debug + 'a,
for<'a> F: Fn(&'a str)
{}
"#,
2019-05-28 09:34:28 -05:00
)
.ok()
.unwrap();
let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap();
let mut predicates = where_clause.predicates();
let pred = predicates.next().unwrap();
let mut bounds = pred.type_bound_list().unwrap().bounds();
assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string());
assert_bound("Clone", bounds.next());
assert_bound("Copy", bounds.next());
assert_bound("Debug", bounds.next());
assert_bound("'static", bounds.next());
let pred = predicates.next().unwrap();
let mut bounds = pred.type_bound_list().unwrap().bounds();
2019-03-30 05:25:53 -05:00
assert_eq!("'a", pred.lifetime_token().unwrap().text());
assert_bound("'b", bounds.next());
assert_bound("'c", bounds.next());
let pred = predicates.next().unwrap();
let mut bounds = pred.type_bound_list().unwrap().bounds();
assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string());
assert_bound("'a", bounds.next());
let pred = predicates.next().unwrap();
let mut bounds = pred.type_bound_list().unwrap().bounds();
assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string());
assert_bound("Debug", bounds.next());
assert_bound("'a", bounds.next());
let pred = predicates.next().unwrap();
let mut bounds = pred.type_bound_list().unwrap().bounds();
assert_eq!("<T as Iterator>::Item", pred.type_ref().unwrap().syntax().text().to_string());
assert_bound("Debug", bounds.next());
assert_bound("'a", bounds.next());
let pred = predicates.next().unwrap();
let mut bounds = pred.type_bound_list().unwrap().bounds();
assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string());
assert_bound("Fn(&'a str)", bounds.next());
}