@@ -350,7 +350,6 @@ impl<'tcx> FfiResult<'tcx> {
350350 /// If the FfiUnsafe variant, 'wraps' all reasons,
351351 /// creating new `FfiUnsafeReason`s, putting the originals as their `inner` fields.
352352 /// Otherwise, keep unchanged.
353- #[ expect( unused) ]
354353 fn wrap_all ( self , ty : Ty < ' tcx > , note : DiagMessage , help : Option < DiagMessage > ) -> Self {
355354 match self {
356355 Self :: FfiUnsafe ( this) => {
@@ -705,6 +704,18 @@ impl VisitorState {
705704 fn is_field ( & self ) -> bool {
706705 self . ephemeral_flags . contains ( EphemeralStateFlags :: IN_ADT )
707706 }
707+
708+ /// Whether the current type is behind a pointer
709+ #[ inline]
710+ fn is_pointee ( & self ) -> bool {
711+ self . ephemeral_flags . contains ( EphemeralStateFlags :: IN_PTR )
712+ }
713+
714+ /// Whether the current type is behind a pointer that doesn't allow mutating this
715+ #[ inline]
716+ fn is_nonmut_pointee ( & self ) -> bool {
717+ self . is_pointee ( ) && !self . ephemeral_flags . contains ( EphemeralStateFlags :: PTR_MUT )
718+ }
708719}
709720
710721/// Visitor used to recursively traverse MIR types and evaluate FFI-safety.
@@ -717,13 +728,30 @@ struct ImproperCTypesVisitor<'a, 'tcx> {
717728 cache : FxHashSet < Ty < ' tcx > > ,
718729 /// The original type being checked, before we recursed
719730 /// to any other types it contains.
720- base_ty : Ty < ' tcx > ,
721731 base_fn_mode : CItemKind ,
722732}
723733
724734impl < ' a , ' tcx > ImproperCTypesVisitor < ' a , ' tcx > {
725- fn new ( cx : & ' a LateContext < ' tcx > , base_ty : Ty < ' tcx > , base_fn_mode : CItemKind ) -> Self {
726- Self { cx, base_ty, base_fn_mode, cache : FxHashSet :: default ( ) }
735+ fn new ( cx : & ' a LateContext < ' tcx > , base_fn_mode : CItemKind ) -> Self {
736+ Self { cx, base_fn_mode, cache : FxHashSet :: default ( ) }
737+ }
738+
739+ /// Return the right help for Cstring and Cstr-linked unsafety.
740+ fn visit_cstr ( & mut self , state : VisitorState , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
741+ debug_assert ! ( matches!( ty. kind( ) , ty:: Adt ( def, _)
742+ if matches!(
743+ self . cx. tcx. get_diagnostic_name( def. did( ) ) ,
744+ Some ( sym:: cstring_type | sym:: cstr_type)
745+ )
746+ ) ) ;
747+
748+ let help = if state. is_nonmut_pointee ( ) {
749+ fluent:: lint_improper_ctypes_cstr_help_const
750+ } else {
751+ fluent:: lint_improper_ctypes_cstr_help_mut
752+ } ;
753+
754+ FfiResult :: new_with_reason ( ty, fluent:: lint_improper_ctypes_cstr_reason, Some ( help) )
727755 }
728756
729757 /// Checks if the given indirection (box,ref,pointer) is "ffi-safe".
@@ -737,6 +765,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
737765 use FfiResult :: * ;
738766 let tcx = self . cx . tcx ;
739767
768+ if let ty:: Adt ( def, _) = inner_ty. kind ( ) {
769+ if let Some ( diag_name @ ( sym:: cstring_type | sym:: cstr_type) ) =
770+ tcx. get_diagnostic_name ( def. did ( ) )
771+ {
772+ // we have better error messages when checking for C-strings directly
773+ let mut cstr_res = self . visit_cstr ( state. get_next ( ty) , inner_ty) ; // always unsafe with one depth-one reason.
774+
775+ // Cstr pointer have metadata, CString is Sized
776+ if diag_name == sym:: cstr_type {
777+ // we need to override the "type" part of `cstr_res`'s only FfiResultReason
778+ // so it says that it's the use of the indirection that is unsafe
779+ match cstr_res {
780+ FfiResult :: FfiUnsafe ( ref mut reasons) => {
781+ reasons. first_mut ( ) . unwrap ( ) . reason . ty = ty;
782+ }
783+ _ => unreachable ! ( ) ,
784+ }
785+ let note = match indirection_kind {
786+ IndirectionKind :: RawPtr => fluent:: lint_improper_ctypes_unsized_ptr,
787+ IndirectionKind :: Ref => fluent:: lint_improper_ctypes_unsized_ref,
788+ IndirectionKind :: Box => fluent:: lint_improper_ctypes_unsized_box,
789+ } ;
790+ return cstr_res. wrap_all ( ty, note, None ) ;
791+ } else {
792+ return cstr_res;
793+ }
794+ }
795+ }
796+
740797 match indirection_kind {
741798 IndirectionKind :: Box => {
742799 // FIXME(ctypes): this logic is broken, but it still fits the current tests:
@@ -977,13 +1034,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
9771034 AdtKind :: Struct | AdtKind :: Union => {
9781035 if let Some ( sym:: cstring_type | sym:: cstr_type) =
9791036 tcx. get_diagnostic_name ( def. did ( ) )
980- && !self . base_ty . is_mutable_ptr ( )
9811037 {
982- return FfiResult :: new_with_reason (
983- ty,
984- fluent:: lint_improper_ctypes_cstr_reason,
985- Some ( fluent:: lint_improper_ctypes_cstr_help) ,
986- ) ;
1038+ return self . visit_cstr ( state, ty) ;
9871039 }
9881040 self . visit_struct_or_union ( state, ty, def, args)
9891041 }
@@ -1232,7 +1284,7 @@ impl<'tcx> ImproperCTypesLint {
12321284 iter:: zip ( visitor. tys . drain ( ..) , visitor. spans . drain ( ..) ) ,
12331285 ) ;
12341286 for ( depth, ( fn_ptr_ty, span) ) in all_types {
1235- let mut visitor = ImproperCTypesVisitor :: new ( cx, fn_ptr_ty , fn_mode) ;
1287+ let mut visitor = ImproperCTypesVisitor :: new ( cx, fn_mode) ;
12361288 let bridge_state = VisitorState { depth, ..state } ;
12371289 // FIXME(ctypes): make a check_for_fnptr
12381290 let ffi_res = visitor. check_type ( bridge_state, fn_ptr_ty) ;
@@ -1283,7 +1335,7 @@ impl<'tcx> ImproperCTypesLint {
12831335
12841336 fn check_foreign_static ( & mut self , cx : & LateContext < ' tcx > , id : hir:: OwnerId , span : Span ) {
12851337 let ty = cx. tcx . type_of ( id) . instantiate_identity ( ) ;
1286- let mut visitor = ImproperCTypesVisitor :: new ( cx, ty , CItemKind :: ImportedExtern ) ;
1338+ let mut visitor = ImproperCTypesVisitor :: new ( cx, CItemKind :: ImportedExtern ) ;
12871339 let ffi_res = visitor. check_type ( VisitorState :: static_var ( ) , ty) ;
12881340 self . process_ffi_result ( cx, span, ffi_res, CItemKind :: ImportedExtern ) ;
12891341 }
@@ -1301,14 +1353,14 @@ impl<'tcx> ImproperCTypesLint {
13011353
13021354 for ( input_ty, input_hir) in iter:: zip ( sig. inputs ( ) , decl. inputs ) {
13031355 let visit_state = VisitorState :: argument_from_fnmode ( fn_mode) ;
1304- let mut visitor = ImproperCTypesVisitor :: new ( cx, * input_ty , fn_mode) ;
1356+ let mut visitor = ImproperCTypesVisitor :: new ( cx, fn_mode) ;
13051357 let ffi_res = visitor. check_type ( visit_state, * input_ty) ;
13061358 self . process_ffi_result ( cx, input_hir. span , ffi_res, fn_mode) ;
13071359 }
13081360
13091361 if let hir:: FnRetTy :: Return ( ret_hir) = decl. output {
13101362 let visit_state = VisitorState :: return_from_fnmode ( fn_mode) ;
1311- let mut visitor = ImproperCTypesVisitor :: new ( cx, sig . output ( ) , fn_mode) ;
1363+ let mut visitor = ImproperCTypesVisitor :: new ( cx, fn_mode) ;
13121364 let ffi_res = visitor. check_type ( visit_state, sig. output ( ) ) ;
13131365 self . process_ffi_result ( cx, ret_hir. span , ffi_res, fn_mode) ;
13141366 }
0 commit comments