aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/fill_match_arms.rs21
-rw-r--r--crates/assists/src/handlers/replace_if_let_with_match.rs73
-rw-r--r--crates/assists/src/utils.rs19
-rw-r--r--crates/ide_db/src/ty_filter.rs15
-rw-r--r--crates/mbe/Cargo.toml3
-rw-r--r--crates/mbe/src/lib.rs72
-rw-r--r--crates/mbe/src/mbe_expander.rs9
-rw-r--r--crates/mbe/src/tests.rs233
-rw-r--r--crates/paths/src/lib.rs51
9 files changed, 384 insertions, 112 deletions
diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs
index da47187e4..7663d211d 100644
--- a/crates/assists/src/handlers/fill_match_arms.rs
+++ b/crates/assists/src/handlers/fill_match_arms.rs
@@ -8,7 +8,7 @@ use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
8use test_utils::mark; 8use test_utils::mark;
9 9
10use crate::{ 10use crate::{
11 utils::{render_snippet, Cursor}, 11 utils::{does_pat_match_variant, render_snippet, Cursor},
12 AssistContext, AssistId, AssistKind, Assists, 12 AssistContext, AssistId, AssistKind, Assists,
13}; 13};
14 14
@@ -147,25 +147,6 @@ fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
147 }) 147 })
148} 148}
149 149
150fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
151 let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text());
152
153 let pat_head = match pat {
154 Pat::IdentPat(bind_pat) => {
155 if let Some(p) = bind_pat.pat() {
156 first_node_text(&p)
157 } else {
158 return false;
159 }
160 }
161 pat => first_node_text(pat),
162 };
163
164 let var_head = first_node_text(var);
165
166 pat_head == var_head
167}
168
169fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { 150fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
170 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { 151 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
171 Some(Adt::Enum(e)) => Some(e), 152 Some(Adt::Enum(e)) => Some(e),
diff --git a/crates/assists/src/handlers/replace_if_let_with_match.rs b/crates/assists/src/handlers/replace_if_let_with_match.rs
index aee3397ab..aee880625 100644
--- a/crates/assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/assists/src/handlers/replace_if_let_with_match.rs
@@ -10,7 +10,10 @@ use syntax::{
10 AstNode, 10 AstNode,
11}; 11};
12 12
13use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; 13use crate::{
14 utils::{does_pat_match_variant, unwrap_trivial_block},
15 AssistContext, AssistId, AssistKind, Assists,
16};
14 17
15// Assist: replace_if_let_with_match 18// Assist: replace_if_let_with_match
16// 19//
@@ -66,7 +69,13 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
66 .sema 69 .sema
67 .type_of_pat(&pat) 70 .type_of_pat(&pat)
68 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)) 71 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
69 .map(|it| it.sad_pattern()) 72 .map(|it| {
73 if does_pat_match_variant(&pat, &it.sad_pattern()) {
74 it.happy_pattern()
75 } else {
76 it.sad_pattern()
77 }
78 })
70 .unwrap_or_else(|| make::wildcard_pat().into()); 79 .unwrap_or_else(|| make::wildcard_pat().into());
71 let else_expr = unwrap_trivial_block(else_block); 80 let else_expr = unwrap_trivial_block(else_block);
72 make::match_arm(vec![pattern], else_expr) 81 make::match_arm(vec![pattern], else_expr)
@@ -279,6 +288,36 @@ fn foo(x: Option<i32>) {
279 } 288 }
280 289
281 #[test] 290 #[test]
291 fn special_case_inverted_option() {
292 check_assist(
293 replace_if_let_with_match,
294 r#"
295enum Option<T> { Some(T), None }
296use Option::*;
297
298fn foo(x: Option<i32>) {
299 $0if let None = x {
300 println!("none")
301 } else {
302 println!("some")
303 }
304}
305 "#,
306 r#"
307enum Option<T> { Some(T), None }
308use Option::*;
309
310fn foo(x: Option<i32>) {
311 match x {
312 None => println!("none"),
313 Some(_) => println!("some"),
314 }
315}
316 "#,
317 );
318 }
319
320 #[test]
282 fn special_case_result() { 321 fn special_case_result() {
283 check_assist( 322 check_assist(
284 replace_if_let_with_match, 323 replace_if_let_with_match,
@@ -309,6 +348,36 @@ fn foo(x: Result<i32, ()>) {
309 } 348 }
310 349
311 #[test] 350 #[test]
351 fn special_case_inverted_result() {
352 check_assist(
353 replace_if_let_with_match,
354 r#"
355enum Result<T, E> { Ok(T), Err(E) }
356use Result::*;
357
358fn foo(x: Result<i32, ()>) {
359 $0if let Err(x) = x {
360 println!("{}", x)
361 } else {
362 println!("ok")
363 }
364}
365 "#,
366 r#"
367enum Result<T, E> { Ok(T), Err(E) }
368use Result::*;
369
370fn foo(x: Result<i32, ()>) {
371 match x {
372 Err(x) => println!("{}", x),
373 Ok(_) => println!("ok"),
374 }
375}
376 "#,
377 );
378 }
379
380 #[test]
312 fn nested_indent() { 381 fn nested_indent() {
313 check_assist( 382 check_assist(
314 replace_if_let_with_match, 383 replace_if_let_with_match,
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 44c35bafa..4e762e18b 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -248,3 +248,22 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
248pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { 248pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
249 [Direction::Next, Direction::Prev].iter().copied() 249 [Direction::Next, Direction::Prev].iter().copied()
250} 250}
251
252pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
253 let first_node_text = |pat: &ast::Pat| pat.syntax().first_child().map(|node| node.text());
254
255 let pat_head = match pat {
256 ast::Pat::IdentPat(bind_pat) => {
257 if let Some(p) = bind_pat.pat() {
258 first_node_text(&p)
259 } else {
260 return pat.syntax().text() == var.syntax().text();
261 }
262 }
263 pat => first_node_text(pat),
264 };
265
266 let var_head = first_node_text(var);
267
268 pat_head == var_head
269}
diff --git a/crates/ide_db/src/ty_filter.rs b/crates/ide_db/src/ty_filter.rs
index 63a945282..f8406851b 100644
--- a/crates/ide_db/src/ty_filter.rs
+++ b/crates/ide_db/src/ty_filter.rs
@@ -49,6 +49,21 @@ impl TryEnum {
49 } 49 }
50 } 50 }
51 51
52 pub fn happy_pattern(self) -> ast::Pat {
53 match self {
54 TryEnum::Result => make::tuple_struct_pat(
55 make::path_unqualified(make::path_segment(make::name_ref("Ok"))),
56 iter::once(make::wildcard_pat().into()),
57 )
58 .into(),
59 TryEnum::Option => make::tuple_struct_pat(
60 make::path_unqualified(make::path_segment(make::name_ref("Some"))),
61 iter::once(make::wildcard_pat().into()),
62 )
63 .into(),
64 }
65 }
66
52 fn type_name(self) -> &'static str { 67 fn type_name(self) -> &'static str {
53 match self { 68 match self {
54 TryEnum::Result => "Result", 69 TryEnum::Result => "Result",
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml
index af80e2be3..43bc10490 100644
--- a/crates/mbe/Cargo.toml
+++ b/crates/mbe/Cargo.toml
@@ -17,6 +17,5 @@ log = "0.4.8"
17syntax = { path = "../syntax", version = "0.0.0" } 17syntax = { path = "../syntax", version = "0.0.0" }
18parser = { path = "../parser", version = "0.0.0" } 18parser = { path = "../parser", version = "0.0.0" }
19tt = { path = "../tt", version = "0.0.0" } 19tt = { path = "../tt", version = "0.0.0" }
20
21[dev-dependencies]
22test_utils = { path = "../test_utils" } 20test_utils = { path = "../test_utils" }
21
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 19543d777..35cde5f10 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -14,6 +14,7 @@ mod tests;
14 14
15use std::fmt; 15use std::fmt;
16 16
17use test_utils::mark;
17pub use tt::{Delimiter, DelimiterKind, Punct}; 18pub use tt::{Delimiter, DelimiterKind, Punct};
18 19
19use crate::{ 20use crate::{
@@ -76,6 +77,14 @@ pub struct MacroRules {
76 shift: Shift, 77 shift: Shift,
77} 78}
78 79
80/// For Macro 2.0
81#[derive(Clone, Debug, PartialEq, Eq)]
82pub struct MacroDef {
83 rules: Vec<Rule>,
84 /// Highest id of the token we have in TokenMap
85 shift: Shift,
86}
87
79#[derive(Clone, Debug, PartialEq, Eq)] 88#[derive(Clone, Debug, PartialEq, Eq)]
80struct Rule { 89struct Rule {
81 lhs: MetaTemplate, 90 lhs: MetaTemplate,
@@ -179,7 +188,7 @@ impl MacroRules {
179 let mut src = TtIter::new(tt); 188 let mut src = TtIter::new(tt);
180 let mut rules = Vec::new(); 189 let mut rules = Vec::new();
181 while src.len() > 0 { 190 while src.len() > 0 {
182 let rule = Rule::parse(&mut src)?; 191 let rule = Rule::parse(&mut src, true)?;
183 rules.push(rule); 192 rules.push(rule);
184 if let Err(()) = src.expect_char(';') { 193 if let Err(()) = src.expect_char(';') {
185 if src.len() > 0 { 194 if src.len() > 0 {
@@ -200,7 +209,58 @@ impl MacroRules {
200 // apply shift 209 // apply shift
201 let mut tt = tt.clone(); 210 let mut tt = tt.clone();
202 self.shift.shift_all(&mut tt); 211 self.shift.shift_all(&mut tt);
203 mbe_expander::expand(self, &tt) 212 mbe_expander::expand_rules(&self.rules, &tt)
213 }
214
215 pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
216 self.shift.shift(id)
217 }
218
219 pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, Origin) {
220 match self.shift.unshift(id) {
221 Some(id) => (id, Origin::Call),
222 None => (id, Origin::Def),
223 }
224 }
225}
226
227impl MacroDef {
228 pub fn parse(tt: &tt::Subtree) -> Result<MacroDef, ParseError> {
229 let mut src = TtIter::new(tt);
230 let mut rules = Vec::new();
231
232 if Some(tt::DelimiterKind::Brace) == tt.delimiter_kind() {
233 mark::hit!(parse_macro_def_rules);
234 while src.len() > 0 {
235 let rule = Rule::parse(&mut src, true)?;
236 rules.push(rule);
237 if let Err(()) = src.expect_char(';') {
238 if src.len() > 0 {
239 return Err(ParseError::Expected("expected `;`".to_string()));
240 }
241 break;
242 }
243 }
244 } else {
245 mark::hit!(parse_macro_def_simple);
246 let rule = Rule::parse(&mut src, false)?;
247 if src.len() != 0 {
248 return Err(ParseError::Expected("remain tokens in macro def".to_string()));
249 }
250 rules.push(rule);
251 }
252 for rule in rules.iter() {
253 validate(&rule.lhs)?;
254 }
255
256 Ok(MacroDef { rules, shift: Shift::new(tt) })
257 }
258
259 pub fn expand(&self, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> {
260 // apply shift
261 let mut tt = tt.clone();
262 self.shift.shift_all(&mut tt);
263 mbe_expander::expand_rules(&self.rules, &tt)
204 } 264 }
205 265
206 pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { 266 pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
@@ -216,12 +276,14 @@ impl MacroRules {
216} 276}
217 277
218impl Rule { 278impl Rule {
219 fn parse(src: &mut TtIter) -> Result<Rule, ParseError> { 279 fn parse(src: &mut TtIter, expect_arrow: bool) -> Result<Rule, ParseError> {
220 let lhs = src 280 let lhs = src
221 .expect_subtree() 281 .expect_subtree()
222 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; 282 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?;
223 src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?; 283 if expect_arrow {
224 src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?; 284 src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?;
285 src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?;
286 }
225 let rhs = src 287 let rhs = src
226 .expect_subtree() 288 .expect_subtree()
227 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; 289 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?;
diff --git a/crates/mbe/src/mbe_expander.rs b/crates/mbe/src/mbe_expander.rs
index a80b73db4..802c8fb0f 100644
--- a/crates/mbe/src/mbe_expander.rs
+++ b/crates/mbe/src/mbe_expander.rs
@@ -10,11 +10,10 @@ use syntax::SmolStr;
10 10
11use crate::{ExpandError, ExpandResult}; 11use crate::{ExpandError, ExpandResult};
12 12
13pub(crate) fn expand(rules: &crate::MacroRules, input: &tt::Subtree) -> ExpandResult<tt::Subtree> { 13pub(crate) fn expand_rules(
14 expand_rules(&rules.rules, input) 14 rules: &[crate::Rule],
15} 15 input: &tt::Subtree,
16 16) -> ExpandResult<tt::Subtree> {
17fn expand_rules(rules: &[crate::Rule], input: &tt::Subtree) -> ExpandResult<tt::Subtree> {
18 let mut match_: Option<(matcher::Match, &crate::Rule)> = None; 17 let mut match_: Option<(matcher::Match, &crate::Rule)> = None;
19 for rule in rules { 18 for rule in rules {
20 let new_match = match matcher::match_(&rule.lhs, input) { 19 let new_match = match matcher::match_(&rule.lhs, input) {
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index bd2977ebd..8d978163d 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -6,7 +6,7 @@ use syntax::{
6 SyntaxKind::{ERROR, IDENT}, 6 SyntaxKind::{ERROR, IDENT},
7 SyntaxNode, WalkEvent, T, 7 SyntaxNode, WalkEvent, T,
8}; 8};
9use test_utils::assert_eq_text; 9use test_utils::{assert_eq_text, mark};
10 10
11use super::*; 11use super::*;
12 12
@@ -675,6 +675,36 @@ fn test_match_literal() {
675 .assert_expand_items("foo! ['('];", "fn foo () {}"); 675 .assert_expand_items("foo! ['('];", "fn foo () {}");
676} 676}
677 677
678#[test]
679fn test_parse_macro_def_simple() {
680 mark::check!(parse_macro_def_simple);
681
682 parse_macro2(
683 r#"
684macro foo($id:ident) {
685 fn $id() {}
686}
687"#,
688 )
689 .assert_expand_items("foo!(bar);", "fn bar () {}");
690}
691
692#[test]
693fn test_parse_macro_def_rules() {
694 mark::check!(parse_macro_def_rules);
695
696 parse_macro2(
697 r#"
698macro foo {
699 ($id:ident) => {
700 fn $id() {}
701 }
702}
703"#,
704 )
705 .assert_expand_items("foo!(bar);", "fn bar () {}");
706}
707
678// The following tests are port from intellij-rust directly 708// The following tests are port from intellij-rust directly
679// https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt 709// https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt
680 710
@@ -1699,95 +1729,122 @@ pub(crate) struct MacroFixture {
1699 rules: MacroRules, 1729 rules: MacroRules,
1700} 1730}
1701 1731
1702impl MacroFixture { 1732pub(crate) struct MacroFixture2 {
1703 pub(crate) fn expand_tt(&self, invocation: &str) -> tt::Subtree { 1733 rules: MacroDef,
1704 self.try_expand_tt(invocation).unwrap() 1734}
1705 }
1706
1707 fn try_expand_tt(&self, invocation: &str) -> Result<tt::Subtree, ExpandError> {
1708 let source_file = ast::SourceFile::parse(invocation).tree();
1709 let macro_invocation =
1710 source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
1711
1712 let (invocation_tt, _) = ast_to_token_tree(&macro_invocation.token_tree().unwrap())
1713 .ok_or_else(|| ExpandError::ConversionError)?;
1714 1735
1715 self.rules.expand(&invocation_tt).result() 1736macro_rules! impl_fixture {
1716 } 1737 ($name:ident) => {
1738 impl $name {
1739 pub(crate) fn expand_tt(&self, invocation: &str) -> tt::Subtree {
1740 self.try_expand_tt(invocation).unwrap()
1741 }
1717 1742
1718 fn assert_expand_err(&self, invocation: &str, err: &ExpandError) { 1743 fn try_expand_tt(&self, invocation: &str) -> Result<tt::Subtree, ExpandError> {
1719 assert_eq!(self.try_expand_tt(invocation).as_ref(), Err(err)); 1744 let source_file = ast::SourceFile::parse(invocation).tree();
1720 } 1745 let macro_invocation =
1746 source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
1721 1747
1722 fn expand_items(&self, invocation: &str) -> SyntaxNode { 1748 let (invocation_tt, _) = ast_to_token_tree(&macro_invocation.token_tree().unwrap())
1723 let expanded = self.expand_tt(invocation); 1749 .ok_or_else(|| ExpandError::ConversionError)?;
1724 token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap().0.syntax_node()
1725 }
1726 1750
1727 fn expand_statements(&self, invocation: &str) -> SyntaxNode { 1751 self.rules.expand(&invocation_tt).result()
1728 let expanded = self.expand_tt(invocation); 1752 }
1729 token_tree_to_syntax_node(&expanded, FragmentKind::Statements).unwrap().0.syntax_node()
1730 }
1731 1753
1732 fn expand_expr(&self, invocation: &str) -> SyntaxNode { 1754 #[allow(unused)]
1733 let expanded = self.expand_tt(invocation); 1755 fn assert_expand_err(&self, invocation: &str, err: &ExpandError) {
1734 token_tree_to_syntax_node(&expanded, FragmentKind::Expr).unwrap().0.syntax_node() 1756 assert_eq!(self.try_expand_tt(invocation).as_ref(), Err(err));
1735 } 1757 }
1736 1758
1737 fn assert_expand_tt(&self, invocation: &str, expected: &str) { 1759 #[allow(unused)]
1738 let expansion = self.expand_tt(invocation); 1760 fn expand_items(&self, invocation: &str) -> SyntaxNode {
1739 assert_eq!(expansion.to_string(), expected); 1761 let expanded = self.expand_tt(invocation);
1740 } 1762 token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap().0.syntax_node()
1763 }
1741 1764
1742 fn assert_expand(&self, invocation: &str, expected: &str) { 1765 #[allow(unused)]
1743 let expansion = self.expand_tt(invocation); 1766 fn expand_statements(&self, invocation: &str) -> SyntaxNode {
1744 let actual = format!("{:?}", expansion); 1767 let expanded = self.expand_tt(invocation);
1745 test_utils::assert_eq_text!(&expected.trim(), &actual.trim()); 1768 token_tree_to_syntax_node(&expanded, FragmentKind::Statements)
1746 } 1769 .unwrap()
1770 .0
1771 .syntax_node()
1772 }
1747 1773
1748 fn assert_expand_items(&self, invocation: &str, expected: &str) -> &MacroFixture { 1774 #[allow(unused)]
1749 self.assert_expansion(FragmentKind::Items, invocation, expected); 1775 fn expand_expr(&self, invocation: &str) -> SyntaxNode {
1750 self 1776 let expanded = self.expand_tt(invocation);
1751 } 1777 token_tree_to_syntax_node(&expanded, FragmentKind::Expr).unwrap().0.syntax_node()
1778 }
1752 1779
1753 fn assert_expand_statements(&self, invocation: &str, expected: &str) -> &MacroFixture { 1780 #[allow(unused)]
1754 self.assert_expansion(FragmentKind::Statements, invocation, expected); 1781 fn assert_expand_tt(&self, invocation: &str, expected: &str) {
1755 self 1782 let expansion = self.expand_tt(invocation);
1756 } 1783 assert_eq!(expansion.to_string(), expected);
1784 }
1757 1785
1758 fn assert_expansion(&self, kind: FragmentKind, invocation: &str, expected: &str) { 1786 #[allow(unused)]
1759 let expanded = self.expand_tt(invocation); 1787 fn assert_expand(&self, invocation: &str, expected: &str) {
1760 assert_eq!(expanded.to_string(), expected); 1788 let expansion = self.expand_tt(invocation);
1761 1789 let actual = format!("{:?}", expansion);
1762 let expected = expected.replace("$crate", "C_C__C"); 1790 test_utils::assert_eq_text!(&expected.trim(), &actual.trim());
1763 1791 }
1764 // wrap the given text to a macro call
1765 let expected = {
1766 let wrapped = format!("wrap_macro!( {} )", expected);
1767 let wrapped = ast::SourceFile::parse(&wrapped);
1768 let wrapped =
1769 wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
1770 let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0;
1771 wrapped.delimiter = None;
1772 wrapped
1773 };
1774 1792
1775 let expanded_tree = token_tree_to_syntax_node(&expanded, kind).unwrap().0.syntax_node(); 1793 fn assert_expand_items(&self, invocation: &str, expected: &str) -> &$name {
1776 let expanded_tree = debug_dump_ignore_spaces(&expanded_tree).trim().to_string(); 1794 self.assert_expansion(FragmentKind::Items, invocation, expected);
1795 self
1796 }
1777 1797
1778 let expected_tree = token_tree_to_syntax_node(&expected, kind).unwrap().0.syntax_node(); 1798 #[allow(unused)]
1779 let expected_tree = debug_dump_ignore_spaces(&expected_tree).trim().to_string(); 1799 fn assert_expand_statements(&self, invocation: &str, expected: &str) -> &$name {
1800 self.assert_expansion(FragmentKind::Statements, invocation, expected);
1801 self
1802 }
1780 1803
1781 let expected_tree = expected_tree.replace("C_C__C", "$crate"); 1804 fn assert_expansion(&self, kind: FragmentKind, invocation: &str, expected: &str) {
1782 assert_eq!( 1805 let expanded = self.expand_tt(invocation);
1783 expanded_tree, expected_tree, 1806 assert_eq!(expanded.to_string(), expected);
1784 "\nleft:\n{}\nright:\n{}", 1807
1785 expanded_tree, expected_tree, 1808 let expected = expected.replace("$crate", "C_C__C");
1786 ); 1809
1787 } 1810 // wrap the given text to a macro call
1811 let expected = {
1812 let wrapped = format!("wrap_macro!( {} )", expected);
1813 let wrapped = ast::SourceFile::parse(&wrapped);
1814 let wrapped = wrapped
1815 .tree()
1816 .syntax()
1817 .descendants()
1818 .find_map(ast::TokenTree::cast)
1819 .unwrap();
1820 let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0;
1821 wrapped.delimiter = None;
1822 wrapped
1823 };
1824
1825 let expanded_tree =
1826 token_tree_to_syntax_node(&expanded, kind).unwrap().0.syntax_node();
1827 let expanded_tree = debug_dump_ignore_spaces(&expanded_tree).trim().to_string();
1828
1829 let expected_tree =
1830 token_tree_to_syntax_node(&expected, kind).unwrap().0.syntax_node();
1831 let expected_tree = debug_dump_ignore_spaces(&expected_tree).trim().to_string();
1832
1833 let expected_tree = expected_tree.replace("C_C__C", "$crate");
1834 assert_eq!(
1835 expanded_tree, expected_tree,
1836 "\nleft:\n{}\nright:\n{}",
1837 expanded_tree, expected_tree,
1838 );
1839 }
1840 }
1841 };
1788} 1842}
1789 1843
1790fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree { 1844impl_fixture!(MacroFixture);
1845impl_fixture!(MacroFixture2);
1846
1847fn parse_macro_rules_to_tt(ra_fixture: &str) -> tt::Subtree {
1791 let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); 1848 let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
1792 let macro_definition = 1849 let macro_definition =
1793 source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap(); 1850 source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap();
@@ -1804,14 +1861,36 @@ fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree {
1804 definition_tt 1861 definition_tt
1805} 1862}
1806 1863
1864fn parse_macro_def_to_tt(ra_fixture: &str) -> tt::Subtree {
1865 let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
1866 let macro_definition =
1867 source_file.syntax().descendants().find_map(ast::MacroDef::cast).unwrap();
1868
1869 let (definition_tt, _) = ast_to_token_tree(&macro_definition.body().unwrap()).unwrap();
1870
1871 let parsed =
1872 parse_to_token_tree(&ra_fixture[macro_definition.body().unwrap().syntax().text_range()])
1873 .unwrap()
1874 .0;
1875 assert_eq!(definition_tt, parsed);
1876
1877 definition_tt
1878}
1879
1807pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { 1880pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture {
1808 let definition_tt = parse_macro_to_tt(ra_fixture); 1881 let definition_tt = parse_macro_rules_to_tt(ra_fixture);
1809 let rules = MacroRules::parse(&definition_tt).unwrap(); 1882 let rules = MacroRules::parse(&definition_tt).unwrap();
1810 MacroFixture { rules } 1883 MacroFixture { rules }
1811} 1884}
1812 1885
1886pub(crate) fn parse_macro2(ra_fixture: &str) -> MacroFixture2 {
1887 let definition_tt = parse_macro_def_to_tt(ra_fixture);
1888 let rules = MacroDef::parse(&definition_tt).unwrap();
1889 MacroFixture2 { rules }
1890}
1891
1813pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError { 1892pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError {
1814 let definition_tt = parse_macro_to_tt(ra_fixture); 1893 let definition_tt = parse_macro_rules_to_tt(ra_fixture);
1815 1894
1816 match MacroRules::parse(&definition_tt) { 1895 match MacroRules::parse(&definition_tt) {
1817 Ok(_) => panic!("Expect error"), 1896 Ok(_) => panic!("Expect error"),
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
index 1b259682d..22011cb33 100644
--- a/crates/paths/src/lib.rs
+++ b/crates/paths/src/lib.rs
@@ -6,6 +6,7 @@ use std::{
6 path::{Component, Path, PathBuf}, 6 path::{Component, Path, PathBuf},
7}; 7};
8 8
9/// Wrapper around an absolute [`PathBuf`].
9#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] 10#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
10pub struct AbsPathBuf(PathBuf); 11pub struct AbsPathBuf(PathBuf);
11 12
@@ -58,18 +59,33 @@ impl PartialEq<AbsPath> for AbsPathBuf {
58} 59}
59 60
60impl AbsPathBuf { 61impl AbsPathBuf {
62 /// Wrap the given absolute path in `AbsPathBuf`
63 ///
64 /// # Panics
65 ///
66 /// Panics if `path` is not absolute.
61 pub fn assert(path: PathBuf) -> AbsPathBuf { 67 pub fn assert(path: PathBuf) -> AbsPathBuf {
62 AbsPathBuf::try_from(path) 68 AbsPathBuf::try_from(path)
63 .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) 69 .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display()))
64 } 70 }
71
72 /// Coerces to a `AbsPath` slice.
73 ///
74 /// Equivalent of [`PathBuf::as_path`] for `AbsPathBuf`.
65 pub fn as_path(&self) -> &AbsPath { 75 pub fn as_path(&self) -> &AbsPath {
66 AbsPath::assert(self.0.as_path()) 76 AbsPath::assert(self.0.as_path())
67 } 77 }
78
79 /// Equivalent of [`PathBuf::pop`] for `AbsPathBuf`.
80 ///
81 /// Note that this won't remove the root component, so `self` will still be
82 /// absolute.
68 pub fn pop(&mut self) -> bool { 83 pub fn pop(&mut self) -> bool {
69 self.0.pop() 84 self.0.pop()
70 } 85 }
71} 86}
72 87
88/// Wrapper around an absolute [`Path`].
73#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] 89#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
74#[repr(transparent)] 90#[repr(transparent)]
75pub struct AbsPath(Path); 91pub struct AbsPath(Path);
@@ -98,28 +114,56 @@ impl<'a> TryFrom<&'a Path> for &'a AbsPath {
98} 114}
99 115
100impl AbsPath { 116impl AbsPath {
117 /// Wrap the given absolute path in `AbsPath`
118 ///
119 /// # Panics
120 ///
121 /// Panics if `path` is not absolute.
101 pub fn assert(path: &Path) -> &AbsPath { 122 pub fn assert(path: &Path) -> &AbsPath {
102 assert!(path.is_absolute()); 123 assert!(path.is_absolute());
103 unsafe { &*(path as *const Path as *const AbsPath) } 124 unsafe { &*(path as *const Path as *const AbsPath) }
104 } 125 }
105 126
127 /// Equivalent of [`Path::parent`] for `AbsPath`.
106 pub fn parent(&self) -> Option<&AbsPath> { 128 pub fn parent(&self) -> Option<&AbsPath> {
107 self.0.parent().map(AbsPath::assert) 129 self.0.parent().map(AbsPath::assert)
108 } 130 }
131
132 /// Equivalent of [`Path::join`] for `AbsPath`.
109 pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { 133 pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
110 self.as_ref().join(path).try_into().unwrap() 134 self.as_ref().join(path).try_into().unwrap()
111 } 135 }
136
137 /// Normalize the given path:
138 /// - Removes repeated separators: `/a//b` becomes `/a/b`
139 /// - Removes occurrences of `.` and resolves `..`.
140 /// - Removes trailing slashes: `/a/b/` becomes `/a/b`.
141 ///
142 /// # Example
143 /// ```
144 /// # use paths::AbsPathBuf;
145 /// let abs_path_buf = AbsPathBuf::assert("/a/../../b/.//c//".into());
146 /// let normalized = abs_path_buf.normalize();
147 /// assert_eq!(normalized, AbsPathBuf::assert("/b/c".into()));
148 /// ```
112 pub fn normalize(&self) -> AbsPathBuf { 149 pub fn normalize(&self) -> AbsPathBuf {
113 AbsPathBuf(normalize_path(&self.0)) 150 AbsPathBuf(normalize_path(&self.0))
114 } 151 }
152
153 /// Equivalent of [`Path::to_path_buf`] for `AbsPath`.
115 pub fn to_path_buf(&self) -> AbsPathBuf { 154 pub fn to_path_buf(&self) -> AbsPathBuf {
116 AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() 155 AbsPathBuf::try_from(self.0.to_path_buf()).unwrap()
117 } 156 }
157
158 /// Equivalent of [`Path::strip_prefix`] for `AbsPath`.
159 ///
160 /// Returns a relative path.
118 pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> { 161 pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> {
119 self.0.strip_prefix(base).ok().map(RelPath::new_unchecked) 162 self.0.strip_prefix(base).ok().map(RelPath::new_unchecked)
120 } 163 }
121} 164}
122 165
166/// Wrapper around a relative [`PathBuf`].
123#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] 167#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
124pub struct RelPathBuf(PathBuf); 168pub struct RelPathBuf(PathBuf);
125 169
@@ -160,11 +204,15 @@ impl TryFrom<&str> for RelPathBuf {
160} 204}
161 205
162impl RelPathBuf { 206impl RelPathBuf {
207 /// Coerces to a `RelPath` slice.
208 ///
209 /// Equivalent of [`PathBuf::as_path`] for `RelPathBuf`.
163 pub fn as_path(&self) -> &RelPath { 210 pub fn as_path(&self) -> &RelPath {
164 RelPath::new_unchecked(self.0.as_path()) 211 RelPath::new_unchecked(self.0.as_path())
165 } 212 }
166} 213}
167 214
215/// Wrapper around a relative [`Path`].
168#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] 216#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
169#[repr(transparent)] 217#[repr(transparent)]
170pub struct RelPath(Path); 218pub struct RelPath(Path);
@@ -183,12 +231,13 @@ impl AsRef<Path> for RelPath {
183} 231}
184 232
185impl RelPath { 233impl RelPath {
234 /// Creates a new `RelPath` from `path`, without checking if it is relative.
186 pub fn new_unchecked(path: &Path) -> &RelPath { 235 pub fn new_unchecked(path: &Path) -> &RelPath {
187 unsafe { &*(path as *const Path as *const RelPath) } 236 unsafe { &*(path as *const Path as *const RelPath) }
188 } 237 }
189} 238}
190 239
191// https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 240/// Taken from https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85
192fn normalize_path(path: &Path) -> PathBuf { 241fn normalize_path(path: &Path) -> PathBuf {
193 let mut components = path.components().peekable(); 242 let mut components = path.components().peekable();
194 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { 243 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {