rust-lang / rust-analyzer

A Rust compiler front-end for IDEs
https://rust-analyzer.github.io/
Apache License 2.0
14.18k stars 1.59k forks source link

Code completion not working in #[tokio::main] #13355

Closed chanced closed 2 years ago

chanced commented 2 years ago

rust-analyzer version: rust-analyzer version: 0.4.1232-standalone (476d04387 2022-10-04) rustc version: rustc 1.64.0 (a55dd71d5 2022-09-19) relevant settings:

Image showing that rust analyzer vs code setting Proc Macro is Enabled

I'm not getting code completion in #[tokio::main] but it seems based on old issues that it should work?

https://user-images.githubusercontent.com/1073892/194185072-9e26b8c1-0085-49e0-a0b1-0411795340c8.mov

This is happening in a fresh project with the following Cargo.toml:

[package]
name = "example"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { version = "1.21.2", features = ["full"] }

Sorry if I should have resurrected an old issue instead of starting a new one.

fasterthanlime commented 2 years ago

I can reproduce this with 0.3.1229-standalone on rustc 1.64 (and rustc 2022-10-08), even though rust-analyzer is able to expand the main! macro successfully, there's no completions in there, cf. https://github.com/rust-lang/rust-analyzer/issues/13373#issuecomment-1272516399

fasterthanlime commented 2 years ago

I've travelled to the past (through VSCode's "Install another version" menu) to confirm completions in tokio::main used to work:

image

That's with 0.3.1123 - so it broke sometime between 0.3.1123 and 0.3.1162 - will try to narrow it down further.

edit: 0.3.1131 still works.

edit: 0.3.1140 still works.

fasterthanlime commented 2 years ago

I've narrowed it down to:

Here's the relevant diff: https://github.com/rust-lang/rust-analyzer/compare/7e2b983fd...2b472f668

I'm going to try and bisect this.

Edit: while bisecting, I encountered "Broken pipe (os error 32)" at dadb8328, so I'm going to try and switch to 1.63.0 for the rest of the bisection and hope it still tells us what we want to know.

Veykril commented 2 years ago

From a quick look (inserting a dbg!) it seems that our completion "analysis"(the part figuring out where we are completing things) is returning None (that is it fails to analyse the current position). A lot of things changed in that code path so its most likely a faulty ? placed somewhere

fasterthanlime commented 2 years ago

My bisection pointed to fba6adfae48d55984934872a81d533dc405b7cd3, so I must've messed something up, unless doc comments are somehow load-bearing. Starting again... (with 1.63.0 the whole time).

Veykril commented 2 years ago

We are hitting this early return which disables completions https://github.com/rust-lang/rust-analyzer/blob/61504c8d951c566eb03037dcb300c96f4bd9a8b6/crates/ide-completion/src/context/analysis.rs#L421 Which is kind of weird, because I don't think any code regarding this has changed there?

Veykril commented 2 years ago

Okay this seems to be a problem with the token mapping again, for some reason we are looking at the offset of the let keyword in this expansion instead of the s in the async block image

fasterthanlime commented 2 years ago

New bisect result:

The merge base 977e12a0bdc3e329af179ef3a9d466af9eb613bb is bad. This means the bug has been fixed between 977e12a0bdc3e329af179ef3a9d466af9eb613bb and [7e2b983fd459977e11026683ee4afb9598960a4c].

Not sure what's going on with my git bisect usage today lol, it thinks I'm looking for a commit that fixed a bug?

Veykril commented 2 years ago

Ye those commits are not related I'm failry certain, so from what I've checked, the problem is that we map the token we are completing to the let keyword, because the tokio proc-macro re-uses the function body spans for the new output body and we just pick the first token there (not a lot we can do there better really I believe). What surprises me is why this only fails now, this behavior should've been the same even before ...

fasterthanlime commented 2 years ago
  • 0.3.1140-standalone (7e2b983fd 2022-07-24) (still works), and

Upon closer inspection, it doesn't work with this version either (at least, with rustc 1.63.0). I'm going to stop posting comments until I can pinpoint it to one commit.

edit: I'm keeping track of my bisect work here https://gist.github.com/fasterthanlime/43142d7bb3bcae4869441852bcee830b

