Skip to content

Commit 9518bba

Browse files
committed
add on_unmatch_args
1 parent ec2d669 commit 9518bba

44 files changed

Lines changed: 563 additions & 11 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub(crate) mod on_const;
2525
pub(crate) mod on_move;
2626
pub(crate) mod on_unimplemented;
2727
pub(crate) mod on_unknown;
28+
pub(crate) mod on_unmatch_args;
2829

2930
#[derive(Copy, Clone)]
3031
pub(crate) enum Mode {
@@ -38,6 +39,8 @@ pub(crate) enum Mode {
3839
DiagnosticOnMove,
3940
/// `#[diagnostic::on_unknown]`
4041
DiagnosticOnUnknown,
42+
/// `#[diagnostic::on_unmatch_args]`
43+
DiagnosticOnUnmatchArgs,
4144
}
4245

4346
impl Mode {
@@ -48,6 +51,7 @@ impl Mode {
4851
Self::DiagnosticOnConst => "diagnostic::on_const",
4952
Self::DiagnosticOnMove => "diagnostic::on_move",
5053
Self::DiagnosticOnUnknown => "diagnostic::on_unknown",
54+
Self::DiagnosticOnUnmatchArgs => "diagnostic::on_unmatch_args",
5155
}
5256
}
5357

@@ -62,6 +66,7 @@ impl Mode {
6266
Self::DiagnosticOnConst => DEFAULT,
6367
Self::DiagnosticOnMove => DEFAULT,
6468
Self::DiagnosticOnUnknown => DEFAULT,
69+
Self::DiagnosticOnUnmatchArgs => DEFAULT,
6570
}
6671
}
6772

