@@ -356,7 +356,6 @@ impl<'tcx> FfiResult<'tcx> {
356356 /// If the FfiUnsafe variant, 'wraps' all reasons,
357357 /// creating new `FfiUnsafeReason`s, putting the originals as their `inner` fields.
358358 /// Otherwise, keep unchanged.
359- #[ expect( unused) ]
360359 fn wrap_all ( self , ty : Ty < ' tcx > , note : DiagMessage , help : Option < DiagMessage > ) -> Self {
361360 match self {
362361 Self :: FfiUnsafe ( this) => {
@@ -525,6 +524,12 @@ bitflags! {
525524#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
526525enum OuterTyKind {
527526 None ,
527+ /// Pointee through ref, raw pointer or Box
528+ /// (we don't need to distinguish the ownership of Box specifically)
529+ Pointee {
530+ mutable : hir:: Mutability ,
531+ raw : bool ,
532+ } ,
528533 /// For struct/enum/union fields
529534 AdtField ,
530535 /// Placeholder for properties that will be used eventually
@@ -536,16 +541,17 @@ impl OuterTyKind {
536541 fn from_outer_ty < ' tcx > ( ty : Ty < ' tcx > ) -> Self {
537542 match ty. kind ( ) {
538543 ty:: FnPtr ( ..) => Self :: None ,
544+ k @ ( ty:: Ref ( _, _, mutable) | ty:: RawPtr ( _, mutable) ) => {
545+ Self :: Pointee { raw : matches ! ( k, ty:: RawPtr ( ..) ) , mutable : * mutable }
546+ }
539547 ty:: Adt ( ..) => {
540548 if ty. boxed_ty ( ) . is_some ( ) {
541- Self :: UnusedVariant
549+ Self :: Pointee { raw : false , mutable : hir :: Mutability :: Mut }
542550 } else {
543551 Self :: AdtField
544552 }
545553 }
546- ty:: RawPtr ( ..) | ty:: Ref ( ..) | ty:: Tuple ( ..) | ty:: Array ( ..) | ty:: Slice ( _) => {
547- Self :: UnusedVariant
548- }
554+ ty:: Tuple ( ..) | ty:: Array ( ..) | ty:: Slice ( _) => Self :: UnusedVariant ,
549555 k @ _ => bug ! ( "Unexpected outer type {:?} of kind {:?}" , ty, k) ,
550556 }
551557 }
@@ -666,6 +672,11 @@ impl VisitorState {
666672 fn is_field ( & self ) -> bool {
667673 matches ! ( self . outer_ty_kind, OuterTyKind :: AdtField )
668674 }
675+
676+ /// Whether the current type is behind a pointer that doesn't allow mutating this
677+ fn is_nonmut_pointee ( & self ) -> bool {
678+ matches ! ( self . outer_ty_kind, OuterTyKind :: Pointee { mutable: hir:: Mutability :: Not , .. } )
679+ }
669680}
670681
671682/// Visitor used to recursively traverse MIR types and evaluate FFI-safety.
@@ -676,14 +687,29 @@ struct ImproperCTypesVisitor<'a, 'tcx> {
676687 /// To prevent problems with recursive types,
677688 /// add a types-in-check cache.
678689 cache : FxHashSet < Ty < ' tcx > > ,
679- /// The original type being checked, before we recursed
680- /// to any other types it contains.
681- base_ty : Ty < ' tcx > ,
682690}
683691
684692impl < ' a , ' tcx > ImproperCTypesVisitor < ' a , ' tcx > {
685- fn new ( cx : & ' a LateContext < ' tcx > , base_ty : Ty < ' tcx > ) -> Self {
686- Self { cx, base_ty, cache : FxHashSet :: default ( ) }
693+ fn new ( cx : & ' a LateContext < ' tcx > ) -> Self {
694+ Self { cx, cache : FxHashSet :: default ( ) }
695+ }
696+
697+ /// Return the right help for Cstring and Cstr-linked unsafety.
698+ fn visit_cstr ( & mut self , state : VisitorState , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
699+ debug_assert ! ( matches!( ty. kind( ) , ty:: Adt ( def, _)
700+ if matches!(
701+ self . cx. tcx. get_diagnostic_name( def. did( ) ) ,
702+ Some ( sym:: cstring_type | sym:: cstr_type)
703+ )
704+ ) ) ;
705+
706+ let help = if state. is_nonmut_pointee ( ) {
707+ fluent:: lint_improper_ctypes_cstr_help_const
708+ } else {
709+ fluent:: lint_improper_ctypes_cstr_help_mut
710+ } ;
711+
712+ FfiResult :: new_with_reason ( ty, fluent:: lint_improper_ctypes_cstr_reason, Some ( help) )
687713 }
688714
689715 /// Checks if the given indirection (box,ref,pointer) is "ffi-safe".
@@ -697,6 +723,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
697723 use FfiResult :: * ;
698724 let tcx = self . cx . tcx ;
699725
726+ if let ty:: Adt ( def, _) = inner_ty. kind ( ) {
727+ if let Some ( diag_name @ ( sym:: cstring_type | sym:: cstr_type) ) =
728+ tcx. get_diagnostic_name ( def. did ( ) )
729+ {
730+ // we have better error messages when checking for C-strings directly
731+ let mut cstr_res = self . visit_cstr ( state. get_next ( ty) , inner_ty) ; // always unsafe with one depth-one reason.
732+
733+ // Cstr pointer have metadata, CString is Sized
734+ if diag_name == sym:: cstr_type {
735+ // we need to override the "type" part of `cstr_res`'s only FfiResultReason
736+ // so it says that it's the use of the indirection that is unsafe
737+ match cstr_res {
738+ FfiResult :: FfiUnsafe ( ref mut reasons) => {
739+ reasons. first_mut ( ) . unwrap ( ) . reason . ty = ty;
740+ }
741+ _ => unreachable ! ( ) ,
742+ }
743+ let note = match indirection_kind {
744+ IndirectionKind :: RawPtr => fluent:: lint_improper_ctypes_unsized_ptr,
745+ IndirectionKind :: Ref => fluent:: lint_improper_ctypes_unsized_ref,
746+ IndirectionKind :: Box => fluent:: lint_improper_ctypes_unsized_box,
747+ } ;
748+ return cstr_res. wrap_all ( ty, note, None ) ;
749+ } else {
750+ return cstr_res;
751+ }
752+ }
753+ }
754+
700755 match indirection_kind {
701756 IndirectionKind :: Box => {
702757 // FIXME(ctypes): this logic is broken, but it still fits the current tests:
@@ -933,13 +988,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
933988 AdtKind :: Struct | AdtKind :: Union => {
934989 if let Some ( sym:: cstring_type | sym:: cstr_type) =
935990 tcx. get_diagnostic_name ( def. did ( ) )
936- && !self . base_ty . is_mutable_ptr ( )
937991 {
938- return FfiResult :: new_with_reason (
939- ty,
940- fluent:: lint_improper_ctypes_cstr_reason,
941- Some ( fluent:: lint_improper_ctypes_cstr_help) ,
942- ) ;
992+ return self . visit_cstr ( state, ty) ;
943993 }
944994 self . visit_struct_or_union ( state, ty, def, args)
945995 }
@@ -1220,7 +1270,7 @@ impl<'tcx> ImproperCTypesLint {
12201270 /// Check that an extern "ABI" static variable is of a ffi-safe type.
12211271 fn check_foreign_static ( & mut self , cx : & LateContext < ' tcx > , id : hir:: OwnerId , span : Span ) {
12221272 let ty = cx. tcx . type_of ( id) . instantiate_identity ( ) ;
1223- let mut visitor = ImproperCTypesVisitor :: new ( cx, ty ) ;
1273+ let mut visitor = ImproperCTypesVisitor :: new ( cx) ;
12241274 let ffi_res = visitor. check_type ( VisitorState :: static_var ( ) , ty) ;
12251275 self . process_ffi_result ( cx, span, ffi_res, CItemKind :: ImportedExtern ) ;
12261276 }
@@ -1239,15 +1289,15 @@ impl<'tcx> ImproperCTypesLint {
12391289 for ( input_ty, input_hir) in iter:: zip ( sig. inputs ( ) , decl. inputs ) {
12401290 let mut state = VisitorState :: entry_point_from_fnmode ( fn_mode, FnPos :: Arg ) ;
12411291 state. depth = depth;
1242- let mut visitor = ImproperCTypesVisitor :: new ( cx, * input_ty ) ;
1292+ let mut visitor = ImproperCTypesVisitor :: new ( cx) ;
12431293 let ffi_res = visitor. check_type ( state, * input_ty) ;
12441294 self . process_ffi_result ( cx, input_hir. span , ffi_res, fn_mode) ;
12451295 }
12461296
12471297 if let hir:: FnRetTy :: Return ( ret_hir) = decl. output {
12481298 let mut state = VisitorState :: entry_point_from_fnmode ( fn_mode, FnPos :: Ret ) ;
12491299 state. depth = depth;
1250- let mut visitor = ImproperCTypesVisitor :: new ( cx, sig . output ( ) ) ;
1300+ let mut visitor = ImproperCTypesVisitor :: new ( cx) ;
12511301 let ffi_res = visitor. check_type ( state, sig. output ( ) ) ;
12521302 self . process_ffi_result ( cx, ret_hir. span , ffi_res, fn_mode) ;
12531303 }
0 commit comments