rustc_passes/
naked_functions.rs1use rustc_hir as hir;
4use rustc_hir::def::DefKind;
5use rustc_hir::def_id::{LocalDefId, LocalModDefId};
6use rustc_hir::intravisit::Visitor;
7use rustc_hir::{ExprKind, HirIdSet, StmtKind};
8use rustc_middle::hir::nested_filter::OnlyBodies;
9use rustc_middle::query::Providers;
10use rustc_middle::span_bug;
11use rustc_middle::ty::TyCtxt;
12use rustc_span::{Span, sym};
13
14use crate::errors::{
15 NakedAsmOutsideNakedFn, NakedFunctionsAsmBlock, NakedFunctionsMustNakedAsm, NoPatterns,
16 ParamsNotAllowed,
17};
18
19pub(crate) fn provide(providers: &mut Providers) {
20 *providers = Providers { check_mod_naked_functions, ..*providers };
21}
22
23fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
24 let items = tcx.hir_module_items(module_def_id);
25 for def_id in items.definitions() {
26 if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
27 continue;
28 }
29
30 let body = match tcx.hir_node_by_def_id(def_id) {
31 hir::Node::Item(hir::Item {
32 kind: hir::ItemKind::Fn { body: body_id, .. }, ..
33 })
34 | hir::Node::TraitItem(hir::TraitItem {
35 kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)),
36 ..
37 })
38 | hir::Node::ImplItem(hir::ImplItem {
39 kind: hir::ImplItemKind::Fn(_, body_id), ..
40 }) => tcx.hir_body(*body_id),
41 _ => continue,
42 };
43
44 if tcx.has_attr(def_id, sym::naked) {
45 check_no_patterns(tcx, body.params);
46 check_no_parameters_use(tcx, body);
47 check_asm(tcx, def_id, body);
48 } else {
49 let mut visitor = CheckNakedAsmInNakedFn { tcx };
51 visitor.visit_body(body);
52 }
53 }
54}
55
56fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) {
58 for param in params {
59 match param.pat.kind {
60 hir::PatKind::Wild | hir::PatKind::Binding(hir::BindingMode::NONE, _, _, None) => {}
61 _ => {
62 tcx.dcx().emit_err(NoPatterns { span: param.pat.span });
63 }
64 }
65 }
66}
67
68fn check_no_parameters_use<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) {
70 let mut params = HirIdSet::default();
71 for param in body.params {
72 param.pat.each_binding(|_binding_mode, hir_id, _span, _ident| {
73 params.insert(hir_id);
74 });
75 }
76 CheckParameters { tcx, params }.visit_body(body);
77}
78
79struct CheckParameters<'tcx> {
80 tcx: TyCtxt<'tcx>,
81 params: HirIdSet,
82}
83
84impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
85 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
86 if let hir::ExprKind::Path(hir::QPath::Resolved(
87 _,
88 hir::Path { res: hir::def::Res::Local(var_hir_id), .. },
89 )) = expr.kind
90 {
91 if self.params.contains(var_hir_id) {
92 self.tcx.dcx().emit_err(ParamsNotAllowed { span: expr.span });
93 return;
94 }
95 }
96 hir::intravisit::walk_expr(self, expr);
97 }
98}
99
100fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) {
102 let mut this = CheckInlineAssembly { items: Vec::new() };
103 this.visit_body(body);
104 if let [(ItemKind::NakedAsm | ItemKind::Err, _)] = this.items[..] {
105 } else {
107 let mut must_show_error = false;
108 let mut has_naked_asm = false;
109 let mut has_err = false;
110 let mut multiple_asms = vec![];
111 let mut non_asms = vec![];
112 for &(kind, span) in &this.items {
113 match kind {
114 ItemKind::NakedAsm if has_naked_asm => {
115 must_show_error = true;
116 multiple_asms.push(span);
117 }
118 ItemKind::NakedAsm => has_naked_asm = true,
119 ItemKind::InlineAsm => {
120 has_err = true;
121
122 tcx.dcx().emit_err(NakedFunctionsMustNakedAsm { span });
123 }
124 ItemKind::NonAsm => {
125 must_show_error = true;
126 non_asms.push(span);
127 }
128 ItemKind::Err => has_err = true,
129 }
130 }
131
132 if must_show_error || !has_err {
136 tcx.dcx().emit_err(NakedFunctionsAsmBlock {
137 span: tcx.def_span(def_id),
138 multiple_asms,
139 non_asms,
140 });
141 }
142 }
143}
144
145struct CheckInlineAssembly {
146 items: Vec<(ItemKind, Span)>,
147}
148
149#[derive(Copy, Clone)]
150enum ItemKind {
151 NakedAsm,
152 InlineAsm,
153 NonAsm,
154 Err,
155}
156
157impl CheckInlineAssembly {
158 fn check_expr<'tcx>(&mut self, expr: &'tcx hir::Expr<'tcx>, span: Span) {
159 match expr.kind {
160 ExprKind::ConstBlock(..)
161 | ExprKind::Array(..)
162 | ExprKind::Call(..)
163 | ExprKind::MethodCall(..)
164 | ExprKind::Use(..)
165 | ExprKind::Tup(..)
166 | ExprKind::Binary(..)
167 | ExprKind::Unary(..)
168 | ExprKind::Lit(..)
169 | ExprKind::Cast(..)
170 | ExprKind::Type(..)
171 | ExprKind::UnsafeBinderCast(..)
172 | ExprKind::Loop(..)
173 | ExprKind::Match(..)
174 | ExprKind::If(..)
175 | ExprKind::Closure { .. }
176 | ExprKind::Assign(..)
177 | ExprKind::AssignOp(..)
178 | ExprKind::Field(..)
179 | ExprKind::Index(..)
180 | ExprKind::Path(..)
181 | ExprKind::AddrOf(..)
182 | ExprKind::Let(..)
183 | ExprKind::Break(..)
184 | ExprKind::Continue(..)
185 | ExprKind::Ret(..)
186 | ExprKind::OffsetOf(..)
187 | ExprKind::Become(..)
188 | ExprKind::Struct(..)
189 | ExprKind::Repeat(..)
190 | ExprKind::Yield(..) => {
191 self.items.push((ItemKind::NonAsm, span));
192 }
193
194 ExprKind::InlineAsm(asm) => match asm.asm_macro {
195 rustc_ast::AsmMacro::Asm => {
196 self.items.push((ItemKind::InlineAsm, span));
197 }
198 rustc_ast::AsmMacro::NakedAsm => {
199 self.items.push((ItemKind::NakedAsm, span));
200 }
201 rustc_ast::AsmMacro::GlobalAsm => {
202 span_bug!(span, "`global_asm!` is not allowed in this position")
203 }
204 },
205
206 ExprKind::DropTemps(..) | ExprKind::Block(..) => {
207 hir::intravisit::walk_expr(self, expr);
208 }
209
210 ExprKind::Err(_) => {
211 self.items.push((ItemKind::Err, span));
212 }
213 }
214 }
215}
216
217impl<'tcx> Visitor<'tcx> for CheckInlineAssembly {
218 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
219 match stmt.kind {
220 StmtKind::Item(..) => {}
221 StmtKind::Let(..) => {
222 self.items.push((ItemKind::NonAsm, stmt.span));
223 }
224 StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
225 self.check_expr(expr, stmt.span);
226 }
227 }
228 }
229
230 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
231 self.check_expr(expr, expr.span);
232 }
233}
234
235struct CheckNakedAsmInNakedFn<'tcx> {
236 tcx: TyCtxt<'tcx>,
237}
238
239impl<'tcx> Visitor<'tcx> for CheckNakedAsmInNakedFn<'tcx> {
240 type NestedFilter = OnlyBodies;
241
242 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
243 self.tcx
244 }
245
246 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
247 if let ExprKind::InlineAsm(inline_asm) = expr.kind {
248 if let rustc_ast::AsmMacro::NakedAsm = inline_asm.asm_macro {
249 self.tcx.dcx().emit_err(NakedAsmOutsideNakedFn { span: expr.span });
250 }
251 }
252
253 hir::intravisit::walk_expr(self, expr);
254 }
255}