rust-lang / rust-analyzer

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

Autocomplete doesn't work inside 'async_stream' function-like macro #12759

Open elimist3 opened 2 years ago

elimist3 commented 2 years ago

rust-analyzer version: rust-analyzer 0.0.0 (5342f47f4 2022-07-09)

rustc version: rustc 1.64.0-nightly (27eb6d701 2022-07-04)


I'm learning Rust and I was playing around with the routeguide tutorial from tonic.

When I open routeguide/client.rs and type some characters at the top of the run_route_chat function, autocomplete works fine. However, when typing characters inside the async_stream::stream! macro block, the autocompletions don't show up.

async fn run_route_chat(client: &mut RouteGuideClient<Channel>) -> Result<(), Box<dyn Error>> {
    let start = time::Instant::now();

    let outbound = async_stream::stream! {

       // ------------------------------------------------------------------------------------
       // ----------------- autocompletions stop working inside here ------------------------
       // ------------------------------------------------------------------------------------

        let mut interval = time::interval(Duration::from_secs(1));

        loop {
            let time = interval.tick().await;
            let elapsed = time.duration_since(start);
            let note = RouteNote {
                location: Some(Point {
                    latitude: 409146138 + elapsed.as_secs() as i32,
                    longitude: -746188906,
                }),
                message: format!("at {:?}", elapsed),
            };

            yield note;
        }
    };

    let response = client.route_chat(Request::new(outbound)).await?;
    let mut inbound = response.into_inner();

    while let Some(note) = inbound.message().await? {
        println!("NOTE = {:?}", note);
    }

    Ok(())
}

Demo: https://streamable.com/c0vl10#

I've read the blog post and all the various issues talking about the root causes so I think I have an understanding of why this is occurring, but I thought I'd file the issue anyway.

lnicola commented 2 years ago

Try using a stable toolchain if you don't depend on nightly.

elimist3 commented 2 years ago

I think I originally had it on rust stable, but changed it to nightly to see if the problem had been patched! I just changed it back to rust stable again to verify, and autocomplete still doesn't work inside the macro.

flodiebold commented 2 years ago

Since async_stream is a function-like proc macro, there isn't much we can do to make it handle incomplete code. So as long as what you're typing isn't syntactically complete Rust, the macro probably refuses to expand and so we can't provide completions. This would have to be fixed by the macro authors. (You can test whether this is the case by 1. using the "expand macro" function to see whether rust-analyzer can actually expand the macro, and 2. writing some complete, but not necessarily valid statement like start.f; at the place you marked and then trying to get completions after the f.)

elimist3 commented 2 years ago

That makes sense. I just tried both 1 and 2 above and it looks like it's definitely the case that the syntactically incomplete Rust is preventing the macro from expanding properly, which in turn is killing the autocomplete - when typing any valid statement or simply holding the cursor inside the macro and invoking macro expand, I can see that rust-analyzer expands it to this:

// =======================================
// Recursive expansion of the stream macro
// =======================================

{
    let (mut __yield_tx, __yield_rx) = $crate::yielder::pair();
    $crate::AsyncStream::new(__yield_rx, async move {
        let mut interval = time::interval(Duration::from_secs(1));
        loop {
            let time = interval.tick().await;
            let elapsed = time.duration_since(start);
            let note = RouteNote {
                location: Some(Point {
                    latitude: 409146138 + elapsed.as_secs() as i32,
                    longitude: -746188906,
                }),
                message: {
                    let res = $crate::fmt::format(std::fmt::Arguments::new_v1(
                        &[],
                        &[std::fmt::ArgumentV1::new(
                            &(elapsed),
                            std::fmt::Display::fmt,
                        )],
                    ));
                    res
                },
            };
            __yield_tx.send(note).await;
        }
    })
}

Typing in any incomplete Rust and attempting to expand the macro fails as predicted.

I'll try to bring it up with the macro authors!

nyxtom commented 2 years ago

I've noticed similar issues with the validator where the .validate() function doesn't show up.


use serde::Deserialize;

// A trait that the Validate derive will impl
use validator::{Validate, ValidationError};

#[derive(Debug, Validate, Deserialize)]
struct SignupData {
    #[validate(email)]
    mail: String,
    #[validate(phone)]
    phone: String,
    #[validate(url)]
    site: String,
    #[validate(length(min = 1), custom = "validate_unique_username")]
    #[serde(rename = "firstName")]
    first_name: String,
    #[validate(range(min = 18, max = 20))]
    age: u32,
}

fn validate_unique_username(username: &str) -> Result<(), ValidationError> {
    if username == "xXxShad0wxXx" {
        // the value of the username will automatically be added later
        return Err(ValidationError::new("terrible_username"));
    }

    Ok(())
}

match signup_data.validate() {
  Ok(_) => (),
  Err(e) => return e;
};

Similar to this I have tried implementing a basic proc-macro like below and I don't get any type information when using the #derive(Trait) for instance. It compiles fine, just no type information / autocomplete

use proc_macro::{self, TokenStream};
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(RequestExt)]
pub fn derive(input: TokenStream) -> TokenStream {
    let DeriveInput { ident, .. } = parse_macro_input!(input);
    let output = quote! {
        impl RequestState<#ident> for Request {
            fn state(&self) -> &#ident {
                self.ext::<#ident>().unwrap()
            }
        }
    };
    output.into()
}
flodiebold commented 2 years ago

@nyxtom That's not the same problem. If proc macros aren't expanding for you, there should be an error underline on the Validate derive. If you're using nightly, it's currently a known problem, otherwise it might be a setup issue.

hasezoey commented 1 year ago

i think i run in the same problem with crossbeam::channel::select macro

code:

fn main() -> Result<(), Box<dyn std::error::Error>> {
    use crossbeam::channel::*;

    let (tx1, rx1) = unbounded::<usize>();
    let (tx2, rx2) = unbounded::<usize>();

    // if the following is just *one* "recv", completion works until something invalid
    select! {
        recv(rx1) -> msg => {
            // no completion at all
        },
        recv(rx2) -> msg => {
            // no completion at all
        },
        default => (),
    }

    return Ok(());
}

the weird thing is if there is just 1 recv inside the select, then it has auto-completion until something invalid / incomplete is written (like writing msg. and asking for suggestions, where msg is Result<_, _>)

personally i would expect there to have full typings like a normal(closure) function (|msg| { here }) would have

full repository

rust-analyzer (vscode): 0.3.1631-standalone rustc 1.71.1 (eb26296b5 2023-08-03)