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