1+ //! Checks validity of naked functions.
2+
3+ use rustc_ast:: InlineAsmOptions ;
14use rustc_hir as hir;
25use rustc_hir:: def_id:: LocalDefId ;
36use rustc_hir:: intravisit:: { ErasedMap , FnKind , NestedVisitorMap , Visitor } ;
7+ use rustc_hir:: { ExprKind , HirId , InlineAsmOperand , StmtKind } ;
48use rustc_middle:: ty:: query:: Providers ;
59use rustc_middle:: ty:: TyCtxt ;
10+ use rustc_session:: lint:: builtin:: UNSUPPORTED_NAKED_FUNCTIONS ;
611use rustc_span:: symbol:: sym;
712use rustc_span:: Span ;
13+ use rustc_target:: spec:: abi:: Abi ;
814
915fn check_mod_naked_functions ( tcx : TyCtxt < ' _ > , module_def_id : LocalDefId ) {
1016 tcx. hir ( ) . visit_item_likes_in_module (
@@ -33,27 +39,52 @@ impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> {
3339 fk : FnKind < ' v > ,
3440 _fd : & ' tcx hir:: FnDecl < ' tcx > ,
3541 body_id : hir:: BodyId ,
36- _span : Span ,
37- _hir_id : hir :: HirId ,
42+ span : Span ,
43+ hir_id : HirId ,
3844 ) {
45+ let ident_span;
46+ let fn_header;
47+
3948 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+ }
4363 }
4464
4565 let naked = fk. attrs ( ) . iter ( ) . any ( |attr| attr. has_name ( sym:: naked) ) ;
4666 if naked {
4767 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) ;
5072 }
5173 }
5274}
5375
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+
5485/// 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 {
5788 match param. pat . kind {
5889 hir:: PatKind :: Wild
5990 | hir:: PatKind :: Binding ( hir:: BindingAnnotation :: Unannotated , _, _, None ) => { }
@@ -69,23 +100,23 @@ fn check_params(tcx: TyCtxt<'_>, body: &hir::Body<'_>) {
69100 }
70101}
71102
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 > ) {
74105 let mut params = hir:: HirIdSet :: default ( ) ;
75106 for param in body. params {
76107 param. pat . each_binding ( |_binding_mode, hir_id, _span, _ident| {
77108 params. insert ( hir_id) ;
78109 } ) ;
79110 }
80- CheckBody { tcx, params } . visit_body ( body) ;
111+ CheckParameters { tcx, params } . visit_body ( body) ;
81112}
82113
83- struct CheckBody < ' tcx > {
114+ struct CheckParameters < ' tcx > {
84115 tcx : TyCtxt < ' tcx > ,
85116 params : hir:: HirIdSet ,
86117}
87118
88- impl < ' tcx > Visitor < ' tcx > for CheckBody < ' tcx > {
119+ impl < ' tcx > Visitor < ' tcx > for CheckParameters < ' tcx > {
89120 type Map = ErasedMap < ' tcx > ;
90121
91122 fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
@@ -103,11 +134,189 @@ impl<'tcx> Visitor<'tcx> for CheckBody<'tcx> {
103134 . sess
104135 . struct_span_err (
105136 expr. span ,
106- "use of parameters not allowed inside naked functions" ,
137+ "referencing function parameters is not allowed in naked functions" ,
107138 )
139+ . help ( "follow the calling convention in asm block to use parameters" )
108140 . emit ( ) ;
141+ return ;
109142 }
110143 }
111144 hir:: intravisit:: walk_expr ( self , expr) ;
112145 }
113146}
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