mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-23 02:27:39 +03:00
Rollup merge of #152943 - CoCo-Japan-pan:impl-restriction-parse, r=Urgau,jhpratt
Parse `impl` restrictions
This PR implements the parsing logic for `impl` restrictions (e.g., `pub impl(crate) trait Foo {}`) as proposed in [RFC 3323](https://rust-lang.github.io/rfcs/3323-restrictions.html).
As the first step of the RFC implementation, this PR focuses strictly on the parsing phase. The new syntax is guarded by the `#![feature(impl_restriction)]` feature gate.
This implementation basically follows the pattern used in rust-lang/rust#141754.
r? @jhpratt
This commit is contained in:
@@ -1025,17 +1025,79 @@ fn parse_defaultness(&mut self) -> Defaultness {
|
||||
}
|
||||
}
|
||||
|
||||
/// Is this an `(const unsafe? auto?| unsafe auto? | auto) trait` item?
|
||||
fn check_trait_front_matter(&mut self) -> bool {
|
||||
// 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, kw::Auto])
|
||||
|| self.check_keyword(exp!(Const)) && ((self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait]))
|
||||
|| self.is_keyword_ahead(1, &[kw::Unsafe]) && self.is_keyword_ahead(2, &[kw::Trait, kw::Auto]))
|
||||
/// Is there an `[ impl(in? path) ]? trait` item `dist` tokens ahead?
|
||||
fn is_trait_with_maybe_impl_restriction_in_front(&self, dist: usize) -> bool {
|
||||
// `trait`
|
||||
if self.is_keyword_ahead(dist, &[kw::Trait]) {
|
||||
return true;
|
||||
}
|
||||
// `impl(`
|
||||
if !self.is_keyword_ahead(dist, &[kw::Impl])
|
||||
|| !self.look_ahead(dist + 1, |t| t == &token::OpenParen)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// `crate | super | self) trait`
|
||||
if self.is_keyword_ahead(dist + 2, &[kw::Crate, kw::Super, kw::SelfLower])
|
||||
&& self.look_ahead(dist + 3, |t| t == &token::CloseParen)
|
||||
&& self.is_keyword_ahead(dist + 4, &[kw::Trait])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// `impl(in? something) trait`
|
||||
// We catch cases where the `in` keyword is missing to provide a
|
||||
// better error message. This is handled later in
|
||||
// `self.recover_incorrect_impl_restriction`.
|
||||
self.tree_look_ahead(dist + 2, |t| {
|
||||
if let TokenTree::Token(token, _) = t { token.is_keyword(kw::Trait) } else { false }
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
|
||||
/// Is this an `(const unsafe? auto? [ impl(in? path) ]? | unsafe auto? [ impl(in? path) ]? | auto [ impl(in? path) ]? | [ impl(in? path) ]?) trait` item?
|
||||
fn check_trait_front_matter(&mut self) -> bool {
|
||||
// `[ impl(in? path) ]? trait`
|
||||
if self.is_trait_with_maybe_impl_restriction_in_front(0) {
|
||||
return true;
|
||||
}
|
||||
// `auto [ impl(in? path) ]? trait`
|
||||
if self.check_keyword(exp!(Auto)) && self.is_trait_with_maybe_impl_restriction_in_front(1) {
|
||||
return true;
|
||||
}
|
||||
// `unsafe auto? [ impl(in? path) ]? trait`
|
||||
if self.check_keyword(exp!(Unsafe))
|
||||
&& (self.is_trait_with_maybe_impl_restriction_in_front(1)
|
||||
|| self.is_keyword_ahead(1, &[kw::Auto])
|
||||
&& self.is_trait_with_maybe_impl_restriction_in_front(2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// `const` ...
|
||||
if !self.check_keyword(exp!(Const)) {
|
||||
return false;
|
||||
}
|
||||
// `const [ impl(in? path) ]? trait`
|
||||
if self.is_trait_with_maybe_impl_restriction_in_front(1) {
|
||||
return true;
|
||||
}
|
||||
// `const (unsafe | auto) [ impl(in? path) ]? trait`
|
||||
if self.is_keyword_ahead(1, &[kw::Unsafe, kw::Auto])
|
||||
&& self.is_trait_with_maybe_impl_restriction_in_front(2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// `const unsafe auto [ impl(in? path) ]? trait`
|
||||
self.is_keyword_ahead(1, &[kw::Unsafe])
|
||||
&& self.is_keyword_ahead(2, &[kw::Auto])
|
||||
&& self.is_trait_with_maybe_impl_restriction_in_front(3)
|
||||
}
|
||||
|
||||
/// Parses `const? unsafe? auto? [impl(in? path)]? trait Foo { ... }` or `trait Foo = Bar;`.
|
||||
///
|
||||
/// FIXME(restrictions): The current keyword order follows the grammar specified in RFC 3323.
|
||||
/// However, whether the restriction should be grouped closer to the visibility modifier
|
||||
/// (e.g., `pub impl(crate) const unsafe auto trait`) remains an unresolved design question.
|
||||
/// This ordering must be kept in sync with the logic in `check_trait_front_matter`.
|
||||
fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemKind> {
|
||||
let constness = self.parse_constness(Case::Sensitive);
|
||||
if let Const::Yes(span) = constness {
|
||||
@@ -1050,6 +1112,8 @@ fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, Ite
|
||||
IsAuto::No
|
||||
};
|
||||
|
||||
let impl_restriction = self.parse_impl_restriction()?;
|
||||
|
||||
self.expect_keyword(exp!(Trait))?;
|
||||
let ident = self.parse_ident()?;
|
||||
let mut generics = self.parse_generics()?;
|
||||
@@ -1078,6 +1142,9 @@ fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, Ite
|
||||
if let Safety::Unsafe(_) = safety {
|
||||
self.dcx().emit_err(errors::TraitAliasCannotBeUnsafe { span: whole_span });
|
||||
}
|
||||
if let RestrictionKind::Restricted { .. } = impl_restriction.kind {
|
||||
self.dcx().emit_err(errors::TraitAliasCannotBeImplRestricted { span: whole_span });
|
||||
}
|
||||
|
||||
self.psess.gated_spans.gate(sym::trait_alias, whole_span);
|
||||
|
||||
@@ -1090,6 +1157,7 @@ fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, Ite
|
||||
constness,
|
||||
is_auto,
|
||||
safety,
|
||||
impl_restriction,
|
||||
ident,
|
||||
generics,
|
||||
bounds,
|
||||
@@ -2858,8 +2926,8 @@ pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> b
|
||||
&& !self.is_unsafe_foreign_mod()
|
||||
// Rule out `async gen {` and `async gen move {`
|
||||
&& !self.is_async_gen_block()
|
||||
// Rule out `const unsafe auto` and `const unsafe trait`.
|
||||
&& !self.is_keyword_ahead(2, &[kw::Auto, kw::Trait])
|
||||
// Rule out `const unsafe auto` and `const unsafe trait` and `const unsafe impl`.
|
||||
&& !self.is_keyword_ahead(2, &[kw::Auto, kw::Trait, kw::Impl])
|
||||
)
|
||||
})
|
||||
// `extern ABI fn`
|
||||
|
||||
@@ -35,8 +35,9 @@
|
||||
use rustc_ast::util::classify;
|
||||
use rustc_ast::{
|
||||
self as ast, AnonConst, AttrArgs, AttrId, BinOpKind, ByRef, Const, CoroutineKind,
|
||||
DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, MgcaDisambiguation,
|
||||
Mutability, Recovered, Safety, StrLit, Visibility, VisibilityKind,
|
||||
DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, ImplRestriction,
|
||||
MgcaDisambiguation, Mutability, Recovered, RestrictionKind, Safety, StrLit, Visibility,
|
||||
VisibilityKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::debug_assert_matches;
|
||||
@@ -50,7 +51,10 @@
|
||||
pub use token_type::{ExpKeywordPair, ExpTokenPair, TokenType};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::errors::{self, IncorrectVisibilityRestriction, NonStringAbiLiteral, TokenDescription};
|
||||
use crate::errors::{
|
||||
self, IncorrectImplRestriction, IncorrectVisibilityRestriction, NonStringAbiLiteral,
|
||||
TokenDescription,
|
||||
};
|
||||
use crate::exp;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -1530,6 +1534,60 @@ fn recover_incorrect_vis_restriction(&mut self) -> PResult<'a, ()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses an optional `impl` restriction.
|
||||
/// Enforces the `impl_restriction` feature gate whenever an explicit restriction is encountered.
|
||||
fn parse_impl_restriction(&mut self) -> PResult<'a, ImplRestriction> {
|
||||
if self.eat_keyword(exp!(Impl)) {
|
||||
let lo = self.prev_token.span;
|
||||
// No units or tuples are allowed to follow `impl` here, so we can safely bump `(`.
|
||||
self.expect(exp!(OpenParen))?;
|
||||
if self.eat_keyword(exp!(In)) {
|
||||
let path = self.parse_path(PathStyle::Mod)?; // `in path`
|
||||
self.expect(exp!(CloseParen))?; // `)`
|
||||
let restriction = RestrictionKind::Restricted {
|
||||
path: Box::new(path),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
shorthand: false,
|
||||
};
|
||||
let span = lo.to(self.prev_token.span);
|
||||
self.psess.gated_spans.gate(sym::impl_restriction, span);
|
||||
return Ok(ImplRestriction { kind: restriction, span, tokens: None });
|
||||
} else if self.look_ahead(1, |t| t == &token::CloseParen)
|
||||
&& self.is_keyword_ahead(0, &[kw::Crate, kw::Super, kw::SelfLower])
|
||||
{
|
||||
let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self`
|
||||
self.expect(exp!(CloseParen))?; // `)`
|
||||
let restriction = RestrictionKind::Restricted {
|
||||
path: Box::new(path),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
shorthand: true,
|
||||
};
|
||||
let span = lo.to(self.prev_token.span);
|
||||
self.psess.gated_spans.gate(sym::impl_restriction, span);
|
||||
return Ok(ImplRestriction { kind: restriction, span, tokens: None });
|
||||
} else {
|
||||
self.recover_incorrect_impl_restriction(lo)?;
|
||||
// Emit diagnostic, but continue with no impl restriction.
|
||||
}
|
||||
}
|
||||
Ok(ImplRestriction {
|
||||
kind: RestrictionKind::Unrestricted,
|
||||
span: self.token.span.shrink_to_lo(),
|
||||
tokens: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Recovery for e.g. `impl(something) trait`
|
||||
fn recover_incorrect_impl_restriction(&mut self, lo: Span) -> PResult<'a, ()> {
|
||||
let path = self.parse_path(PathStyle::Mod)?;
|
||||
self.expect(exp!(CloseParen))?; // `)`
|
||||
let path_str = pprust::path_to_string(&path);
|
||||
self.dcx().emit_err(IncorrectImplRestriction { span: path.span, inner_str: path_str });
|
||||
let end = self.prev_token.span;
|
||||
self.psess.gated_spans.gate(sym::impl_restriction, lo.to(end));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses `extern string_literal?`.
|
||||
fn parse_extern(&mut self, case: Case) -> Extern {
|
||||
if self.eat_keyword_case(exp!(Extern), case) {
|
||||
|
||||
Reference in New Issue
Block a user