1pub mod edit;
4pub mod edit_in_place;
5mod expr_ext;
6mod generated;
7pub mod make;
8mod node_ext;
9mod operators;
10pub mod prec;
11pub mod syntax_factory;
12mod token_ext;
13mod traits;
14
15use std::marker::PhantomData;
16
17use either::Either;
18
19use crate::{
20 syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
21 SyntaxKind,
22};
23
24pub use self::{
25 expr_ext::{ArrayExprKind, BlockModifier, CallableExpr, ElseBranch, LiteralKind},
26 generated::{nodes::*, tokens::*},
27 node_ext::{
28 AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
29 SlicePatComponents, StructKind, TraitOrAlias, TypeBoundKind, TypeOrConstParam,
30 VisibilityKind,
31 },
32 operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
33 token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix},
34 traits::{
35 AttrDocCommentIter, DocCommentIter, HasArgList, HasAttrs, HasDocComments, HasGenericArgs,
36 HasGenericParams, HasLoopBody, HasModuleItem, HasName, HasTypeBounds, HasVisibility,
37 },
38};
39
40pub trait AstNode {
45 fn kind() -> SyntaxKind
47 where
48 Self: Sized,
49 {
50 panic!("dynamic `SyntaxKind` for `AstNode::kind()`")
51 }
52
53 fn can_cast(kind: SyntaxKind) -> bool
54 where
55 Self: Sized;
56
57 fn cast(syntax: SyntaxNode) -> Option<Self>
58 where
59 Self: Sized;
60
61 fn syntax(&self) -> &SyntaxNode;
62 fn clone_for_update(&self) -> Self
63 where
64 Self: Sized,
65 {
66 Self::cast(self.syntax().clone_for_update()).unwrap()
67 }
68 fn clone_subtree(&self) -> Self
69 where
70 Self: Sized,
71 {
72 Self::cast(self.syntax().clone_subtree()).unwrap()
73 }
74}
75
76pub trait AstToken {
78 fn can_cast(token: SyntaxKind) -> bool
79 where
80 Self: Sized;
81
82 fn cast(syntax: SyntaxToken) -> Option<Self>
83 where
84 Self: Sized;
85
86 fn syntax(&self) -> &SyntaxToken;
87
88 fn text(&self) -> &str {
89 self.syntax().text()
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct AstChildren<N> {
96 inner: SyntaxNodeChildren,
97 ph: PhantomData<N>,
98}
99
100impl<N> AstChildren<N> {
101 fn new(parent: &SyntaxNode) -> Self {
102 AstChildren { inner: parent.children(), ph: PhantomData }
103 }
104}
105
106impl<N: AstNode> Iterator for AstChildren<N> {
107 type Item = N;
108 fn next(&mut self) -> Option<N> {
109 self.inner.find_map(N::cast)
110 }
111}
112
113impl<L, R> AstNode for Either<L, R>
114where
115 L: AstNode,
116 R: AstNode,
117{
118 fn can_cast(kind: SyntaxKind) -> bool
119 where
120 Self: Sized,
121 {
122 L::can_cast(kind) || R::can_cast(kind)
123 }
124
125 fn cast(syntax: SyntaxNode) -> Option<Self>
126 where
127 Self: Sized,
128 {
129 if L::can_cast(syntax.kind()) {
130 L::cast(syntax).map(Either::Left)
131 } else {
132 R::cast(syntax).map(Either::Right)
133 }
134 }
135
136 fn syntax(&self) -> &SyntaxNode {
137 self.as_ref().either(L::syntax, R::syntax)
138 }
139}
140
141impl<L, R> HasAttrs for Either<L, R>
142where
143 L: HasAttrs,
144 R: HasAttrs,
145{
146}
147
148pub trait RangeItem {
150 type Bound;
151
152 fn start(&self) -> Option<Self::Bound>;
153 fn end(&self) -> Option<Self::Bound>;
154 fn op_kind(&self) -> Option<RangeOp>;
155 fn op_token(&self) -> Option<SyntaxToken>;
156}
157
158mod support {
159 use super::{AstChildren, AstNode, SyntaxKind, SyntaxNode, SyntaxToken};
160
161 #[inline]
162 pub(super) fn child<N: AstNode>(parent: &SyntaxNode) -> Option<N> {
163 parent.children().find_map(N::cast)
164 }
165
166 #[inline]
167 pub(super) fn children<N: AstNode>(parent: &SyntaxNode) -> AstChildren<N> {
168 AstChildren::new(parent)
169 }
170
171 #[inline]
172 pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> {
173 parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind)
174 }
175}
176
177#[test]
178fn assert_ast_is_dyn_compatible() {
179 fn _f(_: &dyn AstNode, _: &dyn HasName) {}
180}
181
182#[test]
183fn test_doc_comment_none() {
184 let file = SourceFile::parse(
185 r#"
186 // non-doc
187 mod foo {}
188 "#,
189 parser::Edition::CURRENT,
190 )
191 .ok()
192 .unwrap();
193 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
194 assert!(module.doc_comments().doc_comment_text().is_none());
195}
196
197#[test]
198fn test_outer_doc_comment_of_items() {
199 let file = SourceFile::parse(
200 r#"
201 /// doc
202 // non-doc
203 mod foo {}
204 "#,
205 parser::Edition::CURRENT,
206 )
207 .ok()
208 .unwrap();
209 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
210 assert_eq!(" doc", module.doc_comments().doc_comment_text().unwrap());
211}
212
213#[test]
214fn test_inner_doc_comment_of_items() {
215 let file = SourceFile::parse(
216 r#"
217 //! doc
218 // non-doc
219 mod foo {}
220 "#,
221 parser::Edition::CURRENT,
222 )
223 .ok()
224 .unwrap();
225 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
226 assert!(module.doc_comments().doc_comment_text().is_none());
227}
228
229#[test]
230fn test_doc_comment_of_statics() {
231 let file = SourceFile::parse(
232 r#"
233 /// Number of levels
234 static LEVELS: i32 = 0;
235 "#,
236 parser::Edition::CURRENT,
237 )
238 .ok()
239 .unwrap();
240 let st = file.syntax().descendants().find_map(Static::cast).unwrap();
241 assert_eq!(" Number of levels", st.doc_comments().doc_comment_text().unwrap());
242}
243
244#[test]
245fn test_doc_comment_preserves_indents() {
246 let file = SourceFile::parse(
247 r#"
248 /// doc1
249 /// ```
250 /// fn foo() {
251 /// // ...
252 /// }
253 /// ```
254 mod foo {}
255 "#,
256 parser::Edition::CURRENT,
257 )
258 .ok()
259 .unwrap();
260 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
261 assert_eq!(
262 " doc1\n ```\n fn foo() {\n // ...\n }\n ```",
263 module.doc_comments().doc_comment_text().unwrap()
264 );
265}
266
267#[test]
268fn test_doc_comment_preserves_newlines() {
269 let file = SourceFile::parse(
270 r#"
271 /// this
272 /// is
273 /// mod
274 /// foo
275 mod foo {}
276 "#,
277 parser::Edition::CURRENT,
278 )
279 .ok()
280 .unwrap();
281 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
282 assert_eq!(" this\n is\n mod\n foo", module.doc_comments().doc_comment_text().unwrap());
283}
284
285#[test]
286fn test_doc_comment_single_line_block_strips_suffix() {
287 let file = SourceFile::parse(
288 r#"
289 /** this is mod foo*/
290 mod foo {}
291 "#,
292 parser::Edition::CURRENT,
293 )
294 .ok()
295 .unwrap();
296 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
297 assert_eq!(" this is mod foo", module.doc_comments().doc_comment_text().unwrap());
298}
299
300#[test]
301fn test_doc_comment_single_line_block_strips_suffix_whitespace() {
302 let file = SourceFile::parse(
303 r#"
304 /** this is mod foo */
305 mod foo {}
306 "#,
307 parser::Edition::CURRENT,
308 )
309 .ok()
310 .unwrap();
311 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
312 assert_eq!(" this is mod foo ", module.doc_comments().doc_comment_text().unwrap());
313}
314
315#[test]
316fn test_doc_comment_multi_line_block_strips_suffix() {
317 let file = SourceFile::parse(
318 r#"
319 /**
320 this
321 is
322 mod foo
323 */
324 mod foo {}
325 "#,
326 parser::Edition::CURRENT,
327 )
328 .ok()
329 .unwrap();
330 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
331 assert_eq!(
332 "\n this\n is\n mod foo\n ",
333 module.doc_comments().doc_comment_text().unwrap()
334 );
335}
336
337#[test]
338fn test_comments_preserve_trailing_whitespace() {
339 let file = SourceFile::parse(
340 "\n/// Representation of a Realm. \n/// In the specification these are called Realm Records.\nstruct Realm {}", parser::Edition::CURRENT,
341 )
342 .ok()
343 .unwrap();
344 let def = file.syntax().descendants().find_map(Struct::cast).unwrap();
345 assert_eq!(
346 " Representation of a Realm. \n In the specification these are called Realm Records.",
347 def.doc_comments().doc_comment_text().unwrap()
348 );
349}
350
351#[test]
352fn test_four_slash_line_comment() {
353 let file = SourceFile::parse(
354 r#"
355 //// too many slashes to be a doc comment
356 /// doc comment
357 mod foo {}
358 "#,
359 parser::Edition::CURRENT,
360 )
361 .ok()
362 .unwrap();
363 let module = file.syntax().descendants().find_map(Module::cast).unwrap();
364 assert_eq!(" doc comment", module.doc_comments().doc_comment_text().unwrap());
365}
366
367#[test]
368fn test_where_predicates() {
369 fn assert_bound(text: &str, bound: Option<TypeBound>) {
370 assert_eq!(text, bound.unwrap().syntax().text().to_string());
371 }
372
373 let file = SourceFile::parse(
374 r#"
375fn foo()
376where
377 T: Clone + Copy + Debug + 'static,
378 'a: 'b + 'c,
379 Iterator::Item: 'a + Debug,
380 Iterator::Item: Debug + 'a,
381 <T as Iterator>::Item: Debug + 'a,
382 for<'a> F: Fn(&'a str)
383{}
384 "#,
385 parser::Edition::CURRENT,
386 )
387 .ok()
388 .unwrap();
389 let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap();
390
391 let mut predicates = where_clause.predicates();
392
393 let pred = predicates.next().unwrap();
394 let mut bounds = pred.type_bound_list().unwrap().bounds();
395
396 assert!(pred.for_token().is_none());
397 assert!(pred.generic_param_list().is_none());
398 assert_eq!("T", pred.ty().unwrap().syntax().text().to_string());
399 assert_bound("Clone", bounds.next());
400 assert_bound("Copy", bounds.next());
401 assert_bound("Debug", bounds.next());
402 assert_bound("'static", bounds.next());
403
404 let pred = predicates.next().unwrap();
405 let mut bounds = pred.type_bound_list().unwrap().bounds();
406
407 assert_eq!("'a", pred.lifetime().unwrap().lifetime_ident_token().unwrap().text());
408
409 assert_bound("'b", bounds.next());
410 assert_bound("'c", bounds.next());
411
412 let pred = predicates.next().unwrap();
413 let mut bounds = pred.type_bound_list().unwrap().bounds();
414
415 assert_eq!("Iterator::Item", pred.ty().unwrap().syntax().text().to_string());
416 assert_bound("'a", bounds.next());
417
418 let pred = predicates.next().unwrap();
419 let mut bounds = pred.type_bound_list().unwrap().bounds();
420
421 assert_eq!("Iterator::Item", pred.ty().unwrap().syntax().text().to_string());
422 assert_bound("Debug", bounds.next());
423 assert_bound("'a", bounds.next());
424
425 let pred = predicates.next().unwrap();
426 let mut bounds = pred.type_bound_list().unwrap().bounds();
427
428 assert_eq!("<T as Iterator>::Item", pred.ty().unwrap().syntax().text().to_string());
429 assert_bound("Debug", bounds.next());
430 assert_bound("'a", bounds.next());
431
432 let pred = predicates.next().unwrap();
433 let mut bounds = pred.type_bound_list().unwrap().bounds();
434
435 assert!(pred.for_token().is_some());
436 assert_eq!("<'a>", pred.generic_param_list().unwrap().syntax().text().to_string());
437 assert_eq!("F", pred.ty().unwrap().syntax().text().to_string());
438 assert_bound("Fn(&'a str)", bounds.next());
439}