mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-26 13:01:27 +03:00
add on_unmatch_args
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
pub(crate) mod on_move;
|
||||
pub(crate) mod on_unimplemented;
|
||||
pub(crate) mod on_unknown;
|
||||
pub(crate) mod on_unmatch_args;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum Mode {
|
||||
@@ -38,6 +39,8 @@ pub(crate) enum Mode {
|
||||
DiagnosticOnMove,
|
||||
/// `#[diagnostic::on_unknown]`
|
||||
DiagnosticOnUnknown,
|
||||
/// `#[diagnostic::on_unmatch_args]`
|
||||
DiagnosticOnUnmatchArgs,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
@@ -48,6 +51,7 @@ fn as_str(&self) -> &'static str {
|
||||
Self::DiagnosticOnConst => "diagnostic::on_const",
|
||||
Self::DiagnosticOnMove => "diagnostic::on_move",
|
||||
Self::DiagnosticOnUnknown => "diagnostic::on_unknown",
|
||||
Self::DiagnosticOnUnmatchArgs => "diagnostic::on_unmatch_args",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +66,7 @@ fn expected_options(&self) -> &'static str {
|
||||
Self::DiagnosticOnConst => DEFAULT,
|
||||
Self::DiagnosticOnMove => DEFAULT,
|
||||
Self::DiagnosticOnUnknown => DEFAULT,
|
||||
Self::DiagnosticOnUnmatchArgs => DEFAULT,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +80,7 @@ fn allowed_options(&self) -> &'static str {
|
||||
Self::DiagnosticOnConst => DEFAULT,
|
||||
Self::DiagnosticOnMove => DEFAULT,
|
||||
Self::DiagnosticOnUnknown => DEFAULT,
|
||||
Self::DiagnosticOnUnmatchArgs => DEFAULT,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -398,7 +404,9 @@ fn parse_arg(
|
||||
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 { .. } | Mode::DiagnosticOnUnmatchArgs, sym::This) => {
|
||||
FormatArg::This
|
||||
}
|
||||
(Mode::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait,
|
||||
// Any attribute can use these
|
||||
(_, kw::SelfUpper) => FormatArg::SelfUpper,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
use rustc_errors::Diagnostic;
|
||||
use rustc_hir::attrs::diagnostic::Directive;
|
||||
use rustc_session::lint::builtin::MISPLACED_DIAGNOSTIC_ATTRIBUTES;
|
||||
|
||||
use crate::attributes::diagnostic::*;
|
||||
use crate::attributes::prelude::*;
|
||||
use crate::errors::DiagnosticOnUnmatchArgsOnlyForMacros;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct OnUnmatchArgsParser {
|
||||
span: Option<Span>,
|
||||
directive: Option<(Span, Directive)>,
|
||||
}
|
||||
|
||||
impl<S: Stage> AttributeParser<S> for OnUnmatchArgsParser {
|
||||
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
|
||||
&[sym::diagnostic, sym::on_unmatch_args],
|
||||
template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]),
|
||||
|this, cx, args| {
|
||||
if !cx.features().diagnostic_on_unmatch_args() {
|
||||
return;
|
||||
}
|
||||
|
||||
let span = cx.attr_span;
|
||||
this.span = Some(span);
|
||||
|
||||
if !matches!(cx.target, Target::MacroDef) {
|
||||
cx.emit_dyn_lint(
|
||||
MISPLACED_DIAGNOSTIC_ATTRIBUTES,
|
||||
move |dcx, level| DiagnosticOnUnmatchArgsOnlyForMacros.into_diag(dcx, level),
|
||||
span,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let mode = Mode::DiagnosticOnUnmatchArgs;
|
||||
let Some(items) = parse_list(cx, args, mode) else { return };
|
||||
|
||||
let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) else {
|
||||
return;
|
||||
};
|
||||
merge_directives(cx, &mut this.directive, (span, directive));
|
||||
},
|
||||
)];
|
||||
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
|
||||
|
||||
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
|
||||
if let Some(span) = self.span {
|
||||
Some(AttributeKind::OnUnmatchArgs {
|
||||
span,
|
||||
directive: self.directive.map(|d| Box::new(d.1)),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@
|
||||
use crate::attributes::diagnostic::on_move::*;
|
||||
use crate::attributes::diagnostic::on_unimplemented::*;
|
||||
use crate::attributes::diagnostic::on_unknown::*;
|
||||
use crate::attributes::diagnostic::on_unmatch_args::*;
|
||||
use crate::attributes::doc::*;
|
||||
use crate::attributes::dummy::*;
|
||||
use crate::attributes::inline::*;
|
||||
@@ -159,6 +160,7 @@ mod late {
|
||||
OnMoveParser,
|
||||
OnUnimplementedParser,
|
||||
OnUnknownParser,
|
||||
OnUnmatchArgsParser,
|
||||
RustcAlignParser,
|
||||
RustcAlignStaticParser,
|
||||
RustcCguTestAttributeParser,
|
||||
|
||||
@@ -315,6 +315,10 @@ pub(crate) struct DiagnosticOnUnknownOnlyForImports {
|
||||
pub target_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("`#[diagnostic::on_unmatch_args]` can only be applied to macro definitions")]
|
||||
pub(crate) struct DiagnosticOnUnmatchArgsOnlyForMacros;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("`#[diagnostic::do_not_recommend]` can only be placed on trait implementations")]
|
||||
pub(crate) struct IncorrectDoNotRecommendLocation {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use rustc_ast::token::{self, Token};
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage};
|
||||
use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs};
|
||||
use rustc_macros::Subdiagnostic;
|
||||
use rustc_parse::parser::{Parser, Recovery, token_descr};
|
||||
use rustc_session::parse::ParseSess;
|
||||
@@ -32,6 +33,7 @@ pub(super) fn failed_to_match_macro(
|
||||
args: FailedMacro<'_>,
|
||||
body: &TokenStream,
|
||||
rules: &[MacroRule],
|
||||
on_unmatch_args: Option<&Directive>,
|
||||
) -> (Span, ErrorGuaranteed) {
|
||||
debug!("failed to match macro");
|
||||
let def_head_span = if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) {
|
||||
@@ -72,9 +74,30 @@ pub(super) fn failed_to_match_macro(
|
||||
};
|
||||
|
||||
let span = token.span.substitute_dummy(sp);
|
||||
let CustomDiagnostic {
|
||||
message: custom_message, label: custom_label, notes: custom_notes, ..
|
||||
} = {
|
||||
let macro_name = name.to_string();
|
||||
on_unmatch_args
|
||||
.map(|directive| {
|
||||
directive.eval(
|
||||
None,
|
||||
&FormatArgs {
|
||||
this: macro_name.clone(),
|
||||
this_sugared: macro_name,
|
||||
item_context: "macro invocation",
|
||||
generic_args: Vec::new(),
|
||||
},
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
let mut err = psess.dcx().struct_span_err(span, parse_failure_msg(&token, None));
|
||||
err.span_label(span, label);
|
||||
let mut err = match custom_message {
|
||||
Some(message) => psess.dcx().struct_span_err(span, message),
|
||||
None => psess.dcx().struct_span_err(span, parse_failure_msg(&token, None)),
|
||||
};
|
||||
err.span_label(span, custom_label.unwrap_or_else(|| label.to_string()));
|
||||
if !def_head_span.is_dummy() {
|
||||
err.span_label(def_head_span, "when calling this macro");
|
||||
}
|
||||
@@ -86,6 +109,9 @@ pub(super) fn failed_to_match_macro(
|
||||
} else {
|
||||
err.note(format!("while trying to match {remaining_matcher}"));
|
||||
}
|
||||
for note in custom_notes {
|
||||
err.note(note);
|
||||
}
|
||||
|
||||
if let MatcherLoc::Token { token: expected_token } = &remaining_matcher
|
||||
&& (matches!(expected_token.kind, token::OpenInvisible(_))
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan};
|
||||
use rustc_feature::Features;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::attrs::diagnostic::Directive;
|
||||
use rustc_hir::def::MacroKinds;
|
||||
use rustc_hir::find_attr;
|
||||
use rustc_lint_defs::builtin::{
|
||||
@@ -164,6 +165,7 @@ pub struct MacroRulesMacroExpander {
|
||||
node_id: NodeId,
|
||||
name: Ident,
|
||||
span: Span,
|
||||
on_unmatch_args: Option<Directive>,
|
||||
transparency: Transparency,
|
||||
kinds: MacroKinds,
|
||||
rules: Vec<MacroRule>,
|
||||
@@ -194,7 +196,8 @@ pub fn expand_derive(
|
||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||
// This is similar to `expand_macro`, but they have very different signatures, and will
|
||||
// diverge further once derives support arguments.
|
||||
let Self { name, ref rules, node_id, .. } = *self;
|
||||
let name = self.name;
|
||||
let rules = &self.rules;
|
||||
let psess = &cx.sess.psess;
|
||||
|
||||
if cx.trace_macros() {
|
||||
@@ -220,8 +223,8 @@ pub fn expand_derive(
|
||||
trace_macros_note(&mut cx.expansions, sp, msg);
|
||||
}
|
||||
|
||||
if is_defined_in_current_crate(node_id) {
|
||||
cx.resolver.record_macro_rule_usage(node_id, rule_index);
|
||||
if is_defined_in_current_crate(self.node_id) {
|
||||
cx.resolver.record_macro_rule_usage(self.node_id, rule_index);
|
||||
}
|
||||
|
||||
Ok(tts)
|
||||
@@ -236,6 +239,7 @@ pub fn expand_derive(
|
||||
FailedMacro::Derive,
|
||||
body,
|
||||
rules,
|
||||
self.on_unmatch_args.as_ref(),
|
||||
);
|
||||
cx.macro_error_and_trace_macros_diag();
|
||||
Err(guar)
|
||||
@@ -260,6 +264,7 @@ fn expand<'cx, 'a: 'cx>(
|
||||
self.transparency,
|
||||
input,
|
||||
&self.rules,
|
||||
self.on_unmatch_args.as_ref(),
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -294,6 +299,7 @@ fn expand_with_safety(
|
||||
args,
|
||||
body,
|
||||
&self.rules,
|
||||
self.on_unmatch_args.as_ref(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -355,7 +361,7 @@ fn description() -> &'static str {
|
||||
}
|
||||
|
||||
/// Expands the rules based macro defined by `rules` for a given input `arg`.
|
||||
#[instrument(skip(cx, transparency, arg, rules))]
|
||||
#[instrument(skip(cx, transparency, arg, rules, on_unmatch_args))]
|
||||
fn expand_macro<'cx, 'a: 'cx>(
|
||||
cx: &'cx mut ExtCtxt<'_>,
|
||||
sp: Span,
|
||||
@@ -365,6 +371,7 @@ fn expand_macro<'cx, 'a: 'cx>(
|
||||
transparency: Transparency,
|
||||
arg: TokenStream,
|
||||
rules: &'a [MacroRule],
|
||||
on_unmatch_args: Option<&Directive>,
|
||||
) -> Box<dyn MacResult + 'cx> {
|
||||
let psess = &cx.sess.psess;
|
||||
|
||||
@@ -423,6 +430,7 @@ fn expand_macro<'cx, 'a: 'cx>(
|
||||
FailedMacro::Func,
|
||||
&arg,
|
||||
rules,
|
||||
on_unmatch_args,
|
||||
);
|
||||
cx.macro_error_and_trace_macros_diag();
|
||||
DummyResult::any(span, guar)
|
||||
@@ -431,7 +439,7 @@ fn expand_macro<'cx, 'a: 'cx>(
|
||||
}
|
||||
|
||||
/// Expands the rules based macro defined by `rules` for a given attribute `args` and `body`.
|
||||
#[instrument(skip(cx, transparency, args, body, rules))]
|
||||
#[instrument(skip(cx, transparency, args, body, rules, on_unmatch_args))]
|
||||
fn expand_macro_attr(
|
||||
cx: &mut ExtCtxt<'_>,
|
||||
sp: Span,
|
||||
@@ -443,6 +451,7 @@ fn expand_macro_attr(
|
||||
args: TokenStream,
|
||||
body: TokenStream,
|
||||
rules: &[MacroRule],
|
||||
on_unmatch_args: Option<&Directive>,
|
||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||
let psess = &cx.sess.psess;
|
||||
// Macros defined in the current crate have a real node id,
|
||||
@@ -507,6 +516,7 @@ fn expand_macro_attr(
|
||||
FailedMacro::Attr(&args),
|
||||
&body,
|
||||
rules,
|
||||
on_unmatch_args,
|
||||
);
|
||||
cx.trace_macros_diag();
|
||||
Err(guar)
|
||||
@@ -849,7 +859,22 @@ pub fn compile_declarative_macro(
|
||||
// Return the number of rules for unused rule linting, if this is a local macro.
|
||||
let nrules = if is_defined_in_current_crate(node_id) { rules.len() } else { 0 };
|
||||
|
||||
let exp = MacroRulesMacroExpander { name: ident, kinds, span, node_id, transparency, rules };
|
||||
let on_unmatch_args = find_attr!(
|
||||
attrs,
|
||||
OnUnmatchArgs { directive, .. } => directive.clone()
|
||||
)
|
||||
.flatten()
|
||||
.map(|directive| *directive);
|
||||
|
||||
let exp = MacroRulesMacroExpander {
|
||||
name: ident,
|
||||
kinds,
|
||||
span,
|
||||
node_id,
|
||||
on_unmatch_args,
|
||||
transparency,
|
||||
rules,
|
||||
};
|
||||
(mk_syn_ext(SyntaxExtensionKind::MacroRules(Arc::new(exp))), nrules)
|
||||
}
|
||||
|
||||
|
||||
@@ -480,6 +480,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
|
||||
(unstable, diagnostic_on_move, "1.96.0", Some(154181)),
|
||||
/// Allows giving unresolved imports a custom diagnostic message
|
||||
(unstable, diagnostic_on_unknown, "1.96.0", Some(152900)),
|
||||
/// Allows macros to customize macro argument matcher diagnostics.
|
||||
(unstable, diagnostic_on_unmatch_args, "CURRENT_RUSTC_VERSION", Some(155642)),
|
||||
/// Allows `#[doc(cfg(...))]`.
|
||||
(unstable, doc_cfg, "1.21.0", Some(43781)),
|
||||
/// Allows `#[doc(masked)]`.
|
||||
|
||||
@@ -1208,6 +1208,13 @@ pub enum AttributeKind {
|
||||
directive: Option<Box<Directive>>,
|
||||
},
|
||||
|
||||
/// Represents `#[diagnostic::on_unmatch_args]`.
|
||||
OnUnmatchArgs {
|
||||
span: Span,
|
||||
/// None if the directive was malformed in some way.
|
||||
directive: Option<Box<Directive>>,
|
||||
},
|
||||
|
||||
/// Represents `#[optimize(size|speed)]`
|
||||
Optimize(OptimizeAttr, Span),
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
|
||||
OnMove { .. } => Yes,
|
||||
OnUnimplemented { .. } => Yes,
|
||||
OnUnknown { .. } => Yes,
|
||||
OnUnmatchArgs { .. } => Yes,
|
||||
Optimize(..) => No,
|
||||
PanicRuntime => No,
|
||||
PatchableFunctionEntry { .. } => Yes,
|
||||
|
||||
@@ -52,7 +52,6 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
|
||||
lints::MalFormedDiagnosticAttributeLint { attribute, options, span }
|
||||
.into_diag(dcx, level)
|
||||
}
|
||||
|
||||
AttributeLintKind::MalformedDiagnosticFormat { warning } => match warning {
|
||||
FormatWarning::PositionalArgument { .. } => {
|
||||
lints::DisallowedPositionalArgument.into_diag(dcx, level)
|
||||
|
||||
@@ -200,7 +200,10 @@ fn check_attributes(
|
||||
self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id,target)
|
||||
},
|
||||
Attribute::Parsed(AttributeKind::OnUnimplemented{directive,..}) => {self.check_diagnostic_on_unimplemented(hir_id, directive.as_deref())},
|
||||
Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)}
|
||||
Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)},
|
||||
Attribute::Parsed(AttributeKind::OnUnmatchArgs { directive, .. }) => {
|
||||
self.check_diagnostic_on_unmatch_args(hir_id, directive.as_deref())
|
||||
},
|
||||
Attribute::Parsed(AttributeKind::OnMove { directive , .. }) => {
|
||||
self.check_diagnostic_on_move(hir_id, directive.as_deref())
|
||||
},
|
||||
@@ -559,6 +562,20 @@ fn check_diagnostic_on_const(
|
||||
// ...whose generics would that be, anyway? The traits' or the impls'?
|
||||
}
|
||||
|
||||
/// Checks use of generic formatting parameters in `#[diagnostic::on_unmatch_args]`.
|
||||
fn check_diagnostic_on_unmatch_args(&self, hir_id: HirId, directive: Option<&Directive>) {
|
||||
if let Some(directive) = directive {
|
||||
directive.visit_params(&mut |argument_name, span| {
|
||||
self.tcx.emit_node_span_lint(
|
||||
MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
|
||||
hir_id,
|
||||
span,
|
||||
errors::OnUnmatchArgsMalformedFormatLiterals { name: argument_name },
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks use of generic formatting parameters in `#[diagnostic::on_move]`
|
||||
fn check_diagnostic_on_move(&self, hir_id: HirId, directive: Option<&Directive>) {
|
||||
if let Some(directive) = directive {
|
||||
|
||||
@@ -1303,3 +1303,10 @@ pub(crate) struct UnknownFormatParameterForOnUnimplementedAttr {
|
||||
pub(crate) struct OnMoveMalformedFormatLiterals {
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag("unknown parameter `{$name}`")]
|
||||
#[help(r#"use {"`{This}`"} to refer to the macro name"#)]
|
||||
pub(crate) struct OnUnmatchArgsMalformedFormatLiterals {
|
||||
pub name: Symbol,
|
||||
}
|
||||
|
||||
@@ -710,6 +710,7 @@ fn smart_resolve_macro_path(
|
||||
(sym::on_move, Some(sym::diagnostic_on_move)),
|
||||
(sym::on_const, Some(sym::diagnostic_on_const)),
|
||||
(sym::on_unknown, Some(sym::diagnostic_on_unknown)),
|
||||
(sym::on_unmatch_args, Some(sym::diagnostic_on_unmatch_args)),
|
||||
];
|
||||
|
||||
if res == Res::NonMacroAttr(NonMacroAttrKind::Tool)
|
||||
|
||||
@@ -803,6 +803,7 @@
|
||||
diagnostic_on_const,
|
||||
diagnostic_on_move,
|
||||
diagnostic_on_unknown,
|
||||
diagnostic_on_unmatch_args,
|
||||
dialect,
|
||||
direct,
|
||||
discriminant_kind,
|
||||
@@ -1427,6 +1428,7 @@
|
||||
on_move,
|
||||
on_unimplemented,
|
||||
on_unknown,
|
||||
on_unmatch_args,
|
||||
opaque,
|
||||
opaque_module_name_placeholder: "<opaque>",
|
||||
ops,
|
||||
|
||||
@@ -45,6 +45,9 @@ fn clone(&self) -> Self {
|
||||
/// variant must also be specified. Only a single field is supported.
|
||||
#[unstable(feature = "field_projections", issue = "145383")]
|
||||
#[allow_internal_unstable(field_representing_type_raw, builtin_syntax)]
|
||||
#[diagnostic::on_unmatch_args(
|
||||
note = "this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)`"
|
||||
)]
|
||||
// NOTE: when stabilizing this macro, we can never add new trait impls for `FieldRepresentingType`,
|
||||
// since it is `#[fundamental]` and thus could break users of this macro, since the compiler expands
|
||||
// it to `FieldRepresentingType<...>`. Thus stabilizing this requires careful thought about the
|
||||
|
||||
@@ -131,6 +131,7 @@
|
||||
#![feature(deprecated_suggestion)]
|
||||
#![feature(derive_const)]
|
||||
#![feature(diagnostic_on_const)]
|
||||
#![feature(diagnostic_on_unmatch_args)]
|
||||
#![feature(doc_cfg)]
|
||||
#![feature(doc_notable_trait)]
|
||||
#![feature(extern_types)]
|
||||
|
||||
@@ -1567,6 +1567,9 @@ impl<T> SizedTypeProperties for T {}
|
||||
/// [`offset_of_enum`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/offset-of-enum.html
|
||||
/// [`offset_of_slice`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/offset-of-slice.html
|
||||
#[stable(feature = "offset_of", since = "1.77.0")]
|
||||
#[diagnostic::on_unmatch_args(
|
||||
note = "this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)`"
|
||||
)]
|
||||
#[allow_internal_unstable(builtin_syntax, core_intrinsics)]
|
||||
pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) {
|
||||
// The `{}` is for better error messages
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# `diagnostic_on_unmatch_args`
|
||||
|
||||
The tracking issue for this feature is: [#155642]
|
||||
|
||||
[#155642]: https://github.com/rust-lang/rust/issues/155642
|
||||
|
||||
------------------------
|
||||
|
||||
The `diagnostic_on_unmatch_args` feature adds the
|
||||
`#[diagnostic::on_unmatch_args(...)]` attribute for declarative macros.
|
||||
It lets a macro definition customize diagnostics for matcher failures after all arms have been
|
||||
tried, such as incomplete invocations or trailing extra arguments.
|
||||
|
||||
This attribute currently applies to declarative macros such as `macro_rules!` and `pub macro`.
|
||||
It is currently used for errors emitted by declarative macro matching itself; fragment parser
|
||||
errors still use their existing diagnostics.
|
||||
|
||||
```rust,compile_fail
|
||||
#![feature(diagnostic_on_unmatch_args)]
|
||||
|
||||
#[diagnostic::on_unmatch_args(
|
||||
message = "invalid arguments to {This} macro invocation",
|
||||
label = "expected a type and value here",
|
||||
note = "this macro expects a type and a value, like `pair!(u8, 0)`",
|
||||
note = "see <link/to/docs>",
|
||||
)]
|
||||
macro_rules! pair {
|
||||
($ty:ty, $value:expr) => {};
|
||||
}
|
||||
|
||||
pair!(u8);
|
||||
```
|
||||
|
||||
This emits output like:
|
||||
|
||||
```text
|
||||
error: invalid arguments to pair macro invocation
|
||||
--> example.rs:13:9
|
||||
|
|
||||
9 | macro_rules! pair {
|
||||
| ----------------- when calling this macro
|
||||
...
|
||||
13 | pair!(u8);
|
||||
| ^ expected a type and value here
|
||||
|
|
||||
note: while trying to match `,`
|
||||
--> example.rs:10:12
|
||||
|
|
||||
10 | ($ty:ty, $value:expr) => {};
|
||||
| ^
|
||||
= note: this macro expects a type and a value, like `pair!(u8, 0)`
|
||||
= note: see <link/to/docs>
|
||||
```
|
||||
@@ -0,0 +1,12 @@
|
||||
#![feature(diagnostic_on_unmatch_args)]
|
||||
|
||||
#[macro_export]
|
||||
#[diagnostic::on_unmatch_args(
|
||||
message = "invalid arguments to {This} macro invocation",
|
||||
label = "expected a type and value here",
|
||||
note = "this macro expects a type and a value, like `pair!(u8, 0)`",
|
||||
note = "see the macro documentation for accepted forms",
|
||||
)]
|
||||
macro_rules! pair {
|
||||
($ty:ty, $value:expr) => {};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
//@ aux-build:other.rs
|
||||
|
||||
extern crate other;
|
||||
|
||||
fn main() {
|
||||
other::pair!(u8);
|
||||
//~^ ERROR invalid arguments to pair macro invocation
|
||||
//~| NOTE expected a type and value here
|
||||
//~| NOTE while trying to match `,`
|
||||
//~| NOTE this macro expects a type and a value, like `pair!(u8, 0)`
|
||||
//~| NOTE see the macro documentation for accepted forms
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
error: invalid arguments to pair macro invocation
|
||||
--> $DIR/error_is_shown_in_downstream_crates.rs:6:20
|
||||
|
|
||||
LL | other::pair!(u8);
|
||||
| ^ expected a type and value here
|
||||
|
|
||||
note: while trying to match `,`
|
||||
--> $DIR/auxiliary/other.rs:11:12
|
||||
|
|
||||
LL | ($ty:ty, $value:expr) => {};
|
||||
| ^
|
||||
= note: this macro expects a type and a value, like `pair!(u8, 0)`
|
||||
= note: see the macro documentation for accepted forms
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#![feature(diagnostic_on_unmatch_args)]
|
||||
|
||||
#[diagnostic::on_unmatch_args(
|
||||
message = "invalid arguments to {This} macro invocation",
|
||||
label = "expected a type and value here",
|
||||
note = "this macro expects a type and a value, like `pair!(u8, 0)`",
|
||||
note = "see the macro documentation for accepted forms",
|
||||
)]
|
||||
macro_rules! pair {
|
||||
//~^ NOTE when calling this macro
|
||||
($ty:ty, $value:expr) => {};
|
||||
//~^ NOTE while trying to match `,`
|
||||
}
|
||||
|
||||
fn main() {
|
||||
pair!(u8);
|
||||
//~^ ERROR invalid arguments to pair macro invocation
|
||||
//~| NOTE expected a type and value here
|
||||
//~| NOTE this macro expects a type and a value, like `pair!(u8, 0)`
|
||||
//~| NOTE see the macro documentation for accepted forms
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
error: invalid arguments to pair macro invocation
|
||||
--> $DIR/message_and_label.rs:16:13
|
||||
|
|
||||
LL | macro_rules! pair {
|
||||
| ----------------- when calling this macro
|
||||
...
|
||||
LL | pair!(u8);
|
||||
| ^ expected a type and value here
|
||||
|
|
||||
note: while trying to match `,`
|
||||
--> $DIR/message_and_label.rs:11:12
|
||||
|
|
||||
LL | ($ty:ty, $value:expr) => {};
|
||||
| ^
|
||||
= note: this macro expects a type and a value, like `pair!(u8, 0)`
|
||||
= note: see the macro documentation for accepted forms
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#![feature(diagnostic_on_unmatch_args)]
|
||||
|
||||
#[diagnostic::on_unmatch_args(
|
||||
message = "{This}! expects exactly two arguments",
|
||||
label = "unexpected extra input starts here",
|
||||
note = "this macro expects a type and a value, like `pair!(u8, 0)`",
|
||||
note = "make sure to pass both arguments",
|
||||
)]
|
||||
macro_rules! pair {
|
||||
//~^ NOTE when calling this macro
|
||||
($ty:ty, $value:expr) => {};
|
||||
//~^ NOTE while trying to match meta-variable `$value:expr`
|
||||
}
|
||||
|
||||
fn main() {
|
||||
pair!(u8, 0, 42);
|
||||
//~^ ERROR pair! expects exactly two arguments
|
||||
//~| NOTE unexpected extra input starts here
|
||||
//~| NOTE this macro expects a type and a value, like `pair!(u8, 0)`
|
||||
//~| NOTE make sure to pass both arguments
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
error: pair! expects exactly two arguments
|
||||
--> $DIR/notes_on_extra_args.rs:16:16
|
||||
|
|
||||
LL | macro_rules! pair {
|
||||
| ----------------- when calling this macro
|
||||
...
|
||||
LL | pair!(u8, 0, 42);
|
||||
| ^ unexpected extra input starts here
|
||||
|
|
||||
note: while trying to match meta-variable `$value:expr`
|
||||
--> $DIR/notes_on_extra_args.rs:11:14
|
||||
|
|
||||
LL | ($ty:ty, $value:expr) => {};
|
||||
| ^^^^^^^^^^^
|
||||
= note: this macro expects a type and a value, like `pair!(u8, 0)`
|
||||
= note: make sure to pass both arguments
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#![feature(diagnostic_on_unmatch_args)]
|
||||
|
||||
#[diagnostic::on_unmatch_args(
|
||||
note = "this macro expects a type and a value, like `pair!(u8, 0)`",
|
||||
note = "make sure to pass both arguments",
|
||||
)]
|
||||
macro_rules! pair {
|
||||
//~^ NOTE when calling this macro
|
||||
($ty:ty, $value:expr) => {};
|
||||
//~^ NOTE while trying to match `,`
|
||||
}
|
||||
|
||||
fn main() {
|
||||
pair!(u8);
|
||||
//~^ ERROR unexpected end of macro invocation
|
||||
//~| NOTE missing tokens in macro arguments
|
||||
//~| NOTE this macro expects a type and a value, like `pair!(u8, 0)`
|
||||
//~| NOTE make sure to pass both arguments
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
error: unexpected end of macro invocation
|
||||
--> $DIR/on_unmatch_args.rs:14:13
|
||||
|
|
||||
LL | macro_rules! pair {
|
||||
| ----------------- when calling this macro
|
||||
...
|
||||
LL | pair!(u8);
|
||||
| ^ missing tokens in macro arguments
|
||||
|
|
||||
note: while trying to match `,`
|
||||
--> $DIR/on_unmatch_args.rs:9:12
|
||||
|
|
||||
LL | ($ty:ty, $value:expr) => {};
|
||||
| ^
|
||||
= note: this macro expects a type and a value, like `pair!(u8, 0)`
|
||||
= note: make sure to pass both arguments
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#![feature(diagnostic_on_unmatch_args)]
|
||||
|
||||
#[diagnostic::on_unmatch_args(
|
||||
message = "invalid route method",
|
||||
note = "this macro expects a action, like `{This}!(get \"/hello\")`"
|
||||
)]
|
||||
macro_rules! route {
|
||||
(get $path:literal) => {};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
route!(post "/");
|
||||
//~^ ERROR invalid route method
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
error: invalid route method
|
||||
--> $DIR/other_match_macro_error.rs:12:12
|
||||
|
|
||||
LL | macro_rules! route {
|
||||
| ------------------ when calling this macro
|
||||
...
|
||||
LL | route!(post "/");
|
||||
| ^^^^ no rules expected this token in macro call
|
||||
|
|
||||
note: while trying to match `get`
|
||||
--> $DIR/other_match_macro_error.rs:8:6
|
||||
|
|
||||
LL | (get $path:literal) => {};
|
||||
| ^^^
|
||||
= note: this macro expects a action, like `route!(get "/hello")`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
//@ check-pass
|
||||
#![feature(diagnostic_on_unmatch_args)]
|
||||
|
||||
#[diagnostic::on_unmatch_args(
|
||||
message = "{T}! is missing arguments",
|
||||
//~^ WARN unknown parameter `T`
|
||||
)]
|
||||
macro_rules! pair {
|
||||
($ty:ty, $value:expr) => {};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
pair!(u8, 0);
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
warning: unknown parameter `T`
|
||||
--> $DIR/report_warning_on_invalid_formats.rs:5:17
|
||||
|
|
||||
LL | message = "{T}! is missing arguments",
|
||||
| ^
|
||||
|
|
||||
= help: use `{This}` to refer to the macro name
|
||||
= note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
//@ check-pass
|
||||
#![feature(diagnostic_on_unmatch_args)]
|
||||
|
||||
#[diagnostic::on_unmatch_args = "foo"]
|
||||
//~^ WARN malformed `diagnostic::on_unmatch_args` attribute [malformed_diagnostic_attributes]
|
||||
macro_rules! pair {
|
||||
($ty:ty, $value:expr) => {};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
pair!(u8, 0);
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
warning: malformed `diagnostic::on_unmatch_args` attribute
|
||||
--> $DIR/report_warning_on_invalid_meta_item_syntax.rs:4:1
|
||||
|
|
||||
LL | #[diagnostic::on_unmatch_args = "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: 1 warning emitted
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//@ check-pass
|
||||
#![feature(diagnostic_on_unmatch_args)]
|
||||
|
||||
#[diagnostic::on_unmatch_args]
|
||||
//~^ WARN missing options for `diagnostic::on_unmatch_args` attribute [malformed_diagnostic_attributes]
|
||||
macro_rules! pair {
|
||||
($ty:ty, $value:expr) => {};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
pair!(u8, 0);
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
warning: missing options for `diagnostic::on_unmatch_args` attribute
|
||||
--> $DIR/report_warning_on_missing_options.rs:4:1
|
||||
|
|
||||
LL | #[diagnostic::on_unmatch_args]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= 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: 1 warning emitted
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
//@ check-pass
|
||||
#![feature(diagnostic_on_unmatch_args)]
|
||||
|
||||
#[diagnostic::on_unmatch_args(message = "not allowed here")]
|
||||
//~^ WARN `#[diagnostic::on_unmatch_args]` can only be applied to macro definitions
|
||||
struct Foo;
|
||||
|
||||
fn main() {
|
||||
let _ = Foo;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
warning: `#[diagnostic::on_unmatch_args]` can only be applied to macro definitions
|
||||
--> $DIR/report_warning_on_non_macro.rs:4:1
|
||||
|
|
||||
LL | #[diagnostic::on_unmatch_args(message = "not allowed here")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//@ check-pass
|
||||
#![feature(diagnostic_on_unmatch_args)]
|
||||
|
||||
#[diagnostic::on_unmatch_args(unsupported = "foo")]
|
||||
//~^ WARN malformed `diagnostic::on_unmatch_args` attribute [malformed_diagnostic_attributes]
|
||||
macro_rules! pair {
|
||||
($ty:ty, $value:expr) => {};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
pair!(u8, 0);
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
warning: malformed `diagnostic::on_unmatch_args` attribute
|
||||
--> $DIR/report_warning_on_unknown_options.rs:4:31
|
||||
|
|
||||
LL | #[diagnostic::on_unmatch_args(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: 1 warning emitted
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//! This is an unusual feature gate test, as it doesn't test the feature
|
||||
//! gate, but the fact that not adding the feature gate will cause the
|
||||
//! diagnostic to not emit the custom diagnostic message.
|
||||
#[diagnostic::on_unmatch_args(note = "custom note")]
|
||||
macro_rules! pair {
|
||||
//~^ NOTE when calling this macro
|
||||
($ty:ty, $value:expr) => {};
|
||||
//~^ NOTE while trying to match `,`
|
||||
}
|
||||
|
||||
fn main() {
|
||||
pair!(u8);
|
||||
//~^ ERROR unexpected end of macro invocation
|
||||
//~| NOTE missing tokens in macro arguments
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
error: unexpected end of macro invocation
|
||||
--> $DIR/feature-gate-diagnostic-on-unmatch-args.rs:12:13
|
||||
|
|
||||
LL | macro_rules! pair {
|
||||
| ----------------- when calling this macro
|
||||
...
|
||||
LL | pair!(u8);
|
||||
| ^ missing tokens in macro arguments
|
||||
|
|
||||
note: while trying to match `,`
|
||||
--> $DIR/feature-gate-diagnostic-on-unmatch-args.rs:7:12
|
||||
|
|
||||
LL | ($ty:ty, $value:expr) => {};
|
||||
| ^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
@@ -6,6 +6,7 @@ LL | let _: field_of!(Struct);
|
||||
|
|
||||
note: while trying to match `,`
|
||||
--> $SRC_DIR/core/src/field.rs:LL:COL
|
||||
= note: this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)`
|
||||
|
||||
error: unexpected end of macro invocation
|
||||
--> $DIR/invalid.rs:24:29
|
||||
@@ -15,6 +16,7 @@ LL | let _: field_of!(Struct,);
|
||||
|
|
||||
note: while trying to match meta-variable `$fields:expr`
|
||||
--> $SRC_DIR/core/src/field.rs:LL:COL
|
||||
= note: this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)`
|
||||
|
||||
error: no rules expected `extra`
|
||||
--> $DIR/invalid.rs:25:37
|
||||
@@ -23,6 +25,7 @@ LL | let _: field_of!(Struct, field, extra);
|
||||
| ^^^^^ no rules expected this token in macro call
|
||||
|
|
||||
= note: while trying to match sequence end
|
||||
= note: this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)`
|
||||
|
||||
error: offset_of expects dot-separated field and variant names
|
||||
--> $DIR/invalid.rs:27:28
|
||||
|
||||
@@ -6,6 +6,7 @@ LL | let _: field_of!(Struct);
|
||||
|
|
||||
note: while trying to match `,`
|
||||
--> $SRC_DIR/core/src/field.rs:LL:COL
|
||||
= note: this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)`
|
||||
|
||||
error: unexpected end of macro invocation
|
||||
--> $DIR/invalid.rs:24:29
|
||||
@@ -15,6 +16,7 @@ LL | let _: field_of!(Struct,);
|
||||
|
|
||||
note: while trying to match meta-variable `$fields:expr`
|
||||
--> $SRC_DIR/core/src/field.rs:LL:COL
|
||||
= note: this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)`
|
||||
|
||||
error: no rules expected `extra`
|
||||
--> $DIR/invalid.rs:25:37
|
||||
@@ -23,6 +25,7 @@ LL | let _: field_of!(Struct, field, extra);
|
||||
| ^^^^^ no rules expected this token in macro call
|
||||
|
|
||||
= note: while trying to match sequence end
|
||||
= note: this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)`
|
||||
|
||||
error: offset_of expects dot-separated field and variant names
|
||||
--> $DIR/invalid.rs:27:28
|
||||
|
||||
@@ -6,6 +6,7 @@ LL | offset_of!(NotEnoughArguments);
|
||||
|
|
||||
note: while trying to match `,`
|
||||
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||
= note: this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)`
|
||||
|
||||
error: unexpected end of macro invocation
|
||||
--> $DIR/offset-of-arg-count.rs:5:45
|
||||
@@ -15,6 +16,7 @@ LL | offset_of!(NotEnoughArgumentsWithAComma, );
|
||||
|
|
||||
note: while trying to match meta-variable `$fields:expr`
|
||||
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||
= note: this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)`
|
||||
|
||||
error: no rules expected `too`
|
||||
--> $DIR/offset-of-arg-count.rs:6:34
|
||||
@@ -23,6 +25,7 @@ LL | offset_of!(Container, field, too many arguments);
|
||||
| ^^^ no rules expected this token in macro call
|
||||
|
|
||||
= note: while trying to match sequence end
|
||||
= note: this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)`
|
||||
|
||||
error: unexpected token: `)`
|
||||
--> $DIR/offset-of-arg-count.rs:9:21
|
||||
|
||||
@@ -72,6 +72,7 @@ LL | offset_of!((u8, u8), +1);
|
||||
|
|
||||
note: while trying to match meta-variable `$fields:expr`
|
||||
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
||||
= note: this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)`
|
||||
|
||||
error: offset_of expects dot-separated field and variant names
|
||||
--> $DIR/offset-of-tuple.rs:7:26
|
||||
|
||||
Reference in New Issue
Block a user