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

async parses as a syn::Ident #1313

Closed
ahl opened this issue Jan 19, 2023 · 6 comments
Closed

async parses as a syn::Ident #1313

ahl opened this issue Jan 19, 2023 · 6 comments

Comments

@ahl
Copy link

ahl commented Jan 19, 2023

fn main() {
    println!("{}", syn::parse_str::<syn::Ident>("async").is_ok());
    println!("{}", syn::parse_str::<syn::Ident>("type").is_ok());
}
true
false

That's surprising because syn::Ident says this in its docs:

An identifier constructed with Ident::new is permitted to be a Rust keyword, though parsing one through its Parse implementation rejects Rust keywords.

I'm pretty confused as to how this is happening given:
https://github.com/dtolnay/syn/blob/master/src/ident.rs#L20-L35

My apologies if this is working as intended.

@dtolnay
Copy link
Owner

dtolnay commented Jan 19, 2023

Whether async is a legal name for a value/type/macro depends on the Rust edition of the input code. Syn parsed a superset of all editions so I think this is behaving correctly.

In general if you are writing a Parse impl that involves some "keyword" i.e. you want to parse foo {...} differently from arbitrary_other_ident {...} for some specific keyword foo (whether or not that's a language keyword in any current Rust edition), the correct way is to look for your keyword first and then fall back to arbitrary Ident if the input isn't the keyword; not look for arbitrary Ident and fall back to looking for your keyword if Ident did not parse.

@ahl
Copy link
Author

ahl commented Jan 19, 2023

I see. I'm using syn::parse_str::<syn::Ident>() to infer whether a string that the user gave me is a valid variable name for use in generated code. It sounds like I need to reconsider this already-sketchy use of the interface. Thanks.

@dtolnay
Copy link
Owner

dtolnay commented Jan 20, 2023

Yeah that's gonna depend on what edition the user is compiling their crate with.

Usually a macro can just dump an Ident into the output code without a check. For example something like #[serde(serialize_with = "async")] isn't going to be checking for keywords, the caller will get an error if they deserve one.

If the reason you are checking is to legalize the ident in some way like turning keyword into k_keyword in the generated code but keeping non-keywords unchanged, try indiscriminately using r# prefix instead regardless of keyword.

@ahl
Copy link
Author

ahl commented Jan 22, 2023

Using r# is a great tip; thank you!

@ahl ahl closed this as completed Jan 22, 2023
@GilShoshan94
Copy link

Hi @dtolnay,

I am making an attribute proc macro, and one of the argument is a list of identifiers, but cannot be keywords, and I cannot prefix them with r# as the use will refer to it later.

when I tried:

#[my_macro(outputs=[out1, async])
fn foo(in1: u8) -> (bool, String) {
    todo!();
}

I was surprised the async was accepted as a valid variable identifier (non-keyword) and that's how I found this issue.

I found in your repo where I think you are checking if the ident is a keyword.

Now following the Rust Reference about the keywords
async, await, dyn and try got added in the 2018 edition.

I would like to have my attribute to refuse those idents as valid variable names only if the user's crate is using edition 2018 and + but to allow them if it's edition 2015.

I then researched how can I know what are the keywords according to the edition, I imagine that maybe the compilier expose something like that.
I search in rustanalyzer, and in the core and proc-macro libraries but without success....

I found this "Rust Compiler Development Guide": Lexing and Parsing

I understand the rust compiler's lexer and parser are going to be extracted into libraries...

Anyway, this whole thing is pretty complex and I am not knowledgeable enough to know what is relevant....


My questions to you @dtolnay:

1.) Is it possible for a proc macro to detect the edition of the user's crate ? (If possible, how please ?)
2.) Is there a way to use the compiler's parsing to know if an ident is or not a keyword (depending of the edition thus) ?

Syn parsed a superset of all editions so I think this is behaving correctly.

  1. Do you see a way to make Syn aware of the input code's edition ? Maybe using the rustc_* libraries ?

(Thank you for all your wonderful libraries, I lost count on how many I daily use, and I learn so much from studying them).

@dtolnay
Copy link
Owner

dtolnay commented Feb 13, 2023

  1. No, 2. No, 3. No.

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

No branches or pull requests

3 participants