mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-10 08:57:36 +00:00
Rollup merge of #139091 - mejrs:format, r=compiler-errors
Rewrite on_unimplemented format string parser. This PR rewrites the format string parser for `rustc_on_unimplemented` and `diagnostic::on_unimplemented`. I plan on moving this code (and more) into the new attribute parsing system soon and wanted to PR it separately. This PR introduces some minor differences though: - `rustc_on_unimplemented` on trait *implementations* is no longer checked/used - this is actually never used (outside of some tests) so I plan on removing it in the future. - for `rustc_on_unimplemented`, it introduces the `{This}` argument in favor of `{ThisTraitname}` (to be removed later). It'll be easier to parse. - for `rustc_on_unimplemented`, `Self` can now consistently be used as a filter, rather than just `_Self`. It used to not match correctly on for example `Self = "[{integer}]"` - Some error messages now have better spans. Fixes https://github.com/rust-lang/rust/issues/130627
This commit is contained in:
commit
aad59a30de
@ -1232,6 +1232,25 @@ impl DesugaringKind {
|
|||||||
DesugaringKind::PatTyRange => "pattern type",
|
DesugaringKind::PatTyRange => "pattern type",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// For use with `rustc_unimplemented` to support conditions
|
||||||
|
/// like `from_desugaring = "QuestionMark"`
|
||||||
|
pub fn matches(&self, value: &str) -> bool {
|
||||||
|
match self {
|
||||||
|
DesugaringKind::CondTemporary => value == "CondTemporary",
|
||||||
|
DesugaringKind::Async => value == "Async",
|
||||||
|
DesugaringKind::Await => value == "Await",
|
||||||
|
DesugaringKind::QuestionMark => value == "QuestionMark",
|
||||||
|
DesugaringKind::TryBlock => value == "TryBlock",
|
||||||
|
DesugaringKind::YeetExpr => value == "YeetExpr",
|
||||||
|
DesugaringKind::OpaqueTy => value == "OpaqueTy",
|
||||||
|
DesugaringKind::ForLoop => value == "ForLoop",
|
||||||
|
DesugaringKind::WhileLoop => value == "WhileLoop",
|
||||||
|
DesugaringKind::BoundModifier => value == "BoundModifier",
|
||||||
|
DesugaringKind::Contract => value == "Contract",
|
||||||
|
DesugaringKind::PatTyRange => value == "PatTyRange",
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -372,6 +372,7 @@ symbols! {
|
|||||||
SyncUnsafeCell,
|
SyncUnsafeCell,
|
||||||
T,
|
T,
|
||||||
Target,
|
Target,
|
||||||
|
This,
|
||||||
ToOwned,
|
ToOwned,
|
||||||
ToString,
|
ToString,
|
||||||
TokenStream,
|
TokenStream,
|
||||||
|
@ -2,6 +2,8 @@ pub mod ambiguity;
|
|||||||
pub mod call_kind;
|
pub mod call_kind;
|
||||||
mod fulfillment_errors;
|
mod fulfillment_errors;
|
||||||
pub mod on_unimplemented;
|
pub mod on_unimplemented;
|
||||||
|
pub mod on_unimplemented_condition;
|
||||||
|
pub mod on_unimplemented_format;
|
||||||
mod overflow;
|
mod overflow;
|
||||||
pub mod suggestions;
|
pub mod suggestions;
|
||||||
|
|
||||||
|
@ -1,44 +1,31 @@
|
|||||||
use std::iter;
|
use std::iter;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use rustc_ast::MetaItemInner;
|
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit};
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
|
||||||
use rustc_errors::codes::*;
|
use rustc_errors::codes::*;
|
||||||
use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
|
use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_hir::{AttrArgs, Attribute};
|
use rustc_hir::{AttrArgs, Attribute};
|
||||||
use rustc_macros::LintDiagnostic;
|
use rustc_macros::LintDiagnostic;
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
use rustc_middle::ty::print::PrintTraitRefExt;
|
||||||
use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt};
|
use rustc_middle::ty::{self, GenericArgsRef, GenericParamDef, GenericParamDefKind, TyCtxt};
|
||||||
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
|
|
||||||
use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
|
use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
|
||||||
use rustc_span::{Ident, Span, Symbol, kw, sym};
|
use rustc_span::{Span, Symbol, sym};
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
use {rustc_attr_parsing as attr, rustc_hir as hir};
|
use {rustc_attr_parsing as attr, rustc_hir as hir};
|
||||||
|
|
||||||
use super::{ObligationCauseCode, PredicateObligation};
|
use super::{ObligationCauseCode, PredicateObligation};
|
||||||
use crate::error_reporting::TypeErrCtxt;
|
use crate::error_reporting::TypeErrCtxt;
|
||||||
|
use crate::error_reporting::traits::on_unimplemented_condition::{Condition, ConditionOptions};
|
||||||
|
use crate::error_reporting::traits::on_unimplemented_format::{
|
||||||
|
Ctx, FormatArgs, FormatString, FormatWarning,
|
||||||
|
};
|
||||||
use crate::errors::{
|
use crate::errors::{
|
||||||
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
|
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
|
||||||
};
|
};
|
||||||
use crate::infer::InferCtxtExt;
|
use crate::infer::InferCtxtExt;
|
||||||
|
|
||||||
/// The symbols which are always allowed in a format string
|
|
||||||
static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
|
|
||||||
kw::SelfUpper,
|
|
||||||
sym::ItemContext,
|
|
||||||
sym::from_desugaring,
|
|
||||||
sym::direct,
|
|
||||||
sym::cause,
|
|
||||||
sym::integral,
|
|
||||||
sym::integer_,
|
|
||||||
sym::float,
|
|
||||||
sym::_Self,
|
|
||||||
sym::crate_local,
|
|
||||||
sym::Trait,
|
|
||||||
];
|
|
||||||
|
|
||||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
fn impl_similar_to(
|
fn impl_similar_to(
|
||||||
&self,
|
&self,
|
||||||
@ -121,86 +108,78 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||||||
.unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args));
|
.unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args));
|
||||||
let trait_pred = trait_pred.skip_binder();
|
let trait_pred = trait_pred.skip_binder();
|
||||||
|
|
||||||
let mut flags = vec![];
|
let mut self_types = vec![];
|
||||||
|
let mut generic_args: Vec<(Symbol, String)> = vec![];
|
||||||
|
let mut crate_local = false;
|
||||||
// FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs,
|
// FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs,
|
||||||
// but I guess we could synthesize one here. We don't see any errors that rely on
|
// but I guess we could synthesize one here. We don't see any errors that rely on
|
||||||
// that yet, though.
|
// that yet, though.
|
||||||
let enclosure = self.describe_enclosure(obligation.cause.body_id).map(|t| t.to_owned());
|
let item_context = self.describe_enclosure(obligation.cause.body_id).unwrap_or("");
|
||||||
flags.push((sym::ItemContext, enclosure));
|
|
||||||
|
|
||||||
match obligation.cause.code() {
|
let direct = match obligation.cause.code() {
|
||||||
ObligationCauseCode::BuiltinDerived(..)
|
ObligationCauseCode::BuiltinDerived(..)
|
||||||
| ObligationCauseCode::ImplDerived(..)
|
| ObligationCauseCode::ImplDerived(..)
|
||||||
| ObligationCauseCode::WellFormedDerived(..) => {}
|
| ObligationCauseCode::WellFormedDerived(..) => false,
|
||||||
_ => {
|
_ => {
|
||||||
// this is a "direct", user-specified, rather than derived,
|
// this is a "direct", user-specified, rather than derived,
|
||||||
// obligation.
|
// obligation.
|
||||||
flags.push((sym::direct, None));
|
true
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(k) = obligation.cause.span.desugaring_kind() {
|
let from_desugaring = obligation.cause.span.desugaring_kind();
|
||||||
flags.push((sym::from_desugaring, None));
|
|
||||||
flags.push((sym::from_desugaring, Some(format!("{k:?}"))));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
|
let cause = if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
|
||||||
flags.push((sym::cause, Some("MainFunctionType".to_string())));
|
Some("MainFunctionType".to_string())
|
||||||
}
|
} else {
|
||||||
|
None
|
||||||
flags.push((sym::Trait, Some(trait_pred.trait_ref.print_trait_sugared().to_string())));
|
};
|
||||||
|
|
||||||
// Add all types without trimmed paths or visible paths, ensuring they end up with
|
// Add all types without trimmed paths or visible paths, ensuring they end up with
|
||||||
// their "canonical" def path.
|
// their "canonical" def path.
|
||||||
ty::print::with_no_trimmed_paths!(ty::print::with_no_visible_paths!({
|
ty::print::with_no_trimmed_paths!(ty::print::with_no_visible_paths!({
|
||||||
let generics = self.tcx.generics_of(def_id);
|
let generics = self.tcx.generics_of(def_id);
|
||||||
let self_ty = trait_pred.self_ty();
|
let self_ty = trait_pred.self_ty();
|
||||||
// This is also included through the generics list as `Self`,
|
self_types.push(self_ty.to_string());
|
||||||
// but the parser won't allow you to use it
|
|
||||||
flags.push((sym::_Self, Some(self_ty.to_string())));
|
|
||||||
if let Some(def) = self_ty.ty_adt_def() {
|
if let Some(def) = self_ty.ty_adt_def() {
|
||||||
// We also want to be able to select self's original
|
// We also want to be able to select self's original
|
||||||
// signature with no type arguments resolved
|
// signature with no type arguments resolved
|
||||||
flags.push((
|
self_types.push(self.tcx.type_of(def.did()).instantiate_identity().to_string());
|
||||||
sym::_Self,
|
|
||||||
Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for param in generics.own_params.iter() {
|
for GenericParamDef { name, kind, index, .. } in generics.own_params.iter() {
|
||||||
let value = match param.kind {
|
let value = match kind {
|
||||||
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
|
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
|
||||||
args[param.index as usize].to_string()
|
args[*index as usize].to_string()
|
||||||
}
|
}
|
||||||
GenericParamDefKind::Lifetime => continue,
|
GenericParamDefKind::Lifetime => continue,
|
||||||
};
|
};
|
||||||
let name = param.name;
|
generic_args.push((*name, value));
|
||||||
flags.push((name, Some(value)));
|
|
||||||
|
|
||||||
if let GenericParamDefKind::Type { .. } = param.kind {
|
if let GenericParamDefKind::Type { .. } = kind {
|
||||||
let param_ty = args[param.index as usize].expect_ty();
|
let param_ty = args[*index as usize].expect_ty();
|
||||||
if let Some(def) = param_ty.ty_adt_def() {
|
if let Some(def) = param_ty.ty_adt_def() {
|
||||||
// We also want to be able to select the parameter's
|
// We also want to be able to select the parameter's
|
||||||
// original signature with no type arguments resolved
|
// original signature with no type arguments resolved
|
||||||
flags.push((
|
generic_args.push((
|
||||||
name,
|
*name,
|
||||||
Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
|
self.tcx.type_of(def.did()).instantiate_identity().to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) {
|
if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) {
|
||||||
flags.push((sym::crate_local, None));
|
crate_local = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow targeting all integers using `{integral}`, even if the exact type was resolved
|
// Allow targeting all integers using `{integral}`, even if the exact type was resolved
|
||||||
if self_ty.is_integral() {
|
if self_ty.is_integral() {
|
||||||
flags.push((sym::_Self, Some("{integral}".to_owned())));
|
self_types.push("{integral}".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
if self_ty.is_array_slice() {
|
if self_ty.is_array_slice() {
|
||||||
flags.push((sym::_Self, Some("&[]".to_owned())));
|
self_types.push("&[]".to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
if self_ty.is_fn() {
|
if self_ty.is_fn() {
|
||||||
@ -215,53 +194,51 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||||||
hir::Safety::Unsafe => "unsafe fn",
|
hir::Safety::Unsafe => "unsafe fn",
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
flags.push((sym::_Self, Some(shortname.to_owned())));
|
self_types.push(shortname.to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slices give us `[]`, `[{ty}]`
|
// Slices give us `[]`, `[{ty}]`
|
||||||
if let ty::Slice(aty) = self_ty.kind() {
|
if let ty::Slice(aty) = self_ty.kind() {
|
||||||
flags.push((sym::_Self, Some("[]".to_string())));
|
self_types.push("[]".to_owned());
|
||||||
if let Some(def) = aty.ty_adt_def() {
|
if let Some(def) = aty.ty_adt_def() {
|
||||||
// We also want to be able to select the slice's type's original
|
// We also want to be able to select the slice's type's original
|
||||||
// signature with no type arguments resolved
|
// signature with no type arguments resolved
|
||||||
flags.push((
|
self_types
|
||||||
sym::_Self,
|
.push(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity()));
|
||||||
Some(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity())),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if aty.is_integral() {
|
if aty.is_integral() {
|
||||||
flags.push((sym::_Self, Some("[{integral}]".to_string())));
|
self_types.push("[{integral}]".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
|
// Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
|
||||||
if let ty::Array(aty, len) = self_ty.kind() {
|
if let ty::Array(aty, len) = self_ty.kind() {
|
||||||
flags.push((sym::_Self, Some("[]".to_string())));
|
self_types.push("[]".to_string());
|
||||||
let len = len.try_to_target_usize(self.tcx);
|
let len = len.try_to_target_usize(self.tcx);
|
||||||
flags.push((sym::_Self, Some(format!("[{aty}; _]"))));
|
self_types.push(format!("[{aty}; _]"));
|
||||||
if let Some(n) = len {
|
if let Some(n) = len {
|
||||||
flags.push((sym::_Self, Some(format!("[{aty}; {n}]"))));
|
self_types.push(format!("[{aty}; {n}]"));
|
||||||
}
|
}
|
||||||
if let Some(def) = aty.ty_adt_def() {
|
if let Some(def) = aty.ty_adt_def() {
|
||||||
// We also want to be able to select the array's type's original
|
// We also want to be able to select the array's type's original
|
||||||
// signature with no type arguments resolved
|
// signature with no type arguments resolved
|
||||||
let def_ty = self.tcx.type_of(def.did()).instantiate_identity();
|
let def_ty = self.tcx.type_of(def.did()).instantiate_identity();
|
||||||
flags.push((sym::_Self, Some(format!("[{def_ty}; _]"))));
|
self_types.push(format!("[{def_ty}; _]"));
|
||||||
if let Some(n) = len {
|
if let Some(n) = len {
|
||||||
flags.push((sym::_Self, Some(format!("[{def_ty}; {n}]"))));
|
self_types.push(format!("[{def_ty}; {n}]"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if aty.is_integral() {
|
if aty.is_integral() {
|
||||||
flags.push((sym::_Self, Some("[{integral}; _]".to_string())));
|
self_types.push("[{integral}; _]".to_string());
|
||||||
if let Some(n) = len {
|
if let Some(n) = len {
|
||||||
flags.push((sym::_Self, Some(format!("[{{integral}}; {n}]"))));
|
self_types.push(format!("[{{integral}}; {n}]"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let ty::Dynamic(traits, _, _) = self_ty.kind() {
|
if let ty::Dynamic(traits, _, _) = self_ty.kind() {
|
||||||
for t in traits.iter() {
|
for t in traits.iter() {
|
||||||
if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
|
if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
|
||||||
flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
|
self_types.push(self.tcx.def_path_str(trait_ref.def_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,31 +248,76 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||||||
&& let ty::Slice(sty) = ref_ty.kind()
|
&& let ty::Slice(sty) = ref_ty.kind()
|
||||||
&& sty.is_integral()
|
&& sty.is_integral()
|
||||||
{
|
{
|
||||||
flags.push((sym::_Self, Some("&[{integral}]".to_owned())));
|
self_types.push("&[{integral}]".to_owned());
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id);
|
||||||
|
let trait_sugared = trait_pred.trait_ref.print_trait_sugared();
|
||||||
|
|
||||||
|
let condition_options = ConditionOptions {
|
||||||
|
self_types,
|
||||||
|
from_desugaring,
|
||||||
|
cause,
|
||||||
|
crate_local,
|
||||||
|
direct,
|
||||||
|
generic_args,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unlike the generic_args earlier,
|
||||||
|
// this one is *not* collected under `with_no_trimmed_paths!`
|
||||||
|
// for printing the type to the user
|
||||||
|
//
|
||||||
|
// This includes `Self`, as it is the first parameter in `own_params`.
|
||||||
|
let generic_args = self
|
||||||
|
.tcx
|
||||||
|
.generics_of(trait_pred.trait_ref.def_id)
|
||||||
|
.own_params
|
||||||
|
.iter()
|
||||||
|
.filter_map(|param| {
|
||||||
|
let value = match param.kind {
|
||||||
|
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
|
||||||
|
if let Some(ty) = trait_pred.trait_ref.args[param.index as usize].as_type()
|
||||||
|
{
|
||||||
|
self.tcx.short_string(ty, long_ty_file)
|
||||||
|
} else {
|
||||||
|
trait_pred.trait_ref.args[param.index as usize].to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GenericParamDefKind::Lifetime => return None,
|
||||||
|
};
|
||||||
|
let name = param.name;
|
||||||
|
Some((name, value))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let format_args = FormatArgs { this, trait_sugared, generic_args, item_context };
|
||||||
|
|
||||||
if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) {
|
if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) {
|
||||||
command.evaluate(self.tcx, trait_pred.trait_ref, &flags, long_ty_file)
|
command.evaluate(self.tcx, trait_pred.trait_ref, &condition_options, &format_args)
|
||||||
} else {
|
} else {
|
||||||
OnUnimplementedNote::default()
|
OnUnimplementedNote::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a format string in a on_unimplemented attribute,
|
||||||
|
/// like the "content" in `#[diagnostic::on_unimplemented(message = "content")]`
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct OnUnimplementedFormatString {
|
pub struct OnUnimplementedFormatString {
|
||||||
symbol: Symbol,
|
/// Symbol of the format string, i.e. `"content"`
|
||||||
span: Span,
|
pub symbol: Symbol,
|
||||||
is_diagnostic_namespace_variant: bool,
|
///The span of the format string, i.e. `"content"`
|
||||||
|
pub span: Span,
|
||||||
|
pub is_diagnostic_namespace_variant: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct OnUnimplementedDirective {
|
pub struct OnUnimplementedDirective {
|
||||||
pub condition: Option<MetaItemInner>,
|
pub condition: Option<Condition>,
|
||||||
pub subcommands: Vec<OnUnimplementedDirective>,
|
pub subcommands: Vec<OnUnimplementedDirective>,
|
||||||
pub message: Option<OnUnimplementedFormatString>,
|
pub message: Option<(Span, OnUnimplementedFormatString)>,
|
||||||
pub label: Option<OnUnimplementedFormatString>,
|
pub label: Option<(Span, OnUnimplementedFormatString)>,
|
||||||
pub notes: Vec<OnUnimplementedFormatString>,
|
pub notes: Vec<OnUnimplementedFormatString>,
|
||||||
pub parent_label: Option<OnUnimplementedFormatString>,
|
pub parent_label: Option<OnUnimplementedFormatString>,
|
||||||
pub append_const_msg: Option<AppendConstMessage>,
|
pub append_const_msg: Option<AppendConstMessage>,
|
||||||
@ -329,7 +351,7 @@ pub struct MalformedOnUnimplementedAttrLint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MalformedOnUnimplementedAttrLint {
|
impl MalformedOnUnimplementedAttrLint {
|
||||||
fn new(span: Span) -> Self {
|
pub fn new(span: Span) -> Self {
|
||||||
Self { span }
|
Self { span }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,7 +372,7 @@ pub struct IgnoredDiagnosticOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IgnoredDiagnosticOption {
|
impl IgnoredDiagnosticOption {
|
||||||
fn maybe_emit_warning<'tcx>(
|
pub fn maybe_emit_warning<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
item_def_id: DefId,
|
item_def_id: DefId,
|
||||||
new: Option<Span>,
|
new: Option<Span>,
|
||||||
@ -370,29 +392,11 @@ impl IgnoredDiagnosticOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(LintDiagnostic)]
|
|
||||||
#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
|
|
||||||
#[help]
|
|
||||||
pub struct UnknownFormatParameterForOnUnimplementedAttr {
|
|
||||||
argument_name: Symbol,
|
|
||||||
trait_name: Ident,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(LintDiagnostic)]
|
|
||||||
#[diag(trait_selection_disallowed_positional_argument)]
|
|
||||||
#[help]
|
|
||||||
pub struct DisallowedPositionalArgument;
|
|
||||||
|
|
||||||
#[derive(LintDiagnostic)]
|
|
||||||
#[diag(trait_selection_invalid_format_specifier)]
|
|
||||||
#[help]
|
|
||||||
pub struct InvalidFormatSpecifier;
|
|
||||||
|
|
||||||
#[derive(LintDiagnostic)]
|
#[derive(LintDiagnostic)]
|
||||||
#[diag(trait_selection_wrapped_parser_error)]
|
#[diag(trait_selection_wrapped_parser_error)]
|
||||||
pub struct WrappedParserError {
|
pub struct WrappedParserError {
|
||||||
description: String,
|
pub description: String,
|
||||||
label: String,
|
pub label: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> OnUnimplementedDirective {
|
impl<'tcx> OnUnimplementedDirective {
|
||||||
@ -407,12 +411,12 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
let mut errored = None;
|
let mut errored = None;
|
||||||
let mut item_iter = items.iter();
|
let mut item_iter = items.iter();
|
||||||
|
|
||||||
let parse_value = |value_str, value_span| {
|
let parse_value = |value_str, span| {
|
||||||
OnUnimplementedFormatString::try_parse(
|
OnUnimplementedFormatString::try_parse(
|
||||||
tcx,
|
tcx,
|
||||||
item_def_id,
|
item_def_id,
|
||||||
value_str,
|
value_str,
|
||||||
value_span,
|
span,
|
||||||
is_diagnostic_namespace_variant,
|
is_diagnostic_namespace_variant,
|
||||||
)
|
)
|
||||||
.map(Some)
|
.map(Some)
|
||||||
@ -434,7 +438,7 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
}
|
}
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
Some(cond.clone())
|
Some(Condition { inner: cond.clone() })
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut message = None;
|
let mut message = None;
|
||||||
@ -444,24 +448,36 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
let mut subcommands = vec![];
|
let mut subcommands = vec![];
|
||||||
let mut append_const_msg = None;
|
let mut append_const_msg = None;
|
||||||
|
|
||||||
|
let get_value_and_span = |item: &_, key| {
|
||||||
|
if let MetaItemInner::MetaItem(MetaItem {
|
||||||
|
path,
|
||||||
|
kind: MetaItemKind::NameValue(MetaItemLit { span, kind: LitKind::Str(s, _), .. }),
|
||||||
|
..
|
||||||
|
}) = item
|
||||||
|
&& *path == key
|
||||||
|
{
|
||||||
|
Some((*s, *span))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for item in item_iter {
|
for item in item_iter {
|
||||||
if item.has_name(sym::message) && message.is_none() {
|
if let Some((message_, span)) = get_value_and_span(item, sym::message)
|
||||||
if let Some(message_) = item.value_str() {
|
&& message.is_none()
|
||||||
message = parse_value(message_, item.span())?;
|
{
|
||||||
|
message = parse_value(message_, span)?.map(|l| (item.span(), l));
|
||||||
continue;
|
continue;
|
||||||
}
|
} else if let Some((label_, span)) = get_value_and_span(item, sym::label)
|
||||||
} else if item.has_name(sym::label) && label.is_none() {
|
&& label.is_none()
|
||||||
if let Some(label_) = item.value_str() {
|
{
|
||||||
label = parse_value(label_, item.span())?;
|
label = parse_value(label_, span)?.map(|l| (item.span(), l));
|
||||||
continue;
|
continue;
|
||||||
}
|
} else if let Some((note_, span)) = get_value_and_span(item, sym::note) {
|
||||||
} else if item.has_name(sym::note) {
|
if let Some(note) = parse_value(note_, span)? {
|
||||||
if let Some(note_) = item.value_str() {
|
|
||||||
if let Some(note) = parse_value(note_, item.span())? {
|
|
||||||
notes.push(note);
|
notes.push(note);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if item.has_name(sym::parent_label)
|
} else if item.has_name(sym::parent_label)
|
||||||
&& parent_label.is_none()
|
&& parent_label.is_none()
|
||||||
&& !is_diagnostic_namespace_variant
|
&& !is_diagnostic_namespace_variant
|
||||||
@ -539,6 +555,13 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
|
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
|
||||||
|
if !tcx.is_trait(item_def_id) {
|
||||||
|
// It could be a trait_alias (`trait MyTrait = SomeOtherTrait`)
|
||||||
|
// or an implementation (`impl MyTrait for Foo {}`)
|
||||||
|
//
|
||||||
|
// We don't support those.
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
|
if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
|
||||||
return Self::parse_attribute(attr, false, tcx, item_def_id);
|
return Self::parse_attribute(attr, false, tcx, item_def_id);
|
||||||
} else {
|
} else {
|
||||||
@ -554,15 +577,15 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
IgnoredDiagnosticOption::maybe_emit_warning(
|
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||||
tcx,
|
tcx,
|
||||||
item_def_id,
|
item_def_id,
|
||||||
directive.message.as_ref().map(|f| f.span),
|
directive.message.as_ref().map(|f| f.0),
|
||||||
aggr.message.as_ref().map(|f| f.span),
|
aggr.message.as_ref().map(|f| f.0),
|
||||||
"message",
|
"message",
|
||||||
);
|
);
|
||||||
IgnoredDiagnosticOption::maybe_emit_warning(
|
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||||
tcx,
|
tcx,
|
||||||
item_def_id,
|
item_def_id,
|
||||||
directive.label.as_ref().map(|f| f.span),
|
directive.label.as_ref().map(|f| f.0),
|
||||||
aggr.label.as_ref().map(|f| f.span),
|
aggr.label.as_ref().map(|f| f.0),
|
||||||
"label",
|
"label",
|
||||||
);
|
);
|
||||||
IgnoredDiagnosticOption::maybe_emit_warning(
|
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||||
@ -636,13 +659,16 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
condition: None,
|
condition: None,
|
||||||
message: None,
|
message: None,
|
||||||
subcommands: vec![],
|
subcommands: vec![],
|
||||||
label: Some(OnUnimplementedFormatString::try_parse(
|
label: Some((
|
||||||
|
attr.span(),
|
||||||
|
OnUnimplementedFormatString::try_parse(
|
||||||
tcx,
|
tcx,
|
||||||
item_def_id,
|
item_def_id,
|
||||||
value,
|
value,
|
||||||
attr.span(),
|
attr.value_span().unwrap_or(attr.span()),
|
||||||
is_diagnostic_namespace_variant,
|
is_diagnostic_namespace_variant,
|
||||||
)?),
|
)?,
|
||||||
|
)),
|
||||||
notes: Vec::new(),
|
notes: Vec::new(),
|
||||||
parent_label: None,
|
parent_label: None,
|
||||||
append_const_msg: None,
|
append_const_msg: None,
|
||||||
@ -702,43 +728,23 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
&self,
|
&self,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
trait_ref: ty::TraitRef<'tcx>,
|
trait_ref: ty::TraitRef<'tcx>,
|
||||||
options: &[(Symbol, Option<String>)],
|
condition_options: &ConditionOptions,
|
||||||
long_ty_file: &mut Option<PathBuf>,
|
args: &FormatArgs<'tcx>,
|
||||||
) -> OnUnimplementedNote {
|
) -> OnUnimplementedNote {
|
||||||
let mut message = None;
|
let mut message = None;
|
||||||
let mut label = None;
|
let mut label = None;
|
||||||
let mut notes = Vec::new();
|
let mut notes = Vec::new();
|
||||||
let mut parent_label = None;
|
let mut parent_label = None;
|
||||||
let mut append_const_msg = None;
|
let mut append_const_msg = None;
|
||||||
info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
|
info!(
|
||||||
|
"evaluate({:?}, trait_ref={:?}, options={:?}, args ={:?})",
|
||||||
let options_map: FxHashMap<Symbol, String> =
|
self, trait_ref, condition_options, args
|
||||||
options.iter().filter_map(|(k, v)| v.clone().map(|v| (*k, v))).collect();
|
);
|
||||||
|
|
||||||
for command in self.subcommands.iter().chain(Some(self)).rev() {
|
for command in self.subcommands.iter().chain(Some(self)).rev() {
|
||||||
debug!(?command);
|
debug!(?command);
|
||||||
if let Some(ref condition) = command.condition
|
if let Some(ref condition) = command.condition
|
||||||
&& !attr::eval_condition(condition, &tcx.sess, Some(tcx.features()), &mut |cfg| {
|
&& !condition.matches_predicate(tcx, condition_options)
|
||||||
let value = cfg.value.map(|v| {
|
|
||||||
// `with_no_visible_paths` is also used when generating the options,
|
|
||||||
// so we need to match it here.
|
|
||||||
ty::print::with_no_visible_paths!(
|
|
||||||
OnUnimplementedFormatString {
|
|
||||||
symbol: v,
|
|
||||||
span: cfg.span,
|
|
||||||
is_diagnostic_namespace_variant: false
|
|
||||||
}
|
|
||||||
.format(
|
|
||||||
tcx,
|
|
||||||
trait_ref,
|
|
||||||
&options_map,
|
|
||||||
long_ty_file
|
|
||||||
)
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
options.contains(&(cfg.name, value))
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
debug!("evaluate: skipping {:?} due to condition", command);
|
debug!("evaluate: skipping {:?} due to condition", command);
|
||||||
continue;
|
continue;
|
||||||
@ -762,14 +768,10 @@ impl<'tcx> OnUnimplementedDirective {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OnUnimplementedNote {
|
OnUnimplementedNote {
|
||||||
label: label.map(|l| l.format(tcx, trait_ref, &options_map, long_ty_file)),
|
label: label.map(|l| l.1.format(tcx, trait_ref, args)),
|
||||||
message: message.map(|m| m.format(tcx, trait_ref, &options_map, long_ty_file)),
|
message: message.map(|m| m.1.format(tcx, trait_ref, args)),
|
||||||
notes: notes
|
notes: notes.into_iter().map(|n| n.format(tcx, trait_ref, args)).collect(),
|
||||||
.into_iter()
|
parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, args)),
|
||||||
.map(|n| n.format(tcx, trait_ref, &options_map, long_ty_file))
|
|
||||||
.collect(),
|
|
||||||
parent_label: parent_label
|
|
||||||
.map(|e_s| e_s.format(tcx, trait_ref, &options_map, long_ty_file)),
|
|
||||||
append_const_msg,
|
append_const_msg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -780,106 +782,81 @@ impl<'tcx> OnUnimplementedFormatString {
|
|||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
item_def_id: DefId,
|
item_def_id: DefId,
|
||||||
from: Symbol,
|
from: Symbol,
|
||||||
value_span: Span,
|
span: Span,
|
||||||
is_diagnostic_namespace_variant: bool,
|
is_diagnostic_namespace_variant: bool,
|
||||||
) -> Result<Self, ErrorGuaranteed> {
|
) -> Result<Self, ErrorGuaranteed> {
|
||||||
let result = OnUnimplementedFormatString {
|
let result =
|
||||||
symbol: from,
|
OnUnimplementedFormatString { symbol: from, span, is_diagnostic_namespace_variant };
|
||||||
span: value_span,
|
|
||||||
is_diagnostic_namespace_variant,
|
|
||||||
};
|
|
||||||
result.verify(tcx, item_def_id)?;
|
result.verify(tcx, item_def_id)?;
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> {
|
fn verify(&self, tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> {
|
||||||
let trait_def_id = if tcx.is_trait(item_def_id) {
|
if !tcx.is_trait(trait_def_id) {
|
||||||
item_def_id
|
return Ok(());
|
||||||
} else {
|
|
||||||
tcx.trait_id_of_impl(item_def_id)
|
|
||||||
.expect("expected `on_unimplemented` to correspond to a trait")
|
|
||||||
};
|
};
|
||||||
let trait_name = tcx.item_ident(trait_def_id);
|
|
||||||
let generics = tcx.generics_of(item_def_id);
|
let ctx = if self.is_diagnostic_namespace_variant {
|
||||||
let s = self.symbol.as_str();
|
Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }
|
||||||
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
|
} else {
|
||||||
|
Ctx::RustcOnUnimplemented { tcx, trait_def_id }
|
||||||
|
};
|
||||||
|
|
||||||
let mut result = Ok(());
|
let mut result = Ok(());
|
||||||
for token in &mut parser {
|
|
||||||
match token {
|
match FormatString::parse(self.symbol, self.span, &ctx) {
|
||||||
Piece::Lit(_) => (), // Normal string, no need to check it
|
// Warnings about format specifiers, deprecated parameters, wrong parameters etc.
|
||||||
Piece::NextArgument(a) => {
|
// In other words we'd like to let the author know, but we can still try to format the string later
|
||||||
let format_spec = a.format;
|
Ok(FormatString { warnings, .. }) => {
|
||||||
if self.is_diagnostic_namespace_variant
|
|
||||||
&& (format_spec.ty_span.is_some()
|
|
||||||
|| format_spec.width_span.is_some()
|
|
||||||
|| format_spec.precision_span.is_some()
|
|
||||||
|| format_spec.fill_span.is_some())
|
|
||||||
{
|
|
||||||
if let Some(item_def_id) = item_def_id.as_local() {
|
|
||||||
tcx.emit_node_span_lint(
|
|
||||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
|
||||||
tcx.local_def_id_to_hir_id(item_def_id),
|
|
||||||
self.span,
|
|
||||||
InvalidFormatSpecifier,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match a.position {
|
|
||||||
Position::ArgumentNamed(s) => {
|
|
||||||
match Symbol::intern(s) {
|
|
||||||
// `{ThisTraitsName}` is allowed
|
|
||||||
s if s == trait_name.name
|
|
||||||
&& !self.is_diagnostic_namespace_variant =>
|
|
||||||
{
|
|
||||||
()
|
|
||||||
}
|
|
||||||
s if ALLOWED_FORMAT_SYMBOLS.contains(&s)
|
|
||||||
&& !self.is_diagnostic_namespace_variant =>
|
|
||||||
{
|
|
||||||
()
|
|
||||||
}
|
|
||||||
// So is `{A}` if A is a type parameter
|
|
||||||
s if generics.own_params.iter().any(|param| param.name == s) => (),
|
|
||||||
s => {
|
|
||||||
if self.is_diagnostic_namespace_variant {
|
if self.is_diagnostic_namespace_variant {
|
||||||
if let Some(item_def_id) = item_def_id.as_local() {
|
for w in warnings {
|
||||||
tcx.emit_node_span_lint(
|
w.emit_warning(tcx, trait_def_id)
|
||||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
|
||||||
tcx.local_def_id_to_hir_id(item_def_id),
|
|
||||||
self.span,
|
|
||||||
UnknownFormatParameterForOnUnimplementedAttr {
|
|
||||||
argument_name: s,
|
|
||||||
trait_name,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result = Err(struct_span_code_err!(
|
for w in warnings {
|
||||||
|
match w {
|
||||||
|
FormatWarning::UnknownParam { argument_name, span } => {
|
||||||
|
let reported = struct_span_code_err!(
|
||||||
tcx.dcx(),
|
tcx.dcx(),
|
||||||
self.span,
|
span,
|
||||||
E0230,
|
E0230,
|
||||||
"there is no parameter `{}` on {}",
|
"cannot find parameter {} on this trait",
|
||||||
s,
|
argument_name,
|
||||||
if trait_def_id == item_def_id {
|
|
||||||
format!("trait `{trait_name}`")
|
|
||||||
} else {
|
|
||||||
"impl".to_string()
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
.emit());
|
.emit();
|
||||||
|
result = Err(reported);
|
||||||
|
}
|
||||||
|
FormatWarning::PositionalArgument { span, .. } => {
|
||||||
|
let reported = struct_span_code_err!(
|
||||||
|
tcx.dcx(),
|
||||||
|
span,
|
||||||
|
E0231,
|
||||||
|
"positional format arguments are not allowed here"
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
result = Err(reported);
|
||||||
|
}
|
||||||
|
FormatWarning::InvalidSpecifier { .. }
|
||||||
|
| FormatWarning::FutureIncompat { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// `{:1}` and `{}` are not to be used
|
// Errors from the underlying `rustc_parse_format::Parser`
|
||||||
Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
|
Err(errors) => {
|
||||||
|
// we cannot return errors from processing the format string as hard error here
|
||||||
|
// as the diagnostic namespace guarantees that malformed input cannot cause an error
|
||||||
|
//
|
||||||
|
// if we encounter any error while processing we nevertheless want to show it as warning
|
||||||
|
// so that users are aware that something is not correct
|
||||||
|
for e in errors {
|
||||||
if self.is_diagnostic_namespace_variant {
|
if self.is_diagnostic_namespace_variant {
|
||||||
if let Some(item_def_id) = item_def_id.as_local() {
|
if let Some(trait_def_id) = trait_def_id.as_local() {
|
||||||
tcx.emit_node_span_lint(
|
tcx.emit_node_span_lint(
|
||||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||||
tcx.local_def_id_to_hir_id(item_def_id),
|
tcx.local_def_id_to_hir_id(trait_def_id),
|
||||||
self.span,
|
self.span,
|
||||||
DisallowedPositionalArgument,
|
WrappedParserError { description: e.description, label: e.label },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -887,7 +864,8 @@ impl<'tcx> OnUnimplementedFormatString {
|
|||||||
tcx.dcx(),
|
tcx.dcx(),
|
||||||
self.span,
|
self.span,
|
||||||
E0231,
|
E0231,
|
||||||
"only named generic parameters are allowed"
|
"{}",
|
||||||
|
e.description,
|
||||||
)
|
)
|
||||||
.emit();
|
.emit();
|
||||||
result = Err(reported);
|
result = Err(reported);
|
||||||
@ -895,29 +873,6 @@ impl<'tcx> OnUnimplementedFormatString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
// we cannot return errors from processing the format string as hard error here
|
|
||||||
// as the diagnostic namespace guarantees that malformed input cannot cause an error
|
|
||||||
//
|
|
||||||
// if we encounter any error while processing we nevertheless want to show it as warning
|
|
||||||
// so that users are aware that something is not correct
|
|
||||||
for e in parser.errors {
|
|
||||||
if self.is_diagnostic_namespace_variant {
|
|
||||||
if let Some(item_def_id) = item_def_id.as_local() {
|
|
||||||
tcx.emit_node_span_lint(
|
|
||||||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
|
||||||
tcx.local_def_id_to_hir_id(item_def_id),
|
|
||||||
self.span,
|
|
||||||
WrappedParserError { description: e.description, label: e.label },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let reported =
|
|
||||||
struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,).emit();
|
|
||||||
result = Err(reported);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@ -926,85 +881,18 @@ impl<'tcx> OnUnimplementedFormatString {
|
|||||||
&self,
|
&self,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
trait_ref: ty::TraitRef<'tcx>,
|
trait_ref: ty::TraitRef<'tcx>,
|
||||||
options: &FxHashMap<Symbol, String>,
|
args: &FormatArgs<'tcx>,
|
||||||
long_ty_file: &mut Option<PathBuf>,
|
|
||||||
) -> String {
|
) -> String {
|
||||||
let name = tcx.item_name(trait_ref.def_id);
|
let trait_def_id = trait_ref.def_id;
|
||||||
let trait_str = tcx.def_path_str(trait_ref.def_id);
|
let ctx = if self.is_diagnostic_namespace_variant {
|
||||||
let generics = tcx.generics_of(trait_ref.def_id);
|
Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }
|
||||||
let generic_map = generics
|
|
||||||
.own_params
|
|
||||||
.iter()
|
|
||||||
.filter_map(|param| {
|
|
||||||
let value = match param.kind {
|
|
||||||
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
|
|
||||||
if let Some(ty) = trait_ref.args[param.index as usize].as_type() {
|
|
||||||
tcx.short_string(ty, long_ty_file)
|
|
||||||
} else {
|
} else {
|
||||||
trait_ref.args[param.index as usize].to_string()
|
Ctx::RustcOnUnimplemented { tcx, trait_def_id }
|
||||||
}
|
|
||||||
}
|
|
||||||
GenericParamDefKind::Lifetime => return None,
|
|
||||||
};
|
};
|
||||||
let name = param.name;
|
|
||||||
Some((name, value))
|
|
||||||
})
|
|
||||||
.collect::<FxHashMap<Symbol, String>>();
|
|
||||||
let empty_string = String::new();
|
|
||||||
|
|
||||||
let s = self.symbol.as_str();
|
if let Ok(s) = FormatString::parse(self.symbol, self.span, &ctx) {
|
||||||
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
|
s.format(args)
|
||||||
let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
|
|
||||||
let constructed_message = (&mut parser)
|
|
||||||
.map(|p| match p {
|
|
||||||
Piece::Lit(s) => s.to_owned(),
|
|
||||||
Piece::NextArgument(a) => match a.position {
|
|
||||||
Position::ArgumentNamed(arg) => {
|
|
||||||
let s = Symbol::intern(arg);
|
|
||||||
match generic_map.get(&s) {
|
|
||||||
Some(val) => val.to_string(),
|
|
||||||
None if self.is_diagnostic_namespace_variant => {
|
|
||||||
format!("{{{arg}}}")
|
|
||||||
}
|
|
||||||
None if s == name => trait_str.clone(),
|
|
||||||
None => {
|
|
||||||
if let Some(val) = options.get(&s) {
|
|
||||||
val.clone()
|
|
||||||
} else if s == sym::from_desugaring {
|
|
||||||
// don't break messages using these two arguments incorrectly
|
|
||||||
String::new()
|
|
||||||
} else if s == sym::ItemContext
|
|
||||||
&& !self.is_diagnostic_namespace_variant
|
|
||||||
{
|
|
||||||
item_context.clone()
|
|
||||||
} else if s == sym::integral {
|
|
||||||
String::from("{integral}")
|
|
||||||
} else if s == sym::integer_ {
|
|
||||||
String::from("{integer}")
|
|
||||||
} else if s == sym::float {
|
|
||||||
String::from("{float}")
|
|
||||||
} else {
|
} else {
|
||||||
bug!(
|
|
||||||
"broken on_unimplemented {:?} for {:?}: \
|
|
||||||
no argument matching {:?}",
|
|
||||||
self.symbol,
|
|
||||||
trait_ref,
|
|
||||||
s
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Position::ArgumentImplicitlyIs(_) if self.is_diagnostic_namespace_variant => {
|
|
||||||
String::from("{}")
|
|
||||||
}
|
|
||||||
Position::ArgumentIs(idx) if self.is_diagnostic_namespace_variant => {
|
|
||||||
format!("{{{idx}}}")
|
|
||||||
}
|
|
||||||
_ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
// we cannot return errors from processing the format string as hard error here
|
// we cannot return errors from processing the format string as hard error here
|
||||||
// as the diagnostic namespace guarantees that malformed input cannot cause an error
|
// as the diagnostic namespace guarantees that malformed input cannot cause an error
|
||||||
//
|
//
|
||||||
@ -1014,10 +902,7 @@ impl<'tcx> OnUnimplementedFormatString {
|
|||||||
//
|
//
|
||||||
// The actual parser errors are emitted earlier
|
// The actual parser errors are emitted earlier
|
||||||
// as lint warnings in OnUnimplementedFormatString::verify
|
// as lint warnings in OnUnimplementedFormatString::verify
|
||||||
if self.is_diagnostic_namespace_variant && !parser.errors.is_empty() {
|
self.symbol.as_str().into()
|
||||||
String::from(s)
|
|
||||||
} else {
|
|
||||||
constructed_message
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,120 @@
|
|||||||
|
use rustc_ast::MetaItemInner;
|
||||||
|
use rustc_attr_parsing as attr;
|
||||||
|
use rustc_middle::ty::{self, TyCtxt};
|
||||||
|
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
|
||||||
|
use rustc_span::{DesugaringKind, Span, Symbol, kw, sym};
|
||||||
|
|
||||||
|
/// A predicate in an attribute using on, all, any,
|
||||||
|
/// similar to a cfg predicate.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Condition {
|
||||||
|
pub inner: MetaItemInner,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Condition {
|
||||||
|
pub fn span(&self) -> Span {
|
||||||
|
self.inner.span()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn matches_predicate<'tcx>(&self, tcx: TyCtxt<'tcx>, options: &ConditionOptions) -> bool {
|
||||||
|
attr::eval_condition(&self.inner, tcx.sess, Some(tcx.features()), &mut |cfg| {
|
||||||
|
let value = cfg.value.map(|v| {
|
||||||
|
// `with_no_visible_paths` is also used when generating the options,
|
||||||
|
// so we need to match it here.
|
||||||
|
ty::print::with_no_visible_paths!({
|
||||||
|
Parser::new(v.as_str(), None, None, false, ParseMode::Format)
|
||||||
|
.map(|p| match p {
|
||||||
|
Piece::Lit(s) => s.to_owned(),
|
||||||
|
Piece::NextArgument(a) => match a.position {
|
||||||
|
Position::ArgumentNamed(arg) => {
|
||||||
|
let s = Symbol::intern(arg);
|
||||||
|
match options.generic_args.iter().find(|(k, _)| *k == s) {
|
||||||
|
Some((_, val)) => val.to_string(),
|
||||||
|
None => format!("{{{arg}}}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Position::ArgumentImplicitlyIs(_) => String::from("{}"),
|
||||||
|
Position::ArgumentIs(idx) => format!("{{{idx}}}"),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
options.contains(cfg.name, &value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used with `Condition::matches_predicate` to test whether the condition applies
|
||||||
|
///
|
||||||
|
/// For example, given a
|
||||||
|
/// ```rust,ignore (just an example)
|
||||||
|
/// #[rustc_on_unimplemented(
|
||||||
|
/// on(all(from_desugaring = "QuestionMark"),
|
||||||
|
/// message = "the `?` operator can only be used in {ItemContext} \
|
||||||
|
/// that returns `Result` or `Option` \
|
||||||
|
/// (or another type that implements `{FromResidual}`)",
|
||||||
|
/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`",
|
||||||
|
/// parent_label = "this function should return `Result` or `Option` to accept `?`"
|
||||||
|
/// ),
|
||||||
|
/// )]
|
||||||
|
/// pub trait FromResidual<R = <Self as Try>::Residual> {
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// async fn an_async_function() -> u32 {
|
||||||
|
/// let x: Option<u32> = None;
|
||||||
|
/// x?; //~ ERROR the `?` operator
|
||||||
|
/// 22
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// it will look like this:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore (just an example)
|
||||||
|
/// ConditionOptions {
|
||||||
|
/// self_types: ["u32", "{integral}"],
|
||||||
|
/// from_desugaring: Some("QuestionMark"),
|
||||||
|
/// cause: None,
|
||||||
|
/// crate_local: false,
|
||||||
|
/// direct: true,
|
||||||
|
/// generic_args: [("Self","u32"),
|
||||||
|
/// ("R", "core::option::Option<core::convert::Infallible>"),
|
||||||
|
/// ("R", "core::option::Option<T>" ),
|
||||||
|
/// ],
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ConditionOptions {
|
||||||
|
/// All the self types that may apply.
|
||||||
|
/// for example
|
||||||
|
pub self_types: Vec<String>,
|
||||||
|
// The kind of compiler desugaring.
|
||||||
|
pub from_desugaring: Option<DesugaringKind>,
|
||||||
|
/// Match on a variant of [rustc_infer::traits::ObligationCauseCode]
|
||||||
|
pub cause: Option<String>,
|
||||||
|
pub crate_local: bool,
|
||||||
|
/// Is the obligation "directly" user-specified, rather than derived?
|
||||||
|
pub direct: bool,
|
||||||
|
// A list of the generic arguments and their reified types
|
||||||
|
pub generic_args: Vec<(Symbol, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConditionOptions {
|
||||||
|
pub fn contains(&self, key: Symbol, value: &Option<String>) -> bool {
|
||||||
|
match (key, value) {
|
||||||
|
(sym::_Self | kw::SelfUpper, Some(value)) => self.self_types.contains(&value),
|
||||||
|
// from_desugaring as a flag
|
||||||
|
(sym::from_desugaring, None) => self.from_desugaring.is_some(),
|
||||||
|
// from_desugaring as key == value
|
||||||
|
(sym::from_desugaring, Some(v)) if let Some(ds) = self.from_desugaring => ds.matches(v),
|
||||||
|
(sym::cause, Some(value)) => self.cause.as_deref() == Some(value),
|
||||||
|
(sym::crate_local, None) => self.crate_local,
|
||||||
|
(sym::direct, None) => self.direct,
|
||||||
|
(other, Some(value)) => {
|
||||||
|
self.generic_args.iter().any(|(k, v)| *k == other && v == value)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,414 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use errors::*;
|
||||||
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
use rustc_middle::ty::print::TraitRefPrintSugared;
|
||||||
|
use rustc_parse_format::{
|
||||||
|
Alignment, Argument, Count, FormatSpec, InnerSpan, ParseError, ParseMode, Parser,
|
||||||
|
Piece as RpfPiece, Position,
|
||||||
|
};
|
||||||
|
use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
|
||||||
|
use rustc_span::def_id::DefId;
|
||||||
|
use rustc_span::{BytePos, Pos, Span, Symbol, kw, sym};
|
||||||
|
|
||||||
|
/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces",
|
||||||
|
/// either as string pieces or dynamic arguments.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FormatString {
|
||||||
|
#[allow(dead_code, reason = "Debug impl")]
|
||||||
|
input: Symbol,
|
||||||
|
span: Span,
|
||||||
|
pieces: Vec<Piece>,
|
||||||
|
/// The formatting string was parsed succesfully but with warnings
|
||||||
|
pub warnings: Vec<FormatWarning>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Piece {
|
||||||
|
Lit(String),
|
||||||
|
Arg(FormatArg),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum FormatArg {
|
||||||
|
// A generic parameter, like `{T}` if we're on the `From<T>` trait.
|
||||||
|
GenericParam {
|
||||||
|
generic_param: Symbol,
|
||||||
|
},
|
||||||
|
// `{Self}`
|
||||||
|
SelfUpper,
|
||||||
|
/// `{This}` or `{TraitName}`
|
||||||
|
This,
|
||||||
|
/// The sugared form of the trait
|
||||||
|
Trait,
|
||||||
|
/// what we're in, like a function, method, closure etc.
|
||||||
|
ItemContext,
|
||||||
|
/// What the user typed, if it doesn't match anything we can use.
|
||||||
|
AsIs(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Ctx<'tcx> {
|
||||||
|
// `#[rustc_on_unimplemented]`
|
||||||
|
RustcOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId },
|
||||||
|
// `#[diagnostic::...]`
|
||||||
|
DiagnosticOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FormatWarning {
|
||||||
|
UnknownParam { argument_name: Symbol, span: Span },
|
||||||
|
PositionalArgument { span: Span, help: String },
|
||||||
|
InvalidSpecifier { name: String, span: Span },
|
||||||
|
FutureIncompat { span: Span, help: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormatWarning {
|
||||||
|
pub fn emit_warning<'tcx>(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) {
|
||||||
|
match *self {
|
||||||
|
FormatWarning::UnknownParam { argument_name, span } => {
|
||||||
|
let this = tcx.item_ident(item_def_id);
|
||||||
|
if let Some(item_def_id) = item_def_id.as_local() {
|
||||||
|
tcx.emit_node_span_lint(
|
||||||
|
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||||
|
tcx.local_def_id_to_hir_id(item_def_id),
|
||||||
|
span,
|
||||||
|
UnknownFormatParameterForOnUnimplementedAttr {
|
||||||
|
argument_name,
|
||||||
|
trait_name: this,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FormatWarning::PositionalArgument { span, .. } => {
|
||||||
|
if let Some(item_def_id) = item_def_id.as_local() {
|
||||||
|
tcx.emit_node_span_lint(
|
||||||
|
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||||
|
tcx.local_def_id_to_hir_id(item_def_id),
|
||||||
|
span,
|
||||||
|
DisallowedPositionalArgument,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FormatWarning::InvalidSpecifier { span, .. } => {
|
||||||
|
if let Some(item_def_id) = item_def_id.as_local() {
|
||||||
|
tcx.emit_node_span_lint(
|
||||||
|
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||||
|
tcx.local_def_id_to_hir_id(item_def_id),
|
||||||
|
span,
|
||||||
|
InvalidFormatSpecifier,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FormatWarning::FutureIncompat { .. } => {
|
||||||
|
// We've never deprecated anything in diagnostic namespace format strings
|
||||||
|
// but if we do we will emit a warning here
|
||||||
|
|
||||||
|
// FIXME(mejrs) in a couple releases, start emitting warnings for
|
||||||
|
// #[rustc_on_unimplemented] deprecated args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Arguments to fill a [FormatString] with.
|
||||||
|
///
|
||||||
|
/// For example, given a
|
||||||
|
/// ```rust,ignore (just an example)
|
||||||
|
///
|
||||||
|
/// #[rustc_on_unimplemented(
|
||||||
|
/// on(all(from_desugaring = "QuestionMark"),
|
||||||
|
/// message = "the `?` operator can only be used in {ItemContext} \
|
||||||
|
/// that returns `Result` or `Option` \
|
||||||
|
/// (or another type that implements `{FromResidual}`)",
|
||||||
|
/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`",
|
||||||
|
/// parent_label = "this function should return `Result` or `Option` to accept `?`"
|
||||||
|
/// ),
|
||||||
|
/// )]
|
||||||
|
/// pub trait FromResidual<R = <Self as Try>::Residual> {
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// async fn an_async_function() -> u32 {
|
||||||
|
/// let x: Option<u32> = None;
|
||||||
|
/// x?; //~ ERROR the `?` operator
|
||||||
|
/// 22
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// it will look like this:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore (just an example)
|
||||||
|
/// FormatArgs {
|
||||||
|
/// this: "FromResidual",
|
||||||
|
/// trait_sugared: "FromResidual<Option<Infallible>>",
|
||||||
|
/// item_context: "an async function",
|
||||||
|
/// generic_args: [("Self", "u32"), ("R", "Option<Infallible>")],
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FormatArgs<'tcx> {
|
||||||
|
pub this: String,
|
||||||
|
pub trait_sugared: TraitRefPrintSugared<'tcx>,
|
||||||
|
pub item_context: &'static str,
|
||||||
|
pub generic_args: Vec<(Symbol, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormatString {
|
||||||
|
pub fn span(&self) -> Span {
|
||||||
|
self.span
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse<'tcx>(
|
||||||
|
input: Symbol,
|
||||||
|
span: Span,
|
||||||
|
ctx: &Ctx<'tcx>,
|
||||||
|
) -> Result<Self, Vec<ParseError>> {
|
||||||
|
let s = input.as_str();
|
||||||
|
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
|
||||||
|
let mut pieces = Vec::new();
|
||||||
|
let mut warnings = Vec::new();
|
||||||
|
|
||||||
|
for piece in &mut parser {
|
||||||
|
match piece {
|
||||||
|
RpfPiece::Lit(lit) => {
|
||||||
|
pieces.push(Piece::Lit(lit.into()));
|
||||||
|
}
|
||||||
|
RpfPiece::NextArgument(arg) => {
|
||||||
|
warn_on_format_spec(arg.format, &mut warnings, span);
|
||||||
|
let arg = parse_arg(&arg, ctx, &mut warnings, span);
|
||||||
|
pieces.push(Piece::Arg(arg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if parser.errors.is_empty() {
|
||||||
|
Ok(FormatString { input, pieces, span, warnings })
|
||||||
|
} else {
|
||||||
|
Err(parser.errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(&self, args: &FormatArgs<'_>) -> String {
|
||||||
|
let mut ret = String::new();
|
||||||
|
for piece in &self.pieces {
|
||||||
|
match piece {
|
||||||
|
Piece::Lit(s) | Piece::Arg(FormatArg::AsIs(s)) => ret.push_str(&s),
|
||||||
|
|
||||||
|
// `A` if we have `trait Trait<A> {}` and `note = "i'm the actual type of {A}"`
|
||||||
|
Piece::Arg(FormatArg::GenericParam { generic_param }) => {
|
||||||
|
// Should always be some but we can't raise errors here
|
||||||
|
let value = match args.generic_args.iter().find(|(p, _)| p == generic_param) {
|
||||||
|
Some((_, val)) => val.to_string(),
|
||||||
|
None => generic_param.to_string(),
|
||||||
|
};
|
||||||
|
ret.push_str(&value);
|
||||||
|
}
|
||||||
|
// `{Self}`
|
||||||
|
Piece::Arg(FormatArg::SelfUpper) => {
|
||||||
|
let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) {
|
||||||
|
Some((_, val)) => val.to_string(),
|
||||||
|
None => "Self".to_string(),
|
||||||
|
};
|
||||||
|
ret.push_str(&slf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's only `rustc_onunimplemented` from here
|
||||||
|
Piece::Arg(FormatArg::This) => ret.push_str(&args.this),
|
||||||
|
Piece::Arg(FormatArg::Trait) => {
|
||||||
|
let _ = fmt::write(&mut ret, format_args!("{}", &args.trait_sugared));
|
||||||
|
}
|
||||||
|
Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_arg<'tcx>(
|
||||||
|
arg: &Argument<'_>,
|
||||||
|
ctx: &Ctx<'tcx>,
|
||||||
|
warnings: &mut Vec<FormatWarning>,
|
||||||
|
input_span: Span,
|
||||||
|
) -> FormatArg {
|
||||||
|
let (Ctx::RustcOnUnimplemented { tcx, trait_def_id }
|
||||||
|
| Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = ctx;
|
||||||
|
let trait_name = tcx.item_ident(*trait_def_id);
|
||||||
|
let generics = tcx.generics_of(trait_def_id);
|
||||||
|
let span = slice_span(input_span, arg.position_span);
|
||||||
|
|
||||||
|
match arg.position {
|
||||||
|
// Something like "hello {name}"
|
||||||
|
Position::ArgumentNamed(name) => match (ctx, Symbol::intern(name)) {
|
||||||
|
// accepted, but deprecated
|
||||||
|
(Ctx::RustcOnUnimplemented { .. }, sym::_Self) => {
|
||||||
|
warnings
|
||||||
|
.push(FormatWarning::FutureIncompat { span, help: String::from("use {Self}") });
|
||||||
|
FormatArg::SelfUpper
|
||||||
|
}
|
||||||
|
(
|
||||||
|
Ctx::RustcOnUnimplemented { .. },
|
||||||
|
sym::from_desugaring
|
||||||
|
| sym::crate_local
|
||||||
|
| sym::direct
|
||||||
|
| sym::cause
|
||||||
|
| sym::float
|
||||||
|
| sym::integer_
|
||||||
|
| sym::integral,
|
||||||
|
) => {
|
||||||
|
warnings.push(FormatWarning::FutureIncompat {
|
||||||
|
span,
|
||||||
|
help: String::from("don't use this in a format string"),
|
||||||
|
});
|
||||||
|
FormatArg::AsIs(String::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only `#[rustc_on_unimplemented]` can use these
|
||||||
|
(Ctx::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext,
|
||||||
|
(Ctx::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This,
|
||||||
|
(Ctx::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait,
|
||||||
|
// `{ThisTraitsName}`. Some attrs in std use this, but I'd like to change it to the more general `{This}`
|
||||||
|
// because that'll be simpler to parse and extend in the future
|
||||||
|
(Ctx::RustcOnUnimplemented { .. }, name) if name == trait_name.name => {
|
||||||
|
warnings
|
||||||
|
.push(FormatWarning::FutureIncompat { span, help: String::from("use {This}") });
|
||||||
|
FormatArg::This
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any attribute can use these
|
||||||
|
(
|
||||||
|
Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
|
||||||
|
kw::SelfUpper,
|
||||||
|
) => FormatArg::SelfUpper,
|
||||||
|
(
|
||||||
|
Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
|
||||||
|
generic_param,
|
||||||
|
) if generics.own_params.iter().any(|param| param.name == generic_param) => {
|
||||||
|
FormatArg::GenericParam { generic_param }
|
||||||
|
}
|
||||||
|
|
||||||
|
(_, argument_name) => {
|
||||||
|
warnings.push(FormatWarning::UnknownParam { argument_name, span });
|
||||||
|
FormatArg::AsIs(format!("{{{}}}", argument_name.as_str()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// `{:1}` and `{}` are ignored
|
||||||
|
Position::ArgumentIs(idx) => {
|
||||||
|
warnings.push(FormatWarning::PositionalArgument {
|
||||||
|
span,
|
||||||
|
help: format!("use `{{{idx}}}` to print a number in braces"),
|
||||||
|
});
|
||||||
|
FormatArg::AsIs(format!("{{{idx}}}"))
|
||||||
|
}
|
||||||
|
Position::ArgumentImplicitlyIs(_) => {
|
||||||
|
warnings.push(FormatWarning::PositionalArgument {
|
||||||
|
span,
|
||||||
|
help: String::from("use `{{}}` to print empty braces"),
|
||||||
|
});
|
||||||
|
FormatArg::AsIs(String::from("{}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything
|
||||||
|
/// with specifiers, so emit a warning if they are used.
|
||||||
|
fn warn_on_format_spec(spec: FormatSpec<'_>, warnings: &mut Vec<FormatWarning>, input_span: Span) {
|
||||||
|
if !matches!(
|
||||||
|
spec,
|
||||||
|
FormatSpec {
|
||||||
|
fill: None,
|
||||||
|
fill_span: None,
|
||||||
|
align: Alignment::AlignUnknown,
|
||||||
|
sign: None,
|
||||||
|
alternate: false,
|
||||||
|
zero_pad: false,
|
||||||
|
debug_hex: None,
|
||||||
|
precision: Count::CountImplied,
|
||||||
|
precision_span: None,
|
||||||
|
width: Count::CountImplied,
|
||||||
|
width_span: None,
|
||||||
|
ty: _,
|
||||||
|
ty_span: _,
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
let span = spec.ty_span.map(|inner| slice_span(input_span, inner)).unwrap_or(input_span);
|
||||||
|
warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function because `Span` and `rustc_parse_format::InnerSpan` don't know about each other
|
||||||
|
fn slice_span(input: Span, inner: InnerSpan) -> Span {
|
||||||
|
let InnerSpan { start, end } = inner;
|
||||||
|
let span = input.data();
|
||||||
|
|
||||||
|
Span::new(
|
||||||
|
span.lo + BytePos::from_usize(start),
|
||||||
|
span.lo + BytePos::from_usize(end),
|
||||||
|
span.ctxt,
|
||||||
|
span.parent,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod errors {
|
||||||
|
use rustc_macros::LintDiagnostic;
|
||||||
|
use rustc_span::Ident;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
|
||||||
|
#[help]
|
||||||
|
pub struct UnknownFormatParameterForOnUnimplementedAttr {
|
||||||
|
pub argument_name: Symbol,
|
||||||
|
pub trait_name: Ident,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(trait_selection_disallowed_positional_argument)]
|
||||||
|
#[help]
|
||||||
|
pub struct DisallowedPositionalArgument;
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(trait_selection_invalid_format_specifier)]
|
||||||
|
#[help]
|
||||||
|
pub struct InvalidFormatSpecifier;
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(trait_selection_missing_options_for_on_unimplemented_attr)]
|
||||||
|
#[help]
|
||||||
|
pub struct MissingOptionsForOnUnimplementedAttr;
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(trait_selection_ignored_diagnostic_option)]
|
||||||
|
pub struct IgnoredDiagnosticOption {
|
||||||
|
pub option_name: &'static str,
|
||||||
|
#[label]
|
||||||
|
pub span: Span,
|
||||||
|
#[label(trait_selection_other_label)]
|
||||||
|
pub prev_span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IgnoredDiagnosticOption {
|
||||||
|
pub fn maybe_emit_warning<'tcx>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
item_def_id: DefId,
|
||||||
|
new: Option<Span>,
|
||||||
|
old: Option<Span>,
|
||||||
|
option_name: &'static str,
|
||||||
|
) {
|
||||||
|
if let (Some(new_item), Some(old_item)) = (new, old) {
|
||||||
|
if let Some(item_def_id) = item_def_id.as_local() {
|
||||||
|
tcx.emit_node_span_lint(
|
||||||
|
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
|
||||||
|
tcx.local_def_id_to_hir_id(item_def_id),
|
||||||
|
new_item,
|
||||||
|
IgnoredDiagnosticOption {
|
||||||
|
span: new_item,
|
||||||
|
prev_span: old_item,
|
||||||
|
option_name,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3138,7 +3138,6 @@ ui/nll/user-annotations/issue-55241.rs
|
|||||||
ui/nll/user-annotations/issue-55748-pat-types-constrain-bindings.rs
|
ui/nll/user-annotations/issue-55748-pat-types-constrain-bindings.rs
|
||||||
ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs
|
ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs
|
||||||
ui/numbers-arithmetic/issue-8460.rs
|
ui/numbers-arithmetic/issue-8460.rs
|
||||||
ui/on-unimplemented/issue-104140.rs
|
|
||||||
ui/or-patterns/issue-64879-trailing-before-guard.rs
|
ui/or-patterns/issue-64879-trailing-before-guard.rs
|
||||||
ui/or-patterns/issue-67514-irrefutable-param.rs
|
ui/or-patterns/issue-67514-irrefutable-param.rs
|
||||||
ui/or-patterns/issue-68785-irrefutable-param-with-at.rs
|
ui/or-patterns/issue-68785-irrefutable-param-with-at.rs
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
//@ known-bug: #130627
|
|
||||||
|
|
||||||
#![feature(trait_alias)]
|
|
||||||
|
|
||||||
trait Test {}
|
|
||||||
|
|
||||||
#[diagnostic::on_unimplemented(
|
|
||||||
message="message",
|
|
||||||
label="label",
|
|
||||||
note="note"
|
|
||||||
)]
|
|
||||||
trait Alias = Test;
|
|
||||||
|
|
||||||
// Use trait alias as bound on type parameter.
|
|
||||||
fn foo<T: Alias>(v: &T) {
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
foo(&1);
|
|
||||||
}
|
|
17
tests/ui/diagnostic_namespace/on_impl_trait.rs
Normal file
17
tests/ui/diagnostic_namespace/on_impl_trait.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// used to ICE, see <https://github.com/rust-lang/rust/issues/130627>
|
||||||
|
// Instead it should just ignore the diagnostic attribute
|
||||||
|
#![feature(trait_alias)]
|
||||||
|
|
||||||
|
trait Test {}
|
||||||
|
|
||||||
|
#[diagnostic::on_unimplemented(message = "blah", label = "blah", note = "blah")]
|
||||||
|
//~^ WARN `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
|
||||||
|
trait Alias = Test;
|
||||||
|
|
||||||
|
// Use trait alias as bound on type parameter.
|
||||||
|
fn foo<T: Alias>(v: &T) {}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
foo(&1);
|
||||||
|
//~^ ERROR the trait bound `{integer}: Alias` is not satisfied
|
||||||
|
}
|
31
tests/ui/diagnostic_namespace/on_impl_trait.stderr
Normal file
31
tests/ui/diagnostic_namespace/on_impl_trait.stderr
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
|
||||||
|
--> $DIR/on_impl_trait.rs:7:1
|
||||||
|
|
|
||||||
|
LL | #[diagnostic::on_unimplemented(message = "blah", label = "blah", note = "blah")]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
|
||||||
|
|
||||||
|
error[E0277]: the trait bound `{integer}: Alias` is not satisfied
|
||||||
|
--> $DIR/on_impl_trait.rs:15:9
|
||||||
|
|
|
||||||
|
LL | foo(&1);
|
||||||
|
| --- ^^ the trait `Test` is not implemented for `{integer}`
|
||||||
|
| |
|
||||||
|
| required by a bound introduced by this call
|
||||||
|
|
|
||||||
|
help: this trait has no implementations, consider adding one
|
||||||
|
--> $DIR/on_impl_trait.rs:5:1
|
||||||
|
|
|
||||||
|
LL | trait Test {}
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
= note: required for `{integer}` to implement `Alias`
|
||||||
|
note: required by a bound in `foo`
|
||||||
|
--> $DIR/on_impl_trait.rs:12:11
|
||||||
|
|
|
||||||
|
LL | fn foo<T: Alias>(v: &T) {}
|
||||||
|
| ^^^^^ required by this bound in `foo`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error; 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
@ -1,52 +1,52 @@
|
|||||||
warning: unmatched `}` found
|
warning: unmatched `}` found
|
||||||
--> $DIR/broken_format.rs:2:32
|
--> $DIR/broken_format.rs:2:42
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")]
|
LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
|
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
|
||||||
|
|
||||||
warning: positional format arguments are not allowed here
|
warning: positional format arguments are not allowed here
|
||||||
--> $DIR/broken_format.rs:7:32
|
--> $DIR/broken_format.rs:7:49
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "Test {}")]
|
LL | #[diagnostic::on_unimplemented(message = "Test {}")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^
|
| ^
|
||||||
|
|
|
|
||||||
= help: only named format arguments with the name of one of the generic types are allowed in this context
|
= help: only named format arguments with the name of one of the generic types are allowed in this context
|
||||||
|
|
||||||
warning: positional format arguments are not allowed here
|
warning: positional format arguments are not allowed here
|
||||||
--> $DIR/broken_format.rs:12:32
|
--> $DIR/broken_format.rs:12:49
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
|
LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^
|
||||||
|
|
|
|
||||||
= help: only named format arguments with the name of one of the generic types are allowed in this context
|
= help: only named format arguments with the name of one of the generic types are allowed in this context
|
||||||
|
|
||||||
warning: invalid format specifier
|
warning: invalid format specifier
|
||||||
--> $DIR/broken_format.rs:17:32
|
--> $DIR/broken_format.rs:17:42
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")]
|
LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: no format specifier are supported in this position
|
= help: no format specifier are supported in this position
|
||||||
|
|
||||||
warning: expected `}`, found `!`
|
warning: expected `}`, found `!`
|
||||||
--> $DIR/broken_format.rs:22:32
|
--> $DIR/broken_format.rs:22:42
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
|
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
warning: unmatched `}` found
|
warning: unmatched `}` found
|
||||||
--> $DIR/broken_format.rs:22:32
|
--> $DIR/broken_format.rs:22:42
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
|
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
warning: unmatched `}` found
|
warning: unmatched `}` found
|
||||||
--> $DIR/broken_format.rs:2:32
|
--> $DIR/broken_format.rs:2:42
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")]
|
LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
@ -70,10 +70,10 @@ LL | fn check_1(_: impl ImportantTrait1) {}
|
|||||||
| ^^^^^^^^^^^^^^^ required by this bound in `check_1`
|
| ^^^^^^^^^^^^^^^ required by this bound in `check_1`
|
||||||
|
|
||||||
warning: positional format arguments are not allowed here
|
warning: positional format arguments are not allowed here
|
||||||
--> $DIR/broken_format.rs:7:32
|
--> $DIR/broken_format.rs:7:49
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "Test {}")]
|
LL | #[diagnostic::on_unimplemented(message = "Test {}")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^
|
| ^
|
||||||
|
|
|
|
||||||
= help: only named format arguments with the name of one of the generic types are allowed in this context
|
= help: only named format arguments with the name of one of the generic types are allowed in this context
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
@ -98,10 +98,10 @@ LL | fn check_2(_: impl ImportantTrait2) {}
|
|||||||
| ^^^^^^^^^^^^^^^ required by this bound in `check_2`
|
| ^^^^^^^^^^^^^^^ required by this bound in `check_2`
|
||||||
|
|
||||||
warning: positional format arguments are not allowed here
|
warning: positional format arguments are not allowed here
|
||||||
--> $DIR/broken_format.rs:12:32
|
--> $DIR/broken_format.rs:12:49
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
|
LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^
|
||||||
|
|
|
|
||||||
= help: only named format arguments with the name of one of the generic types are allowed in this context
|
= help: only named format arguments with the name of one of the generic types are allowed in this context
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
@ -126,10 +126,10 @@ LL | fn check_3(_: impl ImportantTrait3) {}
|
|||||||
| ^^^^^^^^^^^^^^^ required by this bound in `check_3`
|
| ^^^^^^^^^^^^^^^ required by this bound in `check_3`
|
||||||
|
|
||||||
warning: invalid format specifier
|
warning: invalid format specifier
|
||||||
--> $DIR/broken_format.rs:17:32
|
--> $DIR/broken_format.rs:17:42
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")]
|
LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: no format specifier are supported in this position
|
= help: no format specifier are supported in this position
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
@ -154,18 +154,18 @@ LL | fn check_4(_: impl ImportantTrait4) {}
|
|||||||
| ^^^^^^^^^^^^^^^ required by this bound in `check_4`
|
| ^^^^^^^^^^^^^^^ required by this bound in `check_4`
|
||||||
|
|
||||||
warning: expected `}`, found `!`
|
warning: expected `}`, found `!`
|
||||||
--> $DIR/broken_format.rs:22:32
|
--> $DIR/broken_format.rs:22:42
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
|
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
warning: unmatched `}` found
|
warning: unmatched `}` found
|
||||||
--> $DIR/broken_format.rs:22:32
|
--> $DIR/broken_format.rs:22:42
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
|
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
@ -39,82 +39,82 @@ LL | #[diagnostic::on_unimplemented = "Message"]
|
|||||||
= help: only `message`, `note` and `label` are allowed as options
|
= help: only `message`, `note` and `label` are allowed as options
|
||||||
|
|
||||||
warning: there is no parameter `from_desugaring` on trait `Baz`
|
warning: there is no parameter `from_desugaring` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:17
|
||||||
|
|
|
|
||||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
|
|
||||||
warning: there is no parameter `direct` on trait `Baz`
|
warning: there is no parameter `direct` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:34
|
||||||
|
|
|
|
||||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
|
|
||||||
warning: there is no parameter `cause` on trait `Baz`
|
warning: there is no parameter `cause` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:42
|
||||||
|
|
|
|
||||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
|
|
||||||
warning: there is no parameter `integral` on trait `Baz`
|
warning: there is no parameter `integral` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:49
|
||||||
|
|
|
|
||||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
|
|
||||||
warning: there is no parameter `integer` on trait `Baz`
|
warning: there is no parameter `integer` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:59
|
||||||
|
|
|
|
||||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
|
|
||||||
warning: there is no parameter `float` on trait `Baz`
|
warning: there is no parameter `float` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:15
|
||||||
|
|
|
|
||||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
|
|
||||||
warning: there is no parameter `_Self` on trait `Baz`
|
warning: there is no parameter `_Self` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:22
|
||||||
|
|
|
|
||||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
|
|
||||||
warning: there is no parameter `crate_local` on trait `Baz`
|
warning: there is no parameter `crate_local` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:29
|
||||||
|
|
|
|
||||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
|
|
||||||
warning: there is no parameter `Trait` on trait `Baz`
|
warning: there is no parameter `Trait` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:42
|
||||||
|
|
|
|
||||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
|
|
||||||
warning: there is no parameter `ItemContext` on trait `Baz`
|
warning: there is no parameter `ItemContext` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:49
|
||||||
|
|
|
|
||||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
|
|
||||||
@ -191,91 +191,91 @@ LL | fn takes_bar(_: impl Bar) {}
|
|||||||
| ^^^ required by this bound in `takes_bar`
|
| ^^^ required by this bound in `takes_bar`
|
||||||
|
|
||||||
warning: there is no parameter `from_desugaring` on trait `Baz`
|
warning: there is no parameter `from_desugaring` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:17
|
||||||
|
|
|
|
||||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
warning: there is no parameter `direct` on trait `Baz`
|
warning: there is no parameter `direct` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:34
|
||||||
|
|
|
|
||||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
warning: there is no parameter `cause` on trait `Baz`
|
warning: there is no parameter `cause` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:42
|
||||||
|
|
|
|
||||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
warning: there is no parameter `integral` on trait `Baz`
|
warning: there is no parameter `integral` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:49
|
||||||
|
|
|
|
||||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
warning: there is no parameter `integer` on trait `Baz`
|
warning: there is no parameter `integer` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:59
|
||||||
|
|
|
|
||||||
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
warning: there is no parameter `float` on trait `Baz`
|
warning: there is no parameter `float` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:15
|
||||||
|
|
|
|
||||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
warning: there is no parameter `_Self` on trait `Baz`
|
warning: there is no parameter `_Self` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:22
|
||||||
|
|
|
|
||||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
warning: there is no parameter `crate_local` on trait `Baz`
|
warning: there is no parameter `crate_local` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:29
|
||||||
|
|
|
|
||||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
warning: there is no parameter `Trait` on trait `Baz`
|
warning: there is no parameter `Trait` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:42
|
||||||
|
|
|
|
||||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
warning: there is no parameter `ItemContext` on trait `Baz`
|
warning: there is no parameter `ItemContext` on trait `Baz`
|
||||||
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:5
|
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:49
|
||||||
|
|
|
|
||||||
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
@ -47,10 +47,10 @@ LL | #[diagnostic::on_unimplemented]
|
|||||||
= help: at least one of the `message`, `note` and `label` options are expected
|
= help: at least one of the `message`, `note` and `label` options are expected
|
||||||
|
|
||||||
warning: there is no parameter `DoesNotExist` on trait `Test`
|
warning: there is no parameter `DoesNotExist` on trait `Test`
|
||||||
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:32
|
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:44
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")]
|
LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
|
|
||||||
@ -167,10 +167,10 @@ LL | fn take_whatever(_: impl Whatever) {}
|
|||||||
| ^^^^^^^^ required by this bound in `take_whatever`
|
| ^^^^^^^^ required by this bound in `take_whatever`
|
||||||
|
|
||||||
warning: there is no parameter `DoesNotExist` on trait `Test`
|
warning: there is no parameter `DoesNotExist` on trait `Test`
|
||||||
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:32
|
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:44
|
||||||
|
|
|
|
||||||
LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")]
|
LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: expect either a generic argument name or `{Self}` as format argument
|
= help: expect either a generic argument name or `{Self}` as format argument
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
@ -20,12 +20,12 @@ trait BadAnnotation1
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
#[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"]
|
#[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"]
|
||||||
//~^ ERROR there is no parameter `C` on trait `BadAnnotation2`
|
//~^ ERROR cannot find parameter C on this trait
|
||||||
trait BadAnnotation2<A,B>
|
trait BadAnnotation2<A,B>
|
||||||
{}
|
{}
|
||||||
|
|
||||||
#[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"]
|
#[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"]
|
||||||
//~^ ERROR only named generic parameters are allowed
|
//~^ ERROR positional format arguments are not allowed here
|
||||||
trait BadAnnotation3<A,B>
|
trait BadAnnotation3<A,B>
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -11,17 +11,17 @@ LL | #[rustc_on_unimplemented = "message"]
|
|||||||
LL | #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")]
|
LL | #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")]
|
||||||
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
error[E0230]: there is no parameter `C` on trait `BadAnnotation2`
|
error[E0230]: cannot find parameter C on this trait
|
||||||
--> $DIR/bad-annotation.rs:22:1
|
--> $DIR/bad-annotation.rs:22:90
|
||||||
|
|
|
|
||||||
LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"]
|
LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^
|
||||||
|
|
||||||
error[E0231]: only named generic parameters are allowed
|
error[E0231]: positional format arguments are not allowed here
|
||||||
--> $DIR/bad-annotation.rs:27:1
|
--> $DIR/bad-annotation.rs:27:90
|
||||||
|
|
|
|
||||||
LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"]
|
LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^
|
||||||
|
|
||||||
error[E0232]: this attribute must have a valid value
|
error[E0232]: this attribute must have a valid value
|
||||||
--> $DIR/bad-annotation.rs:32:26
|
--> $DIR/bad-annotation.rs:32:26
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
#![feature(rustc_attrs)]
|
|
||||||
|
|
||||||
trait Foo<A> {
|
|
||||||
fn foo(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rustc_on_unimplemented = "an impl did not match: {A} {B} {C}"]
|
|
||||||
impl<A, B, C> Foo<A> for (A, B, C) {
|
|
||||||
fn foo(self) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
Foo::<usize>::foo((1i32, 1i32, 1i32));
|
|
||||||
//~^ ERROR the trait bound `(i32, i32, i32): Foo<usize>` is not satisfied
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
error[E0277]: the trait bound `(i32, i32, i32): Foo<usize>` is not satisfied
|
|
||||||
--> $DIR/impl-substs.rs:13:23
|
|
||||||
|
|
|
||||||
LL | Foo::<usize>::foo((1i32, 1i32, 1i32));
|
|
||||||
| ----------------- ^^^^^^^^^^^^^^^^^^ an impl did not match: usize _ _
|
|
||||||
| |
|
|
||||||
| required by a bound introduced by this call
|
|
||||||
|
|
|
||||||
= help: the trait `Foo<usize>` is not implemented for `(i32, i32, i32)`
|
|
||||||
but trait `Foo<i32>` is implemented for it
|
|
||||||
= help: for that trait implementation, expected `i32`, found `usize`
|
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0277`.
|
|
@ -1,8 +0,0 @@
|
|||||||
#![feature(rustc_attrs)]
|
|
||||||
|
|
||||||
trait Foo {}
|
|
||||||
|
|
||||||
#[rustc_on_unimplemented] //~ ERROR malformed `rustc_on_unimplemented` attribute input
|
|
||||||
impl Foo for u32 {}
|
|
||||||
|
|
||||||
fn main() {}
|
|
@ -1,15 +0,0 @@
|
|||||||
error: malformed `rustc_on_unimplemented` attribute input
|
|
||||||
--> $DIR/issue-104140.rs:5:1
|
|
||||||
|
|
|
||||||
LL | #[rustc_on_unimplemented]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
help: the following are the possible correct uses
|
|
||||||
|
|
|
||||||
LL | #[rustc_on_unimplemented = "message"]
|
|
||||||
| +++++++++++
|
|
||||||
LL | #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")]
|
|
||||||
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
|||||||
// Test if the on_unimplemented message override works
|
|
||||||
|
|
||||||
#![feature(rustc_attrs)]
|
|
||||||
|
|
||||||
|
|
||||||
struct Foo<T>(T);
|
|
||||||
struct Bar<T>(T);
|
|
||||||
|
|
||||||
#[rustc_on_unimplemented = "trait message"]
|
|
||||||
trait Index<Idx: ?Sized> {
|
|
||||||
type Output: ?Sized;
|
|
||||||
fn index(&self, index: Idx) -> &Self::Output;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rustc_on_unimplemented = "on impl for Foo"]
|
|
||||||
impl Index<Foo<usize>> for [i32] {
|
|
||||||
type Output = i32;
|
|
||||||
fn index(&self, _index: Foo<usize>) -> &i32 {
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rustc_on_unimplemented = "on impl for Bar"]
|
|
||||||
impl Index<Bar<usize>> for [i32] {
|
|
||||||
type Output = i32;
|
|
||||||
fn index(&self, _index: Bar<usize>) -> &i32 {
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
Index::index(&[] as &[i32], 2u32);
|
|
||||||
//~^ ERROR E0277
|
|
||||||
//~| ERROR E0277
|
|
||||||
Index::index(&[] as &[i32], Foo(2u32));
|
|
||||||
//~^ ERROR E0277
|
|
||||||
//~| ERROR E0277
|
|
||||||
Index::index(&[] as &[i32], Bar(2u32));
|
|
||||||
//~^ ERROR E0277
|
|
||||||
//~| ERROR E0277
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
error[E0277]: the trait bound `[i32]: Index<u32>` is not satisfied
|
|
||||||
--> $DIR/multiple-impls.rs:33:33
|
|
||||||
|
|
|
||||||
LL | Index::index(&[] as &[i32], 2u32);
|
|
||||||
| ------------ ^^^^ trait message
|
|
||||||
| |
|
|
||||||
| required by a bound introduced by this call
|
|
||||||
|
|
|
||||||
= help: the trait `Index<u32>` is not implemented for `[i32]`
|
|
||||||
= help: the following other types implement trait `Index<Idx>`:
|
|
||||||
`[i32]` implements `Index<Bar<usize>>`
|
|
||||||
`[i32]` implements `Index<Foo<usize>>`
|
|
||||||
|
|
||||||
error[E0277]: the trait bound `[i32]: Index<Foo<u32>>` is not satisfied
|
|
||||||
--> $DIR/multiple-impls.rs:36:33
|
|
||||||
|
|
|
||||||
LL | Index::index(&[] as &[i32], Foo(2u32));
|
|
||||||
| ------------ ^^^^^^^^^ on impl for Foo
|
|
||||||
| |
|
|
||||||
| required by a bound introduced by this call
|
|
||||||
|
|
|
||||||
= help: the trait `Index<Foo<u32>>` is not implemented for `[i32]`
|
|
||||||
= help: the following other types implement trait `Index<Idx>`:
|
|
||||||
`[i32]` implements `Index<Bar<usize>>`
|
|
||||||
`[i32]` implements `Index<Foo<usize>>`
|
|
||||||
|
|
||||||
error[E0277]: the trait bound `[i32]: Index<Bar<u32>>` is not satisfied
|
|
||||||
--> $DIR/multiple-impls.rs:39:33
|
|
||||||
|
|
|
||||||
LL | Index::index(&[] as &[i32], Bar(2u32));
|
|
||||||
| ------------ ^^^^^^^^^ on impl for Bar
|
|
||||||
| |
|
|
||||||
| required by a bound introduced by this call
|
|
||||||
|
|
|
||||||
= help: the trait `Index<Bar<u32>>` is not implemented for `[i32]`
|
|
||||||
= help: the following other types implement trait `Index<Idx>`:
|
|
||||||
`[i32]` implements `Index<Bar<usize>>`
|
|
||||||
`[i32]` implements `Index<Foo<usize>>`
|
|
||||||
|
|
||||||
error[E0277]: the trait bound `[i32]: Index<u32>` is not satisfied
|
|
||||||
--> $DIR/multiple-impls.rs:33:5
|
|
||||||
|
|
|
||||||
LL | Index::index(&[] as &[i32], 2u32);
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trait message
|
|
||||||
|
|
|
||||||
= help: the trait `Index<u32>` is not implemented for `[i32]`
|
|
||||||
= help: the following other types implement trait `Index<Idx>`:
|
|
||||||
`[i32]` implements `Index<Bar<usize>>`
|
|
||||||
`[i32]` implements `Index<Foo<usize>>`
|
|
||||||
|
|
||||||
error[E0277]: the trait bound `[i32]: Index<Foo<u32>>` is not satisfied
|
|
||||||
--> $DIR/multiple-impls.rs:36:5
|
|
||||||
|
|
|
||||||
LL | Index::index(&[] as &[i32], Foo(2u32));
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ on impl for Foo
|
|
||||||
|
|
|
||||||
= help: the trait `Index<Foo<u32>>` is not implemented for `[i32]`
|
|
||||||
= help: the following other types implement trait `Index<Idx>`:
|
|
||||||
`[i32]` implements `Index<Bar<usize>>`
|
|
||||||
`[i32]` implements `Index<Foo<usize>>`
|
|
||||||
|
|
||||||
error[E0277]: the trait bound `[i32]: Index<Bar<u32>>` is not satisfied
|
|
||||||
--> $DIR/multiple-impls.rs:39:5
|
|
||||||
|
|
|
||||||
LL | Index::index(&[] as &[i32], Bar(2u32));
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ on impl for Bar
|
|
||||||
|
|
|
||||||
= help: the trait `Index<Bar<u32>>` is not implemented for `[i32]`
|
|
||||||
= help: the following other types implement trait `Index<Idx>`:
|
|
||||||
`[i32]` implements `Index<Bar<usize>>`
|
|
||||||
`[i32]` implements `Index<Foo<usize>>`
|
|
||||||
|
|
||||||
error: aborting due to 6 previous errors
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0277`.
|
|
@ -1,25 +0,0 @@
|
|||||||
// Test if the on_unimplemented message override works
|
|
||||||
|
|
||||||
#![feature(rustc_attrs)]
|
|
||||||
|
|
||||||
|
|
||||||
#[rustc_on_unimplemented = "invalid"]
|
|
||||||
trait Index<Idx: ?Sized> {
|
|
||||||
type Output: ?Sized;
|
|
||||||
fn index(&self, index: Idx) -> &Self::Output;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rustc_on_unimplemented = "a usize is required to index into a slice"]
|
|
||||||
impl Index<usize> for [i32] {
|
|
||||||
type Output = i32;
|
|
||||||
fn index(&self, index: usize) -> &i32 {
|
|
||||||
&self[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
Index::<u32>::index(&[1, 2, 3] as &[i32], 2u32);
|
|
||||||
//~^ ERROR E0277
|
|
||||||
//~| ERROR E0277
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
error[E0277]: the trait bound `[i32]: Index<u32>` is not satisfied
|
|
||||||
--> $DIR/on-impl.rs:22:47
|
|
||||||
|
|
|
||||||
LL | Index::<u32>::index(&[1, 2, 3] as &[i32], 2u32);
|
|
||||||
| ------------------- ^^^^ a usize is required to index into a slice
|
|
||||||
| |
|
|
||||||
| required by a bound introduced by this call
|
|
||||||
|
|
|
||||||
= help: the trait `Index<u32>` is not implemented for `[i32]`
|
|
||||||
but trait `Index<usize>` is implemented for it
|
|
||||||
= help: for that trait implementation, expected `usize`, found `u32`
|
|
||||||
|
|
||||||
error[E0277]: the trait bound `[i32]: Index<u32>` is not satisfied
|
|
||||||
--> $DIR/on-impl.rs:22:5
|
|
||||||
|
|
|
||||||
LL | Index::<u32>::index(&[1, 2, 3] as &[i32], 2u32);
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ a usize is required to index into a slice
|
|
||||||
|
|
|
||||||
= help: the trait `Index<u32>` is not implemented for `[i32]`
|
|
||||||
but trait `Index<usize>` is implemented for it
|
|
||||||
= help: for that trait implementation, expected `usize`, found `u32`
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0277`.
|
|
14
tests/ui/on-unimplemented/use_self_no_underscore.rs
Normal file
14
tests/ui/on-unimplemented/use_self_no_underscore.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
|
#[rustc_on_unimplemented(on(
|
||||||
|
all(A = "{integer}", any(Self = "[{integral}; _]",)),
|
||||||
|
message = "an array of type `{Self}` cannot be built directly from an iterator",
|
||||||
|
))]
|
||||||
|
pub trait FromIterator<A>: Sized {
|
||||||
|
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self;
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let iter = 0..42_8;
|
||||||
|
let x: [u8; 8] = FromIterator::from_iter(iter);
|
||||||
|
//~^ ERROR an array of type `[u8; 8]` cannot be built directly from an iterator
|
||||||
|
}
|
15
tests/ui/on-unimplemented/use_self_no_underscore.stderr
Normal file
15
tests/ui/on-unimplemented/use_self_no_underscore.stderr
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
error[E0277]: an array of type `[u8; 8]` cannot be built directly from an iterator
|
||||||
|
--> $DIR/use_self_no_underscore.rs:12:22
|
||||||
|
|
|
||||||
|
LL | let x: [u8; 8] = FromIterator::from_iter(iter);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromIterator<{integer}>` is not implemented for `[u8; 8]`
|
||||||
|
|
|
||||||
|
help: this trait has no implementations, consider adding one
|
||||||
|
--> $DIR/use_self_no_underscore.rs:7:1
|
||||||
|
|
|
||||||
|
LL | pub trait FromIterator<A>: Sized {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Reference in New Issue
Block a user