Skip to content

Migrate builtin_macros::asm diagnostics to translatable diagnostics #111032

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 1, 2023
Merged
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
35 changes: 35 additions & 0 deletions compiler/rustc_builtin_macros/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -169,5 +169,40 @@ builtin_macros_asm_pure_no_output = asm with the `pure` option must have at leas

builtin_macros_asm_modifier_invalid = asm template modifier must be a single character

builtin_macros_asm_requires_template = requires at least a template string argument

builtin_macros_asm_expected_comma = expected token: `,`
.label = expected `,`

builtin_macros_asm_underscore_input = _ cannot be used for input operands

builtin_macros_asm_sym_no_path = expected a path for argument to `sym`

builtin_macros_asm_expected_other = expected operand, {$is_global_asm ->
[true] options
*[false] clobber_abi, options
}, or additional template string

builtin_macros_asm_duplicate_arg = duplicate argument named `{$name}`
.label = previously here
.arg = duplicate argument

builtin_macros_asm_pos_after = positional arguments cannot follow named arguments or explicit register arguments
.pos = positional argument
.named = named argument
.explicit = explicit register argument

builtin_macros_asm_noreturn = asm outputs are not allowed with the `noreturn` option

builtin_macros_global_asm_clobber_abi = `clobber_abi` cannot be used with `global_asm!`

builtin_macros_asm_clobber_no_reg = asm with `clobber_abi` must specify explicit registers for outputs
builtin_macros_asm_clobber_abi = clobber_abi
builtin_macros_asm_clobber_outputs = generic outputs

builtin_macros_asm_opt_already_provided = the `{$symbol}` option was already provided
.label = this option was already provided
.suggestion = remove this option

builtin_macros_test_runner_invalid = `test_runner` argument must be a path
builtin_macros_test_runner_nargs = `#![test_runner(..)]` accepts exactly 1 argument
93 changes: 26 additions & 67 deletions compiler/rustc_builtin_macros/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter};
use rustc_ast::tokenstream::TokenStream;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, PResult};
use rustc_errors::PResult;
use rustc_expand::base::{self, *};
use rustc_parse::parser::Parser;
use rustc_parse_format as parse;
Expand Down Expand Up @@ -49,7 +49,7 @@ pub fn parse_asm_args<'a>(
let diag = &sess.span_diagnostic;

if p.token == token::Eof {
return Err(diag.struct_span_err(sp, "requires at least a template string argument"));
return Err(diag.create_err(errors::AsmRequiresTemplate { span: sp }));
}

