]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/types.rs
Deny the `overflowing_literals` lint for all editions
[rust.git] / src / librustc_lint / types.rs
1 #![allow(non_snake_case)]
2
3 use rustc::hir::Node;
4 use rustc::ty::subst::Substs;
5 use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
6 use rustc::ty::layout::{self, IntegerExt, LayoutOf, VariantIdx};
7 use rustc_data_structures::indexed_vec::Idx;
8 use util::nodemap::FxHashSet;
9 use lint::{LateContext, LintContext, LintArray};
10 use lint::{LintPass, LateLintPass};
11
12 use std::cmp;
13 use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
14
15 use syntax::{ast, attr};
16 use syntax::errors::Applicability;
17 use rustc_target::spec::abi::Abi;
18 use syntax_pos::Span;
19 use syntax::source_map;
20
21 use rustc::hir;
22
23 declare_lint! {
24     UNUSED_COMPARISONS,
25     Warn,
26     "comparisons made useless by limits of the types involved"
27 }
28
29 declare_lint! {
30     OVERFLOWING_LITERALS,
31     Deny,
32     "literal out of range for its type"
33 }
34
35 declare_lint! {
36     VARIANT_SIZE_DIFFERENCES,
37     Allow,
38     "detects enums with widely varying variant sizes"
39 }
40
41 #[derive(Copy, Clone)]
42 pub struct TypeLimits {
43     /// Id of the last visited negated expression
44     negated_expr_id: ast::NodeId,
45 }
46
47 impl TypeLimits {
48     pub fn new() -> TypeLimits {
49         TypeLimits { negated_expr_id: ast::DUMMY_NODE_ID }
50     }
51 }
52
53 impl LintPass for TypeLimits {
54     fn get_lints(&self) -> LintArray {
55         lint_array!(UNUSED_COMPARISONS,
56                     OVERFLOWING_LITERALS)
57     }
58 }
59
60 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
61     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) {
62         match e.node {
63             hir::ExprKind::Unary(hir::UnNeg, ref expr) => {
64                 // propagate negation, if the negation itself isn't negated
65                 if self.negated_expr_id != e.id {
66                     self.negated_expr_id = expr.id;
67                 }
68             }
69             hir::ExprKind::Binary(binop, ref l, ref r) => {
70                 if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
71                     cx.span_lint(UNUSED_COMPARISONS,
72                                  e.span,
73                                  "comparison is useless due to type limits");
74                 }
75             }
76             hir::ExprKind::Lit(ref lit) => {
77                 match cx.tables.node_id_to_type(e.hir_id).sty {
78                     ty::Int(t) => {
79                         match lit.node {
80                             ast::LitKind::Int(v, ast::LitIntType::Signed(_)) |
81                             ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => {
82                                 let int_type = if let ast::IntTy::Isize = t {
83                                     cx.sess().target.isize_ty
84                                 } else {
85                                     t
86                                 };
87                                 let (_, max) = int_ty_range(int_type);
88                                 let max = max as u128;
89                                 let negative = self.negated_expr_id == e.id;
90
91                                 // Detect literal value out of range [min, max] inclusive
92                                 // avoiding use of -min to prevent overflow/panic
93                                 if (negative && v > max + 1) || (!negative && v > max) {
94                                     if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
95                                         report_bin_hex_error(
96                                             cx,
97                                             e,
98                                             ty::Int(t),
99                                             repr_str,
100                                             v,
101                                             negative,
102                                         );
103                                         return;
104                                     }
105                                     cx.span_lint(
106                                         OVERFLOWING_LITERALS,
107                                         e.span,
108                                         &format!("literal out of range for {:?}", t),
109                                     );
110                                     return;
111                                 }
112                             }
113                             _ => bug!(),
114                         };
115                     }
116                     ty::Uint(t) => {
117                         let uint_type = if let ast::UintTy::Usize = t {
118                             cx.sess().target.usize_ty
119                         } else {
120                             t
121                         };
122                         let (min, max) = uint_ty_range(uint_type);
123                         let lit_val: u128 = match lit.node {
124                             // _v is u8, within range by definition
125                             ast::LitKind::Byte(_v) => return,
126                             ast::LitKind::Int(v, _) => v,
127                             _ => bug!(),
128                         };
129                         if lit_val < min || lit_val > max {
130                             let parent_id = cx.tcx.hir().get_parent_node(e.id);
131                             if let Node::Expr(parent_expr) = cx.tcx.hir().get(parent_id) {
132                                 if let hir::ExprKind::Cast(..) = parent_expr.node {
133                                     if let ty::Char = cx.tables.expr_ty(parent_expr).sty {
134                                         let mut err = cx.struct_span_lint(
135                                                              OVERFLOWING_LITERALS,
136                                                              parent_expr.span,
137                                                              "only u8 can be cast into char");
138                                         err.span_suggestion_with_applicability(
139                                             parent_expr.span,
140                                             &"use a char literal instead",
141                                             format!("'\\u{{{:X}}}'", lit_val),
142                                             Applicability::MachineApplicable
143                                         );
144                                         err.emit();
145                                         return
146                                     }
147                                 }
148                             }
149                             if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
150                                 report_bin_hex_error(
151                                     cx,
152                                     e,
153                                     ty::Uint(t),
154                                     repr_str,
155                                     lit_val,
156                                     false,
157                                 );
158                                 return;
159                             }
160                             cx.span_lint(
161                                 OVERFLOWING_LITERALS,
162                                 e.span,
163                                 &format!("literal out of range for {:?}", t),
164                             );
165                         }
166                     }
167                     ty::Float(t) => {
168                         let is_infinite = match lit.node {
169                             ast::LitKind::Float(v, _) |
170                             ast::LitKind::FloatUnsuffixed(v) => {
171                                 match t {
172                                     ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite),
173                                     ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite),
174                                 }
175                             }
176                             _ => bug!(),
177                         };
178                         if is_infinite == Ok(true) {
179                             cx.span_lint(OVERFLOWING_LITERALS,
180                                          e.span,
181                                          &format!("literal out of range for {:?}", t));
182                         }
183                     }
184                     _ => (),
185                 };
186             }
187             _ => (),
188         };
189
190         fn is_valid<T: cmp::PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
191             match binop.node {
192                 hir::BinOpKind::Lt => v > min && v <= max,
193                 hir::BinOpKind::Le => v >= min && v < max,
194                 hir::BinOpKind::Gt => v >= min && v < max,
195                 hir::BinOpKind::Ge => v > min && v <= max,
196                 hir::BinOpKind::Eq | hir::BinOpKind::Ne => v >= min && v <= max,
197                 _ => bug!(),
198             }
199         }
200
201         fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
202             source_map::respan(binop.span,
203                             match binop.node {
204                                 hir::BinOpKind::Lt => hir::BinOpKind::Gt,
205                                 hir::BinOpKind::Le => hir::BinOpKind::Ge,
206                                 hir::BinOpKind::Gt => hir::BinOpKind::Lt,
207                                 hir::BinOpKind::Ge => hir::BinOpKind::Le,
208                                 _ => return binop,
209                             })
210         }
211
212         // for isize & usize, be conservative with the warnings, so that the
213         // warnings are consistent between 32- and 64-bit platforms
214         fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) {
215             match int_ty {
216                 ast::IntTy::Isize => (i64::min_value() as i128, i64::max_value() as i128),
217                 ast::IntTy::I8 => (i8::min_value() as i64 as i128, i8::max_value() as i128),
218                 ast::IntTy::I16 => (i16::min_value() as i64 as i128, i16::max_value() as i128),
219                 ast::IntTy::I32 => (i32::min_value() as i64 as i128, i32::max_value() as i128),
220                 ast::IntTy::I64 => (i64::min_value() as i128, i64::max_value() as i128),
221                 ast::IntTy::I128 =>(i128::min_value() as i128, i128::max_value()),
222             }
223         }
224
225         fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) {
226             match uint_ty {
227                 ast::UintTy::Usize => (u64::min_value() as u128, u64::max_value() as u128),
228                 ast::UintTy::U8 => (u8::min_value() as u128, u8::max_value() as u128),
229                 ast::UintTy::U16 => (u16::min_value() as u128, u16::max_value() as u128),
230                 ast::UintTy::U32 => (u32::min_value() as u128, u32::max_value() as u128),
231                 ast::UintTy::U64 => (u64::min_value() as u128, u64::max_value() as u128),
232                 ast::UintTy::U128 => (u128::min_value(), u128::max_value()),
233             }
234         }
235
236         fn check_limits(cx: &LateContext,
237                         binop: hir::BinOp,
238                         l: &hir::Expr,
239                         r: &hir::Expr)
240                         -> bool {
241             let (lit, expr, swap) = match (&l.node, &r.node) {
242                 (&hir::ExprKind::Lit(_), _) => (l, r, true),
243                 (_, &hir::ExprKind::Lit(_)) => (r, l, false),
244                 _ => return true,
245             };
246             // Normalize the binop so that the literal is always on the RHS in
247             // the comparison
248             let norm_binop = if swap { rev_binop(binop) } else { binop };
249             match cx.tables.node_id_to_type(expr.hir_id).sty {
250                 ty::Int(int_ty) => {
251                     let (min, max) = int_ty_range(int_ty);
252                     let lit_val: i128 = match lit.node {
253                         hir::ExprKind::Lit(ref li) => {
254                             match li.node {
255                                 ast::LitKind::Int(v, ast::LitIntType::Signed(_)) |
256                                 ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => v as i128,
257                                 _ => return true
258                             }
259                         },
260                         _ => bug!()
261                     };
262                     is_valid(norm_binop, lit_val, min, max)
263                 }
264                 ty::Uint(uint_ty) => {
265                     let (min, max) :(u128, u128) = uint_ty_range(uint_ty);
266                     let lit_val: u128 = match lit.node {
267                         hir::ExprKind::Lit(ref li) => {
268                             match li.node {
269                                 ast::LitKind::Int(v, _) => v,
270                                 _ => return true
271                             }
272                         },
273                         _ => bug!()
274                     };
275                     is_valid(norm_binop, lit_val, min, max)
276                 }
277                 _ => true,
278             }
279         }
280
281         fn is_comparison(binop: hir::BinOp) -> bool {
282             match binop.node {
283                 hir::BinOpKind::Eq |
284                 hir::BinOpKind::Lt |
285                 hir::BinOpKind::Le |
286                 hir::BinOpKind::Ne |
287                 hir::BinOpKind::Ge |
288                 hir::BinOpKind::Gt => true,
289                 _ => false,
290             }
291         }
292
293         fn get_bin_hex_repr(cx: &LateContext, lit: &ast::Lit) -> Option<String> {
294             let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?;
295             let firstch = src.chars().next()?;
296
297             if firstch == '0' {
298                 match src.chars().nth(1) {
299                     Some('x') | Some('b') => return Some(src),
300                     _ => return None,
301                 }
302             }
303
304             None
305         }
306
307         // This function finds the next fitting type and generates a suggestion string.
308         // It searches for fitting types in the following way (`X < Y`):
309         //  - `iX`: if literal fits in `uX` => `uX`, else => `iY`
310         //  - `-iX` => `iY`
311         //  - `uX` => `uY`
312         //
313         // No suggestion for: `isize`, `usize`.
314         fn get_type_suggestion<'a>(
315             t: &ty::TyKind,
316             val: u128,
317             negative: bool,
318         ) -> Option<String> {
319             use syntax::ast::IntTy::*;
320             use syntax::ast::UintTy::*;
321             macro_rules! find_fit {
322                 ($ty:expr, $val:expr, $negative:expr,
323                  $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => {
324                     {
325                         let _neg = if negative { 1 } else { 0 };
326                         match $ty {
327                             $($type => {
328                                 $(if !negative && val <= uint_ty_range($utypes).1 {
329                                     return Some(format!("{:?}", $utypes))
330                                 })*
331                                 $(if val <= int_ty_range($itypes).1 as u128 + _neg {
332                                     return Some(format!("{:?}", $itypes))
333                                 })*
334                                 None
335                             },)*
336                             _ => None
337                         }
338                     }
339                 }
340             }
341             match t {
342                 &ty::Int(i) => find_fit!(i, val, negative,
343                               I8 => [U8] => [I16, I32, I64, I128],
344                               I16 => [U16] => [I32, I64, I128],
345                               I32 => [U32] => [I64, I128],
346                               I64 => [U64] => [I128],
347                               I128 => [U128] => []),
348                 &ty::Uint(u) => find_fit!(u, val, negative,
349                               U8 => [U8, U16, U32, U64, U128] => [],
350                               U16 => [U16, U32, U64, U128] => [],
351                               U32 => [U32, U64, U128] => [],
352                               U64 => [U64, U128] => [],
353                               U128 => [U128] => []),
354                 _ => None,
355             }
356         }
357
358         fn report_bin_hex_error(
359             cx: &LateContext,
360             expr: &hir::Expr,
361             ty: ty::TyKind,
362             repr_str: String,
363             val: u128,
364             negative: bool,
365         ) {
366             let (t, actually) = match ty {
367                 ty::Int(t) => {
368                     let ity = attr::IntType::SignedInt(t);
369                     let bits = layout::Integer::from_attr(&cx.tcx, ity).size().bits();
370                     let actually = (val << (128 - bits)) as i128 >> (128 - bits);
371                     (format!("{:?}", t), actually.to_string())
372                 }
373                 ty::Uint(t) => {
374                     let ity = attr::IntType::UnsignedInt(t);
375                     let bits = layout::Integer::from_attr(&cx.tcx, ity).size().bits();
376                     let actually = (val << (128 - bits)) >> (128 - bits);
377                     (format!("{:?}", t), actually.to_string())
378                 }
379                 _ => bug!(),
380             };
381             let mut err = cx.struct_span_lint(
382                 OVERFLOWING_LITERALS,
383                 expr.span,
384                 &format!("literal out of range for {}", t),
385             );
386             err.note(&format!(
387                 "the literal `{}` (decimal `{}`) does not fit into \
388                  an `{}` and will become `{}{}`",
389                 repr_str, val, t, actually, t
390             ));
391             if let Some(sugg_ty) =
392                 get_type_suggestion(&cx.tables.node_id_to_type(expr.hir_id).sty, val, negative)
393             {
394                 if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
395                     let (sans_suffix, _) = repr_str.split_at(pos);
396                     err.span_suggestion_with_applicability(
397                         expr.span,
398                         &format!("consider using `{}` instead", sugg_ty),
399                         format!("{}{}", sans_suffix, sugg_ty),
400                         Applicability::MachineApplicable
401                     );
402                 } else {
403                     err.help(&format!("consider using `{}` instead", sugg_ty));
404                 }
405             }
406
407             err.emit();
408         }
409     }
410 }
411
412 declare_lint! {
413     IMPROPER_CTYPES,
414     Warn,
415     "proper use of libc types in foreign modules"
416 }
417
418 struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
419     cx: &'a LateContext<'a, 'tcx>,
420 }
421
422 enum FfiResult<'tcx> {
423     FfiSafe,
424     FfiPhantom(Ty<'tcx>),
425     FfiUnsafe {
426         ty: Ty<'tcx>,
427         reason: &'static str,
428         help: Option<&'static str>,
429     },
430 }
431
432 /// Check if this enum can be safely exported based on the
433 /// "nullable pointer optimization". Currently restricted
434 /// to function pointers and references, but could be
435 /// expanded to cover NonZero raw pointers and newtypes.
436 /// FIXME: This duplicates code in codegen.
437 fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
438                                   def: &'tcx ty::AdtDef,
439                                   substs: &Substs<'tcx>)
440                                   -> bool {
441     if def.variants.len() == 2 {
442         let data_idx;
443
444         let zero = VariantIdx::new(0);
445         let one = VariantIdx::new(1);
446
447         if def.variants[zero].fields.is_empty() {
448             data_idx = one;
449         } else if def.variants[one].fields.is_empty() {
450             data_idx = zero;
451         } else {
452             return false;
453         }
454
455         if def.variants[data_idx].fields.len() == 1 {
456             match def.variants[data_idx].fields[0].ty(tcx, substs).sty {
457                 ty::FnPtr(_) => {
458                     return true;
459                 }
460                 ty::Ref(..) => {
461                     return true;
462                 }
463                 _ => {}
464             }
465         }
466     }
467     false
468 }
469
470 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
471     /// Check if the given type is "ffi-safe" (has a stable, well-defined
472     /// representation which can be exported to C code).
473     fn check_type_for_ffi(&self,
474                           cache: &mut FxHashSet<Ty<'tcx>>,
475                           ty: Ty<'tcx>) -> FfiResult<'tcx> {
476         use self::FfiResult::*;
477
478         let cx = self.cx.tcx;
479
480         // Protect against infinite recursion, for example
481         // `struct S(*mut S);`.
482         // FIXME: A recursion limit is necessary as well, for irregular
483         // recursive types.
484         if !cache.insert(ty) {
485             return FfiSafe;
486         }
487
488         match ty.sty {
489             ty::Adt(def, substs) => {
490                 if def.is_phantom_data() {
491                     return FfiPhantom(ty);
492                 }
493                 match def.adt_kind() {
494                     AdtKind::Struct => {
495                         if !def.repr.c() && !def.repr.transparent() {
496                             return FfiUnsafe {
497                                 ty: ty,
498                                 reason: "this struct has unspecified layout",
499                                 help: Some("consider adding a #[repr(C)] or #[repr(transparent)] \
500                                             attribute to this struct"),
501                             };
502                         }
503
504                         if def.non_enum_variant().fields.is_empty() {
505                             return FfiUnsafe {
506                                 ty: ty,
507                                 reason: "this struct has no fields",
508                                 help: Some("consider adding a member to this struct"),
509                             };
510                         }
511
512                         // We can't completely trust repr(C) and repr(transparent) markings;
513                         // make sure the fields are actually safe.
514                         let mut all_phantom = true;
515                         for field in &def.non_enum_variant().fields {
516                             let field_ty = cx.normalize_erasing_regions(
517                                 ParamEnv::reveal_all(),
518                                 field.ty(cx, substs),
519                             );
520                             // repr(transparent) types are allowed to have arbitrary ZSTs, not just
521                             // PhantomData -- skip checking all ZST fields
522                             if def.repr.transparent() {
523                                 let is_zst = cx
524                                     .layout_of(cx.param_env(field.did).and(field_ty))
525                                     .map(|layout| layout.is_zst())
526                                     .unwrap_or(false);
527                                 if is_zst {
528                                     continue;
529                                 }
530                             }
531                             let r = self.check_type_for_ffi(cache, field_ty);
532                             match r {
533                                 FfiSafe => {
534                                     all_phantom = false;
535                                 }
536                                 FfiPhantom(..) => {}
537                                 FfiUnsafe { .. } => {
538                                     return r;
539                                 }
540                             }
541                         }
542
543                         if all_phantom { FfiPhantom(ty) } else { FfiSafe }
544                     }
545                     AdtKind::Union => {
546                         if !def.repr.c() {
547                             return FfiUnsafe {
548                                 ty: ty,
549                                 reason: "this union has unspecified layout",
550                                 help: Some("consider adding a #[repr(C)] attribute to this union"),
551                             };
552                         }
553
554                         if def.non_enum_variant().fields.is_empty() {
555                             return FfiUnsafe {
556                                 ty: ty,
557                                 reason: "this union has no fields",
558                                 help: Some("consider adding a field to this union"),
559                             };
560                         }
561
562                         let mut all_phantom = true;
563                         for field in &def.non_enum_variant().fields {
564                             let field_ty = cx.normalize_erasing_regions(
565                                 ParamEnv::reveal_all(),
566                                 field.ty(cx, substs),
567                             );
568                             let r = self.check_type_for_ffi(cache, field_ty);
569                             match r {
570                                 FfiSafe => {
571                                     all_phantom = false;
572                                 }
573                                 FfiPhantom(..) => {}
574                                 FfiUnsafe { .. } => {
575                                     return r;
576                                 }
577                             }
578                         }
579
580                         if all_phantom { FfiPhantom(ty) } else { FfiSafe }
581                     }
582                     AdtKind::Enum => {
583                         if def.variants.is_empty() {
584                             // Empty enums are okay... although sort of useless.
585                             return FfiSafe;
586                         }
587
588                         // Check for a repr() attribute to specify the size of the
589                         // discriminant.
590                         if !def.repr.c() && def.repr.int.is_none() {
591                             // Special-case types like `Option<extern fn()>`.
592                             if !is_repr_nullable_ptr(cx, def, substs) {
593                                 return FfiUnsafe {
594                                     ty: ty,
595                                     reason: "enum has no representation hint",
596                                     help: Some("consider adding a #[repr(...)] attribute \
597                                                 to this enum"),
598                                 };
599                             }
600                         }
601
602                         // Check the contained variants.
603                         for variant in &def.variants {
604                             for field in &variant.fields {
605                                 let arg = cx.normalize_erasing_regions(
606                                     ParamEnv::reveal_all(),
607                                     field.ty(cx, substs),
608                                 );
609                                 let r = self.check_type_for_ffi(cache, arg);
610                                 match r {
611                                     FfiSafe => {}
612                                     FfiUnsafe { .. } => {
613                                         return r;
614                                     }
615                                     FfiPhantom(..) => {
616                                         return FfiUnsafe {
617                                             ty: ty,
618                                             reason: "this enum contains a PhantomData field",
619                                             help: None,
620                                         };
621                                     }
622                                 }
623                             }
624                         }
625                         FfiSafe
626                     }
627                 }
628             }
629
630             ty::Char => FfiUnsafe {
631                 ty: ty,
632                 reason: "the `char` type has no C equivalent",
633                 help: Some("consider using `u32` or `libc::wchar_t` instead"),
634             },
635
636             ty::Int(ast::IntTy::I128) | ty::Uint(ast::UintTy::U128) => FfiUnsafe {
637                 ty: ty,
638                 reason: "128-bit integers don't currently have a known stable ABI",
639                 help: None,
640             },
641
642             // Primitive types with a stable representation.
643             ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
644
645             ty::Slice(_) => FfiUnsafe {
646                 ty: ty,
647                 reason: "slices have no C equivalent",
648                 help: Some("consider using a raw pointer instead"),
649             },
650
651             ty::Dynamic(..) => FfiUnsafe {
652                 ty: ty,
653                 reason: "trait objects have no C equivalent",
654                 help: None,
655             },
656
657             ty::Str => FfiUnsafe {
658                 ty: ty,
659                 reason: "string slices have no C equivalent",
660                 help: Some("consider using `*const u8` and a length instead"),
661             },
662
663             ty::Tuple(..) => FfiUnsafe {
664                 ty: ty,
665                 reason: "tuples have unspecified layout",
666                 help: Some("consider using a struct instead"),
667             },
668
669             ty::RawPtr(ty::TypeAndMut { ty, .. }) |
670             ty::Ref(_, ty, _) => self.check_type_for_ffi(cache, ty),
671
672             ty::Array(ty, _) => self.check_type_for_ffi(cache, ty),
673
674             ty::FnPtr(sig) => {
675                 match sig.abi() {
676                     Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => {
677                         return FfiUnsafe {
678                             ty: ty,
679                             reason: "this function pointer has Rust-specific calling convention",
680                             help: Some("consider using an `extern fn(...) -> ...` \
681                                         function pointer instead"),
682                         }
683                     }
684                     _ => {}
685                 }
686
687                 let sig = cx.erase_late_bound_regions(&sig);
688                 if !sig.output().is_unit() {
689                     let r = self.check_type_for_ffi(cache, sig.output());
690                     match r {
691                         FfiSafe => {}
692                         _ => {
693                             return r;
694                         }
695                     }
696                 }
697                 for arg in sig.inputs() {
698                     let r = self.check_type_for_ffi(cache, arg);
699                     match r {
700                         FfiSafe => {}
701                         _ => {
702                             return r;
703                         }
704                     }
705                 }
706                 FfiSafe
707             }
708
709             ty::Foreign(..) => FfiSafe,
710
711             ty::Param(..) |
712             ty::Infer(..) |
713             ty::Bound(..) |
714             ty::Error |
715             ty::Closure(..) |
716             ty::Generator(..) |
717             ty::GeneratorWitness(..) |
718             ty::Placeholder(..) |
719             ty::UnnormalizedProjection(..) |
720             ty::Projection(..) |
721             ty::Opaque(..) |
722             ty::FnDef(..) => bug!("Unexpected type in foreign function"),
723         }
724     }
725
726     fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>) {
727         // it is only OK to use this function because extern fns cannot have
728         // any generic types right now:
729         let ty = self.cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
730
731         match self.check_type_for_ffi(&mut FxHashSet::default(), ty) {
732             FfiResult::FfiSafe => {}
733             FfiResult::FfiPhantom(ty) => {
734                 self.cx.span_lint(IMPROPER_CTYPES,
735                                   sp,
736                                   &format!("`extern` block uses type `{}` which is not FFI-safe: \
737                                             composed only of PhantomData", ty));
738             }
739             FfiResult::FfiUnsafe { ty: unsafe_ty, reason, help } => {
740                 let msg = format!("`extern` block uses type `{}` which is not FFI-safe: {}",
741                                   unsafe_ty, reason);
742                 let mut diag = self.cx.struct_span_lint(IMPROPER_CTYPES, sp, &msg);
743                 if let Some(s) = help {
744                     diag.help(s);
745                 }
746                 if let ty::Adt(def, _) = unsafe_ty.sty {
747                     if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) {
748                         diag.span_note(sp, "type defined here");
749                     }
750                 }
751                 diag.emit();
752             }
753         }
754     }
755
756     fn check_foreign_fn(&mut self, id: ast::NodeId, decl: &hir::FnDecl) {
757         let def_id = self.cx.tcx.hir().local_def_id(id);
758         let sig = self.cx.tcx.fn_sig(def_id);
759         let sig = self.cx.tcx.erase_late_bound_regions(&sig);
760
761         for (input_ty, input_hir) in sig.inputs().iter().zip(&decl.inputs) {
762             self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty);
763         }
764
765         if let hir::Return(ref ret_hir) = decl.output {
766             let ret_ty = sig.output();
767             if !ret_ty.is_unit() {
768                 self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty);
769             }
770         }
771     }
772
773     fn check_foreign_static(&mut self, id: ast::NodeId, span: Span) {
774         let def_id = self.cx.tcx.hir().local_def_id(id);
775         let ty = self.cx.tcx.type_of(def_id);
776         self.check_type_for_ffi_and_report_errors(span, ty);
777     }
778 }
779
780 #[derive(Copy, Clone)]
781 pub struct ImproperCTypes;
782
783 impl LintPass for ImproperCTypes {
784     fn get_lints(&self) -> LintArray {
785         lint_array!(IMPROPER_CTYPES)
786     }
787 }
788
789 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypes {
790     fn check_foreign_item(&mut self, cx: &LateContext, it: &hir::ForeignItem) {
791         let mut vis = ImproperCTypesVisitor { cx };
792         let abi = cx.tcx.hir().get_foreign_abi(it.id);
793         if abi != Abi::RustIntrinsic && abi != Abi::PlatformIntrinsic {
794             match it.node {
795                 hir::ForeignItemKind::Fn(ref decl, _, _) => {
796                     vis.check_foreign_fn(it.id, decl);
797                 }
798                 hir::ForeignItemKind::Static(ref ty, _) => {
799                     vis.check_foreign_static(it.id, ty.span);
800                 }
801                 hir::ForeignItemKind::Type => ()
802             }
803         }
804     }
805 }
806
807 pub struct VariantSizeDifferences;
808
809 impl LintPass for VariantSizeDifferences {
810     fn get_lints(&self) -> LintArray {
811         lint_array!(VARIANT_SIZE_DIFFERENCES)
812     }
813 }
814
815 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences {
816     fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
817         if let hir::ItemKind::Enum(ref enum_definition, _) = it.node {
818             let item_def_id = cx.tcx.hir().local_def_id(it.id);
819             let t = cx.tcx.type_of(item_def_id);
820             let ty = cx.tcx.erase_regions(&t);
821             match cx.layout_of(ty) {
822                 Ok(layout) => {
823                     let variants = &layout.variants;
824                     if let layout::Variants::Tagged { ref variants, ref tag, .. } = variants {
825                         let discr_size = tag.value.size(&cx.tcx).bytes();
826
827                         debug!("enum `{}` is {} bytes large with layout:\n{:#?}",
828                                t, layout.size.bytes(), layout);
829
830                         let (largest, slargest, largest_index) = enum_definition.variants
831                             .iter()
832                             .zip(variants)
833                             .map(|(variant, variant_layout)| {
834                                 // Subtract the size of the enum discriminant.
835                                 let bytes = variant_layout.size.bytes().saturating_sub(discr_size);
836
837                                 debug!("- variant `{}` is {} bytes large",
838                                        variant.node.ident,
839                                        bytes);
840                                 bytes
841                             })
842                             .enumerate()
843                             .fold((0, 0, 0), |(l, s, li), (idx, size)| if size > l {
844                                 (size, l, idx)
845                             } else if size > s {
846                                 (l, size, li)
847                             } else {
848                                 (l, s, li)
849                             });
850
851                         // We only warn if the largest variant is at least thrice as large as
852                         // the second-largest.
853                         if largest > slargest * 3 && slargest > 0 {
854                             cx.span_lint(VARIANT_SIZE_DIFFERENCES,
855                                             enum_definition.variants[largest_index].span,
856                                             &format!("enum variant is more than three times \
857                                                       larger ({} bytes) than the next largest",
858                                                      largest));
859                         }
860                     }
861                 }
862                 Err(ty::layout::LayoutError::Unknown(_)) => return,
863                 Err(err @ ty::layout::LayoutError::SizeOverflow(_)) => {
864                     bug!("failed to get layout for `{}`: {}", t, err);
865                 }
866             }
867         }
868     }
869 }