Description
Rust-analyzer's "Inline <function>" code action fails1 when the function to be inlined is defined in a macro.
macro_rules! define_function {
() => {
fn test_macro(x: bool) {
if x { println!("x is true!"); }
}
};
}
define_function!();
fn test_free(x: bool) {
if x { println!("x is true!"); }
}
fn main() {
test_macro(true);
test_free(true);
}
Result when selecting test_macro(true)
and running the "Inline `test_macro`" action: (at least if
and x
should be separated)
// ...
fn main() {
{
let x = true; ifx{println!("x is true!");}};
test_free(true);
}
Result when selecting test_free(true)
and running the "Inline `test_free`" action: (to show that the issue only happens for functions defined in macros)
// ...
fn main() {
test_macro(true);
if true { println!("x is true!"); };
}
The problem also occurs in associated functions/methods. I originally ran into this error when trying to inline usize::next_multiple_of
, which is defined in a macro.
rust-analyzer version: (eg. output of "Rust Analyzer: Show RA Version" command)
rust-analyzer version: 0.4.1138-standalone (977e12a 2022-07-23)
("Pre-Release" version on VSCode)
(Also happens on normal version on VSCode, rust-analyzer version: 0.3.1131-standalone (897a7ec 2022-07-17))
rustc version: (eg. output of rustc -V
)
rustc 1.62.1 (e092d0b6b 2022-07-16)
relevant settings: (eg. client settings, or environment variables like CARGO
, RUSTUP_HOME
or CARGO_HOME
)
None that I am aware are relevant.
Footnotes
-
It succeeds but does not produce correct code if any sequential tokens need to be separated by spaces. ↩
Activity
zachs18 commentedon Jul 24, 2022
Additionally, when inlining methods defined in macros,
self
is not replaced.becomes
when inlining
Test.test_method_macro()
zachs18 commentedon Jul 25, 2022
I inserted a
dbg!(&body)
at the beginning offn inline
on line 305 ofcrates/ide-assists/src/handlers/inline_call.rs
, and the main difference between inliningfn test_free
andfn test_macro
appears to be thatfn test_free
containedWHITESPACE
tokens, butfn test_macro
did not.My (limited, possibly wrong) understanding of what is happening (for the whitespace problem, no idea about
self
not being replaced1) is that rust-analyzer converts it's AST representation from SyntaxNodes (which contain comments and whitespace) to TokenTrees (which does not contain comments or whitespace)2 when expanding macros, so it may not be possible (without changes to how macros are expanded by rust-analyzer) to include the original whitespace&comments when inlining macro-produced functions.Maybe it would be easier to just insert whitespace between tokens which cannot be adjacent? This is probably also required for inlining functions defined in proc-macros to work, since in that case there is no "original whitespace" that could even be kept.
Does rust-analyzer have a way to determine if a function was defined in a macro? rustc appears to have this (at least at some level), since if a function defined in a macro contains an error, it will say
note: this error originates in the macro `define_func` (in Nightly builds, run with -Z macro-backtrace for more info)
Footnotes
I also inserted
dbg!(...)
on the usages offn inline
on lines 127 and 221, which showed that the inlining oftest_method
(correctly) replacedself
withthis
, but inliningtest_method_macro
did not. I have not looked further into why, but I think that and the whitespace problem may be separate issues ↩I think this happens in
syntax_node_to_token_tree_with_modifications
on line 30 ofcrates/mbe/src/syntax_bridge.rs
. ↩Veykril commentedon Jul 25, 2022
Yes, macro expansions have no whitespace. This is a recurring thing and currently we just check for whether things come from a macro expansion in whicch ase we do whitespace insertion via https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
Auto merge of #12877 - zachs18:inline-def-in-macro, r=zachs18
inline macro
doesn't produce valid code helix-editor/helix#5667