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