diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_passes/src/naked_functions.rs | 178 |
1 files changed, 68 insertions, 110 deletions
diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 20765abf3..acc54e7e1 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -1,11 +1,11 @@ //! Checks validity of naked functions. -use rustc_ast::{Attribute, InlineAsmOptions}; -use rustc_errors::{struct_span_err, Applicability}; +use rustc_ast::InlineAsmOptions; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{FnKind, Visitor}; -use rustc_hir::{ExprKind, HirId, InlineAsmOperand, StmtKind}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{ExprKind, InlineAsmOperand, StmtKind}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI; @@ -13,73 +13,69 @@ use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi; -fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckNakedFunctions { tcx }); -} +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 }; } -struct CheckNakedFunctions<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> { - fn visit_fn( - &mut self, - fk: FnKind<'_>, - _fd: &'tcx hir::FnDecl<'tcx>, - body_id: hir::BodyId, - span: Span, - hir_id: HirId, - ) { - let ident_span; - let fn_header; - - match fk { - FnKind::Closure => { - // Closures with a naked attribute are rejected during attribute - // check. Don't validate them any further. - return; - } - FnKind::ItemFn(ident, _, ref header, ..) => { - ident_span = ident.span; - fn_header = header; - } - - FnKind::Method(ident, ref sig, ..) => { - ident_span = ident.span; - fn_header = &sig.header; - } +fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { + let items = tcx.hir_module_items(module_def_id); + for def_id in items.definitions() { + if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) { + continue; } - let attrs = self.tcx.hir().attrs(hir_id); - let naked = attrs.iter().any(|attr| attr.has_name(sym::naked)); - if naked { - let body = self.tcx.hir().body(body_id); - check_abi(self.tcx, hir_id, fn_header.abi, ident_span); - check_no_patterns(self.tcx, body.params); - check_no_parameters_use(self.tcx, body); - check_asm(self.tcx, body, span); - check_inline(self.tcx, attrs); + let naked = tcx.has_attr(def_id.to_def_id(), sym::naked); + if !naked { + continue; } + + let (fn_header, body_id) = match tcx.hir().get_by_def_id(def_id) { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(sig, body_id), + .. + }) => (sig.header, *body_id), + _ => continue, + }; + + let body = tcx.hir().body(body_id); + check_abi(tcx, def_id, fn_header.abi); + check_no_patterns(tcx, body.params); + check_no_parameters_use(tcx, body); + check_asm(tcx, def_id, body); + check_inline(tcx, def_id); } } /// Check that the function isn't inlined. -fn check_inline(tcx: TyCtxt<'_>, attrs: &[Attribute]) { - for attr in attrs.iter().filter(|attr| attr.has_name(sym::inline)) { - tcx.sess.struct_span_err(attr.span, "naked functions cannot be inlined").emit(); +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.emit_err(CannotInlineNakedFunction { span: attr.span }); } } /// Checks that function uses non-Rust ABI. -fn check_abi(tcx: TyCtxt<'_>, hir_id: HirId, abi: Abi, fn_ident_span: Span) { +fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: Abi) { if abi == Abi::Rust { - tcx.struct_span_lint_hir(UNDEFINED_NAKED_FUNCTION_ABI, hir_id, fn_ident_span, |lint| { - lint.build("Rust ABI is unsupported in naked functions").emit(); - }); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let span = tcx.def_span(def_id); + tcx.emit_spanned_lint( + UNDEFINED_NAKED_FUNCTION_ABI, + hir_id, + span, + UndefinedNakedFunctionAbi, + ); } } @@ -88,14 +84,9 @@ fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) { for param in params { match param.pat.kind { hir::PatKind::Wild - | hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, _, None) => {} + | 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 }); } } } @@ -125,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; } } @@ -141,32 +125,27 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { } /// Checks that function body contains a single inline assembly block. -fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>, fn_span: Span) { +fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) { let mut this = CheckInlineAssembly { tcx, items: Vec::new() }; this.visit_body(body); if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] { // Ok. } else { - let mut diag = struct_span_err!( - tcx.sess, - fn_span, - 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, } @@ -176,9 +155,11 @@ fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>, fn_span: Span // 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, + }); } } } @@ -259,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> = [ @@ -281,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) { @@ -298,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 }); } } } |