@@ -599,8 +599,6 @@ enum FfiResult<'tcx> {
599
599
reason : DiagMessage ,
600
600
help : Option < DiagMessage > ,
601
601
} ,
602
- // NOTE: this `allow` is only here for one retroactively-added commit
603
- #[ allow( dead_code) ]
604
602
FfiUnsafeWrapper {
605
603
ty : Ty < ' tcx > ,
606
604
reason : DiagMessage ,
@@ -915,16 +913,47 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
915
913
916
914
match * ty. kind ( ) {
917
915
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
+ }
923
937
} 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
+ } ;
924
953
return FfiUnsafe {
925
954
ty,
926
- reason : fluent:: lint_improper_ctypes_box ,
927
- help : None ,
955
+ reason : fluent:: lint_improper_ctypes_unsized_box ,
956
+ help,
928
957
} ;
929
958
}
930
959
}
@@ -1087,15 +1116,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1087
1116
help : Some ( fluent:: lint_improper_ctypes_tuple_help) ,
1088
1117
} ,
1089
1118
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
-
1099
1119
ty:: RawPtr ( ty, _)
1100
1120
if match ty. kind ( ) {
1101
1121
ty:: Tuple ( tuple) => tuple. is_empty ( ) ,
@@ -1105,7 +1125,66 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1105
1125
FfiSafe
1106
1126
}
1107
1127
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
+ }
1109
1188
1110
1189
ty:: Array ( inner_ty, _) => self . check_type_for_ffi ( acc, inner_ty) ,
1111
1190
@@ -1123,7 +1202,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1123
1202
for arg in sig. inputs ( ) {
1124
1203
match self . check_type_for_ffi ( acc, * arg) {
1125
1204
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
+ }
1127
1213
}
1128
1214
}
1129
1215
@@ -1132,7 +1218,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1132
1218
return FfiSafe ;
1133
1219
}
1134
1220
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
+ }
1136
1230
}
1137
1231
1138
1232
ty:: Foreign ( ..) => FfiSafe ,
0 commit comments