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