rust-lang / vscode-rust

Rust extension for Visual Studio Code
https://marketplace.visualstudio.com/items?itemName=rust-lang.rust
Other
1.39k stars 167 forks source link

Rust extension reporting problems which don't(?) exist #854

Open ayesaac opened 4 years ago

ayesaac commented 4 years ago

I'm new to Rust, so it's entirely possible I'm missing something obvious here, but I've run into a weird scenario where the VS Code extension (I've disabled every other extension) is reporting problems which don't exist.

I've written a macro that allows me to annotate types with an identifier, and build an enum containing variants for each of those types, as well as a 'type enumeration' enum which is identical, but without any associated data on the variants. It builds A-OK with no complaints—cargo build works.

However, the Rust extension in VS Code reports four errors (E0428):

the name `Message1` is defined multiple times
`Message1` redefined here
note: `Message1` must be defined only once in the type namespace of this enum

the name `Message1` is defined multiple times
previous definition of the type `Message1` here
note: `Message1` must be defined only once in the type namespace of this enum

the name `Message2` is defined multiple times
`Message2` redefined here
note: `Message2` must be defined only once in the type namespace of this enum

the name `Message2` is defined multiple times
previous definition of the type `Message2` here
note: `Message2` must be defined only once in the type namespace of this enum

The actual code in question is:

    #[message(Test)]
    pub struct Message1 {
        pub val: i32,
    }

    #[message(Test)]
    pub struct Message2;

    #[write_message_type(Test)] // Red squiggly
    pub enum Test {
    }

cargo expand outputs:

    pub struct Message1 {
        pub val: i32,
    }
    pub struct Message2;
    pub enum TestTypes {
        Message1,
        Message2,
    }
    pub enum Test {
        Message1(Message1),
        Message2(Message2),
    }

Given the expansion is correct (and the crate builds and produces the expected outputs when wrapping & unwrapping messages), I'm not sure whether the macro code has much relevance, but just incase, here it is. Be forewarned, it's quick, dirty, unfinished, and my first attempt at writing a macro (without having done enough reading beforehand, frankly).

lazy_static! {
    static ref MESSAGE_MAP: Mutex<HashMap<String, Vec<String>>> = Mutex::new(HashMap::new());
}

#[proc_macro_attribute]
pub fn message(attr: TokenStream, item: TokenStream) -> TokenStream {
    match MESSAGE_MAP.lock() {
        Ok(mut map) => map.entry(attr.to_string()).or_insert(Vec::new()).push(
            syn::parse::<syn::DeriveInput>(item.clone())
                .expect("Invalid target for message attr")
                .ident
                .to_string(),
        ),
        Err(e) => panic!("Message map lock poisoned: '{}'.", e.to_string()),
    }

    item
}

#[proc_macro_attribute]
pub fn write_message_type(attr: TokenStream, _: TokenStream) -> TokenStream {
    let name = attr.to_string();
    match MESSAGE_MAP.lock() {
        Ok(map) => match map.get(&name) {
            Some(entries) => {
                let message_type_enumeration_name = syn::parse_str::<syn::Ident>(&(name.clone() + "Types")).expect("Invalid message wrapper name.");
                let type_entries: Vec<_>= entries.iter().map(|e| syn::parse_str::<syn::Ident>(e).expect("Something went horribly wrong enumerating the wrapped message types")).collect();

                let message_wrapper_name = syn::parse_str::<syn::Variant>(&name).expect("Invalid message wrapper name.");
                let wrapper_entries: Vec<_>= entries.iter().map(|e|{
                    let mut variant = e.clone();
                    variant += "(";
                    variant += e;
                    variant += ")";
                    syn::parse_str::<syn::Variant>(&variant).expect("Something went horribly wrong enumerating the wrapped message types")}).collect();

                     (quote! {
                    pub enum #message_type_enumeration_name {
                        #(#type_entries),*
                    }

                    pub enum #message_wrapper_name {
                        #(#wrapper_entries),*
                    }
                })
                .into()
            }
            None => panic!("Tried to expand message type wrappers '{}', but no message types were recorded for it!", name)
        },
        Err(e) => panic!("Message map lock poisoned: '{}'.", e.to_string()),
    }
}

VS Code is up to date, the Rust extension is up to date, and my Win 10 installation is up to date. As I can build, it's not the end of the world, but it would be nice to not have those errors in the problem panel; if it's a problem with my code, and not the extension, my apologies.

JKAnderson409 commented 4 years ago

I have had this problem many times, it has been around for a while now. In my experience rust-analyzer does not have this problem and that seems to be the language server that is being actively developed. If you're using RLS with this extension try switching to rust-analyzer, either as this extension's back-end or by using its own VS Code extension. I think rust-analyzer is eventually going to be RLS 2.0 but it is already the easier-to-use and, frankly, just plain better IDE experience.

I only use this because I use an alternative grammar (dustypomerleau.rust-syntax) that requires this extension. Using this extension with rust-analyzer required manually installing a binary, which is another problem that has persisted for months. That's why I recommend using the other extension. Despite this and RLS being the de jure defaults rust-analyzer seems to have eclipsed them.

lnicola commented 4 years ago

@JKAnderson409 note that the dustypomerleau.rust-syntax syntax was merged into rust-analyzer recently.

JKAnderson409 commented 4 years ago

@JKAnderson409 note that the dustypomerleau.rust-syntax syntax was merged into rust-analyzer recently.

Yeah that was good news. I actually asked dustypomerleau to try to have it merged on the same day that I made that other comment. It's made it easier to use the nightly releases of rust-analyzer because I don't need to overwrite the grammar every day. The rust-analyzer extension is now my only Rust extension.

I'm grateful that @dustypomerleau put in the effort to go through the review process so that the rust-analyzer extension can ship with a great TextMate grammar.