edit: latest bisect points to ebfbb314c03cd8e70198eebf5a96ece8a2f79e51 (but that's for rustc 1.63.0, it seems that broke at a different place)

git bisect bad
ebfbb314c03cd8e70198eebf5a96ece8a2f79e51 is the first bad commit
commit ebfbb314c03cd8e70198eebf5a96ece8a2f79e51
Author: Jonas Schievink <jonas.schievink@ferrous-systems.com>
Date:   Tue Jul 12 15:13:04 2022 +0200

    Update 1.63 proc macro ABI to match rustc

 .../src/abis/abi_1_63/proc_macro/bridge/client.rs  |   8 --
 .../src/abis/abi_1_63/proc_macro/bridge/handle.rs  |  22 +++-
 .../src/abis/abi_1_63/proc_macro/bridge/mod.rs     | 136 ++++++++++++---------
 .../src/abis/abi_1_63/proc_macro/bridge/rpc.rs     |  53 ++++----
 .../src/abis/abi_1_63/proc_macro/bridge/server.rs  |  22 ++--
 .../src/abis/abi_1_63/proc_macro/mod.rs            |  96 ++++++++++++---
 .../src/abis/abi_1_63/rustc_server.rs              |  87 +++++++------
 7 files changed, 264 insertions(+), 160 deletions(-)

I can confirm that caf23f29 works, but ebfbb314c03cd8e70198eebf5a96ece8a2f79e51 doesn't.

Veykril commented 2 years ago

I assume proc-macro server changes might've changed span association for tokens for r-a (not sure why or how, but it's suspicious given that the commit there removes randomness so I wouldn't be surprised). Tokio with the following patch works as intended https://github.com/tokio-rs/tokio/commit/f307531c0e81e99424930b95a3a6cc4041b68618 (which makes it skip the span on the initial tokens).

So I wouldn't consider this a regression here (I am honestly baffled it supposedly worked for people before?), as this was just sheer luck that it didn't break so far.

Also note, this only affects completions for the tail expression. If you are trying to complete in non-tail position completions work fine, since those spans aren't being re-used.

fasterthanlime commented 2 years ago

I'm glad you were able to narrow it down so much @Veykril but idk how I feel about a patch to tokio — a fix in rust-analyzer would fix it for all tokio versions published so far, so I'd rather have that (and a regression test) if possible.

I'm not sure how to write such a test right now.

Veykril commented 2 years ago

Well to be honest, I don't think we can fix it in r-a, unless we hardcode for the tokio proc-macro which I will absolutely fight against doing. I am still puzzled how it worked before https://github.com/rust-lang/rust-analyzer/commit/ebfbb314c03cd8e70198eebf5a96ece8a2f79e51though, so maybe I am missing something obvious here. But from what I know the problem is just the fact that tokio re-uses the span, which is the only element r-a can use to calculate completions for proc-macro expansions.

fasterthanlime commented 2 years ago

unless we hardcode for the tokio proc-macro which I will absolutely fight against doing

Oh I'm right there with you, that would be awful.

You certainly have a much better understanding of the actual completion code than I do, my observations are only based on the fact that you could get completions in caf23f29144b371035b864a1017dbc32573ad56d, but you can't anymore in ebfbb314c03cd8e70198eebf5a96ece8a2f79e511, so my immediate instinct would be to go and diff the two expansions and see what changed, because I think we're in either of those situations:

If it can be fixed by somehow returning to the older behavior, that'd mean tokio doesn't need to be patched and that seems like a better outcome. Of course someone needs to put in the work to do that research!

fasterthanlime commented 2 years ago

Note that even with the tokio patch, completion is not behaving as expected. The first half of this video is "without tokio::main", and typing connector.connect highlights the connect method as expected. The second half doesn't narrow down anything after the dot, and when picking a completion, it's inserted after the currently typed method (not shown in the video):

https://user-images.githubusercontent.com/7998310/194764768-63d7f47f-2923-4514-9451-6e9818ade0b1.mp4

(I've verified that completion works as expected with rustc 1.63 and caf23f2)

fasterthanlime commented 2 years ago

Oh, completion works in caf23f29 because... expansion fails:

thread 'MacroExpander' panicked at 'assertion failed: `(left != right)`
  left: `0`,
 right: `0`', crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs:22:9
stack backtrace:
   0: rust_begin_unwind
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/panicking.rs:142:14
   2: core::panicking::assert_failed_inner
   3: core::panicking::assert_failed
   4: proc_macro_srv::abis::abi_1_63::proc_macro::bridge::handle::InternedStore<T>::new
   5: proc_macro_srv::abis::abi_1_63::proc_macro::bridge::client::HandleStore<S>::new
   6: proc_macro_srv::abis::abi_1_63::proc_macro::bridge::server::run_server
   7: proc_macro_srv::abis::abi_1_63::proc_macro::bridge::server::<impl proc_macro_srv::abis::abi_1_63::proc_macro::bridge::client::Client<(proc_macro_srv::abis::abi_1_63::proc_macro::TokenStream,proc_macro_srv::abis::abi_1_63::proc_macro::TokenStream),proc_macro_srv::abis::abi_1_63::proc_macro::TokenStream>>::run
   8: proc_macro_srv::abis::abi_1_63::Abi::expand
   9: proc_macro_srv::abis::Abi::expand
  10: proc_macro_srv::dylib::Expander::expand
  11: proc_macro_srv::ProcMacroSrv::expand
  12: proc_macro_srv::cli::run
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
fasterthanlime commented 2 years ago

More data: the completion failure seems to only happen on the last line of main. With any other line, it's fine:

image

Another interesting thing: rename also fails on the last line:

image

But any other line is fine:

image
Veykril commented 2 years ago

because I think we're in either of those situations:

  • the old behavior was correct and span handling was accidentally broken in that commit (in a way that only affects tokio's tokio::main macro, as far as we can tell right now)
    • the new behavior is more correct and the only fix is to patch tokio its

The latter is the case, the problem is that tokio is emitting its extra tokens with the span of the tail expression (for diagnostics purposes when a type mismatch happens), this unfortunately trips up r-a because it uses the spans to figure out a lot of things (completions is just one of the things, as you see renaming is also affected and a lot of other things). r-a usually just picks the first token that matches for a span, which in tokio's case will always be a let token as that is the first token we see that has the corresponding span which is why my patch fixes the problem, as the input function body becomes the first tokens instead again. This is basically a shotcoming of spans I suppose, I am unsure if there are ways we can make this smarter than just picking the first or not.

bjorn3 commented 2 years ago

Would it be possible to give each token and token tree passed to the proc macro a unique id and remap based on that? If a proc macro passes a part of the input back unchanged it would have the same unique id, and it it changes it, a different id would be assigned to the part that was changed.

flodiebold commented 2 years ago

Or maybe we could just prefer tokens that are the same as the original token if we have multiple choices?

fasterthanlime commented 2 years ago

which is why my patch fixes the problem

I think there's more going on though — as mentioned in https://github.com/rust-lang/rust-analyzer/issues/13355#issuecomment-1272563918, your patch doesn't quite fix the problem.

Veykril commented 2 years ago

Ye completions still acting funky comes from the fact that we are now mapping the identifier to a semicolon which has me very confused .. god I hate macros

Veykril commented 2 years ago

Okay, so I went digging in completions a bit and found some general problems there that I fixed in https://github.com/rust-lang/rust-analyzer/pull/13386, with this, completions work correctly with the tokio patch applied. I still have no idea why the behavior changed though, will do some digging for that next, although I still don't consider that a bug. So I might instead just look into ways of making the token mapping here smarter for completions as that is probably a better use of the time.

fasterthanlime commented 2 years ago

Yay! Fwiw my position has changed since I discovered the only reason it /did/ work was because macro expansion didn't work at all. The tokio patch seems like the way to go now, to me.

Veykril commented 2 years ago

Ye I'll probably PR the patch to tokio anyways since it does simplify things in general.

Or maybe we could just prefer tokens that are the same as the original token if we have multiple choices?

We can most likely do this here.

Would it be possible to give each token and token tree passed to the proc macro a unique id and remap based on that? If a proc macro passes a part of the input back unchanged it would have the same unique id, and it it changes it, a different id would be assigned to the part that was changed.

Isn't this what are basically doing already? Our span is a token id, its just the fact that tokio copies the span and re-uses it for unrelated tokens.

bjorn3 commented 2 years ago

its just the fact that tokio copies the span and re-uses it for unrelated tokens.

My suggestion is to tie the token id to the specific token value, so it can't be re-used for unrelated tokens no matter what a proc macro does.

Veykril commented 2 years ago

I guess that would be something for https://github.com/rust-lang/rust-analyzer/issues/9403 then. We want both behaviors in a sense, completion really wants to drill down the unique identity of a token (for now, ideally we would inspect all usages of a token in expansions, though maybe only for decl macros), but we also want to see span re-uses for some other features.

Veykril commented 2 years ago

With https://github.com/rust-lang/rust-analyzer/pull/13392, completions somewhat work in current tokio as well now, there are a few exceptions where it breaks due to https://github.com/rust-lang/rust-analyzer/issues/13388 still.

Veykril commented 2 years ago

I'll close this as its mostly fixed, and the problematic parts are tracked in the linked issue now.

Veykril commented 2 years ago

fwiw I opened a PR for the span patch https://github.com/tokio-rs/tokio/pull/5092 though it would be fully understandable for them to not merge this given its a workaround for a r-a bug

fasterthanlime commented 2 years ago

fwiw I opened a PR for the span patch tokio-rs/tokio#5092 though it would be fully understandable for them to not merge this given its a workaround for a r-a bug

Yeah exactly, one big reason I wanted to make sure this couldn't be fixed on the r-a side is that I think (no matter how useful r-a is) there's limited political capital to spend and get crate maintainers to merge fixes "purely for r-a". From previous experience, I can totally see some of them going "rustc likes my code, that's good enough for me".

chanced commented 2 years ago

Good work ya'll.