]>
Commit | Line | Data |
---|---|---|
fc512014 XL |
1 | //! Checks validity of naked functions. |
2 | ||
f2b60f7d | 3 | use rustc_ast::InlineAsmOptions; |
fc512014 | 4 | use rustc_hir as hir; |
f2b60f7d | 5 | use rustc_hir::def::DefKind; |
fc512014 | 6 | use rustc_hir::def_id::LocalDefId; |
f2b60f7d FG |
7 | use rustc_hir::intravisit::Visitor; |
8 | use rustc_hir::{ExprKind, InlineAsmOperand, StmtKind}; | |
49aad941 | 9 | use rustc_middle::query::Providers; |
fc512014 | 10 | use rustc_middle::ty::TyCtxt; |
94222f64 | 11 | use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI; |
fc512014 XL |
12 | use rustc_span::symbol::sym; |
13 | use rustc_span::Span; | |
14 | use rustc_target::spec::abi::Abi; | |
15 | ||
2b03887a FG |
16 | use crate::errors::{ |
17 | CannotInlineNakedFunction, NakedFunctionsAsmBlock, NakedFunctionsAsmOptions, | |
18 | NakedFunctionsMustUseNoreturn, NakedFunctionsOperands, NoPatterns, ParamsNotAllowed, | |
19 | UndefinedNakedFunctionAbi, | |
20 | }; | |
21 | ||
923072b8 | 22 | pub(crate) fn provide(providers: &mut Providers) { |
fc512014 XL |
23 | *providers = Providers { check_mod_naked_functions, ..*providers }; |
24 | } | |
25 | ||
f2b60f7d FG |
26 | fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { |
27 | let items = tcx.hir_module_items(module_def_id); | |
28 | for def_id in items.definitions() { | |
29 | if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) { | |
30 | continue; | |
fc512014 XL |
31 | } |
32 | ||
353b0b11 | 33 | let naked = tcx.has_attr(def_id, sym::naked); |
f2b60f7d FG |
34 | if !naked { |
35 | continue; | |
fc512014 | 36 | } |
f2b60f7d FG |
37 | |
38 | let (fn_header, body_id) = match tcx.hir().get_by_def_id(def_id) { | |
39 | hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) | |
40 | | hir::Node::TraitItem(hir::TraitItem { | |
41 | kind: hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), | |
42 | .. | |
43 | }) | |
44 | | hir::Node::ImplItem(hir::ImplItem { | |
45 | kind: hir::ImplItemKind::Fn(sig, body_id), | |
46 | .. | |
47 | }) => (sig.header, *body_id), | |
48 | _ => continue, | |
49 | }; | |
50 | ||
51 | let body = tcx.hir().body(body_id); | |
52 | check_abi(tcx, def_id, fn_header.abi); | |
53 | check_no_patterns(tcx, body.params); | |
54 | check_no_parameters_use(tcx, body); | |
55 | check_asm(tcx, def_id, body); | |
56 | check_inline(tcx, def_id); | |
fc512014 XL |
57 | } |
58 | } | |
59 | ||
94222f64 | 60 | /// Check that the function isn't inlined. |
f2b60f7d | 61 | fn check_inline(tcx: TyCtxt<'_>, def_id: LocalDefId) { |
353b0b11 | 62 | let attrs = tcx.get_attrs(def_id, sym::inline); |
f2b60f7d | 63 | for attr in attrs { |
2b03887a | 64 | tcx.sess.emit_err(CannotInlineNakedFunction { span: attr.span }); |
94222f64 XL |
65 | } |
66 | } | |
67 | ||
fc512014 | 68 | /// Checks that function uses non-Rust ABI. |
f2b60f7d | 69 | fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: Abi) { |
fc512014 | 70 | if abi == Abi::Rust { |
f2b60f7d FG |
71 | let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); |
72 | let span = tcx.def_span(def_id); | |
2b03887a FG |
73 | tcx.emit_spanned_lint( |
74 | UNDEFINED_NAKED_FUNCTION_ABI, | |
75 | hir_id, | |
76 | span, | |
77 | UndefinedNakedFunctionAbi, | |
78 | ); | |
fc512014 XL |
79 | } |
80 | } | |
81 | ||
82 | /// Checks that parameters don't use patterns. Mirrors the checks for function declarations. | |
83 | fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) { | |
84 | for param in params { | |
85 | match param.pat.kind { | |
86 | hir::PatKind::Wild | |
f2b60f7d | 87 | | hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, _, None) => {} |
fc512014 | 88 | _ => { |
2b03887a | 89 | tcx.sess.emit_err(NoPatterns { span: param.pat.span }); |
fc512014 XL |
90 | } |
91 | } | |
92 | } | |
93 | } | |
94 | ||
95 | /// Checks that function parameters aren't used in the function body. | |
96 | fn check_no_parameters_use<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) { | |
97 | let mut params = hir::HirIdSet::default(); | |
98 | for param in body.params { | |
99 | param.pat.each_binding(|_binding_mode, hir_id, _span, _ident| { | |
100 | params.insert(hir_id); | |
101 | }); | |
102 | } | |
103 | CheckParameters { tcx, params }.visit_body(body); | |
104 | } | |
105 | ||
106 | struct CheckParameters<'tcx> { | |
107 | tcx: TyCtxt<'tcx>, | |
108 | params: hir::HirIdSet, | |
109 | } | |
110 | ||
111 | impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> { | |
fc512014 XL |
112 | fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { |
113 | if let hir::ExprKind::Path(hir::QPath::Resolved( | |
114 | _, | |
115 | hir::Path { res: hir::def::Res::Local(var_hir_id), .. }, | |
116 | )) = expr.kind | |
117 | { | |
118 | if self.params.contains(var_hir_id) { | |
2b03887a | 119 | self.tcx.sess.emit_err(ParamsNotAllowed { span: expr.span }); |
fc512014 XL |
120 | return; |
121 | } | |
122 | } | |
123 | hir::intravisit::walk_expr(self, expr); | |
124 | } | |
125 | } | |
126 | ||
127 | /// Checks that function body contains a single inline assembly block. | |
f2b60f7d | 128 | fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) { |
fc512014 XL |
129 | let mut this = CheckInlineAssembly { tcx, items: Vec::new() }; |
130 | this.visit_body(body); | |
5e7ed085 | 131 | if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] { |
fc512014 XL |
132 | // Ok. |
133 | } else { | |
5e7ed085 | 134 | let mut must_show_error = false; |
5099ac24 | 135 | let mut has_asm = false; |
5e7ed085 | 136 | let mut has_err = false; |
2b03887a FG |
137 | let mut multiple_asms = vec![]; |
138 | let mut non_asms = vec![]; | |
5099ac24 FG |
139 | for &(kind, span) in &this.items { |
140 | match kind { | |
141 | ItemKind::Asm if has_asm => { | |
5e7ed085 | 142 | must_show_error = true; |
2b03887a | 143 | multiple_asms.push(span); |
5099ac24 FG |
144 | } |
145 | ItemKind::Asm => has_asm = true, | |
146 | ItemKind::NonAsm => { | |
5e7ed085 | 147 | must_show_error = true; |
2b03887a | 148 | non_asms.push(span); |
fc512014 | 149 | } |
5e7ed085 | 150 | ItemKind::Err => has_err = true, |
fc512014 | 151 | } |
5099ac24 | 152 | } |
5e7ed085 FG |
153 | |
154 | // If the naked function only contains a single asm block and a non-zero number of | |
155 | // errors, then don't show an additional error. This allows for appending/prepending | |
156 | // `compile_error!("...")` statements and reduces error noise. | |
157 | if must_show_error || !has_err { | |
2b03887a FG |
158 | tcx.sess.emit_err(NakedFunctionsAsmBlock { |
159 | span: tcx.def_span(def_id), | |
160 | multiple_asms, | |
161 | non_asms, | |
162 | }); | |
5e7ed085 | 163 | } |
fc512014 XL |
164 | } |
165 | } | |
166 | ||
167 | struct CheckInlineAssembly<'tcx> { | |
168 | tcx: TyCtxt<'tcx>, | |
169 | items: Vec<(ItemKind, Span)>, | |
170 | } | |
171 | ||
172 | #[derive(Copy, Clone)] | |
173 | enum ItemKind { | |
174 | Asm, | |
175 | NonAsm, | |
5e7ed085 | 176 | Err, |
fc512014 XL |
177 | } |
178 | ||
179 | impl<'tcx> CheckInlineAssembly<'tcx> { | |
180 | fn check_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, span: Span) { | |
181 | match expr.kind { | |
353b0b11 | 182 | ExprKind::ConstBlock(..) |
fc512014 XL |
183 | | ExprKind::Array(..) |
184 | | ExprKind::Call(..) | |
185 | | ExprKind::MethodCall(..) | |
186 | | ExprKind::Tup(..) | |
187 | | ExprKind::Binary(..) | |
188 | | ExprKind::Unary(..) | |
189 | | ExprKind::Lit(..) | |
190 | | ExprKind::Cast(..) | |
191 | | ExprKind::Type(..) | |
192 | | ExprKind::Loop(..) | |
193 | | ExprKind::Match(..) | |
5869c6ff | 194 | | ExprKind::If(..) |
923072b8 | 195 | | ExprKind::Closure { .. } |
fc512014 XL |
196 | | ExprKind::Assign(..) |
197 | | ExprKind::AssignOp(..) | |
198 | | ExprKind::Field(..) | |
199 | | ExprKind::Index(..) | |
200 | | ExprKind::Path(..) | |
201 | | ExprKind::AddrOf(..) | |
94222f64 | 202 | | ExprKind::Let(..) |
fc512014 XL |
203 | | ExprKind::Break(..) |
204 | | ExprKind::Continue(..) | |
205 | | ExprKind::Ret(..) | |
49aad941 | 206 | | ExprKind::OffsetOf(..) |
fc512014 XL |
207 | | ExprKind::Struct(..) |
208 | | ExprKind::Repeat(..) | |
209 | | ExprKind::Yield(..) => { | |
210 | self.items.push((ItemKind::NonAsm, span)); | |
211 | } | |
212 | ||
213 | ExprKind::InlineAsm(ref asm) => { | |
214 | self.items.push((ItemKind::Asm, span)); | |
5099ac24 | 215 | self.check_inline_asm(asm, span); |
fc512014 XL |
216 | } |
217 | ||
5e7ed085 | 218 | ExprKind::DropTemps(..) | ExprKind::Block(..) => { |
fc512014 XL |
219 | hir::intravisit::walk_expr(self, expr); |
220 | } | |
5e7ed085 | 221 | |
9ffffee4 | 222 | ExprKind::Err(_) => { |
5e7ed085 FG |
223 | self.items.push((ItemKind::Err, span)); |
224 | } | |
fc512014 XL |
225 | } |
226 | } | |
227 | ||
5099ac24 | 228 | fn check_inline_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) { |
fc512014 XL |
229 | let unsupported_operands: Vec<Span> = asm |
230 | .operands | |
231 | .iter() | |
232 | .filter_map(|&(ref op, op_sp)| match op { | |
04454e1e FG |
233 | InlineAsmOperand::Const { .. } |
234 | | InlineAsmOperand::SymFn { .. } | |
235 | | InlineAsmOperand::SymStatic { .. } => None, | |
fc512014 XL |
236 | InlineAsmOperand::In { .. } |
237 | | InlineAsmOperand::Out { .. } | |
238 | | InlineAsmOperand::InOut { .. } | |
239 | | InlineAsmOperand::SplitInOut { .. } => Some(op_sp), | |
240 | }) | |
241 | .collect(); | |
242 | if !unsupported_operands.is_empty() { | |
2b03887a | 243 | self.tcx.sess.emit_err(NakedFunctionsOperands { unsupported_operands }); |
fc512014 XL |
244 | } |
245 | ||
246 | let unsupported_options: Vec<&'static str> = [ | |
5099ac24 | 247 | (InlineAsmOptions::MAY_UNWIND, "`may_unwind`"), |
fc512014 XL |
248 | (InlineAsmOptions::NOMEM, "`nomem`"), |
249 | (InlineAsmOptions::NOSTACK, "`nostack`"), | |
250 | (InlineAsmOptions::PRESERVES_FLAGS, "`preserves_flags`"), | |
251 | (InlineAsmOptions::PURE, "`pure`"), | |
252 | (InlineAsmOptions::READONLY, "`readonly`"), | |
253 | ] | |
254 | .iter() | |
255 | .filter_map(|&(option, name)| if asm.options.contains(option) { Some(name) } else { None }) | |
256 | .collect(); | |
257 | ||
258 | if !unsupported_options.is_empty() { | |
2b03887a | 259 | self.tcx.sess.emit_err(NakedFunctionsAsmOptions { |
5099ac24 | 260 | span, |
2b03887a FG |
261 | unsupported_options: unsupported_options.join(", "), |
262 | }); | |
fc512014 XL |
263 | } |
264 | ||
265 | if !asm.options.contains(InlineAsmOptions::NORETURN) { | |
5e7ed085 FG |
266 | let last_span = asm |
267 | .operands | |
268 | .last() | |
269 | .map_or_else(|| asm.template_strs.last().unwrap().2, |op| op.1) | |
270 | .shrink_to_hi(); | |
271 | ||
2b03887a | 272 | self.tcx.sess.emit_err(NakedFunctionsMustUseNoreturn { span, last_span }); |
fc512014 XL |
273 | } |
274 | } | |
275 | } | |
276 | ||
277 | impl<'tcx> Visitor<'tcx> for CheckInlineAssembly<'tcx> { | |
fc512014 XL |
278 | fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { |
279 | match stmt.kind { | |
280 | StmtKind::Item(..) => {} | |
281 | StmtKind::Local(..) => { | |
282 | self.items.push((ItemKind::NonAsm, stmt.span)); | |
283 | } | |
284 | StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => { | |
285 | self.check_expr(expr, stmt.span); | |
286 | } | |
287 | } | |
288 | } | |
289 | ||
290 | fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { | |
291 | self.check_expr(&expr, expr.span); | |
292 | } | |
293 | } |