rustc_hir_typeck/
naked_functions.rs1use rustc_hir as hir;
4use rustc_hir::def_id::LocalDefId;
5use rustc_hir::intravisit::Visitor;
6use rustc_hir::{ExprKind, HirIdSet, StmtKind};
7use rustc_middle::span_bug;
8use rustc_middle::ty::TyCtxt;
9use rustc_span::{Span, sym};
10
11use crate::errors::{
12 NakedFunctionsAsmBlock, NakedFunctionsMustNakedAsm, NoPatterns, ParamsNotAllowed,
13};
14
15pub(crate) fn typeck_naked_fn<'tcx>(
19 tcx: TyCtxt<'tcx>,
20 def_id: LocalDefId,
21 body: &'tcx hir::Body<'tcx>,
22) {
23 debug_assert!(tcx.has_attr(def_id, sym::naked));
24 check_no_patterns(tcx, body.params);
25 check_no_parameters_use(tcx, body);
26 check_asm(tcx, def_id, body);
27}
28
29fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) {
31 for param in params {
32 match param.pat.kind {
33 hir::PatKind::Wild | hir::PatKind::Binding(hir::BindingMode::NONE, _, _, None) => {}
34 _ => {
35 tcx.dcx().emit_err(NoPatterns { span: param.pat.span });
36 }
37 }
38 }
39}
40
41fn check_no_parameters_use<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) {
43 let mut params = HirIdSet::default();
44 for param in body.params {
45 param.pat.each_binding(|_binding_mode, hir_id, _span, _ident| {
46 params.insert(hir_id);
47 });
48 }
49 CheckParameters { tcx, params }.visit_body(body);
50}
51
52struct CheckParameters<'tcx> {
53 tcx: TyCtxt<'tcx>,
54 params: HirIdSet,
55}
56
57impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
58 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
59 if let hir::ExprKind::Path(hir::QPath::Resolved(
60 _,
61 hir::Path { res: hir::def::Res::Local(var_hir_id), .. },
62 )) = expr.kind
63 {
64 if self.params.contains(var_hir_id) {
65 self.tcx.dcx().emit_err(ParamsNotAllowed { span: expr.span });
66 return;
67 }
68 }
69 hir::intravisit::walk_expr(self, expr);
70 }
71}
72
73fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) {
75 let mut this = CheckInlineAssembly { items: Vec::new() };
76 this.visit_body(body);
77 if let [(ItemKind::NakedAsm | ItemKind::Err, _)] = this.items[..] {
78 } else {
80 let mut must_show_error = false;
81 let mut has_naked_asm = false;
82 let mut has_err = false;
83 let mut multiple_asms = vec![];
84 let mut non_asms = vec![];
85 for &(kind, span) in &this.items {
86 match kind {
87 ItemKind::NakedAsm if has_naked_asm => {
88 must_show_error = true;
89 multiple_asms.push(span);
90 }
91 ItemKind::NakedAsm => has_naked_asm = true,
92 ItemKind::InlineAsm => {
93 has_err = true;
94
95 tcx.dcx().emit_err(NakedFunctionsMustNakedAsm { span });
96 }
97 ItemKind::NonAsm => {
98 must_show_error = true;
99 non_asms.push(span);
100 }
101 ItemKind::Err => has_err = true,
102 }
103 }
104
105 if must_show_error || !has_err {
109 tcx.dcx().emit_err(NakedFunctionsAsmBlock {
110 span: tcx.def_span(def_id),
111 multiple_asms,
112 non_asms,
113 });
114 }
115 }
116}
117
118struct CheckInlineAssembly {
119 items: Vec<(ItemKind, Span)>,
120}
121
122#[derive(Copy, Clone)]
123enum ItemKind {
124 NakedAsm,
125 InlineAsm,
126 NonAsm,
127 Err,
128}
129
130impl CheckInlineAssembly {
131 fn check_expr<'tcx>(&mut self, expr: &'tcx hir::Expr<'tcx>, span: Span) {
132 match expr.kind {
133 ExprKind::ConstBlock(..)
134 | ExprKind::Array(..)
135 | ExprKind::Call(..)
136 | ExprKind::MethodCall(..)
137 | ExprKind::Use(..)
138 | ExprKind::Tup(..)
139 | ExprKind::Binary(..)
140 | ExprKind::Unary(..)
141 | ExprKind::Lit(..)
142 | ExprKind::Cast(..)
143 | ExprKind::Type(..)
144 | ExprKind::UnsafeBinderCast(..)
145 | ExprKind::Loop(..)
146 | ExprKind::Match(..)
147 | ExprKind::If(..)
148 | ExprKind::Closure { .. }
149 | ExprKind::Assign(..)
150 | ExprKind::AssignOp(..)
151 | ExprKind::Field(..)
152 | ExprKind::Index(..)
153 | ExprKind::Path(..)
154 | ExprKind::AddrOf(..)
155 | ExprKind::Let(..)
156 | ExprKind::Break(..)
157 | ExprKind::Continue(..)
158 | ExprKind::Ret(..)
159 | ExprKind::OffsetOf(..)
160 | ExprKind::Become(..)
161 | ExprKind::Struct(..)
162 | ExprKind::Repeat(..)
163 | ExprKind::Yield(..) => {
164 self.items.push((ItemKind::NonAsm, span));
165 }
166
167 ExprKind::InlineAsm(asm) => match asm.asm_macro {
168 rustc_ast::AsmMacro::Asm => {
169 self.items.push((ItemKind::InlineAsm, span));
170 }
171 rustc_ast::AsmMacro::NakedAsm => {
172 self.items.push((ItemKind::NakedAsm, span));
173 }
174 rustc_ast::AsmMacro::GlobalAsm => {
175 span_bug!(span, "`global_asm!` is not allowed in this position")
176 }
177 },
178
179 ExprKind::DropTemps(..) | ExprKind::Block(..) => {
180 hir::intravisit::walk_expr(self, expr);
181 }
182
183 ExprKind::Err(_) => {
184 self.items.push((ItemKind::Err, span));
185 }
186 }
187 }
188}
189
190impl<'tcx> Visitor<'tcx> for CheckInlineAssembly {
191 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
192 match stmt.kind {
193 StmtKind::Item(..) => {}
194 StmtKind::Let(..) => {
195 self.items.push((ItemKind::NonAsm, stmt.span));
196 }
197 StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
198 self.check_expr(expr, stmt.span);
199 }
200 }
201 }
202
203 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
204 self.check_expr(expr, expr.span);
205 }
206}