about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorWesley Wiser <wwiser@gmail.com>2015-09-21 20:58:57 -0400
committerWesley Wiser <wwiser@gmail.com>2015-09-21 20:58:57 -0400
commitb0bcde4b47c1cdf09b58988f87f9a6c3cd8d461e (patch)
tree321580ef83da25f6d9df3de64151e062e9a0b923 /src
parent7e8d19b24de6d06e4962c5959d00603c3239ac8c (diff)
downloadrust-b0bcde4b47c1cdf09b58988f87f9a6c3cd8d461e.tar.gz
rust-b0bcde4b47c1cdf09b58988f87f9a6c3cd8d461e.zip
Split out the type lints into a new module
Part of #22206
Diffstat (limited to 'src')
-rw-r--r--src/librustc_lint/builtin.rs663
-rw-r--r--src/librustc_lint/lib.rs2
-rw-r--r--src/librustc_lint/types.rs685
3 files changed, 689 insertions, 661 deletions
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index febf8ef6e47..e917013474c 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -34,26 +34,19 @@ use middle::def_id::DefId;
 use middle::subst::Substs;
 use middle::ty::{self, Ty};
 use middle::ty::adjustment;
-use middle::const_eval::{eval_const_expr_partial, ConstVal};
-use middle::const_eval::EvalHint::ExprTypeChecked;
 use rustc::front::map as hir_map;
-use util::nodemap::{FnvHashSet, NodeSet};
+use util::nodemap::{NodeSet};
 use lint::{Level, LateContext, LintContext, LintArray, Lint};
 use lint::{LintPass, LateLintPass};
 
 use std::collections::HashSet;
-use std::cmp;
-use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
 
-use syntax::{abi, ast};
+use syntax::{ast};
 use syntax::attr::{self, AttrMetaMethods};
 use syntax::codemap::{self, Span};
-use syntax::feature_gate::{emit_feature_err, GateIssue};
-use syntax::ast::{TyIs, TyUs, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64};
 
 use rustc_front::hir;
 use rustc_front::visit::{self, FnKind, Visitor};
-use rustc_front::util::is_shift_binop;
 
 use bad_style::{MethodLateContext, method_context};
 
