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