aboutsummaryrefslogtreecommitdiff
path: root/crates/syntax
diff options
context:
space:
mode:
Diffstat (limited to 'crates/syntax')
-rw-r--r--crates/syntax/src/ast/edit.rs95
-rw-r--r--crates/syntax/src/ast/make.rs50
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rast (renamed from crates/syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast)0
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rs (renamed from crates/syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rs)0
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast (renamed from crates/syntax/test_data/parser/inline/ok/0145_record_field_pat.rast)0
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs (renamed from crates/syntax/test_data/parser/inline/ok/0145_record_field_pat.rs)0
6 files changed, 125 insertions, 20 deletions
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 060b20966..dda0a0319 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -13,7 +13,7 @@ use crate::{
13 ast::{ 13 ast::{
14 self, 14 self,
15 make::{self, tokens}, 15 make::{self, tokens},
16 AstNode, TypeBoundsOwner, 16 AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner,
17 }, 17 },
18 AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind, 18 AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind,
19 SyntaxKind::{ATTR, COMMENT, WHITESPACE}, 19 SyntaxKind::{ATTR, COMMENT, WHITESPACE},
@@ -46,6 +46,19 @@ impl ast::Fn {
46 to_insert.push(body.syntax().clone().into()); 46 to_insert.push(body.syntax().clone().into());
47 self.replace_children(single_node(old_body_or_semi), to_insert) 47 self.replace_children(single_node(old_body_or_semi), to_insert)
48 } 48 }
49
50 #[must_use]
51 pub fn with_generic_param_list(&self, generic_args: ast::GenericParamList) -> ast::Fn {
52 if let Some(old) = self.generic_param_list() {
53 return self.replace_descendant(old, generic_args);
54 }
55
56 let anchor = self.name().expect("The function must have a name").syntax().clone();
57
58 let mut to_insert: ArrayVec<[SyntaxElement; 1]> = ArrayVec::new();
59 to_insert.push(generic_args.syntax().clone().into());
60 self.insert_children(InsertPosition::After(anchor.into()), to_insert)
61 }
49} 62}
50 63
51fn make_multiline<N>(node: N) -> N 64fn make_multiline<N>(node: N) -> N
@@ -80,6 +93,22 @@ where
80 } 93 }
81} 94}
82 95
96impl ast::Impl {
97 #[must_use]
98 pub fn with_assoc_item_list(&self, items: ast::AssocItemList) -> ast::Impl {
99 let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
100 if let Some(old_items) = self.assoc_item_list() {
101 let to_replace: SyntaxElement = old_items.syntax().clone().into();
102 to_insert.push(items.syntax().clone().into());
103 self.replace_children(single_node(to_replace), to_insert)
104 } else {
105 to_insert.push(make::tokens::single_space().into());
106 to_insert.push(items.syntax().clone().into());
107 self.insert_children(InsertPosition::Last, to_insert)
108 }
109 }
110}
111
83impl ast::AssocItemList { 112impl ast::AssocItemList {
84 #[must_use] 113 #[must_use]
85 pub fn append_items( 114 pub fn append_items(
@@ -260,16 +289,16 @@ impl ast::Path {
260 289
261impl ast::PathSegment { 290impl ast::PathSegment {
262 #[must_use] 291 #[must_use]
263 pub fn with_type_args(&self, type_args: ast::GenericArgList) -> ast::PathSegment { 292 pub fn with_generic_args(&self, type_args: ast::GenericArgList) -> ast::PathSegment {
264 self._with_type_args(type_args, false) 293 self._with_generic_args(type_args, false)
265 } 294 }
266 295
267 #[must_use] 296 #[must_use]
268 pub fn with_turbo_fish(&self, type_args: ast::GenericArgList) -> ast::PathSegment { 297 pub fn with_turbo_fish(&self, type_args: ast::GenericArgList) -> ast::PathSegment {
269 self._with_type_args(type_args, true) 298 self._with_generic_args(type_args, true)
270 } 299 }
271 300
272 fn _with_type_args(&self, type_args: ast::GenericArgList, turbo: bool) -> ast::PathSegment { 301 fn _with_generic_args(&self, type_args: ast::GenericArgList, turbo: bool) -> ast::PathSegment {
273 if let Some(old) = self.generic_arg_list() { 302 if let Some(old) = self.generic_arg_list() {
274 return self.replace_children( 303 return self.replace_children(
275 single_node(old.syntax().clone()), 304 single_node(old.syntax().clone()),
@@ -334,6 +363,7 @@ impl ast::UseTree {
334 self.clone() 363 self.clone()
335 } 364 }
336 365
366 /// Splits off the given prefix, making it the path component of the use tree, appending the rest of the path to all UseTreeList items.
337 #[must_use] 367 #[must_use]
338 pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree { 368 pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree {
339 let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() { 369 let suffix = if self.path().as_ref() == Some(prefix) && self.use_tree_list().is_none() {
@@ -459,6 +489,61 @@ impl ast::MatchArmList {
459 } 489 }
460} 490}
461 491
492impl ast::GenericParamList {
493 #[must_use]
494 pub fn append_params(
495 &self,
496 params: impl IntoIterator<Item = ast::GenericParam>,
497 ) -> ast::GenericParamList {
498 let mut res = self.clone();
499 params.into_iter().for_each(|it| res = res.append_param(it));
500 res
501 }
502
503 #[must_use]
504 pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList {
505 let space = tokens::single_space();
506
507 let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
508 if self.generic_params().next().is_some() {
509 to_insert.push(space.into());
510 }
511 to_insert.push(item.syntax().clone().into());
512
513 macro_rules! after_l_angle {
514 () => {{
515 let anchor = match self.l_angle_token() {
516 Some(it) => it.into(),
517 None => return self.clone(),
518 };
519 InsertPosition::After(anchor)
520 }};
521 }
522
523 macro_rules! after_field {
524 ($anchor:expr) => {
525 if let Some(comma) = $anchor
526 .syntax()
527 .siblings_with_tokens(Direction::Next)
528 .find(|it| it.kind() == T![,])
529 {
530 InsertPosition::After(comma)
531 } else {
532 to_insert.insert(0, make::token(T![,]).into());
533 InsertPosition::After($anchor.syntax().clone().into())
534 }
535 };
536 };
537
538 let position = match self.generic_params().last() {
539 Some(it) => after_field!(it),
540 None => after_l_angle!(),
541 };
542
543 self.insert_children(position, to_insert)
544 }
545}
546
462#[must_use] 547#[must_use]
463pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { 548pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
464 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() 549 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap()
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 7ba625990..4a0ffcbb0 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -21,6 +21,10 @@ pub fn ty(text: &str) -> ast::Type {
21 ast_from_text(&format!("impl {} for D {{}};", text)) 21 ast_from_text(&format!("impl {} for D {{}};", text))
22} 22}
23 23
24pub fn assoc_item_list() -> ast::AssocItemList {
25 ast_from_text("impl C for D {};")
26}
27
24pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { 28pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
25 ast_from_text(&format!("use {};", name_ref)) 29 ast_from_text(&format!("use {};", name_ref))
26} 30}
@@ -33,10 +37,15 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
33pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { 37pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
34 path_from_text(&format!("{}::{}", qual, segment)) 38 path_from_text(&format!("{}::{}", qual, segment))
35} 39}
40// FIXME: make this private
36pub fn path_from_text(text: &str) -> ast::Path { 41pub fn path_from_text(text: &str) -> ast::Path {
37 ast_from_text(text) 42 ast_from_text(text)
38} 43}
39 44
45pub fn glob_use_tree() -> ast::UseTree {
46 ast_from_text("use *;")
47}
48
40pub fn use_tree( 49pub fn use_tree(
41 path: ast::Path, 50 path: ast::Path,
42 use_tree_list: Option<ast::UseTreeList>, 51 use_tree_list: Option<ast::UseTreeList>,
@@ -144,10 +153,6 @@ fn expr_from_text(text: &str) -> ast::Expr {
144 ast_from_text(&format!("const C: () = {};", text)) 153 ast_from_text(&format!("const C: () = {};", text))
145} 154}
146 155
147pub fn try_expr_from_text(text: &str) -> Option<ast::Expr> {
148 try_ast_from_text(&format!("const C: () = {};", text))
149}
150
151pub fn condition(expr: ast::Expr, pattern: Option<ast::Pat>) -> ast::Condition { 156pub fn condition(expr: ast::Expr, pattern: Option<ast::Pat>) -> ast::Condition {
152 match pattern { 157 match pattern {
153 None => ast_from_text(&format!("const _: () = while {} {{}};", expr)), 158 None => ast_from_text(&format!("const _: () = while {} {{}};", expr)),
@@ -297,6 +302,21 @@ pub fn param_list(pats: impl IntoIterator<Item = ast::Param>) -> ast::ParamList
297 ast_from_text(&format!("fn f({}) {{ }}", args)) 302 ast_from_text(&format!("fn f({}) {{ }}", args))
298} 303}
299 304
305pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam {
306 let bound = match ty {
307 Some(it) => format!(": {}", it),
308 None => String::new(),
309 };
310 ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound))
311}
312
313pub fn generic_param_list(
314 pats: impl IntoIterator<Item = ast::GenericParam>,
315) -> ast::GenericParamList {
316 let args = pats.into_iter().join(", ");
317 ast_from_text(&format!("fn f<{}>() {{ }}", args))
318}
319
300pub fn visibility_pub_crate() -> ast::Visibility { 320pub fn visibility_pub_crate() -> ast::Visibility {
301 ast_from_text("pub(crate) struct S") 321 ast_from_text("pub(crate) struct S")
302} 322}
@@ -332,16 +352,6 @@ fn ast_from_text<N: AstNode>(text: &str) -> N {
332 node 352 node
333} 353}
334 354
335fn try_ast_from_text<N: AstNode>(text: &str) -> Option<N> {
336 let parse = SourceFile::parse(text);
337 let node = parse.tree().syntax().descendants().find_map(N::cast)?;
338 let node = node.syntax().clone();
339 let node = unroot(node);
340 let node = N::cast(node).unwrap();
341 assert_eq!(node.syntax().text_range().start(), 0.into());
342 Some(node)
343}
344
345fn unroot(n: SyntaxNode) -> SyntaxNode { 355fn unroot(n: SyntaxNode) -> SyntaxNode {
346 SyntaxNode::new_root(n.green().clone()) 356 SyntaxNode::new_root(n.green().clone())
347} 357}
@@ -352,7 +362,7 @@ pub mod tokens {
352 use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken}; 362 use crate::{ast, AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken};
353 363
354 pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = 364 pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> =
355 Lazy::new(|| SourceFile::parse("const C: <()>::Item = (1 != 1, 2 == 2, !true)\n;")); 365 Lazy::new(|| SourceFile::parse("const C: <()>::Item = (1 != 1, 2 == 2, !true)\n;\n\n"));
356 366
357 pub fn single_space() -> SyntaxToken { 367 pub fn single_space() -> SyntaxToken {
358 SOURCE_FILE 368 SOURCE_FILE
@@ -392,6 +402,16 @@ pub mod tokens {
392 .unwrap() 402 .unwrap()
393 } 403 }
394 404
405 pub fn blank_line() -> SyntaxToken {
406 SOURCE_FILE
407 .tree()
408 .syntax()
409 .descendants_with_tokens()
410 .filter_map(|it| it.into_token())
411 .find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n\n")
412 .unwrap()
413 }
414
395 pub struct WsBuilder(SourceFile); 415 pub struct WsBuilder(SourceFile);
396 416
397 impl WsBuilder { 417 impl WsBuilder {
diff --git a/crates/syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast b/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rast
index 866e60ed8..866e60ed8 100644
--- a/crates/syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rast
diff --git a/crates/syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rs b/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rs
index da3412fa8..da3412fa8 100644
--- a/crates/syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0102_record_pat_field_list.rs
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_field_pat.rast b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast
index 925409bdf..925409bdf 100644
--- a/crates/syntax/test_data/parser/inline/ok/0145_record_field_pat.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rast
diff --git a/crates/syntax/test_data/parser/inline/ok/0145_record_field_pat.rs b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs
index 26b1d5f89..26b1d5f89 100644
--- a/crates/syntax/test_data/parser/inline/ok/0145_record_field_pat.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0145_record_pat_field.rs