@@ -1250,17 +1250,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12501250 }
12511251 }
12521252
1253- fn check_foreign_fn ( & mut self , def_id : LocalDefId , decl : & hir:: FnDecl < ' _ > ) {
1253+ /// Check if a function's argument types and result type are "ffi-safe".
1254+ ///
1255+ /// Argument types and the result type are checked for functions with external ABIs.
1256+ /// For functions with internal ABIs, argument types and the result type are walked to find
1257+ /// fn-ptr types that have external ABIs, as these still need checked.
1258+ fn check_maybe_foreign_fn (
1259+ & mut self ,
1260+ abi : SpecAbi ,
1261+ def_id : LocalDefId ,
1262+ decl : & ' tcx hir:: FnDecl < ' _ > ,
1263+ ) {
12541264 let sig = self . cx . tcx . fn_sig ( def_id) . subst_identity ( ) ;
12551265 let sig = self . cx . tcx . erase_late_bound_regions ( sig) ;
12561266
1267+ let is_internal_abi = self . is_internal_abi ( abi) ;
1268+ let check_ty = |this : & mut ImproperCTypesVisitor < ' a , ' tcx > ,
1269+ hir_ty : & ' tcx hir:: Ty < ' _ > ,
1270+ ty : Ty < ' tcx > ,
1271+ is_return_type : bool | {
1272+ // If this function has an external ABI, then its arguments and return type should be
1273+ // checked..
1274+ if !is_internal_abi {
1275+ this. check_type_for_ffi_and_report_errors ( hir_ty. span , ty, false , is_return_type) ;
1276+ return ;
1277+ }
1278+
1279+ // ..but if this function has an internal ABI, then search the argument or return type
1280+ // for any fn-ptr types with external ABI, which should be checked..
1281+ for ( fn_ptr_ty, span) in this. find_fn_ptr_ty_with_external_abi ( hir_ty, ty) {
1282+ this. check_type_for_ffi_and_report_errors ( span, fn_ptr_ty, false , is_return_type) ;
1283+ }
1284+ } ;
1285+
12571286 for ( input_ty, input_hir) in iter:: zip ( sig. inputs ( ) , decl. inputs ) {
1258- self . check_type_for_ffi_and_report_errors ( input_hir. span , * input_ty, false , false ) ;
1287+ check_ty ( self , input_hir, * input_ty, false ) ;
12591288 }
12601289
12611290 if let hir:: FnRetTy :: Return ( ref ret_hir) = decl. output {
1262- let ret_ty = sig. output ( ) ;
1263- self . check_type_for_ffi_and_report_errors ( ret_hir. span , ret_ty, false , true ) ;
1291+ check_ty ( self , ret_hir, sig. output ( ) , true ) ;
12641292 }
12651293 }
12661294
@@ -1275,28 +1303,134 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12751303 SpecAbi :: Rust | SpecAbi :: RustCall | SpecAbi :: RustIntrinsic | SpecAbi :: PlatformIntrinsic
12761304 )
12771305 }
1306+
1307+ /// Find any fn-ptr types with external ABIs in `ty`.
1308+ ///
1309+ /// For example, `Option<extern "C" fn()>` returns `extern "C" fn()`
1310+ fn find_fn_ptr_ty_with_external_abi (
1311+ & self ,
1312+ hir_ty : & hir:: Ty < ' tcx > ,
1313+ ty : Ty < ' tcx > ,
1314+ ) -> Vec < ( Ty < ' tcx > , Span ) > {
1315+ struct FnPtrFinder < ' parent , ' a , ' tcx > {
1316+ visitor : & ' parent ImproperCTypesVisitor < ' a , ' tcx > ,
1317+ spans : Vec < Span > ,
1318+ tys : Vec < Ty < ' tcx > > ,
1319+ }
1320+
1321+ impl < ' parent , ' a , ' tcx > hir:: intravisit:: Visitor < ' _ > for FnPtrFinder < ' parent , ' a , ' tcx > {
1322+ fn visit_ty ( & mut self , ty : & ' _ hir:: Ty < ' _ > ) {
1323+ debug ! ( ?ty) ;
1324+ if let hir:: TyKind :: BareFn ( hir:: BareFnTy { abi, .. } ) = ty. kind
1325+ && !self . visitor . is_internal_abi ( * abi)
1326+ {
1327+ self . spans . push ( ty. span ) ;
1328+ }
1329+
1330+ hir:: intravisit:: walk_ty ( self , ty)
1331+ }
1332+ }
1333+
1334+ impl < ' vis , ' a , ' tcx > ty:: visit:: TypeVisitor < TyCtxt < ' tcx > > for FnPtrFinder < ' vis , ' a , ' tcx > {
1335+ type BreakTy = Ty < ' tcx > ;
1336+
1337+ fn visit_ty ( & mut self , ty : Ty < ' tcx > ) -> ControlFlow < Self :: BreakTy > {
1338+ if let ty:: FnPtr ( sig) = ty. kind ( ) && !self . visitor . is_internal_abi ( sig. abi ( ) ) {
1339+ self . tys . push ( ty) ;
1340+ }
1341+
1342+ ty. super_visit_with ( self )
1343+ }
1344+ }
1345+
1346+ let mut visitor = FnPtrFinder { visitor : & * self , spans : Vec :: new ( ) , tys : Vec :: new ( ) } ;
1347+ self . cx
1348+ . tcx
1349+ . try_normalize_erasing_regions ( self . cx . param_env , ty)
1350+ . unwrap_or ( ty)
1351+ . visit_with ( & mut visitor) ;
1352+ hir:: intravisit:: Visitor :: visit_ty ( & mut visitor, hir_ty) ;
1353+
1354+ iter:: zip ( visitor. tys . drain ( ..) , visitor. spans . drain ( ..) ) . collect ( )
1355+ }
12781356}
12791357
12801358impl < ' tcx > LateLintPass < ' tcx > for ImproperCTypesDeclarations {
1281- fn check_foreign_item ( & mut self , cx : & LateContext < ' _ > , it : & hir:: ForeignItem < ' _ > ) {
1359+ fn check_foreign_item ( & mut self , cx : & LateContext < ' tcx > , it : & hir:: ForeignItem < ' tcx > ) {
12821360 let mut vis = ImproperCTypesVisitor { cx, mode : CItemKind :: Declaration } ;
12831361 let abi = cx. tcx . hir ( ) . get_foreign_abi ( it. hir_id ( ) ) ;
12841362
1285- if !vis. is_internal_abi ( abi) {
1286- match it. kind {
1287- hir:: ForeignItemKind :: Fn ( ref decl, _, _) => {
1288- vis. check_foreign_fn ( it. owner_id . def_id , decl) ;
1289- }
1290- hir:: ForeignItemKind :: Static ( ref ty, _) => {
1291- vis. check_foreign_static ( it. owner_id , ty. span ) ;
1292- }
1293- hir:: ForeignItemKind :: Type => ( ) ,
1363+ match it. kind {
1364+ hir:: ForeignItemKind :: Fn ( ref decl, _, _) => {
1365+ vis. check_maybe_foreign_fn ( abi, it. owner_id . def_id , decl) ;
12941366 }
1367+ hir:: ForeignItemKind :: Static ( ref ty, _) if !vis. is_internal_abi ( abi) => {
1368+ vis. check_foreign_static ( it. owner_id , ty. span ) ;
1369+ }
1370+ hir:: ForeignItemKind :: Static ( ..) | hir:: ForeignItemKind :: Type => ( ) ,
12951371 }
12961372 }
12971373}
12981374
1375+ impl ImproperCTypesDefinitions {
1376+ fn check_ty_maybe_containing_foreign_fnptr < ' tcx > (
1377+ & mut self ,
1378+ cx : & LateContext < ' tcx > ,
1379+ hir_ty : & ' tcx hir:: Ty < ' _ > ,
1380+ ty : Ty < ' tcx > ,
1381+ ) {
1382+ let mut vis = ImproperCTypesVisitor { cx, mode : CItemKind :: Definition } ;
1383+ for ( fn_ptr_ty, span) in vis. find_fn_ptr_ty_with_external_abi ( hir_ty, ty) {
1384+ vis. check_type_for_ffi_and_report_errors ( span, fn_ptr_ty, true , false ) ;
1385+ }
1386+ }
1387+ }
1388+
1389+ /// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in
1390+ /// `extern "C" { }` blocks):
1391+ ///
1392+ /// - `extern "<abi>" fn` definitions are checked in the same way as the
1393+ /// `ImproperCtypesDeclarations` visitor checks functions if `<abi>` is external (e.g. "C").
1394+ /// - All other items which contain types (e.g. other functions, struct definitions, etc) are
1395+ /// checked for extern fn-ptrs with external ABIs.
12991396impl < ' tcx > LateLintPass < ' tcx > for ImproperCTypesDefinitions {
1397+ fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx hir:: Item < ' tcx > ) {
1398+ match item. kind {
1399+ hir:: ItemKind :: Static ( ty, ..)
1400+ | hir:: ItemKind :: Const ( ty, ..)
1401+ | hir:: ItemKind :: TyAlias ( ty, ..) => {
1402+ self . check_ty_maybe_containing_foreign_fnptr (
1403+ cx,
1404+ ty,
1405+ cx. tcx . type_of ( item. owner_id ) . subst_identity ( ) ,
1406+ ) ;
1407+ }
1408+ // See `check_fn`..
1409+ hir:: ItemKind :: Fn ( ..) => { }
1410+ // See `check_field_def`..
1411+ hir:: ItemKind :: Union ( ..) | hir:: ItemKind :: Struct ( ..) | hir:: ItemKind :: Enum ( ..) => { }
1412+ // Doesn't define something that can contain a external type to be checked.
1413+ hir:: ItemKind :: Impl ( ..)
1414+ | hir:: ItemKind :: TraitAlias ( ..)
1415+ | hir:: ItemKind :: Trait ( ..)
1416+ | hir:: ItemKind :: OpaqueTy ( ..)
1417+ | hir:: ItemKind :: GlobalAsm ( ..)
1418+ | hir:: ItemKind :: ForeignMod { .. }
1419+ | hir:: ItemKind :: Mod ( ..)
1420+ | hir:: ItemKind :: Macro ( ..)
1421+ | hir:: ItemKind :: Use ( ..)
1422+ | hir:: ItemKind :: ExternCrate ( ..) => { }
1423+ }
1424+ }
1425+
1426+ fn check_field_def ( & mut self , cx : & LateContext < ' tcx > , field : & ' tcx hir:: FieldDef < ' tcx > ) {
1427+ self . check_ty_maybe_containing_foreign_fnptr (
1428+ cx,
1429+ field. ty ,
1430+ cx. tcx . type_of ( field. def_id ) . subst_identity ( ) ,
1431+ ) ;
1432+ }
1433+
13001434 fn check_fn (
13011435 & mut self ,
13021436 cx : & LateContext < ' tcx > ,
@@ -1315,9 +1449,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
13151449 } ;
13161450
13171451 let mut vis = ImproperCTypesVisitor { cx, mode : CItemKind :: Definition } ;
1318- if !vis. is_internal_abi ( abi) {
1319- vis. check_foreign_fn ( id, decl) ;
1320- }
1452+ vis. check_maybe_foreign_fn ( abi, id, decl) ;
13211453 }
13221454}
13231455
0 commit comments