Auto merge of #14043 - lowr:fix/completion-within-str-that-changed, r=Veykril
fix: consider relative offset to fake ident token in expansion for completion
Fixes #13836
When we check if the offset of fake ident token is out of bounds in the "actual expansion" (i.e. expansion without the fake ident token), we should take relative offset to it into account to match [how we'd get token after expansion](f1b257f4eb/crates/ide-completion/src/context/analysis.rs (L53-L54)
).
This commit is contained in:
commit
0b32b65ca6
@ -6,7 +6,7 @@
|
||||
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 @@ pub fn parse_with_proc_macros(
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ fn traverse(input: &Subtree) -> Subtree {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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#""#]],
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user