@@ -732,8 +732,6 @@ enum FfiResult<'tcx> {
732732 reason : DiagMessage ,
733733 help : Option < DiagMessage > ,
734734 } ,
735- // NOTE: this `allow` is only here for one retroactively-added commit
736- #[ allow( dead_code) ]
737735 FfiUnsafeWrapper {
738736 ty : Ty < ' tcx > ,
739737 reason : DiagMessage ,
@@ -1044,16 +1042,47 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10441042
10451043 match * ty. kind ( ) {
10461044 ty:: Adt ( def, args) => {
1047- if let Some ( boxed) = ty. boxed_ty ( )
1048- && matches ! ( self . mode, CItemKind :: Definition )
1049- {
1050- if boxed. is_sized ( tcx, self . cx . typing_env ( ) ) {
1051- return FfiSafe ;
1045+ if let Some ( inner_ty) = ty. boxed_ty ( ) {
1046+ if inner_ty. is_sized ( tcx, self . cx . typing_env ( ) )
1047+ || matches ! ( inner_ty. kind( ) , ty:: Foreign ( ..) )
1048+ {
1049+ // discussion on declaration vs definition:
1050+ // see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm
1051+ // of this `match *ty.kind()` block
1052+ if matches ! ( self . mode, CItemKind :: Definition ) {
1053+ return FfiSafe ;
1054+ } else {
1055+ let inner_res = self . check_type_for_ffi ( acc, inner_ty) ;
1056+ return match inner_res {
1057+ FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper {
1058+ ty,
1059+ reason : fluent:: lint_improper_ctypes_sized_ptr_to_unsafe_type,
1060+ wrapped : Box :: new ( inner_res) ,
1061+ help : None ,
1062+ } ,
1063+ _ => inner_res,
1064+ } ;
1065+ }
10521066 } else {
1067+ let help = match inner_ty. kind ( ) {
1068+ ty:: Str => Some ( fluent:: lint_improper_ctypes_str_help) ,
1069+ ty:: Slice ( _) => Some ( fluent:: lint_improper_ctypes_slice_help) ,
1070+ ty:: Adt ( def, _)
1071+ if matches ! ( def. adt_kind( ) , AdtKind :: Struct | AdtKind :: Union )
1072+ && matches ! (
1073+ tcx. get_diagnostic_name( def. did( ) ) ,
1074+ Some ( sym:: cstring_type | sym:: cstr_type)
1075+ )
1076+ && !acc. base_ty . is_mutable_ptr ( ) =>
1077+ {
1078+ Some ( fluent:: lint_improper_ctypes_cstr_help)
1079+ }
1080+ _ => None ,
1081+ } ;
10531082 return FfiUnsafe {
10541083 ty,
1055- reason : fluent:: lint_improper_ctypes_box ,
1056- help : None ,
1084+ reason : fluent:: lint_improper_ctypes_unsized_box ,
1085+ help,
10571086 } ;
10581087 }
10591088 }
@@ -1209,15 +1238,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12091238 help : Some ( fluent:: lint_improper_ctypes_tuple_help) ,
12101239 } ,
12111240
1212- ty:: RawPtr ( ty, _) | ty:: Ref ( _, ty, _)
1213- if {
1214- matches ! ( self . mode, CItemKind :: Definition )
1215- && ty. is_sized ( self . cx . tcx , self . cx . typing_env ( ) )
1216- } =>
1217- {
1218- FfiSafe
1219- }
1220-
12211241 ty:: RawPtr ( ty, _)
12221242 if match ty. kind ( ) {
12231243 ty:: Tuple ( tuple) => tuple. is_empty ( ) ,
@@ -1227,7 +1247,66 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12271247 FfiSafe
12281248 }
12291249
1230- ty:: RawPtr ( ty, _) | ty:: Ref ( _, ty, _) => self . check_type_for_ffi ( acc, ty) ,
1250+ ty:: RawPtr ( inner_ty, _) | ty:: Ref ( _, inner_ty, _) => {
1251+ if inner_ty. is_sized ( tcx, self . cx . typing_env ( ) )
1252+ || matches ! ( inner_ty. kind( ) , ty:: Foreign ( ..) )
1253+ {
1254+ // there's a nuance on what this lint should do for function definitions
1255+ // (touched upon in https://github.com/rust-lang/rust/issues/66220 and https://github.com/rust-lang/rust/pull/72700)
1256+ //
1257+ // (`extern "C" fn fn_name(...) {...}`) versus declarations (`extern "C" {fn fn_name(...);}`).
1258+ // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
1259+ // (which has a stable layout) but points to FFI-unsafe type, is it safe?
1260+ // on one hand, the function's ABI will match that of a similar C-declared function API,
1261+ // on the other, dereferencing the pointer in not-rust will be painful.
1262+ // In this code, the opinion is split between function declarations and function definitions.
1263+ // For declarations, we see this as unsafe, but for definitions, we see this as safe.
1264+ // This is mostly because, for extern function declarations, the actual definition of the function is written somewhere else,
1265+ // so the fact that a pointer's pointee should be treated as opaque to one side or the other can be explicitely written out.
1266+ // For extern function definitions, however, both callee and some callers can be written in rust,
1267+ // so developers need to keep as much typing information as possible.
1268+ if matches ! ( self . mode, CItemKind :: Definition ) {
1269+ return FfiSafe ;
1270+ } else if matches ! ( ty. kind( ) , ty:: RawPtr ( ..) )
1271+ && matches ! ( inner_ty. kind( ) , ty:: Tuple ( tuple) if tuple. is_empty( ) )
1272+ {
1273+ FfiSafe
1274+ } else {
1275+ let inner_res = self . check_type_for_ffi ( acc, inner_ty) ;
1276+ return match inner_res {
1277+ FfiSafe => inner_res,
1278+ _ => FfiUnsafeWrapper {
1279+ ty,
1280+ reason : fluent:: lint_improper_ctypes_sized_ptr_to_unsafe_type,
1281+ wrapped : Box :: new ( inner_res) ,
1282+ help : None ,
1283+ } ,
1284+ } ;
1285+ }
1286+ } else {
1287+ let help = match inner_ty. kind ( ) {
1288+ ty:: Str => Some ( fluent:: lint_improper_ctypes_str_help) ,
1289+ ty:: Slice ( _) => Some ( fluent:: lint_improper_ctypes_slice_help) ,
1290+ ty:: Adt ( def, _)
1291+ if matches ! ( def. adt_kind( ) , AdtKind :: Struct | AdtKind :: Union )
1292+ && matches ! (
1293+ tcx. get_diagnostic_name( def. did( ) ) ,
1294+ Some ( sym:: cstring_type | sym:: cstr_type)
1295+ )
1296+ && !acc. base_ty . is_mutable_ptr ( ) =>
1297+ {
1298+ Some ( fluent:: lint_improper_ctypes_cstr_help)
1299+ }
1300+ _ => None ,
1301+ } ;
1302+ let reason = match ty. kind ( ) {
1303+ ty:: RawPtr ( ..) => fluent:: lint_improper_ctypes_unsized_ptr,
1304+ ty:: Ref ( ..) => fluent:: lint_improper_ctypes_unsized_ref,
1305+ _ => unreachable ! ( ) ,
1306+ } ;
1307+ FfiUnsafe { ty, reason, help }
1308+ }
1309+ }
12311310
12321311 ty:: Array ( inner_ty, _) => self . check_type_for_ffi ( acc, inner_ty) ,
12331312
@@ -1245,7 +1324,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12451324 for arg in sig. inputs ( ) {
12461325 match self . check_type_for_ffi ( acc, * arg) {
12471326 FfiSafe => { }
1248- r => return r,
1327+ r => {
1328+ return FfiUnsafeWrapper {
1329+ ty,
1330+ reason : fluent:: lint_improper_ctypes_fnptr_indirect_reason,
1331+ help : None ,
1332+ wrapped : Box :: new ( r) ,
1333+ } ;
1334+ }
12491335 }
12501336 }
12511337
@@ -1254,7 +1340,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12541340 return FfiSafe ;
12551341 }
12561342
1257- self . check_type_for_ffi ( acc, ret_ty)
1343+ match self . check_type_for_ffi ( acc, ret_ty) {
1344+ r @ ( FfiSafe | FfiPhantom ( _) ) => r,
1345+ r => FfiUnsafeWrapper {
1346+ ty : ty. clone ( ) ,
1347+ reason : fluent:: lint_improper_ctypes_fnptr_indirect_reason,
1348+ help : None ,
1349+ wrapped : Box :: new ( r) ,
1350+ } ,
1351+ }
12581352 }
12591353
12601354 ty:: Foreign ( ..) => FfiSafe ,
0 commit comments