intellij-rust / intellij-rust

Rust plugin for the IntelliJ Platform
https://intellij-rust.github.io
MIT License
4.54k stars 380 forks source link

False "mismatched types [E0308]" #6139

Open dantheman3333 opened 3 years ago

dantheman3333 commented 3 years ago

Environment

Problem description

expected &mut Text, found String on the line:

                text.value = format!("FPS: {:.2}", average);

however text.value is a String and cargo check/build/run succeeds

Steps to reproduce

use bevy::{
    diagnostic::{Diagnostics, FrameTimeDiagnosticsPlugin},
    prelude::*,
};

fn main() {
    App::build()
        .add_default_plugins()
        .add_plugin(FrameTimeDiagnosticsPlugin::default())
        .add_startup_system(setup.system())
        .add_system(text_update_system.system())
        .run();
}

fn text_update_system(diagnostics: Res<Diagnostics>, mut query: Query<&mut Text>) {
    for mut text in &mut query.iter() {
        if let Some(fps) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS) {
            if let Some(average) = fps.average() {
                text.value = format!("FPS: {:.2}", average);
            }
        }
    }
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut materials: ResMut<Assets<ColorMaterial>>) {
    let texture_handle = asset_server.load("assets/branding/icon.png").unwrap();

    let font_handle = asset_server.load("assets/fonts/FiraSans-Bold.ttf").unwrap();
    commands
        .spawn(Camera2dComponents::default())
        .spawn(SpriteComponents {
            material: materials.add(texture_handle.into()),
            ..Default::default()
        })
        // 2d camera
        .spawn(UiCameraComponents::default())
        // texture
        .spawn(TextComponents {
            style: Style {
                align_self: AlignSelf::FlexEnd,
                ..Default::default()
            },
            text: Text {
                value: "FPS:".to_string(),
                font: font_handle,
                style: TextStyle {
                    font_size: 20.0,
                    color: Color::WHITE,
                },
            },
            ..Default::default()
        });
}
[dependencies]
bevy = "*"

Assets can be downloaded from: https://github.com/bevyengine/bevy/tree/master/assets

David-OConnor commented 3 years ago

Same.

ph0ngp commented 3 years ago

I got the exact same error. I am also compiling with bevy, specifically this project https://github.com/TheNeikos/bevy_squares

Rust plugin version: 0.3.131.3366-202 Rust toolchain version: v1.48.0-nightly IDE name and version: CLion 2020.2.3 Operating system: Windows 10

bevy_squares – main rs 9_29_2020 10_44_05 AM

And when I try to go to the definition of text.value, it asks me to choose declaration from two locations bevy_squares – main rs 9_29_2020 10_44_14 AM

Here is the first bevy_squares – main rs 9_29_2020 10_47_51 AM

and here is the second bevy_squares – main rs 9_29_2020 10_47_57 AM

Running cargo check shows no problem

David-OConnor commented 3 years ago

I solved it by going to Settings -> Languages & Frameworks -> Rust -> And setting the standard library to the thumbvibh target I was using. It then was completely fixed! Along with many other IDE niceties that had been absent. When I go back to that settings screen, it shows the normal rust lib as before, instead of what I set. So many setting this snapped it out of a bugged state?

Of note: After making this change, it asked me to "reselect the project" for all my projects, or something similar.

ph0ngp commented 3 years ago

@David-OConnor I'm not sure I understand you correctly, but in my case the rust toolchain version and standard library is correct in Settings, but the false error still shows up

ph0ngp commented 3 years ago

After some reaserch, my understanding is this:

Mut is a struct that implements DerefMut:

impl<'a, T: Component> DerefMut for Mut<'a, T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut T {
        *self.mutated = true;
        self.value
    }
}

which means deref coercion can happen from &mut Mut<T> to &mut T.

In the code, text is an instance of type Mut<Text>. However, since both Mut and Text are structs containing a field called value:

/// Unique borrow of an entity's component
pub struct Mut<'a, T: Component> {
    value: &'a mut T,
    mutated: &'a mut bool,
}
#[derive(Default, Clone)]
pub struct Text {
    pub value: String,
    pub font: Handle<Font>,
    pub style: TextStyle,
}

so text.value can mean both ways. Its type can be either &mut Text (coercion does not happen) or String (coercion happens) depending on the expression type in the right hand side.

                text.value = format!("FPS: {:.2}", average);

In this case the right hand side expression is of type String, so text.value must refers to the String type, which means deref coercion must happen. However, the Intellij Rust plugin does not recognize this and thinks that the value field in text.value is from the Mut struct, instead of the Text struct. That's why it gives a false error.

David-OConnor commented 3 years ago

@phphong Just try that. It seemed to have re-initialized something. This may not be your problem, but worth investigating.

abjorck commented 3 years ago

also have this issue (still). intellij-rust 0.3.137.3543-203 a workaround being to explicitly deref the Mut to it's T

text.deref_mut().value = format!("FPS: {:.2}", average); (use std::ops::DerefMut;)

abjorck commented 3 years ago

Mut's fields are now pub(crate) so idea should be able to take that as a hint to prioritize the other one in the conflict IMHO (having no idea how things work)

pub struct Mut<'a, T: Component> {
    pub(crate) value: &'a mut T,
    pub(crate) flags: &'a mut ComponentFlags,
}
ChristianIvicevic commented 2 years ago

The easiest way to circumvent this is to pick a property name that is not value within your custom components. Admittedly, this isn't really convenient and it'd be nice to have a fix for it at some point as I've just stumbled into the issue myself.

xaoctech commented 1 year ago

Actually, the plugin ignores visibility during Deref / DerefMut coercion, see

mod inner {
    use std::ops::{Deref, DerefMut};

    pub struct Handle<V>(V);

    impl<V> Handle<V> {
        pub fn new(v: V) -> Self {
            Self(v)
        }
    }

    impl<V> Deref for Handle<V> {
        type Target = V;

        fn deref(&self) -> &Self::Target {
            &self.0
        }
    }

    impl<V> DerefMut for Handle<V> {
        fn deref_mut(&mut self) -> &mut Self::Target {
            &mut self.0
        }
    }
}

pub struct Value(i32);

fn main() {
    let mut h = inner::Handle::new(Value(10));
    h.0 = 1;
    println!("{}", h.0)
}

It displays false mismatched type error:

image

Changing Handle type to

pub struct Handle<V>(pub V);

makes them 'true' errors: image