diff --git a/crates/hir-expand/src/builtin/quote.rs b/crates/hir-expand/src/builtin/quote.rs index fcd242bb49..9b637fc768 100644 --- a/crates/hir-expand/src/builtin/quote.rs +++ b/crates/hir-expand/src/builtin/quote.rs @@ -102,6 +102,7 @@ macro_rules! quote_impl__ { ($span:ident $builder:ident # ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '#')}; ($span:ident $builder:ident $ ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '$')}; ($span:ident $builder:ident * ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '*')}; + ($span:ident $builder:ident = ) => {$crate::builtin::quote::__quote!(@PUNCT($span $builder) '=')}; ($span:ident $builder:ident $first:tt $($tail:tt)+ ) => {{ $crate::builtin::quote::__quote!($span $builder $first); diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index a2d44ff376..c4113de7a5 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -441,8 +441,8 @@ fn transform_tt<'a, 'b>( }; let len_diff = replacement.len() as i64 - old_len as i64; tt.splice(i..i + old_len, replacement.flat_tokens().iter().cloned()); - // `+1` for the loop. - i = i.checked_add_signed(len_diff as isize + 1).unwrap(); + // Skip the newly inserted replacement, we don't want to visit it. + i += replacement.len(); for &subtree_idx in &subtrees_stack { let tt::TokenTree::Subtree(subtree) = &mut tt[subtree_idx] else { diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index faa65019ee..31b1774bb7 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -867,6 +867,19 @@ fn foo() { let loop {} } +"#, + ); + } + + #[test] + fn regression_18898() { + check( + r#" +//- proc_macros: issue_18898 +#[proc_macros::issue_18898] +fn foo() { + let +} "#, ); } diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs index 0e72d79687..422aa08e35 100644 --- a/crates/test-fixture/src/lib.rs +++ b/crates/test-fixture/src/lib.rs @@ -376,8 +376,8 @@ impl ChangeFixture { } } -fn default_test_proc_macros() -> [(String, ProcMacro); 8] { - [ +fn default_test_proc_macros() -> Box<[(String, ProcMacro)]> { + Box::new([ ( r#" #[proc_macro_attribute] @@ -498,7 +498,22 @@ pub fn issue_17479(input: TokenStream) -> TokenStream { disabled: false, }, ), - ] + ( + r#" +#[proc_macro_attribute] +pub fn issue_18898(_attr: TokenStream, input: TokenStream) -> TokenStream { + input +} +"# + .into(), + ProcMacro { + name: Symbol::intern("issue_18898"), + kind: ProcMacroKind::Bang, + expander: sync::Arc::new(Issue18898ProcMacroExpander), + disabled: false, + }, + ), + ]) } fn filter_test_proc_macros( @@ -801,3 +816,54 @@ impl ProcMacroExpander for Issue17479ProcMacroExpander { }) } } + +// Reads ident type within string quotes, for issue #17479. +#[derive(Debug)] +struct Issue18898ProcMacroExpander; +impl ProcMacroExpander for Issue18898ProcMacroExpander { + fn expand( + &self, + subtree: &TopSubtree, + _: Option<&TopSubtree>, + _: &Env, + def_site: Span, + _: Span, + _: Span, + _: Option, + ) -> Result { + let span = subtree + .token_trees() + .flat_tokens() + .last() + .ok_or_else(|| ProcMacroExpansionError::Panic("malformed input".to_owned()))? + .first_span(); + let overly_long_subtree = quote! {span => + { + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + let a = 5; + } + }; + Ok(quote! { def_site => + fn foo() { + #overly_long_subtree + } + }) + } +}