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;
|
2020-07-23 10:23:01 -05:00
|
|
|
mod token_ext;
|
|
|
|
mod node_ext;
|
|
|
|
mod expr_ext;
|
2021-08-14 08:58:46 -05:00
|
|
|
mod operators;
|
2019-09-28 11:50:16 -05:00
|
|
|
pub mod edit;
|
2021-01-30 09:19:21 -06:00
|
|
|
pub mod edit_in_place;
|
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::{
|
2020-04-09 03:47:05 -05:00
|
|
|
syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
|
2021-01-19 16:56:11 -06:00
|
|
|
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::{
|
2021-10-03 05:42:00 -05:00
|
|
|
expr_ext::{ArrayExprKind, BlockModifier, CallableExpr, ElseBranch, LiteralKind},
|
2020-11-02 06:13:32 -06:00
|
|
|
generated::{nodes::*, tokens::*},
|
2020-07-23 10:23:01 -05:00
|
|
|
node_ext::{
|
2021-09-21 08:52:11 -05:00
|
|
|
AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
|
|
|
|
SlicePatComponents, StructKind, TypeBoundKind, VisibilityKind,
|
2019-12-24 13:32:42 -06:00
|
|
|
},
|
2021-08-14 10:08:31 -05:00
|
|
|
operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
|
2022-01-15 06:14:59 -06:00
|
|
|
token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix},
|
2021-08-14 08:58:46 -05:00
|
|
|
traits::{
|
2022-01-30 15:18:32 -06:00
|
|
|
AttrDocCommentIter, DocCommentIter, HasArgList, HasAttrs, HasDocComments, HasGenericParams,
|
|
|
|
HasLoopBody, HasModuleItem, HasName, HasTypeBounds, HasVisibility,
|
2021-08-14 08:58:46 -05:00
|
|
|
},
|
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.
|
2020-04-09 03:47:05 -05:00
|
|
|
pub trait AstNode {
|
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
|
|
|
|
2020-04-03 14:12:08 -05:00
|
|
|
fn cast(syntax: SyntaxNode) -> Option<Self>
|
|
|
|
where
|
2020-04-09 03:47:05 -05:00
|
|
|
Self: Sized;
|
2020-04-03 14:12:08 -05:00
|
|
|
|
2019-01-07 07:15:47 -06:00
|
|
|
fn syntax(&self) -> &SyntaxNode;
|
2021-01-30 09:19:21 -06:00
|
|
|
fn clone_for_update(&self) -> Self
|
|
|
|
where
|
|
|
|
Self: Sized,
|
|
|
|
{
|
|
|
|
Self::cast(self.syntax().clone_for_update()).unwrap()
|
|
|
|
}
|
2021-05-10 11:04:41 -05:00
|
|
|
fn clone_subtree(&self) -> Self
|
|
|
|
where
|
|
|
|
Self: Sized,
|
|
|
|
{
|
|
|
|
Self::cast(self.syntax().clone_subtree()).unwrap()
|
|
|
|
}
|
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.
|
2020-04-09 03:47:05 -05:00
|
|
|
pub trait AstToken {
|
2020-04-03 14:12:08 -05:00
|
|
|
fn can_cast(token: SyntaxKind) -> bool
|
2019-04-02 04:47:39 -05:00
|
|
|
where
|
|
|
|
Self: Sized;
|
2020-04-03 14:12:08 -05:00
|
|
|
|
|
|
|
fn cast(syntax: SyntaxToken) -> Option<Self>
|
|
|
|
where
|
2020-04-09 03:47:05 -05:00
|
|
|
Self: Sized;
|
2020-04-03 14:12:08 -05:00
|
|
|
|
2019-07-18 11:23:05 -05:00
|
|
|
fn syntax(&self) -> &SyntaxToken;
|
2020-04-03 14:12:08 -05:00
|
|
|
|
2021-01-19 16:56:11 -06:00
|
|
|
fn text(&self) -> &str {
|
2019-04-02 04:47:39 -05:00
|
|
|
self.syntax().text()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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> {
|
2020-06-01 18:50:05 -05:00
|
|
|
self.inner.find_map(N::cast)
|
2019-04-02 02:09:52 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-09 15:22:58 -05:00
|
|
|
mod support {
|
2020-04-10 08:53:09 -05:00
|
|
|
use super::{AstChildren, AstNode, SyntaxKind, SyntaxNode, SyntaxToken};
|
2020-04-09 15:22:58 -05:00
|
|
|
|
|
|
|
pub(super) fn child<N: AstNode>(parent: &SyntaxNode) -> Option<N> {
|
|
|
|
parent.children().find_map(N::cast)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn children<N: AstNode>(parent: &SyntaxNode) -> AstChildren<N> {
|
|
|
|
AstChildren::new(parent)
|
|
|
|
}
|
|
|
|
|
2020-04-10 08:54:05 -05:00
|
|
|
pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
|
2020-04-09 16:35:05 -05:00
|
|
|
parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind)
|
|
|
|
}
|
2018-08-22 09:01:51 -05:00
|
|
|
}
|
|
|
|
|
2020-04-09 15:22:58 -05:00
|
|
|
#[test]
|
|
|
|
fn assert_ast_is_object_safe() {
|
2021-09-27 05:54:24 -05:00
|
|
|
fn _f(_: &dyn AstNode, _: &dyn HasName) {}
|
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 {}
|
|
|
|
"#,
|
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();
|
2021-03-17 08:38:11 -05:00
|
|
|
assert!(module.doc_comments().doc_comment_text().is_none());
|
2019-01-26 09:35:23 -06:00
|
|
|
}
|
|
|
|
|
2019-01-04 07:51:45 -06:00
|
|
|
#[test]
|
2020-11-12 05:09:12 -06:00
|
|
|
fn test_outer_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#"
|
2020-11-12 05:09:12 -06:00
|
|
|
/// doc
|
2019-01-04 07:51:45 -06:00
|
|
|
// 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();
|
2021-03-17 08:38:11 -05:00
|
|
|
assert_eq!(" doc", module.doc_comments().doc_comment_text().unwrap());
|
2019-01-04 07:51:45 -06:00
|
|
|
}
|
2019-01-25 20:31:31 -06:00
|
|
|
|
2020-11-12 05:09:12 -06:00
|
|
|
#[test]
|
|
|
|
fn test_inner_doc_comment_of_items() {
|
|
|
|
let file = SourceFile::parse(
|
|
|
|
r#"
|
|
|
|
//! doc
|
|
|
|
// non-doc
|
|
|
|
mod foo {}
|
|
|
|
"#,
|
|
|
|
)
|
|
|
|
.ok()
|
|
|
|
.unwrap();
|
|
|
|
let module = file.syntax().descendants().find_map(Module::cast).unwrap();
|
2021-03-17 08:38:11 -05:00
|
|
|
assert!(module.doc_comments().doc_comment_text().is_none());
|
2020-11-12 05:09:12 -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();
|
2020-07-30 11:02:20 -05:00
|
|
|
let st = file.syntax().descendants().find_map(Static::cast).unwrap();
|
2021-03-17 08:38:11 -05:00
|
|
|
assert_eq!(" Number of levels", st.doc_comments().doc_comment_text().unwrap());
|
2019-10-31 15:21:46 -05: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 {}
|
|
|
|
"#,
|
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();
|
2021-03-17 08:38:11 -05:00
|
|
|
assert_eq!(
|
|
|
|
" doc1\n ```\n fn foo() {\n // ...\n }\n ```",
|
|
|
|
module.doc_comments().doc_comment_text().unwrap()
|
|
|
|
);
|
2019-01-04 07:51:45 -06:00
|
|
|
}
|
2019-03-24 12:45:11 -05: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();
|
2021-03-17 08:38:11 -05:00
|
|
|
assert_eq!(" this\n is\n mod\n foo", module.doc_comments().doc_comment_text().unwrap());
|
2019-07-31 09:46:15 -05:00
|
|
|
}
|
|
|
|
|
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();
|
2021-03-17 08:38:11 -05:00
|
|
|
assert_eq!(" this is mod foo", module.doc_comments().doc_comment_text().unwrap());
|
2019-07-31 10:43:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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();
|
2021-03-17 08:38:11 -05:00
|
|
|
assert_eq!(" this is mod foo ", module.doc_comments().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();
|
2019-10-27 15:56:25 -05:00
|
|
|
assert_eq!(
|
2021-03-17 08:38:11 -05:00
|
|
|
"\n this\n is\n mod foo\n ",
|
|
|
|
module.doc_comments().doc_comment_text().unwrap()
|
2019-10-27 15:56:25 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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 {}",
|
2019-10-27 15:56:25 -05:00
|
|
|
)
|
|
|
|
.ok()
|
|
|
|
.unwrap();
|
2020-07-30 10:50:40 -05:00
|
|
|
let def = file.syntax().descendants().find_map(Struct::cast).unwrap();
|
2019-10-27 15:56:25 -05:00
|
|
|
assert_eq!(
|
2021-03-17 08:38:11 -05:00
|
|
|
" Representation of a Realm. \n In the specification these are called Realm Records.",
|
|
|
|
def.doc_comments().doc_comment_text().unwrap()
|
2019-10-27 15:56:25 -05:00
|
|
|
);
|
2019-07-31 10:43:00 -05:00
|
|
|
}
|
|
|
|
|
2020-04-28 03:23:45 -05:00
|
|
|
#[test]
|
|
|
|
fn test_four_slash_line_comment() {
|
|
|
|
let file = SourceFile::parse(
|
|
|
|
r#"
|
|
|
|
//// too many slashes to be a doc comment
|
|
|
|
/// doc comment
|
|
|
|
mod foo {}
|
|
|
|
"#,
|
|
|
|
)
|
|
|
|
.ok()
|
|
|
|
.unwrap();
|
|
|
|
let module = file.syntax().descendants().find_map(Module::cast).unwrap();
|
2021-03-17 08:38:11 -05:00
|
|
|
assert_eq!(" doc comment", module.doc_comments().doc_comment_text().unwrap());
|
2020-04-28 03:23:45 -05:00
|
|
|
}
|
|
|
|
|
2019-03-24 12:45:11 -05:00
|
|
|
#[test]
|
|
|
|
fn test_where_predicates() {
|
2019-07-18 11:23:05 -05:00
|
|
|
fn assert_bound(text: &str, bound: Option<TypeBound>) {
|
2019-03-24 12:45:11 -05:00
|
|
|
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();
|
2019-03-24 12:45:11 -05:00
|
|
|
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();
|
|
|
|
|
2020-06-10 05:30:48 -05:00
|
|
|
assert!(pred.for_token().is_none());
|
2020-07-30 08:36:21 -05:00
|
|
|
assert!(pred.generic_param_list().is_none());
|
2020-07-31 05:06:38 -05:00
|
|
|
assert_eq!("T", pred.ty().unwrap().syntax().text().to_string());
|
2019-03-24 12:45:11 -05:00
|
|
|
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();
|
|
|
|
|
2020-12-15 12:23:51 -06:00
|
|
|
assert_eq!("'a", pred.lifetime().unwrap().lifetime_ident_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();
|
|
|
|
|
2020-07-31 05:06:38 -05:00
|
|
|
assert_eq!("Iterator::Item", pred.ty().unwrap().syntax().text().to_string());
|
2019-03-24 12:45:11 -05:00
|
|
|
assert_bound("'a", bounds.next());
|
|
|
|
|
|
|
|
let pred = predicates.next().unwrap();
|
|
|
|
let mut bounds = pred.type_bound_list().unwrap().bounds();
|
|
|
|
|
2020-07-31 05:06:38 -05:00
|
|
|
assert_eq!("Iterator::Item", pred.ty().unwrap().syntax().text().to_string());
|
2019-03-24 12:45:11 -05:00
|
|
|
assert_bound("Debug", bounds.next());
|
|
|
|
assert_bound("'a", bounds.next());
|
|
|
|
|
|
|
|
let pred = predicates.next().unwrap();
|
|
|
|
let mut bounds = pred.type_bound_list().unwrap().bounds();
|
|
|
|
|
2020-07-31 05:06:38 -05:00
|
|
|
assert_eq!("<T as Iterator>::Item", pred.ty().unwrap().syntax().text().to_string());
|
2019-03-24 12:45:11 -05:00
|
|
|
assert_bound("Debug", bounds.next());
|
|
|
|
assert_bound("'a", bounds.next());
|
|
|
|
|
|
|
|
let pred = predicates.next().unwrap();
|
|
|
|
let mut bounds = pred.type_bound_list().unwrap().bounds();
|
|
|
|
|
2020-06-10 05:30:48 -05:00
|
|
|
assert!(pred.for_token().is_some());
|
2020-07-30 08:36:21 -05:00
|
|
|
assert_eq!("<'a>", pred.generic_param_list().unwrap().syntax().text().to_string());
|
2020-07-31 05:06:38 -05:00
|
|
|
assert_eq!("F", pred.ty().unwrap().syntax().text().to_string());
|
2019-03-24 12:45:11 -05:00
|
|
|
assert_bound("Fn(&'a str)", bounds.next());
|
|
|
|
}
|