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(_) => {
673 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_unsafe_binder, help: None }
674 }
675
676 ty::Param(..)
677 | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..)
678 | ty::Infer(..)
679 | ty::Bound(..)
680 | ty::Error(_)
681 | ty::Closure(..)
682 | ty::CoroutineClosure(..)
683 | ty::Coroutine(..)
684 | ty::CoroutineWitness(..)
685 | ty::Placeholder(..)
686 | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
687 }
688 }
689
690 fn visit_for_opaque_ty(&mut self, ty: Ty<'tcx>) -> PartialFfiResult<'tcx> {
691 struct ProhibitOpaqueTypes;
692 impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
693 type Result = ControlFlow<Ty<'tcx>>;
694
695 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
696 if !ty.has_opaque_types() {
697 return ControlFlow::Continue(());
698 }
699
700 if let ty::Alias(ty::Opaque, ..) = ty.kind() {
701 ControlFlow::Break(ty)
702 } else {
703 ty.super_visit_with(self)
704 }
705 }
706 }
707
708 if let Some(ty) = self
709 .cx
710 .tcx
711 .try_normalize_erasing_regions(self.cx.typing_env(), ty)
712 .unwrap_or(ty)
713 .visit_with(&mut ProhibitOpaqueTypes)
714 .break_value()
715 {
716 Some(FfiResult::FfiUnsafe {
717 ty,
718 reason: fluent::lint_improper_ctypes_opaque,
719 help: None,
720 })
721 } else {
722 None
723 }
724 }
725
726 fn check_for_array_ty(&mut self, ty: Ty<'tcx>) -> PartialFfiResult<'tcx> {
728 if let ty::Array(..) = ty.kind() {
729 Some(FfiResult::FfiUnsafe {
730 ty,
731 reason: fluent::lint_improper_ctypes_array_reason,
732 help: Some(fluent::lint_improper_ctypes_array_help),
733 })
734 } else {
735 None
736 }
737 }
738
739 fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
741 if let Some(res) = self.visit_for_opaque_ty(ty) {
742 return res;
743 }
744
745 let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty);
746
747 if state.is_in_function() {
751 if let Some(res) = self.check_for_array_ty(ty) {
752 return res;
753 }
754 }
755
756 if state.is_in_function_return() && ty.is_unit() {
760 return FfiResult::FfiSafe;
761 }
762
763 self.visit_type(state, ty)
764 }
765}
766
767impl<'tcx> ImproperCTypesLint {
768 fn check_type_for_external_abi_fnptr(
771 &mut self,
772 cx: &LateContext<'tcx>,
773 state: VisitorState,
774 hir_ty: &hir::Ty<'tcx>,
775 ty: Ty<'tcx>,
776 fn_mode: CItemKind,
777 ) {
778 struct FnPtrFinder<'tcx> {
779 spans: Vec<Span>,
780 tys: Vec<Ty<'tcx>>,
781 }
782
783 impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> {
784 fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) {
785 debug!(?ty);
786 if let hir::TyKind::FnPtr(hir::FnPtrTy { abi, .. }) = ty.kind
787 && !abi.is_rustic_abi()
788 {
789 self.spans.push(ty.span);
790 }
791
792 hir::intravisit::walk_ty(self, ty)
793 }
794 }
795
796 impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'tcx> {
797 type Result = ();
798
799 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
800 if let ty::FnPtr(_, hdr) = ty.kind()
801 && !hdr.abi.is_rustic_abi()
802 {
803 self.tys.push(ty);
804 }
805
806 ty.super_visit_with(self)
807 }
808 }
809
810 let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() };
811 ty.visit_with(&mut visitor);
812 visitor.visit_ty_unambig(hir_ty);
813
814 let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..));
815 for (fn_ptr_ty, span) in all_types {
816 let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode);
817 let ffi_res = visitor.check_type(state, fn_ptr_ty);
819
820 self.process_ffi_result(cx, span, ffi_res, fn_mode);
821 }
822 }
823
824 fn check_fn_for_external_abi_fnptr(
827 &mut self,
828 cx: &LateContext<'tcx>,
829 fn_mode: CItemKind,
830 def_id: LocalDefId,
831 decl: &'tcx hir::FnDecl<'_>,
832 ) {
833 let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
834 let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
835
836 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
837 let state = VisitorState::argument_from_fnmode(fn_mode);
838 self.check_type_for_external_abi_fnptr(cx, state, input_hir, *input_ty, fn_mode);
839 }
840
841 if let hir::FnRetTy::Return(ret_hir) = decl.output {
842 let state = VisitorState::return_from_fnmode(fn_mode);
843 self.check_type_for_external_abi_fnptr(cx, state, ret_hir, sig.output(), fn_mode);
844 }
845 }
846
847 fn check_reprc_adt(
849 &mut self,
850 cx: &LateContext<'tcx>,
851 item: &'tcx hir::Item<'tcx>,
852 adt_def: AdtDef<'tcx>,
853 ) {
854 debug_assert!(
855 adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none()
856 );
857
858 check_struct_for_power_alignment(cx, item, adt_def);
862 }
863
864 fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) {
865 let ty = cx.tcx.type_of(id).instantiate_identity();
866 let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration);
867 let ffi_res = visitor.check_type(VisitorState::STATIC_TY, ty);
868 self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration);
869 }
870
871 fn check_foreign_fn(
873 &mut self,
874 cx: &LateContext<'tcx>,
875 fn_mode: CItemKind,
876 def_id: LocalDefId,
877 decl: &'tcx hir::FnDecl<'_>,
878 ) {
879 let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
880 let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
881
882 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
883 let state = VisitorState::argument_from_fnmode(fn_mode);
884 let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode);
885 let ffi_res = visitor.check_type(state, *input_ty);
886 self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode);
887 }
888
889 if let hir::FnRetTy::Return(ret_hir) = decl.output {
890 let state = VisitorState::return_from_fnmode(fn_mode);
891 let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode);
892 let ffi_res = visitor.check_type(state, sig.output());
893 self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode);
894 }
895 }
896
897 fn process_ffi_result(
898 &self,
899 cx: &LateContext<'tcx>,
900 sp: Span,
901 res: FfiResult<'tcx>,
902 fn_mode: CItemKind,
903 ) {
904 match res {
905 FfiResult::FfiSafe => {}
906 FfiResult::FfiPhantom(ty) => {
907 self.emit_ffi_unsafe_type_lint(
908 cx,
909 ty,
910 sp,
911 fluent::lint_improper_ctypes_only_phantomdata,
912 None,
913 fn_mode,
914 );
915 }
916 FfiResult::FfiUnsafe { ty, reason, help } => {
917 self.emit_ffi_unsafe_type_lint(cx, ty, sp, reason, help, fn_mode);
918 }
919 }
920 }
921
922 fn emit_ffi_unsafe_type_lint(
923 &self,
924 cx: &LateContext<'tcx>,
925 ty: Ty<'tcx>,
926 sp: Span,
927 note: DiagMessage,
928 help: Option<DiagMessage>,
929 fn_mode: CItemKind,
930 ) {
931 let lint = match fn_mode {
932 CItemKind::Declaration => IMPROPER_CTYPES,
933 CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
934 };
935 let desc = match fn_mode {
936 CItemKind::Declaration => "block",
937 CItemKind::Definition => "fn",
938 };
939 let span_note = if let ty::Adt(def, _) = ty.kind()
940 && let Some(sp) = cx.tcx.hir_span_if_local(def.did())
941 {
942 Some(sp)
943 } else {
944 None
945 };
946 cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, help, note, span_note });
947 }
948}
949
950impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
958 fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
959 let abi = cx.tcx.hir_get_foreign_abi(it.hir_id());
960
961 match it.kind {
962 hir::ForeignItemKind::Fn(sig, _, _) => {
963 if !abi.is_rustic_abi() {
967 self.check_foreign_fn(cx, CItemKind::Declaration, it.owner_id.def_id, sig.decl);
968 } else {
969 self.check_fn_for_external_abi_fnptr(
970 cx,
971 CItemKind::Declaration,
972 it.owner_id.def_id,
973 sig.decl,
974 );
975 }
976 }
977 hir::ForeignItemKind::Static(ty, _, _) if !abi.is_rustic_abi() => {
978 self.check_foreign_static(cx, it.owner_id, ty.span);
979 }
980 hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
981 }
982 }
983
984 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
985 match item.kind {
986 hir::ItemKind::Static(_, _, ty, _)
987 | hir::ItemKind::Const(_, _, ty, _)
988 | hir::ItemKind::TyAlias(_, _, ty) => {
989 self.check_type_for_external_abi_fnptr(
990 cx,
991 VisitorState::STATIC_TY,
992 ty,
993 cx.tcx.type_of(item.owner_id).instantiate_identity(),
994 CItemKind::Definition,
995 );
996 }
997 hir::ItemKind::Fn { .. } => {}
999 hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {
1000 let adt_def: AdtDef<'tcx> = cx.tcx.adt_def(item.owner_id.to_def_id());
1002
1003 if adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none()
1004 {
1005 self.check_reprc_adt(cx, item, adt_def);
1006 }
1007 }
1008
1009 hir::ItemKind::Impl(..)
1011 | hir::ItemKind::TraitAlias(..)
1012 | hir::ItemKind::Trait(..)
1013 | hir::ItemKind::GlobalAsm { .. }
1014 | hir::ItemKind::ForeignMod { .. }
1015 | hir::ItemKind::Mod(..)
1016 | hir::ItemKind::Macro(..)
1017 | hir::ItemKind::Use(..)
1018 | hir::ItemKind::ExternCrate(..) => {}
1019 }
1020 }
1021
1022 fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
1023 self.check_type_for_external_abi_fnptr(
1024 cx,
1025 VisitorState::STATIC_TY,
1026 field.ty,
1027 cx.tcx.type_of(field.def_id).instantiate_identity(),
1028 CItemKind::Definition,
1029 );
1030 }
1031
1032 fn check_fn(
1033 &mut self,
1034 cx: &LateContext<'tcx>,
1035 kind: hir::intravisit::FnKind<'tcx>,
1036 decl: &'tcx hir::FnDecl<'_>,
1037 _: &'tcx hir::Body<'_>,
1038 _: Span,
1039 id: LocalDefId,
1040 ) {
1041 use hir::intravisit::FnKind;
1042
1043 let abi = match kind {
1044 FnKind::ItemFn(_, _, header, ..) => header.abi,
1045 FnKind::Method(_, sig, ..) => sig.header.abi,
1046 _ => return,
1047 };
1048
1049 if !abi.is_rustic_abi() {
1053 self.check_foreign_fn(cx, CItemKind::Definition, id, decl);
1054 } else {
1055 self.check_fn_for_external_abi_fnptr(cx, CItemKind::Definition, id, decl);
1056 }
1057 }
1058}