]> git.proxmox.com Git - rustc.git/blob - src/librustc_lint/types.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / librustc_lint / types.rs
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.
4 //
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.
10
11 #![allow(non_snake_case)]
12
13 use rustc::hir::def_id::DefId;
14 use rustc::infer;
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};
23
24 use std::cmp;
25 use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
26
27 use syntax::ast;
28 use syntax::abi::Abi;
29 use syntax::attr::{self, AttrMetaMethods};
30 use syntax::codemap::{self, Span};
31
32 use rustc::hir;
33
34 register_long_diagnostics! {
35 E0519: r##"
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.
39
40 ```
41 let x: usize = -1isize as usize;
42 let y: usize = !0;
43 assert_eq!(x, y);
44 ```
45
46 Alternatively you can use the `Wrapping` newtype
47 or the `wrapping_neg` operation that all
48 integral types support:
49
50 ```
51 use std::num::Wrapping;
52 let x: Wrapping<usize> = -Wrapping(1);
53 let Wrapping(x) = x;
54 let y: usize = 1.wrapping_neg();
55 assert_eq!(x, y);
56 ```
57
58 "##
59 }
60
61 declare_lint! {
62 UNUSED_COMPARISONS,
63 Warn,
64 "comparisons made useless by limits of the types involved"
65 }
66
67 declare_lint! {
68 OVERFLOWING_LITERALS,
69 Warn,
70 "literal out of range for its type"
71 }
72
73 declare_lint! {
74 EXCEEDING_BITSHIFTS,
75 Deny,
76 "shift exceeds the type's number of bits"
77 }
78
79 #[derive(Copy, Clone)]
80 pub struct TypeLimits {
81 /// Id of the last visited negated expression
82 negated_expr_id: ast::NodeId,
83 }
84
85 impl TypeLimits {
86 pub fn new() -> TypeLimits {
87 TypeLimits {
88 negated_expr_id: !0,
89 }
90 }
91 }
92
93 impl LintPass for TypeLimits {
94 fn get_lints(&self) -> LintArray {
95 lint_array!(UNUSED_COMPARISONS, OVERFLOWING_LITERALS, EXCEEDING_BITSHIFTS)
96 }
97 }
98
99 impl LateLintPass for TypeLimits {
100 fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
101 match e.node {
102 hir::ExprUnary(hir::UnNeg, ref expr) => {
103 if let hir::ExprLit(ref lit) = expr.node {
104 match lit.node {
105 ast::LitKind::Int(_, ast::LitIntType::Unsigned(_)) => {
106 forbid_unsigned_negation(cx, e.span);
107 },
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);
111 }
112 },
113 _ => ()
114 }
115 } else {
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);
119 }
120 }
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;
124 }
125 },
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");
130 }
131
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)),
136 _ => None
137 };
138
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 }
142 else { false }
143 } else {
144 match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked, None) {
145 Ok(ConstVal::Integral(i)) => {
146 i.is_negative() || i.to_u64()
147 .map(|i| i >= bits)
148 .unwrap_or(true)
149 },
150 _ => { false }
151 }
152 };
153 if exceeding {
154 cx.span_lint(EXCEEDING_BITSHIFTS, e.span,
155 "bitshift exceeds the type's number of bits");
156 }
157 };
158 }
159 },
160 hir::ExprLit(ref lit) => {
161 match cx.tcx.node_id_to_type(e.id).sty {
162 ty::TyInt(t) => {
163 match lit.node {
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
168 } else {
169 t
170 };
171 let (_, max) = int_ty_range(int_type);
172 let negative = self.negated_expr_id == e.id;
173
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));
180 return;
181 }
182 }
183 _ => bug!()
184 };
185 },
186 ty::TyUint(t) => {
187 let uint_type = if let ast::UintTy::Us = t {
188 cx.sess().target.uint_type
189 } else {
190 t
191 };
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,
197 _ => bug!()
198 };
199 if lit_val < min || lit_val > max {
200 cx.span_lint(OVERFLOWING_LITERALS, e.span,
201 &format!("literal out of range for {:?}", t));
202 }
203 },
204 ty::TyFloat(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) => {
209 match v.parse() {
210 Ok(f) => f,
211 Err(_) => return
212 }
213 }
214 _ => bug!()
215 };
216 if lit_val < min || lit_val > max {
217 cx.span_lint(OVERFLOWING_LITERALS, e.span,
218 &format!("literal out of range for {:?}", t));
219 }
220 },
221 _ => ()
222 };
223 },
224 _ => ()
225 };
226
227 fn is_valid<T:cmp::PartialOrd>(binop: hir::BinOp, v: T,
228 min: T, max: T) -> bool {
229 match binop.node {
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,
235 _ => bug!()
236 }
237 }
238
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,
245 _ => return binop
246 })
247 }
248
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) {
252 match int_ty {
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)
258 }
259 }
260
261 fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
262 match uint_ty {
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)
268 }
269 }
270
271 fn float_ty_range(float_ty: ast::FloatTy) -> (f64, f64) {
272 match float_ty {
273 ast::FloatTy::F32 => (f32::MIN as f64, f32::MAX as f64),
274 ast::FloatTy::F64 => (f64::MIN, f64::MAX)
275 }
276 }
277
278 fn int_ty_bits(int_ty: ast::IntTy, target_int_ty: ast::IntTy) -> u64 {
279 match int_ty {
280 ast::IntTy::Is => int_ty_bits(target_int_ty, target_int_ty),
281 ast::IntTy::I8 => 8,
282 ast::IntTy::I16 => 16 as u64,
283 ast::IntTy::I32 => 32,
284 ast::IntTy::I64 => 64,
285 }
286 }
287
288 fn uint_ty_bits(uint_ty: ast::UintTy, target_uint_ty: ast::UintTy) -> u64 {
289 match uint_ty {
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,
295 }
296 }
297
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),
303 _ => return true
304 };
305 // Normalize the binop so that the literal is always on the RHS in
306 // the comparison
307 let norm_binop = if swap {
308 rev_binop(binop)
309 } else {
310 binop
311 };
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,
319 _ => return true
320 },
321 _ => bug!()
322 };
323 is_valid(norm_binop, lit_val, min, max)
324 }
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,
330 _ => return true
331 },
332 _ => bug!()
333 };
334 is_valid(norm_binop, lit_val, min, max)
335 }
336 _ => true
337 }
338 }
339
340 fn is_comparison(binop: hir::BinOp) -> bool {
341 match binop.node {
342 hir::BiEq | hir::BiLt | hir::BiLe |
343 hir::BiNe | hir::BiGe | hir::BiGt => true,
344 _ => false
345 }
346 }
347
348 fn forbid_unsigned_negation(cx: &LateContext, span: Span) {
349 cx.sess()
350 .struct_span_err_with_code(span, "unary negation of unsigned integer", "E0519")
351 .span_help(span, "use a cast or the `!` operator")
352 .emit();
353 }
354 }
355 }
356
357 declare_lint! {
358 IMPROPER_CTYPES,
359 Warn,
360 "proper use of libc types in foreign modules"
361 }
362
363 struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
364 cx: &'a LateContext<'a, 'tcx>
365 }
366
367 enum FfiResult {
368 FfiSafe,
369 FfiUnsafe(&'static str),
370 FfiBadStruct(DefId, &'static str),
371 FfiBadEnum(DefId, &'static str)
372 }
373
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>)
382 -> bool {
383 if def.variants.len() == 2 {
384 let data_idx;
385
386 if def.variants[0].fields.is_empty() {
387 data_idx = 1;
388 } else if def.variants[1].fields.is_empty() {
389 data_idx = 0;
390 } else {
391 return false;
392 }
393
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; }
398 _ => { }
399 }
400 }
401 }
402 false
403 }
404
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>>,
410 ty: Ty<'tcx>)
411 -> FfiResult {
412 use self::FfiResult::*;
413 let cx = &self.cx.tcx;
414
415 // Protect against infinite recursion, for example
416 // `struct S(*mut S);`.
417 // FIXME: A recursion limit is necessary as well, for irregular
418 // recusive types.
419 if !cache.insert(ty) {
420 return FfiSafe;
421 }
422
423 match ty.sty {
424 ty::TyStruct(def, substs) => {
425 if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
426 return FfiUnsafe(
427 "found struct without foreign-function-safe \
428 representation annotation in foreign module, \
429 consider adding a #[repr(C)] attribute to \
430 the type");
431 }
432
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() {
436 return FfiUnsafe(
437 "found zero-size struct in foreign module, consider \
438 adding a member to this struct");
439 }
440
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);
444 match r {
445 FfiSafe => {}
446 FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
447 FfiUnsafe(s) => { return FfiBadStruct(def.did, s); }
448 }
449 }
450 FfiSafe
451 }
452 ty::TyEnum(def, substs) => {
453 if def.variants.is_empty() {
454 // Empty enums are okay... although sort of useless.
455 return FfiSafe
456 }
457
458 // Check for a repr() attribute to specify the size of the
459 // discriminant.
460 let repr_hints = cx.lookup_repr_hints(def.did);
461 match &**repr_hints {
462 [] => {
463 // Special-case types like `Option<extern fn()>`.
464 if !is_repr_nullable_ptr(cx, def, substs) {
465 return FfiUnsafe(
466 "found enum without foreign-function-safe \
467 representation annotation in foreign module, \
468 consider adding a #[repr(...)] attribute to \
469 the type")
470 }
471 }
472 [ref hint] => {
473 if !hint.is_ffi_safe() {
474 // FIXME: This shouldn't be reachable: we should check
475 // this earlier.
476 return FfiUnsafe(
477 "enum has unexpected #[repr(...)] attribute")
478 }
479
480 // Enum with an explicitly sized discriminant; either
481 // a C-style enum or a discriminated union.
482
483 // The layout of enum variants is implicitly repr(C).
484 // FIXME: Is that correct?
485 }
486 _ => {
487 // FIXME: This shouldn't be reachable: we should check
488 // this earlier.
489 return FfiUnsafe(
490 "enum has too many #[repr(...)] attributes");
491 }
492 }
493
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);
499 match r {
500 FfiSafe => {}
501 FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
502 FfiUnsafe(s) => { return FfiBadEnum(def.did, s); }
503 }
504 }
505 }
506 FfiSafe
507 }
508
509 ty::TyChar => {
510 FfiUnsafe("found Rust type `char` in foreign module, while \
511 `u32` or `libc::wchar_t` should be used")
512 }
513
514 // Primitive types with a stable representation.
515 ty::TyBool | ty::TyInt(..) | ty::TyUint(..) |
516 ty::TyFloat(..) => FfiSafe,
517
518 ty::TyBox(..) => {
519 FfiUnsafe("found Rust type Box<_> in foreign module, \
520 consider using a raw pointer instead")
521 }
522
523 ty::TySlice(_) => {
524 FfiUnsafe("found Rust slice type in foreign module, \
525 consider using a raw pointer instead")
526 }
527
528 ty::TyTrait(..) => {
529 FfiUnsafe("found Rust trait type in foreign module, \
530 consider using a raw pointer instead")
531 }
532
533 ty::TyStr => {
534 FfiUnsafe("found Rust type `str` in foreign module; \
535 consider using a `*const libc::c_char`")
536 }
537
538 ty::TyTuple(_) => {
539 FfiUnsafe("found Rust tuple type in foreign module; \
540 consider using a struct instead`")
541 }
542
543 ty::TyRawPtr(ref m) | ty::TyRef(_, ref m) => {
544 self.check_type_for_ffi(cache, m.ty)
545 }
546
547 ty::TyArray(ty, _) => {
548 self.check_type_for_ffi(cache, ty)
549 }
550
551 ty::TyFnPtr(bare_fn) => {
552 match bare_fn.abi {
553 Abi::Rust |
554 Abi::RustIntrinsic |
555 Abi::PlatformIntrinsic |
556 Abi::RustCall => {
557 return FfiUnsafe(
558 "found function pointer with Rust calling \
559 convention in foreign module; consider using an \
560 `extern` function pointer")
561 }
562 _ => {}
563 }
564
565 let sig = cx.erase_late_bound_regions(&bare_fn.sig);
566 match sig.output {
567 ty::FnDiverging => {}
568 ty::FnConverging(output) => {
569 if !output.is_nil() {
570 let r = self.check_type_for_ffi(cache, output);
571 match r {
572 FfiSafe => {}
573 _ => { return r; }
574 }
575 }
576 }
577 }
578 for arg in sig.inputs {
579 let r = self.check_type_for_ffi(cache, arg);
580 match r {
581 FfiSafe => {}
582 _ => { return r; }
583 }
584 }
585 FfiSafe
586 }
587
588 ty::TyParam(..) | ty::TyInfer(..) | ty::TyError |
589 ty::TyClosure(..) | ty::TyProjection(..) |
590 ty::TyFnDef(..) => {
591 bug!("Unexpected type in foreign function")
592 }
593 }
594 }
595
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);
600
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);
605 }
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));
612 }
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 \
618 enum: {}", s));
619 }
620 }
621 }
622
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);
628
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);
631 }
632
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);
637 }
638 }
639 }
640
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);
645 }
646 }
647
648 #[derive(Copy, Clone)]
649 pub struct ImproperCTypes;
650
651 impl LintPass for ImproperCTypes {
652 fn get_lints(&self) -> LintArray {
653 lint_array!(IMPROPER_CTYPES)
654 }
655 }
656
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 {
663 match ni.node {
664 hir::ForeignItemFn(ref decl, _) => {
665 vis.check_foreign_fn(ni.id, decl);
666 }
667 hir::ForeignItemStatic(ref ty, _) => {
668 vis.check_foreign_static(ni.id, ty.span);
669 }
670 }
671 }
672 }
673 }
674 }
675 }