1
+ //! Checks validity of naked functions.
2
+
3
+ use rustc_ast:: InlineAsmOptions ;
1
4
use rustc_hir as hir;
2
5
use rustc_hir:: def_id:: LocalDefId ;
3
6
use rustc_hir:: intravisit:: { ErasedMap , FnKind , NestedVisitorMap , Visitor } ;
7
+ use rustc_hir:: { ExprKind , HirId , InlineAsmOperand , StmtKind } ;
4
8
use rustc_middle:: ty:: query:: Providers ;
5
9
use rustc_middle:: ty:: TyCtxt ;
10
+ use rustc_session:: lint:: builtin:: UNSUPPORTED_NAKED_FUNCTIONS ;
6
11
use rustc_span:: symbol:: sym;
7
12
use rustc_span:: Span ;
13
+ use rustc_target:: spec:: abi:: Abi ;
8
14
9
15
fn check_mod_naked_functions ( tcx : TyCtxt < ' _ > , module_def_id : LocalDefId ) {
10
16
tcx. hir ( ) . visit_item_likes_in_module (
@@ -33,27 +39,52 @@ impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> {
33
39
fk : FnKind < ' v > ,
34
40
_fd : & ' tcx hir:: FnDecl < ' tcx > ,
35
41
body_id : hir:: BodyId ,
36
- _span : Span ,
37
- _hir_id : hir :: HirId ,
42
+ span : Span ,
43
+ hir_id : HirId ,
38
44
) {
45
+ let ident_span;
46
+ let fn_header;
47
+
39
48
match fk {
40
- // Rejected during attribute check. Do not validate further.
41
- FnKind :: Closure ( ..) => return ,
42
- FnKind :: ItemFn ( ..) | FnKind :: Method ( ..) => { }
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
+ }
43
63
}
44
64
45
65
let naked = fk. attrs ( ) . iter ( ) . any ( |attr| attr. has_name ( sym:: naked) ) ;
46
66
if naked {
47
67
let body = self . tcx . hir ( ) . body ( body_id) ;
48
- check_params ( self . tcx , body) ;
49
- check_body ( self . tcx , body) ;
68
+ check_abi ( self . tcx , hir_id, fn_header. abi , ident_span) ;
69
+ check_no_patterns ( self . tcx , body. params ) ;
70
+ check_no_parameters_use ( self . tcx , body) ;
71
+ check_asm ( self . tcx , hir_id, body, span) ;
50
72
}
51
73
}
52
74
}
53
75
76
+ /// Checks that function uses non-Rust ABI.
77
+ fn check_abi ( tcx : TyCtxt < ' _ > , hir_id : HirId , abi : Abi , fn_ident_span : Span ) {
78
+ if abi == Abi :: Rust {
79
+ tcx. struct_span_lint_hir ( UNSUPPORTED_NAKED_FUNCTIONS , hir_id, fn_ident_span, |lint| {
80
+ lint. build ( "Rust ABI is unsupported in naked functions" ) . emit ( ) ;
81
+ } ) ;
82
+ }
83
+ }
84
+
54
85
/// Checks that parameters don't use patterns. Mirrors the checks for function declarations.
55
- fn check_params ( tcx : TyCtxt < ' _ > , body : & hir:: Body < ' _ > ) {
56
- for param in body . params {
86
+ fn check_no_patterns ( tcx : TyCtxt < ' _ > , params : & [ hir:: Param < ' _ > ] ) {
87
+ for param in params {
57
88
match param. pat . kind {
58
89
hir:: PatKind :: Wild
59
90
| hir:: PatKind :: Binding ( hir:: BindingAnnotation :: Unannotated , _, _, None ) => { }
@@ -69,23 +100,23 @@ fn check_params(tcx: TyCtxt<'_>, body: &hir::Body<'_>) {
69
100
}
70
101
}
71
102
72
- /// Checks that function parameters aren't referenced in the function body.
73
- fn check_body < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & ' tcx hir:: Body < ' tcx > ) {
103
+ /// Checks that function parameters aren't used in the function body.
104
+ fn check_no_parameters_use < ' tcx > ( tcx : TyCtxt < ' tcx > , body : & ' tcx hir:: Body < ' tcx > ) {
74
105
let mut params = hir:: HirIdSet :: default ( ) ;
75
106
for param in body. params {
76
107
param. pat . each_binding ( |_binding_mode, hir_id, _span, _ident| {
77
108
params. insert ( hir_id) ;
78
109
} ) ;
79
110
}
80
- CheckBody { tcx, params } . visit_body ( body) ;
111
+ CheckParameters { tcx, params } . visit_body ( body) ;
81
112
}
82
113
83
- struct CheckBody < ' tcx > {
114
+ struct CheckParameters < ' tcx > {
84
115
tcx : TyCtxt < ' tcx > ,
85
116
params : hir:: HirIdSet ,
86
117
}
87
118
88
- impl < ' tcx > Visitor < ' tcx > for CheckBody < ' tcx > {
119
+ impl < ' tcx > Visitor < ' tcx > for CheckParameters < ' tcx > {
89
120
type Map = ErasedMap < ' tcx > ;
90
121
91
122
fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
@@ -103,11 +134,189 @@ impl<'tcx> Visitor<'tcx> for CheckBody<'tcx> {
103
134
. sess
104
135
. struct_span_err (
105
136
expr. span ,
106
- "use of parameters not allowed inside naked functions" ,
137
+ "referencing function parameters is not allowed in naked functions" ,
107
138
)
139
+ . help ( "follow the calling convention in asm block to use parameters" )
108
140
. emit ( ) ;
141
+ return ;
109
142
}
110
143
}
111
144
hir:: intravisit:: walk_expr ( self , expr) ;
112
145
}
113
146
}
147
+
148
+ /// Checks that function body contains a single inline assembly block.
149
+ fn check_asm < ' tcx > ( tcx : TyCtxt < ' tcx > , hir_id : HirId , body : & ' tcx hir:: Body < ' tcx > , fn_span : Span ) {
150
+ let mut this = CheckInlineAssembly { tcx, items : Vec :: new ( ) } ;
151
+ this. visit_body ( body) ;
152
+ if let & [ ( ItemKind :: Asm , _) ] = & this. items [ ..] {
153
+ // Ok.
154
+ } else {
155
+ tcx. struct_span_lint_hir ( UNSUPPORTED_NAKED_FUNCTIONS , hir_id, fn_span, |lint| {
156
+ let mut diag = lint. build ( "naked functions must contain a single asm block" ) ;
157
+ let mut has_asm = false ;
158
+ for & ( kind, span) in & this. items {
159
+ match kind {
160
+ ItemKind :: Asm if has_asm => {
161
+ diag. span_label (
162
+ span,
163
+ "multiple asm blocks are unsupported in naked functions" ,
164
+ ) ;
165
+ }
166
+ ItemKind :: Asm => has_asm = true ,
167
+ ItemKind :: NonAsm => {
168
+ diag. span_label ( span, "non-asm is unsupported in naked functions" ) ;
169
+ }
170
+ }
171
+ }
172
+ diag. emit ( ) ;
173
+ } ) ;
174
+ }
175
+ }
176
+
177
+ struct CheckInlineAssembly < ' tcx > {
178
+ tcx : TyCtxt < ' tcx > ,
179
+ items : Vec < ( ItemKind , Span ) > ,
180
+ }
181
+
182
+ #[ derive( Copy , Clone ) ]
183
+ enum ItemKind {
184
+ Asm ,
185
+ NonAsm ,
186
+ }
187
+
188
+ impl < ' tcx > CheckInlineAssembly < ' tcx > {
189
+ fn check_expr ( & mut self , expr : & ' tcx hir:: Expr < ' tcx > , span : Span ) {
190
+ match expr. kind {
191
+ ExprKind :: Box ( ..)
192
+ | ExprKind :: ConstBlock ( ..)
193
+ | ExprKind :: Array ( ..)
194
+ | ExprKind :: Call ( ..)
195
+ | ExprKind :: MethodCall ( ..)
196
+ | ExprKind :: Tup ( ..)
197
+ | ExprKind :: Binary ( ..)
198
+ | ExprKind :: Unary ( ..)
199
+ | ExprKind :: Lit ( ..)
200
+ | ExprKind :: Cast ( ..)
201
+ | ExprKind :: Type ( ..)
202
+ | ExprKind :: Loop ( ..)
203
+ | ExprKind :: Match ( ..)
204
+ | ExprKind :: Closure ( ..)
205
+ | ExprKind :: Assign ( ..)
206
+ | ExprKind :: AssignOp ( ..)
207
+ | ExprKind :: Field ( ..)
208
+ | ExprKind :: Index ( ..)
209
+ | ExprKind :: Path ( ..)
210
+ | ExprKind :: AddrOf ( ..)
211
+ | ExprKind :: Break ( ..)
212
+ | ExprKind :: Continue ( ..)
213
+ | ExprKind :: Ret ( ..)
214
+ | ExprKind :: Struct ( ..)
215
+ | ExprKind :: Repeat ( ..)
216
+ | ExprKind :: Yield ( ..) => {
217
+ self . items . push ( ( ItemKind :: NonAsm , span) ) ;
218
+ }
219
+
220
+ ExprKind :: InlineAsm ( ref asm) => {
221
+ self . items . push ( ( ItemKind :: Asm , span) ) ;
222
+ self . check_inline_asm ( expr. hir_id , asm, span) ;
223
+ }
224
+
225
+ ExprKind :: LlvmInlineAsm ( ..) => {
226
+ self . items . push ( ( ItemKind :: Asm , span) ) ;
227
+ self . tcx . struct_span_lint_hir (
228
+ UNSUPPORTED_NAKED_FUNCTIONS ,
229
+ expr. hir_id ,
230
+ span,
231
+ |lint| {
232
+ lint. build (
233
+ "the LLVM-style inline assembly is unsupported in naked functions" ,
234
+ )
235
+ . help ( "use the new asm! syntax specified in RFC 2873" )
236
+ . emit ( ) ;
237
+ } ,
238
+ ) ;
239
+ }
240
+
241
+ ExprKind :: DropTemps ( ..) | ExprKind :: Block ( ..) | ExprKind :: Err => {
242
+ hir:: intravisit:: walk_expr ( self , expr) ;
243
+ }
244
+ }
245
+ }
246
+
247
+ fn check_inline_asm ( & self , hir_id : HirId , asm : & ' tcx hir:: InlineAsm < ' tcx > , span : Span ) {
248
+ let unsupported_operands: Vec < Span > = asm
249
+ . operands
250
+ . iter ( )
251
+ . filter_map ( |& ( ref op, op_sp) | match op {
252
+ InlineAsmOperand :: Const { .. } | InlineAsmOperand :: Sym { .. } => None ,
253
+ InlineAsmOperand :: In { .. }
254
+ | InlineAsmOperand :: Out { .. }
255
+ | InlineAsmOperand :: InOut { .. }
256
+ | InlineAsmOperand :: SplitInOut { .. } => Some ( op_sp) ,
257
+ } )
258
+ . collect ( ) ;
259
+ if !unsupported_operands. is_empty ( ) {
260
+ self . tcx . struct_span_lint_hir (
261
+ UNSUPPORTED_NAKED_FUNCTIONS ,
262
+ hir_id,
263
+ unsupported_operands,
264
+ |lint| {
265
+ lint. build ( "only `const` and `sym` operands are supported in naked functions" )
266
+ . emit ( ) ;
267
+ } ,
268
+ ) ;
269
+ }
270
+
271
+ let unsupported_options: Vec < & ' static str > = [
272
+ ( InlineAsmOptions :: NOMEM , "`nomem`" ) ,
273
+ ( InlineAsmOptions :: NOSTACK , "`nostack`" ) ,
274
+ ( InlineAsmOptions :: PRESERVES_FLAGS , "`preserves_flags`" ) ,
275
+ ( InlineAsmOptions :: PURE , "`pure`" ) ,
276
+ ( InlineAsmOptions :: READONLY , "`readonly`" ) ,
277
+ ]
278
+ . iter ( )
279
+ . filter_map ( |& ( option, name) | if asm. options . contains ( option) { Some ( name) } else { None } )
280
+ . collect ( ) ;
281
+
282
+ if !unsupported_options. is_empty ( ) {
283
+ self . tcx . struct_span_lint_hir ( UNSUPPORTED_NAKED_FUNCTIONS , hir_id, span, |lint| {
284
+ lint. build ( & format ! (
285
+ "asm options unsupported in naked functions: {}" ,
286
+ unsupported_options. join( ", " )
287
+ ) )
288
+ . emit ( ) ;
289
+ } ) ;
290
+ }
291
+
292
+ if !asm. options . contains ( InlineAsmOptions :: NORETURN ) {
293
+ self . tcx . struct_span_lint_hir ( UNSUPPORTED_NAKED_FUNCTIONS , hir_id, span, |lint| {
294
+ lint. build ( "asm in naked functions must use `noreturn` option" ) . emit ( ) ;
295
+ } ) ;
296
+ }
297
+ }
298
+ }
299
+
300
+ impl < ' tcx > Visitor < ' tcx > for CheckInlineAssembly < ' tcx > {
301
+ type Map = ErasedMap < ' tcx > ;
302
+
303
+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
304
+ NestedVisitorMap :: None
305
+ }
306
+
307
+ fn visit_stmt ( & mut self , stmt : & ' tcx hir:: Stmt < ' tcx > ) {
308
+ match stmt. kind {
309
+ StmtKind :: Item ( ..) => { }
310
+ StmtKind :: Local ( ..) => {
311
+ self . items . push ( ( ItemKind :: NonAsm , stmt. span ) ) ;
312
+ }
313
+ StmtKind :: Expr ( ref expr) | StmtKind :: Semi ( ref expr) => {
314
+ self . check_expr ( expr, stmt. span ) ;
315
+ }
316
+ }
317
+ }
318
+
319
+ fn visit_expr ( & mut self , expr : & ' tcx hir:: Expr < ' tcx > ) {
320
+ self . check_expr ( & expr, expr. span ) ;
321
+ }
322
+ }
0 commit comments