Skip to main content

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