Skip to content

rust-analyzer flags an E0308 error when rustc does not, connected to Itertools::collect_tuple #17039

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
sourcefrog opened this issue Apr 9, 2024 · 5 comments
Labels
A-pattern pattern handling related things A-ty type system / type inference / traits / method resolution C-bug Category: bug

Comments

@sourcefrog
Copy link

rust-analyzer version: (eg. output of "rust-analyzer: Show RA Version" command, accessible in VSCode via Ctrl/⌘+Shift+P)

0.3.1916-standalone

rustc version: (eg. output of rustc -V)

rustc 1.77.1 (7cf61ebde 2024-03-27)

editor or extension: (eg. VSCode, Vim, Emacs, etc. For VSCode users, specify your extension version; for users of other editors, provide the distribution if applicable)

VSCode v0.3.1916

relevant settings: (eg. client settings, or environment variables like CARGO, RUSTC, RUSTUP_HOME or CARGO_HOME)

repository link (if public, optional): (eg. rust-analyzer)

sourcefrog/cargo-mutants@7901e6d

code snippet to reproduce:

/// Match known key-value maps that can be empty or constructed from pair of
/// recursively-generated values.
fn known_map(path: &Path) -> Option<(&Ident, &Type, &Type)> {
    let last = path.segments.last()?;
    if !["BTreeMap", "HashMap"].iter().any(|v| last.ident == v) {
        return None;
    }
    if let PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) =
        &last.arguments
    {
        // TODO: Skip lifetime args.
        // TODO: Return the path with args stripped out.
        if let Some((GenericArgument::Type(key_type), GenericArgument::Type(value_type))) =
            args.iter().collect_tuple()
        {
            return Some((&last.ident, key_type, value_type));
        }
    }
    None
}

Apply this patch to the commit shown above:

