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