-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
rustc_parser
incorrectly parses groups with Delimiter::None
#67062
Comments
How to reproduce this without constructing Proc macro crate: #[proc_macro]
pub fn add_mul(input: TokenStream) -> TokenStream {
let mul_2 = vec![
TokenTree::from(Punct::new('*', Spacing::Alone)),
TokenTree::from(Literal::u8_unsuffixed(2)),
];
input.into_iter().chain(mul_2.into_iter()).collect()
} User crate: macro_rules! mbe {
($e: expr) => ( add_mul!($e) )
}
fn main() {
let x = mbe!(2 + 2);
assert_eq!(x, 8); // FAIL: the result is 6 != 8
} |
What it means to parse However, it's clear that in pieces of grammar with expression-like priorities they should behave more or less like parentheses to keep the "operator priority hygiene" property of our macros. "How would an interpolated token behave here?" should probably be the primary intuition here. |
cc @dtolnay @alexcrichton |
Encountered in practice in #74036. |
`make_tokenstream` has three commented hacks, and a comment at the top referring to rust-lang#67062. These hacks have no observable effect, at least as judged by running the test suite. The hacks were added in rust-lang#82608, with an explanation [here](rust-lang#82608 (comment)). It appears that one of the following is true: (a) they never did anything useful, (b) they do something useful but we have no test coverage for them, or (c) something has changed in the meantime that means they are no longer necessary. This commit removes the hacks and the comments, in the hope that (b) is not true.
…cks, r=Aaron1011 Remove hacks in `make_token_stream`. `make_tokenstream` has three commented hacks, and a comment at the top referring to rust-lang#67062. These hacks have no observable effect, at least as judged by running the test suite. The hacks were added in rust-lang#82608, with an explanation [here](rust-lang#82608 (comment)). It appears that one of the following is true: (a) they never did anything useful, (b) they do something useful but we have no test coverage for them, or (c) something has changed in the meantime that means they are no longer necessary. This commit removes the hacks and the comments, in the hope that (b) is not true. r? `@Aaron1011`
…cks, r=Aaron1011 Remove hacks in `make_token_stream`. `make_tokenstream` has three commented hacks, and a comment at the top referring to rust-lang#67062. These hacks have no observable effect, at least as judged by running the test suite. The hacks were added in rust-lang#82608, with an explanation [here](rust-lang#82608 (comment)). It appears that one of the following is true: (a) they never did anything useful, (b) they do something useful but we have no test coverage for them, or (c) something has changed in the meantime that means they are no longer necessary. This commit removes the hacks and the comments, in the hope that (b) is not true. r? `@Aaron1011`
I have made a serious attempt to fix this issue in #114647. The commits there eliminate |
…ors,cjgillot macro_rules: Preserve all metavariable spans in a global side table This PR preserves spans of `tt` metavariables used to pass tokens to declarative macros. Such metavariable spans can then be used in span combination operations like `Span::to` to improve all kinds of diagnostics. Spans of non-`tt` metavariables are currently kept in nonterminal tokens, but the long term plan is remove all nonterminal tokens from rustc parser and rely on the proc macro model with invisible delimiters (rust-lang#114647, rust-lang#67062). In particular, `NtIdent` nonterminal (corresponding to `ident` metavariables) becomes easy to remove when this PR lands (rust-lang#119412 does it). The metavariable spans are kept in a global side table keyed by `Span`s of original tokens. The alternative to the side table is keeping them in `SpanData` instead, but the performance regressions would be large because any spans from tokens passed to declarative macros would stop being inline and would work through span interner instead, and the penalty would be paid even if we never use the metavar span for the given original span. (But also see the comment on `fn maybe_use_metavar_location` describing the map collision issues with the side table approach.) There are also other alternatives - keeping the metavar span in `Token` or `TokenTree`, but associating it with `Span` itsel is the most natural choice because metavar spans are used in span combining operations, and those operations are not necessarily tied to tokens.
…ors,cjgillot macro_rules: Preserve all metavariable spans in a global side table This PR preserves spans of `tt` metavariables used to pass tokens to declarative macros. Such metavariable spans can then be used in span combination operations like `Span::to` to improve all kinds of diagnostics. Spans of non-`tt` metavariables are currently kept in nonterminal tokens, but the long term plan is remove all nonterminal tokens from rustc parser and rely on the proc macro model with invisible delimiters (rust-lang#114647, rust-lang#67062). In particular, `NtIdent` nonterminal (corresponding to `ident` metavariables) becomes easy to remove when this PR lands (rust-lang#119412 does it). The metavariable spans are kept in a global side table keyed by `Span`s of original tokens. The alternative to the side table is keeping them in `SpanData` instead, but the performance regressions would be large because any spans from tokens passed to declarative macros would stop being inline and would work through span interner instead, and the penalty would be paid even if we never use the metavar span for the given original span. (But also see the comment on `fn maybe_use_metavar_location` describing the map collision issues with the side table approach.) There are also other alternatives - keeping the metavar span in `Token` or `TokenTree`, but associating it with `Span` itsel is the most natural choice because metavar spans are used in span combining operations, and those operations are not necessarily tied to tokens.
#124974 demonstrates how this can produce wrong answers when a macro_rules macro is combined with a proc_macro that manipulates a TokenStream. The two macros might be fine in isolation, and the combining program need not even have thought about this potential bug. This seems a troublesome hazard to me. Imagine a proc macro that accepts an attribute whose argument is supposed to be an expression. Ideally, the macro would use a group when emitting the output, and it might use a derive-deftly is trying to be such a macro, and derive-deftly's users are potentially affected by this bug. (derive-deftly#68) |
In the meantime I will make an MR that at least notes in the docs that this doesn't work reliably. |
Like this one - #124389 ? |
Err, yes :-). Thanks for the pointer! |
…enkov Add a warning to proc_macro::Delimiter::None that rustc currently does not respect it. It does not provide the behaviour it is indicated to provide when used in a proc_macro context. This seems to be a bug, (rust-lang#67062), but it is a long standing one, and hard to discover. This pull request adds a warning to inform users of this issue, with a link to the relevant issue, and a version number of the last known affected rustc version.
Rollup merge of rust-lang#124389 - CensoredUsername:master, r=petrochenkov Add a warning to proc_macro::Delimiter::None that rustc currently does not respect it. It does not provide the behaviour it is indicated to provide when used in a proc_macro context. This seems to be a bug, (rust-lang#67062), but it is a long standing one, and hard to discover. This pull request adds a warning to inform users of this issue, with a link to the relevant issue, and a version number of the last known affected rustc version.
Added a warning in the documentation for this in the documentation to stop people from getting confused by it in #124389. Documenting this so it can be removed when this issue is fixed. |
Such groups can currently be created by proc macros.
Reproduction, proc macro crate:
User crate:
This is not a huge issue right now because proc macros have no reasons to created
Delimiter::None
delimiters themselves (but they can still get them from input and return them if they return their input partially or fully, see the example below).However, this will became a huge issue when interpolated tokens like
$e: expr
migrate from AST pieces to token streams, because in the token stream model they are represented exactly like groups withDelimiter::None
delimiters (proc macros already see them in this way).The text was updated successfully, but these errors were encountered: