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