From a4d0b5c522405fd2351c56f48c68544b3130a513 Mon Sep 17 00:00:00 2001
From: Ryo Yoshida <low.ryoshida@gmail.com>
Date: Sun, 5 Feb 2023 20:02:16 +0900
Subject: [PATCH] Add regression tests

---
 crates/base-db/src/fixture.rs                 | 62 ++++++++++++++++++-
 .../ide-completion/src/tests/proc_macros.rs   | 30 +++++++++
 2 files changed, 90 insertions(+), 2 deletions(-)

diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs
index 60d1e488d8a..8a7e9dfadfe 100644
--- a/crates/base-db/src/fixture.rs
+++ b/crates/base-db/src/fixture.rs
@@ -6,7 +6,7 @@ use rustc_hash::FxHashMap;
 use test_utils::{
     extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER,
 };
-use tt::token_id::Subtree;
+use tt::token_id::{Leaf, Subtree, TokenTree};
 use vfs::{file_set::FileSet, VfsPath};
 
 use crate::{
@@ -310,7 +310,7 @@ impl ChangeFixture {
     }
 }
 
-fn default_test_proc_macros() -> [(String, ProcMacro); 4] {
+fn default_test_proc_macros() -> [(String, ProcMacro); 5] {
     [
         (
             r#"
@@ -368,6 +368,20 @@ pub fn mirror(input: TokenStream) -> TokenStream {
                 expander: Arc::new(MirrorProcMacroExpander),
             },
         ),
+        (
+            r#"
+#[proc_macro]
+pub fn shorten(input: TokenStream) -> TokenStream {
+    loop {}
+}
+"#
+            .into(),
+            ProcMacro {
+                name: "shorten".into(),
+                kind: crate::ProcMacroKind::FuncLike,
+                expander: Arc::new(ShortenProcMacroExpander),
+            },
+        ),
     ]
 }
 
@@ -508,3 +522,47 @@ impl ProcMacroExpander for MirrorProcMacroExpander {
         Ok(traverse(input))
     }
 }
+
+// Replaces every literal with an empty string literal and every identifier with its first letter,
+// but retains all tokens' span. Useful for testing we don't assume token hasn't been modified by
+// macros even if it retains its span.
+#[derive(Debug)]
+struct ShortenProcMacroExpander;
+impl ProcMacroExpander for ShortenProcMacroExpander {
+    fn expand(
+        &self,
+        input: &Subtree,
+        _: Option<&Subtree>,
+        _: &Env,
+    ) -> Result<Subtree, ProcMacroExpansionError> {
+        return Ok(traverse(input));
+
+        fn traverse(input: &Subtree) -> Subtree {
+            let token_trees = input
+                .token_trees
+                .iter()
+                .map(|it| match it {
+                    TokenTree::Leaf(leaf) => tt::TokenTree::Leaf(modify_leaf(leaf)),
+                    TokenTree::Subtree(subtree) => tt::TokenTree::Subtree(traverse(subtree)),
+                })
+                .collect();
+            Subtree { delimiter: input.delimiter, token_trees }
+        }
+
+        fn modify_leaf(leaf: &Leaf) -> Leaf {
+            let mut leaf = leaf.clone();
+            match &mut leaf {
+                Leaf::Literal(it) => {
+                    // XXX Currently replaces any literals with an empty string, but supporting
+                    // "shortening" other literals would be nice.
+                    it.text = "\"\"".into();
+                }
+                Leaf::Punct(_) => {}
+                Leaf::Ident(it) => {
+                    it.text = it.text.chars().take(1).collect();
+                }
+            }
+            leaf
+        }
+    }
+}
diff --git a/crates/ide-completion/src/tests/proc_macros.rs b/crates/ide-completion/src/tests/proc_macros.rs
index 9eae6f84954..fec149e56a9 100644
--- a/crates/ide-completion/src/tests/proc_macros.rs
+++ b/crates/ide-completion/src/tests/proc_macros.rs
@@ -131,3 +131,33 @@ fn main() {}
         "#]],
     )
 }
+
+#[test]
+fn issue_13836_str() {
+    check(
+        r#"
+//- proc_macros: shorten
+fn main() {
+    let s = proc_macros::shorten!("text.$0");
+}
+"#,
+        expect![[r#""#]],
+    )
+}
+
+#[test]
+fn issue_13836_ident() {
+    check(
+        r#"
+//- proc_macros: shorten
+struct S;
+impl S {
+    fn foo(&self) {}
+}
+fn main() {
+    let s = proc_macros::shorten!(S.fo$0);
+}
+"#,
+        expect![[r#""#]],
+    )
+}