]> git.lizzy.rs Git - rust.git/blob - crates/syntax/src/ast.rs
Merge #8819
[rust.git] / crates / syntax / src / ast.rs
1 //! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s
2
3 mod generated;
4 mod traits;
5 mod token_ext;
6 mod node_ext;
7 mod expr_ext;
8 pub mod edit;
9 pub mod edit_in_place;
10 pub mod make;
11
12 use std::marker::PhantomData;
13
14 use crate::{
15     syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
16     SyntaxKind,
17 };
18
19 pub use self::{
20     expr_ext::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, RangeOp},
21     generated::{nodes::*, tokens::*},
22     node_ext::{
23         AttrKind, AttrsOwnerNode, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind,
24         SelfParamKind, SlicePatComponents, StructKind, TypeBoundKind, VisibilityKind,
25     },
26     token_ext::*,
27     traits::*,
28 };
29
30 /// The main trait to go from untyped `SyntaxNode`  to a typed ast. The
31 /// conversion itself has zero runtime cost: ast and syntax nodes have exactly
32 /// the same representation: a pointer to the tree root and a pointer to the
33 /// node itself.
34 pub trait AstNode {
35     fn can_cast(kind: SyntaxKind) -> bool
36     where
37         Self: Sized;
38
39     fn cast(syntax: SyntaxNode) -> Option<Self>
40     where
41         Self: Sized;
42
43     fn syntax(&self) -> &SyntaxNode;
44     fn clone_for_update(&self) -> Self
45     where
46         Self: Sized,
47     {
48         Self::cast(self.syntax().clone_for_update()).unwrap()
49     }
50     fn clone_subtree(&self) -> Self
51     where
52         Self: Sized,
53     {
54         Self::cast(self.syntax().clone_subtree()).unwrap()
55     }
56 }
57
58 /// Like `AstNode`, but wraps tokens rather than interior nodes.
59 pub trait AstToken {
60     fn can_cast(token: SyntaxKind) -> bool
61     where
62         Self: Sized;
63
64     fn cast(syntax: SyntaxToken) -> Option<Self>
65     where
66         Self: Sized;
67
68     fn syntax(&self) -> &SyntaxToken;
69
70     fn text(&self) -> &str {
71         self.syntax().text()
72     }
73 }
74
75 /// An iterator over `SyntaxNode` children of a particular AST type.
76 #[derive(Debug, Clone)]
77 pub struct AstChildren<N> {
78     inner: SyntaxNodeChildren,
79     ph: PhantomData<N>,
80 }
81
82 impl<N> AstChildren<N> {
83     fn new(parent: &SyntaxNode) -> Self {
84         AstChildren { inner: parent.children(), ph: PhantomData }
85     }
86 }
87
88 impl<N: AstNode> Iterator for AstChildren<N> {
89     type Item = N;
90     fn next(&mut self) -> Option<N> {
91         self.inner.find_map(N::cast)
92     }
93 }
94
95 mod support {
96     use super::{AstChildren, AstNode, SyntaxKind, SyntaxNode, SyntaxToken};
97
98     pub(super) fn child<N: AstNode>(parent: &SyntaxNode) -> Option<N> {
99         parent.children().find_map(N::cast)
100     }
101
102     pub(super) fn children<N: AstNode>(parent: &SyntaxNode) -> AstChildren<N> {
103         AstChildren::new(parent)
104     }
105
106     pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
107         parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind)
108     }
109 }
110
111 #[test]
112 fn assert_ast_is_object_safe() {
113     fn _f(_: &dyn AstNode, _: &dyn NameOwner) {}
114 }
115
116 #[test]
117 fn test_doc_comment_none() {
118     let file = SourceFile::parse(
119         r#"
120         // non-doc
121         mod foo {}
122         "#,
123     )
124     .ok()
125     .unwrap();
126     let module = file.syntax().descendants().find_map(Module::cast).unwrap();
127     assert!(module.doc_comments().doc_comment_text().is_none());
128 }
129
130 #[test]
131 fn test_outer_doc_comment_of_items() {
132     let file = SourceFile::parse(
133         r#"
134         /// doc
135         // non-doc
136         mod foo {}
137         "#,
138     )
139     .ok()
140     .unwrap();
141     let module = file.syntax().descendants().find_map(Module::cast).unwrap();
142     assert_eq!(" doc", module.doc_comments().doc_comment_text().unwrap());
143 }
144
145 #[test]
146 fn test_inner_doc_comment_of_items() {
147     let file = SourceFile::parse(
148         r#"
149         //! doc
150         // non-doc
151         mod foo {}
152         "#,
153     )
154     .ok()
155     .unwrap();
156     let module = file.syntax().descendants().find_map(Module::cast).unwrap();
157     assert!(module.doc_comments().doc_comment_text().is_none());
158 }
159
160 #[test]
161 fn test_doc_comment_of_statics() {
162     let file = SourceFile::parse(
163         r#"
164         /// Number of levels
165         static LEVELS: i32 = 0;
166         "#,
167     )
168     .ok()
169     .unwrap();
170     let st = file.syntax().descendants().find_map(Static::cast).unwrap();
171     assert_eq!(" Number of levels", st.doc_comments().doc_comment_text().unwrap());
172 }
173
174 #[test]
175 fn test_doc_comment_preserves_indents() {
176     let file = SourceFile::parse(
177         r#"
178         /// doc1
179         /// ```
180         /// fn foo() {
181         ///     // ...
182         /// }
183         /// ```
184         mod foo {}
185         "#,
186     )
187     .ok()
188     .unwrap();
189     let module = file.syntax().descendants().find_map(Module::cast).unwrap();
190     assert_eq!(
191         " doc1\n ```\n fn foo() {\n     // ...\n }\n ```",
192         module.doc_comments().doc_comment_text().unwrap()
193     );
194 }
195
196 #[test]
197 fn test_doc_comment_preserves_newlines() {
198     let file = SourceFile::parse(
199         r#"
200         /// this
201         /// is
202         /// mod
203         /// foo
204         mod foo {}
205         "#,
206     )
207     .ok()
208     .unwrap();
209     let module = file.syntax().descendants().find_map(Module::cast).unwrap();
210     assert_eq!(" this\n is\n mod\n foo", module.doc_comments().doc_comment_text().unwrap());
211 }
212
213 #[test]
214 fn test_doc_comment_single_line_block_strips_suffix() {
215     let file = SourceFile::parse(
216         r#"
217         /** this is mod foo*/
218         mod foo {}
219         "#,
220     )
221     .ok()
222     .unwrap();
223     let module = file.syntax().descendants().find_map(Module::cast).unwrap();
224     assert_eq!(" this is mod foo", module.doc_comments().doc_comment_text().unwrap());
225 }
226
227 #[test]
228 fn test_doc_comment_single_line_block_strips_suffix_whitespace() {
229     let file = SourceFile::parse(
230         r#"
231         /** this is mod foo */
232         mod foo {}
233         "#,
234     )
235     .ok()
236     .unwrap();
237     let module = file.syntax().descendants().find_map(Module::cast).unwrap();
238     assert_eq!(" this is mod foo ", module.doc_comments().doc_comment_text().unwrap());
239 }
240
241 #[test]
242 fn test_doc_comment_multi_line_block_strips_suffix() {
243     let file = SourceFile::parse(
244         r#"
245         /**
246         this
247         is
248         mod foo
249         */
250         mod foo {}
251         "#,
252     )
253     .ok()
254     .unwrap();
255     let module = file.syntax().descendants().find_map(Module::cast).unwrap();
256     assert_eq!(
257         "\n        this\n        is\n        mod foo\n        ",
258         module.doc_comments().doc_comment_text().unwrap()
259     );
260 }
261
262 #[test]
263 fn test_comments_preserve_trailing_whitespace() {
264     let file = SourceFile::parse(
265         "\n/// Representation of a Realm.   \n/// In the specification these are called Realm Records.\nstruct Realm {}",
266     )
267     .ok()
268     .unwrap();
269     let def = file.syntax().descendants().find_map(Struct::cast).unwrap();
270     assert_eq!(
271         " Representation of a Realm.   \n In the specification these are called Realm Records.",
272         def.doc_comments().doc_comment_text().unwrap()
273     );
274 }
275
276 #[test]
277 fn test_four_slash_line_comment() {
278     let file = SourceFile::parse(
279         r#"
280         //// too many slashes to be a doc comment
281         /// doc comment
282         mod foo {}
283         "#,
284     )
285     .ok()
286     .unwrap();
287     let module = file.syntax().descendants().find_map(Module::cast).unwrap();
288     assert_eq!(" doc comment", module.doc_comments().doc_comment_text().unwrap());
289 }
290
291 #[test]
292 fn test_where_predicates() {
293     fn assert_bound(text: &str, bound: Option<TypeBound>) {
294         assert_eq!(text, bound.unwrap().syntax().text().to_string());
295     }
296
297     let file = SourceFile::parse(
298         r#"
299 fn foo()
300 where
301    T: Clone + Copy + Debug + 'static,
302    'a: 'b + 'c,
303    Iterator::Item: 'a + Debug,
304    Iterator::Item: Debug + 'a,
305    <T as Iterator>::Item: Debug + 'a,
306    for<'a> F: Fn(&'a str)
307 {}
308         "#,
309     )
310     .ok()
311     .unwrap();
312     let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap();
313
314     let mut predicates = where_clause.predicates();
315
316     let pred = predicates.next().unwrap();
317     let mut bounds = pred.type_bound_list().unwrap().bounds();
318
319     assert!(pred.for_token().is_none());
320     assert!(pred.generic_param_list().is_none());
321     assert_eq!("T", pred.ty().unwrap().syntax().text().to_string());
322     assert_bound("Clone", bounds.next());
323     assert_bound("Copy", bounds.next());
324     assert_bound("Debug", bounds.next());
325     assert_bound("'static", bounds.next());
326
327     let pred = predicates.next().unwrap();
328     let mut bounds = pred.type_bound_list().unwrap().bounds();
329
330     assert_eq!("'a", pred.lifetime().unwrap().lifetime_ident_token().unwrap().text());
331
332     assert_bound("'b", bounds.next());
333     assert_bound("'c", bounds.next());
334
335     let pred = predicates.next().unwrap();
336     let mut bounds = pred.type_bound_list().unwrap().bounds();
337
338     assert_eq!("Iterator::Item", pred.ty().unwrap().syntax().text().to_string());
339     assert_bound("'a", bounds.next());
340
341     let pred = predicates.next().unwrap();
342     let mut bounds = pred.type_bound_list().unwrap().bounds();
343
344     assert_eq!("Iterator::Item", pred.ty().unwrap().syntax().text().to_string());
345     assert_bound("Debug", bounds.next());
346     assert_bound("'a", bounds.next());
347
348     let pred = predicates.next().unwrap();
349     let mut bounds = pred.type_bound_list().unwrap().bounds();
350
351     assert_eq!("<T as Iterator>::Item", pred.ty().unwrap().syntax().text().to_string());
352     assert_bound("Debug", bounds.next());
353     assert_bound("'a", bounds.next());
354
355     let pred = predicates.next().unwrap();
356     let mut bounds = pred.type_bound_list().unwrap().bounds();
357
358     assert!(pred.for_token().is_some());
359     assert_eq!("<'a>", pred.generic_param_list().unwrap().syntax().text().to_string());
360     assert_eq!("F", pred.ty().unwrap().syntax().text().to_string());
361     assert_bound("Fn(&'a str)", bounds.next());
362 }