about summary refs log tree commit diff
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2018-02-23 10:24:52 -0800
committerGitHub <noreply@github.com>2018-02-23 10:24:52 -0800
commit9f36a35e61c2434bb02fcca004d3ecd385c748a9 (patch)
tree3a18ee05ee358e2d5ffc40085e12a05dca22dcc9
parent5d30cbab8e17bbc94b1140de874892775d30c519 (diff)
parent051ea5cc9bac00c7f588b4eae72b8a382d8ceb68 (diff)
downloadrust-9f36a35e61c2434bb02fcca004d3ecd385c748a9.tar.gz
rust-9f36a35e61c2434bb02fcca004d3ecd385c748a9.zip
Rollup merge of #48221 - rkruppe:improve-ctypes-lint, r=estebank
Overhaul improper_ctypes output

This snowballed into a rather big set of improvements to the diagnostics of the improper_ctypes lint. See commits for details, including effects of each change on the `compile-fail/improper-ctypes.rs` test (now a UI test), which is pretty gnarly and hopefully not representative of real code, but covers a lot of different error cases.

Fixes #42050
-rw-r--r--src/librustc_lint/types.rs240
-rw-r--r--src/test/compile-fail/issue-14309.rs10
-rw-r--r--src/test/compile-fail/issue-16250.rs2
-rw-r--r--src/test/compile-fail/lint-ctypes-enum.rs18
-rw-r--r--src/test/compile-fail/union/union-repr-c.rs2
-rw-r--r--src/test/ui/lint-ctypes.rs (renamed from src/test/compile-fail/lint-ctypes.rs)40
-rw-r--r--src/test/ui/lint-ctypes.stderr170
7 files changed, 316 insertions, 166 deletions
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index f734f3182a9..ef9b3d38c63 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -10,7 +10,6 @@
 
 #![allow(non_snake_case)]
 
-use rustc::hir::def_id::DefId;
 use rustc::hir::map as hir_map;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, AdtKind, Ty, TyCtxt};
@@ -26,7 +25,6 @@ use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
 
 use syntax::ast;
 use syntax::abi::Abi;
-use syntax::attr;
 use syntax_pos::Span;
 use syntax::codemap;
 
@@ -353,13 +351,14 @@ struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
     cx: &'a LateContext<'a, 'tcx>,
 }
 
