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