1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
12 use middle::def_id::DefId;
13 use middle::subst::Substs;
14 use middle::ty::{self, Ty};
15 use middle::const_eval::{eval_const_expr_partial, ConstVal};
16 use middle::const_eval::EvalHint::ExprTypeChecked;
17 use util::nodemap::{FnvHashSet};
18 use lint::{LateContext, LintContext, LintArray};
19 use lint::{LintPass, LateLintPass};
22 use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
24 use syntax::{abi, ast};
25 use syntax::attr::{self, AttrMetaMethods};
26 use syntax::codemap::{self, Span};
27 use syntax::feature_gate::{emit_feature_err, GateIssue};
28 use syntax::ast::{TyIs, TyUs, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64};
31 use rustc_front::visit::{self, Visitor};
32 use rustc_front::util::is_shift_binop;
37 "comparisons made useless by limits of the types involved"
43 "literal out of range for its type"
49 "shift exceeds the type's number of bits"
52 #[derive(Copy, Clone)]
53 pub struct TypeLimits {
54 /// Id of the last visited negated expression
55 negated_expr_id: ast::NodeId,
59 pub fn new() -> TypeLimits {
66 impl LintPass for TypeLimits {
67 fn get_lints(&self) -> LintArray {
68 lint_array!(UNUSED_COMPARISONS, OVERFLOWING_LITERALS, EXCEEDING_BITSHIFTS)
72 impl LateLintPass for TypeLimits {
73 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
75 hir::ExprUnary(hir::UnNeg, ref expr) => {
77 hir::ExprLit(ref lit) => {
79 ast::LitInt(_, ast::UnsignedIntLit(_)) => {
80 check_unsigned_negation_feature(cx, e.span);
82 ast::LitInt(_, ast::UnsuffixedIntLit(_)) => {
83 if let ty::TyUint(_) = cx.tcx.node_id_to_type(e.id).sty {
84 check_unsigned_negation_feature(cx, e.span);
91 let t = cx.tcx.node_id_to_type(expr.id);
94 check_unsigned_negation_feature(cx, e.span);
100 // propagate negation, if the negation itself isn't negated
101 if self.negated_expr_id != e.id {
102 self.negated_expr_id = expr.id;
105 hir::ExprBinary(binop, ref l, ref r) => {
106 if is_comparison(binop) && !check_limits(cx.tcx, binop, &**l, &**r) {
107 cx.span_lint(UNUSED_COMPARISONS, e.span,
108 "comparison is useless due to type limits");
111 if is_shift_binop(binop.node) {
112 let opt_ty_bits = match cx.tcx.node_id_to_type(l.id).sty {
113 ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.int_type)),
114 ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.uint_type)),
118 if let Some(bits) = opt_ty_bits {
119 let exceeding = if let hir::ExprLit(ref lit) = r.node {
120 if let ast::LitInt(shift, _) = lit.node { shift >= bits }
123 match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked) {
124 Ok(ConstVal::Int(shift)) => { shift as u64 >= bits },
125 Ok(ConstVal::Uint(shift)) => { shift >= bits },
130 cx.span_lint(EXCEEDING_BITSHIFTS, e.span,
131 "bitshift exceeds the type's number of bits");
136 hir::ExprLit(ref lit) => {
137 match cx.tcx.node_id_to_type(e.id).sty {
140 ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
141 ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => {
142 let int_type = if let ast::TyIs = t {
143 cx.sess().target.int_type
147 let (_, max) = int_ty_range(int_type);
148 let negative = self.negated_expr_id == e.id;
150 // Detect literal value out of range [min, max] inclusive
151 // avoiding use of -min to prevent overflow/panic
152 if (negative && v > max as u64 + 1) ||
153 (!negative && v > max as u64) {
154 cx.span_lint(OVERFLOWING_LITERALS, e.span,
155 &*format!("literal out of range for {:?}", t));
163 let uint_type = if let ast::TyUs = t {
164 cx.sess().target.uint_type
168 let (min, max) = uint_ty_range(uint_type);
169 let lit_val: u64 = match lit.node {
170 ast::LitByte(_v) => return, // _v is u8, within range by definition
171 ast::LitInt(v, _) => v,
174 if lit_val < min || lit_val > max {
175 cx.span_lint(OVERFLOWING_LITERALS, e.span,
176 &*format!("literal out of range for {:?}", t));
180 let (min, max) = float_ty_range(t);
181 let lit_val: f64 = match lit.node {
182 ast::LitFloat(ref v, _) |
183 ast::LitFloatUnsuffixed(ref v) => {
191 if lit_val < min || lit_val > max {
192 cx.span_lint(OVERFLOWING_LITERALS, e.span,
193 &*format!("literal out of range for {:?}", t));
202 fn is_valid<T:cmp::PartialOrd>(binop: hir::BinOp, v: T,
203 min: T, max: T) -> bool {
205 hir::BiLt => v > min && v <= max,
206 hir::BiLe => v >= min && v < max,
207 hir::BiGt => v >= min && v < max,
208 hir::BiGe => v > min && v <= max,
209 hir::BiEq | hir::BiNe => v >= min && v <= max,
214 fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
215 codemap::respan(binop.span, match binop.node {
216 hir::BiLt => hir::BiGt,
217 hir::BiLe => hir::BiGe,
218 hir::BiGt => hir::BiLt,
219 hir::BiGe => hir::BiLe,
224 // for isize & usize, be conservative with the warnings, so that the
225 // warnings are consistent between 32- and 64-bit platforms
226 fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
228 ast::TyIs => (i64::MIN, i64::MAX),
229 ast::TyI8 => (i8::MIN as i64, i8::MAX as i64),
230 ast::TyI16 => (i16::MIN as i64, i16::MAX as i64),
231 ast::TyI32 => (i32::MIN as i64, i32::MAX as i64),
232 ast::TyI64 => (i64::MIN, i64::MAX)
236 fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
238 ast::TyUs => (u64::MIN, u64::MAX),
239 ast::TyU8 => (u8::MIN as u64, u8::MAX as u64),
240 ast::TyU16 => (u16::MIN as u64, u16::MAX as u64),
241 ast::TyU32 => (u32::MIN as u64, u32::MAX as u64),
242 ast::TyU64 => (u64::MIN, u64::MAX)
246 fn float_ty_range(float_ty: ast::FloatTy) -> (f64, f64) {
248 ast::TyF32 => (f32::MIN as f64, f32::MAX as f64),
249 ast::TyF64 => (f64::MIN, f64::MAX)
253 fn int_ty_bits(int_ty: ast::IntTy, target_int_ty: ast::IntTy) -> u64 {
255 ast::TyIs => int_ty_bits(target_int_ty, target_int_ty),
256 ast::TyI8 => i8::BITS as u64,
257 ast::TyI16 => i16::BITS as u64,
258 ast::TyI32 => i32::BITS as u64,
259 ast::TyI64 => i64::BITS as u64
263 fn uint_ty_bits(uint_ty: ast::UintTy, target_uint_ty: ast::UintTy) -> u64 {
265 ast::TyUs => uint_ty_bits(target_uint_ty, target_uint_ty),
266 ast::TyU8 => u8::BITS as u64,
267 ast::TyU16 => u16::BITS as u64,
268 ast::TyU32 => u32::BITS as u64,
269 ast::TyU64 => u64::BITS as u64
273 fn check_limits(tcx: &ty::ctxt, binop: hir::BinOp,
274 l: &hir::Expr, r: &hir::Expr) -> bool {
275 let (lit, expr, swap) = match (&l.node, &r.node) {
276 (&hir::ExprLit(_), _) => (l, r, true),
277 (_, &hir::ExprLit(_)) => (r, l, false),
280 // Normalize the binop so that the literal is always on the RHS in
282 let norm_binop = if swap {
287 match tcx.node_id_to_type(expr.id).sty {
288 ty::TyInt(int_ty) => {
289 let (min, max) = int_ty_range(int_ty);
290 let lit_val: i64 = match lit.node {
291 hir::ExprLit(ref li) => match li.node {
292 ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
293 ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => v as i64,
294 ast::LitInt(v, ast::SignedIntLit(_, ast::Minus)) |
295 ast::LitInt(v, ast::UnsuffixedIntLit(ast::Minus)) => -(v as i64),
300 is_valid(norm_binop, lit_val, min, max)
302 ty::TyUint(uint_ty) => {
303 let (min, max): (u64, u64) = uint_ty_range(uint_ty);
304 let lit_val: u64 = match lit.node {
305 hir::ExprLit(ref li) => match li.node {
306 ast::LitInt(v, _) => v,
311 is_valid(norm_binop, lit_val, min, max)
317 fn is_comparison(binop: hir::BinOp) -> bool {
319 hir::BiEq | hir::BiLt | hir::BiLe |
320 hir::BiNe | hir::BiGe | hir::BiGt => true,
325 fn check_unsigned_negation_feature(cx: &LateContext, span: Span) {
326 if !cx.sess().features.borrow().negate_unsigned {
328 &cx.sess().parse_sess.span_diagnostic,
332 "unary negation of unsigned integers may be removed in the future");
341 "proper use of libc types in foreign modules"
344 struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
345 cx: &'a LateContext<'a, 'tcx>
350 FfiUnsafe(&'static str),
351 FfiBadStruct(DefId, &'static str),
352 FfiBadEnum(DefId, &'static str)
355 /// Check if this enum can be safely exported based on the
356 /// "nullable pointer optimization". Currently restricted
357 /// to function pointers and references, but could be
358 /// expanded to cover NonZero raw pointers and newtypes.
359 /// FIXME: This duplicates code in trans.
360 fn is_repr_nullable_ptr<'tcx>(tcx: &ty::ctxt<'tcx>,
361 def: ty::AdtDef<'tcx>,
362 substs: &Substs<'tcx>)
364 if def.variants.len() == 2 {
367 if def.variants[0].fields.is_empty() {
369 } else if def.variants[1].fields.is_empty() {
375 if def.variants[data_idx].fields.len() == 1 {
376 match def.variants[data_idx].fields[0].ty(tcx, substs).sty {
377 ty::TyBareFn(None, _) => { return true; }
378 ty::TyRef(..) => { return true; }
386 fn ast_ty_to_normalized<'tcx>(tcx: &ty::ctxt<'tcx>,
389 let tty = match tcx.ast_ty_to_ty_cache.borrow().get(&id) {
391 None => panic!("ast_ty_to_ty_cache was incomplete after typeck!")
393 infer::normalize_associated_type(tcx, &tty)
396 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
397 /// Check if the given type is "ffi-safe" (has a stable, well-defined
398 /// representation which can be exported to C code).
399 fn check_type_for_ffi(&self,
400 cache: &mut FnvHashSet<Ty<'tcx>>,
403 use self::FfiResult::*;
404 let cx = &self.cx.tcx;
406 // Protect against infinite recursion, for example
407 // `struct S(*mut S);`.
408 // FIXME: A recursion limit is necessary as well, for irregular
410 if !cache.insert(ty) {
415 ty::TyStruct(def, substs) => {
416 if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
418 "found struct without foreign-function-safe \
419 representation annotation in foreign module, \
420 consider adding a #[repr(C)] attribute to \
424 // We can't completely trust repr(C) markings; make sure the
425 // fields are actually safe.
426 if def.struct_variant().fields.is_empty() {
428 "found zero-size struct in foreign module, consider \
429 adding a member to this struct");
432 for field in &def.struct_variant().fields {
433 let field_ty = infer::normalize_associated_type(cx, &field.ty(cx, substs));
434 let r = self.check_type_for_ffi(cache, field_ty);
437 FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
438 FfiUnsafe(s) => { return FfiBadStruct(def.did, s); }
443 ty::TyEnum(def, substs) => {
444 if def.variants.is_empty() {
445 // Empty enums are okay... although sort of useless.
449 // Check for a repr() attribute to specify the size of the
451 let repr_hints = cx.lookup_repr_hints(def.did);
452 match &**repr_hints {
454 // Special-case types like `Option<extern fn()>`.
455 if !is_repr_nullable_ptr(cx, def, substs) {
457 "found enum without foreign-function-safe \
458 representation annotation in foreign module, \
459 consider adding a #[repr(...)] attribute to \
464 if !hint.is_ffi_safe() {
465 // FIXME: This shouldn't be reachable: we should check
468 "enum has unexpected #[repr(...)] attribute")
471 // Enum with an explicitly sized discriminant; either
472 // a C-style enum or a discriminated union.
474 // The layout of enum variants is implicitly repr(C).
475 // FIXME: Is that correct?
478 // FIXME: This shouldn't be reachable: we should check
481 "enum has too many #[repr(...)] attributes");
485 // Check the contained variants.
486 for variant in &def.variants {
487 for field in &variant.fields {
488 let arg = infer::normalize_associated_type(cx, &field.ty(cx, substs));
489 let r = self.check_type_for_ffi(cache, arg);
492 FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
493 FfiUnsafe(s) => { return FfiBadEnum(def.did, s); }
500 ty::TyInt(ast::TyIs) => {
501 FfiUnsafe("found Rust type `isize` in foreign module, while \
502 `libc::c_int` or `libc::c_long` should be used")
504 ty::TyUint(ast::TyUs) => {
505 FfiUnsafe("found Rust type `usize` in foreign module, while \
506 `libc::c_uint` or `libc::c_ulong` should be used")
509 FfiUnsafe("found Rust type `char` in foreign module, while \
510 `u32` or `libc::wchar_t` should be used")
513 // Primitive types with a stable representation.
514 ty::TyBool | ty::TyInt(..) | ty::TyUint(..) |
515 ty::TyFloat(..) => FfiSafe,
518 FfiUnsafe("found Rust type Box<_> in foreign module, \
519 consider using a raw pointer instead")
523 FfiUnsafe("found Rust slice type in foreign module, \
524 consider using a raw pointer instead")
528 FfiUnsafe("found Rust trait type in foreign module, \
529 consider using a raw pointer instead")
533 FfiUnsafe("found Rust type `str` in foreign module; \
534 consider using a `*const libc::c_char`")
538 FfiUnsafe("found Rust tuple type in foreign module; \
539 consider using a struct instead`")
542 ty::TyRawPtr(ref m) | ty::TyRef(_, ref m) => {
543 self.check_type_for_ffi(cache, m.ty)
546 ty::TyArray(ty, _) => {
547 self.check_type_for_ffi(cache, ty)
550 ty::TyBareFn(None, bare_fn) => {
554 abi::PlatformIntrinsic |
557 "found function pointer with Rust calling \
558 convention in foreign module; consider using an \
559 `extern` function pointer")
564 let sig = cx.erase_late_bound_regions(&bare_fn.sig);
566 ty::FnDiverging => {}
567 ty::FnConverging(output) => {
568 if !output.is_nil() {
569 let r = self.check_type_for_ffi(cache, output);
577 for arg in sig.inputs {
578 let r = self.check_type_for_ffi(cache, arg);
587 ty::TyParam(..) | ty::TyInfer(..) | ty::TyError |
588 ty::TyClosure(..) | ty::TyProjection(..) |
589 ty::TyBareFn(Some(_), _) => {
590 panic!("Unexpected type in foreign function")
595 fn check_def(&mut self, sp: Span, id: ast::NodeId) {
596 let tty = ast_ty_to_normalized(self.cx.tcx, id);
598 match ImproperCTypesVisitor::check_type_for_ffi(self, &mut FnvHashSet(), tty) {
599 FfiResult::FfiSafe => {}
600 FfiResult::FfiUnsafe(s) => {
601 self.cx.span_lint(IMPROPER_CTYPES, sp, s);
603 FfiResult::FfiBadStruct(_, s) => {
604 // FIXME: This diagnostic is difficult to read, and doesn't
605 // point at the relevant field.
606 self.cx.span_lint(IMPROPER_CTYPES, sp,
607 &format!("found non-foreign-function-safe member in \
608 struct marked #[repr(C)]: {}", s));
610 FfiResult::FfiBadEnum(_, s) => {
611 // FIXME: This diagnostic is difficult to read, and doesn't
612 // point at the relevant variant.
613 self.cx.span_lint(IMPROPER_CTYPES, sp,
614 &format!("found non-foreign-function-safe member in \
621 impl<'a, 'tcx, 'v> Visitor<'v> for ImproperCTypesVisitor<'a, 'tcx> {
622 fn visit_ty(&mut self, ty: &hir::Ty) {
625 hir::TyBareFn(..) => self.check_def(ty.span, ty.id),
627 self.cx.span_lint(IMPROPER_CTYPES, ty.span,
628 "found Rust slice type in foreign module, consider \
629 using a raw pointer instead");
631 hir::TyFixedLengthVec(ref ty, _) => self.visit_ty(ty),
633 self.cx.span_lint(IMPROPER_CTYPES, ty.span,
634 "found Rust tuple type in foreign module; \
635 consider using a struct instead`")
637 _ => visit::walk_ty(self, ty)
642 #[derive(Copy, Clone)]
643 pub struct ImproperCTypes;
645 impl LintPass for ImproperCTypes {
646 fn get_lints(&self) -> LintArray {
647 lint_array!(IMPROPER_CTYPES)
651 impl LateLintPass for ImproperCTypes {
652 fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
653 fn check_ty(cx: &LateContext, ty: &hir::Ty) {
654 let mut vis = ImproperCTypesVisitor { cx: cx };
658 fn check_foreign_fn(cx: &LateContext, decl: &hir::FnDecl) {
659 for input in &decl.inputs {
660 check_ty(cx, &*input.ty);
662 if let hir::Return(ref ret_ty) = decl.output {
663 let tty = ast_ty_to_normalized(cx.tcx, ret_ty.id);
665 check_ty(cx, &ret_ty);
671 hir::ItemForeignMod(ref nmod)
672 if nmod.abi != abi::RustIntrinsic &&
673 nmod.abi != abi::PlatformIntrinsic => {
674 for ni in &nmod.items {
676 hir::ForeignItemFn(ref decl, _) => check_foreign_fn(cx, &**decl),
677 hir::ForeignItemStatic(ref t, _) => check_ty(cx, &**t)