@@ -75,6 +80,7 @@ impl Mode {
7580
Self::DiagnosticOnConst => DEFAULT,
7681
Self::DiagnosticOnMove => DEFAULT,
7782
Self::DiagnosticOnUnknown => DEFAULT,
83+
Self::DiagnosticOnUnmatchArgs => DEFAULT,
7884
}
7985
}
8086
}
@@ -398,7 +404,9 @@ fn parse_arg(
398404
Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) {
399405
// Only `#[rustc_on_unimplemented]` can use these
400406
(Mode::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext,
401-
(Mode::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This,
407+
(Mode::RustcOnUnimplemented { .. } | Mode::DiagnosticOnUnmatchArgs, sym::This) => {
408+
FormatArg::This
409+
}
402410
(Mode::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait,
403411
// Any attribute can use these
404412
(_, kw::SelfUpper) => FormatArg::SelfUpper,
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use rustc_hir::attrs::diagnostic::Directive;
2+
3+
use crate::attributes::diagnostic::*;
4+
use crate::attributes::prelude::*;
5+
6+
#[derive(Default)]
7+
pub(crate) struct OnUnmatchArgsParser {
8+
span: Option<Span>,
9+
directive: Option<(Span, Directive)>,
10+
}
11+
12+
impl<S: Stage> AttributeParser<S> for OnUnmatchArgsParser {
13+
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
14+
&[sym::diagnostic, sym::on_unmatch_args],
15+
template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]),
16+
|this, cx, args| {
17+
if !cx.features().diagnostic_on_unmatch_args() {
18+
return;
19+
}
20+
21+
let span = cx.attr_span;
22+
this.span = Some(span);
23+
24+
// Lint emitted in `check_attr.rs`.
25+
if !matches!(cx.target, Target::MacroDef) {
26+
return;
27+
}
28+
29+
let mode = Mode::DiagnosticOnUnmatchArgs;
30+
let Some(items) = parse_list(cx, args, mode) else { return };
31+
32+
let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) else {
33+
return;
34+
};
35+
merge_directives(cx, &mut this.directive, (span, directive));
36+
},
37+
)];
38+
39+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
40+
41+
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
42+
if let Some(span) = self.span {
43+
Some(AttributeKind::OnUnmatchArgs {
44+
span,
45+
directive: self.directive.map(|d| Box::new(d.1)),
46+
})
47+
} else {
48+
None
49+
}
50+
}
51+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use crate::attributes::diagnostic::on_const::*;
3333
use crate::attributes::diagnostic::on_move::*;
3434
use crate::attributes::diagnostic::on_unimplemented::*;
3535
use crate::attributes::diagnostic::on_unknown::*;
36+
use crate::attributes::diagnostic::on_unmatch_args::*;
3637
use crate::attributes::doc::*;
3738
use crate::attributes::dummy::*;
3839
use crate::attributes::inline::*;
@@ -159,6 +160,7 @@ attribute_parsers!(
159160
OnMoveParser,
160161
OnUnimplementedParser,
161162
OnUnknownParser,
163+
OnUnmatchArgsParser,
162164
RustcAlignParser,
163165
RustcAlignStaticParser,
164166
RustcCguTestAttributeParser,

compiler/rustc_expand/src/mbe/diagnostics.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::borrow::Cow;
33
use rustc_ast::token::{self, Token};
44
use rustc_ast::tokenstream::TokenStream;
55
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage};
6+
use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs};
67
use rustc_macros::Subdiagnostic;
78
use rustc_parse::parser::{Parser, Recovery, token_descr};
89
use rustc_session::parse::ParseSess;
@@ -32,6 +33,7 @@ pub(super) fn failed_to_match_macro(
3233
args: FailedMacro<'_>,
3334
body: &TokenStream,
3435
rules: &[MacroRule],
36+
on_unmatch_args: Option<&Directive>,
3537
) -> (Span, ErrorGuaranteed) {
3638
debug!("failed to match macro");
3739
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(
7274
};
7375

7476
let span = token.span.substitute_dummy(sp);
77+
let CustomDiagnostic {
78+
message: custom_message, label: custom_label, notes: custom_notes, ..
79+
} = {
80+
let macro_name = name.to_string();
81+
on_unmatch_args
82+
.map(|directive| {
83+
directive.eval(
84+
None,
85+
&FormatArgs {
86+
this: macro_name.clone(),
87+
this_sugared: macro_name,
88+
item_context: "macro invocation",
89+
generic_args: Vec::new(),
90+
},
91+
)
92+
})
93+
.unwrap_or_default()
94+
};
7595

76-
let mut err = psess.dcx().struct_span_err(span, parse_failure_msg(&token, None));
77-
err.span_label(span, label);
96+
let mut err = match custom_message {
97+
Some(message) => psess.dcx().struct_span_err(span, message),
98+
None => psess.dcx().struct_span_err(span, parse_failure_msg(&token, None)),
99+
};
100+
err.span_label(span, custom_label.unwrap_or_else(|| label.to_string()));
78101
if !def_head_span.is_dummy() {
79102
err.span_label(def_head_span, "when calling this macro");
80103
}
@@ -86,6 +109,9 @@ pub(super) fn failed_to_match_macro(
86109
} else {
87110
err.note(format!("while trying to match {remaining_matcher}"));
88111
}
112+
for note in custom_notes {
113+
err.note(note);
114+
}
89115

90116
if let MatcherLoc::Token { token: expected_token } = &remaining_matcher
91117
&& (matches!(expected_token.kind, token::OpenInvisible(_))

compiler/rustc_expand/src/mbe/macro_rules.rs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
1414
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan};
1515
use rustc_feature::Features;
1616
use rustc_hir as hir;
17+
use rustc_hir::attrs::diagnostic::Directive;
1718
use rustc_hir::def::MacroKinds;
1819
use rustc_hir::find_attr;
1920
use rustc_lint_defs::builtin::{
@@ -164,6 +165,7 @@ pub struct MacroRulesMacroExpander {
164165
node_id: NodeId,
165166
name: Ident,
166167
span: Span,
168+
on_unmatch_args: Option<Directive>,
167169
transparency: Transparency,
168170
kinds: MacroKinds,
169171
rules: Vec<MacroRule>,
@@ -194,7 +196,8 @@ impl MacroRulesMacroExpander {
194196
) -> Result<TokenStream, ErrorGuaranteed> {
195197
// This is similar to `expand_macro`, but they have very different signatures, and will
196198
// diverge further once derives support arguments.
197-
let Self { name, ref rules, node_id, .. } = *self;
199+
let name = self.name;
200+
let rules = &self.rules;
198201
let psess = &cx.sess.psess;
199202

200203
if cx.trace_macros() {
@@ -220,8 +223,8 @@ impl MacroRulesMacroExpander {
220223
trace_macros_note(&mut cx.expansions, sp, msg);
221224
}
222225

223-
if is_defined_in_current_crate(node_id) {
224-
cx.resolver.record_macro_rule_usage(node_id, rule_index);
226+
if is_defined_in_current_crate(self.node_id) {
227+
cx.resolver.record_macro_rule_usage(self.node_id, rule_index);
225228
}
226229

227230
Ok(tts)
@@ -236,6 +239,7 @@ impl MacroRulesMacroExpander {
236239
FailedMacro::Derive,
237240
body,
238241
rules,
242+
self.on_unmatch_args.as_ref(),
239243
);
240244
cx.macro_error_and_trace_macros_diag();
241245
Err(guar)
@@ -260,6 +264,7 @@ impl TTMacroExpander for MacroRulesMacroExpander {
260264
self.transparency,
261265
input,
262266
&self.rules,
267+
self.on_unmatch_args.as_ref(),
263268
))
264269
}
265270
}
@@ -294,6 +299,7 @@ impl AttrProcMacro for MacroRulesMacroExpander {
294299
args,
295300
body,
296301
&self.rules,
302+
self.on_unmatch_args.as_ref(),
297303
)
298304
}
299305
}
@@ -355,7 +361,7 @@ impl<'matcher> Tracker<'matcher> for NoopTracker {
355361
}
356362

357363
/// Expands the rules based macro defined by `rules` for a given input `arg`.
358-
#[instrument(skip(cx, transparency, arg, rules))]
364+
#[instrument(skip(cx, transparency, arg, rules, on_unmatch_args))]
359365
fn expand_macro<'cx, 'a: 'cx>(
360366
cx: &'cx mut ExtCtxt<'_>,
361367
sp: Span,
@@ -365,6 +371,7 @@ fn expand_macro<'cx, 'a: 'cx>(
365371
transparency: Transparency,
366372
arg: TokenStream,
367373
rules: &'a [MacroRule],
374+
on_unmatch_args: Option<&Directive>,
368375
) -> Box<dyn MacResult + 'cx> {
369376
let psess = &cx.sess.psess;
370377

