1use std::iter;
2use std::ops::ControlFlow;
3
4use bitflags::bitflags;
5use rustc_abi::VariantIdx;
6use rustc_data_structures::fx::FxHashSet;
7use rustc_errors::DiagMessage;
8use rustc_hir::def::CtorKind;
9use rustc_hir::intravisit::VisitorExt;
10use rustc_hir::{self as hir, AmbigArg};
11use rustc_middle::bug;
12use rustc_middle::ty::{
13 self, Adt, AdtDef, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
14 TypeVisitableExt,
15};
16use rustc_session::{declare_lint, declare_lint_pass};
17use rustc_span::def_id::LocalDefId;
18use rustc_span::{Span, sym};
19use tracing::debug;
20
21use super::repr_nullable_ptr;
22use crate::lints::{ImproperCTypes, UsesPowerAlignment};
23use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
24
25declare_lint! {
26 IMPROPER_CTYPES,
48 Warn,
49 "proper use of libc types in foreign modules"
50}
51
52declare_lint! {
53 IMPROPER_CTYPES_DEFINITIONS,
75 Warn,
76 "proper use of libc types in foreign item definitions"
77}
78
79declare_lint! {
80 USES_POWER_ALIGNMENT,
130 Warn,
131 "Structs do not follow the power alignment rule under repr(C)"
132}
133
134declare_lint_pass!(ImproperCTypesLint => [
135 IMPROPER_CTYPES,
136 IMPROPER_CTYPES_DEFINITIONS,
137 USES_POWER_ALIGNMENT
138]);
139
140pub(crate) fn check_non_exhaustive_variant(
149 non_exhaustive_variant_list: bool,
150 variant: &ty::VariantDef,
151) -> ControlFlow<DiagMessage, ()> {
152 if non_exhaustive_variant_list {
156 if variant_has_complex_ctor(variant) {
160 return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive);
161 }
162 }
163
164 if variant.field_list_has_applicable_non_exhaustive() {
165 return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant);
166 }
167
168 ControlFlow::Continue(())
169}
170
171fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool {
172 !matches!(variant.ctor_kind(), Some(CtorKind::Const))
174}
175
176fn check_arg_for_power_alignment<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
179 let tcx = cx.tcx;
180 assert!(tcx.sess.target.os == "aix");
181 if ty.is_floating_point() && ty.primitive_size(tcx).bytes() > 4 {
188 return true;
189 } else if let Adt(adt_def, _) = ty.kind()
190 && adt_def.is_struct()
191 && adt_def.repr().c()
192 && !adt_def.repr().packed()
193 && adt_def.repr().align.is_none()
194 {
195 let struct_variant = adt_def.variant(VariantIdx::ZERO);
196 for struct_field in &struct_variant.fields {
200 let field_ty = tcx.type_of(struct_field.did).instantiate_identity();
201 if check_arg_for_power_alignment(cx, field_ty) {
202 return true;
203 }
204 }
205 }
206 return false;
207}
208
209fn check_struct_for_power_alignment<'tcx>(
218 cx: &LateContext<'tcx>,
219 item: &'tcx hir::Item<'tcx>,
220 adt_def: AdtDef<'tcx>,
221) {
222 let tcx = cx.tcx;
223 if adt_def.repr().c()
226 && !adt_def.repr().packed()
227 && adt_def.repr().align.is_none()
228 && tcx.sess.target.os == "aix"
229 && !adt_def.all_fields().next().is_none()
230 {
231 let struct_variant_data = item.expect_struct().2;
232 for field_def in struct_variant_data.fields().iter().skip(1) {
233 let def_id = field_def.def_id;
237 let ty = tcx.type_of(def_id).instantiate_identity();
238 if check_arg_for_power_alignment(cx, ty) {
239 cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment);
240 }
241 }
242 }
243}
244
245#[derive(Clone, Copy)]
246enum CItemKind {
247 Declaration,
248 Definition,
249}
250
251enum FfiResult<'tcx> {
252 FfiSafe,
253 FfiPhantom(Ty<'tcx>),
254 FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> },
255}
256
257type PartialFfiResult<'tcx> = Option<FfiResult<'tcx>>;
261
262bitflags! {
263 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
264 struct VisitorState: u8 {
265 const STATIC = 0b000001;
267 const FUNC = 0b000010;
269 const FN_RETURN = 0b000100;
271 const DEFINED = 0b001000;
273 const THEORETICAL = 0b010000;
276 }
277}
278
279impl VisitorState {
280 const STATIC_TY: Self = Self::STATIC;
282 const ARGUMENT_TY_IN_DEFINITION: Self =
283 Self::from_bits(Self::FUNC.bits() | Self::DEFINED.bits()).unwrap();
284 const RETURN_TY_IN_DEFINITION: Self =
285 Self::from_bits(Self::FUNC.bits() | Self::FN_RETURN.bits() | Self::DEFINED.bits()).unwrap();
286 const ARGUMENT_TY_IN_DECLARATION: Self = Self::FUNC;
287 const RETURN_TY_IN_DECLARATION: Self =
288 Self::from_bits(Self::FUNC.bits() | Self::FN_RETURN.bits()).unwrap();
289 const ARGUMENT_TY_IN_FNPTR: Self =
290 Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits()).unwrap();
291 const RETURN_TY_IN_FNPTR: Self =
292 Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits() | Self::FN_RETURN.bits())
293 .unwrap();
294
295 fn argument_from_fnmode(fn_mode: CItemKind) -> Self {
297 match fn_mode {
298 CItemKind::Definition => VisitorState::ARGUMENT_TY_IN_DEFINITION,
299 CItemKind::Declaration => VisitorState::ARGUMENT_TY_IN_DECLARATION,
300 }
301 }
302
303 fn return_from_fnmode(fn_mode: CItemKind) -> Self {
305 match fn_mode {
306 CItemKind::Definition => VisitorState::RETURN_TY_IN_DEFINITION,
307 CItemKind::Declaration => VisitorState::RETURN_TY_IN_DECLARATION,
308 }
309 }
310
311 fn is_in_function(self) -> bool {
313 let ret = self.contains(Self::FUNC);
314 if ret {
315 debug_assert!(!self.contains(Self::STATIC));
316 }
317 ret
318 }
319 fn is_in_function_return(self) -> bool {
321 let ret = self.contains(Self::FN_RETURN);
322 if ret {
323 debug_assert!(self.is_in_function());
324 }
325 ret
326 }
327 fn is_in_defined_function(self) -> bool {
331 self.contains(Self::DEFINED) && self.is_in_function()
332 }
333
334 fn is_in_fnptr(self) -> bool {
338 self.contains(Self::THEORETICAL) && self.is_in_function()
339 }
340
341 fn can_expect_ty_params(self) -> bool {
343 self.contains(Self::THEORETICAL) || self.is_in_defined_function()
345 }
346}
347
348struct ImproperCTypesVisitor<'a, 'tcx> {
352 cx: &'a LateContext<'tcx>,
353 cache: FxHashSet<Ty<'tcx>>,
356 base_ty: Ty<'tcx>,
359 base_fn_mode: CItemKind,
360}
361
362impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
363 fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self {
364 Self { cx, base_ty, base_fn_mode, cache: FxHashSet::default() }
365 }
366
367 fn check_field_type_for_ffi(
369 &mut self,
370 state: VisitorState,
371 field: &ty::FieldDef,
372 args: GenericArgsRef<'tcx>,
373 ) -> FfiResult<'tcx> {
374 let field_ty = field.ty(self.cx.tcx, args);
375 let field_ty = self
376 .cx
377 .tcx
378 .try_normalize_erasing_regions(self.cx.typing_env(), field_ty)
379 .unwrap_or(field_ty);
380 self.visit_type(state, field_ty)
381 }
382
383 fn check_variant_for_ffi(
385 &mut self,
386 state: VisitorState,
387 ty: Ty<'tcx>,
388 def: ty::AdtDef<'tcx>,
389 variant: &ty::VariantDef,
390 args: GenericArgsRef<'tcx>,
391 ) -> FfiResult<'tcx> {
392 use FfiResult::*;
393 let transparent_with_all_zst_fields = if def.repr().transparent() {
394 if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) {
395 match self.check_field_type_for_ffi(state, field, args) {
397 FfiUnsafe { ty, .. } if ty.is_unit() => (),
398 r => return r,
399 }
400
401 false
402 } else {
403 true
406 }
407 } else {
408 false
409 };
410
411 let mut all_phantom = !variant.fields.is_empty();
413 for field in &variant.fields {
414 all_phantom &= match self.check_field_type_for_ffi(state, field, args) {
415 FfiSafe => false,
416 FfiUnsafe { ty, .. } if ty.is_unit() => false,
418 FfiPhantom(..) => true,
419 r @ FfiUnsafe { .. } => return r,
420 }
421 }
422
423 if all_phantom {
424 FfiPhantom(ty)
425 } else if transparent_with_all_zst_fields {
426 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
427 } else {
428 FfiSafe
429 }
430 }
431
432 fn visit_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
435 use FfiResult::*;
436
437 let tcx = self.cx.tcx;
438
439 if !self.cache.insert(ty) {
444 return FfiSafe;
445 }
446
447 match *ty.kind() {
448 ty::Adt(def, args) => {
449 if let Some(boxed) = ty.boxed_ty()
450 && (
451 state.is_in_defined_function()
453 || (state.is_in_fnptr()
454 && matches!(self.base_fn_mode, CItemKind::Definition))
455 )
456 {
457 if boxed.is_sized(tcx, self.cx.typing_env()) {
458 return FfiSafe;
459 } else {
460 return FfiUnsafe {
461 ty,
462 reason: fluent::lint_improper_ctypes_box,
463 help: None,
464 };
465 }
466 }
467 if def.is_phantom_data() {
468 return FfiPhantom(ty);
469 }
470 match def.adt_kind() {
471 AdtKind::Struct | AdtKind::Union => {
472 if let Some(sym::cstring_type | sym::cstr_type) =
473 tcx.get_diagnostic_name(def.did())
474 && !self.base_ty.is_mutable_ptr()
475 {
476 return FfiUnsafe {
477 ty,
478 reason: fluent::lint_improper_ctypes_cstr_reason,
479 help: Some(fluent::lint_improper_ctypes_cstr_help),
480 };
481 }
482
483 if !def.repr().c() && !def.repr().transparent() {
484 return FfiUnsafe {
485 ty,
486 reason: if def.is_struct() {
487 fluent::lint_improper_ctypes_struct_layout_reason
488 } else {
489 fluent::lint_improper_ctypes_union_layout_reason
490 },
491 help: if def.is_struct() {
492 Some(fluent::lint_improper_ctypes_struct_layout_help)
493 } else {
494 Some(fluent::lint_improper_ctypes_union_layout_help)
495 },
496 };
497 }
498
499 if def.non_enum_variant().field_list_has_applicable_non_exhaustive() {
500 return FfiUnsafe {
501 ty,
502 reason: if def.is_struct() {
503 fluent::lint_improper_ctypes_struct_non_exhaustive
504 } else {
505 fluent::lint_improper_ctypes_union_non_exhaustive
506 },
507 help: None,
508 };
509 }
510
511 if def.non_enum_variant().fields.is_empty() {
512 return FfiUnsafe {
513 ty,
514 reason: if def.is_struct() {
515 fluent::lint_improper_ctypes_struct_fieldless_reason
516 } else {
517 fluent::lint_improper_ctypes_union_fieldless_reason
518 },
519 help: if def.is_struct() {
520 Some(fluent::lint_improper_ctypes_struct_fieldless_help)
521 } else {
522 Some(fluent::lint_improper_ctypes_union_fieldless_help)
523 },
524 };
525 }
526
527 self.check_variant_for_ffi(state, ty, def, def.non_enum_variant(), args)
528 }
529 AdtKind::Enum => {
530 if def.variants().is_empty() {
531 return FfiSafe;
533 }
534 if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
537 {
538 if let Some(ty) =
540 repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty)
541 {
542 return self.visit_type(state, ty);
543 }
544
545 return FfiUnsafe {
546 ty,
547 reason: fluent::lint_improper_ctypes_enum_repr_reason,
548 help: Some(fluent::lint_improper_ctypes_enum_repr_help),
549 };
550 }
551
552 let non_exhaustive = def.variant_list_has_applicable_non_exhaustive();
553 let ret = def.variants().iter().try_for_each(|variant| {
555 check_non_exhaustive_variant(non_exhaustive, variant)
556 .map_break(|reason| FfiUnsafe { ty, reason, help: None })?;
557
558 match self.check_variant_for_ffi(state, ty, def, variant, args) {
559 FfiSafe => ControlFlow::Continue(()),
560 r => ControlFlow::Break(r),
561 }
562 });
563 if let ControlFlow::Break(result) = ret {
564 return result;
565 }
566
567 FfiSafe
568 }
569 }
570 }
571
572 ty::Char => FfiUnsafe {
573 ty,
574 reason: fluent::lint_improper_ctypes_char_reason,
575 help: Some(fluent::lint_improper_ctypes_char_help),
576 },
577
578 ty::Pat(base, ..) => self.visit_type(state, base),
581
582 ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
584
585 ty::Slice(_) => FfiUnsafe {
586 ty,
587 reason: fluent::lint_improper_ctypes_slice_reason,
588 help: Some(fluent::lint_improper_ctypes_slice_help),
589 },
590
591 ty::Dynamic(..) => {
592 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None }
593 }
594
595 ty::Str => FfiUnsafe {
596 ty,
597 reason: fluent::lint_improper_ctypes_str_reason,
598 help: Some(fluent::lint_improper_ctypes_str_help),
599 },
600
601 ty::Tuple(..) => FfiUnsafe {
602 ty,
603 reason: fluent::lint_improper_ctypes_tuple_reason,
604 help: Some(fluent::lint_improper_ctypes_tuple_help),
605 },
606
607 ty::RawPtr(ty, _) | ty::Ref(_, ty, _)
608 if {
609 (state.is_in_defined_function() || state.is_in_fnptr())
610 && ty.is_sized(self.cx.tcx, self.cx.typing_env())
611 } =>
612 {
613 FfiSafe
614 }
615
616 ty::RawPtr(ty, _)
617 if match ty.kind() {
618 ty::Tuple(tuple) => tuple.is_empty(),
619 _ => false,
620 } =>
621 {
622 FfiSafe
623 }
624
625 ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.visit_type(state, ty),
626
627 ty::Array(inner_ty, _) => self.visit_type(state, inner_ty),
628
629 ty::FnPtr(sig_tys, hdr) => {
630 let sig = sig_tys.with(hdr);
631 if sig.abi().is_rustic_abi() {
632 return FfiUnsafe {
633 ty,
634 reason: fluent::lint_improper_ctypes_fnptr_reason,
635 help: Some(fluent::lint_improper_ctypes_fnptr_help),
636 };
637 }
638
639 let sig = tcx.instantiate_bound_regions_with_erased(sig);
640 for arg in sig.inputs() {
641 match self.visit_type(VisitorState::ARGUMENT_TY_IN_FNPTR, *arg) {
642 FfiSafe => {}
643 r => return r,
644 }
645 }
646
647 let ret_ty = sig.output();
648 if ret_ty.is_unit() {
649 return FfiSafe;
650 }
651
652 self.visit_type(VisitorState::RETURN_TY_IN_FNPTR, ret_ty)
653 }
654
655 ty::Foreign(..) => FfiSafe,
656
657 ty::Alias(ty::Opaque, ..) => {
660 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None }
661 }
662
663 ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
666 if state.can_expect_ty_params() =>
667 {
668 FfiSafe
669 }
670
671 ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
672
673 ty::Param(..)
674 | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..)
675 | ty::Infer(..)
676 | ty::Bound(..)
677 | ty::Error(_)
678 | ty::Closure(..)
679 | ty::CoroutineClosure(..)
680 | ty::Coroutine(..)
681 | ty::CoroutineWitness(..)
682 | ty::Placeholder(..)
683 | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
684 }
685 }
686
687 fn visit_for_opaque_ty(&mut self, ty: Ty<'tcx>) -> PartialFfiResult<'tcx> {
688 struct ProhibitOpaqueTypes;
689 impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
690 type Result = ControlFlow<Ty<'tcx>>;
691
692 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
693 if !ty.has_opaque_types() {
694 return ControlFlow::Continue(());
695 }
696
697 if let ty::Alias(ty::Opaque, ..) = ty.kind() {
698 ControlFlow::Break(ty)
699 } else {
700 ty.super_visit_with(self)
701 }
702 }
703 }
704
705 if let Some(ty) = self
706 .cx
707 .tcx
708 .try_normalize_erasing_regions(self.cx.typing_env(), ty)
709 .unwrap_or(ty)
710 .visit_with(&mut ProhibitOpaqueTypes)
711 .break_value()
712 {
713 Some(FfiResult::FfiUnsafe {
714 ty,
715 reason: fluent::lint_improper_ctypes_opaque,
716 help: None,
717 })
718 } else {
719 None
720 }
721 }
722
723 fn check_for_array_ty(&mut self, ty: Ty<'tcx>) -> PartialFfiResult<'tcx> {
725 if let ty::Array(..) = ty.kind() {
726 Some(FfiResult::FfiUnsafe {
727 ty,
728 reason: fluent::lint_improper_ctypes_array_reason,
729 help: Some(fluent::lint_improper_ctypes_array_help),
730 })
731 } else {
732 None
733 }
734 }
735
736 fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
738 if let Some(res) = self.visit_for_opaque_ty(ty) {
739 return res;
740 }
741
742 let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty);
743
744 if state.is_in_function() {
748 if let Some(res) = self.check_for_array_ty(ty) {
749 return res;
750 }
751 }
752
753 if state.is_in_function_return() && ty.is_unit() {
757 return FfiResult::FfiSafe;
758 }
759
760 self.visit_type(state, ty)
761 }
762}
763
764impl<'tcx> ImproperCTypesLint {
765 fn check_type_for_external_abi_fnptr(
768 &mut self,
769 cx: &LateContext<'tcx>,
770 state: VisitorState,
771 hir_ty: &hir::Ty<'tcx>,
772 ty: Ty<'tcx>,
773 fn_mode: CItemKind,
774 ) {
775 struct FnPtrFinder<'tcx> {
776 spans: Vec<Span>,
777 tys: Vec<Ty<'tcx>>,
778 }
779
780 impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> {
781 fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) {
782 debug!(?ty);
783 if let hir::TyKind::FnPtr(hir::FnPtrTy { abi, .. }) = ty.kind
784 && !abi.is_rustic_abi()
785 {
786 self.spans.push(ty.span);
787 }
788
789 hir::intravisit::walk_ty(self, ty)
790 }
791 }
792
793 impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'tcx> {
794 type Result = ();
795
796 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
797 if let ty::FnPtr(_, hdr) = ty.kind()
798 && !hdr.abi.is_rustic_abi()
799 {
800 self.tys.push(ty);
801 }
802
803 ty.super_visit_with(self)
804 }
805 }
806
807 let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() };
808 ty.visit_with(&mut visitor);
809 visitor.visit_ty_unambig(hir_ty);
810
811 let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..));
812 for (fn_ptr_ty, span) in all_types {
813 let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode);
814 let ffi_res = visitor.check_type(state, fn_ptr_ty);
816
817 self.process_ffi_result(cx, span, ffi_res, fn_mode);
818 }
819 }
820
821 fn check_fn_for_external_abi_fnptr(
824 &mut self,
825 cx: &LateContext<'tcx>,
826 fn_mode: CItemKind,
827 def_id: LocalDefId,
828 decl: &'tcx hir::FnDecl<'_>,
829 ) {
830 let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
831 let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
832
833 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
834 let state = VisitorState::argument_from_fnmode(fn_mode);
835 self.check_type_for_external_abi_fnptr(cx, state, input_hir, *input_ty, fn_mode);
836 }
837
838 if let hir::FnRetTy::Return(ret_hir) = decl.output {
839 let state = VisitorState::return_from_fnmode(fn_mode);
840 self.check_type_for_external_abi_fnptr(cx, state, ret_hir, sig.output(), fn_mode);
841 }
842 }
843
844 fn check_reprc_adt(
846 &mut self,
847 cx: &LateContext<'tcx>,
848 item: &'tcx hir::Item<'tcx>,
849 adt_def: AdtDef<'tcx>,
850 ) {
851 debug_assert!(
852 adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none()
853 );
854
855 check_struct_for_power_alignment(cx, item, adt_def);
859 }
860
861 fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) {
862 let ty = cx.tcx.type_of(id).instantiate_identity();
863 let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration);
864 let ffi_res = visitor.check_type(VisitorState::STATIC_TY, ty);
865 self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration);
866 }
867
868 fn check_foreign_fn(
870 &mut self,
871 cx: &LateContext<'tcx>,
872 fn_mode: CItemKind,
873 def_id: LocalDefId,
874 decl: &'tcx hir::FnDecl<'_>,
875 ) {
876 let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
877 let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
878
879 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
880 let state = VisitorState::argument_from_fnmode(fn_mode);
881 let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode);
882 let ffi_res = visitor.check_type(state, *input_ty);
883 self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode);
884 }
885
886 if let hir::FnRetTy::Return(ret_hir) = decl.output {
887 let state = VisitorState::return_from_fnmode(fn_mode);
888 let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode);
889 let ffi_res = visitor.check_type(state, sig.output());
890 self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode);
891 }
892 }
893
894 fn process_ffi_result(
895 &self,
896 cx: &LateContext<'tcx>,
897 sp: Span,
898 res: FfiResult<'tcx>,
899 fn_mode: CItemKind,
900 ) {
901 match res {
902 FfiResult::FfiSafe => {}
903 FfiResult::FfiPhantom(ty) => {
904 self.emit_ffi_unsafe_type_lint(
905 cx,
906 ty,
907 sp,
908 fluent::lint_improper_ctypes_only_phantomdata,
909 None,
910 fn_mode,
911 );
912 }
913 FfiResult::FfiUnsafe { ty, reason, help } => {
914 self.emit_ffi_unsafe_type_lint(cx, ty, sp, reason, help, fn_mode);
915 }
916 }
917 }
918
919 fn emit_ffi_unsafe_type_lint(
920 &self,
921 cx: &LateContext<'tcx>,
922 ty: Ty<'tcx>,
923 sp: Span,
924 note: DiagMessage,
925 help: Option<DiagMessage>,
926 fn_mode: CItemKind,
927 ) {
928 let lint = match fn_mode {
929 CItemKind::Declaration => IMPROPER_CTYPES,
930 CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
931 };
932 let desc = match fn_mode {
933 CItemKind::Declaration => "block",
934 CItemKind::Definition => "fn",
935 };
936 let span_note = if let ty::Adt(def, _) = ty.kind()
937 && let Some(sp) = cx.tcx.hir_span_if_local(def.did())
938 {
939 Some(sp)
940 } else {
941 None
942 };
943 cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, help, note, span_note });
944 }
945}
946
947impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
955 fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
956 let abi = cx.tcx.hir_get_foreign_abi(it.hir_id());
957
958 match it.kind {
959 hir::ForeignItemKind::Fn(sig, _, _) => {
960 if !abi.is_rustic_abi() {
964 self.check_foreign_fn(cx, CItemKind::Declaration, it.owner_id.def_id, sig.decl);
965 } else {
966 self.check_fn_for_external_abi_fnptr(
967 cx,
968 CItemKind::Declaration,
969 it.owner_id.def_id,
970 sig.decl,
971 );
972 }
973 }
974 hir::ForeignItemKind::Static(ty, _, _) if !abi.is_rustic_abi() => {
975 self.check_foreign_static(cx, it.owner_id, ty.span);
976 }
977 hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
978 }
979 }
980
981 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
982 match item.kind {
983 hir::ItemKind::Static(_, _, ty, _)
984 | hir::ItemKind::Const(_, _, ty, _)
985 | hir::ItemKind::TyAlias(_, _, ty) => {
986 self.check_type_for_external_abi_fnptr(
987 cx,
988 VisitorState::STATIC_TY,
989 ty,
990 cx.tcx.type_of(item.owner_id).instantiate_identity(),
991 CItemKind::Definition,
992 );
993 }
994 hir::ItemKind::Fn { .. } => {}
996 hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {
997 let adt_def: AdtDef<'tcx> = cx.tcx.adt_def(item.owner_id.to_def_id());
999
1000 if adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none()
1001 {
1002 self.check_reprc_adt(cx, item, adt_def);
1003 }
1004 }
1005
1006 hir::ItemKind::Impl(..)
1008 | hir::ItemKind::TraitAlias(..)
1009 | hir::ItemKind::Trait(..)
1010 | hir::ItemKind::GlobalAsm { .. }
1011 | hir::ItemKind::ForeignMod { .. }
1012 | hir::ItemKind::Mod(..)
1013 | hir::ItemKind::Macro(..)
1014 | hir::ItemKind::Use(..)
1015 | hir::ItemKind::ExternCrate(..) => {}
1016 }
1017 }
1018
1019 fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
1020 self.check_type_for_external_abi_fnptr(
1021 cx,
1022 VisitorState::STATIC_TY,
1023 field.ty,
1024 cx.tcx.type_of(field.def_id).instantiate_identity(),
1025 CItemKind::Definition,
1026 );
1027 }
1028
1029 fn check_fn(
1030 &mut self,
1031 cx: &LateContext<'tcx>,
1032 kind: hir::intravisit::FnKind<'tcx>,
1033 decl: &'tcx hir::FnDecl<'_>,
1034 _: &'tcx hir::Body<'_>,
1035 _: Span,
1036 id: LocalDefId,
1037 ) {
1038 use hir::intravisit::FnKind;
1039
1040 let abi = match kind {
1041 FnKind::ItemFn(_, _, header, ..) => header.abi,
1042 FnKind::Method(_, sig, ..) => sig.header.abi,
1043 _ => return,
1044 };
1045
1046 if !abi.is_rustic_abi() {
1050 self.check_foreign_fn(cx, CItemKind::Definition, id, decl);
1051 } else {
1052 self.check_fn_for_external_abi_fnptr(cx, CItemKind::Definition, id, decl);
1053 }
1054 }
1055}