Auto merge of #155687 - GuillaumeGomez:rollup-aZ7YrAD, r=GuillaumeGomez

Rollup of 7 pull requests

Successful merges:

 - rust-lang/rust#155469 (Account for titlecase in casing lints)
 - rust-lang/rust#155644 (delegation: support self ty propagation for functions in free to trait reuse)
 - rust-lang/rust#154957 (Fix ICE when const closure appears inside a non-const trait method)
 - rust-lang/rust#155442 (Change keyword order for `impl` restrictions)
 - rust-lang/rust#155561 (Use singular wording for single _ placeholders in type suggestions)
 - rust-lang/rust#155637 (Fix E0191 suggestion for empty dyn trait args)
 - rust-lang/rust#155661 (Remove `AttributeLintKind` variants - part 6)
This commit is contained in:
bors
2026-04-23 15:55:48 +00:00
234 changed files with 1686 additions and 1353 deletions
+1 -1
View File
@@ -3786,10 +3786,10 @@ pub struct TraitAlias {
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct Trait {
pub impl_restriction: ImplRestriction,
pub constness: Const,
pub safety: Safety,
pub is_auto: IsAuto,
pub impl_restriction: ImplRestriction,
pub ident: Ident,
pub generics: Generics,
#[visitable(extra = BoundKind::SuperTraits)]
+10 -1
View File
@@ -143,7 +143,8 @@ pub(crate) fn lower_delegation(
let (param_count, c_variadic) = self.param_count(sig_id);
let mut generics = self.uplift_delegation_generics(delegation, sig_id, item_id);
let mut generics =
self.uplift_delegation_generics(delegation, sig_id, item_id, is_method);
let body_id = self.lower_delegation_body(
delegation,
@@ -301,6 +302,8 @@ fn lower_delegation_decl(
hir::InferDelegationSig::Output(self.arena.alloc(hir::DelegationGenerics {
child_args_segment_id: generics.child.args_segment_id,
parent_args_segment_id: generics.parent.args_segment_id,
self_ty_id: generics.self_ty_id,
propagate_self_ty: generics.propagate_self_ty,
})),
)),
span,
@@ -554,6 +557,12 @@ fn finalize_body_lowering(
}
};
generics.self_ty_id = match new_path {
hir::QPath::Resolved(ty, _) => ty,
hir::QPath::TypeRelative(ty, _) => Some(ty),
}
.map(|ty| ty.hir_id);
let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(new_path), span));
self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span))
};
@@ -67,6 +67,8 @@ pub(super) struct GenericsGenerationResult<'hir> {
pub(super) struct GenericsGenerationResults<'hir> {
pub(super) parent: GenericsGenerationResult<'hir>,
pub(super) child: GenericsGenerationResult<'hir>,
pub(super) self_ty_id: Option<HirId>,
pub(super) propagate_self_ty: bool,
}
pub(super) struct GenericArgsPropagationDetails {
@@ -209,6 +211,7 @@ pub(super) fn uplift_delegation_generics(
delegation: &Delegation,
sig_id: DefId,
item_id: NodeId,
is_method: bool,
) -> GenericsGenerationResults<'hir> {
let delegation_parent_kind =
self.tcx.def_kind(self.tcx.local_parent(self.local_def_id(item_id)));
@@ -230,7 +233,12 @@ pub(super) fn uplift_delegation_generics(
let child = DelegationGenerics::trait_impl(sig_params, child_user_specified);
let child = GenericsGenerationResult::new(child);
return GenericsGenerationResults { parent, child };
return GenericsGenerationResults {
parent,
child,
self_ty_id: None,
propagate_self_ty: false,
};
}
let delegation_in_free_ctx =
@@ -238,13 +246,14 @@ pub(super) fn uplift_delegation_generics(
let sig_parent = self.tcx.parent(sig_id);
let sig_in_trait = matches!(self.tcx.def_kind(sig_parent), DefKind::Trait);
let free_to_trait_delegation = delegation_in_free_ctx && sig_in_trait;
let generate_self = free_to_trait_delegation && is_method && delegation.qself.is_none();
let can_add_generics_to_parent = len >= 2
&& self.get_resolution_id(segments[len - 2].id).is_some_and(|def_id| {
matches!(self.tcx.def_kind(def_id), DefKind::Trait | DefKind::TraitAlias)
});
let generate_self = delegation_in_free_ctx && sig_in_trait;
let parent_generics = if can_add_generics_to_parent {
let sig_parent_params = &self.tcx.generics_of(sig_parent).own_params[..];
@@ -278,6 +287,8 @@ pub(super) fn uplift_delegation_generics(
GenericsGenerationResults {
parent: GenericsGenerationResult::new(parent_generics),
child: GenericsGenerationResult::new(child_generics),
self_ty_id: None,
propagate_self_ty: free_to_trait_delegation && !generate_self,
}
}
+2 -2
View File
@@ -556,10 +556,10 @@ fn lower_item_kind(
})
}
ItemKind::Trait(box Trait {
impl_restriction,
constness,
is_auto,
safety,
impl_restriction,
ident,
generics,
bounds,
@@ -586,10 +586,10 @@ fn lower_item_kind(
},
);
hir::ItemKind::Trait(
impl_restriction,
constness,
*is_auto,
safety,
impl_restriction,
ident,
generics,
bounds,
@@ -371,10 +371,10 @@ pub(crate) fn print_item(&mut self, item: &ast::Item) {
self.bclose(item.span, empty, cb);
}
ast::ItemKind::Trait(box ast::Trait {
impl_restriction,
constness,
safety,
is_auto,
impl_restriction,
ident,
generics,
bounds,
@@ -382,10 +382,10 @@ pub(crate) fn print_item(&mut self, item: &ast::Item) {
}) => {
let (cb, ib) = self.head("");
self.print_visibility(&item.vis);
self.print_impl_restriction(impl_restriction);
self.print_constness(*constness);
self.print_safety(*safety);
self.print_is_auto(*is_auto);
self.print_impl_restriction(impl_restriction);
self.word_nbsp("trait");
self.print_ident(*ident);
self.print_generic_params(&generics.params);
@@ -1,6 +1,6 @@
use std::ops::Range;
use rustc_errors::E0232;
use rustc_errors::{Diagnostic, E0232};
use rustc_hir::AttrPath;
use rustc_hir::attrs::diagnostic::{
Directive, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, Name, NameValue,
@@ -18,6 +18,10 @@
use thin_vec::{ThinVec, thin_vec};
use crate::context::{AcceptContext, Stage};
use crate::errors::{
DisallowedPlaceholder, DisallowedPositionalArgument, IgnoredDiagnosticOption,
InvalidFormatSpecifier, MalFormedDiagnosticAttributeLint, WrappedParserError,
};
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser};
pub(crate) mod do_not_recommend;
@@ -112,12 +116,12 @@ fn merge<T, S: Stage>(
match (first, later) {
(Some(_) | None, None) => {}
(Some((first_span, _)), Some((later_span, _))) => {
cx.emit_lint(
let first_span = *first_span;
cx.emit_dyn_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::IgnoredDiagnosticOption {
first_span: *first_span,
later_span,
option_name,
move |dcx, level| {
IgnoredDiagnosticOption { first_span, later_span, option_name }
.into_diag(dcx, level)
},
later_span,
);
@@ -157,12 +161,15 @@ fn parse_list<'p, S: Stage>(
);
}
ArgParser::NameValue(_) => {
cx.emit_lint(
cx.emit_dyn_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MalFormedDiagnosticAttribute {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
move |dcx, level| {
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}
.into_diag(dcx, level)
},
span,
);
@@ -188,12 +195,15 @@ fn parse_directive_items<'p, S: Stage>(
let span = item.span();
macro malformed() {{
cx.emit_lint(
cx.emit_dyn_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MalFormedDiagnosticAttribute {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
move |dcx, level| {
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}
.into_diag(dcx, level)
},
span,
);
@@ -210,13 +220,14 @@ fn parse_directive_items<'p, S: Stage>(
}}
macro duplicate($name: ident, $($first_span:tt)*) {{
cx.emit_lint(
let first_span = $($first_span)*;
cx.emit_dyn_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::IgnoredDiagnosticOption {
first_span: $($first_span)*,
move |dcx, level| IgnoredDiagnosticOption {
first_span,
later_span: span,
option_name: $name,
},
}.into_diag(dcx, level),
span,
);
}}
@@ -245,9 +256,19 @@ fn parse_directive_items<'p, S: Stage>(
let (FormatWarning::InvalidSpecifier { span, .. }
| FormatWarning::PositionalArgument { span, .. }
| FormatWarning::DisallowedPlaceholder { span }) = warning;
cx.emit_lint(
cx.emit_dyn_lint(
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
AttributeLintKind::MalformedDiagnosticFormat { warning },
move |dcx, level| match warning {
FormatWarning::PositionalArgument { .. } => {
DisallowedPositionalArgument.into_diag(dcx, level)
}
FormatWarning::InvalidSpecifier { .. } => {
InvalidFormatSpecifier.into_diag(dcx, level)
}
FormatWarning::DisallowedPlaceholder { .. } => {
DisallowedPlaceholder.into_diag(dcx, level)
}
},
span,
);
}
@@ -255,12 +276,15 @@ fn parse_directive_items<'p, S: Stage>(
f
}
Err(e) => {
cx.emit_lint(
cx.emit_dyn_lint(
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
AttributeLintKind::DiagnosticWrappedParserError {
description: e.description,
label: e.label,
span: slice_span(input.span, e.span, is_snippet),
move |dcx, level| {
WrappedParserError {
description: &e.description,
label: &e.label,
span: slice_span(input.span, e.span.clone(), is_snippet),
}
.into_diag(dcx, level)
},
input.span,
);
@@ -5,7 +5,6 @@
use rustc_hir::attrs::{
AttributeKind, CfgEntry, CfgHideShow, CfgInfo, DocAttribute, DocInline, HideOrShow,
};
use rustc_hir::lints::AttributeLintKind;
use rustc_session::parse::feature_err;
use rustc_span::{Span, Symbol, edition, sym};
use thin_vec::ThinVec;
@@ -17,7 +16,8 @@
AttrCrateLevelOnly, DocAliasDuplicated, DocAutoCfgExpectsHideOrShow,
DocAutoCfgHideShowExpectsList, DocAutoCfgHideShowUnexpectedItem, DocAutoCfgWrongLiteral,
DocTestLiteral, DocTestTakesList, DocTestUnknown, DocUnknownAny, DocUnknownInclude,
DocUnknownPasses, DocUnknownPlugins, DocUnknownSpotlight, IllFormedAttributeInput,
DocUnknownPasses, DocUnknownPlugins, DocUnknownSpotlight, ExpectedNameValue, ExpectedNoArgs,
IllFormedAttributeInput, MalformedDoc,
};
use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser};
use crate::session_diagnostics::{
@@ -84,18 +84,18 @@ fn expected_name_value<S: Stage>(
span: Span,
_name: Option<Symbol>,
) {
cx.emit_lint(
cx.emit_dyn_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::ExpectedNameValue,
|dcx, level| ExpectedNameValue.into_diag(dcx, level),
span,
);
}
// FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead.
fn expected_no_args<S: Stage>(cx: &mut AcceptContext<'_, '_, S>, span: Span) {
cx.emit_lint(
cx.emit_dyn_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::ExpectedNoArgs,
|dcx, level| ExpectedNoArgs.into_diag(dcx, level),
span,
);
}
@@ -107,9 +107,9 @@ fn expected_string_literal<S: Stage>(
span: Span,
_actual_literal: Option<&MetaItemLit>,
) {
cx.emit_lint(
cx.emit_dyn_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::MalformedDoc,
|dcx, level| MalformedDoc.into_diag(dcx, level),
span,
);
}
@@ -203,9 +203,9 @@ fn parse_single_test_doc_attr_item<S: Stage>(
// FIXME: remove this method once merged and uncomment the line below instead.
// cx.expected_list(cx.attr_span, args);
let span = cx.attr_span;
cx.emit_lint(
cx.emit_dyn_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::MalformedDoc,
|dcx, level| MalformedDoc.into_diag(dcx, level),
span,
);
return;
@@ -399,9 +399,9 @@ fn parse_auto_cfg<S: Stage>(
// FIXME: remove this method once merged and uncomment the line
// below instead.
// cx.expected_identifier(sub_item.path().span());
cx.emit_lint(
cx.emit_dyn_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::MalformedDoc,
|dcx, level| MalformedDoc.into_diag(dcx, level),
sub_item.path().span(),
);
continue;
@@ -605,9 +605,9 @@ macro_rules! string_arg_and_crate_level {
// FIXME: remove this method once merged and uncomment the line
// below instead.
// cx.unexpected_literal(lit.span);
cx.emit_lint(
cx.emit_dyn_lint(
rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES,
AttributeLintKind::MalformedDoc,
|dcx, level| MalformedDoc.into_diag(dcx, level),
lit.span,
);
}
+67
View File
@@ -325,3 +325,70 @@ pub(crate) struct IncorrectDoNotRecommendLocation {
#[label("not a trait implementation")]
pub target_span: Span,
}
#[derive(Diagnostic)]
#[diag("malformed `doc` attribute input")]
#[warning(
"this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!"
)]
pub(crate) struct MalformedDoc;
#[derive(Diagnostic)]
#[diag("didn't expect any arguments here")]
#[warning(
"this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!"
)]
pub(crate) struct ExpectedNoArgs;
#[derive(Diagnostic)]
#[diag("expected this to be of the form `... = \"...\"`")]
#[warning(
"this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!"
)]
pub(crate) struct ExpectedNameValue;
#[derive(Diagnostic)]
#[diag("malformed `{$attribute}` attribute")]
#[help("{$options}")]
pub(crate) struct MalFormedDiagnosticAttributeLint {
pub attribute: &'static str,
pub options: &'static str,
#[label("invalid option found here")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("positional format arguments are not allowed here")]
#[help(
"only named format arguments with the name of one of the generic types are allowed in this context"
)]
pub(crate) struct DisallowedPositionalArgument;
#[derive(Diagnostic)]
#[diag("format arguments are not allowed here")]
#[help("consider removing this format argument")]
pub(crate) struct DisallowedPlaceholder;
#[derive(Diagnostic)]
#[diag("invalid format specifier")]
#[help("no format specifier are supported in this position")]
pub(crate) struct InvalidFormatSpecifier;
#[derive(Diagnostic)]
#[diag("{$description}")]
pub(crate) struct WrappedParserError<'a> {
pub description: &'a str,
#[label("{$label}")]
pub span: Span,
pub label: &'a str,
}
#[derive(Diagnostic)]
#[diag("`{$option_name}` is ignored due to previous definition of `{$option_name}`")]
pub(crate) struct IgnoredDiagnosticOption {
pub option_name: Symbol,
#[label("`{$option_name}` is first declared here")]
pub first_span: Span,
#[label("`{$option_name}` is later redundantly declared here")]
pub later_span: Span,
}
+6 -4
View File
@@ -3761,6 +3761,8 @@ pub enum OpaqueTyOrigin<D> {
pub struct DelegationGenerics {
pub parent_args_segment_id: Option<HirId>,
pub child_args_segment_id: Option<HirId>,
pub self_ty_id: Option<HirId>,
pub propagate_self_ty: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable_Generic)]
@@ -4455,17 +4457,17 @@ pub fn is_struct_or_union(&self) -> bool {
expect_trait,
(
&'hir ImplRestriction<'hir>,
Constness,
IsAuto,
Safety,
&'hir ImplRestriction<'hir>,
Ident,
&'hir Generics<'hir>,
GenericBounds<'hir>,
&'hir [TraitItemId]
),
ItemKind::Trait(constness, is_auto, safety, impl_restriction, ident, generics, bounds, items),
(*constness, *is_auto, *safety, impl_restriction, *ident, generics, bounds, items);
ItemKind::Trait(impl_restriction, constness, is_auto, safety, ident, generics, bounds, items),
(impl_restriction, *constness, *is_auto, *safety, *ident, generics, bounds, items);
expect_trait_alias, (Constness, Ident, &'hir Generics<'hir>, GenericBounds<'hir>),
ItemKind::TraitAlias(constness, ident, generics, bounds), (*constness, *ident, generics, bounds);
@@ -4657,10 +4659,10 @@ pub enum ItemKind<'hir> {
Union(Ident, &'hir Generics<'hir>, VariantData<'hir>),
/// A trait definition.
Trait(
&'hir ImplRestriction<'hir>,
Constness,
IsAuto,
Safety,
&'hir ImplRestriction<'hir>,
Ident,
&'hir Generics<'hir>,
GenericBounds<'hir>,
+1 -1
View File
@@ -619,10 +619,10 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
try_visit!(visitor.visit_variant_data(struct_definition));
}
ItemKind::Trait(
ref impl_restriction,
_constness,
_is_auto,
_safety,
ref impl_restriction,
ident,
ref generics,
bounds,
+2 -2
View File
@@ -895,7 +895,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
let item = tcx.hir_expect_item(def_id);
let (constness, is_alias, is_auto, safety, impl_restriction) = match item.kind {
hir::ItemKind::Trait(constness, is_auto, safety, impl_restriction, ..) => (
hir::ItemKind::Trait(impl_restriction, constness, is_auto, safety, ..) => (
constness,
false,
is_auto == hir::IsAuto::Yes,
@@ -959,9 +959,9 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
ty::TraitDef {
def_id: def_id.to_def_id(),
impl_restriction,
safety,
constness,
impl_restriction,
paren_sugar,
has_auto_impl: is_auto,
is_marker,
+80 -26
View File
@@ -7,7 +7,7 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{HirId, PathSegment};
use rustc_hir::{DelegationGenerics, HirId, PathSegment};
use rustc_middle::ty::{
self, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
};
@@ -65,19 +65,35 @@ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
}
enum SelfPositionKind {
AfterLifetimes,
AfterLifetimes(bool /* Should propagate self ty */),
Zero,
None,
}
fn create_self_position_kind(caller_kind: FnKind, callee_kind: FnKind) -> SelfPositionKind {
match (caller_kind, callee_kind) {
fn get_delegation_generics(tcx: TyCtxt<'_>, delegation_id: LocalDefId) -> &DelegationGenerics {
tcx.hir_node(tcx.local_def_id_to_hir_id(delegation_id))
.fn_sig()
.expect("processing delegation")
.decl
.opt_delegation_generics()
.expect("processing delegation")
}
fn create_self_position_kind(
tcx: TyCtxt<'_>,
delegation_id: LocalDefId,
sig_id: DefId,
) -> SelfPositionKind {
match (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id)) {
(FnKind::AssocInherentImpl, FnKind::AssocTrait)
| (FnKind::AssocTraitImpl, FnKind::AssocTrait)
| (FnKind::AssocTrait, FnKind::AssocTrait)
| (FnKind::AssocTrait, FnKind::Free) => SelfPositionKind::Zero,
(FnKind::Free, FnKind::AssocTrait) => SelfPositionKind::AfterLifetimes,
(FnKind::Free, FnKind::AssocTrait) => {
let propagate_self_ty = get_delegation_generics(tcx, delegation_id).propagate_self_ty;
SelfPositionKind::AfterLifetimes(propagate_self_ty)
}
_ => SelfPositionKind::None,
}
@@ -141,8 +157,7 @@ fn create_mapping<'tcx>(
) -> FxHashMap<u32, u32> {
let mut mapping: FxHashMap<u32, u32> = Default::default();
let (caller_kind, callee_kind) = (fn_kind(tcx, def_id), fn_kind(tcx, sig_id));
let self_pos_kind = create_self_position_kind(caller_kind, callee_kind);
let self_pos_kind = create_self_position_kind(tcx, def_id, sig_id);
let is_self_at_zero = matches!(self_pos_kind, SelfPositionKind::Zero);
// Is self at zero? If so insert mapping, self in sig parent is always at 0.
@@ -156,7 +171,7 @@ fn create_mapping<'tcx>(
args_index += get_delegation_parent_args_count_without_self(tcx, def_id, sig_id);
let sig_generics = tcx.generics_of(sig_id);
let process_sig_parent_generics = matches!(callee_kind, FnKind::AssocTrait);
let process_sig_parent_generics = matches!(fn_kind(tcx, sig_id), FnKind::AssocTrait);
if process_sig_parent_generics {
for i in (sig_generics.has_self as usize)..sig_generics.parent_count {
@@ -176,7 +191,9 @@ fn create_mapping<'tcx>(
}
// If self after lifetimes insert mapping, relying that self is at 0 in sig parent.
if matches!(self_pos_kind, SelfPositionKind::AfterLifetimes) {
// If self ty is propagated (meaning there is no generic param `Self`), the specified
// self ty will be inserted in args in `create_generic_args`.
if matches!(self_pos_kind, SelfPositionKind::AfterLifetimes { .. }) {
mapping.insert(0, args_index as u32);
args_index += 1;
}
@@ -259,6 +276,22 @@ fn get_parent_and_inheritance_kind<'tcx>(
}
}
fn get_delegation_self_ty_or_err(tcx: TyCtxt<'_>, delegation_id: LocalDefId) -> Ty<'_> {
get_delegation_generics(tcx, delegation_id)
.self_ty_id
.map(|id| {
let ctx = ItemCtxt::new(tcx, delegation_id);
ctx.lower_ty(tcx.hir_node(id).expect_ty())
})
.unwrap_or_else(|| {
Ty::new_error_with_message(
tcx,
tcx.def_span(delegation_id),
"the self type must be specified",
)
})
}
fn get_delegation_self_ty<'tcx>(tcx: TyCtxt<'tcx>, delegation_id: LocalDefId) -> Option<Ty<'tcx>> {
let sig_id = tcx.hir_opt_delegation_sig_id(delegation_id).expect("Delegation must have sig_id");
let (caller_kind, callee_kind) = (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id));
@@ -269,15 +302,19 @@ fn get_delegation_self_ty<'tcx>(tcx: TyCtxt<'tcx>, delegation_id: LocalDefId) ->
| (FnKind::Free, FnKind::Free)
| (FnKind::AssocTrait, FnKind::Free)
| (FnKind::AssocTrait, FnKind::AssocTrait) => {
match create_self_position_kind(caller_kind, callee_kind) {
match create_self_position_kind(tcx, delegation_id, sig_id) {
SelfPositionKind::None => None,
SelfPositionKind::AfterLifetimes => {
// Both sig parent and child lifetimes are in included in this count.
Some(tcx.generics_of(delegation_id).own_counts().lifetimes)
SelfPositionKind::AfterLifetimes(propagate_self_ty) => {
if propagate_self_ty {
Some(get_delegation_self_ty_or_err(tcx, delegation_id))
} else {
// Both sig parent and child lifetimes are in included in this count.
let index = tcx.generics_of(delegation_id).own_counts().lifetimes;
Some(Ty::new_param(tcx, index as u32, kw::SelfUpper))
}
}
SelfPositionKind::Zero => Some(0),
SelfPositionKind::Zero => Some(Ty::new_param(tcx, 0, kw::SelfUpper)),
}
.map(|self_index| Ty::new_param(tcx, self_index as u32, kw::SelfUpper))
}
(FnKind::AssocTraitImpl, FnKind::AssocTrait)
@@ -366,7 +403,7 @@ fn create_generic_args<'tcx>(
let mut new_args = vec![];
let self_pos_kind = create_self_position_kind(caller_kind, callee_kind);
let self_pos_kind = create_self_position_kind(tcx, delegation_id, sig_id);
let mut lifetimes_end_pos;
if !parent_args.is_empty() {
@@ -374,7 +411,7 @@ fn create_generic_args<'tcx>(
parent_args.iter().filter(|a| a.as_region().is_some()).count();
match self_pos_kind {
SelfPositionKind::AfterLifetimes => {
SelfPositionKind::AfterLifetimes { .. } => {
new_args.extend(&parent_args[1..1 + parent_args_lifetimes_count]);
lifetimes_end_pos = parent_args_lifetimes_count;
@@ -407,6 +444,15 @@ fn create_generic_args<'tcx>(
.count();
new_args.extend_from_slice(args);
// Parent args are empty, then if we should propagate self ty (meaning Self generic
// param was not generated) then we should insert it, as it won't be in `args`.
if matches!(self_pos_kind, SelfPositionKind::AfterLifetimes(true)) {
new_args.insert(
lifetimes_end_pos,
ty::GenericArg::from(get_delegation_self_ty_or_err(tcx, delegation_id)),
);
}
}
if !child_args.is_empty() {
@@ -427,7 +473,8 @@ fn create_generic_args<'tcx>(
new_args.insert(lifetimes_end_pos + i, child_args[i]);
}
let skip_self = matches!(self_pos_kind, SelfPositionKind::AfterLifetimes);
// If self_ty is propagated it means that Self generic param was not generated.
let skip_self = matches!(self_pos_kind, SelfPositionKind::AfterLifetimes(false));
new_args.extend(&child_args[child_lifetimes_count + skip_self as usize..]);
}
@@ -446,6 +493,7 @@ struct PredicatesCollector<'tcx> {
preds: Vec<(ty::Clause<'tcx>, Span)>,
args: Vec<ty::GenericArg<'tcx>>,
folder: ParamIndexRemapper<'tcx>,
filter_self_preds: bool,
}
impl<'tcx> PredicatesCollector<'tcx> {
@@ -458,6 +506,16 @@ fn with_own_preds(
let args = self.args.as_slice();
for pred in preds.predicates {
// If self ty is specified then there will be no generic param `Self`,
// so we do not need its predicates.
if self.filter_self_preds
&& let Some(trait_pred) = pred.0.as_trait_clause()
// Rely that `Self` has zero index.
&& trait_pred.self_ty().skip_binder().is_param(0)
{
continue;
}
let new_pred = pred.0.fold_with(&mut self.folder);
self.preds.push((
EarlyBinder::bind(new_pred).instantiate(self.tcx, args).skip_norm_wip(),
@@ -484,8 +542,10 @@ fn with_preds(
let (parent_args, child_args) = get_delegation_user_specified_args(tcx, def_id);
let (folder, args) = create_folder_and_args(tcx, def_id, sig_id, parent_args, child_args);
let collector = PredicatesCollector { tcx, preds: vec![], args, folder };
let self_pos_kind = create_self_position_kind(tcx, def_id, sig_id);
let filter_self_preds = matches!(self_pos_kind, SelfPositionKind::AfterLifetimes(true));
let collector = PredicatesCollector { tcx, preds: vec![], args, folder, filter_self_preds };
let (parent, inh_kind) = get_parent_and_inheritance_kind(tcx, def_id, sig_id);
// `explicit_predicates_of` is used here to avoid copying `Self: Trait` predicate.
@@ -573,13 +633,7 @@ fn get_delegation_user_specified_args<'tcx>(
tcx: TyCtxt<'tcx>,
delegation_id: LocalDefId,
) -> (&'tcx [ty::GenericArg<'tcx>], &'tcx [ty::GenericArg<'tcx>]) {
let info = tcx
.hir_node(tcx.local_def_id_to_hir_id(delegation_id))
.fn_sig()
.expect("Lowering delegation")
.decl
.opt_delegation_generics()
.expect("Lowering delegation");
let info = get_delegation_generics(tcx, delegation_id);
let get_segment = |hir_id: HirId| -> Option<(&'tcx PathSegment<'tcx>, DefId)> {
let segment = tcx.hir_node(hir_id).expect_path_segment();
@@ -1115,12 +1115,12 @@ enum Descr {
)
})
.collect();
// FIXME(fmease): Does not account for `dyn Trait<>` (suggs `dyn Trait<, X = Y>`).
let code = if let Some(snippet) = snippet.strip_suffix('>') {
// The user wrote `Trait<'a>` or similar and we don't have a term we can suggest,
// but at least we can clue them to the correct syntax `Trait<'a, Item = /* ... */>`
// while accounting for the `<'a>` in the suggestion.
format!("{}, {}>", snippet, bindings.join(", "))
let code = if let Some(snippet) = snippet.strip_suffix("<>") {
// Empty generics
format!("{snippet}<{}>", bindings.join(", "))
} else if let Some(snippet) = snippet.strip_suffix('>') {
// Non-empty generics
format!("{snippet}, {}>", bindings.join(", "))
} else if in_expr_or_pat {
// The user wrote `Trait`, so we don't have a term we can suggest, but at least we
// can clue them to the correct syntax `Trait::<Item = /* ... */>`.
+2 -2
View File
@@ -758,20 +758,20 @@ fn print_item(&mut self, item: &hir::Item<'_>) {
self.bclose(item.span, cb);
}
hir::ItemKind::Trait(
impl_restriction,
constness,
is_auto,
safety,
impl_restriction,
ident,
generics,
bounds,
trait_items,
) => {
let (cb, ib) = self.head("");
self.print_impl_restriction(impl_restriction);
self.print_constness(constness);
self.print_is_auto(is_auto);
self.print_safety(safety);
self.print_impl_restriction(impl_restriction);
self.word_nbsp("trait");
self.print_ident(ident);
self.print_generic_params(generics.params);
@@ -1900,7 +1900,7 @@ fn handle_unsatisfied_predicates(
entry.1.insert((self_ty.span, String::new()));
}
Some(Node::Item(hir::Item {
kind: hir::ItemKind::Trait(_, rustc_ast::ast::IsAuto::Yes, ..),
kind: hir::ItemKind::Trait(_, _, rustc_ast::ast::IsAuto::Yes, ..),
span: item_span,
..
})) => {
+1 -28
View File
@@ -2,7 +2,7 @@
use rustc_data_structures::sync::DynSend;
use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level};
use rustc_hir::lints::{AttributeLintKind, FormatWarning};
use rustc_hir::lints::AttributeLintKind;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
@@ -43,33 +43,6 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
.into_diag(dcx, level)
}
&AttributeLintKind::MalformedDoc => lints::MalformedDoc.into_diag(dcx, level),
&AttributeLintKind::ExpectedNoArgs => lints::ExpectedNoArgs.into_diag(dcx, level),
&AttributeLintKind::ExpectedNameValue => lints::ExpectedNameValue.into_diag(dcx, level),
&AttributeLintKind::MalFormedDiagnosticAttribute { attribute, options, span } => {
lints::MalFormedDiagnosticAttributeLint { attribute, options, span }
.into_diag(dcx, level)
}
AttributeLintKind::MalformedDiagnosticFormat { warning } => match warning {
FormatWarning::PositionalArgument { .. } => {
lints::DisallowedPositionalArgument.into_diag(dcx, level)
}
FormatWarning::InvalidSpecifier { .. } => {
lints::InvalidFormatSpecifier.into_diag(dcx, level)
}
FormatWarning::DisallowedPlaceholder { .. } => {
lints::DisallowedPlaceholder.into_diag(dcx, level)
}
},
AttributeLintKind::DiagnosticWrappedParserError { description, label, span } => {
lints::WrappedParserError { description, label, span: *span }.into_diag(dcx, level)
}
&AttributeLintKind::IgnoredDiagnosticOption { option_name, first_span, later_span } => {
lints::IgnoredDiagnosticOption { option_name, first_span, later_span }
.into_diag(dcx, level)
}
&AttributeLintKind::MissingOptionsForDiagnosticAttribute { attribute, options } => {
lints::MissingOptionsForDiagnosticAttribute { attribute, options }
.into_diag(dcx, level)
+1
View File
@@ -24,6 +24,7 @@
#![feature(box_patterns)]
#![feature(iter_order_by)]
#![feature(rustc_attrs)]
#![feature(titlecase)]
#![feature(try_blocks)]
// tidy-alphabetical-end
-67
View File
@@ -3282,63 +3282,6 @@ fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
}
}
#[derive(Diagnostic)]
#[diag("malformed `doc` attribute input")]
#[warning(
"this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!"
)]
pub(crate) struct MalformedDoc;
#[derive(Diagnostic)]
#[diag("didn't expect any arguments here")]
#[warning(
"this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!"
)]
pub(crate) struct ExpectedNoArgs;
#[derive(Diagnostic)]
#[diag("expected this to be of the form `... = \"...\"`")]
#[warning(
"this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!"
)]
pub(crate) struct ExpectedNameValue;
#[derive(Diagnostic)]
#[diag("positional format arguments are not allowed here")]
#[help(
"only named format arguments with the name of one of the generic types are allowed in this context"
)]
pub(crate) struct DisallowedPositionalArgument;
#[derive(Diagnostic)]
#[diag("format arguments are not allowed here")]
#[help("consider removing this format argument")]
pub(crate) struct DisallowedPlaceholder;
#[derive(Diagnostic)]
#[diag("invalid format specifier")]
#[help("no format specifier are supported in this position")]
pub(crate) struct InvalidFormatSpecifier;
#[derive(Diagnostic)]
#[diag("{$description}")]
pub(crate) struct WrappedParserError<'a> {
pub description: &'a str,
#[label("{$label}")]
pub span: Span,
pub label: &'a str,
}
#[derive(Diagnostic)]
#[diag("`{$option_name}` is ignored due to previous definition of `{$option_name}`")]
pub(crate) struct IgnoredDiagnosticOption {
pub option_name: Symbol,
#[label("`{$option_name}` is first declared here")]
pub first_span: Span,
#[label("`{$option_name}` is later redundantly declared here")]
pub later_span: Span,
}
#[derive(Diagnostic)]
#[diag("missing options for `{$attribute}` attribute")]
#[help("{$options}")]
@@ -3358,13 +3301,3 @@ pub(crate) struct MissingOptionsForDiagnosticAttribute {
"only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma"
)]
pub(crate) struct NonMetaItemDiagnosticAttribute;
#[derive(Diagnostic)]
#[diag("malformed `{$attribute}` attribute")]
#[help("{$options}")]
pub(crate) struct MalFormedDiagnosticAttributeLint {
pub attribute: &'static str,
pub options: &'static str,
#[label("invalid option found here")]
pub span: Span,
}
+54 -26
View File
@@ -47,34 +47,46 @@
declare_lint_pass!(NonCamelCaseTypes => [NON_CAMEL_CASE_TYPES]);
/// Some unicode characters *have* case, are considered upper case or lower case, but they *can't*
/// be upper cased or lower cased. For the purposes of the lint suggestion, we care about being able
/// Some unicode characters *have* case, are considered upper, title, or lower case, but they *can't*
/// be title cased or lower cased. For the purposes of the lint suggestion, we care about being able
/// to change the char's case.
fn char_has_case(c: char) -> bool {
!c.to_lowercase().eq(c.to_uppercase())
!c.to_lowercase().eq(c.to_titlecase())
}
// contains a capitalisable character followed by, or preceded by, an underscore
fn has_underscore_case(s: &str) -> bool {
/// FIXME: we should add a more efficient version
/// in the stdlib for this
fn changes_when_titlecased(c: char) -> bool {
!c.to_titlecase().eq([c])
}
// contains a capitalisable character followed by, or preceded by, an underscore,
// or contains an uppercase character that changes when titlecased,
// or contains `__`
fn not_camel_case(s: &str) -> bool {
let mut last = '\0';
s.chars().any(|c| match (std::mem::replace(&mut last, c), c) {
('_', cs) | (cs, '_') => char_has_case(cs),
_ => false,
s.chars().any(|snd| {
let fst = std::mem::replace(&mut last, snd);
match (fst, snd) {
('_', '_') => return true,
('_', _) if char_has_case(snd) => return true,
(_, '_') if char_has_case(fst) => return true,
_ => snd.is_uppercase() && changes_when_titlecased(snd),
}
})
}
fn is_camel_case(name: &str) -> bool {
fn is_upper_camel_case(name: &str) -> bool {
let name = name.trim_matches('_');
let Some(first) = name.chars().next() else {
return true;
};
// start with a non-lowercase letter rather than uppercase
// ones (some scripts don't have a concept of upper/lowercase)
!(first.is_lowercase() || name.contains("__") || has_underscore_case(name))
// some scripts don't have a concept of upper/lowercase
!(changes_when_titlecased(first) || not_camel_case(name))
}
fn to_camel_case(s: &str) -> String {
fn to_upper_camel_case(s: &str) -> String {
s.trim_matches('_')
.split('_')
.filter(|component| !component.is_empty())
@@ -83,24 +95,31 @@ fn to_camel_case(s: &str) -> String {
let mut new_word = true;
let mut prev_is_lower_case = true;
let mut prev_is_lowercased_sigma = false;
for c in component.chars() {
// Preserve the case if an uppercase letter follows a lowercase letter, so that
// `camelCase` is converted to `CamelCase`.
if prev_is_lower_case && c.is_uppercase() {
if prev_is_lower_case && (c.is_uppercase() | c.is_titlecase()) {
new_word = true;
}
if new_word {
camel_cased_component.extend(c.to_uppercase());
camel_cased_component.extend(c.to_titlecase());
} else {
camel_cased_component.extend(c.to_lowercase());
}
prev_is_lower_case = c.is_lowercase();
prev_is_lower_case = c.is_lowercase() || c.is_titlecase();
prev_is_lowercased_sigma = !new_word && c == 'Σ';
new_word = false;
}
if prev_is_lowercased_sigma {
camel_cased_component.pop();
camel_cased_component.push('ς');
}
camel_cased_component
})
.fold((String::new(), None), |(acc, prev): (String, Option<String>), next| {
@@ -122,8 +141,8 @@ impl NonCamelCaseTypes {
fn check_case(&self, cx: &EarlyContext<'_>, sort: &str, ident: &Ident) {
let name = ident.name.as_str();
if !is_camel_case(name) {
let cc = to_camel_case(name);
if !is_upper_camel_case(name) {
let cc = to_upper_camel_case(name);
let sub = if *name != cc {
NonCamelCaseTypeSub::Suggestion { span: ident.span, replace: cc }
} else {
@@ -235,14 +254,20 @@ fn to_snake_case(mut name: &str) -> String {
continue;
}
for ch in s.chars() {
if !buf.is_empty() && buf != "'" && ch.is_uppercase() && !last_upper {
words.push(buf);
if !buf.is_empty()
&& buf != "'"
&& (ch.is_uppercase() || ch.is_titlecase())
&& !last_upper
{
// We lowercase only at the end, to handle final sigma correctly
words.push(buf.to_lowercase());
buf = String::new();
}
last_upper = ch.is_uppercase();
buf.extend(ch.to_lowercase());
last_upper = ch.is_uppercase() || ch.is_titlecase();
buf.push(ch);
}
words.push(buf);
// We lowercase only at the end, to handle final sigma correctly
words.push(buf.to_lowercase());
}
words.join("_")
}
@@ -262,7 +287,8 @@ fn is_snake_case(ident: &str) -> bool {
// This correctly handles letters in languages with and without
// cases, as well as numbers and underscores.
!ident.chars().any(char::is_uppercase)
// FIXME: we should add a standard library impl of `c.to_lowercase().eq([c])`
ident.chars().all(|c| c.to_lowercase().eq([c]))
}
let name = ident.name.as_str();
@@ -474,10 +500,12 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
impl NonUpperCaseGlobals {
fn check_upper_case(cx: &LateContext<'_>, sort: &str, did: Option<LocalDefId>, ident: &Ident) {
let name = ident.name.as_str();
if name.chars().any(|c| c.is_lowercase()) {
// FIXME: we should add a more efficient version
// in the stdlib for `c.to_uppercase().eq([c])`
if !name.chars().all(|c| c.to_uppercase().eq([c])) {
let uc = NonSnakeCase::to_snake_case(name).to_uppercase();
// If the item is exported, suggesting changing it's name would be breaking-change
// If the item is exported, suggesting changing its name would be a breaking change
// and could break users without a "nice" applicable fix, so let's avoid it.
let can_change_usages = if let Some(did) = did {
!cx.tcx.effective_visibilities(()).is_exported(did)
@@ -1,21 +1,37 @@
use super::{is_camel_case, to_camel_case};
use super::{is_upper_camel_case, to_upper_camel_case};
#[test]
fn camel_case() {
assert!(!is_camel_case("userData"));
assert_eq!(to_camel_case("userData"), "UserData");
assert!(!is_upper_camel_case("userData"));
assert_eq!(to_upper_camel_case("userData"), "UserData");
assert!(is_camel_case("X86_64"));
assert!(is_upper_camel_case("X86_64"));
assert!(!is_camel_case("X86__64"));
assert_eq!(to_camel_case("X86__64"), "X86_64");
assert!(!is_upper_camel_case("X86__64"));
assert_eq!(to_upper_camel_case("X86__64"), "X86_64");
assert!(!is_camel_case("Abc_123"));
assert_eq!(to_camel_case("Abc_123"), "Abc123");
assert!(!is_upper_camel_case("Abc_123"));
assert_eq!(to_upper_camel_case("Abc_123"), "Abc123");
assert!(!is_camel_case("A1_b2_c3"));
assert_eq!(to_camel_case("A1_b2_c3"), "A1B2C3");
assert!(!is_upper_camel_case("A1_b2_c3"));
assert_eq!(to_upper_camel_case("A1_b2_c3"), "A1B2C3");
assert!(!is_camel_case("ONE_TWO_THREE"));
assert_eq!(to_camel_case("ONE_TWO_THREE"), "OneTwoThree");
assert!(!is_upper_camel_case("ONE_TWO_THREE"));
assert_eq!(to_upper_camel_case("ONE_TWO_THREE"), "OneTwoThree");
// FIXME(@Jules-Bertholet): This test doesn't work due to what I believe
// is a Unicode spec bug - uppercase Georgian letters have
// incorrect titlecase mappings.
// I've reported it to Unicode.
// Georgian mtavruli is only used in all-caps
//assert!(!is_upper_camel_case("ᲫალაᲔრთობაშია"));
//assert_eq!(to_upper_camel_case("ᲫალაᲔრთობაშია"), "ძალა_ერთობაშია");
assert!(!is_upper_camel_case("LJNJaaaDŽooo"));
assert_eq!(to_upper_camel_case("LJNJaaLjNJaDŽooo"), "LjnjaaLjNjaDžooo");
// Final sigma
assert!(!is_upper_camel_case("ΦΙΛΟΣ_ΦΙΛΟΣ"));
assert_eq!(to_upper_camel_case("ΦΙΛΟΣ_ΦΙΛΟΣ"), "ΦιλοςΦιλος");
assert!(is_upper_camel_case("ΦιλοσΦιλοσ"));
}
-7
View File
@@ -656,13 +656,6 @@ pub enum DeprecatedSinceKind {
pub enum AttributeLintKind {
UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>),
UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>),
MalformedDoc,
ExpectedNoArgs,
ExpectedNameValue,
MalFormedDiagnosticAttribute { attribute: &'static str, options: &'static str, span: Span },
MalformedDiagnosticFormat { warning: FormatWarning },
DiagnosticWrappedParserError { description: String, label: String, span: Span },
IgnoredDiagnosticOption { option_name: Symbol, first_span: Span, later_span: Span },
MissingOptionsForDiagnosticAttribute { attribute: &'static str, options: &'static str },
NonMetaItemDiagnosticAttribute,
}
+11 -1
View File
@@ -319,7 +319,17 @@ pub fn hir_body_const_context(self, local_def_id: LocalDefId) -> Option<ConstCon
BodyOwnerKind::Fn if self.is_constructor(def_id) => return None,
// Const closures use their parent's const context
BodyOwnerKind::Closure if self.is_const_fn(def_id) => {
return self.hir_body_const_context(self.local_parent(local_def_id));
return Some(
self.hir_body_const_context(self.local_parent(local_def_id)).unwrap_or_else(
|| {
assert!(
self.dcx().has_errors().is_some(),
"`const` closure with no enclosing const context",
);
ConstContext::ConstFn
},
),
);
}
BodyOwnerKind::Fn if self.is_const_fn(def_id) => ConstContext::ConstFn,
BodyOwnerKind::Fn | BodyOwnerKind::Closure | BodyOwnerKind::GlobalAsm => return None,
+3 -3
View File
@@ -20,14 +20,14 @@
pub struct TraitDef {
pub def_id: DefId,
/// Restrictions on trait implementations.
pub impl_restriction: ImplRestrictionKind,
pub safety: hir::Safety,
/// Whether this trait is `const`.
pub constness: hir::Constness,
/// Restrictions on trait implementations.
pub impl_restriction: ImplRestrictionKind,
/// If `true`, then this trait had the `#[rustc_paren_sugar]`
/// attribute, indicating that it should be used with `Foo()`
/// sugar. This is a temporary thing -- eventually any trait will
+60 -73
View File
@@ -1052,80 +1052,69 @@ fn parse_defaultness(&mut self) -> Defaultness {
}
}
/// 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)
}
/// Is this an `(const unsafe? auto? [ impl(in? path) ]? | unsafe auto? [ impl(in? path) ]? | auto [ impl(in? path) ]? | [ impl(in? path) ]?) trait` item?
/// Is this an `[impl(in? path)]? const? unsafe? auto? 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;
const SUFFIXES: &[&[Symbol]] = &[
&[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) {
// `impl(in` unambiguously introduces an `impl` restriction
if self.is_keyword_ahead(2, &[kw::In]) {
return true;
}
// `impl(crate | self | super)` + SUFFIX
if self.is_keyword_ahead(2, &[kw::Crate, kw::SelfLower, kw::Super])
&& self.look_ahead(3, |t| t == &token::CloseParen)
&& SUFFIXES.iter().any(|suffix| {
suffix.iter().enumerate().all(|(i, kw)| self.is_keyword_ahead(i + 4, &[*kw]))
})
{
return true;
}
// Recover cases like `impl(path::to::module)` + SUFFIX to suggest inserting `in`.
SUFFIXES.iter().any(|suffix| {
suffix.iter().enumerate().all(|(i, kw)| {
self.tree_look_ahead(i + 2, |t| {
if let TokenTree::Token(token, _) = t {
token.is_keyword(*kw)
} else {
false
}
})
.unwrap_or(false)
})
})
} else {
SUFFIXES.iter().any(|suffix| {
suffix.iter().enumerate().all(|(i, kw)| {
// We use `check_keyword` for the first token to include it in the expected tokens.
if i == 0 {
match *kw {
kw::Const => self.check_keyword(exp!(Const)),
kw::Unsafe => self.check_keyword(exp!(Unsafe)),
kw::Auto => self.check_keyword(exp!(Auto)),
kw::Trait => self.check_keyword(exp!(Trait)),
_ => unreachable!(),
}
} else {
self.is_keyword_ahead(i, &[*kw])
}
})
})
}
// `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`.
/// Parses `[impl(in? path)]? const? unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemKind> {
let impl_restriction = self.parse_impl_restriction()?;
let constness = self.parse_constness(Case::Sensitive);
if let Const::Yes(span) = constness {
self.psess.gated_spans.gate(sym::const_trait_impl, span);
@@ -1139,8 +1128,6 @@ 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()?;
@@ -1181,10 +1168,10 @@ fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, Ite
generics.where_clause = self.parse_where_clause()?;
let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?;
Ok(ItemKind::Trait(Box::new(Trait {
impl_restriction,
constness,
is_auto,
safety,
impl_restriction,
ident,
generics,
bounds,
@@ -2966,7 +2953,7 @@ 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` and `const unsafe impl`.
// Rule out `const unsafe auto` and `const unsafe trait` and `const unsafe impl`
&& !self.is_keyword_ahead(2, &[kw::Auto, kw::Trait, kw::Impl])
)
})
@@ -28,7 +28,7 @@
AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError,
SourceKindMultiSuggestion, SourceKindSubdiag,
};
use crate::infer::InferCtxt;
use crate::infer::{InferCtxt, TyOrConstInferVar};
pub enum TypeAnnotationNeeded {
/// ```compile_fail,E0282
@@ -82,13 +82,27 @@ fn can_add_more_info(&self) -> bool {
!(self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }))
}
fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str {
fn where_x_is_kind<'tcx>(&self, infcx: &InferCtxt<'tcx>, in_type: Ty<'tcx>) -> &'static str {
if in_type.is_ty_or_numeric_infer() {
""
} else if self.name == "_" {
// FIXME: Consider specializing this message if there is a single `_`
// in the type.
"underscore"
let displayed_ty = infcx
.resolve_vars_if_possible(in_type)
.fold_with(&mut ClosureEraser { infcx, depth: 0 });
if displayed_ty.is_ty_or_numeric_infer() {
""
} else {
match displayed_ty
.walk()
.filter_map(TyOrConstInferVar::maybe_from_generic_arg)
.take(2)
.count()
{
0 => "",
1 => "underscore_single",
_ => "underscore_multiple",
}
}
} else {
"has_name"
}
@@ -555,7 +569,7 @@ pub fn emit_inference_failure_err_with_type_hint(
infer_subdiags.push(SourceKindSubdiag::LetLike {
span: insert_span,
name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new),
x_kind: arg_data.where_x_is_kind(ty),
x_kind: arg_data.where_x_is_kind(self.infcx, ty),
prefix_kind: arg_data.kind.clone(),
prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
arg_name: arg_data.name,
@@ -567,7 +581,7 @@ pub fn emit_inference_failure_err_with_type_hint(
infer_subdiags.push(SourceKindSubdiag::LetLike {
span: insert_span,
name: String::new(),
x_kind: arg_data.where_x_is_kind(ty),
x_kind: arg_data.where_x_is_kind(self.infcx, ty),
prefix_kind: arg_data.kind.clone(),
prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
arg_name: arg_data.name,
@@ -4142,7 +4142,7 @@ pub(super) fn note_obligation_cause_code<G: EmissionGuarantee, T>(
let mut is_auto_trait = false;
match tcx.hir_get_if_local(data.impl_or_alias_def_id) {
Some(Node::Item(hir::Item {
kind: hir::ItemKind::Trait(_, is_auto, _, _, ident, _, _, _),
kind: hir::ItemKind::Trait(_, _, is_auto, _, ident, _, _, _),
..
})) => {
// FIXME: we should do something else so that it works even on crate foreign
+2 -1
View File
@@ -277,7 +277,8 @@ pub(crate) enum SourceKindSubdiag<'a> {
[const_with_param] value of const parameter
[const] value of the constant
} `{$arg_name}` is specified
[underscore] , where the placeholders `_` are specified
[underscore_single] , where the placeholder `_` is specified
[underscore_multiple] , where the placeholders `_` are specified
*[empty] {\"\"}
}",
style = "verbose",