Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItem

pub(crate) mod do_not_recommend;
pub(crate) mod on_const;
pub(crate) mod on_missing_args;
pub(crate) mod on_move;
pub(crate) mod on_unimplemented;

Expand All @@ -33,6 +34,8 @@ pub(crate) enum Mode {
DiagnosticOnUnimplemented,
/// `#[diagnostic::on_const]`
DiagnosticOnConst,
/// `#[diagnostic::on_missing_args]`
DiagnosticOnMissingArgs,
/// `#[diagnostic::on_move]`
DiagnosticOnMove,
}
Expand Down Expand Up @@ -115,6 +118,13 @@ fn parse_directive_items<'p, S: Stage>(
span,
);
}
Mode::DiagnosticOnMissingArgs => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MalformedOnMissingArgsAttr { span },
span,
);
}
Mode::DiagnosticOnMove => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
Expand All @@ -140,7 +150,10 @@ fn parse_directive_items<'p, S: Stage>(
Mode::RustcOnUnimplemented => {
cx.emit_err(NoValueInOnUnimplemented { span: item.span() });
}
Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove => {
Mode::DiagnosticOnUnimplemented
| Mode::DiagnosticOnConst
| Mode::DiagnosticOnMissingArgs
| Mode::DiagnosticOnMove => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::IgnoredDiagnosticOption {
Expand Down Expand Up @@ -332,7 +345,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::DiagnosticOnMissingArgs, sym::This) => {
FormatArg::This
}
(Mode::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait,
// Any attribute can use these
(_, kw::SelfUpper) => FormatArg::SelfUpper,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
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 OnMissingArgsParser {
span: Option<Span>,
directive: Option<(Span, Directive)>,
}

impl<S: Stage> AttributeParser<S> for OnMissingArgsParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
&[sym::diagnostic, sym::on_missing_args],
template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]),
|this, cx, args| {
if !cx.features().diagnostic_on_missing_args() {
return;
}

let span = cx.attr_span;
this.span = Some(span);

// Lint emitted in `check_attr.rs`.
if !matches!(cx.target, Target::MacroDef) {
return;
}

let items = match args {
ArgParser::List(items) if items.len() != 0 => items,
ArgParser::NoArgs | ArgParser::List(_) => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MissingOptionsForOnMissingArgs,
span,
);
return;
}
ArgParser::NameValue(_) => {
cx.emit_lint(
MALFORMED_DIAGNOSTIC_ATTRIBUTES,
AttributeLintKind::MalformedOnMissingArgsAttr { span },
span,
);
return;
}
};

let Some(directive) =
parse_directive_items(cx, Mode::DiagnosticOnMissingArgs, 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::OnMissingArgs {
span,
directive: self.directive.map(|d| Box::new(d.1)),
})
} else {
None
}
}
}
2 changes: 2 additions & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use crate::attributes::debugger::*;
use crate::attributes::deprecation::*;
use crate::attributes::diagnostic::do_not_recommend::*;
use crate::attributes::diagnostic::on_const::*;
use crate::attributes::diagnostic::on_missing_args::*;
use crate::attributes::diagnostic::on_move::*;
use crate::attributes::diagnostic::on_unimplemented::*;
use crate::attributes::doc::*;
Expand Down Expand Up @@ -154,6 +155,7 @@ attribute_parsers!(
MacroUseParser,
NakedParser,
OnConstParser,
OnMissingArgsParser,
OnMoveParser,
OnUnimplementedParser,
RustcAlignParser,
Expand Down
30 changes: 28 additions & 2 deletions compiler/rustc_expand/src/mbe/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::borrow::Cow;
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;
Expand Down Expand Up @@ -32,6 +33,7 @@ pub(super) fn failed_to_match_macro(
args: FailedMacro<'_>,
body: &TokenStream,
rules: &[MacroRule],
on_missing_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) {
Expand Down Expand Up @@ -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_missing_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");
}
Expand All @@ -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(_))
Expand Down
37 changes: 31 additions & 6 deletions compiler/rustc_expand/src/mbe/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
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::{
Expand Down Expand Up @@ -164,6 +165,7 @@ pub struct MacroRulesMacroExpander {
node_id: NodeId,
name: Ident,
span: Span,
on_missing_args: Option<Directive>,
transparency: Transparency,
kinds: MacroKinds,
rules: Vec<MacroRule>,
Expand Down Expand Up @@ -194,7 +196,8 @@ impl MacroRulesMacroExpander {
) -> 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() {
Expand All @@ -220,8 +223,8 @@ impl MacroRulesMacroExpander {
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)
Expand All @@ -236,6 +239,7 @@ impl MacroRulesMacroExpander {
FailedMacro::Derive,
body,
rules,
self.on_missing_args.as_ref(),
);
cx.macro_error_and_trace_macros_diag();
Err(guar)
Expand All @@ -260,6 +264,7 @@ impl TTMacroExpander for MacroRulesMacroExpander {
self.transparency,
input,
&self.rules,
self.on_missing_args.as_ref(),
))
}
}
Expand Down Expand Up @@ -294,6 +299,7 @@ impl AttrProcMacro for MacroRulesMacroExpander {
args,
body,
&self.rules,
self.on_missing_args.as_ref(),
)
}
}
Expand Down Expand Up @@ -355,7 +361,7 @@ impl<'matcher> Tracker<'matcher> for NoopTracker {
}

