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