-enum FfiResult {
+enum FfiResult<'tcx> {
     FfiSafe,
-    FfiPhantom,
-    FfiUnsafe(&'static str),
-    FfiBadStruct(DefId, &'static str),
-    FfiBadUnion(DefId, &'static str),
-    FfiBadEnum(DefId, &'static str),
+    FfiPhantom(Ty<'tcx>),
+    FfiUnsafe {
+        ty: Ty<'tcx>,
+        reason: &'static str,
+        help: Option<&'static str>,
+    },
 }
 
 /// Check if this enum can be safely exported based on the
@@ -397,23 +396,12 @@ fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     false
 }
 
-fn is_ffi_safe(ty: attr::IntType) -> bool {
-    match ty {
-        attr::SignedInt(ast::IntTy::I8) | attr::UnsignedInt(ast::UintTy::U8) |
-        attr::SignedInt(ast::IntTy::I16) | attr::UnsignedInt(ast::UintTy::U16) |
-        attr::SignedInt(ast::IntTy::I32) | attr::UnsignedInt(ast::UintTy::U32) |
-        attr::SignedInt(ast::IntTy::I64) | attr::UnsignedInt(ast::UintTy::U64) |
-        attr::SignedInt(ast::IntTy::I128) | attr::UnsignedInt(ast::UintTy::U128) => true,
-        attr::SignedInt(ast::IntTy::Isize) | attr::UnsignedInt(ast::UintTy::Usize) => false
-    }
-}
-
 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
     /// Check if the given type is "ffi-safe" (has a stable, well-defined
     /// representation which can be exported to C code).
     fn check_type_for_ffi(&self,
                           cache: &mut FxHashSet<Ty<'tcx>>,
-                          ty: Ty<'tcx>) -> FfiResult {
+                          ty: Ty<'tcx>) -> FfiResult<'tcx> {
         use self::FfiResult::*;
 
         let cx = self.cx.tcx;
@@ -429,19 +417,25 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         match ty.sty {
             ty::TyAdt(def, substs) => {
                 if def.is_phantom_data() {
-                    return FfiPhantom;
+                    return FfiPhantom(ty);
                 }
                 match def.adt_kind() {
                     AdtKind::Struct => {
                         if !def.repr.c() && !def.repr.transparent() {
-                            return FfiUnsafe("found struct without foreign-function-safe \
-                                              representation annotation in foreign module, \
-                                              consider adding a #[repr(C)] attribute to the type");
+                            return FfiUnsafe {
+                                ty: ty,
+                                reason: "this struct has unspecified layout",
+                                help: Some("consider adding a #[repr(C)] or #[repr(transparent)] \
+                                            attribute to this struct"),
+                            };
                         }
 
                         if def.non_enum_variant().fields.is_empty() {
-                            return FfiUnsafe("found zero-size struct in foreign module, consider \
-                                              adding a member to this struct");
+                            return FfiUnsafe {
+                                ty: ty,
+                                reason: "this struct has no fields",
+                                help: Some("consider adding a member to this struct"),
+                            };
                         }
 
                         // We can't completely trust repr(C) and repr(transparent) markings;
@@ -467,28 +461,30 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                                 FfiSafe => {
                                     all_phantom = false;
                                 }
-                                FfiPhantom => {}
-                                FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
+                                FfiPhantom(..) => {}
+                                FfiUnsafe { .. } => {
                                     return r;
                                 }
-                                FfiUnsafe(s) => {
-                                    return FfiBadStruct(def.did, s);
-                                }
                             }
                         }
 
-                        if all_phantom { FfiPhantom } else { FfiSafe }
+                        if all_phantom { FfiPhantom(ty) } else { FfiSafe }
                     }
                     AdtKind::Union => {
                         if !def.repr.c() {
-                            return FfiUnsafe("found union without foreign-function-safe \
-                                              representation annotation in foreign module, \
-                                              consider adding a #[repr(C)] attribute to the type");
+                            return FfiUnsafe {
+                                ty: ty,
+                                reason: "this union has unspecified layout",
+                                help: Some("consider adding a #[repr(C)] attribute to this union"),
+                            };
                         }
 
                         if def.non_enum_variant().fields.is_empty() {
-                            return FfiUnsafe("found zero-size union in foreign module, consider \
-                                              adding a member to this union");
+                            return FfiUnsafe {
+                                ty: ty,
+                                reason: "this union has no fields",
+                                help: Some("consider adding a field to this union"),
+                            };
                         }
 
                         let mut all_phantom = true;
@@ -501,17 +497,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                                 FfiSafe => {
                                     all_phantom = false;
                                 }
-                                FfiPhantom => {}
-                                FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
+                                FfiPhantom(..) => {}
+                                FfiUnsafe { .. } => {
                                     return r;
                                 }
-                                FfiUnsafe(s) => {
-                                    return FfiBadUnion(def.did, s);
-                                }
                             }
                         }
 
-                        if all_phantom { FfiPhantom } else { FfiSafe }
+                        if all_phantom { FfiPhantom(ty) } else { FfiSafe }
                     }
                     AdtKind::Enum => {
                         if def.variants.is_empty() {
@@ -524,25 +517,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                         if !def.repr.c() && def.repr.int.is_none() {
                             // Special-case types like `Option<extern fn()>`.
                             if !is_repr_nullable_ptr(cx, def, substs) {
-                                return FfiUnsafe("found enum without foreign-function-safe \
-                                                  representation annotation in foreign \
-                                                  module, consider adding a #[repr(...)] \
-                                                  attribute to the type");
-                            }
-                        }
-
-                        if let Some(int_ty) = def.repr.int {
-                            if !is_ffi_safe(int_ty) {
-                                // FIXME: This shouldn't be reachable: we should check
-                                // this earlier.
-                                return FfiUnsafe("enum has unexpected #[repr(...)] attribute");
+                                return FfiUnsafe {
+                                    ty: ty,
+                                    reason: "enum has no representation hint",
+                                    help: Some("consider adding a #[repr(...)] attribute \
+                                                to this enum"),
+                                };
                             }
-
-                            // Enum with an explicitly sized discriminant; either
-                            // a C-style enum or a discriminated union.
-
-                            // The layout of enum variants is implicitly repr(C).
-                            // FIXME: Is that correct?
                         }
 
                         // Check the contained variants.
@@ -554,15 +535,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                                 let r = self.check_type_for_ffi(cache, arg);
                                 match r {
                                     FfiSafe => {}
-                                    FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
+                                    FfiUnsafe { .. } => {
                                         return r;
                                     }
-                                    FfiPhantom => {
-                                        return FfiBadEnum(def.did,
-                                                          "Found phantom data in enum variant");
-                                    }
-                                    FfiUnsafe(s) => {
-                                        return FfiBadEnum(def.did, s);
+                                    FfiPhantom(..) => {
+                                        return FfiUnsafe {
+                                            ty: ty,
+                                            reason: "this enum contains a PhantomData field",
+                                            help: None,
+                                        };
                                     }
                                 }
                             }
@@ -572,45 +553,44 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                 }
             }
 
-            ty::TyChar => {
-                FfiUnsafe("found Rust type `char` in foreign module, while \
-                           `u32` or `libc::wchar_t` should be used")
-            }
+            ty::TyChar => FfiUnsafe {
+                ty: ty,
+                reason: "the `char` type has no C equivalent",
+                help: Some("consider using `u32` or `libc::wchar_t` instead"),
+            },
 
-            ty::TyInt(ast::IntTy::I128) => {
-                FfiUnsafe("found Rust type `i128` in foreign module, but \
-                           128-bit integers don't currently have a known \
-                           stable ABI")
-            }
-
-            ty::TyUint(ast::UintTy::U128) => {
-                FfiUnsafe("found Rust type `u128` in foreign module, but \
-                           128-bit integers don't currently have a known \
-                           stable ABI")
-            }
+            ty::TyInt(ast::IntTy::I128) | ty::TyUint(ast::UintTy::U128) => FfiUnsafe {
+                ty: ty,
+                reason: "128-bit integers don't currently have a known stable ABI",
+                help: None,
+            },
 
             // Primitive types with a stable representation.
             ty::TyBool | ty::TyInt(..) | ty::TyUint(..) | ty::TyFloat(..) | ty::TyNever => FfiSafe,
 
-            ty::TySlice(_) => {
-                FfiUnsafe("found Rust slice type in foreign module, \
-                           consider using a raw pointer instead")
-            }
-
-            ty::TyDynamic(..) => {
-                FfiUnsafe("found Rust trait type in foreign module, \
-                           consider using a raw pointer instead")
-            }
-
-            ty::TyStr => {
-                FfiUnsafe("found Rust type `str` in foreign module; \
-                           consider using a `*const libc::c_char`")
-            }
-
-            ty::TyTuple(..) => {
-                FfiUnsafe("found Rust tuple type in foreign module; \
-                           consider using a struct instead")
-            }
+            ty::TySlice(_) => FfiUnsafe {
+                ty: ty,
+                reason: "slices have no C equivalent",
+                help: Some("consider using a raw pointer instead"),
+            },
+
+            ty::TyDynamic(..) => FfiUnsafe {
+                ty: ty,
+                reason: "trait objects have no C equivalent",
+                help: None,
+            },
+
+            ty::TyStr => FfiUnsafe {
+                ty: ty,
+                reason: "string slices have no C equivalent",
+                help: Some("consider using `*const u8` and a length instead"),
+            },
+
+            ty::TyTuple(..) => FfiUnsafe {
+                ty: ty,
+                reason: "tuples have unspecified layout",
+                help: Some("consider using a struct instead"),
+            },
 
             ty::TyRawPtr(ref m) |
             ty::TyRef(_, ref m) => self.check_type_for_ffi(cache, m.ty),
@@ -620,9 +600,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             ty::TyFnPtr(sig) => {
                 match sig.abi() {
                     Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => {
-                        return FfiUnsafe("found function pointer with Rust calling convention in \
-                                          foreign module; consider using an `extern` function \
-                                          pointer")
+                        return FfiUnsafe {
+                            ty: ty,
+                            reason: "this function pointer has Rust-specific calling convention",
+                            help: Some("consider using an `fn \"extern\"(...) -> ...` \
+                                        function pointer instead"),
+                        }
                     }
                     _ => {}
                 }
@@ -670,40 +653,25 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
         match self.check_type_for_ffi(&mut FxHashSet(), ty) {
             FfiResult::FfiSafe => {}
-            FfiResult::FfiPhantom => {
-                self.cx.span_lint(IMPROPER_CTYPES,
-                                  sp,
-                                  &format!("found zero-sized type composed only \
-                                            of phantom-data in a foreign-function."));
-            }
-            FfiResult::FfiUnsafe(s) => {
-                self.cx.span_lint(IMPROPER_CTYPES, sp, s);
-            }
-            FfiResult::FfiBadStruct(_, s) => {
-                // FIXME: This diagnostic is difficult to read, and doesn't
-                // point at the relevant field.
+            FfiResult::FfiPhantom(ty) => {
                 self.cx.span_lint(IMPROPER_CTYPES,
                                   sp,
-                                  &format!("found non-foreign-function-safe member in struct \
-                                            marked #[repr(C)]: {}",
-                                           s));
+                                  &format!("`extern` block uses type `{}` which is not FFI-safe: \
+                                            composed only of PhantomData", ty));
             }
-            FfiResult::FfiBadUnion(_, s) => {
-                // FIXME: This diagnostic is difficult to read, and doesn't
-                // point at the relevant field.
-                self.cx.span_lint(IMPROPER_CTYPES,
-                                  sp,
-                                  &format!("found non-foreign-function-safe member in union \
-                                            marked #[repr(C)]: {}",
-                                           s));
-            }
-            FfiResult::FfiBadEnum(_, s) => {
-                // FIXME: This diagnostic is difficult to read, and doesn't
-                // point at the relevant variant.
-                self.cx.span_lint(IMPROPER_CTYPES,
-                                  sp,
-                                  &format!("found non-foreign-function-safe member in enum: {}",
-                                           s));
+            FfiResult::FfiUnsafe { ty: unsafe_ty, reason, help } => {
+                let msg = format!("`extern` block uses type `{}` which is not FFI-safe: {}",
+                                  unsafe_ty, reason);
+                let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg);
+                if let Some(s) = help {
+                    diag.help(s);
+                }
+                if let ty::TyAdt(def, _) = unsafe_ty.sty {
+                    if let Some(sp) = self.cx.tcx.hir.span_if_local(def.did) {
+                        diag.span_note(sp, "type defined here");
+                    }
+                }
+                diag.emit();
             }
         }
     }
diff --git a/src/test/compile-fail/issue-14309.rs b/src/test/compile-fail/issue-14309.rs
index 56261c34a03..f76fa3e4a8e 100644
--- a/src/test/compile-fail/issue-14309.rs
+++ b/src/test/compile-fail/issue-14309.rs
@@ -37,13 +37,13 @@ struct D {
 }
 
 extern "C" {
-    fn foo(x: A); //~ ERROR found struct without foreign-function-safe
-    fn bar(x: B); //~ ERROR foreign-function-safe
+    fn foo(x: A); //~ ERROR type `A` which is not FFI-safe
+    fn bar(x: B); //~ ERROR type `A`
     fn baz(x: C);
-    fn qux(x: A2); //~ ERROR foreign-function-safe
-    fn quux(x: B2); //~ ERROR foreign-function-safe
+    fn qux(x: A2); //~ ERROR type `A`
+    fn quux(x: B2); //~ ERROR type `A`
     fn corge(x: C2);
-    fn fred(x: D); //~ ERROR foreign-function-safe
+    fn fred(x: D); //~ ERROR type `A`
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/issue-16250.rs b/src/test/compile-fail/issue-16250.rs
index 288fe4a9abb..f9d01003005 100644
--- a/src/test/compile-fail/issue-16250.rs
+++ b/src/test/compile-fail/issue-16250.rs
@@ -13,7 +13,7 @@
 pub struct Foo;
 
 extern {
-    pub fn foo(x: (Foo)); //~ ERROR found struct without
+    pub fn foo(x: (Foo)); //~ ERROR unspecified layout
 }
 
 fn main() {
diff --git a/src/test/compile-fail/lint-ctypes-enum.rs b/src/test/compile-fail/lint-ctypes-enum.rs
index e35dadbea9d..7b7ffd8fc10 100644
--- a/src/test/compile-fail/lint-ctypes-enum.rs
+++ b/src/test/compile-fail/lint-ctypes-enum.rs
@@ -16,11 +16,23 @@ enum U { A }
 enum B { C, D }
 enum T { E, F, G }
 
+#[repr(C)]
+enum ReprC { A, B, C }
+
+#[repr(u8)]
+enum U8 { A, B, C }
+
+#[repr(isize)]
+enum Isize { A, B, C }
+
 extern {
    fn zf(x: Z);
-   fn uf(x: U); //~ ERROR found enum without foreign-function-safe
-   fn bf(x: B); //~ ERROR found enum without foreign-function-safe
-   fn tf(x: T); //~ ERROR found enum without foreign-function-safe
+   fn uf(x: U); //~ ERROR enum has no representation hint
+   fn bf(x: B); //~ ERROR enum has no representation hint
+   fn tf(x: T); //~ ERROR enum has no representation hint
+   fn reprc(x: ReprC);
+   fn u8(x: U8);
+   fn isize(x: Isize);
 }
 
 pub fn main() { }
diff --git a/src/test/compile-fail/union/union-repr-c.rs b/src/test/compile-fail/union/union-repr-c.rs
index 15a4197fe94..36c42ce1104 100644
--- a/src/test/compile-fail/union/union-repr-c.rs
+++ b/src/test/compile-fail/union/union-repr-c.rs
@@ -22,7 +22,7 @@ union W {
 
 extern "C" {
     static FOREIGN1: U; // OK
-    static FOREIGN2: W; //~ ERROR found union without foreign-function-safe representation
+    static FOREIGN2: W; //~ ERROR union has unspecified layout
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/lint-ctypes.rs b/src/test/ui/lint-ctypes.rs
index c22239dee0a..77cb1ef0f51 100644
--- a/src/test/compile-fail/lint-ctypes.rs
+++ b/src/test/ui/lint-ctypes.rs
@@ -51,27 +51,27 @@ pub struct TransparentCustomZst(i32, ZeroSize);
 pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>);
 
 extern {
-    pub fn ptr_type1(size: *const Foo); //~ ERROR: found struct without
-    pub fn ptr_type2(size: *const Foo); //~ ERROR: found struct without
-    pub fn slice_type(p: &[u32]); //~ ERROR: found Rust slice type
-    pub fn str_type(p: &str); //~ ERROR: found Rust type
-    pub fn box_type(p: Box<u32>); //~ ERROR found struct without
-    pub fn char_type(p: char); //~ ERROR found Rust type
-    pub fn i128_type(p: i128); //~ ERROR found Rust type
-    pub fn u128_type(p: u128); //~ ERROR found Rust type
-    pub fn trait_type(p: &Clone); //~ ERROR found Rust trait type
-    pub fn tuple_type(p: (i32, i32)); //~ ERROR found Rust tuple type
-    pub fn tuple_type2(p: I32Pair); //~ ERROR found Rust tuple type
-    pub fn zero_size(p: ZeroSize); //~ ERROR found zero-size struct
-    pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR found zero-sized type
+    pub fn ptr_type1(size: *const Foo); //~ ERROR: uses type `Foo`
+    pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo`
+    pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]`
+    pub fn str_type(p: &str); //~ ERROR: uses type `str`
+    pub fn box_type(p: Box<u32>); //~ ERROR uses type `std::boxed::Box<u32>`
+    pub fn char_type(p: char); //~ ERROR uses type `char`
+    pub fn i128_type(p: i128); //~ ERROR uses type `i128`
+    pub fn u128_type(p: u128); //~ ERROR uses type `u128`
+    pub fn trait_type(p: &Clone); //~ ERROR uses type `std::clone::Clone`
+    pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)`
+    pub fn tuple_type2(p: I32Pair); //~ ERROR uses type `(i32, i32)`
+    pub fn zero_size(p: ZeroSize); //~ ERROR struct has no fields
+    pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR composed only of PhantomData
     pub fn zero_size_phantom_toplevel()
-        -> ::std::marker::PhantomData<bool>; //~ ERROR: found zero-sized type
-    pub fn fn_type(p: RustFn); //~ ERROR found function pointer with Rust
-    pub fn fn_type2(p: fn()); //~ ERROR found function pointer with Rust
-    pub fn fn_contained(p: RustBadRet); //~ ERROR: found struct without
-    pub fn transparent_i128(p: TransparentI128); //~ ERROR: found Rust type `i128`
-    pub fn transparent_str(p: TransparentStr); //~ ERROR: found Rust type `str`
-    pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: found struct without
+        -> ::std::marker::PhantomData<bool>; //~ ERROR: composed only of PhantomData
+    pub fn fn_type(p: RustFn); //~ ERROR function pointer has Rust-specific
+    pub fn fn_type2(p: fn()); //~ ERROR function pointer has Rust-specific
+    pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `std::boxed::Box<u32>`
+    pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128`
+    pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str`
+    pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `std::boxed::Box<u32>`
 
     pub fn good3(fptr: Option<extern fn()>);
     pub fn good4(aptr: &[u8; 4 as usize]);
diff --git a/src/test/ui/lint-ctypes.stderr b/src/test/ui/lint-ctypes.stderr
new file mode 100644
index 00000000000..748c311055f
--- /dev/null
+++ b/src/test/ui/lint-ctypes.stderr
@@ -0,0 +1,170 @@
+error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout
+  --> $DIR/lint-ctypes.rs:54:28
+   |
+54 |     pub fn ptr_type1(size: *const Foo); //~ ERROR: uses type `Foo`
+   |                            ^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/lint-ctypes.rs:11:9
+   |
+11 | #![deny(improper_ctypes)]
+   |         ^^^^^^^^^^^^^^^
+   = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
+note: type defined here
+  --> $DIR/lint-ctypes.rs:32:1
+   |
+32 | pub struct Foo;
+   | ^^^^^^^^^^^^^^^
+
+error: `extern` block uses type `Foo` which is not FFI-safe: this struct has unspecified layout
+  --> $DIR/lint-ctypes.rs:55:28
+   |
+55 |     pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo`
+   |                            ^^^^^^^^^^
+   |
+   = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
+note: type defined here
+  --> $DIR/lint-ctypes.rs:32:1
+   |
+32 | pub struct Foo;
+   | ^^^^^^^^^^^^^^^
+
+error: `extern` block uses type `[u32]` which is not FFI-safe: slices have no C equivalent
+  --> $DIR/lint-ctypes.rs:56:26
+   |
+56 |     pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]`
+   |                          ^^^^^^
+   |
+   = help: consider using a raw pointer instead
+
+error: `extern` block uses type `str` which is not FFI-safe: string slices have no C equivalent
+  --> $DIR/lint-ctypes.rs:57:24
+   |
+57 |     pub fn str_type(p: &str); //~ ERROR: uses type `str`
+   |                        ^^^^
+   |
+   = help: consider using `*const u8` and a length instead
+
+error: `extern` block uses type `std::boxed::Box<u32>` which is not FFI-safe: this struct has unspecified layout
+  --> $DIR/lint-ctypes.rs:58:24
+   |
+58 |     pub fn box_type(p: Box<u32>); //~ ERROR uses type `std::boxed::Box<u32>`
+   |                        ^^^^^^^^
+   |
+   = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
+
+error: `extern` block uses type `char` which is not FFI-safe: the `char` type has no C equivalent
+  --> $DIR/lint-ctypes.rs:59:25
+   |
+59 |     pub fn char_type(p: char); //~ ERROR uses type `char`
+   |                         ^^^^
+   |
+   = help: consider using `u32` or `libc::wchar_t` instead
+
+error: `extern` block uses type `i128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
+  --> $DIR/lint-ctypes.rs:60:25
+   |
+60 |     pub fn i128_type(p: i128); //~ ERROR uses type `i128`
+   |                         ^^^^
+
+error: `extern` block uses type `u128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
+  --> $DIR/lint-ctypes.rs:61:25
+   |
+61 |     pub fn u128_type(p: u128); //~ ERROR uses type `u128`
+   |                         ^^^^
+
+error: `extern` block uses type `std::clone::Clone` which is not FFI-safe: trait objects have no C equivalent
+  --> $DIR/lint-ctypes.rs:62:26
+   |
+62 |     pub fn trait_type(p: &Clone); //~ ERROR uses type `std::clone::Clone`
+   |                          ^^^^^^
+
+error: `extern` block uses type `(i32, i32)` which is not FFI-safe: tuples have unspecified layout
+  --> $DIR/lint-ctypes.rs:63:26
+   |
+63 |     pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)`
+   |                          ^^^^^^^^^^
+   |
+   = help: consider using a struct instead
+
+error: `extern` block uses type `(i32, i32)` which is not FFI-safe: tuples have unspecified layout
+  --> $DIR/lint-ctypes.rs:64:27
+   |
+64 |     pub fn tuple_type2(p: I32Pair); //~ ERROR uses type `(i32, i32)`
+   |                           ^^^^^^^
+   |
+   = help: consider using a struct instead
+
+error: `extern` block uses type `ZeroSize` which is not FFI-safe: this struct has no fields
+  --> $DIR/lint-ctypes.rs:65:25
+   |
+65 |     pub fn zero_size(p: ZeroSize); //~ ERROR struct has no fields
+   |                         ^^^^^^^^
+   |
+   = help: consider adding a member to this struct
+note: type defined here
+  --> $DIR/lint-ctypes.rs:28:1
+   |
+28 | pub struct ZeroSize;
+   | ^^^^^^^^^^^^^^^^^^^^
+
+error: `extern` block uses type `ZeroSizeWithPhantomData` which is not FFI-safe: composed only of PhantomData
+  --> $DIR/lint-ctypes.rs:66:33
+   |
+66 |     pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); //~ ERROR composed only of PhantomData
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `extern` block uses type `std::marker::PhantomData<bool>` which is not FFI-safe: composed only of PhantomData
+  --> $DIR/lint-ctypes.rs:68:12
+   |
+68 |         -> ::std::marker::PhantomData<bool>; //~ ERROR: composed only of PhantomData
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `extern` block uses type `fn()` which is not FFI-safe: this function pointer has Rust-specific calling convention
+  --> $DIR/lint-ctypes.rs:69:23
+   |
+69 |     pub fn fn_type(p: RustFn); //~ ERROR function pointer has Rust-specific
+   |                       ^^^^^^
+   |
+   = help: consider using an `fn "extern"(...) -> ...` function pointer instead
+
+error: `extern` block uses type `fn()` which is not FFI-safe: this function pointer has Rust-specific calling convention
+  --> $DIR/lint-ctypes.rs:70:24
+   |
+70 |     pub fn fn_type2(p: fn()); //~ ERROR function pointer has Rust-specific
+   |                        ^^^^
+   |
+   = help: consider using an `fn "extern"(...) -> ...` function pointer instead
+
+error: `extern` block uses type `std::boxed::Box<u32>` which is not FFI-safe: this struct has unspecified layout
+  --> $DIR/lint-ctypes.rs:71:28
+   |
+71 |     pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `std::boxed::Box<u32>`
+   |                            ^^^^^^^^^^
+   |
+   = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
+
+error: `extern` block uses type `i128` which is not FFI-safe: 128-bit integers don't currently have a known stable ABI
+  --> $DIR/lint-ctypes.rs:72:32
+   |
+72 |     pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128`
+   |                                ^^^^^^^^^^^^^^^
+
+error: `extern` block uses type `str` which is not FFI-safe: string slices have no C equivalent
+  --> $DIR/lint-ctypes.rs:73:31
+   |
+73 |     pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str`
+   |                               ^^^^^^^^^^^^^^
+   |
+   = help: consider using `*const u8` and a length instead
+
+error: `extern` block uses type `std::boxed::Box<u32>` which is not FFI-safe: this struct has unspecified layout
+  --> $DIR/lint-ctypes.rs:74:30
+   |
+74 |     pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `std::boxed::Box<u32>`
+   |                              ^^^^^^^^^^^^^^^^
+   |
+   = help: consider adding a #[repr(C)] or #[repr(transparent)] attribute to this struct
+
+error: aborting due to 20 previous errors
+