Write the format string parserand split it from conditions parser
This commit is contained in:
parent
199ee40843
commit
2007c8994d
11 changed files with 613 additions and 360 deletions
|
@ -372,6 +372,7 @@ symbols! {
|
|||
SyncUnsafeCell,
|
||||
T,
|
||||
Target,
|
||||
This,
|
||||
ToOwned,
|
||||
ToString,
|
||||
TokenStream,
|
||||
|
|
|
@ -2,6 +2,7 @@ pub mod ambiguity;
|
|||
pub mod call_kind;
|
||||
mod fulfillment_errors;
|
||||
pub mod on_unimplemented;
|
||||
pub mod on_unimplemented_condition;
|
||||
pub mod on_unimplemented_format;
|
||||
mod overflow;
|
||||
pub mod suggestions;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::iter;
|
||||
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::{ErrorGuaranteed, struct_span_code_err};
|
||||
|
@ -10,35 +10,21 @@ use rustc_hir::{AttrArgs, Attribute};
|
|||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::print::PrintTraitRefExt as _;
|
||||
use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt};
|
||||
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
|
||||
use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
|
||||
use rustc_span::{Span, Symbol, kw, sym};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use tracing::{debug, info};
|
||||
use {rustc_attr_parsing as attr, rustc_hir as hir};
|
||||
|
||||
use super::{ObligationCauseCode, PredicateObligation};
|
||||
use crate::error_reporting::TypeErrCtxt;
|
||||
use crate::error_reporting::traits::on_unimplemented_condition::Condition;
|
||||
use crate::error_reporting::traits::on_unimplemented_format::errors::*;
|
||||
use crate::error_reporting::traits::on_unimplemented_format::{Ctx, FormatString};
|
||||
use crate::errors::{
|
||||
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
|
||||
};
|
||||
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> {
|
||||
fn impl_similar_to(
|
||||
&self,
|
||||
|
@ -275,6 +261,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
}));
|
||||
|
||||
flags.push((sym::This, Some(self.tcx.def_path_str(trait_pred.trait_ref.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)
|
||||
} else {
|
||||
|
@ -283,19 +271,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Represents a format string in a on_unimplemented attribute,
|
||||
/// like the "content" in `#[diagnostic::on_unimplemented(message = "content")]`
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OnUnimplementedFormatString {
|
||||
symbol: Symbol,
|
||||
span: Span,
|
||||
is_diagnostic_namespace_variant: bool,
|
||||
/// Symbol of the format string, i.e. `"content"`
|
||||
pub symbol: Symbol,
|
||||
///The span of the format string, i.e. `"content"`
|
||||
pub span: Span,
|
||||
pub is_diagnostic_namespace_variant: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OnUnimplementedDirective {
|
||||
pub condition: Option<MetaItemInner>,
|
||||
pub condition: Option<Condition>,
|
||||
pub subcommands: Vec<OnUnimplementedDirective>,
|
||||
pub message: Option<OnUnimplementedFormatString>,
|
||||
pub label: Option<OnUnimplementedFormatString>,
|
||||
pub message: Option<(Span, OnUnimplementedFormatString)>,
|
||||
pub label: Option<(Span, OnUnimplementedFormatString)>,
|
||||
pub notes: Vec<OnUnimplementedFormatString>,
|
||||
pub parent_label: Option<OnUnimplementedFormatString>,
|
||||
pub append_const_msg: Option<AppendConstMessage>,
|
||||
|
@ -332,12 +324,12 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
let mut errored = None;
|
||||
let mut item_iter = items.iter();
|
||||
|
||||
let parse_value = |value_str, value_span| {
|
||||
let parse_value = |value_str, span| {
|
||||
OnUnimplementedFormatString::try_parse(
|
||||
tcx,
|
||||
item_def_id,
|
||||
value_str,
|
||||
value_span,
|
||||
span,
|
||||
is_diagnostic_namespace_variant,
|
||||
)
|
||||
.map(Some)
|
||||
|
@ -359,7 +351,7 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
}
|
||||
true
|
||||
});
|
||||
Some(cond.clone())
|
||||
Some(Condition { inner: cond.clone() })
|
||||
};
|
||||
|
||||
let mut message = None;
|
||||
|
@ -369,24 +361,36 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
let mut subcommands = vec![];
|
||||
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 {
|
||||
if item.has_name(sym::message) && message.is_none() {
|
||||
if let Some(message_) = item.value_str() {
|
||||
message = parse_value(message_, item.span())?;
|
||||
if let Some((message_, span)) = get_value_and_span(item, sym::message)
|
||||
&& message.is_none()
|
||||
{
|
||||
message = parse_value(message_, span)?.map(|l| (item.span(), l));
|
||||
continue;
|
||||
} else if let Some((label_, span)) = get_value_and_span(item, sym::label)
|
||||
&& label.is_none()
|
||||
{
|
||||
label = parse_value(label_, span)?.map(|l| (item.span(), l));
|
||||
continue;
|
||||
} else if let Some((note_, span)) = get_value_and_span(item, sym::note) {
|
||||
if let Some(note) = parse_value(note_, span)? {
|
||||
notes.push(note);
|
||||
continue;
|
||||
}
|
||||
} else if item.has_name(sym::label) && label.is_none() {
|
||||
if let Some(label_) = item.value_str() {
|
||||
label = parse_value(label_, item.span())?;
|
||||
continue;
|
||||
}
|
||||
} else if item.has_name(sym::note) {
|
||||
if let Some(note_) = item.value_str() {
|
||||
if let Some(note) = parse_value(note_, item.span())? {
|
||||
notes.push(note);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if item.has_name(sym::parent_label)
|
||||
&& parent_label.is_none()
|
||||
&& !is_diagnostic_namespace_variant
|
||||
|
@ -479,15 +483,15 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||
tcx,
|
||||
item_def_id,
|
||||
directive.message.as_ref().map(|f| f.span),
|
||||
aggr.message.as_ref().map(|f| f.span),
|
||||
directive.message.as_ref().map(|f| f.0),
|
||||
aggr.message.as_ref().map(|f| f.0),
|
||||
"message",
|
||||
);
|
||||
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||
tcx,
|
||||
item_def_id,
|
||||
directive.label.as_ref().map(|f| f.span),
|
||||
aggr.label.as_ref().map(|f| f.span),
|
||||
directive.label.as_ref().map(|f| f.0),
|
||||
aggr.label.as_ref().map(|f| f.0),
|
||||
"label",
|
||||
);
|
||||
IgnoredDiagnosticOption::maybe_emit_warning(
|
||||
|
@ -561,13 +565,16 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
condition: None,
|
||||
message: None,
|
||||
subcommands: vec![],
|
||||
label: Some(OnUnimplementedFormatString::try_parse(
|
||||
tcx,
|
||||
item_def_id,
|
||||
value,
|
||||
label: Some((
|
||||
attr.span(),
|
||||
is_diagnostic_namespace_variant,
|
||||
)?),
|
||||
OnUnimplementedFormatString::try_parse(
|
||||
tcx,
|
||||
item_def_id,
|
||||
value,
|
||||
attr.value_span().unwrap_or(attr.span()),
|
||||
is_diagnostic_namespace_variant,
|
||||
)?,
|
||||
)),
|
||||
notes: Vec::new(),
|
||||
parent_label: None,
|
||||
append_const_msg: None,
|
||||
|
@ -643,27 +650,7 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
for command in self.subcommands.iter().chain(Some(self)).rev() {
|
||||
debug!(?command);
|
||||
if let Some(ref condition) = command.condition
|
||||
&& !attr::eval_condition(condition, &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!(
|
||||
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))
|
||||
})
|
||||
&& !condition.matches_predicate(tcx, options, &options_map)
|
||||
{
|
||||
debug!("evaluate: skipping {:?} due to condition", command);
|
||||
continue;
|
||||
|
@ -687,8 +674,8 @@ impl<'tcx> OnUnimplementedDirective {
|
|||
}
|
||||
|
||||
OnUnimplementedNote {
|
||||
label: label.map(|l| l.format(tcx, trait_ref, &options_map, long_ty_file)),
|
||||
message: message.map(|m| m.format(tcx, trait_ref, &options_map, long_ty_file)),
|
||||
label: label.map(|l| l.1.format(tcx, trait_ref, &options_map, long_ty_file)),
|
||||
message: message.map(|m| m.1.format(tcx, trait_ref, &options_map, long_ty_file)),
|
||||
notes: notes
|
||||
.into_iter()
|
||||
.map(|n| n.format(tcx, trait_ref, &options_map, long_ty_file))
|
||||
|
@ -705,144 +692,65 @@ impl<'tcx> OnUnimplementedFormatString {
|
|||
tcx: TyCtxt<'tcx>,
|
||||
item_def_id: DefId,
|
||||
from: Symbol,
|
||||
value_span: Span,
|
||||
span: Span,
|
||||
is_diagnostic_namespace_variant: bool,
|
||||
) -> Result<Self, ErrorGuaranteed> {
|
||||
let result = OnUnimplementedFormatString {
|
||||
symbol: from,
|
||||
span: value_span,
|
||||
is_diagnostic_namespace_variant,
|
||||
};
|
||||
let result =
|
||||
OnUnimplementedFormatString { symbol: from, span, is_diagnostic_namespace_variant };
|
||||
result.verify(tcx, item_def_id)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> {
|
||||
let trait_def_id = if tcx.is_trait(item_def_id) {
|
||||
item_def_id
|
||||
let trait_def_id = if tcx.is_trait(item_def_id) { item_def_id } else { return Ok(()) };
|
||||
|
||||
let ctx = if self.is_diagnostic_namespace_variant {
|
||||
Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }
|
||||
} else {
|
||||
tcx.trait_id_of_impl(item_def_id)
|
||||
.expect("expected `on_unimplemented` to correspond to a trait")
|
||||
Ctx::RustcOnUnimplemented { tcx, trait_def_id }
|
||||
};
|
||||
let trait_name = tcx.item_ident(trait_def_id);
|
||||
let generics = tcx.generics_of(item_def_id);
|
||||
let s = self.symbol.as_str();
|
||||
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
|
||||
|
||||
let mut result = Ok(());
|
||||
for token in &mut parser {
|
||||
match token {
|
||||
Piece::Lit(_) => (), // Normal string, no need to check it
|
||||
Piece::NextArgument(a) => {
|
||||
let format_spec = a.format;
|
||||
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())
|
||||
{
|
||||
|
||||
match FormatString::parse(self.symbol, self.span, &ctx) {
|
||||
// Warnings about format specifiers, deprecated parameters, wrong parameters etc.
|
||||
// In other words we'd like to let the author know, but we can still try to format the string later
|
||||
Ok(FormatString { warnings, .. }) => {
|
||||
for w in warnings {
|
||||
w.emit_warning(tcx, trait_def_id)
|
||||
}
|
||||
}
|
||||
// Errors from the underlying `rustc_parse_format::Parser`
|
||||
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 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,
|
||||
WrappedParserError { description: e.description, label: e.label },
|
||||
);
|
||||
}
|
||||
}
|
||||
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 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,
|
||||
UnknownFormatParameterForOnUnimplementedAttr {
|
||||
argument_name: s,
|
||||
trait_name,
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
result = Err(struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
self.span,
|
||||
E0230,
|
||||
"there is no parameter `{}` on {}",
|
||||
s,
|
||||
if trait_def_id == item_def_id {
|
||||
format!("trait `{trait_name}`")
|
||||
} else {
|
||||
"impl".to_string()
|
||||
}
|
||||
)
|
||||
.emit());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// `{:1}` and `{}` are not to be used
|
||||
Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
|
||||
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,
|
||||
DisallowedPositionalArgument,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let reported = struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
self.span,
|
||||
E0231,
|
||||
"only named generic parameters are allowed"
|
||||
)
|
||||
.emit();
|
||||
result = Err(reported);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let reported = struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
self.span,
|
||||
E0231,
|
||||
"{}",
|
||||
e.description,
|
||||
)
|
||||
.emit();
|
||||
result = Err(reported);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
@ -854,95 +762,26 @@ impl<'tcx> OnUnimplementedFormatString {
|
|||
options: &FxHashMap<Symbol, String>,
|
||||
long_ty_file: &mut Option<PathBuf>,
|
||||
) -> String {
|
||||
let name = tcx.item_name(trait_ref.def_id);
|
||||
let trait_str = tcx.def_path_str(trait_ref.def_id);
|
||||
let generics = tcx.generics_of(trait_ref.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 {
|
||||
trait_ref.args[param.index as usize].to_string()
|
||||
}
|
||||
}
|
||||
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();
|
||||
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
|
||||
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 {
|
||||
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
|
||||
// as the diagnostic namespace guarantees that malformed input cannot cause an error
|
||||
//
|
||||
// if we encounter any error while processing the format string
|
||||
// we don't want to show the potentially half assembled formatted string,
|
||||
// therefore we fall back to just showing the input string in this case
|
||||
//
|
||||
// The actual parser errors are emitted earlier
|
||||
// as lint warnings in OnUnimplementedFormatString::verify
|
||||
if self.is_diagnostic_namespace_variant && !parser.errors.is_empty() {
|
||||
String::from(s)
|
||||
let trait_def_id = trait_ref.def_id;
|
||||
let ctx = if self.is_diagnostic_namespace_variant {
|
||||
Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }
|
||||
} else {
|
||||
constructed_message
|
||||
Ctx::RustcOnUnimplemented { tcx, trait_def_id }
|
||||
};
|
||||
|
||||
if let Ok(s) = FormatString::parse(self.symbol, self.span, &ctx) {
|
||||
s.format(tcx, trait_ref, options, long_ty_file)
|
||||
} else {
|
||||
// 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 the format string
|
||||
// we don't want to show the potentially half assembled formatted string,
|
||||
// therefore we fall back to just showing the input string in this case
|
||||
//
|
||||
// The actual parser errors are emitted earlier
|
||||
// as lint warnings in OnUnimplementedFormatString::verify
|
||||
self.symbol.as_str().into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
use rustc_ast::MetaItemInner;
|
||||
use rustc_attr_parsing as attr;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
pub static ALLOWED_CONDITION_SYMBOLS: &[Symbol] = &[
|
||||
sym::from_desugaring,
|
||||
sym::direct,
|
||||
sym::cause,
|
||||
sym::integral,
|
||||
sym::integer_,
|
||||
sym::float,
|
||||
sym::_Self,
|
||||
sym::crate_local,
|
||||
];
|
||||
|
||||
#[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: &[(Symbol, Option<String>)],
|
||||
options_map: &FxHashMap<Symbol, String>,
|
||||
) -> 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!({
|
||||
let mut parser = Parser::new(v.as_str(), None, None, false, ParseMode::Format);
|
||||
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 options_map.get(&s) {
|
||||
Some(val) => val.to_string(),
|
||||
None => format!("{{{arg}}}"),
|
||||
}
|
||||
}
|
||||
Position::ArgumentImplicitlyIs(_) => String::from("{}"),
|
||||
Position::ArgumentIs(idx) => format!("{{{idx}}}"),
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
constructed_message
|
||||
})
|
||||
});
|
||||
|
||||
options.contains(&(cfg.name, value))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,3 +1,347 @@
|
|||
use std::fmt::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use errors::*;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
|
||||
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};
|
||||
|
||||
pub struct FormatString {
|
||||
input: Symbol,
|
||||
input_span: Span,
|
||||
pieces: Vec<Piece>,
|
||||
// the formatting string was parsed succesfully but with warnings
|
||||
pub warnings: Vec<FormatWarning>,
|
||||
}
|
||||
|
||||
enum Piece {
|
||||
Lit(String),
|
||||
Arg(FormatArg),
|
||||
}
|
||||
|
||||
pub enum FormatArg {
|
||||
// A generic parameter, like `{T}` if we're on the `From<T>` trait.
|
||||
GenericParam { generic_param: Symbol, span: Span },
|
||||
// `{Self}`
|
||||
SelfUpper,
|
||||
This,
|
||||
Trait,
|
||||
ItemContext,
|
||||
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 },
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatString {
|
||||
pub fn parse(input: Symbol, input_span: Span, ctx: &Ctx<'_>) -> 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, input_span);
|
||||
let arg = parse_arg(&arg, ctx, &mut warnings, input_span);
|
||||
pieces.push(Piece::Arg(arg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if parser.errors.is_empty() {
|
||||
Ok(FormatString { input, input_span, pieces, warnings })
|
||||
} else {
|
||||
Err(parser.errors)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format<'tcx>(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_ref: ty::TraitRef<'tcx>,
|
||||
options: &FxHashMap<Symbol, String>,
|
||||
long_ty_file: &mut Option<PathBuf>,
|
||||
) -> String {
|
||||
let generics = tcx.generics_of(trait_ref.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 {
|
||||
trait_ref.args[param.index as usize].to_string()
|
||||
}
|
||||
}
|
||||
GenericParamDefKind::Lifetime => return None,
|
||||
};
|
||||
let name = param.name;
|
||||
Some((name, value))
|
||||
})
|
||||
.collect::<FxHashMap<Symbol, 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, span }) => {
|
||||
// Should always be some but we can't raise errors here
|
||||
if let Some(value) = generic_map.get(&generic_param) {
|
||||
ret.push_str(value);
|
||||
} else if cfg!(debug_assertions) {
|
||||
span_bug!(*span, "invalid generic parameter");
|
||||
} else {
|
||||
let _ = ret.write_fmt(format_args!("{{{}}}", generic_param.as_str()));
|
||||
}
|
||||
}
|
||||
// `{Self}`
|
||||
Piece::Arg(FormatArg::SelfUpper) => {
|
||||
let Some(slf) = generic_map.get(&kw::SelfUpper) else {
|
||||
span_bug!(
|
||||
self.input_span,
|
||||
"broken format string {:?} for {:?}: \
|
||||
no argument matching `Self`",
|
||||
self.input,
|
||||
trait_ref,
|
||||
)
|
||||
};
|
||||
ret.push_str(&slf);
|
||||
}
|
||||
|
||||
// It's only `rustc_onunimplemented` from here
|
||||
Piece::Arg(FormatArg::This) => {
|
||||
let Some(this) = options.get(&sym::This) else {
|
||||
span_bug!(
|
||||
self.input_span,
|
||||
"broken format string {:?} for {:?}: \
|
||||
no argument matching This",
|
||||
self.input,
|
||||
trait_ref,
|
||||
)
|
||||
};
|
||||
ret.push_str(this);
|
||||
}
|
||||
Piece::Arg(FormatArg::Trait) => {
|
||||
let Some(this) = options.get(&sym::Trait) else {
|
||||
span_bug!(
|
||||
self.input_span,
|
||||
"broken format string {:?} for {:?}: \
|
||||
no argument matching Trait",
|
||||
self.input,
|
||||
trait_ref,
|
||||
)
|
||||
};
|
||||
ret.push_str(this);
|
||||
}
|
||||
Piece::Arg(FormatArg::ItemContext) => {
|
||||
let itemcontext = options.get(&sym::ItemContext);
|
||||
ret.push_str(itemcontext.unwrap_or(&String::new()));
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_arg(
|
||||
arg: &Argument<'_>,
|
||||
ctx: &Ctx<'_>,
|
||||
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, span }
|
||||
}
|
||||
|
||||
(_, 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() })
|
||||
}
|
||||
}
|
||||
|
||||
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_middle::ty::TyCtxt;
|
||||
|
|
|
@ -1,52 +1,52 @@
|
|||
warning: unmatched `}` found
|
||||
--> $DIR/broken_format.rs:2:32
|
||||
--> $DIR/broken_format.rs:2:42
|
||||
|
|
||||
LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
|
||||
|
||||
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 {}")]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
| ^
|
||||
|
|
||||
= 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
|
||||
--> $DIR/broken_format.rs:12:32
|
||||
--> $DIR/broken_format.rs:12:49
|
||||
|
|
||||
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
|
||||
|
||||
warning: invalid format specifier
|
||||
--> $DIR/broken_format.rs:17:32
|
||||
--> $DIR/broken_format.rs:17:42
|
||||
|
|
||||
LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: no format specifier are supported in this position
|
||||
|
||||
warning: expected `}`, found `!`
|
||||
--> $DIR/broken_format.rs:22:32
|
||||
--> $DIR/broken_format.rs:22:42
|
||||
|
|
||||
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
warning: unmatched `}` found
|
||||
--> $DIR/broken_format.rs:22:32
|
||||
--> $DIR/broken_format.rs:22:42
|
||||
|
|
||||
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
warning: unmatched `}` found
|
||||
--> $DIR/broken_format.rs:2:32
|
||||
--> $DIR/broken_format.rs:2:42
|
||||
|
|
||||
LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= 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`
|
||||
|
||||
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 {}")]
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
| ^
|
||||
|
|
||||
= 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`
|
||||
|
@ -98,10 +98,10 @@ LL | fn check_2(_: impl ImportantTrait2) {}
|
|||
| ^^^^^^^^^^^^^^^ required by this bound in `check_2`
|
||||
|
||||
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:}")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^
|
||||
|
|
||||
= 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`
|
||||
|
@ -126,10 +126,10 @@ LL | fn check_3(_: impl ImportantTrait3) {}
|
|||
| ^^^^^^^^^^^^^^^ required by this bound in `check_3`
|
||||
|
||||
warning: invalid format specifier
|
||||
--> $DIR/broken_format.rs:17:32
|
||||
--> $DIR/broken_format.rs:17:42
|
||||
|
|
||||
LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: no format specifier are supported in this position
|
||||
= 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`
|
||||
|
||||
warning: expected `}`, found `!`
|
||||
--> $DIR/broken_format.rs:22:32
|
||||
--> $DIR/broken_format.rs:22:42
|
||||
|
|
||||
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
warning: unmatched `}` found
|
||||
--> $DIR/broken_format.rs:22:32
|
||||
--> $DIR/broken_format.rs:22:42
|
||||
|
|
||||
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= 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
|
||||
|
||||
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}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
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}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
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}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
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}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
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}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
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}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
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}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
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}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
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}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
|
||||
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}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= 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`
|
||||
|
||||
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}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
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}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
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}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
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}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
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}",
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
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}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
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}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
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}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
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}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
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}"
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= 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
|
||||
|
||||
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}")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= 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`
|
||||
|
||||
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}")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= 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}>`"]
|
||||
//~^ ERROR there is no parameter `C` on trait `BadAnnotation2`
|
||||
//~^ WARNING there is no parameter `C` on trait `BadAnnotation2`
|
||||
trait BadAnnotation2<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>
|
||||
{}
|
||||
|
||||
|
|
|
@ -11,17 +11,22 @@ LL | #[rustc_on_unimplemented = "message"]
|
|||
LL | #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")]
|
||||
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
error[E0230]: there is no parameter `C` on trait `BadAnnotation2`
|
||||
--> $DIR/bad-annotation.rs:22:1
|
||||
warning: there is no parameter `C` on trait `BadAnnotation2`
|
||||
--> $DIR/bad-annotation.rs:22:90
|
||||
|
|
||||
LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^
|
||||
|
|
||||
= help: expect either a generic argument name or `{Self}` as format argument
|
||||
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
|
||||
|
||||
error[E0231]: only named generic parameters are allowed
|
||||
--> $DIR/bad-annotation.rs:27:1
|
||||
warning: positional format arguments are not allowed here
|
||||
--> $DIR/bad-annotation.rs:27:90
|
||||
|
|
||||
LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^
|
||||
|
|
||||
= help: only named format arguments with the name of one of the generic types are allowed in this context
|
||||
|
||||
error[E0232]: this attribute must have a valid value
|
||||
--> $DIR/bad-annotation.rs:32:26
|
||||
|
@ -77,7 +82,6 @@ LL | #[rustc_on_unimplemented(on(desugared, on(desugared, message="x")), message
|
|||
|
|
||||
= note: eg `#[rustc_on_unimplemented(message="foo")]`
|
||||
|
||||
error: aborting due to 10 previous errors
|
||||
error: aborting due to 8 previous errors; 2 warnings emitted
|
||||
|
||||
Some errors have detailed explanations: E0230, E0231, E0232.
|
||||
For more information about an error, try `rustc --explain E0230`.
|
||||
For more information about this error, try `rustc --explain E0232`.
|
||||
|
|
|
@ -2,7 +2,7 @@ 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 _ _
|
||||
| ----------------- ^^^^^^^^^^^^^^^^^^ an impl did not match: usize {B} {C}
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue