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