diff --git a/src/fnvalue.rs b/src/fnvalue.rs
index 112afe9..0f00bd3 100644
--- a/src/fnvalue.rs
+++ b/src/fnvalue.rs
@@ -333,7 +333,7 @@ fn known_map(path: &Path) -> Option<(&Ident, &Type, &Type)> {
     {
         // TODO: Skip lifetime args.
         // TODO: Return the path with args stripped out.
-        if let Some((GenericArgument::Type(ref key_type), GenericArgument::Type(ref value_type))) =
+        if let Some((GenericArgument::Type(key_type), GenericArgument::Type(value_type))) =
             args.iter().collect_tuple()
         {
             return Some((&last.ident, key_type, value_type));

What I would expect is that either rustc and rust-analyzer both accept this, or they both reject it.

What happens is that cargo build passes (on today's stable, beta, and nightly), but rust-analyzer flags two errors:

[{
	"resource": "/home/mbp/src/mutants/src/fnvalue.rs",
	"owner": "rustc",
	"code": {
		"value": "E0308",
		"target": {
			"$mid": 1,
			"path": "/stable/error_codes/E0308.html",
			"scheme": "https",
			"authority": "doc.rust-lang.org"
		}
	},
	"severity": 8,
	"message": "expected &Type, found Type",
	"source": "rust-analyzer",
	"startLineNumber": 339,
	"startColumn": 39,
	"endLineNumber": 339,
	"endColumn": 47
},{
	"resource": "/home/mbp/src/mutants/src/fnvalue.rs",
	"owner": "rustc",
	"code": {
		"value": "E0308",
		"target": {
			"$mid": 1,
			"path": "/stable/error_codes/E0308.html",
			"scheme": "https",
			"authority": "doc.rust-lang.org"
		}
	},
	"severity": 8,
	"message": "expected &Type, found Type",
	"source": "rust-analyzer",
	"startLineNumber": 339,
	"startColumn": 49,
	"endLineNumber": 339,
	"endColumn": 59
}]
@sourcefrog
Copy link
Author

I'll see if I can minimize this.

sourcefrog added a commit to sourcefrog/rust-analyzer-17039-repro that referenced this issue Apr 16, 2024
@sourcefrog
Copy link
Author

sourcefrog commented Apr 16, 2024

https://github.com/sourcefrog/rust-analyzer-17039-repro has a self contained crate that reproduces the error. In that tree, cargo build passes but rust-analyzer flags the two errors shown above.

With this slightly shorter code:

fn known_map(path: &Path) -> Option<(&Ident, &Type, &Type)> {
    let last = path.segments.last()?;
    if let PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) =
        &last.arguments
    {
        if let Some((GenericArgument::Type(key_type), GenericArgument::Type(value_type))) =
            args.iter().collect_tuple()
        {
            return Some((&last.ident, key_type, value_type));
        }
    }
    None
}

VSCode shows that rust-analyzer thinks that key_type in the match for the results of collect_tuple has type syn::Type. So, it would make sense that it complains expected &Type, found Type. However, rustc seems to infer a different type there, presumably &Type.

rust-analyzer thinks that args.iter() is an Iter<'static, GenericArgument>, which seems right from the syn docs. That implements Iterator<Item = &GenericArgument>.

If I break it into smaller statements like this then it seems to infer the right types

        let arg_iter = args.iter();
        let tup: Option<(_, _)> = arg_iter.collect_tuple();
        if let Some((GenericArgument::Type(key_type), GenericArgument::Type(value_type))) =
            tup
        {
            return Some((&last.ident, key_type, value_type));
        } 

However if the collect_tuple moves into the if-let expression then it seems to get it wrong:

        let arg_iter = args.iter();
        if let Some((GenericArgument::Type(key_type), GenericArgument::Type(value_type))) =
            arg_iter.collect_tuple()
        {
            return Some((&last.ident, key_type, value_type));
        }

I tried to remove the syn types but did not yet succeed in doing that while still causing the same error. This is smaller:

fn known_map(args: &[GenericArgument]) -> Option<(&Type, &Type)> {
    let arg_iter = args.iter();
    if let Some((GenericArgument::Type(key_type), GenericArgument::Type(value_type))) =
        arg_iter.collect_tuple()
    {
        return Some((key_type, value_type));
    }
    None
}

I'm not blocked on this; there are other ways to write it and I thought of some simpler things while experimenting here. I'm just filing in the hope the report is useful.

@Veykril
Copy link
Member

Veykril commented Apr 16, 2024

struct Struct;

enum Enum {
    Variant(Struct),
}

fn f() -> Option<(&Struct, &Struct)> {
    use itertools::Itertools;
    let arg_iter = vec![&Enum::Variant(Struct)].iter();
    if let Some((Enum::Variant(thing), Enum::Variant(thing2))) = arg_iter.collect_tuple() {
        return Some((thing, thing2));
    }
    None
}

reproduces this. Now to get rid of the itertools dependency

@Veykril Veykril added A-ty type system / type inference / traits / method resolution A-pattern pattern handling related things labels Apr 16, 2024
sourcefrog added a commit to sourcefrog/rust-analyzer-17039-repro that referenced this issue Apr 16, 2024
@sourcefrog sourcefrog changed the title rust-analyzer flags an E0308 error when rustc does not rust-analyzer flags an E0308 error when rustc does not, connected to Itertools::collect_tuple Apr 17, 2024
@sourcefrog
Copy link
Author

So it seems to be connected to Itertools::collect_tuple where type influence is affected by the expected return type of collect_tuple.

    fn collect_tuple<T>(mut self) -> Option<T>
    where
        Self: Sized + Iterator<Item = T::Item>,
        T: traits::HomogeneousTuple {}

I guess rustc and rust-analyzer infer different types for T::Item.

@poliorcetics
Copy link
Contributor

Related to #17066 and #17075 I think

onderjan added a commit to onderjan/machine-check that referenced this issue Jan 9, 2025
Slight readability improvements. Also slight reference-taking changes to go around the newly occuring issue rust-lang/rust-analyzer#17039 where needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-pattern pattern handling related things A-ty type system / type inference / traits / method resolution C-bug Category: bug
Projects
None yet
Development

No branches or pull requests

3 participants