1 //! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s
10 pub mod edit_in_place;
13 use std::marker::PhantomData;
16 syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
21 expr_ext::{ArrayExprKind, BlockModifier, CallableExpr, ElseBranch, LiteralKind},
22 generated::{nodes::*, tokens::*},
24 AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
25 SlicePatComponents, StructKind, TypeBoundKind, VisibilityKind,
27 operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
28 token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix},
30 DocCommentIter, HasArgList, HasAttrs, HasDocComments, HasGenericParams, HasLoopBody,
31 HasModuleItem, HasName, HasTypeBounds, HasVisibility,
35 /// The main trait to go from untyped `SyntaxNode` to a typed ast. The
36 /// conversion itself has zero runtime cost: ast and syntax nodes have exactly
37 /// the same representation: a pointer to the tree root and a pointer to the
40 fn can_cast(kind: SyntaxKind) -> bool
44 fn cast(syntax: SyntaxNode) -> Option<Self>
48 fn syntax(&self) -> &SyntaxNode;
49 fn clone_for_update(&self) -> Self
53 Self::cast(self.syntax().clone_for_update()).unwrap()
55 fn clone_subtree(&self) -> Self
59 Self::cast(self.syntax().clone_subtree()).unwrap()
63 /// Like `AstNode`, but wraps tokens rather than interior nodes.
65 fn can_cast(token: SyntaxKind) -> bool
69 fn cast(syntax: SyntaxToken) -> Option<Self>
73 fn syntax(&self) -> &SyntaxToken;
75 fn text(&self) -> &str {
80 /// An iterator over `SyntaxNode` children of a particular AST type.
81 #[derive(Debug, Clone)]
82 pub struct AstChildren<N> {
83 inner: SyntaxNodeChildren,
87 impl<N> AstChildren<N> {
88 fn new(parent: &SyntaxNode) -> Self {
89 AstChildren { inner: parent.children(), ph: PhantomData }
93 impl<N: AstNode> Iterator for AstChildren<N> {
95 fn next(&mut self) -> Option<N> {
96 self.inner.find_map(N::cast)
101 use super::{AstChildren, AstNode, SyntaxKind, SyntaxNode, SyntaxToken};
103 pub(super) fn child<N: AstNode>(parent: &SyntaxNode) -> Option<N> {
104 parent.children().find_map(N::cast)
107 pub(super) fn children<N: AstNode>(parent: &SyntaxNode) -> AstChildren<N> {
108 AstChildren::new(parent)
111 pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
112 parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind)
117 fn assert_ast_is_object_safe() {
118 fn _f(_: &dyn AstNode, _: &dyn HasName) {}
122 fn test_doc_comment_none() {
123 let file = SourceFile::parse(
131 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
132 assert!(module.doc_comments().doc_comment_text().is_none());
136 fn test_outer_doc_comment_of_items() {
137 let file = SourceFile::parse(
146 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
147 assert_eq!(" doc", module.doc_comments().doc_comment_text().unwrap());
151 fn test_inner_doc_comment_of_items() {
152 let file = SourceFile::parse(
161 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
162 assert!(module.doc_comments().doc_comment_text().is_none());
166 fn test_doc_comment_of_statics() {
167 let file = SourceFile::parse(
170 static LEVELS: i32 = 0;
175 let st = file.syntax().descendants().find_map(Static::cast).unwrap();
176 assert_eq!(" Number of levels", st.doc_comments().doc_comment_text().unwrap());
180 fn test_doc_comment_preserves_indents() {
181 let file = SourceFile::parse(
194 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
196 " doc1\n ```\n fn foo() {\n // ...\n }\n ```",
197 module.doc_comments().doc_comment_text().unwrap()
202 fn test_doc_comment_preserves_newlines() {
203 let file = SourceFile::parse(
214 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
215 assert_eq!(" this\n is\n mod\n foo", module.doc_comments().doc_comment_text().unwrap());
219 fn test_doc_comment_single_line_block_strips_suffix() {
220 let file = SourceFile::parse(
222 /** this is mod foo*/
228 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
229 assert_eq!(" this is mod foo", module.doc_comments().doc_comment_text().unwrap());
233 fn test_doc_comment_single_line_block_strips_suffix_whitespace() {
234 let file = SourceFile::parse(
236 /** this is mod foo */
242 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
243 assert_eq!(" this is mod foo ", module.doc_comments().doc_comment_text().unwrap());
247 fn test_doc_comment_multi_line_block_strips_suffix() {
248 let file = SourceFile::parse(
260 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
262 "\n this\n is\n mod foo\n ",
263 module.doc_comments().doc_comment_text().unwrap()
268 fn test_comments_preserve_trailing_whitespace() {
269 let file = SourceFile::parse(
270 "\n/// Representation of a Realm. \n/// In the specification these are called Realm Records.\nstruct Realm {}",
274 let def = file.syntax().descendants().find_map(Struct::cast).unwrap();
276 " Representation of a Realm. \n In the specification these are called Realm Records.",
277 def.doc_comments().doc_comment_text().unwrap()
282 fn test_four_slash_line_comment() {
283 let file = SourceFile::parse(
285 //// too many slashes to be a doc comment
292 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
293 assert_eq!(" doc comment", module.doc_comments().doc_comment_text().unwrap());
297 fn test_where_predicates() {
298 fn assert_bound(text: &str, bound: Option<TypeBound>) {
299 assert_eq!(text, bound.unwrap().syntax().text().to_string());
302 let file = SourceFile::parse(
306 T: Clone + Copy + Debug + 'static,
308 Iterator::Item: 'a + Debug,
309 Iterator::Item: Debug + 'a,
310 <T as Iterator>::Item: Debug + 'a,
311 for<'a> F: Fn(&'a str)
317 let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap();
319 let mut predicates = where_clause.predicates();
321 let pred = predicates.next().unwrap();
322 let mut bounds = pred.type_bound_list().unwrap().bounds();
324 assert!(pred.for_token().is_none());
325 assert!(pred.generic_param_list().is_none());
326 assert_eq!("T", pred.ty().unwrap().syntax().text().to_string());
327 assert_bound("Clone", bounds.next());
328 assert_bound("Copy", bounds.next());
329 assert_bound("Debug", bounds.next());
330 assert_bound("'static", bounds.next());
332 let pred = predicates.next().unwrap();
333 let mut bounds = pred.type_bound_list().unwrap().bounds();
335 assert_eq!("'a", pred.lifetime().unwrap().lifetime_ident_token().unwrap().text());
337 assert_bound("'b", bounds.next());
338 assert_bound("'c", bounds.next());
340 let pred = predicates.next().unwrap();
341 let mut bounds = pred.type_bound_list().unwrap().bounds();
343 assert_eq!("Iterator::Item", pred.ty().unwrap().syntax().text().to_string());
344 assert_bound("'a", bounds.next());
346 let pred = predicates.next().unwrap();
347 let mut bounds = pred.type_bound_list().unwrap().bounds();
349 assert_eq!("Iterator::Item", pred.ty().unwrap().syntax().text().to_string());
350 assert_bound("Debug", bounds.next());
351 assert_bound("'a", bounds.next());
353 let pred = predicates.next().unwrap();
354 let mut bounds = pred.type_bound_list().unwrap().bounds();
356 assert_eq!("<T as Iterator>::Item", pred.ty().unwrap().syntax().text().to_string());
357 assert_bound("Debug", bounds.next());
358 assert_bound("'a", bounds.next());
360 let pred = predicates.next().unwrap();
361 let mut bounds = pred.type_bound_list().unwrap().bounds();
363 assert!(pred.for_token().is_some());
364 assert_eq!("<'a>", pred.generic_param_list().unwrap().syntax().text().to_string());
365 assert_eq!("F", pred.ty().unwrap().syntax().text().to_string());
366 assert_bound("Fn(&'a str)", bounds.next());