let first_template = p.parse_expr()?;
Expand All @@ -68,8 +68,7 @@ pub fn parse_asm_args<'a>(
if !p.eat(&token::Comma) {
if allow_templates {
// After a template string, we always expect *only* a comma...
let mut err = diag.struct_span_err(p.token.span, "expected token: `,`");
err.span_label(p.token.span, "expected `,`");
let mut err = diag.create_err(errors::AsmExpectedComma { span: p.token.span });
p.maybe_annotate_with_ascription(&mut err, false);
return Err(err);
} else {
Expand Down Expand Up @@ -112,7 +111,7 @@ pub fn parse_asm_args<'a>(
let op = if !is_global_asm && p.eat_keyword(kw::In) {
let reg = parse_reg(p, &mut explicit_reg)?;
if p.eat_keyword(kw::Underscore) {
let err = diag.struct_span_err(p.token.span, "_ cannot be used for input operands");
let err = diag.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
Expand All @@ -128,7 +127,7 @@ pub fn parse_asm_args<'a>(
} else if !is_global_asm && p.eat_keyword(sym::inout) {
let reg = parse_reg(p, &mut explicit_reg)?;
if p.eat_keyword(kw::Underscore) {
let err = diag.struct_span_err(p.token.span, "_ cannot be used for input operands");
let err = diag.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
Expand All @@ -142,7 +141,7 @@ pub fn parse_asm_args<'a>(
} else if !is_global_asm && p.eat_keyword(sym::inlateout) {
let reg = parse_reg(p, &mut explicit_reg)?;
if p.eat_keyword(kw::Underscore) {
let err = diag.struct_span_err(p.token.span, "_ cannot be used for input operands");
let err = diag.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
Expand All @@ -160,7 +159,7 @@ pub fn parse_asm_args<'a>(
let expr = p.parse_expr()?;
let ast::ExprKind::Path(qself, path) = &expr.kind else {
let err = diag
.struct_span_err(expr.span, "expected a path for argument to `sym`");
.create_err(errors::AsmSymNoPath { span: expr.span });
return Err(err);
};
let sym = ast::InlineAsmSym {
Expand All @@ -181,13 +180,10 @@ pub fn parse_asm_args<'a>(
) => {}
ast::ExprKind::MacCall(..) => {}
_ => {
let errstr = if is_global_asm {
"expected operand, options, or additional template string"
} else {
"expected operand, clobber_abi, options, or additional template string"
};
let mut err = diag.struct_span_err(template.span, errstr);
err.span_label(template.span, errstr);
let err = diag.create_err(errors::AsmExpectedOther {
span: template.span,
is_global_asm,
});
return Err(err);
}
}
Expand All @@ -212,28 +208,16 @@ pub fn parse_asm_args<'a>(
args.reg_args.insert(slot);
} else if let Some(name) = name {
if let Some(&prev) = args.named_args.get(&name) {
diag.struct_span_err(span, &format!("duplicate argument named `{}`", name))
.span_label(args.operands[prev].1, "previously here")
.span_label(span, "duplicate argument")
.emit();
diag.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 });
continue;
}
args.named_args.insert(name, slot);
} else {
if !args.named_args.is_empty() || !args.reg_args.is_empty() {
let mut err = diag.struct_span_err(
span,
"positional arguments cannot follow named arguments \
or explicit register arguments",
);
err.span_label(span, "positional argument");
for pos in args.named_args.values() {
err.span_label(args.operands[*pos].1, "named argument");
}
for pos in &args.reg_args {
err.span_label(args.operands[*pos].1, "explicit register argument");
}
err.emit();
let named = args.named_args.values().map(|p| args.operands[*p].1).collect();
let explicit = args.reg_args.iter().map(|p| args.operands[*p].1).collect();

diag.emit_err(errors::AsmPositionalAfter { span, named, explicit });
}
}
}
Expand Down Expand Up @@ -284,34 +268,25 @@ pub fn parse_asm_args<'a>(
diag.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
}
if args.options.contains(ast::InlineAsmOptions::NORETURN) && !outputs_sp.is_empty() {
let err = diag
.struct_span_err(outputs_sp, "asm outputs are not allowed with the `noreturn` option");

let err = diag.create_err(errors::AsmNoReturn { outputs_sp });
// Bail out now since this is likely to confuse MIR
return Err(err);
}

if args.clobber_abis.len() > 0 {
if is_global_asm {
let err = diag.struct_span_err(
args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(),
"`clobber_abi` cannot be used with `global_asm!`",
);
let err = diag.create_err(errors::GlobalAsmClobberAbi {
spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
});

// Bail out now since this is likely to confuse later stages
return Err(err);
}
if !regclass_outputs.is_empty() {
diag.struct_span_err(
regclass_outputs.clone(),
"asm with `clobber_abi` must specify explicit registers for outputs",
)
.span_labels(
args.clobber_abis.iter().map(|(_, span)| *span).collect::<Vec<Span>>(),
"clobber_abi",
)
.span_labels(regclass_outputs, "generic outputs")
.emit();
diag.emit_err(errors::AsmClobberNoReg {
spans: regclass_outputs,
clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
});
}
}

Expand All @@ -323,25 +298,9 @@ pub fn parse_asm_args<'a>(
/// This function must be called immediately after the option token is parsed.
/// Otherwise, the suggestion will be incorrect.
fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) {
let mut err = p
.sess
.span_diagnostic
.struct_span_err(span, &format!("the `{}` option was already provided", symbol));
err.span_label(span, "this option was already provided");

// Tool-only output
let mut full_span = span;
if p.token.kind == token::Comma {
full_span = full_span.to(p.token.span);
}
err.tool_only_span_suggestion(
full_span,
"remove this option",
"",
Applicability::MachineApplicable,
);

err.emit();
let full_span = if p.token.kind == token::Comma { span.to(p.token.span) } else { span };
p.sess.span_diagnostic.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
}

