]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/types.rs
Specify output filenames for compatibility with Windows
[rust.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::ty::subst::Substs;
15 use rustc::ty::{self, AdtKind, Ty, TyCtxt};
16 use rustc::ty::layout::{self, LayoutOf};
17 use middle::const_val::ConstVal;
18 use rustc_const_eval::ConstContext;
19 use util::nodemap::FxHashSet;
20 use lint::{LateContext, LintContext, LintArray};
21 use lint::{LintPass, LateLintPass};
22
23 use std::cmp;
24 use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
25
26 use syntax::ast;
27 use syntax::abi::Abi;
28 use syntax::attr;
29 use syntax_pos::Span;
30 use syntax::codemap;
31
32 use rustc::hir;
33
34 declare_lint! {
35     UNUSED_COMPARISONS,
36     Warn,
37     "comparisons made useless by limits of the types involved"
38 }
39
40 declare_lint! {
41     OVERFLOWING_LITERALS,
42     Warn,
43     "literal out of range for its type"
44 }
45
46 declare_lint! {
47     EXCEEDING_BITSHIFTS,
48     Deny,
49     "shift exceeds the type's number of bits"
50 }
51
52 declare_lint! {
53     VARIANT_SIZE_DIFFERENCES,
54     Allow,
55     "detects enums with widely varying variant sizes"
56 }
57
58 #[derive(Copy, Clone)]
59 pub struct TypeLimits {
60     /// Id of the last visited negated expression
61     negated_expr_id: ast::NodeId,
62 }
63
64 impl TypeLimits {
65     pub fn new() -> TypeLimits {
66         TypeLimits { negated_expr_id: ast::DUMMY_NODE_ID }
67     }
68 }
69
70 impl LintPass for TypeLimits {
71     fn get_lints(&self) -> LintArray {
72         lint_array!(UNUSED_COMPARISONS,
73                     OVERFLOWING_LITERALS,
74                     EXCEEDING_BITSHIFTS)
75     }
76 }
77
78 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
79     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) {
80         match e.node {
81             hir::ExprUnary(hir::UnNeg, ref expr) => {
82                 // propagate negation, if the negation itself isn't negated
83                 if self.negated_expr_id != e.id {
84                     self.negated_expr_id = expr.id;
85                 }
86             }
87             hir::ExprBinary(binop, ref l, ref r) => {
88                 if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
89                     cx.span_lint(UNUSED_COMPARISONS,
90                                  e.span,
91                                  "comparison is useless due to type limits");
92                 }
93
94                 if binop.node.is_shift() {
95                     let opt_ty_bits = match cx.tables.node_id_to_type(l.hir_id).sty {
96                         ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.isize_ty)),
97                         ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.usize_ty)),
98                         _ => None,
99                     };
100
101                     if let Some(bits) = opt_ty_bits {
102                         let exceeding = if let hir::ExprLit(ref lit) = r.node {
103                             if let ast::LitKind::Int(shift, _) = lit.node {
104                                 shift as u64 >= bits
105                             } else {
106                                 false
107                             }
108                         } else {
109                             // HACK(eddyb) This might be quite inefficient.
110                             // This would be better left to MIR constant propagation,
111                             // perhaps even at trans time (like is the case already
112                             // when the value being shifted is *also* constant).
113                             let parent_item = cx.tcx.hir.get_parent(e.id);
114                             let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
115                             let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
116                             let const_cx = ConstContext::new(cx.tcx,
117                                                              cx.param_env.and(substs),
118                                                              cx.tables);
119                             match const_cx.eval(&r) {
120                                 Ok(&ty::Const { val: ConstVal::Integral(i), .. }) => {
121                                     i.is_negative() ||
122                                     i.to_u64()
123                                         .map(|i| i >= bits)
124                                         .unwrap_or(true)
125                                 }
126                                 _ => false,
127                             }
128                         };
129                         if exceeding {
130                             cx.span_lint(EXCEEDING_BITSHIFTS,
131                                          e.span,
132                                          "bitshift exceeds the type's number of bits");
133                         }
134                     };
135                 }
136             }
137             hir::ExprLit(ref lit) => {
138                 match cx.tables.node_id_to_type(e.hir_id).sty {
139                     ty::TyInt(t) => {
140                         match lit.node {
141                             ast::LitKind::Int(v, ast::LitIntType::Signed(_)) |
142                             ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => {
143                                 let int_type = if let ast::IntTy::Isize = t {
144                                     cx.sess().target.isize_ty
145                                 } else {
146                                     t
147                                 };
148                                 let (_, max) = int_ty_range(int_type);
149                                 let max = max as u128;
150                                 let negative = self.negated_expr_id == e.id;
151
152                                 // Detect literal value out of range [min, max] inclusive
153                                 // avoiding use of -min to prevent overflow/panic
154                                 if (negative && v > max + 1) ||
155                                    (!negative && v > max) {
156                                     cx.span_lint(OVERFLOWING_LITERALS,
157                                                  e.span,
158                                                  &format!("literal out of range for {:?}", t));
159                                     return;
160                                 }
161                             }
162                             _ => bug!(),
163                         };
164                     }
165                     ty::TyUint(t) => {
166                         let uint_type = if let ast::UintTy::Usize = t {
167                             cx.sess().target.usize_ty
168                         } else {
169                             t
170                         };
171                         let (min, max) = uint_ty_range(uint_type);
172                         let lit_val: u128 = match lit.node {
173                             // _v is u8, within range by definition
174                             ast::LitKind::Byte(_v) => return,
175                             ast::LitKind::Int(v, _) => v,
176                             _ => bug!(),
177                         };
178                         if lit_val < min || lit_val > max {
179                             cx.span_lint(OVERFLOWING_LITERALS,
180                                          e.span,
181                                          &format!("literal out of range for {:?}", t));
182                         }
183                     }
184                     ty::TyFloat(t) => {
185                         let is_infinite = match lit.node {
186                             ast::LitKind::Float(v, _) |
187                             ast::LitKind::FloatUnsuffixed(v) => {
188                                 match t {
189                                     ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite),
190                                     ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite),
191                                 }
192                             }
193                             _ => bug!(),
194                         };
195                         if is_infinite == Ok(true) {
196                             cx.span_lint(OVERFLOWING_LITERALS,
197                                          e.span,
198                                          &format!("literal out of range for {:?}", t));
199                         }
200                     }
201                     _ => (),
202                 };
203             }
204             _ => (),
205         };
206
207         fn is_valid<T: cmp::PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
208             match binop.node {
209                 hir::BiLt => v > min && v <= max,
210                 hir::BiLe => v >= min && v < max,
211                 hir::BiGt => v >= min && v < max,
212                 hir::BiGe => v > min && v <= max,
213                 hir::BiEq | hir::BiNe => v >= min && v <= max,
214                 _ => bug!(),
215             }
216         }
217
218         fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
219             codemap::respan(binop.span,
220                             match binop.node {
221                                 hir::BiLt => hir::BiGt,
222                                 hir::BiLe => hir::BiGe,
223                                 hir::BiGt => hir::BiLt,
224                                 hir::BiGe => hir::BiLe,
225                                 _ => return binop,
226                             })
227         }
228
229         // for isize & usize, be conservative with the warnings, so that the
230         // warnings are consistent between 32- and 64-bit platforms
231         fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) {
232             match int_ty {
233                 ast::IntTy::Isize => (i64::min_value() as i128, i64::max_value() as i128),
234                 ast::IntTy::I8 => (i8::min_value() as i64 as i128, i8::max_value() as i128),
235                 ast::IntTy::I16 => (i16::min_value() as i64 as i128, i16::max_value() as i128),
236                 ast::IntTy::I32 => (i32::min_value() as i64 as i128, i32::max_value() as i128),
237                 ast::IntTy::I64 => (i64::min_value() as i128, i64::max_value() as i128),
238                 ast::IntTy::I128 =>(i128::min_value() as i128, i128::max_value()),
239             }
240         }
241
242         fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) {
243             match uint_ty {
244                 ast::UintTy::Usize => (u64::min_value() as u128, u64::max_value() as u128),
245                 ast::UintTy::U8 => (u8::min_value() as u128, u8::max_value() as u128),
246                 ast::UintTy::U16 => (u16::min_value() as u128, u16::max_value() as u128),
247                 ast::UintTy::U32 => (u32::min_value() as u128, u32::max_value() as u128),
248                 ast::UintTy::U64 => (u64::min_value() as u128, u64::max_value() as u128),
249                 ast::UintTy::U128 => (u128::min_value(), u128::max_value()),
250             }
251         }
252
253         fn int_ty_bits(int_ty: ast::IntTy, isize_ty: ast::IntTy) -> u64 {
254             match int_ty {
255                 ast::IntTy::Isize => int_ty_bits(isize_ty, isize_ty),
256                 ast::IntTy::I8 => 8,
257                 ast::IntTy::I16 => 16 as u64,
258                 ast::IntTy::I32 => 32,
259                 ast::IntTy::I64 => 64,
260                 ast::IntTy::I128 => 128,
261             }
262         }
263
264         fn uint_ty_bits(uint_ty: ast::UintTy, usize_ty: ast::UintTy) -> u64 {
265             match uint_ty {
266                 ast::UintTy::Usize => uint_ty_bits(usize_ty, usize_ty),
267                 ast::UintTy::U8 => 8,
268                 ast::UintTy::U16 => 16,
269                 ast::UintTy::U32 => 32,
270                 ast::UintTy::U64 => 64,
271                 ast::UintTy::U128 => 128,
272             }
273         }
274
275         fn check_limits(cx: &LateContext,
276                         binop: hir::BinOp,
277                         l: &hir::Expr,
278                         r: &hir::Expr)
279                         -> bool {
280             let (lit, expr, swap) = match (&l.node, &r.node) {
281                 (&hir::ExprLit(_), _) => (l, r, true),
282                 (_, &hir::ExprLit(_)) => (r, l, false),
283                 _ => return true,
284             };
285             // Normalize the binop so that the literal is always on the RHS in
286             // the comparison
287             let norm_binop = if swap { rev_binop(binop) } else { binop };
288             match cx.tables.node_id_to_type(expr.hir_id).sty {
289                 ty::TyInt(int_ty) => {
290                     let (min, max) = int_ty_range(int_ty);
291                     let lit_val: i128 = match lit.node {
292                         hir::ExprLit(ref li) => {
293                             match li.node {
294                                 ast::LitKind::Int(v, ast::LitIntType::Signed(_)) |
295                                 ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => v as i128,
296                                 _ => return true
297                             }
298                         },
299                         _ => bug!()
300                     };
301                     is_valid(norm_binop, lit_val, min, max)
302                 }
303                 ty::TyUint(uint_ty) => {
304                     let (min, max) :(u128, u128) = uint_ty_range(uint_ty);
305                     let lit_val: u128 = match lit.node {
306                         hir::ExprLit(ref li) => {
307                             match li.node {
308                                 ast::LitKind::Int(v, _) => v,
309                                 _ => return true
310                             }
311                         },
312                         _ => bug!()
313                     };
314                     is_valid(norm_binop, lit_val, min, max)
315                 }
316                 _ => true,
317             }
318         }
319
320         fn is_comparison(binop: hir::BinOp) -> bool {
321             match binop.node {
322                 hir::BiEq | hir::BiLt | hir::BiLe | hir::BiNe | hir::BiGe | hir::BiGt => true,
323                 _ => false,
324             }
325         }
326     }
327 }
328
329 declare_lint! {
330     IMPROPER_CTYPES,
331     Warn,
332     "proper use of libc types in foreign modules"
333 }
334
335 struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
336     cx: &'a LateContext<'a, 'tcx>,
337 }
338
339 enum FfiResult {
340     FfiSafe,
341     FfiPhantom,
342     FfiUnsafe(&'static str),
343     FfiBadStruct(DefId, &'static str),
344     FfiBadUnion(DefId, &'static str),
345     FfiBadEnum(DefId, &'static str),
346 }
347
348 /// Check if this enum can be safely exported based on the
349 /// "nullable pointer optimization". Currently restricted
350 /// to function pointers and references, but could be
351 /// expanded to cover NonZero raw pointers and newtypes.
352 /// FIXME: This duplicates code in trans.
353 fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
354                                   def: &'tcx ty::AdtDef,
355                                   substs: &Substs<'tcx>)
356                                   -> bool {
357     if def.variants.len() == 2 {
358         let data_idx;
359
360         if def.variants[0].fields.is_empty() {
361             data_idx = 1;
362         } else if def.variants[1].fields.is_empty() {
363             data_idx = 0;
364         } else {
365             return false;
366         }
367
368         if def.variants[data_idx].fields.len() == 1 {
369             match def.variants[data_idx].fields[0].ty(tcx, substs).sty {
370                 ty::TyFnPtr(_) => {
371                     return true;
372                 }
373                 ty::TyRef(..) => {
374                     return true;
375                 }
376                 _ => {}
377             }
378         }
379     }
380     false
381 }
382
383 fn is_ffi_safe(ty: attr::IntType) -> bool {
384     match ty {
385         attr::SignedInt(ast::IntTy::I8) | attr::UnsignedInt(ast::UintTy::U8) |
386         attr::SignedInt(ast::IntTy::I16) | attr::UnsignedInt(ast::UintTy::U16) |
387         attr::SignedInt(ast::IntTy::I32) | attr::UnsignedInt(ast::UintTy::U32) |
388         attr::SignedInt(ast::IntTy::I64) | attr::UnsignedInt(ast::UintTy::U64) |
389         attr::SignedInt(ast::IntTy::I128) | attr::UnsignedInt(ast::UintTy::U128) => true,
390         attr::SignedInt(ast::IntTy::Isize) | attr::UnsignedInt(ast::UintTy::Usize) => false
391     }
392 }
393
394 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
395     /// Check if the given type is "ffi-safe" (has a stable, well-defined
396     /// representation which can be exported to C code).
397     fn check_type_for_ffi(&self,
398                           cache: &mut FxHashSet<Ty<'tcx>>,
399                           ty: Ty<'tcx>) -> FfiResult {
400         use self::FfiResult::*;
401
402         let cx = self.cx.tcx;
403
404         // Protect against infinite recursion, for example
405         // `struct S(*mut S);`.
406         // FIXME: A recursion limit is necessary as well, for irregular
407         // recusive types.
408         if !cache.insert(ty) {
409             return FfiSafe;
410         }
411
412         match ty.sty {
413             ty::TyAdt(def, substs) => {
414                 if def.is_phantom_data() {
415                     return FfiPhantom;
416                 }
417                 match def.adt_kind() {
418                     AdtKind::Struct => {
419                         if !def.repr.c() && !def.repr.transparent() {
420                             return FfiUnsafe("found struct without foreign-function-safe \
421                                               representation annotation in foreign module, \
422                                               consider adding a #[repr(C)] attribute to the type");
423                         }
424
425                         if def.non_enum_variant().fields.is_empty() {
426                             return FfiUnsafe("found zero-size struct in foreign module, consider \
427                                               adding a member to this struct");
428                         }
429
430                         // We can't completely trust repr(C) and repr(transparent) markings;
431                         // make sure the fields are actually safe.
432                         let mut all_phantom = true;
433                         for field in &def.non_enum_variant().fields {
434                             let field_ty = cx.fully_normalize_associated_types_in(
435                                 &field.ty(cx, substs)
436                             );
437                             // repr(transparent) types are allowed to have arbitrary ZSTs, not just
438                             // PhantomData -- skip checking all ZST fields
439                             if def.repr.transparent() {
440                                 let is_zst = (cx, cx.param_env(field.did))
441                                     .layout_of(field_ty)
442                                     .map(|layout| layout.is_zst())
443                                     .unwrap_or(false);
444                                 if is_zst {
445                                     continue;
446                                 }
447                             }
448                             let r = self.check_type_for_ffi(cache, field_ty);
449                             match r {
450                                 FfiSafe => {
451                                     all_phantom = false;
452                                 }
453                                 FfiPhantom => {}
454                                 FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
455                                     return r;
456                                 }
457                                 FfiUnsafe(s) => {
458                                     return FfiBadStruct(def.did, s);
459                                 }
460                             }
461                         }
462
463                         if all_phantom { FfiPhantom } else { FfiSafe }
464                     }
465                     AdtKind::Union => {
466                         if !def.repr.c() {
467                             return FfiUnsafe("found union without foreign-function-safe \
468                                               representation annotation in foreign module, \
469                                               consider adding a #[repr(C)] attribute to the type");
470                         }
471
472                         if def.non_enum_variant().fields.is_empty() {
473                             return FfiUnsafe("found zero-size union in foreign module, consider \
474                                               adding a member to this union");
475                         }
476
477                         let mut all_phantom = true;
478                         for field in &def.non_enum_variant().fields {
479                             let field_ty = cx.fully_normalize_associated_types_in(
480                                 &field.ty(cx, substs)
481                             );
482                             let r = self.check_type_for_ffi(cache, field_ty);
483                             match r {
484                                 FfiSafe => {
485                                     all_phantom = false;
486                                 }
487                                 FfiPhantom => {}
488                                 FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
489                                     return r;
490                                 }
491                                 FfiUnsafe(s) => {
492                                     return FfiBadUnion(def.did, s);
493                                 }
494                             }
495                         }
496
497                         if all_phantom { FfiPhantom } else { FfiSafe }
498                     }
499                     AdtKind::Enum => {
500                         if def.variants.is_empty() {
501                             // Empty enums are okay... although sort of useless.
502                             return FfiSafe;
503                         }
504
505                         // Check for a repr() attribute to specify the size of the
506                         // discriminant.
507                         if !def.repr.c() && def.repr.int.is_none() {
508                             // Special-case types like `Option<extern fn()>`.
509                             if !is_repr_nullable_ptr(cx, def, substs) {
510                                 return FfiUnsafe("found enum without foreign-function-safe \
511                                                   representation annotation in foreign \
512                                                   module, consider adding a #[repr(...)] \
513                                                   attribute to the type");
514                             }
515                         }
516
517                         if let Some(int_ty) = def.repr.int {
518                             if !is_ffi_safe(int_ty) {
519                                 // FIXME: This shouldn't be reachable: we should check
520                                 // this earlier.
521                                 return FfiUnsafe("enum has unexpected #[repr(...)] attribute");
522                             }
523
524                             // Enum with an explicitly sized discriminant; either
525                             // a C-style enum or a discriminated union.
526
527                             // The layout of enum variants is implicitly repr(C).
528                             // FIXME: Is that correct?
529                         }
530
531                         // Check the contained variants.
532                         for variant in &def.variants {
533                             for field in &variant.fields {
534                                 let arg = cx.fully_normalize_associated_types_in(
535                                     &field.ty(cx, substs)
536                                 );
537                                 let r = self.check_type_for_ffi(cache, arg);
538                                 match r {
539                                     FfiSafe => {}
540                                     FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => {
541                                         return r;
542                                     }
543                                     FfiPhantom => {
544                                         return FfiBadEnum(def.did,
545                                                           "Found phantom data in enum variant");
546                                     }
547                                     FfiUnsafe(s) => {
548                                         return FfiBadEnum(def.did, s);
549                                     }
550                                 }
551                             }
552                         }
553                         FfiSafe
554                     }
555                 }
556             }
557
558             ty::TyChar => {
559                 FfiUnsafe("found Rust type `char` in foreign module, while \
560                            `u32` or `libc::wchar_t` should be used")
561             }
562
563             ty::TyInt(ast::IntTy::I128) => {
564                 FfiUnsafe("found Rust type `i128` in foreign module, but \
565                            128-bit integers don't currently have a known \
566                            stable ABI")
567             }
568
569             ty::TyUint(ast::UintTy::U128) => {
570                 FfiUnsafe("found Rust type `u128` in foreign module, but \
571                            128-bit integers don't currently have a known \
572                            stable ABI")
573             }
574
575             // Primitive types with a stable representation.
576             ty::TyBool | ty::TyInt(..) | ty::TyUint(..) | ty::TyFloat(..) | ty::TyNever => FfiSafe,
577
578             ty::TySlice(_) => {
579                 FfiUnsafe("found Rust slice type in foreign module, \
580                            consider using a raw pointer instead")
581             }
582
583             ty::TyDynamic(..) => {
584                 FfiUnsafe("found Rust trait type in foreign module, \
585                            consider using a raw pointer instead")
586             }
587
588             ty::TyStr => {
589                 FfiUnsafe("found Rust type `str` in foreign module; \
590                            consider using a `*const libc::c_char`")
591             }
592
593             ty::TyTuple(..) => {
594                 FfiUnsafe("found Rust tuple type in foreign module; \
595                            consider using a struct instead")
596             }
597
598             ty::TyRawPtr(ref m) |
599             ty::TyRef(_, ref m) => self.check_type_for_ffi(cache, m.ty),
600
601             ty::TyArray(ty, _) => self.check_type_for_ffi(cache, ty),
602
603             ty::TyFnPtr(sig) => {
604                 match sig.abi() {
605                     Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => {
606                         return FfiUnsafe("found function pointer with Rust calling convention in \
607                                           foreign module; consider using an `extern` function \
608                                           pointer")
609                     }
610                     _ => {}
611                 }
612
613                 let sig = cx.erase_late_bound_regions(&sig);
614                 if !sig.output().is_nil() {
615                     let r = self.check_type_for_ffi(cache, sig.output());
616                     match r {
617                         FfiSafe => {}
618                         _ => {
619                             return r;
620                         }
621                     }
622                 }
623                 for arg in sig.inputs() {
624                     let r = self.check_type_for_ffi(cache, arg);
625                     match r {
626                         FfiSafe => {}
627                         _ => {
628                             return r;
629                         }
630                     }
631                 }
632                 FfiSafe
633             }
634
635             ty::TyForeign(..) => FfiSafe,
636
637             ty::TyParam(..) |
638             ty::TyInfer(..) |
639             ty::TyError |
640             ty::TyClosure(..) |
641             ty::TyGenerator(..) |
642             ty::TyGeneratorWitness(..) |
643             ty::TyProjection(..) |
644             ty::TyAnon(..) |
645             ty::TyFnDef(..) => bug!("Unexpected type in foreign function"),
646         }
647     }
648
649     fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>) {
650         // it is only OK to use this function because extern fns cannot have
651         // any generic types right now:
652         let ty = self.cx.tcx.fully_normalize_associated_types_in(&ty);
653
654         match self.check_type_for_ffi(&mut FxHashSet(), ty) {
655             FfiResult::FfiSafe => {}
656             FfiResult::FfiPhantom => {
657                 self.cx.span_lint(IMPROPER_CTYPES,
658                                   sp,
659                                   &format!("found zero-sized type composed only \
660                                             of phantom-data in a foreign-function."));
661             }
662             FfiResult::FfiUnsafe(s) => {
663                 self.cx.span_lint(IMPROPER_CTYPES, sp, s);
664             }
665             FfiResult::FfiBadStruct(_, s) => {
666                 // FIXME: This diagnostic is difficult to read, and doesn't
667                 // point at the relevant field.
668                 self.cx.span_lint(IMPROPER_CTYPES,
669                                   sp,
670                                   &format!("found non-foreign-function-safe member in struct \
671                                             marked #[repr(C)]: {}",
672                                            s));
673             }
674             FfiResult::FfiBadUnion(_, s) => {
675                 // FIXME: This diagnostic is difficult to read, and doesn't
676                 // point at the relevant field.
677                 self.cx.span_lint(IMPROPER_CTYPES,
678                                   sp,
679                                   &format!("found non-foreign-function-safe member in union \
680                                             marked #[repr(C)]: {}",
681                                            s));
682             }
683             FfiResult::FfiBadEnum(_, s) => {
684                 // FIXME: This diagnostic is difficult to read, and doesn't
685                 // point at the relevant variant.
686                 self.cx.span_lint(IMPROPER_CTYPES,
687                                   sp,
688                                   &format!("found non-foreign-function-safe member in enum: {}",
689                                            s));
690             }
691         }
692     }
693
694     fn check_foreign_fn(&mut self, id: ast::NodeId, decl: &hir::FnDecl) {
695         let def_id = self.cx.tcx.hir.local_def_id(id);
696         let sig = self.cx.tcx.fn_sig(def_id);
697         let sig = self.cx.tcx.erase_late_bound_regions(&sig);
698
699         for (input_ty, input_hir) in sig.inputs().iter().zip(&decl.inputs) {
700             self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty);
701         }
702
703         if let hir::Return(ref ret_hir) = decl.output {
704             let ret_ty = sig.output();
705             if !ret_ty.is_nil() {
706                 self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty);
707             }
708         }
709     }
710
711     fn check_foreign_static(&mut self, id: ast::NodeId, span: Span) {
712         let def_id = self.cx.tcx.hir.local_def_id(id);
713         let ty = self.cx.tcx.type_of(def_id);
714         self.check_type_for_ffi_and_report_errors(span, ty);
715     }
716 }
717
718 #[derive(Copy, Clone)]
719 pub struct ImproperCTypes;
720
721 impl LintPass for ImproperCTypes {
722     fn get_lints(&self) -> LintArray {
723         lint_array!(IMPROPER_CTYPES)
724     }
725 }
726
727 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypes {
728     fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
729         let mut vis = ImproperCTypesVisitor { cx: cx };
730         if let hir::ItemForeignMod(ref nmod) = it.node {
731             if nmod.abi != Abi::RustIntrinsic && nmod.abi != Abi::PlatformIntrinsic {
732                 for ni in &nmod.items {
733                     match ni.node {
734                         hir::ForeignItemFn(ref decl, _, _) => {
735                             vis.check_foreign_fn(ni.id, decl);
736                         }
737                         hir::ForeignItemStatic(ref ty, _) => {
738                             vis.check_foreign_static(ni.id, ty.span);
739                         }
740                         hir::ForeignItemType => ()
741                     }
742                 }
743             }
744         }
745     }
746 }
747
748 pub struct VariantSizeDifferences;
749
750 impl LintPass for VariantSizeDifferences {
751     fn get_lints(&self) -> LintArray {
752         lint_array!(VARIANT_SIZE_DIFFERENCES)
753     }
754 }
755
756 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences {
757     fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
758         if let hir::ItemEnum(ref enum_definition, ref gens) = it.node {
759             if gens.params.iter().all(|param| param.is_lifetime_param()) {
760                 // sizes only make sense for non-generic types
761                 let item_def_id = cx.tcx.hir.local_def_id(it.id);
762                 let t = cx.tcx.type_of(item_def_id);
763                 let ty = cx.tcx.erase_regions(&t);
764                 let layout = cx.layout_of(ty).unwrap_or_else(|e| {
765                     bug!("failed to get layout for `{}`: {}", t, e)
766                 });
767
768                 if let layout::Variants::Tagged { ref variants, ref discr, .. } = layout.variants {
769                     let discr_size = discr.value.size(cx.tcx).bytes();
770
771                     debug!("enum `{}` is {} bytes large with layout:\n{:#?}",
772                       t, layout.size.bytes(), layout);
773
774                     let (largest, slargest, largest_index) = enum_definition.variants
775                         .iter()
776                         .zip(variants)
777                         .map(|(variant, variant_layout)| {
778                             // Subtract the size of the enum discriminant
779                             let bytes = variant_layout.size.bytes()
780                                 .saturating_sub(discr_size);
781
782                             debug!("- variant `{}` is {} bytes large", variant.node.name, bytes);
783                             bytes
784                         })
785                         .enumerate()
786                         .fold((0, 0, 0), |(l, s, li), (idx, size)| if size > l {
787                             (size, l, idx)
788                         } else if size > s {
789                             (l, size, li)
790                         } else {
791                             (l, s, li)
792                         });
793
794                     // we only warn if the largest variant is at least thrice as large as
795                     // the second-largest.
796                     if largest > slargest * 3 && slargest > 0 {
797                         cx.span_lint(VARIANT_SIZE_DIFFERENCES,
798                                      enum_definition.variants[largest_index].span,
799                                      &format!("enum variant is more than three times larger \
800                                                ({} bytes) than the next largest",
801                                               largest));
802                     }
803                 }
804             }
805         }
806     }
807 }