/// 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_missing_args))]
fn expand_macro<'cx, 'a: 'cx>(
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
Expand All @@ -365,6 +371,7 @@ fn expand_macro<'cx, 'a: 'cx>(
transparency: Transparency,
arg: TokenStream,
rules: &'a [MacroRule],
on_missing_args: Option<&Directive>,
) -> Box<dyn MacResult + 'cx> {
let psess = &cx.sess.psess;

Expand Down Expand Up @@ -423,6 +430,7 @@ fn expand_macro<'cx, 'a: 'cx>(
FailedMacro::Func,
&arg,
rules,
on_missing_args,
);
cx.macro_error_and_trace_macros_diag();
DummyResult::any(span, guar)
Expand All @@ -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_missing_args))]
fn expand_macro_attr(
cx: &mut ExtCtxt<'_>,
sp: Span,
Expand All @@ -443,6 +451,7 @@ fn expand_macro_attr(
args: TokenStream,
body: TokenStream,
rules: &[MacroRule],
on_missing_args: Option<&Directive>,
) -> Result<TokenStream, ErrorGuaranteed> {
let psess = &cx.sess.psess;
// Macros defined in the current crate have a real node id,
Expand Down Expand Up @@ -507,6 +516,7 @@ fn expand_macro_attr(
FailedMacro::Attr(&args),
&body,
rules,
on_missing_args,
);
cx.trace_macros_diag();
Err(guar)
Expand Down Expand Up @@ -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_missing_args = find_attr!(
attrs,
OnMissingArgs { directive, .. } => directive.clone()
)
.flatten()
.map(|directive| *directive);

let exp = MacroRulesMacroExpander {
name: ident,
kinds,
span,
node_id,
on_missing_args,
transparency,
rules,
};
(mk_syn_ext(SyntaxExtensionKind::MacroRules(Arc::new(exp))), nrules)
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1587,6 +1587,7 @@ pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool
match sym {
sym::on_unimplemented | sym::do_not_recommend => true,
sym::on_const => features.diagnostic_on_const(),
sym::on_missing_args => features.diagnostic_on_missing_args(),
sym::on_move => features.diagnostic_on_move(),
_ => false,
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,8 @@ declare_features! (
(unstable, derive_from, "1.91.0", Some(144889)),
/// Allows giving non-const impls custom diagnostic messages if attempted to be used as const
(unstable, diagnostic_on_const, "1.93.0", Some(143874)),
/// Allows macros to customize incomplete-argument diagnostics.
(unstable, diagnostic_on_missing_args, "CURRENT_RUSTC_VERSION", Some(152494)),
/// Allows giving on-move borrowck custom diagnostic messages for a type
(unstable, diagnostic_on_move, "CURRENT_RUSTC_VERSION", Some(154181)),
/// Allows `#[doc(cfg(...))]`.
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,13 @@ pub enum AttributeKind {
directive: Option<Box<Directive>>,
},

/// Represents `#[diagnostic::on_missing_args]`.
OnMissingArgs {
span: Span,
/// None if the directive was malformed in some way.
directive: Option<Box<Directive>>,
},

/// Represents `#[diagnostic::on_move]`
OnMove {
span: Span,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ impl AttributeKind {
NoStd(..) => No,
NonExhaustive(..) => Yes, // Needed for rustdoc
OnConst { .. } => Yes,
OnMissingArgs { .. } => Yes,
OnMove { .. } => Yes,
OnUnimplemented { .. } => Yes,
Optimize(..) => No,
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_lint/src/early/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> {
&AttributeLintKind::MalformedOnConstAttr { span } => {
lints::MalformedOnConstAttrLint { span }.into_diag(dcx, level)
}
&AttributeLintKind::MalformedOnMissingArgsAttr { span } => {
lints::MalformedOnMissingArgsAttrLint { span }.into_diag(dcx, level)
}
AttributeLintKind::MalformedDiagnosticFormat { warning } => match warning {
FormatWarning::PositionalArgument { .. } => {
lints::DisallowedPositionalArgument.into_diag(dcx, level)
Expand All @@ -302,6 +305,9 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> {
&AttributeLintKind::MissingOptionsForOnConst => {
lints::MissingOptionsForOnConstAttr.into_diag(dcx, level)
}
&AttributeLintKind::MissingOptionsForOnMissingArgs => {
lints::MissingOptionsForOnMissingArgsAttr.into_diag(dcx, level)
}
&AttributeLintKind::MalformedOnMoveAttr { span } => {
lints::MalformedOnMoveAttrLint { span }.into_diag(dcx, level)
}
Expand Down
Loading
Loading