Skip to content
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

Incorrect compiler hint for complicated type handler #64548

Open
olegnn opened this issue Sep 17, 2019 · 4 comments
Open

Incorrect compiler hint for complicated type handler #64548

olegnn opened this issue Sep 17, 2019 · 4 comments

Comments

@olegnn
Copy link
Contributor

@olegnn olegnn commented Sep 17, 2019

I was trying to implement custom macro parser but fall into strange issue.
Having following code

use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream, Peek};
use syn::token::Token;

#[derive(Debug)]
struct ParseType {
    pub tokens: TokenStream,
}

mod custom {
    syn::custom_punctuation!(Arrow, ==>);
}

fn parse_until<T: Token>(
    input: ParseStream,
    ending_tokens: &[&impl Peek<Token = T>],
) -> syn::Result<TokenStream> {
    let mut tokens = TokenStream::new();
    while !input.is_empty() && !ending_tokens.into_iter().any(|token| input.peek(token)) {
        let next: proc_macro2::TokenTree = input.parse()?;
        tokens.extend(Some(next));
    }
    Ok(tokens)
}

impl Parse for ParseType {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(ParseType {
            tokens: parse_until(input, &[&custom::Arrow])?,
        })
    }
}

fn main() {
    let result: syn::Result<ParseType> =
        syn::parse2(quote! { { Ok::<usize, usize>(1) } ==> to the moon });
    println!("Parsed: {:?}", result.unwrap());
}

(Playground)

I got an error

error[E0277]: expected a `std::ops::Fn<(syn::lookahead::TokenMarker,)>` closure, found `impl Peek<Token = T>`
  --> src/main.rs:20:77
   |
20 |     while !input.is_empty() && !ending_tokens.into_iter().any(|token| input.peek(token)) {
   |                                                                             ^^^^ expected an `Fn<(syn::lookahead::TokenMarker,)>` closure, found `impl Peek<Token = T>`
   |
   = help: the trait `std::ops::Fn<(syn::lookahead::TokenMarker,)>` is not implemented for `impl Peek<Token = T>`
   = help: consider adding a `where impl Peek<Token = T>: std::ops::Fn<(syn::lookahead::TokenMarker,)>` bound
   = note: required because of the requirements on the impl of `std::ops::FnOnce<(syn::lookahead::TokenMarker,)>` for `&impl Peek<Token = T>`
   = note: required because of the requirements on the impl of `syn::lookahead::Peek` for `&&impl Peek<Token = T>`

So I added where impl Peek<Token = T>: std::ops::Fn<(syn::lookahead::TokenMarker,)> to the code and then situation became worse:

error[E0603]: module `lookahead` is private
  --> src/main.rs:18:78
   |
18 | ) -> syn::Result<TokenStream> where impl Peek<Token = T>: std::ops::Fn<(syn::lookahead::TokenMarker,)> {
   |                                                                              ^^^^^^^^^

error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
  --> src/main.rs:18:37
   |
18 | ) -> syn::Result<TokenStream> where impl Peek<Token = T>: std::ops::Fn<(syn::lookahead::TokenMarker,)> {
   |                                     ^^^^^^^^^^^^^^^^^^^^

error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change. Use parenthetical notation (Fn(Foo, Bar) -> Baz) instead
  --> src/main.rs:18:59
   |
18 | ) -> syn::Result<TokenStream> where impl Peek<Token = T>: std::ops::Fn<(syn::lookahead::TokenMarker,)> {
   |                                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: for more information, see https://github.com/rust-lang/rust/issues/29625

For the first of them it's ok [for me at least] that compiler can't understand which module is private, however the second and the third make me very confused, because this's the code generated by compiler itself.

@olegnn
Copy link
Contributor Author

@olegnn olegnn commented Sep 17, 2019

input.peek(**token)

solved the problem but compiler hints in this case only made this little problem look so terrifying.

@cuviper
Copy link
Member

@cuviper cuviper commented Sep 21, 2019

#64565 offers a more succinct version of that hint, suggesting where impl Trait bounds that aren't actually valid to write. But any hint along those lines would still be misleading in your case.

@estebank
Copy link
Contributor

@estebank estebank commented Nov 25, 2019

Current output (minimal change to suggestion):

error[E0277]: expected a `std::ops::Fn<(syn::lookahead::TokenMarker,)>` closure, found `impl Peek<Token = T>`
  --> src/main.rs:20:82
   |
17 |     ending_tokens: &[&impl Peek<Token = T>],
   |                       -------------------- help: consider further restricting this bound: `impl Peek<Token = T> + std::ops::Fn<(syn::lookahead::TokenMarker,)>`
...
20 |     while !input.is_empty() && !ending_tokens.into_iter().any(|token| input.peek(token)) {
   |                                                                                  ^^^^^ expected an `Fn<(syn::lookahead::TokenMarker,)>` closure, found `impl Peek<Token = T>`
   |
   = help: the trait `std::ops::Fn<(syn::lookahead::TokenMarker,)>` is not implemented for `impl Peek<Token = T>`
   = note: required because of the requirements on the impl of `std::ops::FnOnce<(syn::lookahead::TokenMarker,)>` for `&impl Peek<Token = T>`
   = note: required because of the requirements on the impl of `syn::lookahead::Peek` for `&&impl Peek<Token = T>`

We now check if borrowing the argument would make the expression valid and suggest adding & if so. We should add a check that does the reverse (check if dereferencing the argument would make it valid) too.

@steveklabnik
Copy link
Member

@steveklabnik steveklabnik commented Dec 28, 2020

Triage: output has slightly changed:

  Compiling playground v0.0.1 (/playground)
error[E0277]: expected a `std::ops::Fn<(syn::lookahead::TokenMarker,)>` closure, found `impl Peek<Token = T>`
  --> src/main.rs:20:82
   |
20 |     while !input.is_empty() && !ending_tokens.into_iter().any(|token| input.peek(token)) {
   |                                                                                  ^^^^^ expected an `Fn<(syn::lookahead::TokenMarker,)>` closure, found `impl Peek<Token = T>`
   |
   = note: required because of the requirements on the impl of `FnOnce<(syn::lookahead::TokenMarker,)>` for `&impl Peek<Token = T>`
   = note: required because of the requirements on the impl of `Peek` for `&&impl Peek<Token = T>`
help: consider further restricting this bound
   |
17 |     ending_tokens: &[&impl Peek<Token = T> + std::ops::Fn<(syn::lookahead::TokenMarker,)>],
   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants