@@ -599,8 +599,6 @@ enum FfiResult<'tcx> {
599599 reason : DiagMessage ,
600600 help : Option < DiagMessage > ,
601601 } ,
602- // NOTE: this `allow` is only here for one retroactively-added commit
603- #[ allow( dead_code) ]
604602 FfiUnsafeWrapper {
605603 ty : Ty < ' tcx > ,
606604 reason : DiagMessage ,
@@ -915,16 +913,47 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
915913
916914 match * ty. kind ( ) {
917915 ty:: Adt ( def, args) => {
918- if let Some ( boxed) = ty. boxed_ty ( )
919- && matches ! ( self . mode, CItemKind :: Definition )
920- {
921- if boxed. is_sized ( tcx, self . cx . param_env ) {
922- return FfiSafe ;
916+ if let Some ( inner_ty) = ty. boxed_ty ( ) {
917+ if inner_ty. is_sized ( tcx, self . cx . param_env )
918+ || matches ! ( inner_ty. kind( ) , ty:: Foreign ( ..) )
919+ {
920+ // discussion on declaration vs definition:
921+ // see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm
922+ // of this `match *ty.kind()` block
923+ if matches ! ( self . mode, CItemKind :: Definition ) {
924+ return FfiSafe ;
925+ } else {
926+ let inner_res = self . check_type_for_ffi ( acc, inner_ty) ;
927+ return match inner_res {
928+ FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper {
929+ ty,
930+ reason : fluent:: lint_improper_ctypes_sized_ptr_to_unsafe_type,
931+ wrapped : Box :: new ( inner_res) ,
932+ help : None ,
933+ } ,
934+ _ => inner_res,
935+ } ;
936+ }
923937 } else {
938+ let help = match inner_ty. kind ( ) {
939+ ty:: Str => Some ( fluent:: lint_improper_ctypes_str_help) ,
940+ ty:: Slice ( _) => Some ( fluent:: lint_improper_ctypes_slice_help) ,
941+ ty:: Adt ( def, _)
942+ if matches ! ( def. adt_kind( ) , AdtKind :: Struct | AdtKind :: Union )
943+ && matches ! (
944+ tcx. get_diagnostic_name( def. did( ) ) ,
945+ Some ( sym:: cstring_type | sym:: cstr_type)
946+ )
947+ && !acc. base_ty . is_mutable_ptr ( ) =>
948+ {
949+ Some ( fluent:: lint_improper_ctypes_cstr_help)
950+ }
951+ _ => None ,
952+ } ;
924953 return FfiUnsafe {
925954 ty,
926- reason : fluent:: lint_improper_ctypes_box ,
927- help : None ,
955+ reason : fluent:: lint_improper_ctypes_unsized_box ,
956+ help,
928957 } ;
929958 }
930959 }
@@ -1087,15 +1116,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10871116 help : Some ( fluent:: lint_improper_ctypes_tuple_help) ,
10881117 } ,
10891118
1090- ty:: RawPtr ( ty, _) | ty:: Ref ( _, ty, _)
1091- if {
1092- matches ! ( self . mode, CItemKind :: Definition )
1093- && ty. is_sized ( self . cx . tcx , self . cx . param_env )
1094- } =>
1095- {
1096- FfiSafe
1097- }
1098-
10991119 ty:: RawPtr ( ty, _)
11001120 if match ty. kind ( ) {
11011121 ty:: Tuple ( tuple) => tuple. is_empty ( ) ,
@@ -1105,7 +1125,66 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11051125 FfiSafe
11061126 }
11071127
1108- ty:: RawPtr ( ty, _) | ty:: Ref ( _, ty, _) => self . check_type_for_ffi ( acc, ty) ,
1128+ ty:: RawPtr ( inner_ty, _) | ty:: Ref ( _, inner_ty, _) => {
1129+ if inner_ty. is_sized ( tcx, self . cx . param_env )
1130+ || matches ! ( inner_ty. kind( ) , ty:: Foreign ( ..) )
1131+ {
1132+ // there's a nuance on what this lint should do for function definitions
1133+ // (touched upon in https://github.com/rust-lang/rust/issues/66220 and https://github.com/rust-lang/rust/pull/72700)
1134+ //
1135+ // (`extern "C" fn fn_name(...) {...}`) versus declarations (`extern "C" {fn fn_name(...);}`).
1136+ // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
1137+ // (which has a stable layout) but points to FFI-unsafe type, is it safe?
1138+ // on one hand, the function's ABI will match that of a similar C-declared function API,
1139+ // on the other, dereferencing the pointer in not-rust will be painful.
1140+ // In this code, the opinion is split between function declarations and function definitions.
1141+ // For declarations, we see this as unsafe, but for definitions, we see this as safe.
1142+ // This is mostly because, for extern function declarations, the actual definition of the function is written somewhere else,
1143+ // so the fact that a pointer's pointee should be treated as opaque to one side or the other can be explicitely written out.
1144+ // For extern function definitions, however, both callee and some callers can be written in rust,
1145+ // so developers need to keep as much typing information as possible.
1146+ if matches ! ( self . mode, CItemKind :: Definition ) {
1147+ return FfiSafe ;
1148+ } else if matches ! ( ty. kind( ) , ty:: RawPtr ( ..) )
1149+ && matches ! ( inner_ty. kind( ) , ty:: Tuple ( tuple) if tuple. is_empty( ) )
1150+ {
1151+ FfiSafe
1152+ } else {
1153+ let inner_res = self . check_type_for_ffi ( acc, inner_ty) ;
1154+ return match inner_res {
1155+ FfiSafe => inner_res,
1156+ _ => FfiUnsafeWrapper {
1157+ ty,
1158+ reason : fluent:: lint_improper_ctypes_sized_ptr_to_unsafe_type,
1159+ wrapped : Box :: new ( inner_res) ,
1160+ help : None ,
1161+ } ,
1162+ } ;
1163+ }
1164+ } else {
1165+ let help = match inner_ty. kind ( ) {
1166+ ty:: Str => Some ( fluent:: lint_improper_ctypes_str_help) ,
1167+ ty:: Slice ( _) => Some ( fluent:: lint_improper_ctypes_slice_help) ,
1168+ ty:: Adt ( def, _)
1169+ if matches ! ( def. adt_kind( ) , AdtKind :: Struct | AdtKind :: Union )
1170+ && matches ! (
1171+ tcx. get_diagnostic_name( def. did( ) ) ,
1172+ Some ( sym:: cstring_type | sym:: cstr_type)
1173+ )
1174+ && !acc. base_ty . is_mutable_ptr ( ) =>
1175+ {
1176+ Some ( fluent:: lint_improper_ctypes_cstr_help)
1177+ }
1178+ _ => None ,
1179+ } ;
1180+ let reason = match ty. kind ( ) {
1181+ ty:: RawPtr ( ..) => fluent:: lint_improper_ctypes_unsized_ptr,
1182+ ty:: Ref ( ..) => fluent:: lint_improper_ctypes_unsized_ref,
1183+ _ => unreachable ! ( ) ,
1184+ } ;
1185+ FfiUnsafe { ty, reason, help }
1186+ }
1187+ }
11091188
11101189 ty:: Array ( inner_ty, _) => self . check_type_for_ffi ( acc, inner_ty) ,
11111190
@@ -1123,7 +1202,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11231202 for arg in sig. inputs ( ) {
11241203 match self . check_type_for_ffi ( acc, * arg) {
11251204 FfiSafe => { }
1126- r => return r,
1205+ r => {
1206+ return FfiUnsafeWrapper {
1207+ ty,
1208+ reason : fluent:: lint_improper_ctypes_fnptr_indirect_reason,
1209+ help : None ,
1210+ wrapped : Box :: new ( r) ,
1211+ } ;
1212+ }
11271213 }
11281214 }
11291215
@@ -1132,7 +1218,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
11321218 return FfiSafe ;
11331219 }
11341220
1135- self . check_type_for_ffi ( acc, ret_ty)
1221+ match self . check_type_for_ffi ( acc, ret_ty) {
1222+ r @ ( FfiSafe | FfiPhantom ( _) ) => r,
1223+ r => FfiUnsafeWrapper {
1224+ ty : ty. clone ( ) ,
1225+ reason : fluent:: lint_improper_ctypes_fnptr_indirect_reason,
1226+ help : None ,
1227+ wrapped : Box :: new ( r) ,
1228+ } ,
1229+ }
11361230 }
11371231
11381232 ty:: Foreign ( ..) => FfiSafe ,
0 commit comments