]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/types.rs
Auto merge of #28821 - dagnir:formatting-fix, r=steveklabnik
[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 use middle::{infer};
12 use middle::def_id::DefId;
13 use middle::subst::Substs;
14 use middle::ty::{self, Ty};
15 use middle::const_eval::{eval_const_expr_partial, ConstVal};
16 use middle::const_eval::EvalHint::ExprTypeChecked;
17 use util::nodemap::{FnvHashSet};
18 use lint::{LateContext, LintContext, LintArray};
19 use lint::{LintPass, LateLintPass};
20
21 use std::cmp;
22 use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
23
24 use syntax::{abi, ast};
25 use syntax::attr::{self, AttrMetaMethods};
26 use syntax::codemap::{self, Span};
27 use syntax::feature_gate::{emit_feature_err, GateIssue};
28 use syntax::ast::{TyIs, TyUs, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64};
29
30 use rustc_front::hir;
31 use rustc_front::visit::{self, Visitor};
32 use rustc_front::util::is_shift_binop;
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 #[derive(Copy, Clone)]
53 pub struct TypeLimits {
54     /// Id of the last visited negated expression
55     negated_expr_id: ast::NodeId,
56 }
57
58 impl TypeLimits {
59     pub fn new() -> TypeLimits {
60         TypeLimits {
61             negated_expr_id: !0,
62         }
63     }
64 }
65
66 impl LintPass for TypeLimits {
67     fn get_lints(&self) -> LintArray {
68         lint_array!(UNUSED_COMPARISONS, OVERFLOWING_LITERALS, EXCEEDING_BITSHIFTS)
69     }
70 }
71
72 impl LateLintPass for TypeLimits {
73     fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
74         match e.node {
75             hir::ExprUnary(hir::UnNeg, ref expr) => {
76                 match expr.node  {
77                     hir::ExprLit(ref lit) => {
78                         match lit.node {
79                             ast::LitInt(_, ast::UnsignedIntLit(_)) => {
80                                 check_unsigned_negation_feature(cx, e.span);
81                             },
82                             ast::LitInt(_, ast::UnsuffixedIntLit(_)) => {
83                                 if let ty::TyUint(_) = cx.tcx.node_id_to_type(e.id).sty {
84                                     check_unsigned_negation_feature(cx, e.span);
85                                 }
86                             },
87                             _ => ()
88                         }
89                     },
90                     _ => {
91                         let t = cx.tcx.node_id_to_type(expr.id);
92                         match t.sty {
93                             ty::TyUint(_) => {
94                                 check_unsigned_negation_feature(cx, e.span);
95                             },
96                             _ => ()
97                         }
98                     }
99                 };
100                 // propagate negation, if the negation itself isn't negated
101                 if self.negated_expr_id != e.id {
102                     self.negated_expr_id = expr.id;
103                 }
104             },
105             hir::ExprBinary(binop, ref l, ref r) => {
106                 if is_comparison(binop) && !check_limits(cx.tcx, binop, &**l, &**r) {
107                     cx.span_lint(UNUSED_COMPARISONS, e.span,
108                                  "comparison is useless due to type limits");
109                 }
110
111                 if is_shift_binop(binop.node) {
112                     let opt_ty_bits = match cx.tcx.node_id_to_type(l.id).sty {
113                         ty::TyInt(t) => Some(int_ty_bits(t, cx.sess().target.int_type)),
114                         ty::TyUint(t) => Some(uint_ty_bits(t, cx.sess().target.uint_type)),
115                         _ => None
116                     };
117
118                     if let Some(bits) = opt_ty_bits {
119                         let exceeding = if let hir::ExprLit(ref lit) = r.node {
120                             if let ast::LitInt(shift, _) = lit.node { shift >= bits }
121                             else { false }
122                         } else {
123                             match eval_const_expr_partial(cx.tcx, &r, ExprTypeChecked) {
124                                 Ok(ConstVal::Int(shift)) => { shift as u64 >= bits },
125                                 Ok(ConstVal::Uint(shift)) => { shift >= bits },
126                                 _ => { false }
127                             }
128                         };
129                         if exceeding {
130                             cx.span_lint(EXCEEDING_BITSHIFTS, e.span,
131                                          "bitshift exceeds the type's number of bits");
132                         }
133                     };
134                 }
135             },
136             hir::ExprLit(ref lit) => {
137                 match cx.tcx.node_id_to_type(e.id).sty {
138                     ty::TyInt(t) => {
139                         match lit.node {
140                             ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
141                             ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => {
142                                 let int_type = if let ast::TyIs = t {
143                                     cx.sess().target.int_type
144                                 } else {
145                                     t
146                                 };
147                                 let (_, max) = int_ty_range(int_type);
148                                 let negative = self.negated_expr_id == e.id;
149
150                                 // Detect literal value out of range [min, max] inclusive
151                                 // avoiding use of -min to prevent overflow/panic
152                                 if (negative && v > max as u64 + 1) ||
153                                    (!negative && v > max as u64) {
154                                     cx.span_lint(OVERFLOWING_LITERALS, e.span,
155                                                  &*format!("literal out of range for {:?}", t));
156                                     return;
157                                 }
158                             }
159                             _ => panic!()
160                         };
161                     },
162                     ty::TyUint(t) => {
163                         let uint_type = if let ast::TyUs = t {
164                             cx.sess().target.uint_type
165                         } else {
166                             t
167                         };
168                         let (min, max) = uint_ty_range(uint_type);
169                         let lit_val: u64 = match lit.node {
170                             ast::LitByte(_v) => return,  // _v is u8, within range by definition
171                             ast::LitInt(v, _) => v,
172                             _ => panic!()
173                         };
174                         if lit_val < min || lit_val > max {
175                             cx.span_lint(OVERFLOWING_LITERALS, e.span,
176                                          &*format!("literal out of range for {:?}", t));
177                         }
178                     },
179                     ty::TyFloat(t) => {
180                         let (min, max) = float_ty_range(t);
181                         let lit_val: f64 = match lit.node {
182                             ast::LitFloat(ref v, _) |
183                             ast::LitFloatUnsuffixed(ref v) => {
184                                 match v.parse() {
185                                     Ok(f) => f,
186                                     Err(_) => return
187                                 }
188                             }
189                             _ => panic!()
190                         };
191                         if lit_val < min || lit_val > max {
192                             cx.span_lint(OVERFLOWING_LITERALS, e.span,
193                                          &*format!("literal out of range for {:?}", t));
194                         }
195                     },
196                     _ => ()
197                 };
198             },
199             _ => ()
200         };
201
202         fn is_valid<T:cmp::PartialOrd>(binop: hir::BinOp, v: T,
203                                 min: T, max: T) -> bool {
204             match binop.node {
205                 hir::BiLt => v >  min && v <= max,
206                 hir::BiLe => v >= min && v <  max,
207                 hir::BiGt => v >= min && v <  max,
208                 hir::BiGe => v >  min && v <= max,
209                 hir::BiEq | hir::BiNe => v >= min && v <= max,
210                 _ => panic!()
211             }
212         }
213
214         fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
215             codemap::respan(binop.span, match binop.node {
216                 hir::BiLt => hir::BiGt,
217                 hir::BiLe => hir::BiGe,
218                 hir::BiGt => hir::BiLt,
219                 hir::BiGe => hir::BiLe,
220                 _ => return binop
221             })
222         }
223
224         // for isize & usize, be conservative with the warnings, so that the
225         // warnings are consistent between 32- and 64-bit platforms
226         fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
227             match int_ty {
228                 ast::TyIs => (i64::MIN,        i64::MAX),
229                 ast::TyI8 =>    (i8::MIN  as i64, i8::MAX  as i64),
230                 ast::TyI16 =>   (i16::MIN as i64, i16::MAX as i64),
231                 ast::TyI32 =>   (i32::MIN as i64, i32::MAX as i64),
232                 ast::TyI64 =>   (i64::MIN,        i64::MAX)
233             }
234         }
235
236         fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
237             match uint_ty {
238                 ast::TyUs => (u64::MIN,         u64::MAX),
239                 ast::TyU8 =>    (u8::MIN   as u64, u8::MAX   as u64),
240                 ast::TyU16 =>   (u16::MIN  as u64, u16::MAX  as u64),
241                 ast::TyU32 =>   (u32::MIN  as u64, u32::MAX  as u64),
242                 ast::TyU64 =>   (u64::MIN,         u64::MAX)
243             }
244         }
245
246         fn float_ty_range(float_ty: ast::FloatTy) -> (f64, f64) {
247             match float_ty {
248                 ast::TyF32 => (f32::MIN as f64, f32::MAX as f64),
249                 ast::TyF64 => (f64::MIN,        f64::MAX)
250             }
251         }
252
253         fn int_ty_bits(int_ty: ast::IntTy, target_int_ty: ast::IntTy) -> u64 {
254             match int_ty {
255                 ast::TyIs => int_ty_bits(target_int_ty, target_int_ty),
256                 ast::TyI8 =>    i8::BITS  as u64,
257                 ast::TyI16 =>   i16::BITS as u64,
258                 ast::TyI32 =>   i32::BITS as u64,
259                 ast::TyI64 =>   i64::BITS as u64
260             }
261         }
262
263         fn uint_ty_bits(uint_ty: ast::UintTy, target_uint_ty: ast::UintTy) -> u64 {
264             match uint_ty {
265                 ast::TyUs => uint_ty_bits(target_uint_ty, target_uint_ty),
266                 ast::TyU8 =>    u8::BITS  as u64,
267                 ast::TyU16 =>   u16::BITS as u64,
268                 ast::TyU32 =>   u32::BITS as u64,
269                 ast::TyU64 =>   u64::BITS as u64
270             }
271         }
272
273         fn check_limits(tcx: &ty::ctxt, binop: hir::BinOp,
274                         l: &hir::Expr, r: &hir::Expr) -> bool {
275             let (lit, expr, swap) = match (&l.node, &r.node) {
276                 (&hir::ExprLit(_), _) => (l, r, true),
277                 (_, &hir::ExprLit(_)) => (r, l, false),
278                 _ => return true
279             };
280             // Normalize the binop so that the literal is always on the RHS in
281             // the comparison
282             let norm_binop = if swap {
283                 rev_binop(binop)
284             } else {
285                 binop
286             };
287             match tcx.node_id_to_type(expr.id).sty {
288                 ty::TyInt(int_ty) => {
289                     let (min, max) = int_ty_range(int_ty);
290                     let lit_val: i64 = match lit.node {
291                         hir::ExprLit(ref li) => match li.node {
292                             ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
293                             ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => v as i64,
294                             ast::LitInt(v, ast::SignedIntLit(_, ast::Minus)) |
295                             ast::LitInt(v, ast::UnsuffixedIntLit(ast::Minus)) => -(v as i64),
296                             _ => return true
297                         },
298                         _ => panic!()
299                     };
300                     is_valid(norm_binop, lit_val, min, max)
301                 }
302                 ty::TyUint(uint_ty) => {
303                     let (min, max): (u64, u64) = uint_ty_range(uint_ty);
304                     let lit_val: u64 = match lit.node {
305                         hir::ExprLit(ref li) => match li.node {
306                             ast::LitInt(v, _) => v,
307                             _ => return true
308                         },
309                         _ => panic!()
310                     };
311                     is_valid(norm_binop, lit_val, min, max)
312                 }
313                 _ => true
314             }
315         }
316
317         fn is_comparison(binop: hir::BinOp) -> bool {
318             match binop.node {
319                 hir::BiEq | hir::BiLt | hir::BiLe |
320                 hir::BiNe | hir::BiGe | hir::BiGt => true,
321                 _ => false
322             }
323         }
324
325         fn check_unsigned_negation_feature(cx: &LateContext, span: Span) {
326             if !cx.sess().features.borrow().negate_unsigned {
327                 emit_feature_err(
328                     &cx.sess().parse_sess.span_diagnostic,
329                     "negate_unsigned",
330                     span,
331                     GateIssue::Language,
332                     "unary negation of unsigned integers may be removed in the future");
333             }
334         }
335     }
336 }
337
338 declare_lint! {
339     IMPROPER_CTYPES,
340     Warn,
341     "proper use of libc types in foreign modules"
342 }
343
344 struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
345     cx: &'a LateContext<'a, 'tcx>
346 }
347
348 enum FfiResult {
349     FfiSafe,
350     FfiUnsafe(&'static str),
351     FfiBadStruct(DefId, &'static str),
352     FfiBadEnum(DefId, &'static str)
353 }
354
355 /// Check if this enum can be safely exported based on the
356 /// "nullable pointer optimization". Currently restricted
357 /// to function pointers and references, but could be
358 /// expanded to cover NonZero raw pointers and newtypes.
359 /// FIXME: This duplicates code in trans.
360 fn is_repr_nullable_ptr<'tcx>(tcx: &ty::ctxt<'tcx>,
361                               def: ty::AdtDef<'tcx>,
362                               substs: &Substs<'tcx>)
363                               -> bool {
364     if def.variants.len() == 2 {
365         let data_idx;
366
367         if def.variants[0].fields.is_empty() {
368             data_idx = 1;
369         } else if def.variants[1].fields.is_empty() {
370             data_idx = 0;
371         } else {
372             return false;
373         }
374
375         if def.variants[data_idx].fields.len() == 1 {
376             match def.variants[data_idx].fields[0].ty(tcx, substs).sty {
377                 ty::TyBareFn(None, _) => { return true; }
378                 ty::TyRef(..) => { return true; }
379                 _ => { }
380             }
381         }
382     }
383     false
384 }
385
386 fn ast_ty_to_normalized<'tcx>(tcx: &ty::ctxt<'tcx>,
387                               id: ast::NodeId)
388                               -> Ty<'tcx> {
389     let tty = match tcx.ast_ty_to_ty_cache.borrow().get(&id) {
390         Some(&t) => t,
391         None => panic!("ast_ty_to_ty_cache was incomplete after typeck!")
392     };
393     infer::normalize_associated_type(tcx, &tty)
394 }
395
396 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
397     /// Check if the given type is "ffi-safe" (has a stable, well-defined
398     /// representation which can be exported to C code).
399     fn check_type_for_ffi(&self,
400                           cache: &mut FnvHashSet<Ty<'tcx>>,
401                           ty: Ty<'tcx>)
402                           -> FfiResult {
403         use self::FfiResult::*;
404         let cx = &self.cx.tcx;
405
406         // Protect against infinite recursion, for example
407         // `struct S(*mut S);`.
408         // FIXME: A recursion limit is necessary as well, for irregular
409         // recusive types.
410         if !cache.insert(ty) {
411             return FfiSafe;
412         }
413
414         match ty.sty {
415             ty::TyStruct(def, substs) => {
416                 if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
417                     return FfiUnsafe(
418                         "found struct without foreign-function-safe \
419                          representation annotation in foreign module, \
420                          consider adding a #[repr(C)] attribute to \
421                          the type");
422                 }
423
424                 // We can't completely trust repr(C) markings; make sure the
425                 // fields are actually safe.
426                 if def.struct_variant().fields.is_empty() {
427                     return FfiUnsafe(
428                         "found zero-size struct in foreign module, consider \
429                          adding a member to this struct");
430                 }
431
432                 for field in &def.struct_variant().fields {
433                     let field_ty = infer::normalize_associated_type(cx, &field.ty(cx, substs));
434                     let r = self.check_type_for_ffi(cache, field_ty);
435                     match r {
436                         FfiSafe => {}
437                         FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
438                         FfiUnsafe(s) => { return FfiBadStruct(def.did, s); }
439                     }
440                 }
441                 FfiSafe
442             }
443             ty::TyEnum(def, substs) => {
444                 if def.variants.is_empty() {
445                     // Empty enums are okay... although sort of useless.
446                     return FfiSafe
447                 }
448
449                 // Check for a repr() attribute to specify the size of the
450                 // discriminant.
451                 let repr_hints = cx.lookup_repr_hints(def.did);
452                 match &**repr_hints {
453                     [] => {
454                         // Special-case types like `Option<extern fn()>`.
455                         if !is_repr_nullable_ptr(cx, def, substs) {
456                             return FfiUnsafe(
457                                 "found enum without foreign-function-safe \
458                                  representation annotation in foreign module, \
459                                  consider adding a #[repr(...)] attribute to \
460                                  the type")
461                         }
462                     }
463                     [ref hint] => {
464                         if !hint.is_ffi_safe() {
465                             // FIXME: This shouldn't be reachable: we should check
466                             // this earlier.
467                             return FfiUnsafe(
468                                 "enum has unexpected #[repr(...)] attribute")
469                         }
470
471                         // Enum with an explicitly sized discriminant; either
472                         // a C-style enum or a discriminated union.
473
474                         // The layout of enum variants is implicitly repr(C).
475                         // FIXME: Is that correct?
476                     }
477                     _ => {
478                         // FIXME: This shouldn't be reachable: we should check
479                         // this earlier.
480                         return FfiUnsafe(
481                             "enum has too many #[repr(...)] attributes");
482                     }
483                 }
484
485                 // Check the contained variants.
486                 for variant in &def.variants {
487                     for field in &variant.fields {
488                         let arg = infer::normalize_associated_type(cx, &field.ty(cx, substs));
489                         let r = self.check_type_for_ffi(cache, arg);
490                         match r {
491                             FfiSafe => {}
492                             FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
493                             FfiUnsafe(s) => { return FfiBadEnum(def.did, s); }
494                         }
495                     }
496                 }
497                 FfiSafe
498             }
499
500             ty::TyInt(ast::TyIs) => {
501                 FfiUnsafe("found Rust type `isize` in foreign module, while \
502                           `libc::c_int` or `libc::c_long` should be used")
503             }
504             ty::TyUint(ast::TyUs) => {
505                 FfiUnsafe("found Rust type `usize` in foreign module, while \
506                           `libc::c_uint` or `libc::c_ulong` should be used")
507             }
508             ty::TyChar => {
509                 FfiUnsafe("found Rust type `char` in foreign module, while \
510                            `u32` or `libc::wchar_t` should be used")
511             }
512
513             // Primitive types with a stable representation.
514             ty::TyBool | ty::TyInt(..) | ty::TyUint(..) |
515             ty::TyFloat(..) => FfiSafe,
516
517             ty::TyBox(..) => {
518                 FfiUnsafe("found Rust type Box<_> in foreign module, \
519                            consider using a raw pointer instead")
520             }
521
522             ty::TySlice(_) => {
523                 FfiUnsafe("found Rust slice type in foreign module, \
524                            consider using a raw pointer instead")
525             }
526
527             ty::TyTrait(..) => {
528                 FfiUnsafe("found Rust trait type in foreign module, \
529                            consider using a raw pointer instead")
530             }
531
532             ty::TyStr => {
533                 FfiUnsafe("found Rust type `str` in foreign module; \
534                            consider using a `*const libc::c_char`")
535             }
536
537             ty::TyTuple(_) => {
538                 FfiUnsafe("found Rust tuple type in foreign module; \
539                            consider using a struct instead`")
540             }
541
542             ty::TyRawPtr(ref m) | ty::TyRef(_, ref m) => {
543                 self.check_type_for_ffi(cache, m.ty)
544             }
545
546             ty::TyArray(ty, _) => {
547                 self.check_type_for_ffi(cache, ty)
548             }
549
550             ty::TyBareFn(None, bare_fn) => {
551                 match bare_fn.abi {
552                     abi::Rust |
553                     abi::RustIntrinsic |
554                     abi::PlatformIntrinsic |
555                     abi::RustCall => {
556                         return FfiUnsafe(
557                             "found function pointer with Rust calling \
558                              convention in foreign module; consider using an \
559                              `extern` function pointer")
560                     }
561                     _ => {}
562                 }
563
564                 let sig = cx.erase_late_bound_regions(&bare_fn.sig);
565                 match sig.output {
566                     ty::FnDiverging => {}
567                     ty::FnConverging(output) => {
568                         if !output.is_nil() {
569                             let r = self.check_type_for_ffi(cache, output);
570                             match r {
571                                 FfiSafe => {}
572                                 _ => { return r; }
573                             }
574                         }
575                     }
576                 }
577                 for arg in sig.inputs {
578                     let r = self.check_type_for_ffi(cache, arg);
579                     match r {
580                         FfiSafe => {}
581                         _ => { return r; }
582                     }
583                 }
584                 FfiSafe
585             }
586
587             ty::TyParam(..) | ty::TyInfer(..) | ty::TyError |
588             ty::TyClosure(..) | ty::TyProjection(..) |
589             ty::TyBareFn(Some(_), _) => {
590                 panic!("Unexpected type in foreign function")
591             }
592         }
593     }
594
595     fn check_def(&mut self, sp: Span, id: ast::NodeId) {
596         let tty = ast_ty_to_normalized(self.cx.tcx, id);
597
598         match ImproperCTypesVisitor::check_type_for_ffi(self, &mut FnvHashSet(), tty) {
599             FfiResult::FfiSafe => {}
600             FfiResult::FfiUnsafe(s) => {
601                 self.cx.span_lint(IMPROPER_CTYPES, sp, s);
602             }
603             FfiResult::FfiBadStruct(_, s) => {
604                 // FIXME: This diagnostic is difficult to read, and doesn't
605                 // point at the relevant field.
606                 self.cx.span_lint(IMPROPER_CTYPES, sp,
607                     &format!("found non-foreign-function-safe member in \
608                               struct marked #[repr(C)]: {}", s));
609             }
610             FfiResult::FfiBadEnum(_, s) => {
611                 // FIXME: This diagnostic is difficult to read, and doesn't
612                 // point at the relevant variant.
613                 self.cx.span_lint(IMPROPER_CTYPES, sp,
614                     &format!("found non-foreign-function-safe member in \
615                               enum: {}", s));
616             }
617         }
618     }
619 }
620
621 impl<'a, 'tcx, 'v> Visitor<'v> for ImproperCTypesVisitor<'a, 'tcx> {
622     fn visit_ty(&mut self, ty: &hir::Ty) {
623         match ty.node {
624             hir::TyPath(..) |
625             hir::TyBareFn(..) => self.check_def(ty.span, ty.id),
626             hir::TyVec(..) => {
627                 self.cx.span_lint(IMPROPER_CTYPES, ty.span,
628                     "found Rust slice type in foreign module, consider \
629                      using a raw pointer instead");
630             }
631             hir::TyFixedLengthVec(ref ty, _) => self.visit_ty(ty),
632             hir::TyTup(..) => {
633                 self.cx.span_lint(IMPROPER_CTYPES, ty.span,
634                     "found Rust tuple type in foreign module; \
635                      consider using a struct instead`")
636             }
637             _ => visit::walk_ty(self, ty)
638         }
639     }
640 }
641
642 #[derive(Copy, Clone)]
643 pub struct ImproperCTypes;
644
645 impl LintPass for ImproperCTypes {
646     fn get_lints(&self) -> LintArray {
647         lint_array!(IMPROPER_CTYPES)
648     }
649 }
650
651 impl LateLintPass for ImproperCTypes {
652     fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
653         fn check_ty(cx: &LateContext, ty: &hir::Ty) {
654             let mut vis = ImproperCTypesVisitor { cx: cx };
655             vis.visit_ty(ty);
656         }
657
658         fn check_foreign_fn(cx: &LateContext, decl: &hir::FnDecl) {
659             for input in &decl.inputs {
660                 check_ty(cx, &*input.ty);
661             }
662             if let hir::Return(ref ret_ty) = decl.output {
663                 let tty = ast_ty_to_normalized(cx.tcx, ret_ty.id);
664                 if !tty.is_nil() {
665                     check_ty(cx, &ret_ty);
666                 }
667             }
668         }
669
670         match it.node {
671             hir::ItemForeignMod(ref nmod)
672                 if nmod.abi != abi::RustIntrinsic &&
673                    nmod.abi != abi::PlatformIntrinsic => {
674                 for ni in &nmod.items {
675                     match ni.node {
676                         hir::ForeignItemFn(ref decl, _) => check_foreign_fn(cx, &**decl),
677                         hir::ForeignItemStatic(ref t, _) => check_ty(cx, &**t)
678                     }
679                 }
680             }
681             _ => (),
682         }
683     }
684 }
685