Skip to content

Commit 9b5f47e

Browse files
author
Robin Kruppe
committed
[improper_ctypes] Overhaul primary label
- Always name the non-FFI-safe - Explain *why* the type is not FFI-safe - Stop vaguely gesturing at structs/enums/unions if the non-FFI-safe types occured in a field. The last part is arguably a regression, but it's minor now that the non-FFI-safe type is actually named. Removing it avoids some code duplication.
1 parent ae92dfa commit 9b5f47e

File tree

7 files changed

+171
-227
lines changed

7 files changed

+171
-227
lines changed

src/librustc_lint/types.rs

Lines changed: 91 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
#![allow(non_snake_case)]
1212

13-
use rustc::hir::def_id::DefId;
1413
use rustc::hir::map as hir_map;
1514
use rustc::ty::subst::Substs;
1615
use rustc::ty::{self, AdtKind, Ty, TyCtxt};
@@ -352,18 +351,14 @@ struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
352351
cx: &'a LateContext<'a, 'tcx>,
353352
}
354353

355-
struct FfiError {
356-
message: &'static str,
357-
help: Option<&'static str>,
358-
}
359-
360-
enum FfiResult {
354+
enum FfiResult<'tcx> {
361355
FfiSafe,
362-
FfiPhantom,
363-
FfiUnsafe(FfiError),
364-
FfiBadStruct(DefId, FfiError),
365-
FfiBadUnion(DefId, FfiError),
366-
FfiBadEnum(DefId, FfiError),
356+
FfiPhantom(Ty<'tcx>),
357+
FfiUnsafe {
358+
ty: Ty<'tcx>,
359+
reason: &'static str,
360+
help: Option<&'static str>,
361+
},
367362
}
368363

369364
/// Check if this enum can be safely exported based on the
@@ -406,7 +401,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
406401
/// representation which can be exported to C code).
407402
fn check_type_for_ffi(&self,
408403
cache: &mut FxHashSet<Ty<'tcx>>,
409-
ty: Ty<'tcx>) -> FfiResult {
404+
ty: Ty<'tcx>) -> FfiResult<'tcx> {
410405
use self::FfiResult::*;
411406

412407
let cx = self.cx.tcx;
@@ -422,23 +417,24 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
422417
match ty.sty {
423418
ty::TyAdt(def, substs) => {
424419
if def.is_phantom_data() {
425-
return FfiPhantom;
420+
return FfiPhantom(ty);
426421
}
427422
match def.adt_kind() {
428423
AdtKind::Struct => {
429424
if !def.repr.c() && !def.repr.transparent() {
430-
return FfiUnsafe(FfiError {
431-
message: "found struct without foreign-function-safe \
432-
representation annotation in foreign module",
433-
help: Some("consider adding a #[repr(C)] attribute to the type"),
434-
});
425+
return FfiUnsafe {
426+
ty: ty,
427+
reason: "this struct has unspecified layout",
428+
help: Some("consider adding a #[repr(C)] attribute to this struct"),
429+
};
435430
}
436431

437432
if def.non_enum_variant().fields.is_empty() {
438-
return FfiUnsafe(FfiError {
439-
message: "found zero-size struct in foreign module",
433+
return FfiUnsafe {
434+
ty: ty,
435+
reason: "this struct has no fields",
440436
help: Some("consider adding a member to this struct"),
441-
});
437+
};
442438
}
443439

444440
// We can't completely trust repr(C) and repr(transparent) markings;
@@ -464,32 +460,30 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
464460
FfiSafe => {
465461
all_phantom = false;
466462
}
467-
FfiPhantom => {}
468-
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
463+
FfiPhantom(..) => {}
464+
FfiUnsafe { .. } => {
469465
return r;
470466
}
471-
FfiUnsafe(err) => {
472-
return FfiBadStruct(def.did, err);
473-
}
474467
}
475468
}
476469

477-
if all_phantom { FfiPhantom } else { FfiSafe }
470+
if all_phantom { FfiPhantom(ty) } else { FfiSafe }
478471
}
479472
AdtKind::Union => {
480473
if !def.repr.c() {
481-
return FfiUnsafe(FfiError {
482-
message: "found union without foreign-function-safe \
483-
representation annotation in foreign module",
484-
help: Some("consider adding a #[repr(C)] attribute to the type"),
485-
});
474+
return FfiUnsafe {
475+
ty: ty,
476+
reason: "this union has unspecified layout",
477+
help: Some("consider adding a #[repr(C)] attribute to this union"),
478+
};
486479
}
487480

488481
if def.non_enum_variant().fields.is_empty() {
489-
return FfiUnsafe(FfiError {
490-
message: "found zero-size union in foreign module",
491-
help: Some("consider adding a member to this union"),
492-
});
482+
return FfiUnsafe {
483+
ty: ty,
484+
reason: "this union has no fields",
485+
help: Some("consider adding a field to this union"),
486+
};
493487
}
494488

495489
let mut all_phantom = true;
@@ -502,17 +496,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
502496
FfiSafe => {
503497
all_phantom = false;
504498
}
505-
FfiPhantom => {}
506-
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
499+
FfiPhantom(..) => {}
500+
FfiUnsafe { .. } => {
507501
return r;
508502
}
509-
FfiUnsafe(err) => {
510-
return FfiBadUnion(def.did, err);
511-
}
512503
}
513504
}
514505

515-
if all_phantom { FfiPhantom } else { FfiSafe }
506+
if all_phantom { FfiPhantom(ty) } else { FfiSafe }
516507
}
517508
AdtKind::Enum => {
518509
if def.variants.is_empty() {
@@ -525,12 +516,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
525516
if !def.repr.c() && def.repr.int.is_none() {
526517
// Special-case types like `Option<extern fn()>`.
527518
if !is_repr_nullable_ptr(cx, def, substs) {
528-
return FfiUnsafe(FfiError {
529-
message: "found enum without foreign-function-safe \
530-
representation annotation in foreign module",
519+
return FfiUnsafe {
520+
ty: ty,
521+
reason: "enum has no representation hint",
531522
help: Some("consider adding a #[repr(...)] attribute \
532-
to the type"),
533-
});
523+
to this enum"),
524+
};
534525
}
535526
}
536527

@@ -543,17 +534,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
543534
let r = self.check_type_for_ffi(cache, arg);
544535
match r {
545536
FfiSafe => {}
546-
FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
537+
FfiUnsafe { .. } => {
547538
return r;
548539
}
549-
FfiPhantom => {
550-
return FfiBadEnum(def.did, FfiError {
551-
message: "Found phantom data in enum variant",
540+
FfiPhantom(..) => {
541+
return FfiUnsafe {
542+
ty: ty,
543+
reason: "this enum contains a PhantomData field",
552544
help: None,
553-
});
554-
}
555-
FfiUnsafe(err) => {
556-
return FfiBadEnum(def.did, err);
545+
};
557546
}
558547
}
559548
}
@@ -563,59 +552,44 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
563552
}
564553
}
565554

566-
ty::TyChar => {
567-
FfiUnsafe(FfiError {
568-
message: "found Rust type `char` in foreign module",
569-
help: Some("consider using `u32` or `libc::wchar_t`"),
570-
})
571-
}
555+
ty::TyChar => FfiUnsafe {
556+
ty: ty,
557+
reason: "the `char` type has no C equivalent",
558+
help: Some("consider using `u32` or `libc::wchar_t` instead"),
559+
},
572560

573-
ty::TyInt(ast::IntTy::I128) => {
574-
FfiUnsafe(FfiError {
575-
message: "found Rust type `i128` in foreign module, but 128-bit \
576-
integers don't currently have a known stable ABI",
577-
help: None,
578-
})
579-
}
580-
581-
ty::TyUint(ast::UintTy::U128) => {
582-
FfiUnsafe(FfiError {
583-
message: "found Rust type `u128` in foreign module, but 128-bit \
584-
integers don't currently have a known stable ABI",
585-
help: None,
586-
})
587-
}
561+
ty::TyInt(ast::IntTy::I128) | ty::TyUint(ast::UintTy::U128) => FfiUnsafe {
562+
ty: ty,
563+
reason: "128-bit integers don't currently have a known stable ABI",
564+
help: None,
565+
},
588566

589567
// Primitive types with a stable representation.
590568
ty::TyBool | ty::TyInt(..) | ty::TyUint(..) | ty::TyFloat(..) | ty::TyNever => FfiSafe,
591569

592-
ty::TySlice(_) => {
593-
FfiUnsafe(FfiError {
594-
message: "found Rust slice type in foreign module",
595-
help: Some("consider using a raw pointer instead"),
596-
})
597-
}
598-
599-
ty::TyDynamic(..) => {
600-
FfiUnsafe(FfiError {
601-
message: "found Rust trait type in foreign module",
602-
help: Some("consider using a raw pointer instead"),
603-
})
604-
}
605-
606-
ty::TyStr => {
607-
FfiUnsafe(FfiError {
608-
message: "found Rust type `str` in foreign module",
609-
help: Some("consider using a `*const libc::c_char`"),
610-
})
611-
}
612-
613-
ty::TyTuple(..) => {
614-
FfiUnsafe(FfiError {
615-
message: "found Rust tuple type in foreign module",
616-
help: Some("consider using a struct instead"),
617-
})
618-
}
570+
ty::TySlice(_) => FfiUnsafe {
571+
ty: ty,
572+
reason: "slices have no C equivalent",
573+
help: Some("consider using a raw pointer instead"),
574+
},
575+
576+
ty::TyDynamic(..) => FfiUnsafe {
577+
ty: ty,
578+
reason: "trait objects have no C equivalent",
579+
help: Some("consider using a raw pointer instead"),
580+
},
581+
582+
ty::TyStr => FfiUnsafe {
583+
ty: ty,
584+
reason: "string slices have no C equivalent",
585+
help: Some("consider using `*const u8` and a length instead"),
586+
},
587+
588+
ty::TyTuple(..) => FfiUnsafe {
589+
ty: ty,
590+
reason: "tuples have unspecified layout",
591+
help: Some("consider using a struct instead"),
592+
},
619593

620594
ty::TyRawPtr(ref m) |
621595
ty::TyRef(_, ref m) => self.check_type_for_ffi(cache, m.ty),
@@ -625,11 +599,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
625599
ty::TyFnPtr(sig) => {
626600
match sig.abi() {
627601
Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => {
628-
return FfiUnsafe(FfiError {
629-
message: "found function pointer with Rust calling convention in \
630-
foreign module",
631-
help: Some("consider using an `extern` function pointer"),
632-
})
602+
return FfiUnsafe {
603+
ty: ty,
604+
reason: "this function pointer has Rust-specific calling convention",
605+
help: Some("consider using an `fn \"extern\"(...) -> ...` \
606+
function pointer instead"),
607+
}
633608
}
634609
_ => {}
635610
}
@@ -677,48 +652,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
677652

678653
match self.check_type_for_ffi(&mut FxHashSet(), ty) {
679654
FfiResult::FfiSafe => {}
680-
FfiResult::FfiPhantom => {
655+
FfiResult::FfiPhantom(ty) => {
681656
self.cx.span_lint(IMPROPER_CTYPES,
682657
sp,
683-
&format!("found zero-sized type composed only \
684-
of phantom-data in a foreign-function."));
685-
}
686-
FfiResult::FfiUnsafe(err) => {
687-
let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, err.message);
688-
if let Some(s) = err.help {
689-
diag.help(s);
690-
}
691-
diag.emit();
692-
}
693-
FfiResult::FfiBadStruct(_, err) => {
694-
// FIXME: This diagnostic is difficult to read, and doesn't
695-
// point at the relevant field.
696-
let msg = format!("found non-foreign-function-safe member in struct \
697-
marked #[repr(C)]: {}", err.message);
698-
let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg);
699-
if let Some(s) = err.help {
700-
diag.help(s);
701-
}
702-
diag.emit();
703-
}
704-
FfiResult::FfiBadUnion(_, err) => {
705-
// FIXME: This diagnostic is difficult to read, and doesn't
706-
// point at the relevant field.
707-
let msg = format!("found non-foreign-function-safe member in union \
708-
marked #[repr(C)]: {}", err.message);
709-
let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg);
710-
if let Some(s) = err.help {
711-
diag.help(s);
712-
}
713-
diag.emit();
658+
&format!("`extern` block uses type `{}` which is not FFI-safe: \
659+
composed only of PhantomData", ty));
714660
}
715-
FfiResult::FfiBadEnum(_, err) => {
716-
// FIXME: This diagnostic is difficult to read, and doesn't
717-
// point at the relevant variant.
718-
let msg = format!("found non-foreign-function-safe member in enum: {}",
719-
err.message);
661+
FfiResult::FfiUnsafe { ty: unsafe_ty, reason, help } => {
662+
let msg = format!("`extern` block uses type `{}` which is not FFI-safe: {}",
663+
unsafe_ty, reason);
720664
let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg);
721-
if let Some(s) = err.help {
665+
if let Some(s) = help {
722666
diag.help(s);
723667
}
724668
diag.emit();

src/test/compile-fail/issue-14309.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ struct D {
3737
}
3838

3939
extern "C" {
40-
fn foo(x: A); //~ ERROR found struct without foreign-function-safe
41-
fn bar(x: B); //~ ERROR foreign-function-safe
40+
fn foo(x: A); //~ ERROR type `A` which is not FFI-safe
41+
fn bar(x: B); //~ ERROR type `A`
4242
fn baz(x: C);
43-
fn qux(x: A2); //~ ERROR foreign-function-safe
44-
fn quux(x: B2); //~ ERROR foreign-function-safe
43+
fn qux(x: A2); //~ ERROR type `A`
44+
fn quux(x: B2); //~ ERROR type `A`
4545
fn corge(x: C2);
46-
fn fred(x: D); //~ ERROR foreign-function-safe
46+
fn fred(x: D); //~ ERROR type `A`
4747
}
4848

4949
fn main() { }

src/test/compile-fail/issue-16250.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
pub struct Foo;
1414

1515
extern {
16-
pub fn foo(x: (Foo)); //~ ERROR found struct without
16+
pub fn foo(x: (Foo)); //~ ERROR unspecified layout
1717
}
1818

1919
fn main() {

src/test/compile-fail/lint-ctypes-enum.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ enum Isize { A, B, C }
2727

2828
extern {
2929
fn zf(x: Z);
30-
fn uf(x: U); //~ ERROR found enum without foreign-function-safe
31-
fn bf(x: B); //~ ERROR found enum without foreign-function-safe
32-
fn tf(x: T); //~ ERROR found enum without foreign-function-safe
30+
fn uf(x: U); //~ ERROR enum has no representation hint
31+
fn bf(x: B); //~ ERROR enum has no representation hint
32+
fn tf(x: T); //~ ERROR enum has no representation hint
3333
fn reprc(x: ReprC);
3434
fn u8(x: U8);
3535
fn isize(x: Isize);

0 commit comments

Comments
 (0)