@@ -89,658 +82,6 @@ impl LateLintPass for WhileTrue {
 }
 
 declare_lint! {
-    UNUSED_COMPARISONS,
-    Warn,
-    "comparisons made useless by limits of the types involved"
-}
-
-declare_lint! {
-    OVERFLOWING_LITERALS,
-    Warn,
-    "literal out of range for its type"
-}
-
-declare_lint! {
-    EXCEEDING_BITSHIFTS,
-    Deny,
-    "shift exceeds the type's number of bits"
-}
-
-#[derive(Copy, Clone)]
-pub struct TypeLimits {
-    /// Id of the last visited negated expression
-    negated_expr_id: ast::NodeId,
-}
-
-impl TypeLimits {
-    pub fn new() -> TypeLimits {
-        TypeLimits {
-            negated_expr_id: !0,
-        }
-    }
-}
-
-impl LintPass for TypeLimits {
-    fn get_lints(&self) -> LintArray {
-        lint_array!(UNUSED_COMPARISONS, OVERFLOWING_LITERALS, EXCEEDING_BITSHIFTS)
-    }
-}
-
-impl LateLintPass for TypeLimits {
-    fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
-        match e.node {
-            hir::ExprUnary(hir::UnNeg, ref expr) => {
-                match expr.node  {
-                    hir::ExprLit(ref lit) => {
-                        match lit.node {
-                            ast::LitInt(_, ast::UnsignedIntLit(_)) => {
-                                check_unsigned_negation_feature(cx, e.span);
-                            },
-                            ast::LitInt(_, ast::UnsuffixedIntLit(_)) => {
-                                if let ty::TyUint(_) = cx.tcx.node_id_to_type(e.id).sty {
-                                    check_unsigned_negation_feature(cx, e.span);
-                                }
-                            },
-                            _ => ()
-                        }
-                    },
-                    _ => {
-                        let t = cx.tcx.node_id_to_type(expr.id);
-                        match t.sty {
-                            ty::TyUint(_) => {
-                                check_unsigned_negation_feature(cx, e.span);
-                            },
-                            _ => ()
-                        }
-                    }
-                };
-                // propagate negation, if the negation itself isn't negated
-                if self.negated_expr_id != e.id {
-                    self.negated_expr_id = expr.id;
-                }
-            },
-            hir::ExprBinary(binop, ref l, ref r) => {
-                if is_comparison(binop) && !check_limits(cx.tcx, binop, &**l, &**r) {
-                    cx.span_lint(UNUSED_COMPARISONS, e.span,
-                                 "comparison is useless due to type limits");
-                }
-
-                if is_shift_binop(binop.node) {
-                    let opt_ty_bits = match cx.tcx.node_id_to_type(l.id).sty {
-                        ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.int_type)),
-                        ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.uint_type)),
-                        _ => None
-                    };
-
-                    if let Some(bits) = opt_ty_bits {
-                        let exceeding = if let hir::ExprLit(ref lit) = r.node {
-                            if let ast::LitInt(shift, _) = lit.node { shift >= bits }
-                            else { false }
-                        } else {
-                            match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked) {
-                                Ok(ConstVal::Int(shift)) => { shift as u64 >= bits },
-                                Ok(ConstVal::Uint(shift)) => { shift >= bits },
-                                _ => { false }
-                            }
-                        };
-                        if exceeding {
-                            cx.span_lint(EXCEEDING_BITSHIFTS, e.span,
-                                         "bitshift exceeds the type's number of bits");
-                        }
-                    };
-                }
-            },
-            hir::ExprLit(ref lit) => {
-                match cx.tcx.node_id_to_type(e.id).sty {
-                    ty::TyInt(t) => {
-                        match lit.node {
-                            ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
-                            ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => {
-                                let int_type = if let ast::TyIs = t {
-                                    cx.sess().target.int_type
-                                } else {
-                                    t
-                                };
-                                let (_, max) = int_ty_range(int_type);
-                                let negative = self.negated_expr_id == e.id;
-
-                                // Detect literal value out of range [min, max] inclusive
-                                // avoiding use of -min to prevent overflow/panic
-                                if (negative && v > max as u64 + 1) ||
-                                   (!negative && v > max as u64) {
-                                    cx.span_lint(OVERFLOWING_LITERALS, e.span,
-                                                 &*format!("literal out of range for {:?}", t));
-                                    return;
-                                }
-                            }
-                            _ => panic!()
-                        };
-                    },
-                    ty::TyUint(t) => {
-                        let uint_type = if let ast::TyUs = t {
-                            cx.sess().target.uint_type
-                        } else {
-                            t
-                        };
-                        let (min, max) = uint_ty_range(uint_type);
-                        let lit_val: u64 = match lit.node {
-                            ast::LitByte(_v) => return,  // _v is u8, within range by definition
-                            ast::LitInt(v, _) => v,
-                            _ => panic!()
-                        };
-                        if lit_val < min || lit_val > max {
-                            cx.span_lint(OVERFLOWING_LITERALS, e.span,
-                                         &*format!("literal out of range for {:?}", t));
-                        }
-                    },
-                    ty::TyFloat(t) => {
-                        let (min, max) = float_ty_range(t);
-                        let lit_val: f64 = match lit.node {
-                            ast::LitFloat(ref v, _) |
-                            ast::LitFloatUnsuffixed(ref v) => {
-                                match v.parse() {
-                                    Ok(f) => f,
-                                    Err(_) => return
-                                }
-                            }
-                            _ => panic!()
-                        };
-                        if lit_val < min || lit_val > max {
-                            cx.span_lint(OVERFLOWING_LITERALS, e.span,
-                                         &*format!("literal out of range for {:?}", t));
-                        }
-                    },
-                    _ => ()
-                };
-            },
-            _ => ()
-        };
-
-        fn is_valid<T:cmp::PartialOrd>(binop: hir::BinOp, v: T,
-                                min: T, max: T) -> bool {
-            match binop.node {
-                hir::BiLt => v >  min && v <= max,
-                hir::BiLe => v >= min && v <  max,
-                hir::BiGt => v >= min && v <  max,
-                hir::BiGe => v >  min && v <= max,
-                hir::BiEq | hir::BiNe => v >= min && v <= max,
-                _ => panic!()
-            }
-        }
-
-        fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
-            codemap::respan(binop.span, match binop.node {
-                hir::BiLt => hir::BiGt,
-                hir::BiLe => hir::BiGe,
-                hir::BiGt => hir::BiLt,
-                hir::BiGe => hir::BiLe,
-                _ => return binop
-            })
-        }
-
-        // for isize & usize, be conservative with the warnings, so that the
-        // warnings are consistent between 32- and 64-bit platforms
-        fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
-            match int_ty {
-                ast::TyIs => (i64::MIN,        i64::MAX),
-                ast::TyI8 =>    (i8::MIN  as i64, i8::MAX  as i64),
-                ast::TyI16 =>   (i16::MIN as i64, i16::MAX as i64),
-                ast::TyI32 =>   (i32::MIN as i64, i32::MAX as i64),
-                ast::TyI64 =>   (i64::MIN,        i64::MAX)
-            }
-        }
-
-        fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
-            match uint_ty {
-                ast::TyUs => (u64::MIN,         u64::MAX),
-                ast::TyU8 =>    (u8::MIN   as u64, u8::MAX   as u64),
-                ast::TyU16 =>   (u16::MIN  as u64, u16::MAX  as u64),
-                ast::TyU32 =>   (u32::MIN  as u64, u32::MAX  as u64),
-                ast::TyU64 =>   (u64::MIN,         u64::MAX)
-            }
-        }
-
-        fn float_ty_range(float_ty: ast::FloatTy) -> (f64, f64) {
-            match float_ty {
-                ast::TyF32 => (f32::MIN as f64, f32::MAX as f64),
-                ast::TyF64 => (f64::MIN,        f64::MAX)
-            }
-        }
-
-        fn int_ty_bits(int_ty: ast::IntTy, target_int_ty: ast::IntTy) -> u64 {
-            match int_ty {
-                ast::TyIs => int_ty_bits(target_int_ty, target_int_ty),
-                ast::TyI8 =>    i8::BITS  as u64,
-                ast::TyI16 =>   i16::BITS as u64,
-                ast::TyI32 =>   i32::BITS as u64,
-                ast::TyI64 =>   i64::BITS as u64
-            }
-        }
-
-        fn uint_ty_bits(uint_ty: ast::UintTy, target_uint_ty: ast::UintTy) -> u64 {
-            match uint_ty {
-                ast::TyUs => uint_ty_bits(target_uint_ty, target_uint_ty),
-                ast::TyU8 =>    u8::BITS  as u64,
-                ast::TyU16 =>   u16::BITS as u64,
-                ast::TyU32 =>   u32::BITS as u64,
-                ast::TyU64 =>   u64::BITS as u64
-            }
-        }
-
-        fn check_limits(tcx: &ty::ctxt, binop: hir::BinOp,
-                        l: &hir::Expr, r: &hir::Expr) -> bool {
-            let (lit, expr, swap) = match (&l.node, &r.node) {
-                (&hir::ExprLit(_), _) => (l, r, true),
-                (_, &hir::ExprLit(_)) => (r, l, false),
-                _ => return true
-            };
-            // Normalize the binop so that the literal is always on the RHS in
-            // the comparison
-            let norm_binop = if swap {
-                rev_binop(binop)
-            } else {
-                binop
-            };
-            match tcx.node_id_to_type(expr.id).sty {
-                ty::TyInt(int_ty) => {
-                    let (min, max) = int_ty_range(int_ty);
-                    let lit_val: i64 = match lit.node {
-                        hir::ExprLit(ref li) => match li.node {
-                            ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
-                            ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => v as i64,
-                            ast::LitInt(v, ast::SignedIntLit(_, ast::Minus)) |
-                            ast::LitInt(v, ast::UnsuffixedIntLit(ast::Minus)) => -(v as i64),
-                            _ => return true
-                        },
-                        _ => panic!()
-                    };
-                    is_valid(norm_binop, lit_val, min, max)
-                }
-                ty::TyUint(uint_ty) => {
-                    let (min, max): (u64, u64) = uint_ty_range(uint_ty);
-                    let lit_val: u64 = match lit.node {
-                        hir::ExprLit(ref li) => match li.node {
-                            ast::LitInt(v, _) => v,
-                            _ => return true
-                        },
-                        _ => panic!()
-                    };
-                    is_valid(norm_binop, lit_val, min, max)
-                }
-                _ => true
-            }
-        }
-
-        fn is_comparison(binop: hir::BinOp) -> bool {
-            match binop.node {
-                hir::BiEq | hir::BiLt | hir::BiLe |
-                hir::BiNe | hir::BiGe | hir::BiGt => true,
-                _ => false
-            }
-        }
-
-        fn check_unsigned_negation_feature(cx: &LateContext, span: Span) {
-            if !cx.sess().features.borrow().negate_unsigned {
-                emit_feature_err(
-                    &cx.sess().parse_sess.span_diagnostic,
-                    "negate_unsigned",
-                    span,
-                    GateIssue::Language,
-                    "unary negation of unsigned integers may be removed in the future");
-            }
-        }
-    }
-}
-
-declare_lint! {
-    IMPROPER_CTYPES,
-    Warn,
-    "proper use of libc types in foreign modules"
-}
-
-struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
-    cx: &'a LateContext<'a, 'tcx>
-}
-
-enum FfiResult {
-    FfiSafe,
-    FfiUnsafe(&'static str),
-    FfiBadStruct(DefId, &'static str),
-    FfiBadEnum(DefId, &'static str)
-}
-
-/// 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>(tcx: &ty::ctxt<'tcx>,
-                              def: ty::AdtDef<'tcx>,
-                              substs: &Substs<'tcx>)
-                              -> bool {
-    if def.variants.len() == 2 {
-        let data_idx;
-
-        if def.variants[0].fields.is_empty() {
-            data_idx = 1;
-        } else if def.variants[1].fields.is_empty() {
-            data_idx = 0;
-        } else {
-            return false;
-        }
-
-        if def.variants[data_idx].fields.len() == 1 {
-            match def.variants[data_idx].fields[0].ty(tcx, substs).sty {
-                ty::TyBareFn(None, _) => { return true; }
-                ty::TyRef(..) => { return true; }
-                _ => { }
-            }
-        }
-    }
-    false
-}
-
-fn ast_ty_to_normalized<'tcx>(tcx: &ty::ctxt<'tcx>,
-                              id: ast::NodeId)
-                              -> Ty<'tcx> {
-    let tty = match tcx.ast_ty_to_ty_cache.borrow().get(&id) {
-        Some(&t) => t,
-        None => panic!("ast_ty_to_ty_cache was incomplete after typeck!")
-    };
-    infer::normalize_associated_type(tcx, &tty)
-}
-
-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(def, substs) => {
-                if !cx.lookup_repr_hints(def.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.
-                if def.struct_variant().fields.is_empty() {
-                    return FfiUnsafe(
-                        "found zero-size struct in foreign module, consider \
-                         adding a member to this struct");
-                }
-
-                for field in &def.struct_variant().fields {
-                    let field_ty = infer::normalize_associated_type(cx, &field.ty(cx, substs));
-                    let r = self.check_type_for_ffi(cache, field_ty);
-                    match r {
-                        FfiSafe => {}
-                        FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
-                        FfiUnsafe(s) => { return FfiBadStruct(def.did, s); }
-                    }
-                }
-                FfiSafe
-            }
-            ty::TyEnum(def, substs) => {
-                if def.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(def.did);
-                match &**repr_hints {
-                    [] => {
-                        // 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")
-                        }
-                    }
-                    [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 &def.variants {
-                    for field in &variant.fields {
-                        let arg = infer::normalize_associated_type(cx, &field.ty(cx, substs));
-                        let r = self.check_type_for_ffi(cache, arg);
-                        match r {
-                            FfiSafe => {}
-                            FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
-                            FfiUnsafe(s) => { return FfiBadEnum(def.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::PlatformIntrinsic |
-                    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 = ast_ty_to_normalized(self.cx.tcx, id);
-
-        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: &hir::Ty) {
-        match ty.node {
-            hir::TyPath(..) |
-            hir::TyBareFn(..) => self.check_def(ty.span, ty.id),
-            hir::TyVec(..) => {
-                self.cx.span_lint(IMPROPER_CTYPES, ty.span,
-                    "found Rust slice type in foreign module, consider \
-                     using a raw pointer instead");
-            }
-            hir::TyFixedLengthVec(ref ty, _) => self.visit_ty(ty),
-            hir::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)
-        }
-    }
-}
-
-#[derive(Copy, Clone)]
-pub struct ImproperCTypes;
-
-impl LintPass for ImproperCTypes {
-    fn get_lints(&self) -> LintArray {
-        lint_array!(IMPROPER_CTYPES)
-    }
-}
-
-impl LateLintPass for ImproperCTypes {
-    fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
-        fn check_ty(cx: &LateContext, ty: &hir::Ty) {
-            let mut vis = ImproperCTypesVisitor { cx: cx };
-            vis.visit_ty(ty);
-        }
-
-        fn check_foreign_fn(cx: &LateContext, decl: &hir::FnDecl) {
-            for input in &decl.inputs {
-                check_ty(cx, &*input.ty);
-            }
-            if let hir::Return(ref ret_ty) = decl.output {
-                let tty = ast_ty_to_normalized(cx.tcx, ret_ty.id);
-                if !tty.is_nil() {
-                    check_ty(cx, &ret_ty);
-                }
-            }
-        }
-
-        match it.node {
-            hir::ItemForeignMod(ref nmod)
-                if nmod.abi != abi::RustIntrinsic &&
-                   nmod.abi != abi::PlatformIntrinsic => {
-                for ni in &nmod.items {
-                    match ni.node {
-                        hir::ForeignItemFn(ref decl, _) => check_foreign_fn(cx, &**decl),
-                        hir::ForeignItemStatic(ref t, _) => check_ty(cx, &**t)
-                    }
-                }
-            }
-            _ => (),
-        }
-    }
-}
-
-declare_lint! {
     BOX_POINTERS,
     Allow,
     "use of owned (Box type) heap memory"
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index 674631cd3a5..cba058e92a2 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -60,10 +60,12 @@ use lint::LintId;
 
 mod bad_style;
 mod builtin;
+mod types;
 mod unused;
 
 use bad_style::*;
 use builtin::*;
+use types::*;
 use unused::*;
 
 /// Tell the `LintStore` about all the built-in lints (the ones
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
new file mode 100644
index 00000000000..cf6cc9bb7ce
--- /dev/null
+++ b/src/librustc_lint/types.rs
@@ -0,0 +1,685 @@
+// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use middle::{infer};
+use middle::def_id::DefId;
+use middle::subst::Substs;
+use middle::ty::{self, Ty};
+use middle::const_eval::{eval_const_expr_partial, ConstVal};
+use middle::const_eval::EvalHint::ExprTypeChecked;
+use util::nodemap::{FnvHashSet};
+use lint::{LateContext, LintContext, LintArray};
+use lint::{LintPass, LateLintPass};
+
+use std::cmp;
+use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
+
+use syntax::{abi, ast};
+use syntax::attr::{self, AttrMetaMethods};
+use syntax::codemap::{self, Span};
+use syntax::feature_gate::{emit_feature_err, GateIssue};
+use syntax::ast::{TyIs, TyUs, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64};
+
+use rustc_front::hir;
+use rustc_front::visit::{self, Visitor};
+use rustc_front::util::is_shift_binop;
+
+declare_lint! {
+    UNUSED_COMPARISONS,
+    Warn,
+    "comparisons made useless by limits of the types involved"
+}
+
+declare_lint! {
+    OVERFLOWING_LITERALS,
+    Warn,
+    "literal out of range for its type"
+}
+
+declare_lint! {
+    EXCEEDING_BITSHIFTS,
+    Deny,
+    "shift exceeds the type's number of bits"
+}
+
+#[derive(Copy, Clone)]
+pub struct TypeLimits {
+    /// Id of the last visited negated expression
+    negated_expr_id: ast::NodeId,
+}
+
+impl TypeLimits {
+    pub fn new() -> TypeLimits {
+        TypeLimits {
+            negated_expr_id: !0,
+        }
+    }
+}
+
+impl LintPass for TypeLimits {
+    fn get_lints(&self) -> LintArray {
+        lint_array!(UNUSED_COMPARISONS, OVERFLOWING_LITERALS, EXCEEDING_BITSHIFTS)
+    }
+}
+
+impl LateLintPass for TypeLimits {
+    fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
+        match e.node {
+            hir::ExprUnary(hir::UnNeg, ref expr) => {
+                match expr.node  {
+                    hir::ExprLit(ref lit) => {
+                        match lit.node {
+                            ast::LitInt(_, ast::UnsignedIntLit(_)) => {
+                                check_unsigned_negation_feature(cx, e.span);
+                            },
+                            ast::LitInt(_, ast::UnsuffixedIntLit(_)) => {
+                                if let ty::TyUint(_) = cx.tcx.node_id_to_type(e.id).sty {
+                                    check_unsigned_negation_feature(cx, e.span);
+                                }
+                            },
+                            _ => ()
+                        }
+                    },
+                    _ => {
+                        let t = cx.tcx.node_id_to_type(expr.id);
+                        match t.sty {
+                            ty::TyUint(_) => {
+                                check_unsigned_negation_feature(cx, e.span);
+                            },
+                            _ => ()
+                        }
+                    }
+                };
+                // propagate negation, if the negation itself isn't negated
+                if self.negated_expr_id != e.id {
+                    self.negated_expr_id = expr.id;
+                }
+            },
+            hir::ExprBinary(binop, ref l, ref r) => {
+                if is_comparison(binop) && !check_limits(cx.tcx, binop, &**l, &**r) {
+                    cx.span_lint(UNUSED_COMPARISONS, e.span,
+                                 "comparison is useless due to type limits");
+                }
+
+                if is_shift_binop(binop.node) {
+                    let opt_ty_bits = match cx.tcx.node_id_to_type(l.id).sty {
+                        ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.int_type)),
+                        ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.uint_type)),
+                        _ => None
+                    };
+
+                    if let Some(bits) = opt_ty_bits {
+                        let exceeding = if let hir::ExprLit(ref lit) = r.node {
+                            if let ast::LitInt(shift, _) = lit.node { shift >= bits }
+                            else { false }
+                        } else {
+                            match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked) {
+                                Ok(ConstVal::Int(shift)) => { shift as u64 >= bits },
+                                Ok(ConstVal::Uint(shift)) => { shift >= bits },
+                                _ => { false }
+                            }
+                        };
+                        if exceeding {
+                            cx.span_lint(EXCEEDING_BITSHIFTS, e.span,
+                                         "bitshift exceeds the type's number of bits");
+                        }
+                    };
+                }
+            },
+            hir::ExprLit(ref lit) => {
+                match cx.tcx.node_id_to_type(e.id).sty {
+                    ty::TyInt(t) => {
+                        match lit.node {
+                            ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
+                            ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => {
+                                let int_type = if let ast::TyIs = t {
+                                    cx.sess().target.int_type
+                                } else {
+                                    t
+                                };
+                                let (_, max) = int_ty_range(int_type);
+                                let negative = self.negated_expr_id == e.id;
+
+                                // Detect literal value out of range [min, max] inclusive
+                                // avoiding use of -min to prevent overflow/panic
+                                if (negative && v > max as u64 + 1) ||
+                                   (!negative && v > max as u64) {
+                                    cx.span_lint(OVERFLOWING_LITERALS, e.span,
+                                                 &*format!("literal out of range for {:?}", t));
+                                    return;
+                                }
+                            }
+                            _ => panic!()
+                        };
+                    },
+                    ty::TyUint(t) => {
+                        let uint_type = if let ast::TyUs = t {
+                            cx.sess().target.uint_type
+                        } else {
+                            t
+                        };
+                        let (min, max) = uint_ty_range(uint_type);
+                        let lit_val: u64 = match lit.node {
+                            ast::LitByte(_v) => return,  // _v is u8, within range by definition
+                            ast::LitInt(v, _) => v,
+                            _ => panic!()
+                        };
+                        if lit_val < min || lit_val > max {
+                            cx.span_lint(OVERFLOWING_LITERALS, e.span,
+                                         &*format!("literal out of range for {:?}", t));
+                        }
+                    },
+                    ty::TyFloat(t) => {
+                        let (min, max) = float_ty_range(t);
+                        let lit_val: f64 = match lit.node {
+                            ast::LitFloat(ref v, _) |
+                            ast::LitFloatUnsuffixed(ref v) => {
+                                match v.parse() {
+                                    Ok(f) => f,
+                                    Err(_) => return
+                                }
+                            }
+                            _ => panic!()
+                        };
+                        if lit_val < min || lit_val > max {
+                            cx.span_lint(OVERFLOWING_LITERALS, e.span,
+                                         &*format!("literal out of range for {:?}", t));
+                        }
+                    },
+                    _ => ()
+                };
+            },
+            _ => ()
+        };
+
+        fn is_valid<T:cmp::PartialOrd>(binop: hir::BinOp, v: T,
+                                min: T, max: T) -> bool {
+            match binop.node {
+                hir::BiLt => v >  min && v <= max,
+                hir::BiLe => v >= min && v <  max,
+                hir::BiGt => v >= min && v <  max,
+                hir::BiGe => v >  min && v <= max,
+                hir::BiEq | hir::BiNe => v >= min && v <= max,
+                _ => panic!()
+            }
+        }
+
+        fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
+            codemap::respan(binop.span, match binop.node {
+                hir::BiLt => hir::BiGt,
+                hir::BiLe => hir::BiGe,
+                hir::BiGt => hir::BiLt,
+                hir::BiGe => hir::BiLe,
+                _ => return binop
+            })
+        }
+
+        // for isize & usize, be conservative with the warnings, so that the
+        // warnings are consistent between 32- and 64-bit platforms
+        fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
+            match int_ty {
+                ast::TyIs => (i64::MIN,        i64::MAX),
+                ast::TyI8 =>    (i8::MIN  as i64, i8::MAX  as i64),
+                ast::TyI16 =>   (i16::MIN as i64, i16::MAX as i64),
+                ast::TyI32 =>   (i32::MIN as i64, i32::MAX as i64),
+                ast::TyI64 =>   (i64::MIN,        i64::MAX)
+            }
+        }
+
+        fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
+            match uint_ty {
+                ast::TyUs => (u64::MIN,         u64::MAX),
+                ast::TyU8 =>    (u8::MIN   as u64, u8::MAX   as u64),
+                ast::TyU16 =>   (u16::MIN  as u64, u16::MAX  as u64),
+                ast::TyU32 =>   (u32::MIN  as u64, u32::MAX  as u64),
+                ast::TyU64 =>   (u64::MIN,         u64::MAX)
+            }
+        }
+
+        fn float_ty_range(float_ty: ast::FloatTy) -> (f64, f64) {
+            match float_ty {
+                ast::TyF32 => (f32::MIN as f64, f32::MAX as f64),
+                ast::TyF64 => (f64::MIN,        f64::MAX)
+            }
+        }
+
+        fn int_ty_bits(int_ty: ast::IntTy, target_int_ty: ast::IntTy) -> u64 {
+            match int_ty {
+                ast::TyIs => int_ty_bits(target_int_ty, target_int_ty),
+                ast::TyI8 =>    i8::BITS  as u64,
+                ast::TyI16 =>   i16::BITS as u64,
+                ast::TyI32 =>   i32::BITS as u64,
+                ast::TyI64 =>   i64::BITS as u64
+            }
+        }
+
+        fn uint_ty_bits(uint_ty: ast::UintTy, target_uint_ty: ast::UintTy) -> u64 {
+            match uint_ty {
+                ast::TyUs => uint_ty_bits(target_uint_ty, target_uint_ty),
+                ast::TyU8 =>    u8::BITS  as u64,
+                ast::TyU16 =>   u16::BITS as u64,
+                ast::TyU32 =>   u32::BITS as u64,
+                ast::TyU64 =>   u64::BITS as u64
+            }
+        }
+
+        fn check_limits(tcx: &ty::ctxt, binop: hir::BinOp,
+                        l: &hir::Expr, r: &hir::Expr) -> bool {
+            let (lit, expr, swap) = match (&l.node, &r.node) {
+                (&hir::ExprLit(_), _) => (l, r, true),
+                (_, &hir::ExprLit(_)) => (r, l, false),
+                _ => return true
+            };
+            // Normalize the binop so that the literal is always on the RHS in
+            // the comparison
+            let norm_binop = if swap {
+                rev_binop(binop)
+            } else {
+                binop
+            };
+            match tcx.node_id_to_type(expr.id).sty {
+                ty::TyInt(int_ty) => {
+                    let (min, max) = int_ty_range(int_ty);
+                    let lit_val: i64 = match lit.node {
+                        hir::ExprLit(ref li) => match li.node {
+                            ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
+                            ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => v as i64,
+                            ast::LitInt(v, ast::SignedIntLit(_, ast::Minus)) |
+                            ast::LitInt(v, ast::UnsuffixedIntLit(ast::Minus)) => -(v as i64),
+                            _ => return true
+                        },
+                        _ => panic!()
+                    };
+                    is_valid(norm_binop, lit_val, min, max)
+                }
+                ty::TyUint(uint_ty) => {
+                    let (min, max): (u64, u64) = uint_ty_range(uint_ty);
+                    let lit_val: u64 = match lit.node {
+                        hir::ExprLit(ref li) => match li.node {
+                            ast::LitInt(v, _) => v,
+                            _ => return true
+                        },
+                        _ => panic!()
+                    };
+                    is_valid(norm_binop, lit_val, min, max)
+                }
+                _ => true
+            }
+        }
+
+        fn is_comparison(binop: hir::BinOp) -> bool {
+            match binop.node {
+                hir::BiEq | hir::BiLt | hir::BiLe |
+                hir::BiNe | hir::BiGe | hir::BiGt => true,
+                _ => false
+            }
+        }
+
+        fn check_unsigned_negation_feature(cx: &LateContext, span: Span) {
+            if !cx.sess().features.borrow().negate_unsigned {
+                emit_feature_err(
+                    &cx.sess().parse_sess.span_diagnostic,
+                    "negate_unsigned",
+                    span,
+                    GateIssue::Language,
+                    "unary negation of unsigned integers may be removed in the future");
+            }
+        }
+    }
+}
+
+declare_lint! {
+    IMPROPER_CTYPES,
+    Warn,
+    "proper use of libc types in foreign modules"
+}
+
+struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
+    cx: &'a LateContext<'a, 'tcx>
+}
+
+enum FfiResult {
+    FfiSafe,
+    FfiUnsafe(&'static str),
+    FfiBadStruct(DefId, &'static str),
+    FfiBadEnum(DefId, &'static str)
+}
+
+/// 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>(tcx: &ty::ctxt<'tcx>,
+                              def: ty::AdtDef<'tcx>,
+                              substs: &Substs<'tcx>)
+                              -> bool {
+    if def.variants.len() == 2 {
+        let data_idx;
+
+        if def.variants[0].fields.is_empty() {
+            data_idx = 1;
+        } else if def.variants[1].fields.is_empty() {
+            data_idx = 0;
+        } else {
+            return false;
+        }
+
+        if def.variants[data_idx].fields.len() == 1 {
+            match def.variants[data_idx].fields[0].ty(tcx, substs).sty {
+                ty::TyBareFn(None, _) => { return true; }
+                ty::TyRef(..) => { return true; }
+                _ => { }
+            }
+        }
+    }
+    false
+}
+
+fn ast_ty_to_normalized<'tcx>(tcx: &ty::ctxt<'tcx>,
+                              id: ast::NodeId)
+                              -> Ty<'tcx> {
+    let tty = match tcx.ast_ty_to_ty_cache.borrow().get(&id) {
+        Some(&t) => t,
+        None => panic!("ast_ty_to_ty_cache was incomplete after typeck!")
+    };
+    infer::normalize_associated_type(tcx, &tty)
+}
+
+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(def, substs) => {
+                if !cx.lookup_repr_hints(def.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.
+                if def.struct_variant().fields.is_empty() {
+                    return FfiUnsafe(
+                        "found zero-size struct in foreign module, consider \
+                         adding a member to this struct");
+                }
+
+                for field in &def.struct_variant().fields {
+                    let field_ty = infer::normalize_associated_type(cx, &field.ty(cx, substs));
+                    let r = self.check_type_for_ffi(cache, field_ty);
+                    match r {
+                        FfiSafe => {}
+                        FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
+                        FfiUnsafe(s) => { return FfiBadStruct(def.did, s); }
+                    }
+                }
+                FfiSafe
+            }
+            ty::TyEnum(def, substs) => {
+                if def.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(def.did);
+                match &**repr_hints {
+                    [] => {
+                        // 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")
+                        }
+                    }
+                    [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 &def.variants {
+                    for field in &variant.fields {
+                        let arg = infer::normalize_associated_type(cx, &field.ty(cx, substs));
+                        let r = self.check_type_for_ffi(cache, arg);
+                        match r {
+                            FfiSafe => {}
+                            FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
+                            FfiUnsafe(s) => { return FfiBadEnum(def.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::PlatformIntrinsic |
+                    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 = ast_ty_to_normalized(self.cx.tcx, id);
+
+        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: &hir::Ty) {
+        match ty.node {
+            hir::TyPath(..) |
+            hir::TyBareFn(..) => self.check_def(ty.span, ty.id),
+            hir::TyVec(..) => {
+                self.cx.span_lint(IMPROPER_CTYPES, ty.span,
+                    "found Rust slice type in foreign module, consider \
+                     using a raw pointer instead");
+            }
+            hir::TyFixedLengthVec(ref ty, _) => self.visit_ty(ty),
+            hir::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)
+        }
+    }
+}
+
+#[derive(Copy, Clone)]
+pub struct ImproperCTypes;
+
+impl LintPass for ImproperCTypes {
+    fn get_lints(&self) -> LintArray {
+        lint_array!(IMPROPER_CTYPES)
+    }
+}
+
+impl LateLintPass for ImproperCTypes {
+    fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
+        fn check_ty(cx: &LateContext, ty: &hir::Ty) {
+            let mut vis = ImproperCTypesVisitor { cx: cx };
+            vis.visit_ty(ty);
+        }
+
+        fn check_foreign_fn(cx: &LateContext, decl: &hir::FnDecl) {
+            for input in &decl.inputs {
+                check_ty(cx, &*input.ty);
+            }
+            if let hir::Return(ref ret_ty) = decl.output {
+                let tty = ast_ty_to_normalized(cx.tcx, ret_ty.id);
+                if !tty.is_nil() {
+                    check_ty(cx, &ret_ty);
+                }
+            }
+        }
+
+        match it.node {
+            hir::ItemForeignMod(ref nmod)
+                if nmod.abi != abi::RustIntrinsic &&
+                   nmod.abi != abi::PlatformIntrinsic => {
+                for ni in &nmod.items {
+                    match ni.node {
+                        hir::ForeignItemFn(ref decl, _) => check_foreign_fn(cx, &**decl),
+                        hir::ForeignItemStatic(ref t, _) => check_ty(cx, &**t)
+                    }
+                }
+            }
+            _ => (),
+        }
+    }
+}
+