@@ -423,6 +430,7 @@ fn expand_macro<'cx, 'a: 'cx>(
423430
FailedMacro::Func,
424431
&arg,
425432
rules,
433+
on_unmatch_args,
426434
);
427435
cx.macro_error_and_trace_macros_diag();
428436
DummyResult::any(span, guar)
@@ -431,7 +439,7 @@ fn expand_macro<'cx, 'a: 'cx>(
431439
}
432440

433441
/// Expands the rules based macro defined by `rules` for a given attribute `args` and `body`.
434-
#[instrument(skip(cx, transparency, args, body, rules))]
442+
#[instrument(skip(cx, transparency, args, body, rules, on_unmatch_args))]
435443
fn expand_macro_attr(
436444
cx: &mut ExtCtxt<'_>,
437445
sp: Span,
@@ -443,6 +451,7 @@ fn expand_macro_attr(
443451
args: TokenStream,
444452
body: TokenStream,
445453
rules: &[MacroRule],
454+
on_unmatch_args: Option<&Directive>,
446455
) -> Result<TokenStream, ErrorGuaranteed> {
447456
let psess = &cx.sess.psess;
448457
// Macros defined in the current crate have a real node id,
@@ -507,6 +516,7 @@ fn expand_macro_attr(
507516
FailedMacro::Attr(&args),
508517
&body,
509518
rules,
519+
on_unmatch_args,
510520
);
511521
cx.trace_macros_diag();
512522
Err(guar)
@@ -849,7 +859,22 @@ pub fn compile_declarative_macro(
849859
// Return the number of rules for unused rule linting, if this is a local macro.
850860
let nrules = if is_defined_in_current_crate(node_id) { rules.len() } else { 0 };
851861

852-
let exp = MacroRulesMacroExpander { name: ident, kinds, span, node_id, transparency, rules };
862+
let on_unmatch_args = find_attr!(
863+
attrs,
864+
OnUnmatchArgs { directive, .. } => directive.clone()
865+
)
866+
.flatten()
867+
.map(|directive| *directive);
868+
869+
let exp = MacroRulesMacroExpander {
870+
name: ident,
871+
kinds,
872+
span,
873+
node_id,
874+
on_unmatch_args,
875+
transparency,
876+
rules,
877+
};
853878
(mk_syn_ext(SyntaxExtensionKind::MacroRules(Arc::new(exp))), nrules)
854879
}
855880

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,8 @@ declare_features! (
478478
(unstable, diagnostic_on_move, "1.96.0", Some(154181)),
479479
/// Allows giving unresolved imports a custom diagnostic message
480480
(unstable, diagnostic_on_unknown, "1.96.0", Some(152900)),
481+
/// Allows macros to customize macro argument matcher diagnostics.
482+
(unstable, diagnostic_on_unmatch_args, "CURRENT_RUSTC_VERSION", Some(152494)),
481483
/// Allows `#[doc(cfg(...))]`.
482484
(unstable, doc_cfg, "1.21.0", Some(43781)),
483485
/// Allows `#[doc(masked)]`.

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,13 @@ pub enum AttributeKind {
12081208
directive: Option<Box<Directive>>,
12091209
},
12101210

1211+
/// Represents `#[diagnostic::on_unmatch_args]`.
1212+
OnUnmatchArgs {
1213+
span: Span,
1214+
/// None if the directive was malformed in some way.
1215+
directive: Option<Box<Directive>>,
1216+
},
1217+
12111218
/// Represents `#[optimize(size|speed)]`
12121219
Optimize(OptimizeAttr, Span),
12131220

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ impl AttributeKind {
8080
OnMove { .. } => Yes,
8181
OnUnimplemented { .. } => Yes,
8282
OnUnknown { .. } => Yes,
83+
OnUnmatchArgs { .. } => Yes,
8384
Optimize(..) => No,
8485
PanicRuntime => No,
8586
PatchableFunctionEntry { .. } => Yes,

compiler/rustc_lint/src/early/diagnostics.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> {
168168
lints::MalFormedDiagnosticAttributeLint { attribute, options, span }
169169
.into_diag(dcx, level)
170170
}
171-
172171
AttributeLintKind::MalformedDiagnosticFormat { warning } => match warning {
173172
FormatWarning::PositionalArgument { .. } => {
174173
lints::DisallowedPositionalArgument.into_diag(dcx, level)

0 commit comments

Comments
 (0)