rustc_hir_typeck/
naked_functions.rs

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