/// Try to set the provided option in the provided `AsmArgs`.
Expand Down
123 changes: 118 additions & 5 deletions compiler/rustc_builtin_macros/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use rustc_errors::{
AddToDiagnostic, EmissionGuarantee, IntoDiagnostic, MultiSpan, SingleLabelManySpans,
AddToDiagnostic, DiagnosticBuilder, EmissionGuarantee, Handler, IntoDiagnostic, MultiSpan,
SingleLabelManySpans,
};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{symbol::Ident, Span, Symbol};
Expand Down Expand Up @@ -370,11 +371,12 @@ pub(crate) struct EnvNotDefined {
// Hand-written implementation to support custom user messages
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefined {
#[track_caller]
fn into_diagnostic(
self,
handler: &'a rustc_errors::Handler,
) -> rustc_errors::DiagnosticBuilder<'a, G> {
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> {
let mut diag = if let Some(msg) = self.msg {
#[expect(
rustc::untranslatable_diagnostic,
reason = "cannot translate user-provided messages"
)]
handler.struct_diagnostic(msg.as_str())
} else {
handler.struct_diagnostic(crate::fluent_generated::builtin_macros_env_not_defined)
Expand Down Expand Up @@ -606,6 +608,117 @@ pub(crate) struct AsmModifierInvalid {
pub(crate) span: Span,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_asm_requires_template)]
pub(crate) struct AsmRequiresTemplate {
#[primary_span]
pub(crate) span: Span,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_asm_expected_comma)]
pub(crate) struct AsmExpectedComma {
#[primary_span]
#[label]
pub(crate) span: Span,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_asm_underscore_input)]
pub(crate) struct AsmUnderscoreInput {
#[primary_span]
pub(crate) span: Span,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_asm_sym_no_path)]
pub(crate) struct AsmSymNoPath {
#[primary_span]
pub(crate) span: Span,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_asm_expected_other)]
pub(crate) struct AsmExpectedOther {
#[primary_span]
#[label(builtin_macros_asm_expected_other)]
pub(crate) span: Span,
pub(crate) is_global_asm: bool,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_asm_duplicate_arg)]
pub(crate) struct AsmDuplicateArg {
#[primary_span]
#[label(builtin_macros_arg)]
pub(crate) span: Span,
#[label]
pub(crate) prev: Span,
pub(crate) name: Symbol,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_asm_pos_after)]
pub(crate) struct AsmPositionalAfter {
#[primary_span]
#[label(builtin_macros_pos)]
pub(crate) span: Span,
#[label(builtin_macros_named)]
pub(crate) named: Vec<Span>,
#[label(builtin_macros_explicit)]
pub(crate) explicit: Vec<Span>,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_asm_noreturn)]
pub(crate) struct AsmNoReturn {
#[primary_span]
pub(crate) outputs_sp: Vec<Span>,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_global_asm_clobber_abi)]
pub(crate) struct GlobalAsmClobberAbi {
#[primary_span]
pub(crate) spans: Vec<Span>,
}

pub(crate) struct AsmClobberNoReg {
pub(crate) spans: Vec<Span>,
pub(crate) clobbers: Vec<Span>,
}

impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for AsmClobberNoReg {
fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, G> {
let mut diag =
handler.struct_diagnostic(crate::fluent_generated::builtin_macros_asm_clobber_no_reg);
diag.set_span(self.spans.clone());
// eager translation as `span_labels` takes `AsRef<str>`
let lbl1 = handler.eagerly_translate_to_string(
crate::fluent_generated::builtin_macros_asm_clobber_abi,
[].into_iter(),
);
diag.span_labels(self.clobbers, &lbl1);
let lbl2 = handler.eagerly_translate_to_string(
crate::fluent_generated::builtin_macros_asm_clobber_outputs,
[].into_iter(),
);
diag.span_labels(self.spans, &lbl2);
diag
}
}

#[derive(Diagnostic)]
#[diag(builtin_macros_asm_opt_already_provided)]
pub(crate) struct AsmOptAlreadyprovided {
#[primary_span]
#[label]
pub(crate) span: Span,
pub(crate) symbol: Symbol,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub(crate) full_span: Span,
}

#[derive(Diagnostic)]
#[diag(builtin_macros_test_runner_invalid)]
pub(crate) struct TestRunnerInvalid {
Expand Down