rustc_hir_typeck/
naked_functions.rs

1//! Checks validity of naked functions.
2
3use rustc_hir as hir;
4use rustc_hir::def_id::LocalDefId;
5use rustc_hir::intravisit::Visitor;
6use rustc_hir::{ExprKind, HirIdSet, StmtKind};
7use rustc_middle::span_bug;
8use rustc_middle::ty::TyCtxt;
9use rustc_span::{Span, sym};
10
11use crate::errors::{
12    NakedFunctionsAsmBlock, NakedFunctionsMustNakedAsm, NoPatterns, ParamsNotAllowed,
13};
14
15/// Naked fns can only have trivial binding patterns in arguments,
16/// may not actually use those arguments, and the body must consist of just
17/// a single asm statement.
18pub(crate) fn typeck_naked_fn<'tcx>(
19    tcx: TyCtxt<'tcx>,
20    def_id: LocalDefId,
21    body: &'tcx hir::Body<'tcx>,
22) {
23    debug_assert!(tcx.has_attr(def_id, sym::naked));
24    check_no_patterns(tcx, body.params);
25    check_no_parameters_use(tcx, body);
26    check_asm(tcx, def_id, body);
27}
28
29/// Checks that parameters don't use patterns. Mirrors the checks for function declarations.
30fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) {
31    for param in params {
32        match param.pat.kind {
33            hir::PatKind::Wild | hir::PatKind::Binding(hir::BindingMode::NONE, _, _, None) => {}
34            _ => {
35                tcx.dcx().emit_err(NoPatterns { span: param.pat.span });
36            }
37        }
38    }
39}
40
41/// Checks that function parameters aren't used in the function body.
42fn check_no_parameters_use<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) {
43    let mut params = HirIdSet::default();
44    for param in body.params {
45        param.pat.each_binding(|_binding_mode, hir_id, _span, _ident| {
46            params.insert(hir_id);
47        });
48    }
49    CheckParameters { tcx, params }.visit_body(body);
50}
51
52struct CheckParameters<'tcx> {
53    tcx: TyCtxt<'tcx>,
54    params: HirIdSet,
55}
56
57impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
58    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
59        if let hir::ExprKind::Path(hir::QPath::Resolved(
60            _,
61            hir::Path { res: hir::def::Res::Local(var_hir_id), .. },
62        )) = expr.kind
63        {
64            if self.params.contains(var_hir_id) {
65                self.tcx.dcx().emit_err(ParamsNotAllowed { span: expr.span });
66                return;
67            }
68        }
69        hir::intravisit::walk_expr(self, expr);
70    }
71}
72
73/// Checks that function body contains a single inline assembly block.
74fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) {
75    let mut this = CheckInlineAssembly { items: Vec::new() };
76    this.visit_body(body);
77    if let [(ItemKind::NakedAsm | ItemKind::Err, _)] = this.items[..] {
78        // Ok.
79    } else {
80        let mut must_show_error = false;
81        let mut has_naked_asm = false;
82        let mut has_err = false;
83        let mut multiple_asms = vec![];
84        let mut non_asms = vec![];
85        for &(kind, span) in &this.items {
86            match kind {
87                ItemKind::NakedAsm if has_naked_asm => {
88                    must_show_error = true;
89                    multiple_asms.push(span);
90                }
91                ItemKind::NakedAsm => has_naked_asm = true,
92                ItemKind::InlineAsm => {
93                    has_err = true;
94
95                    tcx.dcx().emit_err(NakedFunctionsMustNakedAsm { span });
96                }
97                ItemKind::NonAsm => {
98                    must_show_error = true;
99                    non_asms.push(span);
100                }
101                ItemKind::Err => has_err = true,
102            }
103        }
104
105        // If the naked function only contains a single asm block and a non-zero number of
106        // errors, then don't show an additional error. This allows for appending/prepending
107        // `compile_error!("...")` statements and reduces error noise.
108        if must_show_error || !has_err {
109            tcx.dcx().emit_err(NakedFunctionsAsmBlock {
110                span: tcx.def_span(def_id),
111                multiple_asms,
112                non_asms,
113            });
114        }
115    }
116}
117
118struct CheckInlineAssembly {
119    items: Vec<(ItemKind, Span)>,
120}
121
122#[derive(Copy, Clone)]
123enum ItemKind {
124    NakedAsm,
125    InlineAsm,
126    NonAsm,
127    Err,
128}
129
130impl CheckInlineAssembly {
131    fn check_expr<'tcx>(&mut self, expr: &'tcx hir::Expr<'tcx>, span: Span) {
132        match expr.kind {
133            ExprKind::ConstBlock(..)
134            | ExprKind::Array(..)
135            | ExprKind::Call(..)
136            | ExprKind::MethodCall(..)
137            | ExprKind::Use(..)
138            | ExprKind::Tup(..)
139            | ExprKind::Binary(..)
140            | ExprKind::Unary(..)
141            | ExprKind::Lit(..)
142            | ExprKind::Cast(..)
143            | ExprKind::Type(..)
144            | ExprKind::UnsafeBinderCast(..)
145            | ExprKind::Loop(..)
146            | ExprKind::Match(..)
147            | ExprKind::If(..)
148            | ExprKind::Closure { .. }
149            | ExprKind::Assign(..)
150            | ExprKind::AssignOp(..)
151            | ExprKind::Field(..)
152            | ExprKind::Index(..)
153            | ExprKind::Path(..)
154            | ExprKind::AddrOf(..)
155            | ExprKind::Let(..)
156            | ExprKind::Break(..)
157            | ExprKind::Continue(..)
158            | ExprKind::Ret(..)
159            | ExprKind::OffsetOf(..)
160            | ExprKind::Become(..)
161            | ExprKind::Struct(..)
162            | ExprKind::Repeat(..)
163            | ExprKind::Yield(..) => {
164                self.items.push((ItemKind::NonAsm, span));
165            }
166
167            ExprKind::InlineAsm(asm) => match asm.asm_macro {
168                rustc_ast::AsmMacro::Asm => {
169                    self.items.push((ItemKind::InlineAsm, span));
170                }
171                rustc_ast::AsmMacro::NakedAsm => {
172                    self.items.push((ItemKind::NakedAsm, span));
173                }
174                rustc_ast::AsmMacro::GlobalAsm => {
175                    span_bug!(span, "`global_asm!` is not allowed in this position")
176                }
177            },
178
179            ExprKind::DropTemps(..) | ExprKind::Block(..) => {
180                hir::intravisit::walk_expr(self, expr);
181            }
182
183            ExprKind::Err(_) => {
184                self.items.push((ItemKind::Err, span));
185            }
186        }
187    }
188}
189
190impl<'tcx> Visitor<'tcx> for CheckInlineAssembly {
191    fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
192        match stmt.kind {
193            StmtKind::Item(..) => {}
194            StmtKind::Let(..) => {
195                self.items.push((ItemKind::NonAsm, stmt.span));
196            }
197            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
198                self.check_expr(expr, stmt.span);
199            }
200        }
201    }
202
203    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
204        self.check_expr(expr, expr.span);
205    }
206}