migrate naked_functions.rs to translateable diagnostics

This commit is contained in:
Nathan Stocks 2022-09-27 09:44:56 -06:00
parent 69766e4f16
commit 96f92eab68
3 changed files with 129 additions and 59 deletions

View file

@ -467,3 +467,31 @@ passes_unlabeled_in_labeled_block =
passes_unlabeled_cf_in_while_condition =
`break` or `continue` with no label in the condition of a `while` loop
.label = unlabeled `{$cf_type}` in the condition of a `while` loop
passes_cannot_inline_naked_function =
naked functions cannot be inlined
passes_undefined_naked_function_abi =
Rust ABI is unsupported in naked functions
passes_no_patterns =
patterns not allowed in naked function parameters
passes_params_not_allowed =
referencing function parameters is not allowed in naked functions
.help = follow the calling convention in asm block to use parameters
passes_naked_functions_asm_block =
naked functions must contain a single asm block
.label_multiple_asm = multiple asm blocks are unsupported in naked functions
.label_non_asm = non-asm is unsupported in naked functions
passes_naked_functions_operands =
only `const` and `sym` operands are supported in naked functions
passes_naked_functions_asm_options =
asm options unsupported in naked functions: {$unsupported_options}
passes_naked_functions_must_use_noreturn =
asm in naked functions must use `noreturn` option
.suggestion = consider specifying that the asm block is responsible for returning from the function

View file

@ -987,3 +987,79 @@ pub struct UnlabeledCfInWhileCondition<'a> {
pub span: Span,
pub cf_type: &'a str,
}
#[derive(Diagnostic)]
#[diag(passes::cannot_inline_naked_function)]
pub struct CannotInlineNakedFunction {
#[primary_span]
pub span: Span,
}
#[derive(LintDiagnostic)]
#[diag(passes::undefined_naked_function_abi)]
pub struct UndefinedNakedFunctionAbi;
#[derive(Diagnostic)]
#[diag(passes::no_patterns)]
pub struct NoPatterns {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(passes::params_not_allowed)]
#[help]
pub struct ParamsNotAllowed {
#[primary_span]
pub span: Span,
}
pub struct NakedFunctionsAsmBlock {
pub span: Span,
pub multiple_asms: Vec<Span>,
pub non_asms: Vec<Span>,
}
impl IntoDiagnostic<'_> for NakedFunctionsAsmBlock {
fn into_diagnostic(
self,
handler: &rustc_errors::Handler,
) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
let mut diag = handler.struct_span_err_with_code(
self.span,
rustc_errors::fluent::passes::naked_functions_asm_block,
error_code!(E0787),
);
for span in self.multiple_asms.iter() {
diag.span_label(*span, rustc_errors::fluent::passes::label_multiple_asm);
}
for span in self.non_asms.iter() {
diag.span_label(*span, rustc_errors::fluent::passes::label_non_asm);
}
diag
}
}
#[derive(Diagnostic)]
#[diag(passes::naked_functions_operands, code = "E0787")]
pub struct NakedFunctionsOperands {
#[primary_span]
pub unsupported_operands: Vec<Span>,
}
#[derive(Diagnostic)]
#[diag(passes::naked_functions_asm_options, code = "E0787")]
pub struct NakedFunctionsAsmOptions {
#[primary_span]
pub span: Span,
pub unsupported_options: String,
}
#[derive(Diagnostic)]
#[diag(passes::naked_functions_must_use_noreturn, code = "E0787")]
pub struct NakedFunctionsMustUseNoreturn {
#[primary_span]
pub span: Span,
#[suggestion(code = ", options(noreturn)", applicability = "machine-applicable")]
pub last_span: Span,
}

View file

