Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/base-db/src/fixture.rs62
-rw-r--r--crates/ide-completion/src/context/analysis.rs9
-rw-r--r--crates/ide-completion/src/tests/proc_macros.rs30
3 files changed, 96 insertions, 5 deletions
diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs
index 60d1e488d8..8a7e9dfadf 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/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index e34824e22e..f606d79ad2 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -48,7 +48,9 @@ pub(super) fn expand_and_analyze(
// make the offset point to the start of the original token, as that is what the
// intermediate offsets calculated in expansion always points to
let offset = offset - relative_offset;
- let expansion = expand(sema, original_file, speculative_file, offset, fake_ident_token);
+ let expansion =
+ expand(sema, original_file, speculative_file, offset, fake_ident_token, relative_offset);
+
// add the relative offset back, so that left_biased finds the proper token
let offset = expansion.offset + relative_offset;
let token = expansion.original_file.token_at_offset(offset).left_biased()?;
@@ -67,6 +69,7 @@ fn expand(
mut speculative_file: SyntaxNode,
mut offset: TextSize,
mut fake_ident_token: SyntaxToken,
+ relative_offset: TextSize,
) -> ExpansionResult {
let _p = profile::span("CompletionContext::expand");
let mut derive_ctx = None;
@@ -97,7 +100,7 @@ fn expand(
// successful expansions
(Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
let new_offset = fake_mapped_token.text_range().start();
- if new_offset > actual_expansion.text_range().end() {
+ if new_offset + relative_offset > actual_expansion.text_range().end() {
// offset outside of bounds from the original expansion,
// stop here to prevent problems from happening
break 'expansion;
@@ -176,7 +179,7 @@ fn expand(
// successful expansions
(Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
let new_offset = fake_mapped_token.text_range().start();
- if new_offset > actual_expansion.text_range().end() {
+ if new_offset + relative_offset > actual_expansion.text_range().end() {
// offset outside of bounds from the original expansion,
// stop here to prevent problems from happening
break 'expansion;
diff --git a/crates/ide-completion/src/tests/proc_macros.rs b/crates/ide-completion/src/tests/proc_macros.rs
index 9eae6f8495..fec149e56a 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#""#]],
+ )
+}