mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
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:
+1
-1
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
pub use pretty_printing::PrintAttribute;
|
||||
|
||||
mod data_structures;
|
||||
pub mod diagnostic;
|
||||
mod encode_cross_crate;
|
||||
mod pretty_printing;
|
||||
|
||||
|
||||
@@ -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::{
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-322
@@ -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 {
|
||||
|
||||
@@ -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`.
|
||||
|
||||
-15
@@ -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 {}
|
||||
|
||||
+104
-239
@@ -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`.
|
||||
|
||||
-5
@@ -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) {}
|
||||
|
||||
+59
-104
@@ -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`.
|
||||
|
||||
-22
@@ -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
|
||||
}
|
||||
-63
@@ -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 {}
|
||||
|
||||
+7
-29
@@ -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`.
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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 { }
|
||||
|
||||
Reference in New Issue
Block a user