]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_passes/src/naked_functions.rs
Auto merge of #79780 - camelid:use-summary_opts, r=GuillaumeGomez
[rust.git] / compiler / rustc_passes / src / naked_functions.rs
1 //! Checks validity of naked functions.
2
3 use rustc_ast::InlineAsmOptions;
4 use rustc_hir as hir;
5 use rustc_hir::def_id::LocalDefId;
6 use rustc_hir::intravisit::{ErasedMap, FnKind, NestedVisitorMap, Visitor};
7 use rustc_hir::{ExprKind, HirId, InlineAsmOperand, StmtKind};
8 use rustc_middle::ty::query::Providers;
9 use rustc_middle::ty::TyCtxt;
10 use rustc_session::lint::builtin::UNSUPPORTED_NAKED_FUNCTIONS;
11 use rustc_span::symbol::sym;
12 use rustc_span::Span;
13 use rustc_target::spec::abi::Abi;
14
15 fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
16     tcx.hir().visit_item_likes_in_module(
17         module_def_id,
18         &mut CheckNakedFunctions { tcx }.as_deep_visitor(),
19     );
20 }
21
22 crate fn provide(providers: &mut Providers) {
23     *providers = Providers { check_mod_naked_functions, ..*providers };
24 }
25
26 struct CheckNakedFunctions<'tcx> {
27     tcx: TyCtxt<'tcx>,
28 }
29
30 impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> {
31     type Map = ErasedMap<'tcx>;
32
33     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
34         NestedVisitorMap::None
35     }
36
37     fn visit_fn(
38         &mut self,
39         fk: FnKind<'v>,
40         _fd: &'tcx hir::FnDecl<'tcx>,
41         body_id: hir::BodyId,
42         span: Span,
43         hir_id: HirId,
44     ) {
45         let ident_span;
46         let fn_header;
47
48         match fk {
49             FnKind::Closure(..) => {
50                 // Closures with a naked attribute are rejected during attribute
51                 // check. Don't validate them any further.
52                 return;
53             }
54             FnKind::ItemFn(ident, _, ref header, ..) => {
55                 ident_span = ident.span;
56                 fn_header = header;
57             }
58
59             FnKind::Method(ident, ref sig, ..) => {
60                 ident_span = ident.span;
61                 fn_header = &sig.header;
62             }
63         }
64
65         let naked = fk.attrs().iter().any(|attr| attr.has_name(sym::naked));
66         if naked {
67             let body = self.tcx.hir().body(body_id);
68             check_abi(self.tcx, hir_id, fn_header.abi, ident_span);
69             check_no_patterns(self.tcx, body.params);
70             check_no_parameters_use(self.tcx, body);
71             check_asm(self.tcx, hir_id, body, span);
72         }
73     }
74 }
75
76 /// Checks that function uses non-Rust ABI.
77 fn check_abi(tcx: TyCtxt<'_>, hir_id: HirId, abi: Abi, fn_ident_span: Span) {
78     if abi == Abi::Rust {
79         tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, fn_ident_span, |lint| {
80             lint.build("Rust ABI is unsupported in naked functions").emit();
81         });
82     }
83 }
84
85 /// Checks that parameters don't use patterns. Mirrors the checks for function declarations.
86 fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) {
87     for param in params {
88         match param.pat.kind {
89             hir::PatKind::Wild
90             | hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, _, None) => {}
91             _ => {
92                 tcx.sess
93                     .struct_span_err(
94                         param.pat.span,
95                         "patterns not allowed in naked function parameters",
96                     )
97                     .emit();
98             }
99         }
100     }
101 }
102
103 /// Checks that function parameters aren't used in the function body.
104 fn check_no_parameters_use<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) {
105     let mut params = hir::HirIdSet::default();
106     for param in body.params {
107         param.pat.each_binding(|_binding_mode, hir_id, _span, _ident| {
108             params.insert(hir_id);
109         });
110     }
111     CheckParameters { tcx, params }.visit_body(body);
112 }
113
114 struct CheckParameters<'tcx> {
115     tcx: TyCtxt<'tcx>,
116     params: hir::HirIdSet,
117 }
118
119 impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
120     type Map = ErasedMap<'tcx>;
121
122     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
123         NestedVisitorMap::None
124     }
125
126     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
127         if let hir::ExprKind::Path(hir::QPath::Resolved(
128             _,
129             hir::Path { res: hir::def::Res::Local(var_hir_id), .. },
130         )) = expr.kind
131         {
132             if self.params.contains(var_hir_id) {
133                 self.tcx
134                     .sess
135                     .struct_span_err(
136                         expr.span,
137                         "referencing function parameters is not allowed in naked functions",
138                     )
139                     .help("follow the calling convention in asm block to use parameters")
140                     .emit();
141                 return;
142             }
143         }
144         hir::intravisit::walk_expr(self, expr);
145     }
146 }
147
148 /// Checks that function body contains a single inline assembly block.
149 fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, hir_id: HirId, body: &'tcx hir::Body<'tcx>, fn_span: Span) {
150     let mut this = CheckInlineAssembly { tcx, items: Vec::new() };
151     this.visit_body(body);
152     if let &[(ItemKind::Asm, _)] = &this.items[..] {
153         // Ok.
154     } else {
155         tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, fn_span, |lint| {
156             let mut diag = lint.build("naked functions must contain a single asm block");
157             let mut has_asm = false;
158             for &(kind, span) in &this.items {
159                 match kind {
160                     ItemKind::Asm if has_asm => {
161                         diag.span_label(
162                             span,
163                             "multiple asm blocks are unsupported in naked functions",
164                         );
165                     }
166                     ItemKind::Asm => has_asm = true,
167                     ItemKind::NonAsm => {
168                         diag.span_label(span, "non-asm is unsupported in naked functions");
169                     }
170                 }
171             }
172             diag.emit();
173         });
174     }
175 }
176
177 struct CheckInlineAssembly<'tcx> {
178     tcx: TyCtxt<'tcx>,
179     items: Vec<(ItemKind, Span)>,
180 }
181
182 #[derive(Copy, Clone)]
183 enum ItemKind {
184     Asm,
185     NonAsm,
186 }
187
188 impl<'tcx> CheckInlineAssembly<'tcx> {
189     fn check_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, span: Span) {
190         match expr.kind {
191             ExprKind::Box(..)
192             | ExprKind::ConstBlock(..)
193             | ExprKind::Array(..)
194             | ExprKind::Call(..)
195             | ExprKind::MethodCall(..)
196             | ExprKind::Tup(..)
197             | ExprKind::Binary(..)
198             | ExprKind::Unary(..)
199             | ExprKind::Lit(..)
200             | ExprKind::Cast(..)
201             | ExprKind::Type(..)
202             | ExprKind::Loop(..)
203             | ExprKind::Match(..)
204             | ExprKind::Closure(..)
205             | ExprKind::Assign(..)
206             | ExprKind::AssignOp(..)
207             | ExprKind::Field(..)
208             | ExprKind::Index(..)
209             | ExprKind::Path(..)
210             | ExprKind::AddrOf(..)
211             | ExprKind::Break(..)
212             | ExprKind::Continue(..)
213             | ExprKind::Ret(..)
214             | ExprKind::Struct(..)
215             | ExprKind::Repeat(..)
216             | ExprKind::Yield(..) => {
217                 self.items.push((ItemKind::NonAsm, span));
218             }
219
220             ExprKind::InlineAsm(ref asm) => {
221                 self.items.push((ItemKind::Asm, span));
222                 self.check_inline_asm(expr.hir_id, asm, span);
223             }
224
225             ExprKind::LlvmInlineAsm(..) => {
226                 self.items.push((ItemKind::Asm, span));
227                 self.tcx.struct_span_lint_hir(
228                     UNSUPPORTED_NAKED_FUNCTIONS,
229                     expr.hir_id,
230                     span,
231                     |lint| {
232                         lint.build(
233                             "the LLVM-style inline assembly is unsupported in naked functions",
234                         )
235                         .help("use the new asm! syntax specified in RFC 2873")
236                         .emit();
237                     },
238                 );
239             }
240
241             ExprKind::DropTemps(..) | ExprKind::Block(..) | ExprKind::Err => {
242                 hir::intravisit::walk_expr(self, expr);
243             }
244         }
245     }
246
247     fn check_inline_asm(&self, hir_id: HirId, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) {
248         let unsupported_operands: Vec<Span> = asm
249             .operands
250             .iter()
251             .filter_map(|&(ref op, op_sp)| match op {
252                 InlineAsmOperand::Const { .. } | InlineAsmOperand::Sym { .. } => None,
253                 InlineAsmOperand::In { .. }
254                 | InlineAsmOperand::Out { .. }
255                 | InlineAsmOperand::InOut { .. }
256                 | InlineAsmOperand::SplitInOut { .. } => Some(op_sp),
257             })
258             .collect();
259         if !unsupported_operands.is_empty() {
260             self.tcx.struct_span_lint_hir(
261                 UNSUPPORTED_NAKED_FUNCTIONS,
262                 hir_id,
263                 unsupported_operands,
264                 |lint| {
265                     lint.build("only `const` and `sym` operands are supported in naked functions")
266                         .emit();
267                 },
268             );
269         }
270
271         let unsupported_options: Vec<&'static str> = [
272             (InlineAsmOptions::NOMEM, "`nomem`"),
273             (InlineAsmOptions::NOSTACK, "`nostack`"),
274             (InlineAsmOptions::PRESERVES_FLAGS, "`preserves_flags`"),
275             (InlineAsmOptions::PURE, "`pure`"),
276             (InlineAsmOptions::READONLY, "`readonly`"),
277         ]
278         .iter()
279         .filter_map(|&(option, name)| if asm.options.contains(option) { Some(name) } else { None })
280         .collect();
281
282         if !unsupported_options.is_empty() {
283             self.tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, span, |lint| {
284                 lint.build(&format!(
285                     "asm options unsupported in naked functions: {}",
286                     unsupported_options.join(", ")
287                 ))
288                 .emit();
289             });
290         }
291
292         if !asm.options.contains(InlineAsmOptions::NORETURN) {
293             self.tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, span, |lint| {
294                 lint.build("asm in naked functions must use `noreturn` option").emit();
295             });
296         }
297     }
298 }
299
300 impl<'tcx> Visitor<'tcx> for CheckInlineAssembly<'tcx> {
301     type Map = ErasedMap<'tcx>;
302
303     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
304         NestedVisitorMap::None
305     }
306
307     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
308         match stmt.kind {
309             StmtKind::Item(..) => {}
310             StmtKind::Local(..) => {
311                 self.items.push((ItemKind::NonAsm, stmt.span));
312             }
313             StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
314                 self.check_expr(expr, stmt.span);
315             }
316         }
317     }
318
319     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
320         self.check_expr(&expr, expr.span);
321     }
322 }