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.
11 #![allow(non_snake_case)]
13 use rustc
::hir
::def_id
::DefId
;
15 use rustc
::ty
::subst
::Substs
;
16 use rustc
::ty
::{self, Ty, TyCtxt}
;
17 use middle
::const_val
::ConstVal
;
18 use rustc_const_eval
::eval_const_expr_partial
;
19 use rustc_const_eval
::EvalHint
::ExprTypeChecked
;
20 use util
::nodemap
::{FnvHashSet}
;
21 use lint
::{LateContext, LintContext, LintArray}
;
22 use lint
::{LintPass, LateLintPass}
;
25 use std
::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}
;
29 use syntax
::attr
::{self, AttrMetaMethods}
;
30 use syntax
::codemap
::{self, Span}
;
34 register_long_diagnostics
! {
36 It is not allowed to negate an unsigned integer.
37 You can negate a signed integer and cast it to an
38 unsigned integer or use the `!` operator.
41 let x: usize = -1isize as usize;
46 Alternatively you can use the `Wrapping` newtype
47 or the `wrapping_neg` operation that all
48 integral types support:
51 use std::num::Wrapping;
52 let x: Wrapping<usize> = -Wrapping(1);
54 let y: usize = 1.wrapping_neg();
64 "comparisons made useless by limits of the types involved"
70 "literal out of range for its type"
76 "shift exceeds the type's number of bits"
79 #[derive(Copy, Clone)]
80 pub struct TypeLimits
{
81 /// Id of the last visited negated expression
82 negated_expr_id
: ast
::NodeId
,
86 pub fn new() -> TypeLimits
{
93 impl LintPass
for TypeLimits
{
94 fn get_lints(&self) -> LintArray
{
95 lint_array
!(UNUSED_COMPARISONS
, OVERFLOWING_LITERALS
, EXCEEDING_BITSHIFTS
)
99 impl LateLintPass
for TypeLimits
{
100 fn check_expr(&mut self, cx
: &LateContext
, e
: &hir
::Expr
) {
102 hir
::ExprUnary(hir
::UnNeg
, ref expr
) => {
103 if let hir
::ExprLit(ref lit
) = expr
.node
{
105 ast
::LitKind
::Int(_
, ast
::LitIntType
::Unsigned(_
)) => {
106 forbid_unsigned_negation(cx
, e
.span
);
108 ast
::LitKind
::Int(_
, ast
::LitIntType
::Unsuffixed
) => {
109 if let ty
::TyUint(_
) = cx
.tcx
.node_id_to_type(e
.id
).sty
{
110 forbid_unsigned_negation(cx
, e
.span
);
116 let t
= cx
.tcx
.node_id_to_type(expr
.id
);
117 if let ty
::TyUint(_
) = t
.sty
{
118 forbid_unsigned_negation(cx
, e
.span
);
121 // propagate negation, if the negation itself isn't negated
122 if self.negated_expr_id
!= e
.id
{
123 self.negated_expr_id
= expr
.id
;
126 hir
::ExprBinary(binop
, ref l
, ref r
) => {
127 if is_comparison(binop
) && !check_limits(cx
.tcx
, binop
, &l
, &r
) {
128 cx
.span_lint(UNUSED_COMPARISONS
, e
.span
,
129 "comparison is useless due to type limits");
132 if binop
.node
.is_shift() {
133 let opt_ty_bits
= match cx
.tcx
.node_id_to_type(l
.id
).sty
{
134 ty
::TyInt(t
) => Some(int_ty_bits(t
, cx
.sess().target
.int_type
)),
135 ty
::TyUint(t
) => Some(uint_ty_bits(t
, cx
.sess().target
.uint_type
)),
139 if let Some(bits
) = opt_ty_bits
{
140 let exceeding
= if let hir
::ExprLit(ref lit
) = r
.node
{
141 if let ast
::LitKind
::Int(shift
, _
) = lit
.node { shift >= bits }
144 match eval_const_expr_partial(cx
.tcx
, &r
, ExprTypeChecked
, None
) {
145 Ok(ConstVal
::Integral(i
)) => {
146 i
.is_negative() || i
.to_u64()
154 cx
.span_lint(EXCEEDING_BITSHIFTS
, e
.span
,
155 "bitshift exceeds the type's number of bits");
160 hir
::ExprLit(ref lit
) => {
161 match cx
.tcx
.node_id_to_type(e
.id
).sty
{
164 ast
::LitKind
::Int(v
, ast
::LitIntType
::Signed(_
)) |
165 ast
::LitKind
::Int(v
, ast
::LitIntType
::Unsuffixed
) => {
166 let int_type
= if let ast
::IntTy
::Is
= t
{
167 cx
.sess().target
.int_type
171 let (_
, max
) = int_ty_range(int_type
);
172 let negative
= self.negated_expr_id
== e
.id
;
174 // Detect literal value out of range [min, max] inclusive
175 // avoiding use of -min to prevent overflow/panic
176 if (negative
&& v
> max
as u64 + 1) ||
177 (!negative
&& v
> max
as u64) {
178 cx
.span_lint(OVERFLOWING_LITERALS
, e
.span
,
179 &format
!("literal out of range for {:?}", t
));
187 let uint_type
= if let ast
::UintTy
::Us
= t
{
188 cx
.sess().target
.uint_type
192 let (min
, max
) = uint_ty_range(uint_type
);
193 let lit_val
: u64 = match lit
.node
{
194 // _v is u8, within range by definition
195 ast
::LitKind
::Byte(_v
) => return,
196 ast
::LitKind
::Int(v
, _
) => v
,
199 if lit_val
< min
|| lit_val
> max
{
200 cx
.span_lint(OVERFLOWING_LITERALS
, e
.span
,
201 &format
!("literal out of range for {:?}", t
));
205 let (min
, max
) = float_ty_range(t
);
206 let lit_val
: f64 = match lit
.node
{
207 ast
::LitKind
::Float(ref v
, _
) |
208 ast
::LitKind
::FloatUnsuffixed(ref v
) => {
216 if lit_val
< min
|| lit_val
> max
{
217 cx
.span_lint(OVERFLOWING_LITERALS
, e
.span
,
218 &format
!("literal out of range for {:?}", t
));
227 fn is_valid
<T
:cmp
::PartialOrd
>(binop
: hir
::BinOp
, v
: T
,
228 min
: T
, max
: T
) -> bool
{
230 hir
::BiLt
=> v
> min
&& v
<= max
,
231 hir
::BiLe
=> v
>= min
&& v
< max
,
232 hir
::BiGt
=> v
>= min
&& v
< max
,
233 hir
::BiGe
=> v
> min
&& v
<= max
,
234 hir
::BiEq
| hir
::BiNe
=> v
>= min
&& v
<= max
,
239 fn rev_binop(binop
: hir
::BinOp
) -> hir
::BinOp
{
240 codemap
::respan(binop
.span
, match binop
.node
{
241 hir
::BiLt
=> hir
::BiGt
,
242 hir
::BiLe
=> hir
::BiGe
,
243 hir
::BiGt
=> hir
::BiLt
,
244 hir
::BiGe
=> hir
::BiLe
,
249 // for isize & usize, be conservative with the warnings, so that the
250 // warnings are consistent between 32- and 64-bit platforms
251 fn int_ty_range(int_ty
: ast
::IntTy
) -> (i64, i64) {
253 ast
::IntTy
::Is
=> (i64::MIN
, i64::MAX
),
254 ast
::IntTy
::I8
=> (i8::MIN
as i64, i8::MAX
as i64),
255 ast
::IntTy
::I16
=> (i16::MIN
as i64, i16::MAX
as i64),
256 ast
::IntTy
::I32
=> (i32::MIN
as i64, i32::MAX
as i64),
257 ast
::IntTy
::I64
=> (i64::MIN
, i64::MAX
)
261 fn uint_ty_range(uint_ty
: ast
::UintTy
) -> (u64, u64) {
263 ast
::UintTy
::Us
=> (u64::MIN
, u64::MAX
),
264 ast
::UintTy
::U8
=> (u8::MIN
as u64, u8::MAX
as u64),
265 ast
::UintTy
::U16
=> (u16::MIN
as u64, u16::MAX
as u64),
266 ast
::UintTy
::U32
=> (u32::MIN
as u64, u32::MAX
as u64),
267 ast
::UintTy
::U64
=> (u64::MIN
, u64::MAX
)
271 fn float_ty_range(float_ty
: ast
::FloatTy
) -> (f64, f64) {
273 ast
::FloatTy
::F32
=> (f32::MIN
as f64, f32::MAX
as f64),
274 ast
::FloatTy
::F64
=> (f64::MIN
, f64::MAX
)
278 fn int_ty_bits(int_ty
: ast
::IntTy
, target_int_ty
: ast
::IntTy
) -> u64 {
280 ast
::IntTy
::Is
=> int_ty_bits(target_int_ty
, target_int_ty
),
282 ast
::IntTy
::I16
=> 16 as u64,
283 ast
::IntTy
::I32
=> 32,
284 ast
::IntTy
::I64
=> 64,
288 fn uint_ty_bits(uint_ty
: ast
::UintTy
, target_uint_ty
: ast
::UintTy
) -> u64 {
290 ast
::UintTy
::Us
=> uint_ty_bits(target_uint_ty
, target_uint_ty
),
291 ast
::UintTy
::U8
=> 8,
292 ast
::UintTy
::U16
=> 16,
293 ast
::UintTy
::U32
=> 32,
294 ast
::UintTy
::U64
=> 64,
298 fn check_limits(tcx
: &TyCtxt
, binop
: hir
::BinOp
,
299 l
: &hir
::Expr
, r
: &hir
::Expr
) -> bool
{
300 let (lit
, expr
, swap
) = match (&l
.node
, &r
.node
) {
301 (&hir
::ExprLit(_
), _
) => (l
, r
, true),
302 (_
, &hir
::ExprLit(_
)) => (r
, l
, false),
305 // Normalize the binop so that the literal is always on the RHS in
307 let norm_binop
= if swap
{
312 match tcx
.node_id_to_type(expr
.id
).sty
{
313 ty
::TyInt(int_ty
) => {
314 let (min
, max
) = int_ty_range(int_ty
);
315 let lit_val
: i64 = match lit
.node
{
316 hir
::ExprLit(ref li
) => match li
.node
{
317 ast
::LitKind
::Int(v
, ast
::LitIntType
::Signed(_
)) |
318 ast
::LitKind
::Int(v
, ast
::LitIntType
::Unsuffixed
) => v
as i64,
323 is_valid(norm_binop
, lit_val
, min
, max
)
325 ty
::TyUint(uint_ty
) => {
326 let (min
, max
): (u64, u64) = uint_ty_range(uint_ty
);
327 let lit_val
: u64 = match lit
.node
{
328 hir
::ExprLit(ref li
) => match li
.node
{
329 ast
::LitKind
::Int(v
, _
) => v
,
334 is_valid(norm_binop
, lit_val
, min
, max
)
340 fn is_comparison(binop
: hir
::BinOp
) -> bool
{
342 hir
::BiEq
| hir
::BiLt
| hir
::BiLe
|
343 hir
::BiNe
| hir
::BiGe
| hir
::BiGt
=> true,
348 fn forbid_unsigned_negation(cx
: &LateContext
, span
: Span
) {
350 .struct_span_err_with_code(span
, "unary negation of unsigned integer", "E0519")
351 .span_help(span
, "use a cast or the `!` operator")
360 "proper use of libc types in foreign modules"
363 struct ImproperCTypesVisitor
<'a
, 'tcx
: 'a
> {
364 cx
: &'a LateContext
<'a
, 'tcx
>
369 FfiUnsafe(&'
static str),
370 FfiBadStruct(DefId
, &'
static str),
371 FfiBadEnum(DefId
, &'
static str)
374 /// Check if this enum can be safely exported based on the
375 /// "nullable pointer optimization". Currently restricted
376 /// to function pointers and references, but could be
377 /// expanded to cover NonZero raw pointers and newtypes.
378 /// FIXME: This duplicates code in trans.
379 fn is_repr_nullable_ptr
<'tcx
>(tcx
: &TyCtxt
<'tcx
>,
380 def
: ty
::AdtDef
<'tcx
>,
381 substs
: &Substs
<'tcx
>)
383 if def
.variants
.len() == 2 {
386 if def
.variants
[0].fields
.is_empty() {
388 } else if def
.variants
[1].fields
.is_empty() {
394 if def
.variants
[data_idx
].fields
.len() == 1 {
395 match def
.variants
[data_idx
].fields
[0].ty(tcx
, substs
).sty
{
396 ty
::TyFnPtr(_
) => { return true; }
397 ty
::TyRef(..) => { return true; }
405 impl<'a
, 'tcx
> ImproperCTypesVisitor
<'a
, 'tcx
> {
406 /// Check if the given type is "ffi-safe" (has a stable, well-defined
407 /// representation which can be exported to C code).
408 fn check_type_for_ffi(&self,
409 cache
: &mut FnvHashSet
<Ty
<'tcx
>>,
412 use self::FfiResult
::*;
413 let cx
= &self.cx
.tcx
;
415 // Protect against infinite recursion, for example
416 // `struct S(*mut S);`.
417 // FIXME: A recursion limit is necessary as well, for irregular
419 if !cache
.insert(ty
) {
424 ty
::TyStruct(def
, substs
) => {
425 if !cx
.lookup_repr_hints(def
.did
).contains(&attr
::ReprExtern
) {
427 "found struct without foreign-function-safe \
428 representation annotation in foreign module, \
429 consider adding a #[repr(C)] attribute to \
433 // We can't completely trust repr(C) markings; make sure the
434 // fields are actually safe.
435 if def
.struct_variant().fields
.is_empty() {
437 "found zero-size struct in foreign module, consider \
438 adding a member to this struct");
441 for field
in &def
.struct_variant().fields
{
442 let field_ty
= infer
::normalize_associated_type(cx
, &field
.ty(cx
, substs
));
443 let r
= self.check_type_for_ffi(cache
, field_ty
);
446 FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
447 FfiUnsafe(s
) => { return FfiBadStruct(def.did, s); }
452 ty
::TyEnum(def
, substs
) => {
453 if def
.variants
.is_empty() {
454 // Empty enums are okay... although sort of useless.
458 // Check for a repr() attribute to specify the size of the
460 let repr_hints
= cx
.lookup_repr_hints(def
.did
);
461 match &**repr_hints
{
463 // Special-case types like `Option<extern fn()>`.
464 if !is_repr_nullable_ptr(cx
, def
, substs
) {
466 "found enum without foreign-function-safe \
467 representation annotation in foreign module, \
468 consider adding a #[repr(...)] attribute to \
473 if !hint
.is_ffi_safe() {
474 // FIXME: This shouldn't be reachable: we should check
477 "enum has unexpected #[repr(...)] attribute")
480 // Enum with an explicitly sized discriminant; either
481 // a C-style enum or a discriminated union.
483 // The layout of enum variants is implicitly repr(C).
484 // FIXME: Is that correct?
487 // FIXME: This shouldn't be reachable: we should check
490 "enum has too many #[repr(...)] attributes");
494 // Check the contained variants.
495 for variant
in &def
.variants
{
496 for field
in &variant
.fields
{
497 let arg
= infer
::normalize_associated_type(cx
, &field
.ty(cx
, substs
));
498 let r
= self.check_type_for_ffi(cache
, arg
);
501 FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
502 FfiUnsafe(s
) => { return FfiBadEnum(def.did, s); }
510 FfiUnsafe("found Rust type `char` in foreign module, while \
511 `u32` or `libc::wchar_t` should be used")
514 // Primitive types with a stable representation.
515 ty
::TyBool
| ty
::TyInt(..) | ty
::TyUint(..) |
516 ty
::TyFloat(..) => FfiSafe
,
519 FfiUnsafe("found Rust type Box<_> in foreign module, \
520 consider using a raw pointer instead")
524 FfiUnsafe("found Rust slice type in foreign module, \
525 consider using a raw pointer instead")
529 FfiUnsafe("found Rust trait type in foreign module, \
530 consider using a raw pointer instead")
534 FfiUnsafe("found Rust type `str` in foreign module; \
535 consider using a `*const libc::c_char`")
539 FfiUnsafe("found Rust tuple type in foreign module; \
540 consider using a struct instead`")
543 ty
::TyRawPtr(ref m
) | ty
::TyRef(_
, ref m
) => {
544 self.check_type_for_ffi(cache
, m
.ty
)
547 ty
::TyArray(ty
, _
) => {
548 self.check_type_for_ffi(cache
, ty
)
551 ty
::TyFnPtr(bare_fn
) => {
555 Abi
::PlatformIntrinsic
|
558 "found function pointer with Rust calling \
559 convention in foreign module; consider using an \
560 `extern` function pointer")
565 let sig
= cx
.erase_late_bound_regions(&bare_fn
.sig
);
567 ty
::FnDiverging
=> {}
568 ty
::FnConverging(output
) => {
569 if !output
.is_nil() {
570 let r
= self.check_type_for_ffi(cache
, output
);
578 for arg
in sig
.inputs
{
579 let r
= self.check_type_for_ffi(cache
, arg
);
588 ty
::TyParam(..) | ty
::TyInfer(..) | ty
::TyError
|
589 ty
::TyClosure(..) | ty
::TyProjection(..) |
591 bug
!("Unexpected type in foreign function")
596 fn check_type_for_ffi_and_report_errors(&mut self, sp
: Span
, ty
: Ty
<'tcx
>) {
597 // it is only OK to use this function because extern fns cannot have
598 // any generic types right now:
599 let ty
= infer
::normalize_associated_type(self.cx
.tcx
, &ty
);
601 match self.check_type_for_ffi(&mut FnvHashSet(), ty
) {
602 FfiResult
::FfiSafe
=> {}
603 FfiResult
::FfiUnsafe(s
) => {
604 self.cx
.span_lint(IMPROPER_CTYPES
, sp
, s
);
606 FfiResult
::FfiBadStruct(_
, s
) => {
607 // FIXME: This diagnostic is difficult to read, and doesn't
608 // point at the relevant field.
609 self.cx
.span_lint(IMPROPER_CTYPES
, sp
,
610 &format
!("found non-foreign-function-safe member in \
611 struct marked #[repr(C)]: {}", s
));
613 FfiResult
::FfiBadEnum(_
, s
) => {
614 // FIXME: This diagnostic is difficult to read, and doesn't
615 // point at the relevant variant.
616 self.cx
.span_lint(IMPROPER_CTYPES
, sp
,
617 &format
!("found non-foreign-function-safe member in \
623 fn check_foreign_fn(&mut self, id
: ast
::NodeId
, decl
: &hir
::FnDecl
) {
624 let def_id
= self.cx
.tcx
.map
.local_def_id(id
);
625 let scheme
= self.cx
.tcx
.lookup_item_type(def_id
);
626 let sig
= scheme
.ty
.fn_sig();
627 let sig
= self.cx
.tcx
.erase_late_bound_regions(&sig
);
629 for (&input_ty
, input_hir
) in sig
.inputs
.iter().zip(&decl
.inputs
) {
630 self.check_type_for_ffi_and_report_errors(input_hir
.ty
.span
, &input_ty
);
633 if let hir
::Return(ref ret_hir
) = decl
.output
{
634 let ret_ty
= sig
.output
.unwrap();
635 if !ret_ty
.is_nil() {
636 self.check_type_for_ffi_and_report_errors(ret_hir
.span
, ret_ty
);
641 fn check_foreign_static(&mut self, id
: ast
::NodeId
, span
: Span
) {
642 let def_id
= self.cx
.tcx
.map
.local_def_id(id
);
643 let scheme
= self.cx
.tcx
.lookup_item_type(def_id
);
644 self.check_type_for_ffi_and_report_errors(span
, scheme
.ty
);
648 #[derive(Copy, Clone)]
649 pub struct ImproperCTypes
;
651 impl LintPass
for ImproperCTypes
{
652 fn get_lints(&self) -> LintArray
{
653 lint_array
!(IMPROPER_CTYPES
)
657 impl LateLintPass
for ImproperCTypes
{
658 fn check_item(&mut self, cx
: &LateContext
, it
: &hir
::Item
) {
659 let mut vis
= ImproperCTypesVisitor { cx: cx }
;
660 if let hir
::ItemForeignMod(ref nmod
) = it
.node
{
661 if nmod
.abi
!= Abi
::RustIntrinsic
&& nmod
.abi
!= Abi
::PlatformIntrinsic
{
662 for ni
in &nmod
.items
{
664 hir
::ForeignItemFn(ref decl
, _
) => {
665 vis
.check_foreign_fn(ni
.id
, decl
);
667 hir
::ForeignItemStatic(ref ty
, _
) => {
668 vis
.check_foreign_static(ni
.id
, ty
.span
);