Skip to content

Commit ce2dae3

Browse files
committed
Parse impl restriction on trait definitions
1 parent 4549003 commit ce2dae3

File tree

11 files changed

+206
-15
lines changed

11 files changed

+206
-15
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3530,6 +3530,7 @@ impl Default for FnHeader {
35303530

35313531
#[derive(Clone, Encodable, Decodable, Debug)]
35323532
pub struct Trait {
3533+
pub impl_restriction: Restriction,
35333534
pub safety: Safety,
35343535
pub is_auto: IsAuto,
35353536
pub ident: Ident,

compiler/rustc_ast/src/mut_visit.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1182,7 +1182,16 @@ impl WalkItemKind for ItemKind {
11821182
vis.flat_map_assoc_item(item, AssocCtxt::Impl { of_trait: of_trait.is_some() })
11831183
});
11841184
}
1185-
ItemKind::Trait(box Trait { safety, is_auto: _, ident, generics, bounds, items }) => {
1185+
ItemKind::Trait(box Trait {
1186+
impl_restriction,
1187+
safety,
1188+
is_auto: _,
1189+
ident,
1190+
generics,
1191+
bounds,
1192+
items,
1193+
}) => {
1194+
vis.visit_restriction(impl_restriction);
11861195
visit_safety(vis, safety);
11871196
vis.visit_ident(ident);
11881197
vis.visit_generics(generics);

compiler/rustc_ast/src/visit.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,13 +522,15 @@ impl WalkItemKind for ItemKind {
522522
try_visit!(visitor.visit_variant_data(struct_definition));
523523
}
524524
ItemKind::Trait(box Trait {
525+
impl_restriction,
525526
safety: _,
526527
is_auto: _,
527528
ident,
528529
generics,
529530
bounds,
530531
items,
531532
}) => {
533+
try_visit!(visitor.visit_restriction(impl_restriction));
532534
try_visit!(visitor.visit_ident(ident));
533535
try_visit!(visitor.visit_generics(generics));
534536
walk_list!(visitor, visit_param_bound, bounds, BoundKind::SuperTraits);

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
415415
items: new_impl_items,
416416
}))
417417
}
418-
ItemKind::Trait(box Trait { is_auto, safety, ident, generics, bounds, items }) => {
418+
ItemKind::Trait(box Trait {
419+
impl_restriction: _,
420+
is_auto,
421+
safety,
422+
ident,
423+
generics,
424+
bounds,
425+
items,
426+
}) => {
419427
let ident = self.lower_ident(*ident);
420428
let (generics, (safety, items, bounds)) = self.lower_generics(
421429
generics,

compiler/rustc_ast_pretty/src/pprust/state/item.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ impl<'a> State<'a> {
357357
self.bclose(item.span, empty, cb);
358358
}
359359
ast::ItemKind::Trait(box ast::Trait {
360+
impl_restriction,
360361
safety,
361362
is_auto,
362363
ident,
@@ -366,6 +367,7 @@ impl<'a> State<'a> {
366367
}) => {
367368
let (cb, ib) = self.head("");
368369
self.print_visibility(&item.vis);
370+
self.print_restriction("impl", impl_restriction);
369371
self.print_safety(*safety);
370372
self.print_is_auto(*is_auto);
371373
self.word_nbsp("trait");

compiler/rustc_parse/messages.ftl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,13 @@ parse_inclusive_range_no_end = inclusive range with no end
353353
parse_incorrect_parens_trait_bounds = incorrect parentheses around trait bounds
354354
parse_incorrect_parens_trait_bounds_sugg = fix the parentheses
355355
356+
parse_incorrect_restriction = incorrect {$noun} restriction
357+
.help = some possible {$noun} restrictions are:
358+
`{$keyword}(crate)`: {$adjective} only in the current crate
359+
`{$keyword}(super)`: {$adjective} only in the current module's parent
360+
`{$keyword}(in path::to::module)`: {$adjective} only in the specified path
361+
.suggestion = make this {$adjective} only to module `{$inner_str}` with `in`
362+
356363
parse_incorrect_semicolon =
357364
expected item, found `;`
358365
.suggestion = remove this semicolon
@@ -829,6 +836,7 @@ parse_trailing_vert_not_allowed = a trailing `|` is not allowed in an or-pattern
829836
parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto`
830837
parse_trait_alias_cannot_be_unsafe = trait aliases cannot be `unsafe`
831838
839+
parse_trait_alias_cannot_have_impl_restriction = trait alias cannot have `impl` restriction
832840
parse_transpose_dyn_or_impl = `for<...>` expected after `{$kw}`, not before
833841
.suggestion = move `{$kw}` before the `for<...>`
834842

compiler/rustc_parse/src/errors.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,19 @@ pub(crate) struct IncorrectVisibilityRestriction {
10411041
pub inner_str: String,
10421042
}
10431043

1044+
#[derive(Diagnostic)]
1045+
#[diag(parse_incorrect_restriction, code = E0704)]
1046+
#[help]
1047+
pub(crate) struct IncorrectRestriction<'kw> {
1048+
#[primary_span]
1049+
#[suggestion(code = "in {inner_str}", applicability = "machine-applicable", style = "verbose")]
1050+
pub span: Span,
1051+
pub inner_str: String,
1052+
pub keyword: &'kw str,
1053+
pub adjective: &'static str,
1054+
pub noun: &'static str,
1055+
}
1056+
10441057
#[derive(Diagnostic)]
10451058
#[diag(parse_assignment_else_not_allowed)]
10461059
pub(crate) struct AssignmentElseNotAllowed {
@@ -1946,6 +1959,16 @@ pub(crate) struct BoundsNotAllowedOnTraitAliases {
19461959
pub span: Span,
19471960
}
19481961

1962+
#[derive(Diagnostic)]
1963+
#[diag(parse_trait_alias_cannot_have_impl_restriction)]
1964+
pub(crate) struct TraitAliasCannotHaveImplRestriction {
1965+
#[primary_span]
1966+
#[label(parse_trait_alias_cannot_have_impl_restriction)]
1967+
pub span: Span,
1968+
#[suggestion(code = "", applicability = "machine-applicable", style = "short")]
1969+
pub restriction: Span,
1970+
}
1971+
19491972
#[derive(Diagnostic)]
19501973
#[diag(parse_trait_alias_cannot_be_auto)]
19511974
pub(crate) struct TraitAliasCannotBeAuto {

compiler/rustc_parse/src/parser/item.rs

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ impl<'a> Parser<'a> {
262262
define_opaque: None,
263263
}))
264264
}
265-
} else if self.check_keyword(exp!(Trait)) || self.check_auto_or_unsafe_trait_item() {
265+
} else if self.check_keyword(exp!(Trait)) || self.check_trait_item_with_modifiers() {
266266
// TRAIT ITEM
267267
self.parse_item_trait(attrs, lo)?
268268
} else if self.check_keyword(exp!(Impl))
@@ -373,7 +373,7 @@ impl<'a> Parser<'a> {
373373
pub(super) fn is_path_start_item(&mut self) -> bool {
374374
self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }`
375375
|| self.is_reuse_path_item()
376-
|| self.check_auto_or_unsafe_trait_item() // no: `auto::b`, yes: `auto trait X { .. }`
376+
|| self.check_trait_item_with_modifiers() // no: `auto::b`, yes: `auto trait X { .. }`
377377
|| self.is_async_fn() // no(2015): `async::b`, yes: `async fn`
378378
|| matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac`
379379
}
@@ -872,16 +872,61 @@ impl<'a> Parser<'a> {
872872
}
873873
}
874874

875-
/// Is this an `(unsafe auto? | auto) trait` item?
876-
fn check_auto_or_unsafe_trait_item(&mut self) -> bool {
877-
// auto trait
878-
self.check_keyword(exp!(Auto)) && self.is_keyword_ahead(1, &[kw::Trait])
879-
// unsafe auto trait
880-
|| self.check_keyword(exp!(Unsafe)) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto])
875+
/// Is this an `[ impl(in path) ]? unsafe? auto? trait` item?
876+
fn check_trait_item_with_modifiers(&mut self) -> bool {
877+
let current_token = TokenTree::Token(self.token.clone(), self.token_spacing);
878+
let look_ahead = |index| match index {
879+
0 => Some(&current_token),
880+
_ => self.token_cursor.curr.look_ahead(index - 1),
881+
};
882+
883+
let mut has_impl_restriction = false;
884+
let mut has_unsafe = false;
885+
let mut has_auto = false;
886+
let mut has_trait = false;
887+
888+
let mut index = 0;
889+
890+
if let Some(TokenTree::Token(token, _)) = look_ahead(index)
891+
&& token.is_keyword(kw::Impl)
892+
{
893+
has_impl_restriction = true;
894+
index += 1;
895+
}
896+
// impl restrictions require parens, but we enforce this later.
897+
if has_impl_restriction
898+
&& matches!(
899+
look_ahead(index),
900+
Some(TokenTree::Delimited(_, _, Delimiter::Parenthesis, _))
901+
)
902+
{
903+
index += 1;
904+
}
905+
if let Some(TokenTree::Token(token, _)) = look_ahead(index)
906+
&& token.is_keyword(kw::Unsafe)
907+
{
908+
has_unsafe = true;
909+
index += 1;
910+
}
911+
if let Some(TokenTree::Token(token, _)) = look_ahead(index)
912+
&& token.is_keyword(kw::Auto)
913+
{
914+
has_auto = true;
915+
index += 1;
916+
}
917+
if let Some(TokenTree::Token(token, _)) = look_ahead(index)
918+
&& token.is_keyword(kw::Trait)
919+
{
920+
has_trait = true;
921+
}
922+
923+
(has_impl_restriction || has_unsafe || has_auto) && has_trait
881924
}
882925

883-
/// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
926+
/// Parses `[ impl(in path) ]? unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
884927
fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemKind> {
928+
let impl_restriction =
929+
self.parse_restriction(exp!(Impl), "implementable", "impl", FollowedByType::No)?;
885930
let safety = self.parse_safety(Case::Sensitive);
886931
// Parse optional `auto` prefix.
887932
let is_auto = if self.eat_keyword(exp!(Auto)) {
@@ -913,6 +958,12 @@ impl<'a> Parser<'a> {
913958
self.expect_semi()?;
914959

915960
let whole_span = lo.to(self.prev_token.span);
961+
if !matches!(impl_restriction.kind, RestrictionKind::Implied) {
962+
self.dcx().emit_err(errors::TraitAliasCannotHaveImplRestriction {
963+
span: whole_span,
964+
restriction: impl_restriction.span,
965+
});
966+
}
916967
if is_auto == IsAuto::Yes {
917968
self.dcx().emit_err(errors::TraitAliasCannotBeAuto { span: whole_span });
918969
}
@@ -927,7 +978,15 @@ impl<'a> Parser<'a> {
927978
// It's a normal trait.
928979
generics.where_clause = self.parse_where_clause()?;
929980
let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?;
930-
Ok(ItemKind::Trait(Box::new(Trait { is_auto, safety, ident, generics, bounds, items })))
981+
Ok(ItemKind::Trait(Box::new(Trait {
982+
impl_restriction,
983+
is_auto,
984+
safety,
985+
ident,
986+
generics,
987+
bounds,
988+
items,
989+
})))
931990
}
932991
}
933992

compiler/rustc_parse/src/parser/mod.rs

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ use rustc_ast::tokenstream::{
3030
use rustc_ast::util::case::Case;
3131
use rustc_ast::{
3232
self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID,
33-
DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit,
34-
Visibility, VisibilityKind,
33+
DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Restriction,
34+
Safety, StrLit, Visibility, VisibilityKind,
3535
};
3636
use rustc_ast_pretty::pprust;
3737
use rustc_data_structures::fx::FxHashMap;
@@ -44,7 +44,9 @@ use token_type::TokenTypeSet;
4444
pub use token_type::{ExpKeywordPair, ExpTokenPair, TokenType};
4545
use tracing::debug;
4646

47-
use crate::errors::{self, IncorrectVisibilityRestriction, NonStringAbiLiteral};
47+
use crate::errors::{
48+
self, IncorrectRestriction, IncorrectVisibilityRestriction, NonStringAbiLiteral,
49+
};
4850
use crate::exp;
4951

5052
#[cfg(test)]
@@ -1513,6 +1515,80 @@ impl<'a> Parser<'a> {
15131515
Ok(())
15141516
}
15151517

1518+
/// Parses `kw`, `kw(in path)` plus shortcuts `kw(crate)` for `kw(in crate)`, `kw(self)` for
1519+
/// `kw(in self)` and `kw(super)` for `kw(in super)`.
1520+
fn parse_restriction(
1521+
&mut self,
1522+
kw: ExpKeywordPair,
1523+
action: &'static str,
1524+
description: &'static str,
1525+
fbt: FollowedByType,
1526+
) -> PResult<'a, Restriction> {
1527+
if !self.eat_keyword(kw) {
1528+
// We need a span, but there's inherently no keyword to grab a span from for an implied
1529+
// restriction. An empty span at the beginning of the current token is a reasonable
1530+
// fallback.
1531+
return Ok(Restriction::implied().with_span(self.token.span.shrink_to_lo()));
1532+
}
1533+
1534+
let lo = self.prev_token.span;
1535+
1536+
if self.check(exp!(OpenParen)) {
1537+
// We don't `self.bump()` the `(` yet because this might be a struct definition where
1538+
// `()` or a tuple might be allowed. For example, `struct Struct(kw (), kw (usize));`.
1539+
// Because of this, we only `bump` the `(` if we're assured it is appropriate to do so
1540+
// by the following tokens.
1541+
if self.is_keyword_ahead(1, &[kw::In]) {
1542+
// Parse `kw(in path)`.
1543+
self.bump(); // `(`
1544+
self.bump(); // `in`
1545+
let path = self.parse_path(PathStyle::Mod)?; // `path`
1546+
self.expect(exp!(CloseParen))?; // `)`
1547+
return Ok(Restriction::restricted(P(path), ast::DUMMY_NODE_ID, false)
1548+
.with_span(lo.to(self.prev_token.span)));
1549+
} else if self.look_ahead(2, |t| t == &TokenKind::CloseParen)
1550+
&& self.is_keyword_ahead(1, &[kw::Crate, kw::Super, kw::SelfLower])
1551+
{
1552+
// Parse `kw(crate)`, `kw(self)`, or `kw(super)`.
1553+
self.bump(); // `(`
1554+
let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self`
1555+
self.expect(exp!(CloseParen))?; // `)`
1556+
return Ok(Restriction::restricted(P(path), ast::DUMMY_NODE_ID, false)
1557+
.with_span(lo.to(self.prev_token.span)));
1558+
} else if let FollowedByType::No = fbt {
1559+
// Provide this diagnostic if a type cannot follow;
1560+
// in particular, if this is not a tuple struct.
1561+
self.recover_incorrect_restriction(kw.kw.as_str(), action, description)?;
1562+
// Emit diagnostic, but continue unrestricted.
1563+
}
1564+
}
1565+
1566+
Ok(Restriction::unrestricted().with_span(lo))
1567+
}
1568+
1569+
/// Recovery for e.g. `kw(something) fn ...` or `struct X { kw(something) y: Z }`
1570+
fn recover_incorrect_restriction<'kw>(
1571+
&mut self,
1572+
kw: &'kw str,
1573+
action: &'static str,
1574+
description: &'static str,
1575+
) -> PResult<'a, ()> {
1576+
self.bump(); // `(`
1577+
let path = self.parse_path(PathStyle::Mod)?;
1578+
self.expect(exp!(CloseParen))?; // `)`
1579+
1580+
let path_str = pprust::path_to_string(&path);
1581+
self.dcx().emit_err(IncorrectRestriction {
1582+
span: path.span,
1583+
inner_str: path_str,
1584+
keyword: kw,
1585+
adjective: action,
1586+
noun: description,
1587+
});
1588+
1589+
Ok(())
1590+
}
1591+
15161592
/// Parses `extern string_literal?`.
15171593
fn parse_extern(&mut self, case: Case) -> Extern {
15181594
if self.eat_keyword_case(exp!(Extern), case) {

src/tools/clippy/clippy_utils/src/ast_utils/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
444444
},
445445
(
446446
Trait(box ast::Trait {
447+
impl_restriction: _, // does not affect item kind
447448
is_auto: la,
448449
safety: lu,
449450
ident: li,
@@ -452,6 +453,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
452453
items: lis,
453454
}),
454455
Trait(box ast::Trait {
456+
impl_restriction: _, // does not affect item kind
455457
is_auto: ra,
456458
safety: ru,
457459
ident: ri,

src/tools/rustfmt/src/items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,7 @@ pub(crate) fn format_trait(
11721172
unreachable!();
11731173
};
11741174
let ast::Trait {
1175+
impl_restriction: _,
11751176
is_auto,
11761177
safety,
11771178
ident,

0 commit comments

Comments
 (0)