Rewrite the improper_ctypes lint.
Makes the lint a bit more accurate, and improves the quality of the diagnostic messages by explicitly returning an error message. The new lint is also a little more aggressive: specifically, it now rejects tuples, and it recurses into function pointers.
This commit is contained in:
parent
d4d4206e56
commit
6fa17b43d3
src
librustc/middle
librustc_lint
librustc_llvm
librustc_trans/back/msvc
libstd
test
compile-fail
run-make/execution-engine
@ -3967,7 +3967,6 @@ def_type_content_sets! {
|
||||
None = 0b0000_0000__0000_0000__0000,
|
||||
|
||||
// Things that are interior to the value (first nibble):
|
||||
InteriorUnsized = 0b0000_0000__0000_0000__0001,
|
||||
InteriorUnsafe = 0b0000_0000__0000_0000__0010,
|
||||
InteriorParam = 0b0000_0000__0000_0000__0100,
|
||||
// InteriorAll = 0b00000000__00000000__1111,
|
||||
@ -3977,18 +3976,9 @@ def_type_content_sets! {
|
||||
OwnsDtor = 0b0000_0000__0000_0010__0000,
|
||||
OwnsAll = 0b0000_0000__1111_1111__0000,
|
||||
|
||||
// Things that are reachable by the value in any way (fourth nibble):
|
||||
ReachesBorrowed = 0b0000_0010__0000_0000__0000,
|
||||
ReachesMutable = 0b0000_1000__0000_0000__0000,
|
||||
ReachesFfiUnsafe = 0b0010_0000__0000_0000__0000,
|
||||
ReachesAll = 0b0011_1111__0000_0000__0000,
|
||||
|
||||
// Things that mean drop glue is necessary
|
||||
NeedsDrop = 0b0000_0000__0000_0111__0000,
|
||||
|
||||
// Things that prevent values from being considered sized
|
||||
Nonsized = 0b0000_0000__0000_0000__0001,
|
||||
|
||||
// All bits
|
||||
All = 0b1111_1111__1111_1111__1111
|
||||
}
|
||||
@ -4007,10 +3997,6 @@ impl TypeContents {
|
||||
self.intersects(TC::OwnsOwned)
|
||||
}
|
||||
|
||||
pub fn is_sized(&self, _: &ctxt) -> bool {
|
||||
!self.intersects(TC::Nonsized)
|
||||
}
|
||||
|
||||
pub fn interior_param(&self) -> bool {
|
||||
self.intersects(TC::InteriorParam)
|
||||
}
|
||||
@ -4019,29 +4005,13 @@ impl TypeContents {
|
||||
self.intersects(TC::InteriorUnsafe)
|
||||
}
|
||||
|
||||
pub fn interior_unsized(&self) -> bool {
|
||||
self.intersects(TC::InteriorUnsized)
|
||||
}
|
||||
|
||||
pub fn needs_drop(&self, _: &ctxt) -> bool {
|
||||
self.intersects(TC::NeedsDrop)
|
||||
}
|
||||
|
||||
/// Includes only those bits that still apply when indirected through a `Box` pointer
|
||||
pub fn owned_pointer(&self) -> TypeContents {
|
||||
TC::OwnsOwned | (
|
||||
*self & (TC::OwnsAll | TC::ReachesAll))
|
||||
}
|
||||
|
||||
/// Includes only those bits that still apply when indirected through a reference (`&`)
|
||||
pub fn reference(&self, bits: TypeContents) -> TypeContents {
|
||||
bits | (
|
||||
*self & TC::ReachesAll)
|
||||
}
|
||||
|
||||
/// Includes only those bits that still apply when indirected through a raw pointer (`*`)
|
||||
pub fn unsafe_pointer(&self) -> TypeContents {
|
||||
*self & TC::ReachesAll
|
||||
TC::OwnsOwned | (*self & TC::OwnsAll)
|
||||
}
|
||||
|
||||
pub fn union<T, F>(v: &[T], mut f: F) -> TypeContents where
|
||||
@ -4129,7 +4099,7 @@ impl<'tcx> TyS<'tcx> {
|
||||
let result = match ty.sty {
|
||||
// usize and isize are ffi-unsafe
|
||||
TyUint(ast::TyUs) | TyInt(ast::TyIs) => {
|
||||
TC::ReachesFfiUnsafe
|
||||
TC::None
|
||||
}
|
||||
|
||||
// Scalar and unique types are sendable, and durable
|
||||
@ -4140,20 +4110,19 @@ impl<'tcx> TyS<'tcx> {
|
||||
}
|
||||
|
||||
TyBox(typ) => {
|
||||
TC::ReachesFfiUnsafe | tc_ty(cx, typ, cache).owned_pointer()
|
||||
tc_ty(cx, typ, cache).owned_pointer()
|
||||
}
|
||||
|
||||
TyTrait(box TraitTy { ref bounds, .. }) => {
|
||||
object_contents(bounds) | TC::ReachesFfiUnsafe | TC::Nonsized
|
||||
TyTrait(_) => {
|
||||
TC::All - TC::InteriorParam
|
||||
}
|
||||
|
||||
TyRawPtr(ref mt) => {
|
||||
tc_ty(cx, mt.ty, cache).unsafe_pointer()
|
||||
TyRawPtr(_) => {
|
||||
TC::None
|
||||
}
|
||||
|
||||
TyRef(r, ref mt) => {
|
||||
tc_ty(cx, mt.ty, cache).reference(borrowed_contents(*r, mt.mutbl)) |
|
||||
TC::ReachesFfiUnsafe
|
||||
TyRef(_, _) => {
|
||||
TC::None
|
||||
}
|
||||
|
||||
TyArray(ty, _) => {
|
||||
@ -4161,19 +4130,15 @@ impl<'tcx> TyS<'tcx> {
|
||||
}
|
||||
|
||||
TySlice(ty) => {
|
||||
tc_ty(cx, ty, cache) | TC::Nonsized
|
||||
tc_ty(cx, ty, cache)
|
||||
}
|
||||
TyStr => TC::Nonsized,
|
||||
TyStr => TC::None,
|
||||
|
||||
TyStruct(did, substs) => {
|
||||
let flds = cx.struct_fields(did, substs);
|
||||
let mut res =
|
||||
TypeContents::union(&flds[..],
|
||||
|f| tc_mt(cx, f.mt, cache));
|
||||
|
||||
if !cx.lookup_repr_hints(did).contains(&attr::ReprExtern) {
|
||||
res = res | TC::ReachesFfiUnsafe;
|
||||
}
|
||||
|f| tc_ty(cx, f.mt.ty, cache));
|
||||
|
||||
if cx.has_dtor(did) {
|
||||
res = res | TC::OwnsDtor;
|
||||
@ -4182,7 +4147,6 @@ impl<'tcx> TyS<'tcx> {
|
||||
}
|
||||
|
||||
TyClosure(did, substs) => {
|
||||
// FIXME(#14449): `borrowed_contents` below assumes `&mut` closure.
|
||||
let param_env = cx.empty_parameter_environment();
|
||||
let infcx = infer::new_infer_ctxt(cx, &cx.tables, Some(param_env), false);
|
||||
let upvars = infcx.closure_upvars(did, substs).unwrap();
|
||||
@ -4208,44 +4172,6 @@ impl<'tcx> TyS<'tcx> {
|
||||
res = res | TC::OwnsDtor;
|
||||
}
|
||||
|
||||
if !variants.is_empty() {
|
||||
let repr_hints = cx.lookup_repr_hints(did);
|
||||
if repr_hints.len() > 1 {
|
||||
// this is an error later on, but this type isn't safe
|
||||
res = res | TC::ReachesFfiUnsafe;
|
||||
}
|
||||
|
||||
match repr_hints.get(0) {
|
||||
Some(h) => if !h.is_ffi_safe() {
|
||||
res = res | TC::ReachesFfiUnsafe;
|
||||
},
|
||||
// ReprAny
|
||||
None => {
|
||||
res = res | TC::ReachesFfiUnsafe;
|
||||
|
||||
// We allow ReprAny enums if they are eligible for
|
||||
// the nullable pointer optimization and the
|
||||
// contained type is an `extern fn`
|
||||
|
||||
if variants.len() == 2 {
|
||||
let mut data_idx = 0;
|
||||
|
||||
if variants[0].args.is_empty() {
|
||||
data_idx = 1;
|
||||
}
|
||||
|
||||
if variants[data_idx].args.len() == 1 {
|
||||
match variants[data_idx].args[0].sty {
|
||||
TyBareFn(..) => { res = res - TC::ReachesFfiUnsafe; }
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
apply_lang_items(cx, did, res)
|
||||
}
|
||||
|
||||
@ -4264,14 +4190,6 @@ impl<'tcx> TyS<'tcx> {
|
||||
result
|
||||
}
|
||||
|
||||
fn tc_mt<'tcx>(cx: &ctxt<'tcx>,
|
||||
mt: TypeAndMut<'tcx>,
|
||||
cache: &mut FnvHashMap<Ty<'tcx>, TypeContents>) -> TypeContents
|
||||
{
|
||||
let mc = TC::ReachesMutable.when(mt.mutbl == MutMutable);
|
||||
mc | tc_ty(cx, mt.ty, cache)
|
||||
}
|
||||
|
||||
fn apply_lang_items(cx: &ctxt, did: ast::DefId, tc: TypeContents)
|
||||
-> TypeContents {
|
||||
if Some(did) == cx.lang_items.unsafe_cell_type() {
|
||||
@ -4280,32 +4198,6 @@ impl<'tcx> TyS<'tcx> {
|
||||
tc
|
||||
}
|
||||
}
|
||||
|
||||
/// Type contents due to containing a reference with
|
||||
/// the region `region` and borrow kind `bk`.
|
||||
fn borrowed_contents(region: ty::Region,
|
||||
mutbl: ast::Mutability)
|
||||
-> TypeContents {
|
||||
let b = match mutbl {
|
||||
ast::MutMutable => TC::ReachesMutable,
|
||||
ast::MutImmutable => TC::None,
|
||||
};
|
||||
b | (TC::ReachesBorrowed).when(region != ty::ReStatic)
|
||||
}
|
||||
|
||||
fn object_contents(bounds: &ExistentialBounds) -> TypeContents {
|
||||
// These are the type contents of the (opaque) interior. We
|
||||
// make no assumptions (other than that it cannot have an
|
||||
// in-scope type parameter within, which makes no sense).
|
||||
let mut tc = TC::All - TC::InteriorParam;
|
||||
for bound in &bounds.builtin_bounds {
|
||||
tc = tc - match bound {
|
||||
BoundSync | BoundSend | BoundCopy => TC::None,
|
||||
BoundSized => TC::Nonsized,
|
||||
};
|
||||
}
|
||||
return tc;
|
||||
}
|
||||
}
|
||||
|
||||
fn impls_bound<'a>(&'tcx self, param_env: &ParameterEnvironment<'a,'tcx>,
|
||||
@ -4399,10 +4291,6 @@ impl<'tcx> TyS<'tcx> {
|
||||
result
|
||||
}
|
||||
|
||||
pub fn is_ffi_safe(&'tcx self, cx: &ctxt<'tcx>) -> bool {
|
||||
!self.type_contents(cx).intersects(TC::ReachesFfiUnsafe)
|
||||
}
|
||||
|
||||
// True if instantiating an instance of `r_ty` requires an instance of `r_ty`.
|
||||
pub fn is_instantiable(&'tcx self, cx: &ctxt<'tcx>) -> bool {
|
||||
fn type_requires<'tcx>(cx: &ctxt<'tcx>, seen: &mut Vec<DefId>,
|
||||
|
@ -32,23 +32,21 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use metadata::{csearch, decoder};
|
||||
use middle::{cfg, def, infer, pat_util, stability, traits};
|
||||
use middle::def::*;
|
||||
use middle::infer;
|
||||
use middle::subst::Substs;
|
||||
use middle::ty::{self, Ty};
|
||||
use middle::traits;
|
||||
use middle::{def, pat_util, stability};
|
||||
use middle::const_eval::{eval_const_expr_partial, ConstVal};
|
||||
use middle::const_eval::EvalHint::ExprTypeChecked;
|
||||
use middle::cfg;
|
||||
use rustc::ast_map;
|
||||
use util::nodemap::{FnvHashMap, NodeSet};
|
||||
use util::nodemap::{FnvHashMap, FnvHashSet, NodeSet};
|
||||
use lint::{Level, Context, LintPass, LintArray, Lint};
|
||||
|
||||
use std::collections::{HashSet, BitSet};
|
||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||
use std::{cmp, slice};
|
||||
use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
|
||||
use std::rc::Rc;
|
||||
|
||||
use syntax::{abi, ast};
|
||||
use syntax::ast_util::{self, is_shift_binop, local_def};
|
||||
@ -405,43 +403,288 @@ struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a Context<'a, 'tcx>
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
||||
fn check_def(&mut self, sp: Span, id: ast::NodeId) {
|
||||
match self.cx.tcx.def_map.borrow().get(&id).unwrap().full_def() {
|
||||
def::DefPrimTy(ast::TyInt(ast::TyIs)) => {
|
||||
self.cx.span_lint(IMPROPER_CTYPES, sp,
|
||||
"found rust type `isize` in foreign module, while \
|
||||
libc::c_int or libc::c_long should be used");
|
||||
}
|
||||
def::DefPrimTy(ast::TyUint(ast::TyUs)) => {
|
||||
self.cx.span_lint(IMPROPER_CTYPES, sp,
|
||||
"found rust type `usize` in foreign module, while \
|
||||
libc::c_uint or libc::c_ulong should be used");
|
||||
}
|
||||
def::DefTy(..) => {
|
||||
let tty = match self.cx.tcx.ast_ty_to_ty_cache.borrow().get(&id) {
|
||||
Some(&t) => t,
|
||||
None => panic!("ast_ty_to_ty_cache was incomplete after typeck!")
|
||||
};
|
||||
enum FfiResult {
|
||||
FfiSafe,
|
||||
FfiUnsafe(&'static str),
|
||||
FfiBadStruct(ast::DefId, &'static str),
|
||||
FfiBadEnum(ast::DefId, &'static str)
|
||||
}
|
||||
|
||||
if !tty.is_ffi_safe(self.cx.tcx) {
|
||||
self.cx.span_lint(IMPROPER_CTYPES, sp,
|
||||
"found type without foreign-function-safe \
|
||||
representation annotation in foreign module, consider \
|
||||
adding a #[repr(...)] attribute to the type");
|
||||
}
|
||||
/// Check if this enum can be safely exported based on the
|
||||
/// "nullable pointer optimization". Currently restricted
|
||||
/// to function pointers and references, but could be
|
||||
/// expanded to cover NonZero raw pointers and newtypes.
|
||||
/// FIXME: This duplicates code in trans.
|
||||
fn is_repr_nullable_ptr<'tcx>(variants: &Vec<Rc<ty::VariantInfo<'tcx>>>) -> bool {
|
||||
if variants.len() == 2 {
|
||||
let mut data_idx = 0;
|
||||
|
||||
if variants[0].args.is_empty() {
|
||||
data_idx = 1;
|
||||
} else if !variants[1].args.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if variants[data_idx].args.len() == 1 {
|
||||
match variants[data_idx].args[0].sty {
|
||||
ty::TyBareFn(None, _) => { return true; }
|
||||
ty::TyRef(..) => { return true; }
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
}
|
||||
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 FnvHashSet<Ty<'tcx>>,
|
||||
ty: Ty<'tcx>)
|
||||
-> FfiResult {
|
||||
use self::FfiResult::*;
|
||||
let cx = &self.cx.tcx;
|
||||
|
||||
// Protect against infinite recursion, for example
|
||||
// `struct S(*mut S);`.
|
||||
// FIXME: A recursion limit is necessary as well, for irregular
|
||||
// recusive types.
|
||||
if !cache.insert(ty) {
|
||||
return FfiSafe;
|
||||
}
|
||||
|
||||
match ty.sty {
|
||||
ty::TyStruct(did, substs) => {
|
||||
if !cx.lookup_repr_hints(did).contains(&attr::ReprExtern) {
|
||||
return FfiUnsafe(
|
||||
"found struct without foreign-function-safe \
|
||||
representation annotation in foreign module, \
|
||||
consider adding a #[repr(C)] attribute to \
|
||||
the type");
|
||||
}
|
||||
|
||||
// We can't completely trust repr(C) markings; make sure the
|
||||
// fields are actually safe.
|
||||
let fields = cx.struct_fields(did, substs);
|
||||
|
||||
if fields.is_empty() {
|
||||
return FfiUnsafe(
|
||||
"found zero-size struct in foreign module, consider \
|
||||
adding a member to this struct");
|
||||
}
|
||||
|
||||
for field in fields {
|
||||
let field_ty = infer::normalize_associated_type(cx, &field.mt.ty);
|
||||
let r = self.check_type_for_ffi(cache, field_ty);
|
||||
match r {
|
||||
FfiSafe => {}
|
||||
FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
|
||||
FfiUnsafe(s) => { return FfiBadStruct(did, s); }
|
||||
}
|
||||
}
|
||||
FfiSafe
|
||||
}
|
||||
ty::TyEnum(did, substs) => {
|
||||
let variants = cx.substd_enum_variants(did, substs);
|
||||
if variants.is_empty() {
|
||||
// Empty enums are okay... although sort of useless.
|
||||
return FfiSafe
|
||||
}
|
||||
|
||||
// Check for a repr() attribute to specify the size of the
|
||||
// discriminant.
|
||||
let repr_hints = cx.lookup_repr_hints(did);
|
||||
match &**repr_hints {
|
||||
[] => {
|
||||
// Special-case types like `Option<extern fn()>`.
|
||||
if !is_repr_nullable_ptr(&variants) {
|
||||
return FfiUnsafe(
|
||||
"found enum without foreign-function-safe \
|
||||
representation annotation in foreign module, \
|
||||
consider adding a #[repr(...)] attribute to \
|
||||
the type")
|
||||
}
|
||||
}
|
||||
[ref hint] => {
|
||||
if !hint.is_ffi_safe() {
|
||||
// FIXME: This shouldn't be reachable: we should check
|
||||
// this earlier.
|
||||
return FfiUnsafe(
|
||||
"enum has unexpected #[repr(...)] attribute")
|
||||
}
|
||||
|
||||
// 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?
|
||||
}
|
||||
_ => {
|
||||
// FIXME: This shouldn't be reachable: we should check
|
||||
// this earlier.
|
||||
return FfiUnsafe(
|
||||
"enum has too many #[repr(...)] attributes");
|
||||
}
|
||||
}
|
||||
|
||||
// Check the contained variants.
|
||||
for variant in variants {
|
||||
for arg in &variant.args {
|
||||
let arg = infer::normalize_associated_type(cx, arg);
|
||||
let r = self.check_type_for_ffi(cache, arg);
|
||||
match r {
|
||||
FfiSafe => {}
|
||||
FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
|
||||
FfiUnsafe(s) => { return FfiBadEnum(did, s); }
|
||||
}
|
||||
}
|
||||
}
|
||||
FfiSafe
|
||||
}
|
||||
|
||||
ty::TyInt(ast::TyIs) => {
|
||||
FfiUnsafe("found Rust type `isize` in foreign module, while \
|
||||
`libc::c_int` or `libc::c_long` should be used")
|
||||
}
|
||||
ty::TyUint(ast::TyUs) => {
|
||||
FfiUnsafe("found Rust type `usize` in foreign module, while \
|
||||
`libc::c_uint` or `libc::c_ulong` should be used")
|
||||
}
|
||||
ty::TyChar => {
|
||||
FfiUnsafe("found Rust type `char` in foreign module, while \
|
||||
`u32` or `libc::wchar_t` should be used")
|
||||
}
|
||||
|
||||
// Primitive types with a stable representation.
|
||||
ty::TyBool | ty::TyInt(..) | ty::TyUint(..) |
|
||||
ty::TyFloat(..) => FfiSafe,
|
||||
|
||||
ty::TyBox(..) => {
|
||||
FfiUnsafe("found Rust type Box<_> in foreign module, \
|
||||
consider using a raw pointer instead")
|
||||
}
|
||||
|
||||
ty::TySlice(_) => {
|
||||
FfiUnsafe("found Rust slice type in foreign module, \
|
||||
consider using a raw pointer instead")
|
||||
}
|
||||
|
||||
ty::TyTrait(..) => {
|
||||
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::TyRawPtr(ref m) | ty::TyRef(_, ref m) => {
|
||||
self.check_type_for_ffi(cache, m.ty)
|
||||
}
|
||||
|
||||
ty::TyArray(ty, _) => {
|
||||
self.check_type_for_ffi(cache, ty)
|
||||
}
|
||||
|
||||
ty::TyBareFn(None, bare_fn) => {
|
||||
match bare_fn.abi {
|
||||
abi::Rust |
|
||||
abi::RustIntrinsic |
|
||||
abi::RustCall => {
|
||||
return FfiUnsafe(
|
||||
"found function pointer with Rust calling \
|
||||
convention in foreign module; consider using an \
|
||||
`extern` function pointer")
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let sig = cx.erase_late_bound_regions(&bare_fn.sig);
|
||||
match sig.output {
|
||||
ty::FnDiverging => {}
|
||||
ty::FnConverging(output) => {
|
||||
if !output.is_nil() {
|
||||
let r = self.check_type_for_ffi(cache, output);
|
||||
match r {
|
||||
FfiSafe => {}
|
||||
_ => { return r; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for arg in sig.inputs {
|
||||
let r = self.check_type_for_ffi(cache, arg);
|
||||
match r {
|
||||
FfiSafe => {}
|
||||
_ => { return r; }
|
||||
}
|
||||
}
|
||||
FfiSafe
|
||||
}
|
||||
|
||||
ty::TyParam(..) | ty::TyInfer(..) | ty::TyError |
|
||||
ty::TyClosure(..) | ty::TyProjection(..) |
|
||||
ty::TyBareFn(Some(_), _) => {
|
||||
panic!("Unexpected type in foreign function")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_def(&mut self, sp: Span, id: ast::NodeId) {
|
||||
let tty = match self.cx.tcx.ast_ty_to_ty_cache.borrow().get(&id) {
|
||||
Some(&t) => t,
|
||||
None => panic!("ast_ty_to_ty_cache was incomplete after typeck!")
|
||||
};
|
||||
let tty = infer::normalize_associated_type(self.cx.tcx, &tty);
|
||||
|
||||
match ImproperCTypesVisitor::check_type_for_ffi(self, &mut FnvHashSet(), tty) {
|
||||
FfiResult::FfiSafe => {}
|
||||
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.
|
||||
self.cx.span_lint(IMPROPER_CTYPES, sp,
|
||||
&format!("found non-foreign-function-safe member in \
|
||||
struct 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));
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v> Visitor<'v> for ImproperCTypesVisitor<'a, 'tcx> {
|
||||
fn visit_ty(&mut self, ty: &ast::Ty) {
|
||||
if let ast::TyPath(..) = ty.node {
|
||||
self.check_def(ty.span, ty.id);
|
||||
match ty.node {
|
||||
ast::TyPath(..) |
|
||||
ast::TyBareFn(..) => self.check_def(ty.span, ty.id),
|
||||
ast::TyVec(..) => {
|
||||
self.cx.span_lint(IMPROPER_CTYPES, ty.span,
|
||||
"found Rust slice type in foreign module, consider \
|
||||
using a raw pointer instead");
|
||||
}
|
||||
ast::TyFixedLengthVec(ref ty, _) => self.visit_ty(ty),
|
||||
ast::TyTup(..) => {
|
||||
self.cx.span_lint(IMPROPER_CTYPES, ty.span,
|
||||
"found Rust tuple type in foreign module; \
|
||||
consider using a struct instead`")
|
||||
}
|
||||
_ => visit::walk_ty(self, ty)
|
||||
}
|
||||
visit::walk_ty(self, ty);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#![feature(ref_slice)]
|
||||
#![feature(rustc_diagnostic_macros)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(slice_patterns)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(str_char)]
|
||||
|
||||
|
@ -685,7 +685,7 @@ extern {
|
||||
pub fn LLVMGetArrayLength(ArrayTy: TypeRef) -> c_uint;
|
||||
pub fn LLVMGetPointerAddressSpace(PointerTy: TypeRef) -> c_uint;
|
||||
pub fn LLVMGetPointerToGlobal(EE: ExecutionEngineRef, V: ValueRef)
|
||||
-> *const ();
|
||||
-> *const c_void;
|
||||
pub fn LLVMGetVectorSize(VectorTy: TypeRef) -> c_uint;
|
||||
|
||||
/* Operations on other types */
|
||||
|
@ -13,6 +13,7 @@ use std::ffi::{OsString, OsStr};
|
||||
use std::os::windows::prelude::*;
|
||||
use std::ops::RangeFrom;
|
||||
use libc::{DWORD, LPCWSTR, LONG, LPDWORD, LPBYTE, ERROR_SUCCESS};
|
||||
use libc::c_void;
|
||||
|
||||
const HKEY_LOCAL_MACHINE: HKEY = 0x80000002 as HKEY;
|
||||
const KEY_WOW64_32KEY: REGSAM = 0x0200;
|
||||
@ -32,7 +33,7 @@ pub type HKEY = *mut __HKEY__;
|
||||
pub type PHKEY = *mut HKEY;
|
||||
pub type REGSAM = DWORD;
|
||||
pub type LPWSTR = *mut u16;
|
||||
pub type PFILETIME = *mut ();
|
||||
pub type PFILETIME = *mut c_void;
|
||||
|
||||
#[link(name = "advapi32")]
|
||||
extern "system" {
|
||||
|
@ -251,12 +251,11 @@ pub mod eabi {
|
||||
use rt::libunwind as uw;
|
||||
use libc::{c_void, c_int};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EXCEPTION_RECORD;
|
||||
#[repr(C)]
|
||||
pub struct CONTEXT;
|
||||
#[repr(C)]
|
||||
pub struct DISPATCHER_CONTEXT;
|
||||
// Fake definitions; these are actually complicated structs,
|
||||
// but we don't use the contents here.
|
||||
pub type EXCEPTION_RECORD = c_void;
|
||||
pub type CONTEXT = c_void;
|
||||
pub type DISPATCHER_CONTEXT = c_void;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -82,6 +82,7 @@ pub unsafe fn make_handler() -> Handler {
|
||||
Handler { _data: 0 as *mut libc::c_void }
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EXCEPTION_RECORD {
|
||||
pub ExceptionCode: DWORD,
|
||||
pub ExceptionFlags: DWORD,
|
||||
@ -91,6 +92,7 @@ pub struct EXCEPTION_RECORD {
|
||||
pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS]
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EXCEPTION_POINTERS {
|
||||
pub ExceptionRecord: *mut EXCEPTION_RECORD,
|
||||
pub ContextRecord: LPVOID
|
||||
|
@ -335,13 +335,13 @@ mod imp {
|
||||
#[linkage = "extern_weak"]
|
||||
static __dso_handle: *mut u8;
|
||||
#[linkage = "extern_weak"]
|
||||
static __cxa_thread_atexit_impl: *const ();
|
||||
static __cxa_thread_atexit_impl: *const libc::c_void;
|
||||
}
|
||||
if !__cxa_thread_atexit_impl.is_null() {
|
||||
type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8),
|
||||
arg: *mut u8,
|
||||
dso_handle: *mut u8) -> libc::c_int;
|
||||
mem::transmute::<*const (), F>(__cxa_thread_atexit_impl)
|
||||
mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)
|
||||
(dtor, t, &__dso_handle as *const _ as *mut _);
|
||||
return
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ struct D {
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn foo(x: A); //~ ERROR found type without foreign-function-safe
|
||||
fn foo(x: A); //~ ERROR found struct without foreign-function-safe
|
||||
fn bar(x: B); //~ ERROR foreign-function-safe
|
||||
fn baz(x: C);
|
||||
fn qux(x: A2); //~ ERROR foreign-function-safe
|
||||
|
@ -11,7 +11,7 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
extern {
|
||||
pub fn foo(x: (isize)); //~ ERROR found rust type `isize` in foreign module
|
||||
pub fn foo(x: (isize)); //~ ERROR found Rust type `isize` in foreign module
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -18,9 +18,9 @@ enum T { E, F, G }
|
||||
|
||||
extern {
|
||||
fn zf(x: Z);
|
||||
fn uf(x: U); //~ ERROR found type without foreign-function-safe
|
||||
fn bf(x: B); //~ ERROR found type without foreign-function-safe
|
||||
fn tf(x: T); //~ ERROR found type without foreign-function-safe
|
||||
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
|
||||
}
|
||||
|
||||
pub fn main() { }
|
||||
|
@ -13,14 +13,45 @@
|
||||
|
||||
extern crate libc;
|
||||
|
||||
trait Mirror { type It; }
|
||||
impl<T> Mirror for T { type It = Self; }
|
||||
#[repr(C)]
|
||||
pub struct StructWithProjection(*mut <StructWithProjection as Mirror>::It);
|
||||
#[repr(C)]
|
||||
pub struct StructWithProjectionAndLifetime<'a>(
|
||||
&'a mut <StructWithProjectionAndLifetime<'a> as Mirror>::It
|
||||
);
|
||||
pub type I32Pair = (i32, i32);
|
||||
#[repr(C)]
|
||||
pub struct ZeroSize;
|
||||
pub type RustFn = fn();
|
||||
pub type RustBadRet = extern fn() -> Box<u32>;
|
||||
|
||||
extern {
|
||||
pub fn bare_type1(size: isize); //~ ERROR: found rust type
|
||||
pub fn bare_type2(size: usize); //~ ERROR: found rust type
|
||||
pub fn ptr_type1(size: *const isize); //~ ERROR: found rust type
|
||||
pub fn ptr_type2(size: *const usize); //~ ERROR: found rust type
|
||||
pub fn bare_type1(size: isize); //~ ERROR: found Rust type
|
||||
pub fn bare_type2(size: usize); //~ ERROR: found Rust type
|
||||
pub fn ptr_type1(size: *const isize); //~ ERROR: found Rust type
|
||||
pub fn ptr_type2(size: *const usize); //~ ERROR: found Rust type
|
||||
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 Rust type
|
||||
pub fn char_type(p: char); //~ 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 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 Rust type
|
||||
|
||||
pub fn good1(size: *const libc::c_int);
|
||||
pub fn good2(size: *const libc::c_uint);
|
||||
pub fn good3(fptr: Option<extern fn()>);
|
||||
pub fn good4(aptr: &[u8; 4 as usize]);
|
||||
pub fn good5(s: StructWithProjection);
|
||||
pub fn good6(s: StructWithProjectionAndLifetime);
|
||||
pub fn good7(fptr: extern fn() -> ());
|
||||
pub fn good8(fptr: extern fn() -> !);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -13,9 +13,9 @@
|
||||
|
||||
mod xx {
|
||||
extern {
|
||||
pub fn strlen(str: *const u8) -> usize; //~ ERROR found rust type `usize`
|
||||
pub fn foo(x: isize, y: usize); //~ ERROR found rust type `isize`
|
||||
//~^ ERROR found rust type `usize`
|
||||
pub fn strlen(str: *const u8) -> usize; //~ ERROR found Rust type `usize`
|
||||
pub fn foo(x: isize, y: usize); //~ ERROR found Rust type `isize`
|
||||
//~^ ERROR found Rust type `usize`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,9 @@
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(rustc_private)]
|
||||
#![feature(libc)]
|
||||
|
||||
extern crate libc;
|
||||
extern crate rustc;
|
||||
extern crate rustc_driver;
|
||||
extern crate rustc_lint;
|
||||
@ -29,6 +31,7 @@ use rustc::session::config::{self, basic_options, build_configuration, Input, Op
|
||||
use rustc::session::build_session;
|
||||
use rustc_driver::driver;
|
||||
use rustc_resolve::MakeGlobMap;
|
||||
use libc::c_void;
|
||||
|
||||
use syntax::diagnostics::registry::Registry;
|
||||
|
||||
@ -111,7 +114,7 @@ impl ExecutionEngine {
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the named function.
|
||||
pub fn get_function(&mut self, name: &str) -> Option<*const ()> {
|
||||
pub fn get_function(&mut self, name: &str) -> Option<*const c_void> {
|
||||
let s = CString::new(name.as_bytes()).unwrap();
|
||||
|
||||
for &m in &self.modules {
|
||||
@ -128,7 +131,7 @@ impl ExecutionEngine {
|
||||
}
|
||||
|
||||
/// Returns a raw pointer to the named global item.
|
||||
pub fn get_global(&mut self, name: &str) -> Option<*const ()> {
|
||||
pub fn get_global(&mut self, name: &str) -> Option<*const c_void> {
|
||||
let s = CString::new(name.as_bytes()).unwrap();
|
||||
|
||||
for &m in &self.modules {
|
||||
|
Loading…
x
Reference in New Issue
Block a user