Rollup merge of #151558 - mejrs:port_on_unimplemented, r=jdonszelmann,jonathanbrouwer

Port diagnostic attributes

*[View all comments](https://triagebot.infra.rust-lang.org/gh-comments/rust-lang/rust/pull/151558)*

Ports all the diagnostic attributes: on_const, on_unimplemented and rustc_on_unimplemented.

I thought about migrating them one by one but that would leave a lot of code duplicated. So this PR looks big but it's mostly a lot of moving code around with slight modifications.

r? @JonathanBrouwer

cc @jdonszelmann feel free to review if you want, not sure which of you wants it.
This commit is contained in:
Jonathan Brouwer
2026-02-24 14:41:54 +01:00
committed by GitHub
51 changed files with 1730 additions and 2166 deletions
+1 -1
View File
@@ -3548,6 +3548,7 @@ dependencies = [
"rustc_lexer",
"rustc_macros",
"rustc_parse",
"rustc_parse_format",
"rustc_session",
"rustc_span",
"rustc_target",
@@ -4682,7 +4683,6 @@ dependencies = [
"rustc_macros",
"rustc_middle",
"rustc_next_trait_solver",
"rustc_parse_format",
"rustc_session",
"rustc_span",
"rustc_transmute",
+1
View File
@@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_macros = { path = "../rustc_macros" }
rustc_parse = { path = "../rustc_parse" }
rustc_parse_format = { path = "../rustc_parse_format" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
@@ -0,0 +1,536 @@
use std::ops::Range;
use rustc_errors::E0232;
use rustc_hir::AttrPath;
use rustc_hir::attrs::diagnostic::{
AppendConstMessage, Directive, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg,
Name, NameValue, OnUnimplementedCondition, Piece, Predicate,
};
use rustc_hir::lints::{AttributeLintKind, FormatWarning};
use rustc_macros::Diagnostic;
use rustc_parse_format::{
Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position,
};
use rustc_session::lint::builtin::{
MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
};
use rustc_span::{Ident, InnerSpan, Span, Symbol, kw, sym};
use thin_vec::{ThinVec, thin_vec};
use crate::context::{AcceptContext, Stage};
use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser};
pub(crate) mod do_not_recommend;
pub(crate) mod on_const;
pub(crate) mod on_unimplemented;
#[derive(Copy, Clone)]
pub(crate) enum Mode {
/// `#[rustc_on_unimplemented]`
RustcOnUnimplemented,
/// `#[diagnostic::on_unimplemented]`
DiagnosticOnUnimplemented,
/// `#[diagnostic::on_const]`
DiagnosticOnConst,
}
fn merge_directives<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
first: &mut Option<(Span, Directive)>,
later: (Span, Directive),
) {
if let Some((_, first)) = first {
if first.is_rustc_attr || later.1.is_rustc_attr {
cx.emit_err(DupesNotAllowed);
}
merge(cx, &mut first.message, later.1.message, sym::message);
merge(cx, &mut first.label, later.1.label, sym::label);
first.notes.extend(later.1.notes);
} else {
*first = Some(later);
}
}
fn merge<T, S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
first: &mut Option<(Span, T)>,
later: Option<(Span, T)>,
option_name: Symbol,
) {
match (first, later) {
(Some(_) | None, None) => {}
(Some((first_span, _)), Some((later_span, _))) => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::IgnoredDiagnosticOption {
first_span: *first_span,
later_span,
option_name,
},
later_span,
);
}
(first @ None, Some(later)) => {
first.get_or_insert(later);
}
}
}
fn parse_directive_items<'p, S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
mode: Mode,
items: impl Iterator<Item = &'p MetaItemOrLitParser>,
is_root: bool,
) -> Option<Directive> {
let condition = None;
let mut message: Option<(Span, _)> = None;
let mut label: Option<(Span, _)> = None;
let mut notes = ThinVec::new();
let mut parent_label = None;
let mut subcommands = ThinVec::new();
let mut append_const_msg = None;
for item in items {
let span = item.span();
macro malformed() {{
match mode {
Mode::RustcOnUnimplemented => {
cx.emit_err(NoValueInOnUnimplemented { span: item.span() });
}
Mode::DiagnosticOnUnimplemented => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MalformedOnUnimplementedAttr { span },
span,
);
}
Mode::DiagnosticOnConst => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MalformedOnConstAttr { span },
span,
);
}
}
continue;
}}
macro or_malformed($($code:tt)*) {{
let Some(ret) = (||{
Some($($code)*)
})() else {
malformed!()
};
ret
}}
macro duplicate($name: ident, $($first_span:tt)*) {{
match mode {
Mode::RustcOnUnimplemented => {
cx.emit_err(NoValueInOnUnimplemented { span: item.span() });
}
Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::IgnoredDiagnosticOption {
first_span: $($first_span)*,
later_span: span,
option_name: $name,
},
span,
);
}
}
}}
let item: &MetaItemParser = or_malformed!(item.meta_item()?);
let name = or_malformed!(item.ident()?).name;
// Some things like `message = "message"` must have a value.
// But with things like `append_const_msg` that is optional.
let value: Option<Ident> = match item.args().name_value() {
Some(nv) => Some(or_malformed!(nv.value_as_ident()?)),
None => None,
};
let mut parse_format = |input: Ident| {
let snippet = cx.sess.source_map().span_to_snippet(input.span).ok();
let is_snippet = snippet.is_some();
match parse_format_string(input.name, snippet, input.span, mode) {
Ok((f, warnings)) => {
for warning in warnings {
let (FormatWarning::InvalidSpecifier { span, .. }
| FormatWarning::PositionalArgument { span, .. }) = warning;
cx.emit_lint(
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
AttributeLintKind::MalformedDiagnosticFormat { warning },
span,
);
}
f
}
Err(e) => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
AttributeLintKind::DiagnosticWrappedParserError {
description: e.description,
label: e.label,
span: slice_span(input.span, e.span, is_snippet),
},
input.span,
);
// We could not parse the input, just use it as-is.
FormatString {
input: input.name,
span: input.span,
pieces: thin_vec![Piece::Lit(input.name)],
}
}
}
};
match (mode, name) {
(_, sym::message) => {
let value = or_malformed!(value?);
if let Some(message) = &message {
duplicate!(name, message.0)
} else {
message = Some((item.span(), parse_format(value)));
}
}
(_, sym::label) => {
let value = or_malformed!(value?);
if let Some(label) = &label {
duplicate!(name, label.0)
} else {
label = Some((item.span(), parse_format(value)));
}
}
(_, sym::note) => {
let value = or_malformed!(value?);
notes.push(parse_format(value))
}
(Mode::RustcOnUnimplemented, sym::append_const_msg) => {
append_const_msg = if let Some(msg) = value {
Some(AppendConstMessage::Custom(msg.name, item.span()))
} else {
Some(AppendConstMessage::Default)
}
}
(Mode::RustcOnUnimplemented, sym::parent_label) => {
let value = or_malformed!(value?);
if parent_label.is_none() {
parent_label = Some(parse_format(value));
} else {
duplicate!(name, span)
}
}
(Mode::RustcOnUnimplemented, sym::on) => {
if is_root {
let items = or_malformed!(item.args().list()?);
let mut iter = items.mixed();
let condition: &MetaItemOrLitParser = match iter.next() {
Some(c) => c,
None => {
cx.emit_err(InvalidOnClause::Empty { span });
continue;
}
};
let condition = parse_condition(condition);
if items.len() < 2 {
// Something like `#[rustc_on_unimplemented(on(.., /* nothing */))]`
// There's a condition but no directive behind it, this is a mistake.
malformed!();
}
let mut directive =
or_malformed!(parse_directive_items(cx, mode, iter, false)?);
match condition {
Ok(c) => {
directive.condition = Some(c);
subcommands.push(directive);
}
Err(e) => {
cx.emit_err(e);
}
}
} else {
malformed!();
}
}
_other => {
malformed!();
}
}
}
Some(Directive {
is_rustc_attr: matches!(mode, Mode::RustcOnUnimplemented),
condition,
subcommands,
message,
label,
notes,
parent_label,
append_const_msg,
})
}
pub(crate) fn parse_format_string(
input: Symbol,
snippet: Option<String>,
span: Span,
mode: Mode,
) -> Result<(FormatString, Vec<FormatWarning>), ParseError> {
let s = input.as_str();
let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic);
let pieces: Vec<_> = parser.by_ref().collect();
if let Some(err) = parser.errors.into_iter().next() {
return Err(err);
}
let mut warnings = Vec::new();
let pieces = pieces
.into_iter()
.map(|piece| match piece {
RpfPiece::Lit(lit) => Piece::Lit(Symbol::intern(lit)),
RpfPiece::NextArgument(arg) => {
warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal);
let arg = parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal);
Piece::Arg(arg)
}
})
.collect();
Ok((FormatString { input, pieces, span }, warnings))
}
fn parse_arg(
arg: &Argument<'_>,
mode: Mode,
warnings: &mut Vec<FormatWarning>,
input_span: Span,
is_source_literal: bool,
) -> FormatArg {
let span = slice_span(input_span, arg.position_span.clone(), is_source_literal);
match arg.position {
// Something like "hello {name}"
Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) {
// Only `#[rustc_on_unimplemented]` can use these
(Mode::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext,
(Mode::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This,
(Mode::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait,
// Any attribute can use these
(_, kw::SelfUpper) => FormatArg::SelfUpper,
(_, generic_param) => FormatArg::GenericParam { generic_param, span },
},
// `{:1}` and `{}` are ignored
Position::ArgumentIs(idx) => {
warnings.push(FormatWarning::PositionalArgument {
span,
help: format!("use `{{{idx}}}` to print a number in braces"),
});
FormatArg::AsIs(Symbol::intern(&format!("{{{idx}}}")))
}
Position::ArgumentImplicitlyIs(_) => {
warnings.push(FormatWarning::PositionalArgument {
span,
help: String::from("use `{{}}` to print empty braces"),
});
FormatArg::AsIs(sym::empty_braces)
}
}
}
/// `#[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,
is_source_literal: bool,
) {
if spec.ty != "" {
let span = spec
.ty_span
.as_ref()
.map(|inner| slice_span(input_span, inner.clone(), is_source_literal))
.unwrap_or(input_span);
warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() })
}
}
fn slice_span(input: Span, Range { start, end }: Range<usize>, is_source_literal: bool) -> Span {
if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input }
}
pub(crate) fn parse_condition(
input: &MetaItemOrLitParser,
) -> Result<OnUnimplementedCondition, InvalidOnClause> {
let span = input.span();
let pred = parse_predicate(input)?;
Ok(OnUnimplementedCondition { span, pred })
}
fn parse_predicate(input: &MetaItemOrLitParser) -> Result<Predicate, InvalidOnClause> {
let Some(meta_item) = input.meta_item() else {
return Err(InvalidOnClause::UnsupportedLiteral { span: input.span() });
};
let Some(predicate) = meta_item.ident() else {
return Err(InvalidOnClause::ExpectedIdentifier {
span: meta_item.path().span(),
path: meta_item.path().get_attribute_path(),
});
};
match meta_item.args() {
ArgParser::List(mis) => match predicate.name {
sym::any => Ok(Predicate::Any(parse_predicate_sequence(mis)?)),
sym::all => Ok(Predicate::All(parse_predicate_sequence(mis)?)),
sym::not => {
if let Some(single) = mis.single() {
Ok(Predicate::Not(Box::new(parse_predicate(single)?)))
} else {
Err(InvalidOnClause::ExpectedOnePredInNot { span: mis.span })
}
}
invalid_pred => {
Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred })
}
},
ArgParser::NameValue(p) => {
let Some(value) = p.value_as_ident() else {
return Err(InvalidOnClause::UnsupportedLiteral { span: p.args_span() });
};
let name = parse_name(predicate.name);
let value = parse_filter(value.name);
let kv = NameValue { name, value };
Ok(Predicate::Match(kv))
}
ArgParser::NoArgs => {
let flag = parse_flag(predicate)?;
Ok(Predicate::Flag(flag))
}
}
}
fn parse_predicate_sequence(
sequence: &MetaItemListParser,
) -> Result<ThinVec<Predicate>, InvalidOnClause> {
sequence.mixed().map(parse_predicate).collect()
}
fn parse_flag(Ident { name, span }: Ident) -> Result<Flag, InvalidOnClause> {
match name {
sym::crate_local => Ok(Flag::CrateLocal),
sym::direct => Ok(Flag::Direct),
sym::from_desugaring => Ok(Flag::FromDesugaring),
invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }),
}
}
fn parse_name(name: Symbol) -> Name {
match name {
kw::SelfUpper => Name::SelfUpper,
sym::from_desugaring => Name::FromDesugaring,
sym::cause => Name::Cause,
generic => Name::GenericArg(generic),
}
}
fn parse_filter(input: Symbol) -> FilterFormatString {
let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic)
.map(|p| match p {
RpfPiece::Lit(s) => LitOrArg::Lit(Symbol::intern(s)),
// We just ignore formatspecs here
RpfPiece::NextArgument(a) => match a.position {
// In `TypeErrCtxt::on_unimplemented_note` we substitute `"{integral}"` even
// if the integer type has been resolved, to allow targeting all integers.
// `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet,
// from the `Display` impl of `InferTy` to be precise.
//
// Don't try to format these later!
Position::ArgumentNamed(arg @ ("integer" | "integral" | "float")) => {
LitOrArg::Lit(Symbol::intern(&format!("{{{arg}}}")))
}
Position::ArgumentNamed(arg) => LitOrArg::Arg(Symbol::intern(arg)),
Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(sym::empty_braces),
Position::ArgumentIs(idx) => LitOrArg::Lit(Symbol::intern(&format!("{{{idx}}}"))),
},
})
.collect();
FilterFormatString { pieces }
}
#[derive(Diagnostic)]
pub(crate) enum InvalidOnClause {
#[diag("empty `on`-clause in `#[rustc_on_unimplemented]`", code = E0232)]
Empty {
#[primary_span]
#[label("empty `on`-clause here")]
span: Span,
},
#[diag("expected a single predicate in `not(..)`", code = E0232)]
ExpectedOnePredInNot {
#[primary_span]
#[label("unexpected quantity of predicates here")]
span: Span,
},
#[diag("literals inside `on`-clauses are not supported", code = E0232)]
UnsupportedLiteral {
#[primary_span]
#[label("unexpected literal here")]
span: Span,
},
#[diag("expected an identifier inside this `on`-clause", code = E0232)]
ExpectedIdentifier {
#[primary_span]
#[label("expected an identifier here, not `{$path}`")]
span: Span,
path: AttrPath,
},
#[diag("this predicate is invalid", code = E0232)]
InvalidPredicate {
#[primary_span]
#[label("expected one of `any`, `all` or `not` here, not `{$invalid_pred}`")]
span: Span,
invalid_pred: Symbol,
},
#[diag("invalid flag in `on`-clause", code = E0232)]
InvalidFlag {
#[primary_span]
#[label(
"expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}`"
)]
span: Span,
invalid_flag: Symbol,
},
}
#[derive(Diagnostic)]
#[diag("this attribute must have a value", code = E0232)]
#[note("e.g. `#[rustc_on_unimplemented(message=\"foo\")]`")]
pub(crate) struct NoValueInOnUnimplemented {
#[primary_span]
#[label("expected value here")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(
"using multiple `rustc_on_unimplemented` (or mixing it with `diagnostic::on_unimplemented`) is not supported"
)]
pub(crate) struct DupesNotAllowed;
@@ -0,0 +1,65 @@
use rustc_hir::attrs::diagnostic::Directive;
use rustc_hir::lints::AttributeLintKind;
use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES;
use crate::attributes::diagnostic::*;
use crate::attributes::prelude::*;
#[derive(Default)]
pub(crate) struct OnConstParser {
span: Option<Span>,
directive: Option<(Span, Directive)>,
}
impl<S: Stage> AttributeParser<S> for OnConstParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
&[sym::diagnostic, sym::on_const],
template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]),
|this, cx, args| {
if !cx.features().diagnostic_on_const() {
return;
}
let span = cx.attr_span;
this.span = Some(span);
let items = match args {
ArgParser::List(items) if items.len() != 0 => items,
ArgParser::NoArgs | ArgParser::List(_) => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MissingOptionsForOnConst,
span,
);
return;
}
ArgParser::NameValue(_) => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MalformedOnConstAttr { span },
span,
);
return;
}
};
let Some(directive) =
parse_directive_items(cx, Mode::DiagnosticOnConst, items.mixed(), true)
else {
return;
};
merge_directives(cx, &mut this.directive, (span, directive));
},
)];
//FIXME Still checked in `check_attr.rs`
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if let Some(span) = self.span {
Some(AttributeKind::OnConst { span, directive: self.directive.map(|d| Box::new(d.1)) })
} else {
None
}
}
}
@@ -0,0 +1,88 @@
use rustc_hir::attrs::diagnostic::Directive;
use rustc_hir::lints::AttributeLintKind;
use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES;
use crate::attributes::diagnostic::*;
use crate::attributes::prelude::*;
#[derive(Default)]
pub(crate) struct OnUnimplementedParser {
span: Option<Span>,
directive: Option<(Span, Directive)>,
}
impl OnUnimplementedParser {
fn parse<'sess, S: Stage>(
&mut self,
cx: &mut AcceptContext<'_, 'sess, S>,
args: &ArgParser,
mode: Mode,
) {
let span = cx.attr_span;
self.span = Some(span);
// If target is not a trait, returning early will make `finalize` emit a
// `AttributeKind::OnUnimplemented {span, directive: None }`, to prevent it being
// accidentally used on non-trait items like trait aliases.
if !matches!(cx.target, Target::Trait) {
// Lint later emitted in check_attr
return;
}
let items = match args {
ArgParser::List(items) if items.len() != 0 => items,
ArgParser::NoArgs | ArgParser::List(_) => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MissingOptionsForOnUnimplemented,
span,
);
return;
}
ArgParser::NameValue(_) => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MalformedOnUnimplementedAttr { span },
span,
);
return;
}
};
if let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) {
merge_directives(cx, &mut self.directive, (span, directive));
};
}
}
impl<S: Stage> AttributeParser<S> for OnUnimplementedParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[
(
&[sym::diagnostic, sym::on_unimplemented],
template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]),
|this, cx, args| {
this.parse(cx, args, Mode::DiagnosticOnUnimplemented);
},
),
(
&[sym::rustc_on_unimplemented],
template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]),
|this, cx, args| {
this.parse(cx, args, Mode::RustcOnUnimplemented);
},
),
];
//FIXME attribute is not parsed for non-traits but diagnostics are issued in `check_attr.rs`
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if let Some(span) = self.span {
Some(AttributeKind::OnUnimplemented {
span,
directive: self.directive.map(|d| Box::new(d.1)),
})
} else {
None
}
}
}
@@ -39,7 +39,7 @@
pub(crate) mod crate_level;
pub(crate) mod debugger;
pub(crate) mod deprecation;
pub(crate) mod do_not_recommend;
pub(crate) mod diagnostic;
pub(crate) mod doc;
pub(crate) mod dummy;
pub(crate) mod inline;
+5 -1
View File
@@ -26,7 +26,9 @@
use crate::attributes::crate_level::*;
use crate::attributes::debugger::*;
use crate::attributes::deprecation::*;
use crate::attributes::do_not_recommend::*;
use crate::attributes::diagnostic::do_not_recommend::*;
use crate::attributes::diagnostic::on_const::*;
use crate::attributes::diagnostic::on_unimplemented::*;
use crate::attributes::doc::*;
use crate::attributes::dummy::*;
use crate::attributes::inline::*;
@@ -147,6 +149,8 @@ mod late {
DocParser,
MacroUseParser,
NakedParser,
OnConstParser,
OnUnimplementedParser,
RustcCguTestAttributeParser,
StabilityParser,
UsedParser,
@@ -1,26 +1,4 @@
The `#[rustc_on_unimplemented]` attribute lets you specify a custom error
message for when a particular trait isn't implemented on a type placed in a
position that needs that trait. For example, when the following code is
compiled:
#### Note: this error code is no longer emitted by the compiler.
```compile_fail,E0230
#![feature(rustc_attrs)]
#![allow(internal_features)]
#[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{B}>`"] // error
trait BadAnnotation<A> {}
```
There will be an error about `bool` not implementing `Index<u8>`, followed by a
note saying "the type `bool` cannot be indexed by `u8`".
As you can see, you can specify type parameters in curly braces for
instantiation with the actual types (using the regular format string syntax) in
a given situation. Furthermore, `{Self}` will be instantiated to the type (in
this case, `bool`) that we tried to use.
This error appears when the curly braces contain an identifier which doesn't
match with any of the type parameters or the string `Self`. This might happen
if you misspelled a type parameter, or if you intended to use literal curly
braces. If it is the latter, escape the curly braces with a second curly brace
of the same type; e.g., a literal `{` is `{{`.
The `#[rustc_on_unimplemented]` attribute used to raise this error for various
misuses of the attribute; these are now warnings.
@@ -1,24 +1 @@
The `#[rustc_on_unimplemented]` attribute lets you specify a custom error
message for when a particular trait isn't implemented on a type placed in a
position that needs that trait. For example, when the following code is
compiled:
```compile_fail,E0231
#![feature(rustc_attrs)]
#![allow(internal_features)]
#[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{}>`"] // error!
trait BadAnnotation<A> {}
```
there will be an error about `bool` not implementing `Index<u8>`, followed by a
note saying "the type `bool` cannot be indexed by `u8`".
As you can see, you can specify type parameters in curly braces for
instantiation with the actual types (using the regular format string syntax) in
a given situation. Furthermore, `{Self}` will be instantiated to the type (in
this case, `bool`) that we tried to use.
This error appears when the curly braces do not contain an identifier. Please
add one of the same name as a type parameter. If you intended to use literal
braces, use `{{` and `}}` to escape them.
#### Note: this error code is no longer emitted by the compiler
@@ -16,6 +16,7 @@
pub use rustc_target::spec::SanitizerSet;
use thin_vec::ThinVec;
use crate::attrs::diagnostic::*;
use crate::attrs::pretty_printing::PrintAttribute;
use crate::limit::Limit;
use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability};
@@ -1082,6 +1083,20 @@ pub enum AttributeKind {
/// Represents `#[non_exhaustive]`
NonExhaustive(Span),
/// Represents `#[diagnostic::on_const]`.
OnConst {
span: Span,
/// None if the directive was malformed in some way.
directive: Option<Box<Directive>>,
},
/// Represents `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`.
OnUnimplemented {
span: Span,
/// None if the directive was malformed in some way.
directive: Option<Box<Directive>>,
},
/// Represents `#[optimize(size|speed)]`
Optimize(OptimizeAttr, Span),
+476
View File
@@ -0,0 +1,476 @@
//! Contains the data structures used by the diagnostic attribute family.
use std::fmt;
use std::fmt::Debug;
pub use rustc_ast::attr::data_structures::*;
use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
use rustc_span::{DesugaringKind, Span, Symbol, kw};
use thin_vec::ThinVec;
use tracing::{debug, info};
use crate::attrs::PrintAttribute;
#[derive(Clone, Default, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub struct Directive {
pub is_rustc_attr: bool,
pub condition: Option<OnUnimplementedCondition>,
pub subcommands: ThinVec<Directive>,
pub message: Option<(Span, FormatString)>,
pub label: Option<(Span, FormatString)>,
pub notes: ThinVec<FormatString>,
pub parent_label: Option<FormatString>,
pub append_const_msg: Option<AppendConstMessage>,
}
impl Directive {
/// Visit all the generic arguments used in the attribute, to see whether they are actually a
/// generic of the item. If not then `visit` must issue a diagnostic.
///
/// We can't check this while parsing the attribute because `rustc_attr_parsing` doesn't have
/// access to the item an attribute is on. Instead we later call this function in `check_attr`.
pub fn visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) {
if let Some(condition) = &self.condition {
condition.visit_params(visit);
}
for subcommand in &self.subcommands {
subcommand.visit_params(visit);
}
if let Some((_, message)) = &self.message {
message.visit_params(visit);
}
if let Some((_, label)) = &self.label {
label.visit_params(visit);
}
for note in &self.notes {
note.visit_params(visit);
}
if let Some(parent_label) = &self.parent_label {
parent_label.visit_params(visit);
}
}
pub fn evaluate_directive(
&self,
trait_name: impl Debug,
condition_options: &ConditionOptions,
args: &FormatArgs,
) -> OnUnimplementedNote {
let mut message = None;
let mut label = None;
let mut notes = Vec::new();
let mut parent_label = None;
let mut append_const_msg = None;
info!(
"evaluate_directive({:?}, trait_ref={:?}, options={:?}, args ={:?})",
self, trait_name, condition_options, args
);
for command in self.subcommands.iter().chain(Some(self)).rev() {
debug!(?command);
if let Some(ref condition) = command.condition
&& !condition.matches_predicate(condition_options)
{
debug!("evaluate_directive: skipping {:?} due to condition", command);
continue;
}
debug!("evaluate_directive: {:?} succeeded", command);
if let Some(ref message_) = command.message {
message = Some(message_.clone());
}
if let Some(ref label_) = command.label {
label = Some(label_.clone());
}
notes.extend(command.notes.clone());
if let Some(ref parent_label_) = command.parent_label {
parent_label = Some(parent_label_.clone());
}
append_const_msg = command.append_const_msg;
}
OnUnimplementedNote {
label: label.map(|l| l.1.format(args)),
message: message.map(|m| m.1.format(args)),
notes: notes.into_iter().map(|n| n.format(args)).collect(),
parent_label: parent_label.map(|e_s| e_s.format(args)),
append_const_msg,
}
}
}
#[derive(Default, Debug)]
pub struct OnUnimplementedNote {
pub message: Option<String>,
pub label: Option<String>,
pub notes: Vec<String>,
pub parent_label: Option<String>,
// If none, should fall back to a generic message
pub append_const_msg: Option<AppendConstMessage>,
}
/// Append a message for `[const] Trait` errors.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub enum AppendConstMessage {
#[default]
Default,
Custom(Symbol, Span),
}
/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces",
/// either as string pieces or dynamic arguments.
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub struct FormatString {
pub input: Symbol,
pub span: Span,
pub pieces: ThinVec<Piece>,
}
impl FormatString {
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.as_str()),
// `A` if we have `trait Trait<A> {}` and `note = "i'm the actual type of {A}"`
Piece::Arg(FormatArg::GenericParam { generic_param, .. }) => {
match args.generic_args.iter().find(|(p, _)| p == generic_param) {
Some((_, val)) => ret.push_str(val.as_str()),
None => {
// Apparently this was not actually a generic parameter, so lets write
// what the user wrote.
let _ = fmt::write(&mut ret, format_args!("{{{generic_param}}}"));
}
}
}
// `{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 visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) {
for piece in &self.pieces {
if let Piece::Arg(FormatArg::GenericParam { generic_param, span }) = piece {
visit(*generic_param, *span);
}
}
}
}
/// 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 {
pub this: String,
pub trait_sugared: String,
pub item_context: &'static str,
pub generic_args: Vec<(Symbol, String)>,
}
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub enum Piece {
Lit(Symbol),
Arg(FormatArg),
}
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
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}` 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(Symbol),
}
/// Represents the `on` filter in `#[rustc_on_unimplemented]`.
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub struct OnUnimplementedCondition {
pub span: Span,
pub pred: Predicate,
}
impl OnUnimplementedCondition {
pub fn matches_predicate(self: &OnUnimplementedCondition, options: &ConditionOptions) -> bool {
self.pred.eval(&mut |p| match p {
FlagOrNv::Flag(b) => options.has_flag(*b),
FlagOrNv::NameValue(NameValue { name, value }) => {
let value = value.format(&options.generic_args);
options.contains(*name, value)
}
})
}
pub fn visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) {
self.pred.visit_params(self.span, visit);
}
}
/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`OnUnimplementedCondition`].
///
/// It is similar to the predicate in the `cfg` attribute,
/// and may contain nested predicates.
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub enum Predicate {
/// A condition like `on(crate_local)`.
Flag(Flag),
/// A match, like `on(Rhs = "Whatever")`.
Match(NameValue),
/// Negation, like `on(not($pred))`.
Not(Box<Predicate>),
/// True if all predicates are true, like `on(all($a, $b, $c))`.
All(ThinVec<Predicate>),
/// True if any predicate is true, like `on(any($a, $b, $c))`.
Any(ThinVec<Predicate>),
}
impl Predicate {
pub fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool {
match self {
Predicate::Flag(flag) => eval(FlagOrNv::Flag(flag)),
Predicate::Match(nv) => eval(FlagOrNv::NameValue(nv)),
Predicate::Not(not) => !not.eval(eval),
Predicate::All(preds) => preds.into_iter().all(|pred| pred.eval(eval)),
Predicate::Any(preds) => preds.into_iter().any(|pred| pred.eval(eval)),
}
}
pub fn visit_params(&self, span: Span, visit: &mut impl FnMut(Symbol, Span)) {
match self {
Predicate::Flag(_) => {}
Predicate::Match(nv) => nv.visit_params(span, visit),
Predicate::Not(not) => not.visit_params(span, visit),
Predicate::All(preds) | Predicate::Any(preds) => {
preds.iter().for_each(|pred| pred.visit_params(span, visit))
}
}
}
}
/// Represents a `MetaWord` in an `on`-filter.
#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub enum Flag {
/// Whether the code causing the trait bound to not be fulfilled
/// is part of the user's crate.
CrateLocal,
/// Whether the obligation is user-specified rather than derived.
Direct,
/// Whether we are in some kind of desugaring like
/// `?` or `try { .. }`.
FromDesugaring,
}
/// A `MetaNameValueStr` in an `on`-filter.
///
/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`.
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub struct NameValue {
pub name: Name,
/// Something like `"&str"` or `"alloc::string::String"`,
/// in which case it just contains a single string piece.
/// But if it is something like `"&[{A}]"` then it must be formatted later.
pub value: FilterFormatString,
}
impl NameValue {
pub fn visit_params(&self, span: Span, visit: &mut impl FnMut(Symbol, Span)) {
if let Name::GenericArg(arg) = self.name {
visit(arg, span);
}
self.value.visit_params(span, visit);
}
}
/// The valid names of the `on` filter.
#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub enum Name {
Cause,
FromDesugaring,
SelfUpper,
GenericArg(Symbol),
}
#[derive(Debug, Clone)]
pub enum FlagOrNv<'p> {
Flag(&'p Flag),
NameValue(&'p NameValue),
}
/// Represents a value inside an `on` filter.
///
/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`.
/// If it is a simple literal like this then `pieces` will be `[LitOrArg::Lit("value")]`.
/// The `Arg` variant is used when it contains formatting like
/// `#[rustc_on_unimplemented(on(Self = "&[{A}]", message = "hello"))]`.
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub struct FilterFormatString {
pub pieces: ThinVec<LitOrArg>,
}
impl FilterFormatString {
fn format(&self, generic_args: &[(Symbol, String)]) -> String {
let mut ret = String::new();
for piece in &self.pieces {
match piece {
LitOrArg::Lit(s) => ret.push_str(s.as_str()),
LitOrArg::Arg(s) => match generic_args.iter().find(|(k, _)| k == s) {
Some((_, val)) => ret.push_str(val),
None => {
let _ = std::fmt::write(&mut ret, format_args!("{{{s}}}"));
}
},
}
}
ret
}
pub fn visit_params(&self, span: Span, visit: &mut impl FnMut(Symbol, Span)) {
for piece in &self.pieces {
if let LitOrArg::Arg(arg) = piece {
visit(*arg, span);
}
}
}
}
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub enum LitOrArg {
Lit(Symbol),
Arg(Symbol),
}
/// Used with `OnUnimplementedCondition::matches_predicate` to evaluate the
/// [`OnUnimplementedCondition`].
///
/// 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.
pub self_types: Vec<String>,
// The kind of compiler desugaring.
pub from_desugaring: Option<DesugaringKind>,
/// Match on a variant of rustc_infer's `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 has_flag(&self, name: Flag) -> bool {
match name {
Flag::CrateLocal => self.crate_local,
Flag::Direct => self.direct,
Flag::FromDesugaring => self.from_desugaring.is_some(),
}
}
pub fn contains(&self, name: Name, value: String) -> bool {
match name {
Name::SelfUpper => self.self_types.contains(&value),
Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)),
Name::Cause => self.cause == Some(value),
Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)),
}
}
}
@@ -78,6 +78,8 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
NoMangle(..) => Yes, // Needed for rustdoc
NoStd(..) => No,
NonExhaustive(..) => Yes, // Needed for rustdoc
OnConst { .. } => Yes,
OnUnimplemented { .. } => Yes,
Optimize(..) => No,
PanicRuntime => No,
PatchableFunctionEntry { .. } => Yes,
+1
View File
@@ -9,6 +9,7 @@
pub use pretty_printing::PrintAttribute;
mod data_structures;
pub mod diagnostic;
mod encode_cross_crate;
mod pretty_printing;
+1 -1
View File
@@ -1,6 +1,6 @@
use rustc_data_structures::fingerprint::Fingerprint;
pub use rustc_lint_defs::AttributeLintKind;
use rustc_lint_defs::LintId;
pub use rustc_lint_defs::{AttributeLintKind, FormatWarning};
use rustc_macros::HashStable_Generic;
use rustc_span::Span;
@@ -26,7 +26,6 @@
use rustc_span::source_map::Spanned;
use rustc_target::spec::{AbiMap, AbiMapping};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use tracing::{debug, instrument};
@@ -805,7 +804,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
tcx.ensure_ok().type_of(def_id);
tcx.ensure_ok().predicates_of(def_id);
tcx.ensure_ok().associated_items(def_id);
check_diagnostic_attrs(tcx, def_id);
if of_trait {
let impl_trait_header = tcx.impl_trait_header(def_id);
res = res.and(
@@ -828,7 +826,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
tcx.ensure_ok().predicates_of(def_id);
tcx.ensure_ok().associated_items(def_id);
let assoc_items = tcx.associated_items(def_id);
check_diagnostic_attrs(tcx, def_id);
for &assoc_item in assoc_items.in_definition_order() {
match assoc_item.kind {
@@ -1122,11 +1119,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
})
}
pub(super) fn check_diagnostic_attrs(tcx: TyCtxt<'_>, def_id: LocalDefId) {
// an error would be reported if this fails.
let _ = OnUnimplementedDirective::of_item(tcx, def_id.to_def_id());
}
pub(super) fn check_specialization_validity<'tcx>(
tcx: TyCtxt<'tcx>,
trait_def: &ty::TraitDef,
@@ -16,6 +16,7 @@
use rustc_errors::{
Applicability, Diag, MultiSpan, StashKey, listify, pluralize, struct_span_code_err,
};
use rustc_hir::attrs::diagnostic::OnUnimplementedNote;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor};
@@ -37,7 +38,6 @@
kw, sym,
};
use rustc_trait_selection::error_reporting::traits::DefIdOrName;
use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::{
+28 -1
View File
@@ -4,7 +4,7 @@
use rustc_errors::{
Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion,
};
use rustc_hir::lints::AttributeLintKind;
use rustc_hir::lints::{AttributeLintKind, FormatWarning};
use rustc_middle::middle::stability;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
@@ -446,5 +446,32 @@ pub fn decorate_attribute_lint(
&AttributeLintKind::ExpectedNoArgs => lints::ExpectedNoArgs.decorate_lint(diag),
&AttributeLintKind::ExpectedNameValue => lints::ExpectedNameValue.decorate_lint(diag),
&AttributeLintKind::MalformedOnUnimplementedAttr { span } => {
lints::MalformedOnUnimplementedAttrLint { span }.decorate_lint(diag)
}
&AttributeLintKind::MalformedOnConstAttr { span } => {
lints::MalformedOnConstAttrLint { span }.decorate_lint(diag)
}
AttributeLintKind::MalformedDiagnosticFormat { warning } => match warning {
FormatWarning::PositionalArgument { .. } => {
lints::DisallowedPositionalArgument.decorate_lint(diag)
}
FormatWarning::InvalidSpecifier { .. } => {
lints::InvalidFormatSpecifier.decorate_lint(diag)
}
},
AttributeLintKind::DiagnosticWrappedParserError { description, label, span } => {
lints::WrappedParserError { description, label, span: *span }.decorate_lint(diag)
}
&AttributeLintKind::IgnoredDiagnosticOption { option_name, first_span, later_span } => {
lints::IgnoredDiagnosticOption { option_name, first_span, later_span }
.decorate_lint(diag)
}
&AttributeLintKind::MissingOptionsForOnUnimplemented => {
lints::MissingOptionsForOnUnimplementedAttr.decorate_lint(diag)
}
&AttributeLintKind::MissingOptionsForOnConst => {
lints::MissingOptionsForOnConstAttr.decorate_lint(diag)
}
}
}
+57
View File
@@ -3869,3 +3869,60 @@ pub(crate) struct UnreachableCfgSelectPredicateWildcard {
#[label("always matches")]
pub wildcard_span: Span,
}
#[derive(LintDiagnostic)]
#[diag("positional format arguments are not allowed here")]
#[help(
"only named format arguments with the name of one of the generic types are allowed in this context"
)]
pub(crate) struct DisallowedPositionalArgument;
#[derive(LintDiagnostic)]
#[diag("invalid format specifier")]
#[help("no format specifier are supported in this position")]
pub(crate) struct InvalidFormatSpecifier;
#[derive(LintDiagnostic)]
#[diag("{$description}")]
pub(crate) struct WrappedParserError<'a> {
pub description: &'a str,
#[label("{$label}")]
pub span: Span,
pub label: &'a str,
}
#[derive(LintDiagnostic)]
#[diag("`{$option_name}` is ignored due to previous definition of `{$option_name}`")]
pub(crate) struct IgnoredDiagnosticOption {
pub option_name: Symbol,
#[label("`{$option_name}` is first declared here")]
pub first_span: Span,
#[label("`{$option_name}` is later redundantly declared here")]
pub later_span: Span,
}
#[derive(LintDiagnostic)]
#[diag("missing options for `on_unimplemented` attribute")]
#[help("at least one of the `message`, `note` and `label` options are expected")]
pub(crate) struct MissingOptionsForOnUnimplementedAttr;
#[derive(LintDiagnostic)]
#[diag("missing options for `on_const` attribute")]
#[help("at least one of the `message`, `note` and `label` options are expected")]
pub(crate) struct MissingOptionsForOnConstAttr;
#[derive(LintDiagnostic)]
#[diag("malformed `on_unimplemented` attribute")]
#[help("only `message`, `note` and `label` are allowed as options")]
pub(crate) struct MalformedOnUnimplementedAttrLint {
#[label("invalid option found here")]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag("malformed `on_const` attribute")]
#[help("only `message`, `note` and `label` are allowed as options")]
pub(crate) struct MalformedOnConstAttrLint {
#[label("invalid option found here")]
pub span: Span,
}
+27
View File
@@ -834,6 +834,33 @@ pub enum AttributeLintKind {
MalformedDoc,
ExpectedNoArgs,
ExpectedNameValue,
MalformedOnUnimplementedAttr {
span: Span,
},
MalformedOnConstAttr {
span: Span,
},
MalformedDiagnosticFormat {
warning: FormatWarning,
},
DiagnosticWrappedParserError {
description: String,
label: String,
span: Span,
},
IgnoredDiagnosticOption {
option_name: Symbol,
first_span: Span,
later_span: Span,
},
MissingOptionsForOnUnimplemented,
MissingOptionsForOnConst,
}
#[derive(Debug, Clone, HashStable_Generic)]
pub enum FormatWarning {
PositionalArgument { span: Span, help: String },
InvalidSpecifier { name: String, span: Span },
}
pub type RegisteredTools = FxIndexSet<Ident>;
+51 -12
View File
@@ -20,6 +20,7 @@
ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP,
BuiltinAttribute,
};
use rustc_hir::attrs::diagnostic::Directive;
use rustc_hir::attrs::{
AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr,
MirDialect, MirPhase, ReprAttr, SanitizerSet,
@@ -28,9 +29,9 @@
use rustc_hir::def_id::LocalModDefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{
self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, HirId, Item, ItemKind,
MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target, TraitItem,
find_attr,
self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, GenericParamKind, HirId,
Item, ItemKind, MethodKind, Node, ParamName, PartialConstStability, Safety, Stability,
StabilityLevel, Target, TraitItem, find_attr,
};
use rustc_macros::LintDiagnostic;
use rustc_middle::hir::nested_filter;
@@ -43,8 +44,8 @@
use rustc_session::config::CrateType;
use rustc_session::lint;
use rustc_session::lint::builtin::{
CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES,
UNUSED_ATTRIBUTES,
CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
};
use rustc_session::parse::feature_err;
use rustc_span::edition::Edition;
@@ -231,6 +232,8 @@ fn check_attributes(
self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id,target)
},
Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)},
Attribute::Parsed(AttributeKind::OnUnimplemented{span, directive}) => {self.check_diagnostic_on_unimplemented(*span, hir_id, target,directive.as_deref())},
Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)}
Attribute::Parsed(
// tidy-alphabetical-start
AttributeKind::RustcAllowIncoherentImpl(..)
@@ -386,12 +389,6 @@ fn check_attributes(
Attribute::Unparsed(attr_item) => {
style = Some(attr_item.style);
match attr.path().as_slice() {
[sym::diagnostic, sym::on_unimplemented, ..] => {
self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target)
}
[sym::diagnostic, sym::on_const, ..] => {
self.check_diagnostic_on_const(attr.span(), hir_id, target, item)
}
[sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => {
self.check_autodiff(hir_id, attr, span, target)
}
@@ -608,7 +605,13 @@ fn check_do_not_recommend(
}
/// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition
fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) {
fn check_diagnostic_on_unimplemented(
&self,
attr_span: Span,
hir_id: HirId,
target: Target,
directive: Option<&Directive>,
) {
if !matches!(target, Target::Trait) {
self.tcx.emit_node_span_lint(
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
@@ -617,6 +620,39 @@ fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, targ
DiagnosticOnUnimplementedOnlyForTraits,
);
}
if let Some(directive) = directive {
if let Node::Item(Item {
kind: ItemKind::Trait(_, _, _, trait_name, generics, _, _),
..
}) = self.tcx.hir_node(hir_id)
{
directive.visit_params(&mut |argument_name, span| {
let has_generic = generics.params.iter().any(|p| {
if !matches!(p.kind, GenericParamKind::Lifetime { .. })
&& let ParamName::Plain(name) = p.name
&& name.name == argument_name
{
true
} else {
false
}
});
if !has_generic {
self.tcx.emit_node_span_lint(
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
hir_id,
span,
errors::UnknownFormatParameterForOnUnimplementedAttr {
argument_name,
trait_name: *trait_name,
help: !directive.is_rustc_attr,
},
)
}
})
}
}
}
/// Checks if `#[diagnostic::on_const]` is applied to a trait impl
@@ -652,6 +688,9 @@ fn check_diagnostic_on_const(
attr_span,
DiagnosticOnConstOnlyForTraitImpls { item_span },
);
// We don't check the validity of generic args here...whose generics would that be, anyway?
// The traits' or the impls'?
}
/// Checks if an `#[inline]` is applied to a function or a closure.
+11 -1
View File
@@ -10,7 +10,7 @@
use rustc_hir::attrs::{MirDialect, MirPhase};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{MainDefinition, Ty};
use rustc_span::{DUMMY_SP, Span, Symbol};
use rustc_span::{DUMMY_SP, Ident, Span, Symbol};
use crate::check_attr::ProcMacroKind;
use crate::lang_items::Duplicate;
@@ -1449,3 +1449,13 @@ pub(crate) struct FunctionNamesDuplicated {
#[primary_span]
pub spans: Vec<Span>,
}
#[derive(LintDiagnostic)]
#[diag("there is no parameter `{$argument_name}` on trait `{$trait_name}`")]
pub(crate) struct UnknownFormatParameterForOnUnimplementedAttr {
pub argument_name: Symbol,
pub trait_name: Ident,
// `false` if we're in rustc_on_unimplemented, since its syntax is a lot more complex.
#[help(r#"expect either a generic argument name or {"`{Self}`"} as format argument"#)]
pub help: bool,
}
+1
View File
@@ -864,6 +864,7 @@
// it's clearer that it's intended as a dummy value, and more likely
// to be detected if it accidentally does get used.
empty: "",
empty_braces: "{}",
emscripten_wasm_eh,
enable,
end,
@@ -15,7 +15,6 @@ rustc_infer = { path = "../rustc_infer" }
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_next_trait_solver = { path = "../rustc_next_trait_solver" }
rustc_parse_format = { path = "../rustc_parse_format" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] }
@@ -14,9 +14,10 @@
Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions, msg,
pluralize, struct_span_code_err,
};
use rustc_hir::attrs::diagnostic::{AppendConstMessage, OnUnimplementedNote};
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::intravisit::Visitor;
use rustc_hir::{self as hir, LangItem, Node};
use rustc_hir::{self as hir, LangItem, Node, find_attr};
use rustc_infer::infer::{InferOk, TypeTrace};
use rustc_infer::traits::ImplSource;
use rustc_infer::traits::solve::Goal;
@@ -37,14 +38,12 @@
use rustc_span::{BytePos, DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym};
use tracing::{debug, instrument};
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote};
use super::suggestions::get_explanation_based_on_obligation;
use super::{
ArgKind, CandidateSimilarity, FindExprBySpan, GetSafeTransmuteErrorAndReason, ImplCandidate,
};
use crate::error_reporting::TypeErrCtxt;
use crate::error_reporting::infer::TyCategory;
use crate::error_reporting::traits::on_unimplemented::OnUnimplementedDirective;
use crate::error_reporting::traits::report_dyn_incompatibility;
use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, CoroClosureNotFn};
use crate::infer::{self, InferCtxt, InferCtxtExt as _};
@@ -392,7 +391,7 @@ pub fn report_selection_error(
if let Some(s) = label {
// If it has a custom `#[rustc_on_unimplemented]`
// error message, let's display it as the label!
err.span_label(span, s);
err.span_label(span, s.as_str().to_owned());
if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_))
// When the self type is a type param We don't need to "the trait
// `std::marker::Sized` is not implemented for `T`" as we will point
@@ -912,11 +911,9 @@ fn report_host_effect_error(
diag.long_ty_path(),
);
if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, impl_did)
{
let note = command.evaluate(
self.tcx,
predicate.skip_binder().trait_ref,
if let Some(command) = find_attr!(self.tcx, impl_did, OnConst {directive, ..} => directive.as_deref()).flatten(){
let note = command.evaluate_directive(
predicate.skip_binder().trait_ref,
&condition_options,
&format_args,
);
@@ -2,8 +2,6 @@
pub mod call_kind;
pub mod fulfillment_errors;
pub mod on_unimplemented;
pub mod on_unimplemented_condition;
pub mod on_unimplemented_format;
mod overflow;
pub mod suggestions;
@@ -1,82 +1,18 @@
use std::iter;
use std::path::PathBuf;
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit};
use rustc_errors::codes::*;
use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{AttrArgs, Attribute};
use rustc_macros::LintDiagnostic;
use rustc_middle::bug;
use rustc_hir::attrs::diagnostic::{ConditionOptions, FormatArgs, OnUnimplementedNote};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::find_attr;
pub use rustc_hir::lints::FormatWarning;
use rustc_middle::ty::print::PrintTraitRefExt;
use rustc_middle::ty::{self, GenericArgsRef, GenericParamDef, GenericParamDefKind, TyCtxt};
use rustc_session::lint::builtin::{
MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
};
use rustc_span::{Span, Symbol, sym};
use tracing::{debug, info};
use rustc_middle::ty::{self, GenericParamDef, GenericParamDefKind};
use rustc_span::Symbol;
use super::{ObligationCauseCode, PredicateObligation};
use crate::error_reporting::TypeErrCtxt;
use crate::error_reporting::traits::on_unimplemented_condition::{
ConditionOptions, OnUnimplementedCondition,
};
use crate::error_reporting::traits::on_unimplemented_format::{
Ctx, FormatArgs, FormatString, FormatWarning,
};
use crate::errors::{InvalidOnClause, NoValueInOnUnimplemented};
use crate::infer::InferCtxtExt;
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
fn impl_similar_to(
&self,
trait_pred: ty::PolyTraitPredicate<'tcx>,
obligation: &PredicateObligation<'tcx>,
) -> Option<(DefId, GenericArgsRef<'tcx>)> {
let tcx = self.tcx;
let param_env = obligation.param_env;
self.enter_forall(trait_pred, |trait_pred| {
let trait_self_ty = trait_pred.self_ty();
let mut self_match_impls = vec![];
let mut fuzzy_match_impls = vec![];
self.tcx.for_each_relevant_impl(trait_pred.def_id(), trait_self_ty, |def_id| {
let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id);
let impl_trait_ref = tcx.impl_trait_ref(def_id).instantiate(tcx, impl_args);
let impl_self_ty = impl_trait_ref.self_ty();
if self.can_eq(param_env, trait_self_ty, impl_self_ty) {
self_match_impls.push((def_id, impl_args));
if iter::zip(
trait_pred.trait_ref.args.types().skip(1),
impl_trait_ref.args.types().skip(1),
)
.all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
{
fuzzy_match_impls.push((def_id, impl_args));
}
}
});
let impl_def_id_and_args = if let [impl_] = self_match_impls[..] {
impl_
} else if let [impl_] = fuzzy_match_impls[..] {
impl_
} else {
return None;
};
#[allow(deprecated)]
tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented)
.then_some(impl_def_id_and_args)
})
}
/// Used to set on_unimplemented's `ItemContext`
/// to be the enclosing (async) block/function/closure
fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str> {
@@ -107,10 +43,8 @@ pub fn on_unimplemented_note(
}
let (condition_options, format_args) =
self.on_unimplemented_components(trait_pred, obligation, long_ty_path);
if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, trait_pred.def_id())
{
command.evaluate(
self.tcx,
if let Some(command) = find_attr!(self.tcx, trait_pred.def_id(), OnUnimplemented {directive, ..} => directive.as_deref()).flatten() {
command.evaluate_directive(
trait_pred.skip_binder().trait_ref,
&condition_options,
&format_args,
@@ -125,10 +59,8 @@ pub(crate) fn on_unimplemented_components(
trait_pred: ty::PolyTraitPredicate<'tcx>,
obligation: &PredicateObligation<'tcx>,
long_ty_path: &mut Option<PathBuf>,
) -> (ConditionOptions, FormatArgs<'tcx>) {
let (def_id, args) = self
.impl_similar_to(trait_pred, obligation)
.unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args));
) -> (ConditionOptions, FormatArgs) {
let (def_id, args) = (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args);
let trait_pred = trait_pred.skip_binder();
let mut self_types = vec![];
@@ -276,7 +208,7 @@ pub(crate) fn on_unimplemented_components(
}));
let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id);
let trait_sugared = trait_pred.trait_ref.print_trait_sugared();
let trait_sugared = trait_pred.trait_ref.print_trait_sugared().to_string();
let condition_options = ConditionOptions {
self_types,
@@ -318,617 +250,3 @@ pub(crate) fn on_unimplemented_components(
(condition_options, format_args)
}
}
/// 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 of the format string, i.e. `"content"`
symbol: Symbol,
/// The span of the format string, i.e. `"content"`
span: Span,
is_diagnostic_namespace_variant: bool,
}
#[derive(Debug)]
pub struct OnUnimplementedDirective {
condition: Option<OnUnimplementedCondition>,
subcommands: Vec<OnUnimplementedDirective>,
message: Option<(Span, OnUnimplementedFormatString)>,
label: Option<(Span, OnUnimplementedFormatString)>,
notes: Vec<OnUnimplementedFormatString>,
parent_label: Option<OnUnimplementedFormatString>,
append_const_msg: Option<AppendConstMessage>,
}
/// For the `#[rustc_on_unimplemented]` attribute
#[derive(Default, Debug)]
pub struct OnUnimplementedNote {
pub message: Option<String>,
pub label: Option<String>,
pub notes: Vec<String>,
pub parent_label: Option<String>,
// If none, should fall back to a generic message
pub append_const_msg: Option<AppendConstMessage>,
}
/// Append a message for `[const] Trait` errors.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum AppendConstMessage {
#[default]
Default,
Custom(Symbol, Span),
}
#[derive(LintDiagnostic)]
#[diag("malformed `on_unimplemented` attribute")]
#[help("only `message`, `note` and `label` are allowed as options")]
pub struct MalformedOnUnimplementedAttrLint {
#[label("invalid option found here")]
pub span: Span,
}
impl MalformedOnUnimplementedAttrLint {
pub fn new(span: Span) -> Self {
Self { span }
}
}
#[derive(LintDiagnostic)]
#[diag("missing options for `on_unimplemented` attribute")]
#[help("at least one of the `message`, `note` and `label` options are expected")]
pub struct MissingOptionsForOnUnimplementedAttr;
#[derive(LintDiagnostic)]
#[diag("`{$option_name}` is ignored due to previous definition of `{$option_name}`")]
pub struct IgnoredDiagnosticOption {
pub option_name: &'static str,
#[label("`{$option_name}` is already declared here")]
pub span: Span,
#[label("`{$option_name}` is first declared here")]
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)
&& let Some(item_def_id) = item_def_id.as_local()
{
tcx.emit_node_span_lint(
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 },
);
}
}
}
#[derive(LintDiagnostic)]
#[diag("{$description}")]
pub struct WrappedParserError {
pub description: String,
pub label: String,
}
impl<'tcx> OnUnimplementedDirective {
fn parse(
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
items: &[MetaItemInner],
span: Span,
is_root: bool,
is_diagnostic_namespace_variant: bool,
) -> Result<Option<Self>, ErrorGuaranteed> {
let mut errored = None;
let mut item_iter = items.iter();
let parse_value = |value_str, span| {
OnUnimplementedFormatString::try_parse(
tcx,
item_def_id,
value_str,
span,
is_diagnostic_namespace_variant,
)
.map(Some)
};
let condition = if is_root {
None
} else {
let cond = item_iter
.next()
.ok_or_else(|| tcx.dcx().emit_err(InvalidOnClause::Empty { span }))?;
let generics: Vec<Symbol> = tcx
.generics_of(item_def_id)
.own_params
.iter()
.filter_map(|param| {
if matches!(param.kind, GenericParamDefKind::Lifetime) {
None
} else {
Some(param.name)
}
})
.collect();
match OnUnimplementedCondition::parse(cond, &generics) {
Ok(condition) => Some(condition),
Err(e) => return Err(tcx.dcx().emit_err(e)),
}
};
let mut message = None;
let mut label = None;
let mut notes = Vec::new();
let mut parent_label = None;
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 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::parent_label)
&& parent_label.is_none()
&& !is_diagnostic_namespace_variant
{
if let Some(parent_label_) = item.value_str() {
parent_label = parse_value(parent_label_, item.span())?;
continue;
}
} else if item.has_name(sym::on)
&& is_root
&& message.is_none()
&& label.is_none()
&& notes.is_empty()
&& !is_diagnostic_namespace_variant
// FIXME(diagnostic_namespace): disallow filters for now
{
if let Some(items) = item.meta_item_list() {
match Self::parse(
tcx,
item_def_id,
items,
item.span(),
false,
is_diagnostic_namespace_variant,
) {
Ok(Some(subcommand)) => subcommands.push(subcommand),
Ok(None) => bug!(
"This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false"
),
Err(reported) => errored = Some(reported),
};
continue;
}
} else if item.has_name(sym::append_const_msg)
&& append_const_msg.is_none()
&& !is_diagnostic_namespace_variant
{
if let Some(msg) = item.value_str() {
append_const_msg = Some(AppendConstMessage::Custom(msg, item.span()));
continue;
} else if item.is_word() {
append_const_msg = Some(AppendConstMessage::Default);
continue;
}
}
if is_diagnostic_namespace_variant {
if let Some(def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(def_id),
vec![item.span()],
MalformedOnUnimplementedAttrLint::new(item.span()),
);
}
} else {
// nothing found
tcx.dcx().emit_err(NoValueInOnUnimplemented { span: item.span() });
}
}
if let Some(reported) = errored {
if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) }
} else {
Ok(Some(OnUnimplementedDirective {
condition,
subcommands,
message,
label,
notes,
parent_label,
append_const_msg,
}))
}
}
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
let attr = if tcx.is_trait(item_def_id) {
sym::on_unimplemented
} else if let DefKind::Impl { of_trait: true } = tcx.def_kind(item_def_id) {
sym::on_const
} else {
// 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) = {
#[allow(deprecated)]
tcx.get_attr(item_def_id, sym::rustc_on_unimplemented)
} {
return Self::parse_attribute(attr, false, tcx, item_def_id);
} else {
tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, attr])
.filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose())
.try_fold(None, |aggr: Option<Self>, directive| {
let directive = directive?;
if let Some(aggr) = aggr {
let mut subcommands = aggr.subcommands;
subcommands.extend(directive.subcommands);
let mut notes = aggr.notes;
notes.extend(directive.notes);
IgnoredDiagnosticOption::maybe_emit_warning(
tcx,
item_def_id,
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.0),
aggr.label.as_ref().map(|f| f.0),
"label",
);
IgnoredDiagnosticOption::maybe_emit_warning(
tcx,
item_def_id,
directive.condition.as_ref().map(|i| i.span()),
aggr.condition.as_ref().map(|i| i.span()),
"condition",
);
IgnoredDiagnosticOption::maybe_emit_warning(
tcx,
item_def_id,
directive.parent_label.as_ref().map(|f| f.span),
aggr.parent_label.as_ref().map(|f| f.span),
"parent_label",
);
IgnoredDiagnosticOption::maybe_emit_warning(
tcx,
item_def_id,
directive.append_const_msg.as_ref().and_then(|c| {
if let AppendConstMessage::Custom(_, s) = c {
Some(*s)
} else {
None
}
}),
aggr.append_const_msg.as_ref().and_then(|c| {
if let AppendConstMessage::Custom(_, s) = c {
Some(*s)
} else {
None
}
}),
"append_const_msg",
);
Ok(Some(Self {
condition: aggr.condition.or(directive.condition),
subcommands,
message: aggr.message.or(directive.message),
label: aggr.label.or(directive.label),
notes,
parent_label: aggr.parent_label.or(directive.parent_label),
append_const_msg: aggr.append_const_msg.or(directive.append_const_msg),
}))
} else {
Ok(Some(directive))
}
})
}
}
fn parse_attribute(
attr: &Attribute,
is_diagnostic_namespace_variant: bool,
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
) -> Result<Option<Self>, ErrorGuaranteed> {
let result = if let Some(items) = attr.meta_item_list() {
Self::parse(
tcx,
item_def_id,
&items,
attr.span(),
true,
is_diagnostic_namespace_variant,
)
} else if let Some(value) = attr.value_str() {
if !is_diagnostic_namespace_variant {
Ok(Some(OnUnimplementedDirective {
condition: None,
message: None,
subcommands: vec![],
label: Some((
attr.span(),
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,
}))
} else {
let item = attr.get_normal_item();
let report_span = match &item.args {
AttrArgs::Empty => item.path.span,
AttrArgs::Delimited(args) => args.dspan.entire(),
AttrArgs::Eq { eq_span, expr } => eq_span.to(expr.span),
};
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
report_span,
MalformedOnUnimplementedAttrLint::new(report_span),
);
}
Ok(None)
}
} else if is_diagnostic_namespace_variant {
match attr {
Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => {
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
attr.span(),
MalformedOnUnimplementedAttrLint::new(attr.span()),
);
}
}
_ => {
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
attr.span(),
MissingOptionsForOnUnimplementedAttr,
)
}
}
};
Ok(None)
} else {
let reported = tcx.dcx().delayed_bug("of_item: neither meta_item_list nor value_str");
return Err(reported);
};
debug!("of_item({:?}) = {:?}", item_def_id, result);
result
}
pub(crate) fn evaluate(
&self,
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
condition_options: &ConditionOptions,
args: &FormatArgs<'tcx>,
) -> OnUnimplementedNote {
let mut message = None;
let mut label = None;
let mut notes = Vec::new();
let mut parent_label = None;
let mut append_const_msg = None;
info!(
"evaluate({:?}, trait_ref={:?}, options={:?}, args ={:?})",
self, trait_ref, condition_options, args
);
for command in self.subcommands.iter().chain(Some(self)).rev() {
debug!(?command);
if let Some(ref condition) = command.condition
&& !condition.matches_predicate(condition_options)
{
debug!("evaluate: skipping {:?} due to condition", command);
continue;
}
debug!("evaluate: {:?} succeeded", command);
if let Some(ref message_) = command.message {
message = Some(message_.clone());
}
if let Some(ref label_) = command.label {
label = Some(label_.clone());
}
notes.extend(command.notes.clone());
if let Some(ref parent_label_) = command.parent_label {
parent_label = Some(parent_label_.clone());
}
append_const_msg = command.append_const_msg;
}
OnUnimplementedNote {
label: label.map(|l| l.1.format(tcx, trait_ref, args)),
message: message.map(|m| m.1.format(tcx, trait_ref, args)),
notes: notes.into_iter().map(|n| n.format(tcx, trait_ref, args)).collect(),
parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, args)),
append_const_msg,
}
}
}
impl<'tcx> OnUnimplementedFormatString {
fn try_parse(
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
from: Symbol,
span: Span,
is_diagnostic_namespace_variant: bool,
) -> Result<Self, ErrorGuaranteed> {
let result =
OnUnimplementedFormatString { symbol: from, span, is_diagnostic_namespace_variant };
result.verify(tcx, item_def_id)?;
Ok(result)
}
fn verify(&self, tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> {
if !tcx.is_trait(trait_def_id) {
return Ok(());
};
let ctx = if self.is_diagnostic_namespace_variant {
Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }
} else {
Ctx::RustcOnUnimplemented { tcx, trait_def_id }
};
let mut result = Ok(());
let snippet = tcx.sess.source_map().span_to_snippet(self.span).ok();
match FormatString::parse(self.symbol, snippet, 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, .. }) => {
if self.is_diagnostic_namespace_variant {
for w in warnings {
w.emit_warning(tcx, trait_def_id)
}
} else {
for w in warnings {
match w {
FormatWarning::UnknownParam { argument_name, span } => {
let reported = struct_span_code_err!(
tcx.dcx(),
span,
E0230,
"cannot find parameter {} on this trait",
argument_name,
)
.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 { .. } => {}
}
}
}
}
// Error from the underlying `rustc_parse_format::Parser`
Err(e) => {
// 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
if self.is_diagnostic_namespace_variant {
if let Some(trait_def_id) = trait_def_id.as_local() {
tcx.emit_node_span_lint(
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
tcx.local_def_id_to_hir_id(trait_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
}
pub fn format(
&self,
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
args: &FormatArgs<'tcx>,
) -> String {
let trait_def_id = trait_ref.def_id;
let ctx = if self.is_diagnostic_namespace_variant {
Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }
} else {
Ctx::RustcOnUnimplemented { tcx, trait_def_id }
};
// No point passing a snippet here, we already did that in `verify`
if let Ok(s) = FormatString::parse(self.symbol, None, self.span, &ctx) {
s.format(args)
} 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()
}
}
}
@@ -1,322 +0,0 @@
use rustc_ast::{MetaItemInner, MetaItemKind, MetaItemLit};
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym};
use crate::errors::InvalidOnClause;
/// Represents the `on` filter in `#[rustc_on_unimplemented]`.
#[derive(Debug)]
pub(crate) struct OnUnimplementedCondition {
span: Span,
pred: Predicate,
}
impl OnUnimplementedCondition {
pub(crate) fn span(&self) -> Span {
self.span
}
pub(crate) fn matches_predicate(&self, options: &ConditionOptions) -> bool {
self.pred.eval(&mut |p| match p {
FlagOrNv::Flag(b) => options.has_flag(*b),
FlagOrNv::NameValue(NameValue { name, value }) => {
let value = value.format(&options.generic_args);
options.contains(*name, value)
}
})
}
pub(crate) fn parse(
input: &MetaItemInner,
generics: &[Symbol],
) -> Result<Self, InvalidOnClause> {
let span = input.span();
let pred = Predicate::parse(input, generics)?;
Ok(OnUnimplementedCondition { span, pred })
}
}
/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`OnUnimplementedCondition`].
///
/// It is similar to the predicate in the `cfg` attribute,
/// and may contain nested predicates.
#[derive(Debug)]
enum Predicate {
/// A condition like `on(crate_local)`.
Flag(Flag),
/// A match, like `on(Rhs = "Whatever")`.
Match(NameValue),
/// Negation, like `on(not($pred))`.
Not(Box<Predicate>),
/// True if all predicates are true, like `on(all($a, $b, $c))`.
All(Vec<Predicate>),
/// True if any predicate is true, like `on(any($a, $b, $c))`.
Any(Vec<Predicate>),
}
impl Predicate {
fn parse(input: &MetaItemInner, generics: &[Symbol]) -> Result<Self, InvalidOnClause> {
let meta_item = match input {
MetaItemInner::MetaItem(meta_item) => meta_item,
MetaItemInner::Lit(lit) => {
return Err(InvalidOnClause::UnsupportedLiteral { span: lit.span });
}
};
let Some(predicate) = meta_item.ident() else {
return Err(InvalidOnClause::ExpectedIdentifier {
span: meta_item.path.span,
path: meta_item.path.clone(),
});
};
match meta_item.kind {
MetaItemKind::List(ref mis) => match predicate.name {
sym::any => Ok(Predicate::Any(Predicate::parse_sequence(mis, generics)?)),
sym::all => Ok(Predicate::All(Predicate::parse_sequence(mis, generics)?)),
sym::not => match &**mis {
[one] => Ok(Predicate::Not(Box::new(Predicate::parse(one, generics)?))),
[first, .., last] => Err(InvalidOnClause::ExpectedOnePredInNot {
span: first.span().to(last.span()),
}),
[] => Err(InvalidOnClause::ExpectedOnePredInNot { span: meta_item.span }),
},
invalid_pred => {
Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred })
}
},
MetaItemKind::NameValue(MetaItemLit { symbol, .. }) => {
let name = Name::parse(predicate, generics)?;
let value = FilterFormatString::parse(symbol);
let kv = NameValue { name, value };
Ok(Predicate::Match(kv))
}
MetaItemKind::Word => {
let flag = Flag::parse(predicate)?;
Ok(Predicate::Flag(flag))
}
}
}
fn parse_sequence(
sequence: &[MetaItemInner],
generics: &[Symbol],
) -> Result<Vec<Self>, InvalidOnClause> {
sequence.iter().map(|item| Predicate::parse(item, generics)).collect()
}
fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool {
match self {
Predicate::Flag(flag) => eval(FlagOrNv::Flag(flag)),
Predicate::Match(nv) => eval(FlagOrNv::NameValue(nv)),
Predicate::Not(not) => !not.eval(eval),
Predicate::All(preds) => preds.into_iter().all(|pred| pred.eval(eval)),
Predicate::Any(preds) => preds.into_iter().any(|pred| pred.eval(eval)),
}
}
}
/// Represents a `MetaWord` in an `on`-filter.
#[derive(Debug, Clone, Copy)]
enum Flag {
/// Whether the code causing the trait bound to not be fulfilled
/// is part of the user's crate.
CrateLocal,
/// Whether the obligation is user-specified rather than derived.
Direct,
/// Whether we are in some kind of desugaring like
/// `?` or `try { .. }`.
FromDesugaring,
}
impl Flag {
fn parse(Ident { name, span }: Ident) -> Result<Self, InvalidOnClause> {
match name {
sym::crate_local => Ok(Flag::CrateLocal),
sym::direct => Ok(Flag::Direct),
sym::from_desugaring => Ok(Flag::FromDesugaring),
invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }),
}
}
}
/// A `MetaNameValueStr` in an `on`-filter.
///
/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`.
#[derive(Debug, Clone)]
struct NameValue {
name: Name,
/// Something like `"&str"` or `"alloc::string::String"`,
/// in which case it just contains a single string piece.
/// But if it is something like `"&[{A}]"` then it must be formatted later.
value: FilterFormatString,
}
/// The valid names of the `on` filter.
#[derive(Debug, Clone, Copy)]
enum Name {
Cause,
FromDesugaring,
SelfUpper,
GenericArg(Symbol),
}
impl Name {
fn parse(Ident { name, span }: Ident, generics: &[Symbol]) -> Result<Self, InvalidOnClause> {
match name {
kw::SelfUpper => Ok(Name::SelfUpper),
sym::from_desugaring => Ok(Name::FromDesugaring),
sym::cause => Ok(Name::Cause),
generic if generics.contains(&generic) => Ok(Name::GenericArg(generic)),
invalid_name => Err(InvalidOnClause::InvalidName { invalid_name, span }),
}
}
}
#[derive(Debug, Clone)]
enum FlagOrNv<'p> {
Flag(&'p Flag),
NameValue(&'p NameValue),
}
/// Represents a value inside an `on` filter.
///
/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`.
/// If it is a simple literal like this then `pieces` will be `[LitOrArg::Lit("value")]`.
/// The `Arg` variant is used when it contains formatting like
/// `#[rustc_on_unimplemented(on(Self = "&[{A}]", message = "hello"))]`.
#[derive(Debug, Clone)]
struct FilterFormatString {
pieces: Vec<LitOrArg>,
}
#[derive(Debug, Clone)]
enum LitOrArg {
Lit(String),
Arg(String),
}
impl FilterFormatString {
fn parse(input: Symbol) -> Self {
let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic)
.map(|p| match p {
Piece::Lit(s) => LitOrArg::Lit(s.to_owned()),
// We just ignore formatspecs here
Piece::NextArgument(a) => match a.position {
// In `TypeErrCtxt::on_unimplemented_note` we substitute `"{integral}"` even
// if the integer type has been resolved, to allow targeting all integers.
// `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet,
// from the `Display` impl of `InferTy` to be precise.
//
// Don't try to format these later!
Position::ArgumentNamed(arg @ "integer" | arg @ "integral" | arg @ "float") => {
LitOrArg::Lit(format!("{{{arg}}}"))
}
// FIXME(mejrs) We should check if these correspond to a generic of the trait.
Position::ArgumentNamed(arg) => LitOrArg::Arg(arg.to_owned()),
// FIXME(mejrs) These should really be warnings/errors
Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(String::from("{}")),
Position::ArgumentIs(idx) => LitOrArg::Lit(format!("{{{idx}}}")),
},
})
.collect();
Self { pieces }
}
fn format(&self, generic_args: &[(Symbol, String)]) -> String {
let mut ret = String::new();
for piece in &self.pieces {
match piece {
LitOrArg::Lit(s) => ret.push_str(s),
LitOrArg::Arg(arg) => {
let s = Symbol::intern(arg);
match generic_args.iter().find(|(k, _)| *k == s) {
Some((_, val)) => ret.push_str(val),
None => {
// FIXME(mejrs) If we start checking as mentioned in
// FilterFormatString::parse then this shouldn't happen
let _ = std::fmt::write(&mut ret, format_args!("{{{s}}}"));
}
}
}
}
}
ret
}
}
/// Used with `OnUnimplementedCondition::matches_predicate` to evaluate the
/// [`OnUnimplementedCondition`].
///
/// 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(crate) struct ConditionOptions {
/// All the self types that may apply.
pub(crate) self_types: Vec<String>,
// The kind of compiler desugaring.
pub(crate) from_desugaring: Option<DesugaringKind>,
/// Match on a variant of [rustc_infer::traits::ObligationCauseCode].
pub(crate) cause: Option<String>,
pub(crate) crate_local: bool,
/// Is the obligation "directly" user-specified, rather than derived?
pub(crate) direct: bool,
// A list of the generic arguments and their reified types.
pub(crate) generic_args: Vec<(Symbol, String)>,
}
impl ConditionOptions {
fn has_flag(&self, name: Flag) -> bool {
match name {
Flag::CrateLocal => self.crate_local,
Flag::Direct => self.direct,
Flag::FromDesugaring => self.from_desugaring.is_some(),
}
}
fn contains(&self, name: Name, value: String) -> bool {
match name {
Name::SelfUpper => self.self_types.contains(&value),
Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)),
Name::Cause => self.cause == Some(value),
Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)),
}
}
}
@@ -1,336 +0,0 @@
use std::fmt;
use std::ops::Range;
use errors::*;
use rustc_middle::ty::print::TraitRefPrintSugared;
use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
use rustc_parse_format::{
Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position,
};
use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_FORMAT_LITERALS;
use rustc_span::def_id::DefId;
use rustc_span::{InnerSpan, 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 successfully 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(
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
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(
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
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(
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
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,
snippet: Option<String>,
span: Span,
ctx: &Ctx<'tcx>,
) -> Result<Self, ParseError> {
let s = input.as_str();
let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic);
let pieces: Vec<_> = parser.by_ref().collect();
if let Some(err) = parser.errors.into_iter().next() {
return Err(err);
}
let mut warnings = Vec::new();
let pieces = pieces
.into_iter()
.map(|piece| match piece {
RpfPiece::Lit(lit) => Piece::Lit(lit.into()),
RpfPiece::NextArgument(arg) => {
warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal);
let arg = parse_arg(&arg, ctx, &mut warnings, span, parser.is_source_literal);
Piece::Arg(arg)
}
})
.collect();
Ok(FormatString { input, pieces, span, warnings })
}
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,
is_source_literal: bool,
) -> FormatArg {
let (Ctx::RustcOnUnimplemented { tcx, trait_def_id }
| Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = *ctx;
let span = slice_span(input_span, arg.position_span.clone(), is_source_literal);
match arg.position {
// Something like "hello {name}"
Position::ArgumentNamed(name) => match (ctx, Symbol::intern(name)) {
// 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,
// Any attribute can use these
(
Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
kw::SelfUpper,
) => FormatArg::SelfUpper,
(
Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
generic_param,
) if tcx.generics_of(trait_def_id).own_params.iter().any(|param| {
!matches!(param.kind, GenericParamDefKind::Lifetime) && 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,
is_source_literal: bool,
) {
if spec.ty != "" {
let span = spec
.ty_span
.as_ref()
.map(|inner| slice_span(input_span, inner.clone(), is_source_literal))
.unwrap_or(input_span);
warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() })
}
}
fn slice_span(input: Span, Range { start, end }: Range<usize>, is_source_literal: bool) -> Span {
if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input }
}
pub mod errors {
use rustc_macros::LintDiagnostic;
use rustc_span::Ident;
use super::*;
#[derive(LintDiagnostic)]
#[diag("there is no parameter `{$argument_name}` on trait `{$trait_name}`")]
#[help("expect either a generic argument name or {\"`{Self}`\"} as format argument")]
pub struct UnknownFormatParameterForOnUnimplementedAttr {
pub argument_name: Symbol,
pub trait_name: Ident,
}
#[derive(LintDiagnostic)]
#[diag("positional format arguments are not allowed here")]
#[help(
"only named format arguments with the name of one of the generic types are allowed in this context"
)]
pub struct DisallowedPositionalArgument;
#[derive(LintDiagnostic)]
#[diag("invalid format specifier")]
#[help("no format specifier are supported in this position")]
pub struct InvalidFormatSpecifier;
#[derive(LintDiagnostic)]
#[diag("missing options for `on_unimplemented` attribute")]
#[help("at least one of the `message`, `note` and `label` options are expected")]
pub struct MissingOptionsForOnUnimplementedAttr;
}
@@ -1,4 +1,3 @@
use rustc_ast::Path;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::{
@@ -28,60 +27,6 @@ pub struct UnableToConstructConstantValue<'a> {
pub unevaluated: ty::UnevaluatedConst<'a>,
}
#[derive(Diagnostic)]
pub enum InvalidOnClause {
#[diag("empty `on`-clause in `#[rustc_on_unimplemented]`", code = E0232)]
Empty {
#[primary_span]
#[label("empty `on`-clause here")]
span: Span,
},
#[diag("expected a single predicate in `not(..)`", code = E0232)]
ExpectedOnePredInNot {
#[primary_span]
#[label("unexpected quantity of predicates here")]
span: Span,
},
#[diag("literals inside `on`-clauses are not supported", code = E0232)]
UnsupportedLiteral {
#[primary_span]
#[label("unexpected literal here")]
span: Span,
},
#[diag("expected an identifier inside this `on`-clause", code = E0232)]
ExpectedIdentifier {
#[primary_span]
#[label("expected an identifier here, not `{$path}`")]
span: Span,
path: Path,
},
#[diag("this predicate is invalid", code = E0232)]
InvalidPredicate {
#[primary_span]
#[label("expected one of `any`, `all` or `not` here, not `{$invalid_pred}`")]
span: Span,
invalid_pred: Symbol,
},
#[diag("invalid flag in `on`-clause", code = E0232)]
InvalidFlag {
#[primary_span]
#[label(
"expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}`"
)]
span: Span,
invalid_flag: Symbol,
},
#[diag("invalid name in `on`-clause", code = E0232)]
InvalidName {
#[primary_span]
#[label(
"expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `{$invalid_name}`"
)]
span: Span,
invalid_name: Symbol,
},
}
#[derive(Diagnostic)]
#[diag("this attribute must have a value", code = E0232)]
#[note("e.g. `#[rustc_on_unimplemented(message=\"foo\")]`")]
@@ -9,13 +9,12 @@
#![feature(rustc_attrs)]
#![deny(unused_attributes)]
#[rustc_on_unimplemented = "invalid"]
#[rustc_on_unimplemented(label ="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 {
+17 -17
View File
@@ -723,23 +723,6 @@ help: use `#[rustc_align(...)]` instead
LL | #[repr]
| ^^^^^^^
warning: missing options for `on_unimplemented` attribute
--> $DIR/malformed-attrs.rs:142:1
|
LL | #[diagnostic::on_unimplemented]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: at least one of the `message`, `note` and `label` options are expected
= note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: malformed `on_unimplemented` attribute
--> $DIR/malformed-attrs.rs:144:1
|
LL | #[diagnostic::on_unimplemented = 1]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]`
--> $DIR/malformed-attrs.rs:41:1
|
@@ -810,6 +793,23 @@ LL | #[no_implicit_prelude = 23]
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= help: `#[no_implicit_prelude]` can be applied to crates and modules
warning: missing options for `on_unimplemented` attribute
--> $DIR/malformed-attrs.rs:142:1
|
LL | #[diagnostic::on_unimplemented]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: at least one of the `message`, `note` and `label` options are expected
= note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: malformed `on_unimplemented` attribute
--> $DIR/malformed-attrs.rs:144:1
|
LL | #[diagnostic::on_unimplemented = 1]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
--> $DIR/malformed-attrs.rs:151:1
|
@@ -0,0 +1,10 @@
#![feature(diagnostic_on_const)]
pub struct X;
#[diagnostic::on_const(message = "message", label = "label", note = "note")]
impl PartialEq for X {
fn eq(&self, _other: &X) -> bool {
true
}
}
@@ -1,4 +1,4 @@
#![feature(const_trait_impl, const_cmp)]
#![feature(const_trait_impl, const_cmp, diagnostic_on_const)]
#![deny(misplaced_diagnostic_attributes)]
#[diagnostic::on_const(message = "tadaa", note = "boing")]
@@ -1,34 +1,27 @@
//@ reference: attributes.diagnostic.on_unimplemented.invalid-string
#[diagnostic::on_unimplemented(message = "{{Test } thing")]
//~^WARN unmatched `}` found
//~|WARN unmatched `}` found
trait ImportantTrait1 {}
#[diagnostic::on_unimplemented(message = "Test {}")]
//~^WARN positional format arguments are not allowed here
//~|WARN positional format arguments are not allowed here
trait ImportantTrait2 {}
#[diagnostic::on_unimplemented(message = "Test {1:}")]
//~^WARN positional format arguments are not allowed here
//~|WARN positional format arguments are not allowed here
//~|WARN invalid format specifier [malformed_diagnostic_format_literals]
//~|WARN invalid format specifier [malformed_diagnostic_format_literals]
trait ImportantTrait3 {}
#[diagnostic::on_unimplemented(message = "Test {Self:123}")]
//~^WARN invalid format specifier
//~|WARN invalid format specifier
trait ImportantTrait4 {}
#[diagnostic::on_unimplemented(message = "Test {Self:!}")]
//~^WARN invalid format specifier [malformed_diagnostic_format_literals]
//~|WARN invalid format specifier [malformed_diagnostic_format_literals]
trait ImportantTrait5 {}
#[diagnostic::on_unimplemented(message = "Test {Self:}")]
//~^WARN invalid format specifier [malformed_diagnostic_format_literals]
//~|WARN invalid format specifier [malformed_diagnostic_format_literals]
trait ImportantTrait6 {}
@@ -2,12 +2,14 @@ warning: unmatched `}` found
--> $DIR/broken_format.rs:2:42
|
LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")]
| ^^^^^^^^^^^^^^^^
| ^^^^^^^^-^^^^^^^
| |
| unmatched `}`
|
= note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: positional format arguments are not allowed here
--> $DIR/broken_format.rs:7:49
--> $DIR/broken_format.rs:6:49
|
LL | #[diagnostic::on_unimplemented(message = "Test {}")]
| ^
@@ -15,7 +17,7 @@ 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: invalid format specifier
--> $DIR/broken_format.rs:12:50
--> $DIR/broken_format.rs:10:50
|
LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
| ^
@@ -23,7 +25,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
= help: no format specifier are supported in this position
warning: positional format arguments are not allowed here
--> $DIR/broken_format.rs:12:49
--> $DIR/broken_format.rs:10:49
|
LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
| ^
@@ -31,7 +33,7 @@ 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:19:53
--> $DIR/broken_format.rs:15:53
|
LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")]
| ^^^^
@@ -39,7 +41,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")]
= help: no format specifier are supported in this position
warning: invalid format specifier
--> $DIR/broken_format.rs:24:53
--> $DIR/broken_format.rs:19:53
|
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
| ^^
@@ -47,23 +49,15 @@ LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
= help: no format specifier are supported in this position
warning: invalid format specifier
--> $DIR/broken_format.rs:29:53
--> $DIR/broken_format.rs:23:53
|
LL | #[diagnostic::on_unimplemented(message = "Test {Self:}")]
| ^
|
= help: no format specifier are supported in this position
warning: unmatched `}` found
--> $DIR/broken_format.rs:2:42
|
LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")]
| ^^^^^^^^^^^^^^^^
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: {{Test } thing
--> $DIR/broken_format.rs:43:13
--> $DIR/broken_format.rs:36:13
|
LL | check_1(());
| ------- ^^ the trait `ImportantTrait1` is not implemented for `()`
@@ -71,27 +65,18 @@ LL | check_1(());
| required by a bound introduced by this call
|
help: this trait has no implementations, consider adding one
--> $DIR/broken_format.rs:5:1
--> $DIR/broken_format.rs:4:1
|
LL | trait ImportantTrait1 {}
| ^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `check_1`
--> $DIR/broken_format.rs:35:20
--> $DIR/broken_format.rs:28:20
|
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: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`
error[E0277]: Test {}
--> $DIR/broken_format.rs:45:13
--> $DIR/broken_format.rs:38:13
|
LL | check_2(());
| ------- ^^ the trait `ImportantTrait2` is not implemented for `()`
@@ -99,36 +84,18 @@ LL | check_2(());
| required by a bound introduced by this call
|
help: this trait has no implementations, consider adding one
--> $DIR/broken_format.rs:10:1
--> $DIR/broken_format.rs:8:1
|
LL | trait ImportantTrait2 {}
| ^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `check_2`
--> $DIR/broken_format.rs:36:20
--> $DIR/broken_format.rs:29:20
|
LL | fn check_2(_: impl ImportantTrait2) {}
| ^^^^^^^^^^^^^^^ required by this bound in `check_2`
warning: invalid format specifier
--> $DIR/broken_format.rs:12:50
|
LL | #[diagnostic::on_unimplemented(message = "Test {1:}")]
| ^
|
= help: no format specifier are supported in this position
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
warning: positional format arguments are not allowed here
--> $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`
error[E0277]: Test {1}
--> $DIR/broken_format.rs:47:13
--> $DIR/broken_format.rs:40:13
|
LL | check_3(());
| ------- ^^ the trait `ImportantTrait3` is not implemented for `()`
@@ -136,27 +103,18 @@ LL | check_3(());
| required by a bound introduced by this call
|
help: this trait has no implementations, consider adding one
--> $DIR/broken_format.rs:17:1
--> $DIR/broken_format.rs:13:1
|
LL | trait ImportantTrait3 {}
| ^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `check_3`
--> $DIR/broken_format.rs:37:20
--> $DIR/broken_format.rs:30:20
|
LL | fn check_3(_: impl ImportantTrait3) {}
| ^^^^^^^^^^^^^^^ required by this bound in `check_3`
warning: invalid format specifier
--> $DIR/broken_format.rs:19:53
|
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`
error[E0277]: Test ()
--> $DIR/broken_format.rs:49:13
--> $DIR/broken_format.rs:42:13
|
LL | check_4(());
| ------- ^^ the trait `ImportantTrait4` is not implemented for `()`
@@ -164,27 +122,18 @@ LL | check_4(());
| required by a bound introduced by this call
|
help: this trait has no implementations, consider adding one
--> $DIR/broken_format.rs:22:1
--> $DIR/broken_format.rs:17:1
|
LL | trait ImportantTrait4 {}
| ^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `check_4`
--> $DIR/broken_format.rs:38:20
--> $DIR/broken_format.rs:31:20
|
LL | fn check_4(_: impl ImportantTrait4) {}
| ^^^^^^^^^^^^^^^ required by this bound in `check_4`
warning: invalid format specifier
--> $DIR/broken_format.rs:24:53
|
LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")]
| ^^
|
= help: no format specifier are supported in this position
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: Test ()
--> $DIR/broken_format.rs:51:13
--> $DIR/broken_format.rs:44:13
|
LL | check_5(());
| ------- ^^ the trait `ImportantTrait5` is not implemented for `()`
@@ -192,27 +141,18 @@ LL | check_5(());
| required by a bound introduced by this call
|
help: this trait has no implementations, consider adding one
--> $DIR/broken_format.rs:27:1
--> $DIR/broken_format.rs:21:1
|
LL | trait ImportantTrait5 {}
| ^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `check_5`
--> $DIR/broken_format.rs:39:20
--> $DIR/broken_format.rs:32:20
|
LL | fn check_5(_: impl ImportantTrait5) {}
| ^^^^^^^^^^^^^^^ required by this bound in `check_5`
warning: invalid format specifier
--> $DIR/broken_format.rs:29:53
|
LL | #[diagnostic::on_unimplemented(message = "Test {Self:}")]
| ^
|
= help: no format specifier are supported in this position
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: Test ()
--> $DIR/broken_format.rs:53:13
--> $DIR/broken_format.rs:46:13
|
LL | check_6(());
| ------- ^^ the trait `ImportantTrait6` is not implemented for `()`
@@ -220,16 +160,16 @@ LL | check_6(());
| required by a bound introduced by this call
|
help: this trait has no implementations, consider adding one
--> $DIR/broken_format.rs:32:1
--> $DIR/broken_format.rs:25:1
|
LL | trait ImportantTrait6 {}
| ^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `check_6`
--> $DIR/broken_format.rs:40:20
--> $DIR/broken_format.rs:33:20
|
LL | fn check_6(_: impl ImportantTrait6) {}
| ^^^^^^^^^^^^^^^ required by this bound in `check_6`
error: aborting due to 6 previous errors; 14 warnings emitted
error: aborting due to 6 previous errors; 7 warnings emitted
For more information about this error, try `rustc --explain E0277`.
@@ -5,22 +5,18 @@
#[diagnostic::on_unimplemented(
on(Self = "&str"),
//~^WARN malformed `on_unimplemented` attribute
//~|WARN malformed `on_unimplemented` attribute
message = "trait has `{Self}` and `{T}` as params",
label = "trait has `{Self}` and `{T}` as params",
note = "trait has `{Self}` and `{T}` as params",
parent_label = "in this scope",
//~^WARN malformed `on_unimplemented` attribute
//~|WARN malformed `on_unimplemented` attribute
append_const_msg
//~^WARN malformed `on_unimplemented` attribute
//~|WARN malformed `on_unimplemented` attribute
)]
trait Foo<T> {}
#[diagnostic::on_unimplemented = "Message"]
//~^WARN malformed `on_unimplemented` attribute
//~|WARN malformed `on_unimplemented` attribute
trait Bar {}
#[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")]
@@ -32,27 +28,16 @@ impl Bar for i32 {}
#[diagnostic::on_unimplemented(
message = "{from_desugaring}{direct}{cause}{integral}{integer}",
//~^WARN there is no parameter `from_desugaring` on trait `Baz`
//~|WARN there is no parameter `from_desugaring` on trait `Baz`
//~|WARN there is no parameter `direct` on trait `Baz`
//~|WARN there is no parameter `direct` on trait `Baz`
//~|WARN there is no parameter `cause` on trait `Baz`
//~|WARN there is no parameter `cause` on trait `Baz`
//~|WARN there is no parameter `integral` on trait `Baz`
//~|WARN there is no parameter `integral` on trait `Baz`
//~|WARN there is no parameter `integer` on trait `Baz`
//~|WARN there is no parameter `integer` on trait `Baz`
label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
//~^WARN there is no parameter `float` on trait `Baz`
//~|WARN there is no parameter `float` on trait `Baz`
//~|WARN there is no parameter `_Self` on trait `Baz`
//~|WARN there is no parameter `_Self` on trait `Baz`
//~|WARN there is no parameter `crate_local` on trait `Baz`
//~|WARN there is no parameter `crate_local` on trait `Baz`
//~|WARN there is no parameter `Trait` on trait `Baz`
//~|WARN there is no parameter `Trait` on trait `Baz`
//~|WARN there is no parameter `ItemContext` on trait `Baz`
//~|WARN there is no parameter `ItemContext` on trait `Baz`
//~|WARN there is no parameter `This` on trait `Baz`
//~|WARN there is no parameter `This` on trait `Baz`
)]
trait Baz {}
@@ -1,11 +1,100 @@
warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:26:1
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:22:1
|
LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: there is no parameter `from_desugaring` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:17
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
| ^^^^^^^^^^^^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
= note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: there is no parameter `direct` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29: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:29: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:29: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:29: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:35:15
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^
|
= 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:35:22
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^
|
= 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:35:29
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^^^^^^^
|
= 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:35:42
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^
|
= 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:35:49
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^^^^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
warning: there is no parameter `This` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:62
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:6:5
|
@@ -16,7 +105,7 @@ LL | on(Self = "&str"),
= note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:12:5
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:11:5
|
LL | parent_label = "in this scope",
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
@@ -24,7 +113,7 @@ LL | parent_label = "in this scope",
= help: only `message`, `note` and `label` are allowed as options
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:15:5
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:13:5
|
LL | append_const_msg
| ^^^^^^^^^^^^^^^^ invalid option found here
@@ -32,131 +121,15 @@ LL | append_const_msg
= help: only `message`, `note` and `label` are allowed as options
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:21:32
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:18:1
|
LL | #[diagnostic::on_unimplemented = "Message"]
| ^^^^^^^^^^^ invalid option found here
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= 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:17
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
| ^^^^^^^^^^^^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
= note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: there is no parameter `direct` on trait `Baz`
--> $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: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: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: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:15
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^
|
= 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:22
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^
|
= 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:29
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^^^^^^^
|
= 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:42
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^
|
= 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:49
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^^^^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
warning: there is no parameter `This` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:62
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:6:5
|
LL | on(Self = "&str"),
| ^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:12:5
|
LL | parent_label = "in this scope",
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:15:5
|
LL | append_const_msg
| ^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: trait has `()` and `i32` as params
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:65:15
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:50:15
|
LL | takes_foo(());
| --------- ^^ trait has `()` and `i32` as params
@@ -166,27 +139,18 @@ LL | takes_foo(());
= help: the trait `Foo<i32>` is not implemented for `()`
= note: trait has `()` and `i32` as params
help: this trait has no implementations, consider adding one
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:19:1
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:16:1
|
LL | trait Foo<T> {}
| ^^^^^^^^^^^^
note: required by a bound in `takes_foo`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:60:22
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:45:22
|
LL | fn takes_foo(_: impl Foo<i32>) {}
| ^^^^^^^^ required by this bound in `takes_foo`
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:21:32
|
LL | #[diagnostic::on_unimplemented = "Message"]
| ^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: the trait bound `(): Bar` is not satisfied
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:67:15
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:52:15
|
LL | takes_bar(());
| --------- ^^ the trait `Bar` is not implemented for `()`
@@ -194,117 +158,18 @@ LL | takes_bar(());
| required by a bound introduced by this call
|
help: the trait `Bar` is implemented for `i32`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:28:1
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:24:1
|
LL | impl Bar for i32 {}
| ^^^^^^^^^^^^^^^^
note: required by a bound in `takes_bar`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:61:22
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:46:22
|
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: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: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: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: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: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:15
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^
|
= 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:22
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^
|
= 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:29
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^^^^^^^
|
= 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:42
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^
|
= 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:49
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^^^^^^^
|
= 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 `This` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:62
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer}
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:69:15
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:54:15
|
LL | takes_baz(());
| --------- ^^ {float}{_Self}{crate_local}{Trait}{ItemContext}{This}
@@ -313,16 +178,16 @@ LL | takes_baz(());
|
= help: the trait `Baz` is not implemented for `()`
help: this trait has no implementations, consider adding one
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:58:1
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:43:1
|
LL | trait Baz {}
| ^^^^^^^^^
note: required by a bound in `takes_baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:62:22
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:47:22
|
LL | fn takes_baz(_: impl Baz) {}
| ^^^ required by this bound in `takes_baz`
error: aborting due to 3 previous errors; 31 warnings emitted
error: aborting due to 3 previous errors; 16 warnings emitted
For more information about this error, try `rustc --explain E0277`.
@@ -2,7 +2,6 @@
//@ reference: attributes.diagnostic.on_unimplemented.unknown-keys
#[diagnostic::on_unimplemented(unsupported = "foo")]
//~^WARN malformed `on_unimplemented` attribute
//~|WARN malformed `on_unimplemented` attribute
trait Foo {}
#[diagnostic::on_unimplemented(message = "Baz")]
@@ -11,12 +10,10 @@ struct Bar {}
#[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")]
//~^WARN malformed `on_unimplemented` attribute
//~|WARN malformed `on_unimplemented` attribute
trait Baz {}
#[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))]
//~^WARN malformed `on_unimplemented` attribute
//~|WARN malformed `on_unimplemented` attribute
trait Boom {}
#[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))]
@@ -29,12 +26,10 @@ trait Doom {}
#[diagnostic::on_unimplemented]
//~^WARN missing options for `on_unimplemented` attribute
//~|WARN missing options for `on_unimplemented` attribute
trait Whatever {}
#[diagnostic::on_unimplemented(message = "{DoesNotExist}")]
//~^WARN there is no parameter `DoesNotExist` on trait `Test`
//~|WARN there is no parameter `DoesNotExist` on trait `Test`
trait Test {}
fn take_foo(_: impl Foo) {}
@@ -1,62 +1,13 @@
warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:8:1
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:7:1
|
LL | #[diagnostic::on_unimplemented(message = "Baz")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32
|
LL | #[diagnostic::on_unimplemented(unsupported = "foo")]
| ^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
= note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50
|
LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")]
| ^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50
|
LL | #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:22:50
|
LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:26:32
|
LL | #[diagnostic::on_unimplemented = "boom"]
| ^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
warning: missing options for `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:30:1
|
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:35:44
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:44
|
LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")]
| ^^^^^^^^^^^^
@@ -71,10 +22,50 @@ LL | #[diagnostic::on_unimplemented(unsupported = "foo")]
| ^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
= note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:11:50
|
LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")]
| ^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:15:50
|
LL | #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:19:50
|
LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:23:1
|
LL | #[diagnostic::on_unimplemented = "boom"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
warning: missing options for `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:27:1
|
LL | #[diagnostic::on_unimplemented]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: at least one of the `message`, `note` and `label` options are expected
error[E0277]: the trait bound `i32: Foo` is not satisfied
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:47:14
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:42:14
|
LL | take_foo(1_i32);
| -------- ^^^^^ the trait `Foo` is not implemented for `i32`
@@ -82,27 +73,18 @@ LL | take_foo(1_i32);
| required by a bound introduced by this call
|
help: this trait has no implementations, consider adding one
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:6:1
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:5:1
|
LL | trait Foo {}
| ^^^^^^^^^
note: required by a bound in `take_foo`
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:40:21
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:21
|
LL | fn take_foo(_: impl Foo) {}
| ^^^ required by this bound in `take_foo`
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50
|
LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")]
| ^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: Boom
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:49:14
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:44:14
|
LL | take_baz(1_i32);
| -------- ^^^^^ the trait `Baz` is not implemented for `i32`
@@ -110,27 +92,18 @@ LL | take_baz(1_i32);
| required by a bound introduced by this call
|
help: this trait has no implementations, consider adding one
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:15:1
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:13:1
|
LL | trait Baz {}
| ^^^^^^^^^
note: required by a bound in `take_baz`
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:41:21
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:36:21
|
LL | fn take_baz(_: impl Baz) {}
| ^^^ required by this bound in `take_baz`
warning: malformed `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50
|
LL | #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: Boom
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:51:15
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:46:15
|
LL | take_boom(1_i32);
| --------- ^^^^^ the trait `Boom` is not implemented for `i32`
@@ -138,27 +111,18 @@ LL | take_boom(1_i32);
| required by a bound introduced by this call
|
help: this trait has no implementations, consider adding one
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:20:1
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:1
|
LL | trait Boom {}
| ^^^^^^^^^^
note: required by a bound in `take_boom`
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:42:22
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:37:22
|
LL | fn take_boom(_: impl Boom) {}
| ^^^^ required by this bound in `take_boom`
warning: missing options for `on_unimplemented` attribute
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:30:1
|
LL | #[diagnostic::on_unimplemented]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: at least one of the `message`, `note` and `label` options are expected
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: the trait bound `i32: Whatever` is not satisfied
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:53:19
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:48:19
|
LL | take_whatever(1_i32);
| ------------- ^^^^^ the trait `Whatever` is not implemented for `i32`
@@ -166,27 +130,18 @@ LL | take_whatever(1_i32);
| required by a bound introduced by this call
|
help: this trait has no implementations, consider adding one
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:1
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:29:1
|
LL | trait Whatever {}
| ^^^^^^^^^^^^^^
note: required by a bound in `take_whatever`
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:43:26
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:38:26
|
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:35: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`
error[E0277]: {DoesNotExist}
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:55:15
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:50:15
|
LL | take_test(());
| --------- ^^ the trait `Test` is not implemented for `()`
@@ -194,16 +149,16 @@ LL | take_test(());
| required by a bound introduced by this call
|
help: this trait has no implementations, consider adding one
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:38:1
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:1
|
LL | trait Test {}
| ^^^^^^^^^^
note: required by a bound in `take_test`
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:44:22
--> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:39:22
|
LL | fn take_test(_: impl Test) {}
| ^^^^ required by this bound in `take_test`
error: aborting due to 5 previous errors; 13 warnings emitted
error: aborting due to 5 previous errors; 8 warnings emitted
For more information about this error, try `rustc --explain E0277`.
@@ -1,22 +0,0 @@
//@ reference: attributes.diagnostic.on_unimplemented.repetition
//@ reference: attributes.diagnostic.on_unimplemented.syntax
#[diagnostic::on_unimplemented(
if(Self = "()"),
//~^WARN malformed `on_unimplemented` attribute
//~|WARN malformed `on_unimplemented` attribute
message = "custom message",
note = "custom note"
)]
#[diagnostic::on_unimplemented(message = "fallback!!")]
//~^ WARN `message` is ignored due to previous definition of `message`
//~| WARN `message` is ignored due to previous definition of `message`
#[diagnostic::on_unimplemented(label = "fallback label")]
#[diagnostic::on_unimplemented(note = "fallback note")]
trait Foo {}
fn takes_foo(_: impl Foo) {}
fn main() {
takes_foo(());
//~^ERROR custom message
}
@@ -1,63 +0,0 @@
warning: malformed `on_unimplemented` attribute
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:4:5
|
LL | if(Self = "()"),
| ^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
= note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: `message` is ignored due to previous definition of `message`
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32
|
LL | message = "custom message",
| -------------------------- `message` is first declared here
...
LL | #[diagnostic::on_unimplemented(message = "fallback!!")]
| ^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
warning: malformed `on_unimplemented` attribute
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:4:5
|
LL | if(Self = "()"),
| ^^^^^^^^^^^^^^^ invalid option found here
|
= help: only `message`, `note` and `label` are allowed as options
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
warning: `message` is ignored due to previous definition of `message`
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32
|
LL | message = "custom message",
| -------------------------- `message` is first declared here
...
LL | #[diagnostic::on_unimplemented(message = "fallback!!")]
| ^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: custom message
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:20:15
|
LL | takes_foo(());
| --------- ^^ fallback label
| |
| required by a bound introduced by this call
|
= help: the trait `Foo` is not implemented for `()`
= note: custom note
= note: fallback note
help: this trait has no implementations, consider adding one
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:1
|
LL | trait Foo {}
| ^^^^^^^^^
note: required by a bound in `takes_foo`
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:17:22
|
LL | fn takes_foo(_: impl Foo) {}
| ^^^ required by this bound in `takes_foo`
error: aborting due to 1 previous error; 4 warnings emitted
For more information about this error, try `rustc --explain E0277`.
@@ -7,10 +7,8 @@
#[diagnostic::on_unimplemented(
message = "second message",
//~^WARN `message` is ignored due to previous definition of `message`
//~|WARN `message` is ignored due to previous definition of `message`
label = "second label",
//~^WARN `label` is ignored due to previous definition of `label`
//~|WARN `label` is ignored due to previous definition of `label`
note = "second note"
)]
trait Foo {}
@@ -5,43 +5,21 @@ LL | message = "first message",
| ------------------------- `message` is first declared here
...
LL | message = "second message",
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is later redundantly declared here
|
= note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: `label` is ignored due to previous definition of `label`
--> $DIR/report_warning_on_duplicated_options.rs:11:5
--> $DIR/report_warning_on_duplicated_options.rs:10:5
|
LL | label = "first label",
| --------------------- `label` is first declared here
...
LL | label = "second label",
| ^^^^^^^^^^^^^^^^^^^^^^ `label` is already declared here
warning: `message` is ignored due to previous definition of `message`
--> $DIR/report_warning_on_duplicated_options.rs:8:5
|
LL | message = "first message",
| ------------------------- `message` is first declared here
...
LL | message = "second message",
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
warning: `label` is ignored due to previous definition of `label`
--> $DIR/report_warning_on_duplicated_options.rs:11:5
|
LL | label = "first label",
| --------------------- `label` is first declared here
...
LL | label = "second label",
| ^^^^^^^^^^^^^^^^^^^^^^ `label` is already declared here
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
| ^^^^^^^^^^^^^^^^^^^^^^ `label` is later redundantly declared here
error[E0277]: first message
--> $DIR/report_warning_on_duplicated_options.rs:22:15
--> $DIR/report_warning_on_duplicated_options.rs:20:15
|
LL | takes_foo(());
| --------- ^^ first label
@@ -52,16 +30,16 @@ LL | takes_foo(());
= note: custom note
= note: second note
help: this trait has no implementations, consider adding one
--> $DIR/report_warning_on_duplicated_options.rs:16:1
--> $DIR/report_warning_on_duplicated_options.rs:14:1
|
LL | trait Foo {}
| ^^^^^^^^^
note: required by a bound in `takes_foo`
--> $DIR/report_warning_on_duplicated_options.rs:19:22
--> $DIR/report_warning_on_duplicated_options.rs:17:22
|
LL | fn takes_foo(_: impl Foo) {}
| ^^^ required by this bound in `takes_foo`
error: aborting due to 1 previous error; 4 warnings emitted
error: aborting due to 1 previous error; 2 warnings emitted
For more information about this error, try `rustc --explain E0277`.
+17 -15
View File
@@ -2,26 +2,28 @@
#![feature(rustc_attrs)]
#![allow(unused)]
#[rustc_on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}`"]
#[rustc_on_unimplemented(label = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}`")]
trait Foo<Bar, Baz, Quux> {}
#[rustc_on_unimplemented = "a collection of type `{Self}` cannot \
be built from an iterator over elements of type `{A}`"]
#[rustc_on_unimplemented(label = "a collection of type `{Self}` cannot \
be built from an iterator over elements of type `{A}`")]
trait MyFromIterator<A> {
/// Builds a container with elements from an external iterator.
fn my_from_iter<T: Iterator<Item = A>>(iterator: T) -> Self;
}
#[rustc_on_unimplemented]
//~^ ERROR malformed `rustc_on_unimplemented` attribute
//~^ WARN missing options for `on_unimplemented` attribute
//~| NOTE part of
trait NoContent {}
#[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"]
//~^ ERROR cannot find parameter C on this trait
#[rustc_on_unimplemented(label = "Unimplemented error on `{Self}` with params `<{A},{B},{C}>`")]
//~^ WARN there is no parameter `C` on trait `ParameterNotPresent`
//~| NOTE part of
trait ParameterNotPresent<A, B> {}
#[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"]
//~^ ERROR positional format arguments are not allowed here
#[rustc_on_unimplemented(label = "Unimplemented error on `{Self}` with params `<{A},{B},{}>`")]
//~^ WARN positional format arguments are not allowed here
trait NoPositionalArgs<A, B> {}
#[rustc_on_unimplemented(lorem = "")]
@@ -43,9 +45,8 @@ trait Invalid {}
trait DuplicateMessage {}
#[rustc_on_unimplemented(message = "x", on(desugared, message = "y"))]
//~^ ERROR this attribute must have a value
//~^^ NOTE e.g. `#[rustc_on_unimplemented(message="foo")]`
//~^^^ NOTE expected value here
//~^ ERROR invalid flag in `on`-clause
//~| NOTE expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `desugared`
trait OnInWrongPosition {}
#[rustc_on_unimplemented(on(), message = "y")]
@@ -60,6 +61,9 @@ trait EmptyOn {}
trait ExpectedPredicateInOn {}
#[rustc_on_unimplemented(on(Self = "y"), message = "y")]
//~^ ERROR this attribute must have a value
//~| NOTE expected value here
//~| NOTE e.g. `#[rustc_on_unimplemented(message="foo")]`
trait OnWithoutDirectives {}
#[rustc_on_unimplemented(on(from_desugaring, on(from_desugaring, message = "x")), message = "y")]
@@ -109,11 +113,9 @@ trait InvalidPredicate {}
trait InvalidFlag {}
#[rustc_on_unimplemented(on(_Self = "y", message = "y"))]
//~^ ERROR invalid name in `on`-clause
//~^^ NOTE expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `_Self`
//~^ WARN there is no parameter `_Self` on trait `InvalidName`
trait InvalidName {}
#[rustc_on_unimplemented(on(abc = "y", message = "y"))]
//~^ ERROR invalid name in `on`-clause
//~^^ NOTE expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `abc`
//~^ WARN there is no parameter `abc` on trait `InvalidName2`
trait InvalidName2 {}
+60 -55
View File
@@ -1,30 +1,5 @@
error: malformed `rustc_on_unimplemented` attribute input
--> $DIR/bad-annotation.rs:15: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[E0230]: cannot find parameter C on this trait
--> $DIR/bad-annotation.rs:19:90
|
LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"]
| ^
error[E0231]: positional format arguments are not allowed here
--> $DIR/bad-annotation.rs:23:90
|
LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{}>`"]
| ^
error[E0232]: this attribute must have a value
--> $DIR/bad-annotation.rs:27:26
--> $DIR/bad-annotation.rs:29:26
|
LL | #[rustc_on_unimplemented(lorem = "")]
| ^^^^^^^^^^ expected value here
@@ -32,7 +7,7 @@ LL | #[rustc_on_unimplemented(lorem = "")]
= note: e.g. `#[rustc_on_unimplemented(message="foo")]`
error[E0232]: this attribute must have a value
--> $DIR/bad-annotation.rs:33:26
--> $DIR/bad-annotation.rs:35:26
|
LL | #[rustc_on_unimplemented(lorem(ipsum(dolor)))]
| ^^^^^^^^^^^^^^^^^^^ expected value here
@@ -40,29 +15,27 @@ LL | #[rustc_on_unimplemented(lorem(ipsum(dolor)))]
= note: e.g. `#[rustc_on_unimplemented(message="foo")]`
error[E0232]: this attribute must have a value
--> $DIR/bad-annotation.rs:39:41
--> $DIR/bad-annotation.rs:41:41
|
LL | #[rustc_on_unimplemented(message = "x", message = "y")]
| ^^^^^^^^^^^^^ expected value here
|
= note: e.g. `#[rustc_on_unimplemented(message="foo")]`
error[E0232]: this attribute must have a value
--> $DIR/bad-annotation.rs:45:41
error[E0232]: invalid flag in `on`-clause
--> $DIR/bad-annotation.rs:47:44
|
LL | #[rustc_on_unimplemented(message = "x", on(desugared, message = "y"))]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected value here
|
= note: e.g. `#[rustc_on_unimplemented(message="foo")]`
| ^^^^^^^^^ expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `desugared`
error[E0232]: empty `on`-clause in `#[rustc_on_unimplemented]`
--> $DIR/bad-annotation.rs:51:26
--> $DIR/bad-annotation.rs:52:26
|
LL | #[rustc_on_unimplemented(on(), message = "y")]
| ^^^^ empty `on`-clause here
error[E0232]: this attribute must have a value
--> $DIR/bad-annotation.rs:56:26
--> $DIR/bad-annotation.rs:57:26
|
LL | #[rustc_on_unimplemented(on = "x", message = "y")]
| ^^^^^^^^ expected value here
@@ -70,7 +43,15 @@ LL | #[rustc_on_unimplemented(on = "x", message = "y")]
= note: e.g. `#[rustc_on_unimplemented(message="foo")]`
error[E0232]: this attribute must have a value
--> $DIR/bad-annotation.rs:65:46
--> $DIR/bad-annotation.rs:63:26
|
LL | #[rustc_on_unimplemented(on(Self = "y"), message = "y")]
| ^^^^^^^^^^^^^^ expected value here
|
= note: e.g. `#[rustc_on_unimplemented(message="foo")]`
error[E0232]: this attribute must have a value
--> $DIR/bad-annotation.rs:69:46
|
LL | #[rustc_on_unimplemented(on(from_desugaring, on(from_desugaring, message = "x")), message = "y")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected value here
@@ -78,66 +59,90 @@ LL | #[rustc_on_unimplemented(on(from_desugaring, on(from_desugaring, message =
= note: e.g. `#[rustc_on_unimplemented(message="foo")]`
error[E0232]: literals inside `on`-clauses are not supported
--> $DIR/bad-annotation.rs:71:29
--> $DIR/bad-annotation.rs:75:29
|
LL | #[rustc_on_unimplemented(on("y", message = "y"))]
| ^^^ unexpected literal here
error[E0232]: literals inside `on`-clauses are not supported
--> $DIR/bad-annotation.rs:76:29
--> $DIR/bad-annotation.rs:80:29
|
LL | #[rustc_on_unimplemented(on(42, message = "y"))]
| ^^ unexpected literal here
error[E0232]: expected a single predicate in `not(..)`
--> $DIR/bad-annotation.rs:81:33
--> $DIR/bad-annotation.rs:85:32
|
LL | #[rustc_on_unimplemented(on(not(a, b), message = "y"))]
| ^^^^ unexpected quantity of predicates here
| ^^^^^^ unexpected quantity of predicates here
error[E0232]: expected a single predicate in `not(..)`
--> $DIR/bad-annotation.rs:86:29
--> $DIR/bad-annotation.rs:90:32
|
LL | #[rustc_on_unimplemented(on(not(), message = "y"))]
| ^^^^^ unexpected quantity of predicates here
| ^^ unexpected quantity of predicates here
error[E0232]: expected an identifier inside this `on`-clause
--> $DIR/bad-annotation.rs:91:29
--> $DIR/bad-annotation.rs:95:29
|
LL | #[rustc_on_unimplemented(on(thing::What, message = "y"))]
| ^^^^^^^^^^^ expected an identifier here, not `thing::What`
error[E0232]: expected an identifier inside this `on`-clause
--> $DIR/bad-annotation.rs:96:29
--> $DIR/bad-annotation.rs:100:29
|
LL | #[rustc_on_unimplemented(on(thing::What = "value", message = "y"))]
| ^^^^^^^^^^^ expected an identifier here, not `thing::What`
error[E0232]: this predicate is invalid
--> $DIR/bad-annotation.rs:101:29
--> $DIR/bad-annotation.rs:105:29
|
LL | #[rustc_on_unimplemented(on(aaaaaaaaaaaaaa(a, b), message = "y"))]
| ^^^^^^^^^^^^^^ expected one of `any`, `all` or `not` here, not `aaaaaaaaaaaaaa`
error[E0232]: invalid flag in `on`-clause
--> $DIR/bad-annotation.rs:106:29
--> $DIR/bad-annotation.rs:110:29
|
LL | #[rustc_on_unimplemented(on(something, message = "y"))]
| ^^^^^^^^^ expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `something`
error[E0232]: invalid name in `on`-clause
--> $DIR/bad-annotation.rs:111:29
warning: there is no parameter `C` on trait `ParameterNotPresent`
--> $DIR/bad-annotation.rs:20:90
|
LL | #[rustc_on_unimplemented(label = "Unimplemented error on `{Self}` with params `<{A},{B},{C}>`")]
| ^
|
= note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
warning: there is no parameter `_Self` on trait `InvalidName`
--> $DIR/bad-annotation.rs:115:29
|
LL | #[rustc_on_unimplemented(on(_Self = "y", message = "y"))]
| ^^^^^ expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `_Self`
| ^^^^^^^^^^^
error[E0232]: invalid name in `on`-clause
--> $DIR/bad-annotation.rs:116:29
warning: there is no parameter `abc` on trait `InvalidName2`
--> $DIR/bad-annotation.rs:119:29
|
LL | #[rustc_on_unimplemented(on(abc = "y", message = "y"))]
| ^^^ expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `abc`
| ^^^^^^^^^
error: aborting due to 20 previous errors
warning: missing options for `on_unimplemented` attribute
--> $DIR/bad-annotation.rs:15:1
|
LL | #[rustc_on_unimplemented]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: at least one of the `message`, `note` and `label` options are expected
= note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
Some errors have detailed explanations: E0230, E0231, E0232.
For more information about an error, try `rustc --explain E0230`.
warning: positional format arguments are not allowed here
--> $DIR/bad-annotation.rs:25:90
|
LL | #[rustc_on_unimplemented(label = "Unimplemented 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: aborting due to 16 previous errors; 5 warnings emitted
For more information about this error, try `rustc --explain E0232`.
@@ -1,6 +1,6 @@
// Test that `#[rustc_on_unimplemented]` is gated by `rustc_attrs` feature gate.
#[rustc_on_unimplemented = "test error `{Self}` with `{Bar}`"]
#[rustc_on_unimplemented(label = "test error `{Self}` with `{Bar}`")]
//~^ ERROR use of an internal attribute [E0658]
//~| NOTE the `#[rustc_on_unimplemented]` attribute is an internal implementation detail that will never be stable
//~| NOTE see `#[diagnostic::on_unimplemented]` for the stable equivalent of this attribute
@@ -1,8 +1,8 @@
error[E0658]: use of an internal attribute
--> $DIR/feature-gate-on-unimplemented.rs:3:1
|
LL | #[rustc_on_unimplemented = "test error `{Self}` with `{Bar}`"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | #[rustc_on_unimplemented(label = "test error `{Self}` with `{Bar}`")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
= note: the `#[rustc_on_unimplemented]` attribute is an internal implementation detail that will never be stable
+2 -2
View File
@@ -3,7 +3,7 @@
#![feature(rustc_attrs)]
pub mod Bar {
#[rustc_on_unimplemented = "test error `{Self}` with `{Bar}` `{Baz}` `{Quux}` in `{This}`"]
#[rustc_on_unimplemented(label ="test error `{Self}` with `{Bar}` `{Baz}` `{Quux}` in `{This}`")]
pub trait Foo<Bar, Baz, Quux> {}
}
@@ -13,7 +13,7 @@ fn foobar<U: Clone, T: Foo<u8, U, u32>>() -> T {
panic!()
}
#[rustc_on_unimplemented="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`"]
#[rustc_on_unimplemented(label ="a collection of type `{Self}` cannot be built from an iterator over elements of type `{A}`")]
trait MyFromIterator<A> {
/// Builds a container with elements from an external iterator.
fn my_from_iter<T: Iterator<Item=A>>(iterator: T) -> Self;
+11
View File
@@ -1,6 +1,7 @@
//@ compile-flags: -Zunpretty=hir
//@ check-pass
//@ edition: 2015
#![feature(diagnostic_on_const)]
#[diagnostic::on_unimplemented(
message = "My Message for `ImportantTrait<{A}>` implemented for `{Self}`",
@@ -12,3 +13,13 @@ pub trait ImportantTrait<A> {}
#[diagnostic::do_not_recommend]
impl<T> ImportantTrait<T> for T where T: Clone {}
pub struct X;
#[diagnostic::on_const(
message = "My Message for `ImportantTrait<u8>` implemented for `{Self}`",
label = "My Label",
note = "Note 1",
note = "Note 2"
)]
impl ImportantTrait<u8> for X {}
+21 -3
View File
@@ -1,3 +1,4 @@
#![attr = Feature([diagnostic_on_const#0])]
extern crate std;
#[attr = PreludeImport]
use ::std::prelude::rust_2015::*;
@@ -5,10 +6,27 @@ use ::std::prelude::rust_2015::*;
//@ check-pass
//@ edition: 2015
#[diagnostic::on_unimplemented(message =
"My Message for `ImportantTrait<{A}>` implemented for `{Self}`", label =
"My Label", note = "Note 1", note = "Note 2")]
#[attr = OnUnimplemented {directive: Directive {is_rustc_attr: false,
subcommands: [],
message: FormatString {input: "My Message for `ImportantTrait<{A}>` implemented for `{Self}`",
pieces: [Lit("My Message for `ImportantTrait<"),
Arg(GenericParam {generic_param: "A"}), Lit(">` implemented for `"),
Arg(SelfUpper), Lit("`")]}, label: FormatString {input: "My Label",
pieces: [Lit("My Label")]}, notes: [FormatString {input: "Note 1",
pieces: [Lit("Note 1")]}, FormatString {input: "Note 2",
pieces: [Lit("Note 2")]}]}}]
trait ImportantTrait<A> { }
#[attr = DoNotRecommend]
impl <T> ImportantTrait<T> for T where T: Clone { }
struct X;
#[attr = OnConst {directive: Directive {is_rustc_attr: false, subcommands: [],
message: FormatString {input: "My Message for `ImportantTrait<u8>` implemented for `{Self}`",
pieces: [Lit("My Message for `ImportantTrait<u8>` implemented for `"),
Arg(SelfUpper), Lit("`")]}, label: FormatString {input: "My Label",
pieces: [Lit("My Label")]}, notes: [FormatString {input: "Note 1",
pieces: [Lit("Note 1")]}, FormatString {input: "Note 2",
pieces: [Lit("Note 2")]}]}}]
impl ImportantTrait<u8> for X { }