add on_unmatch_args

This commit is contained in:
yukang
2026-04-19 20:57:37 +08:00
parent cf1817bc6e
commit 8d75f0cbfc
45 changed files with 576 additions and 11 deletions
@@ -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 {
+28 -2
View File
@@ -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(_))
+31 -6
View File
@@ -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)
}
+2
View File
@@ -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)
+18 -1
View File
@@ -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 {
+7
View File
@@ -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,
}
+1
View File
@@ -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)
+2
View File
@@ -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,
+3
View File
@@ -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
+1
View File
@@ -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)]
+3
View File
@@ -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
}
@@ -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);
}
@@ -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
@@ -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);
}
@@ -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);
}
@@ -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);
}
@@ -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