2019-02-21 06:24:42 -06:00
|
|
|
//! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s
|
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;
|
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::{
|
2019-04-02 04:47:39 -05:00
|
|
|
syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken},
|
2018-10-15 16:44:23 -05:00
|
|
|
SmolStr,
|
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::{
|
|
|
|
generated::*,
|
|
|
|
traits::*,
|
2019-04-02 02:23:18 -05:00
|
|
|
tokens::*,
|
2019-04-02 04:47:39 -05:00
|
|
|
extensions::{PathSegmentKind, StructFlavor, SelfParamFlavor},
|
|
|
|
expr_extensions::{ElseBranch, PrefixOp, BinOp, LiteralFlavor},
|
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.
|
2019-01-24 17:09:31 -06:00
|
|
|
pub trait AstNode:
|
|
|
|
rowan::TransparentNewType<Repr = rowan::SyntaxNode<RaTypes>> + ToOwned<Owned = TreeArc<Self>>
|
|
|
|
{
|
2019-01-07 07:15:47 -06:00
|
|
|
fn cast(syntax: &SyntaxNode) -> Option<&Self>
|
2018-10-15 16:44:23 -05:00
|
|
|
where
|
|
|
|
Self: Sized;
|
2019-01-07 07:15:47 -06:00
|
|
|
fn syntax(&self) -> &SyntaxNode;
|
2018-08-09 08:03:21 -05:00
|
|
|
}
|
|
|
|
|
2019-04-02 04:47:39 -05:00
|
|
|
/// 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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-02 02:09:52 -05:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct AstChildren<'a, N> {
|
|
|
|
inner: SyntaxNodeChildren<'a>,
|
|
|
|
ph: PhantomData<N>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, N> AstChildren<'a, N> {
|
|
|
|
fn new(parent: &'a SyntaxNode) -> Self {
|
|
|
|
AstChildren { inner: parent.children(), ph: PhantomData }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, N: AstNode + 'a> Iterator for AstChildren<'a, N> {
|
|
|
|
type Item = &'a N;
|
|
|
|
fn next(&mut self) -> Option<&'a N> {
|
|
|
|
self.inner.by_ref().find_map(N::cast)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-07 07:15:47 -06:00
|
|
|
fn child_opt<P: AstNode, C: AstNode>(parent: &P) -> Option<&C> {
|
2018-08-22 09:01:51 -05:00
|
|
|
children(parent).next()
|
|
|
|
}
|
|
|
|
|
2019-01-07 07:15:47 -06:00
|
|
|
fn children<P: AstNode, 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
|
|
|
}
|
|
|
|
|
2019-01-26 09:35:23 -06:00
|
|
|
#[test]
|
|
|
|
fn test_doc_comment_none() {
|
|
|
|
let file = SourceFile::parse(
|
|
|
|
r#"
|
|
|
|
// non-doc
|
|
|
|
mod foo {}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
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 {}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
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
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_doc_comment_preserves_indents() {
|
|
|
|
let file = SourceFile::parse(
|
|
|
|
r#"
|
|
|
|
/// doc1
|
|
|
|
/// ```
|
|
|
|
/// fn foo() {
|
|
|
|
/// // ...
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
mod foo {}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
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-03-24 12:45:11 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_where_predicates() {
|
|
|
|
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)
|
|
|
|
{}
|
|
|
|
"#,
|
|
|
|
);
|
|
|
|
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());
|
2019-03-24 12:45:11 -05:00
|
|
|
|
|
|
|
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());
|
|
|
|
}
|