]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_passes/src/naked_functions.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / compiler / rustc_passes / src / naked_functions.rs
CommitLineData
fc512014
XL
1//! Checks validity of naked functions.
2
f2b60f7d 3use rustc_ast::InlineAsmOptions;
fc512014 4use rustc_hir as hir;
f2b60f7d 5use rustc_hir::def::DefKind;
fc512014 6use rustc_hir::def_id::LocalDefId;
f2b60f7d
FG
7use rustc_hir::intravisit::Visitor;
8use rustc_hir::{ExprKind, InlineAsmOperand, StmtKind};
49aad941 9use rustc_middle::query::Providers;
fc512014 10use rustc_middle::ty::TyCtxt;
94222f64 11use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI;
fc512014
XL
12use rustc_span::symbol::sym;
13use rustc_span::Span;
14use rustc_target::spec::abi::Abi;
15
2b03887a
FG
16use crate::errors::{
17 CannotInlineNakedFunction, NakedFunctionsAsmBlock, NakedFunctionsAsmOptions,
18 NakedFunctionsMustUseNoreturn, NakedFunctionsOperands, NoPatterns, ParamsNotAllowed,
19 UndefinedNakedFunctionAbi,
20};
21
923072b8 22pub(crate) fn provide(providers: &mut Providers) {
fc512014
XL
23 *providers = Providers { check_mod_naked_functions, ..*providers };
24}
25
f2b60f7d
FG
26fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
27 let items = tcx.hir_module_items(module_def_id);
28 for def_id in items.definitions() {
29 if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
30 continue;
fc512014
XL
31 }
32
353b0b11 33 let naked = tcx.has_attr(def_id, sym::naked);
f2b60f7d
FG
34 if !naked {
35 continue;
fc512014 36 }
f2b60f7d
FG
37
38 let (fn_header, body_id) = match tcx.hir().get_by_def_id(def_id) {
39 hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. })
40 | hir::Node::TraitItem(hir::TraitItem {
41 kind: hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)),
42 ..
43 })
44 | hir::Node::ImplItem(hir::ImplItem {
45 kind: hir::ImplItemKind::Fn(sig, body_id),
46 ..
47 }) => (sig.header, *body_id),
48 _ => continue,
49 };
50
51 let body = tcx.hir().body(body_id);
52 check_abi(tcx, def_id, fn_header.abi);
53 check_no_patterns(tcx, body.params);
54 check_no_parameters_use(tcx, body);
55 check_asm(tcx, def_id, body);
56 check_inline(tcx, def_id);
fc512014
XL
57 }
58}
59
94222f64 60/// Check that the function isn't inlined.
f2b60f7d 61fn check_inline(tcx: TyCtxt<'_>, def_id: LocalDefId) {
353b0b11 62 let attrs = tcx.get_attrs(def_id, sym::inline);
f2b60f7d 63 for attr in attrs {
2b03887a 64 tcx.sess.emit_err(CannotInlineNakedFunction { span: attr.span });
94222f64
XL
65 }
66}
67
fc512014 68/// Checks that function uses non-Rust ABI.
f2b60f7d 69fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: Abi) {
fc512014 70 if abi == Abi::Rust {
f2b60f7d
FG
71 let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
72 let span = tcx.def_span(def_id);
2b03887a
FG
73 tcx.emit_spanned_lint(
74 UNDEFINED_NAKED_FUNCTION_ABI,
75 hir_id,
76 span,
77 UndefinedNakedFunctionAbi,
78 );
fc512014
XL
79 }
80}
81
82/// Checks that parameters don't use patterns. Mirrors the checks for function declarations.
83fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) {
84 for param in params {
85 match param.pat.kind {
86 hir::PatKind::Wild
f2b60f7d 87 | hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, _, None) => {}
fc512014 88 _ => {
2b03887a 89 tcx.sess.emit_err(NoPatterns { span: param.pat.span });
fc512014
XL
90 }
91 }
92 }
93}
94
95/// Checks that function parameters aren't used in the function body.
96fn check_no_parameters_use<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>) {
97 let mut params = hir::HirIdSet::default();
98 for param in body.params {
99 param.pat.each_binding(|_binding_mode, hir_id, _span, _ident| {
100 params.insert(hir_id);
101 });
102 }
103 CheckParameters { tcx, params }.visit_body(body);
104}
105
106struct CheckParameters<'tcx> {
107 tcx: TyCtxt<'tcx>,
108 params: hir::HirIdSet,
109}
110
111impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
fc512014
XL
112 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
113 if let hir::ExprKind::Path(hir::QPath::Resolved(
114 _,
115 hir::Path { res: hir::def::Res::Local(var_hir_id), .. },
116 )) = expr.kind
117 {
118 if self.params.contains(var_hir_id) {
2b03887a 119 self.tcx.sess.emit_err(ParamsNotAllowed { span: expr.span });
fc512014
XL
120 return;
121 }
122 }
123 hir::intravisit::walk_expr(self, expr);
124 }
125}
126
127/// Checks that function body contains a single inline assembly block.
f2b60f7d 128fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'tcx hir::Body<'tcx>) {
fc512014
XL
129 let mut this = CheckInlineAssembly { tcx, items: Vec::new() };
130 this.visit_body(body);
5e7ed085 131 if let [(ItemKind::Asm | ItemKind::Err, _)] = this.items[..] {
fc512014
XL
132 // Ok.
133 } else {
5e7ed085 134 let mut must_show_error = false;
5099ac24 135 let mut has_asm = false;
5e7ed085 136 let mut has_err = false;
2b03887a
FG
137 let mut multiple_asms = vec![];
138 let mut non_asms = vec![];
5099ac24
FG
139 for &(kind, span) in &this.items {
140 match kind {
141 ItemKind::Asm if has_asm => {
5e7ed085 142 must_show_error = true;
2b03887a 143 multiple_asms.push(span);
5099ac24
FG
144 }
145 ItemKind::Asm => has_asm = true,
146 ItemKind::NonAsm => {
5e7ed085 147 must_show_error = true;
2b03887a 148 non_asms.push(span);
fc512014 149 }
5e7ed085 150 ItemKind::Err => has_err = true,
fc512014 151 }
5099ac24 152 }
5e7ed085
FG
153
154 // If the naked function only contains a single asm block and a non-zero number of
155 // errors, then don't show an additional error. This allows for appending/prepending
156 // `compile_error!("...")` statements and reduces error noise.
157 if must_show_error || !has_err {
2b03887a
FG
158 tcx.sess.emit_err(NakedFunctionsAsmBlock {
159 span: tcx.def_span(def_id),
160 multiple_asms,
161 non_asms,
162 });
5e7ed085 163 }
fc512014
XL
164 }
165}
166
167struct CheckInlineAssembly<'tcx> {
168 tcx: TyCtxt<'tcx>,
169 items: Vec<(ItemKind, Span)>,
170}
171
172#[derive(Copy, Clone)]
173enum ItemKind {
174 Asm,
175 NonAsm,
5e7ed085 176 Err,
fc512014
XL
177}
178
179impl<'tcx> CheckInlineAssembly<'tcx> {
180 fn check_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, span: Span) {
181 match expr.kind {
353b0b11 182 ExprKind::ConstBlock(..)
fc512014
XL
183 | ExprKind::Array(..)
184 | ExprKind::Call(..)
185 | ExprKind::MethodCall(..)
186 | ExprKind::Tup(..)
187 | ExprKind::Binary(..)
188 | ExprKind::Unary(..)
189 | ExprKind::Lit(..)
190 | ExprKind::Cast(..)
191 | ExprKind::Type(..)
192 | ExprKind::Loop(..)
193 | ExprKind::Match(..)
5869c6ff 194 | ExprKind::If(..)
923072b8 195 | ExprKind::Closure { .. }
fc512014
XL
196 | ExprKind::Assign(..)
197 | ExprKind::AssignOp(..)
198 | ExprKind::Field(..)
199 | ExprKind::Index(..)
200 | ExprKind::Path(..)
201 | ExprKind::AddrOf(..)
94222f64 202 | ExprKind::Let(..)
fc512014
XL
203 | ExprKind::Break(..)
204 | ExprKind::Continue(..)
205 | ExprKind::Ret(..)
49aad941 206 | ExprKind::OffsetOf(..)
fc512014
XL
207 | ExprKind::Struct(..)
208 | ExprKind::Repeat(..)
209 | ExprKind::Yield(..) => {
210 self.items.push((ItemKind::NonAsm, span));
211 }
212
213 ExprKind::InlineAsm(ref asm) => {
214 self.items.push((ItemKind::Asm, span));
5099ac24 215 self.check_inline_asm(asm, span);
fc512014
XL
216 }
217
5e7ed085 218 ExprKind::DropTemps(..) | ExprKind::Block(..) => {
fc512014
XL
219 hir::intravisit::walk_expr(self, expr);
220 }
5e7ed085 221
9ffffee4 222 ExprKind::Err(_) => {
5e7ed085
FG
223 self.items.push((ItemKind::Err, span));
224 }
fc512014
XL
225 }
226 }
227
5099ac24 228 fn check_inline_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) {
fc512014
XL
229 let unsupported_operands: Vec<Span> = asm
230 .operands
231 .iter()
232 .filter_map(|&(ref op, op_sp)| match op {
04454e1e
FG
233 InlineAsmOperand::Const { .. }
234 | InlineAsmOperand::SymFn { .. }
235 | InlineAsmOperand::SymStatic { .. } => None,
fc512014
XL
236 InlineAsmOperand::In { .. }
237 | InlineAsmOperand::Out { .. }
238 | InlineAsmOperand::InOut { .. }
239 | InlineAsmOperand::SplitInOut { .. } => Some(op_sp),
240 })
241 .collect();
242 if !unsupported_operands.is_empty() {
2b03887a 243 self.tcx.sess.emit_err(NakedFunctionsOperands { unsupported_operands });
fc512014
XL
244 }
245
246 let unsupported_options: Vec<&'static str> = [
5099ac24 247 (InlineAsmOptions::MAY_UNWIND, "`may_unwind`"),
fc512014
XL
248 (InlineAsmOptions::NOMEM, "`nomem`"),
249 (InlineAsmOptions::NOSTACK, "`nostack`"),
250 (InlineAsmOptions::PRESERVES_FLAGS, "`preserves_flags`"),
251 (InlineAsmOptions::PURE, "`pure`"),
252 (InlineAsmOptions::READONLY, "`readonly`"),
253 ]
254 .iter()
255 .filter_map(|&(option, name)| if asm.options.contains(option) { Some(name) } else { None })
256 .collect();
257
258 if !unsupported_options.is_empty() {
2b03887a 259 self.tcx.sess.emit_err(NakedFunctionsAsmOptions {
5099ac24 260 span,
2b03887a
FG
261 unsupported_options: unsupported_options.join(", "),
262 });
fc512014
XL
263 }
264
265 if !asm.options.contains(InlineAsmOptions::NORETURN) {
5e7ed085
FG
266 let last_span = asm
267 .operands
268 .last()
269 .map_or_else(|| asm.template_strs.last().unwrap().2, |op| op.1)
270 .shrink_to_hi();
271
2b03887a 272 self.tcx.sess.emit_err(NakedFunctionsMustUseNoreturn { span, last_span });
fc512014
XL
273 }
274 }
275}
276
277impl<'tcx> Visitor<'tcx> for CheckInlineAssembly<'tcx> {
fc512014
XL
278 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
279 match stmt.kind {
280 StmtKind::Item(..) => {}
281 StmtKind::Local(..) => {
282 self.items.push((ItemKind::NonAsm, stmt.span));
283 }
284 StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => {
285 self.check_expr(expr, stmt.span);
286 }
287 }
288 }
289
290 fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
291 self.check_expr(&expr, expr.span);
292 }
293}