@ -1,7 +1,6 @@
//! Checks validity of naked functions.
use rustc_ast::InlineAsmOptions;
use rustc_errors::{struct_span_err, Applicability};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
@ -14,6 +13,12 @@ use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
use crate::errors::{
CannotInlineNakedFunction, NakedFunctionsAsmBlock, NakedFunctionsAsmOptions,
NakedFunctionsMustUseNoreturn, NakedFunctionsOperands, NoPatterns, ParamsNotAllowed,
UndefinedNakedFunctionAbi,
};
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { check_mod_naked_functions, ..*providers };
}
@ -56,7 +61,7 @@ fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
fn check_inline(tcx: TyCtxt<'_>, def_id: LocalDefId) {
let attrs = tcx.get_attrs(def_id.to_def_id(), sym::inline);
for attr in attrs {
tcx.sess.struct_span_err(attr.span, "naked functions cannot be inlined").emit();
tcx.sess.emit_err(CannotInlineNakedFunction { span: attr.span });
}
}
@ -65,12 +70,11 @@ fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: Abi) {
if abi == Abi::Rust {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let span = tcx.def_span(def_id);
tcx.struct_span_lint_hir(
tcx.emit_spanned_lint(
UNDEFINED_NAKED_FUNCTION_ABI,
hir_id,
span,
"Rust ABI is unsupported in naked functions",
|lint| lint,
UndefinedNakedFunctionAbi,
);
}
}
@ -82,12 +86,7 @@ fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) {
hir::PatKind::Wild
| hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, _, None) => {}
_ => {
tcx.sess
.struct_span_err(
param.pat.span,
"patterns not allowed in naked function parameters",
)
.emit();
tcx.sess.emit_err(NoPatterns { span: param.pat.span });
}
}
}
@ -117,14 +116,7 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
)) = expr.kind
{
if self.params.contains(var_hir_id) {
self.tcx
.sess
.struct_span_err(
expr.span,
"referencing function parameters is not allowed in naked functions",
)
.help("follow the calling convention in asm block to use parameters")
.emit();
self.tcx.sess.emit_err(ParamsNotAllowed { span: expr.span });
return;
}
}
@ -139,26 +131,21 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<
if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] {
// Ok.
} else {
let mut diag = struct_span_err!(
tcx.sess,
tcx.def_span(def_id),
E0787,
"naked functions must contain a single asm block"
);
let mut must_show_error = false;
let mut has_asm = false;
let mut has_err = false;
let mut multiple_asms = vec![];
let mut non_asms = vec![];
for &(kind, span) in &this.items {
match kind {
ItemKind::Asm if has_asm => {
must_show_error = true;
diag.span_label(span, "multiple asm blocks are unsupported in naked functions");
multiple_asms.push(span);
}
ItemKind::Asm => has_asm = true,
ItemKind::NonAsm => {
must_show_error = true;
diag.span_label(span, "non-asm is unsupported in naked functions");
non_asms.push(span);
}
ItemKind::Err => has_err = true,
}
@ -168,9 +155,11 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<
// errors, then don't show an additional error. This allows for appending/prepending
// `compile_error!("...")` statements and reduces error noise.
if must_show_error || !has_err {
diag.emit();
} else {
diag.cancel();
tcx.sess.emit_err(NakedFunctionsAsmBlock {
span: tcx.def_span(def_id),
multiple_asms,
non_asms,
});
}
}
}
@ -251,13 +240,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
})
.collect();
if !unsupported_operands.is_empty() {
struct_span_err!(
self.tcx.sess,
unsupported_operands,
E0787,
"only `const` and `sym` operands are supported in naked functions",
)
.emit();
self.tcx.sess.emit_err(NakedFunctionsOperands { unsupported_operands });
}
let unsupported_options: Vec<&'static str> = [
@ -273,14 +256,10 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
.collect();
if !unsupported_options.is_empty() {
struct_span_err!(
self.tcx.sess,
self.tcx.sess.emit_err(NakedFunctionsAsmOptions {
span,
E0787,
"asm options unsupported in naked functions: {}",
unsupported_options.join(", ")
)
.emit();
unsupported_options: unsupported_options.join(", "),
});
}
if !asm.options.contains(InlineAsmOptions::NORETURN) {
@ -290,20 +269,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
.map_or_else(|| asm.template_strs.last().unwrap().2, |op| op.1)
.shrink_to_hi();
struct_span_err!(
self.tcx.sess,
span,
E0787,
"asm in naked functions must use `noreturn` option"
)
.span_suggestion(
last_span,
"consider specifying that the asm block is responsible \
for returning from the function",
", options(noreturn)",
Applicability::MachineApplicable,
)
.emit();
self.tcx.sess.emit_err(NakedFunctionsMustUseNoreturn { span, last_span });
}
}
}