Change keyword order for impl restrictions#155442
Change keyword order for impl restrictions#155442CoCo-Japan-pan wants to merge 5 commits intorust-lang:mainfrom
impl restrictions#155442Conversation
|
The parser was modified, potentially altering the grammar of (stable) Rust cc @fmease |
8e2d081 to
5d955ca
Compare
This comment has been minimized.
This comment has been minimized.
5d955ca to
4178059
Compare
|
The auto keyword was accidentally removed from the set of possible tokens, which caused UI tests to fail. |
This comment has been minimized.
This comment has been minimized.
| pub impl(crate::foo) trait Baz {} //~ ERROR incorrect `impl` restriction | ||
| //[without_gate]~^ ERROR `impl` restrictions are experimental | ||
| pub unsafe impl(crate::foo) trait BazUnsafe {} //~ ERROR incorrect `impl` restriction | ||
| pub impl(crate::foo) unsafe trait BazUnsafe {} //~ ERROR incorrect `impl` restriction | ||
| //[without_gate]~^ ERROR `impl` restrictions are experimental | ||
| pub auto impl(crate::foo) trait BazAuto {} //~ ERROR incorrect `impl` restriction | ||
| pub impl(crate::foo) auto trait BazAuto {} //~ ERROR incorrect `impl` restriction |
There was a problem hiding this comment.
I suspect users will be confused about the positioning of the impl(...).
Could add a test with some invalid impl(...) positions and add a fixme in it to improve the recovery and diagnostics.
| /// Returns whether any of the given keywords are `dist` tokens ahead over the current token tree. | ||
| pub(crate) fn is_keyword_tree_ahead(&self, dist: usize, kws: &[Symbol]) -> bool { |
There was a problem hiding this comment.
I'm not sure I understand why we need this function. Can't we keep using is_keyword_ahead? or look_ahead?
There was a problem hiding this comment.
This is because the path in impl(in? path) can be arbitrarily long, and we need to parse it to determine how many tokens to look ahead. To handle this, we treat the (in? path) portion as a TokenTree::Delimited, so we look ahead over token trees rather than tokens.
For this reason, I have not added an explicit check for CloseParen at the moment. #155442 (comment) However, it might make sense to instead check that the Delimiter is Parenthesis.
There was a problem hiding this comment.
I see thanks, that makes sense.
Would be good if you could add a comment explaining that in code, other people might wonder the same thing as me.
|
Some changes occurred in src/tools/clippy cc @rust-lang/clippy Some changes occurred in src/tools/rustfmt cc @rust-lang/rustfmt |
b7bec21 to
646fa9a
Compare
646fa9a to
78cb850
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
| // `impl(` | ||
| if self.check_keyword(exp!(Impl)) && self.look_ahead(1, |t| t == &token::OpenParen) { | ||
| // `impl(..) trait` | ||
| self.is_keyword_tree_ahead(2, &[kw::Trait]) || | ||
| // `impl(..) [const | unsafe | auto] trait` | ||
| (self.is_keyword_tree_ahead(2, &[kw::Const, kw::Unsafe, kw::Auto]) && self.is_keyword_tree_ahead(3, &[kw::Trait])) || | ||
| // `impl(..) const [unsafe | auto] trait` | ||
| (self.is_keyword_tree_ahead(2, &[kw::Const]) && self.is_keyword_tree_ahead(3, &[kw::Unsafe, kw::Auto]) && self.is_keyword_tree_ahead(4, &[kw::Trait])) || | ||
| // `impl(..) unsafe auto trait` | ||
| (self.is_keyword_tree_ahead(2, &[kw::Unsafe]) && self.is_keyword_tree_ahead(3, &[kw::Auto]) && self.is_keyword_tree_ahead(4, &[kw::Trait])) || | ||
| // `impl(..) const unsafe auto trait` | ||
| (self.is_keyword_tree_ahead(2, &[kw::Const]) && self.is_keyword_tree_ahead(3, &[kw::Unsafe]) && self.is_keyword_tree_ahead(4, &[kw::Auto]) && self.is_keyword_tree_ahead(5, &[kw::Trait])) | ||
| } else { | ||
| // `trait` | ||
| self.check_keyword(exp!(Trait)) || | ||
| // `auto trait` | ||
| self.check_keyword(exp!(Auto)) && self.is_keyword_ahead(1, &[kw::Trait]) || | ||
| // `unsafe auto? trait` | ||
| self.check_keyword(exp!(Unsafe)) && (self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait])) || | ||
| // `const unsafe? auto? trait` | ||
| self.check_keyword(exp!(Const)) && (self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Unsafe, kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Unsafe]) && self.is_keyword_ahead(2, &[kw::Auto]) && self.is_keyword_ahead(3, &[kw::Trait])) | ||
| } |
There was a problem hiding this comment.
I don't this blob of code, it seems brittle and very easy to miss a case. (like impl(..) auto trait?)
I wonder if we could be cleaver enumerate all the combinations ahead and exhaustively check them.
I'm thinking of something like that. (I have not tested it, it's
Rust-ish code)
| // `impl(` | |
| if self.check_keyword(exp!(Impl)) && self.look_ahead(1, |t| t == &token::OpenParen) { | |
| // `impl(..) trait` | |
| self.is_keyword_tree_ahead(2, &[kw::Trait]) || | |
| // `impl(..) [const | unsafe | auto] trait` | |
| (self.is_keyword_tree_ahead(2, &[kw::Const, kw::Unsafe, kw::Auto]) && self.is_keyword_tree_ahead(3, &[kw::Trait])) || | |
| // `impl(..) const [unsafe | auto] trait` | |
| (self.is_keyword_tree_ahead(2, &[kw::Const]) && self.is_keyword_tree_ahead(3, &[kw::Unsafe, kw::Auto]) && self.is_keyword_tree_ahead(4, &[kw::Trait])) || | |
| // `impl(..) unsafe auto trait` | |
| (self.is_keyword_tree_ahead(2, &[kw::Unsafe]) && self.is_keyword_tree_ahead(3, &[kw::Auto]) && self.is_keyword_tree_ahead(4, &[kw::Trait])) || | |
| // `impl(..) const unsafe auto trait` | |
| (self.is_keyword_tree_ahead(2, &[kw::Const]) && self.is_keyword_tree_ahead(3, &[kw::Unsafe]) && self.is_keyword_tree_ahead(4, &[kw::Auto]) && self.is_keyword_tree_ahead(5, &[kw::Trait])) | |
| } else { | |
| // `trait` | |
| self.check_keyword(exp!(Trait)) || | |
| // `auto trait` | |
| self.check_keyword(exp!(Auto)) && self.is_keyword_ahead(1, &[kw::Trait]) || | |
| // `unsafe auto? trait` | |
| self.check_keyword(exp!(Unsafe)) && (self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait])) || | |
| // `const unsafe? auto? trait` | |
| self.check_keyword(exp!(Const)) && (self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Unsafe, kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Unsafe]) && self.is_keyword_ahead(2, &[kw::Auto]) && self.is_keyword_ahead(3, &[kw::Trait])) | |
| } | |
| const SUFFIXES: &[&[kw::Keyword]] = &[ | |
| &[kw::Trait], | |
| &[kw::Auto, kw::Trait], | |
| &[kw::Unsafe, kw::Trait], | |
| &[kw::Unsafe, kw::Auto, kw::Trait], | |
| &[kw::Const, kw::Trait], | |
| &[kw::Const, kw::Auto, kw::Trait], | |
| &[kw::Const, kw::Unsafe, kw::Trait], | |
| &[kw::Const, kw::Unsafe, kw::Auto, kw::Trait], | |
| ]; | |
| // `impl(` | |
| if self.check_keyword(exp!(Impl)) && self.look_ahead(1, |t| t == &token::OpenParen) { | |
| SUFFIXES.iter().enumerate().any(|(idx, prefix)| self.is_keyword_tree_ahead(idx + 2, prefix)); | |
| } else { | |
| SUFFIXES.iter().enumerate().any(|(idx, prefix)| self.is_keyword_ahead(idx, prefix)); | |
| } | |
| } |
Based on #155222, this PR reorders keywords in trait definitions to group restrictions with visibility. It changes the order from
pub(...) const unsafe auto impl(...) trait Foo {...}topub(...) impl(...) const unsafe auto trait Foo {...}.Tracking issue for restrictions: #105077
r? @Urgau
cc @jhpratt