]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/types.rs
Rollup merge of #67657 - jumbatm:cleanup-const-hack, r=oli-obk
[rust.git] / src / librustc_lint / types.rs
1 #![allow(non_snake_case)]
2
3 use crate::hir::def_id::DefId;
4 use lint::{LateContext, LintArray, LintContext};
5 use lint::{LateLintPass, LintPass};
6 use rustc::hir::{is_range_literal, ExprKind, Node};
7 use rustc::ty::layout::{self, IntegerExt, LayoutOf, SizeSkeleton, VariantIdx};
8 use rustc::ty::subst::SubstsRef;
9 use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
10 use rustc::{lint, util};
11 use rustc_index::vec::Idx;
12 use util::nodemap::FxHashSet;
13
14 use std::cmp;
15 use std::{f32, f64, i16, i32, i64, i8, u16, u32, u64, u8};
16
17 use rustc_target::spec::abi::Abi;
18 use syntax::errors::Applicability;
19 use syntax::symbol::sym;
20 use syntax::{ast, attr, source_map};
21 use syntax_pos::Span;
22
23 use rustc::hir;
24
25 use rustc::mir::interpret::{sign_extend, truncate};
26
27 use log::debug;
28
29 declare_lint! {
30     UNUSED_COMPARISONS,
31     Warn,
32     "comparisons made useless by limits of the types involved"
33 }
34
35 declare_lint! {
36     OVERFLOWING_LITERALS,
37     Deny,
38     "literal out of range for its type"
39 }
40
41 declare_lint! {
42     VARIANT_SIZE_DIFFERENCES,
43     Allow,
44     "detects enums with widely varying variant sizes"
45 }
46
47 #[derive(Copy, Clone)]
48 pub struct TypeLimits {
49     /// Id of the last visited negated expression
50     negated_expr_id: hir::HirId,
51 }
52
53 impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS]);
54
55 impl TypeLimits {
56     pub fn new() -> TypeLimits {
57         TypeLimits { negated_expr_id: hir::DUMMY_HIR_ID }
58     }
59 }
60
61 /// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint.
62 /// Returns `true` iff the lint was overridden.
63 fn lint_overflowing_range_endpoint<'a, 'tcx>(
64     cx: &LateContext<'a, 'tcx>,
65     lit: &hir::Lit,
66     lit_val: u128,
67     max: u128,
68     expr: &'tcx hir::Expr<'tcx>,
69     parent_expr: &'tcx hir::Expr<'tcx>,
70     ty: &str,
71 ) -> bool {
72     // We only want to handle exclusive (`..`) ranges,
73     // which are represented as `ExprKind::Struct`.
74     if let ExprKind::Struct(_, eps, _) = &parent_expr.kind {
75         if eps.len() != 2 {
76             return false;
77         }
78         // We can suggest using an inclusive range
79         // (`..=`) instead only if it is the `end` that is
80         // overflowing and only by 1.
81         if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max {
82             let mut err = cx.struct_span_lint(
83                 OVERFLOWING_LITERALS,
84                 parent_expr.span,
85                 &format!("range endpoint is out of range for `{}`", ty),
86             );
87             if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) {
88                 use ast::{LitIntType, LitKind};
89                 // We need to preserve the literal's suffix,
90                 // as it may determine typing information.
91                 let suffix = match lit.node {
92                     LitKind::Int(_, LitIntType::Signed(s)) => format!("{}", s.name_str()),
93                     LitKind::Int(_, LitIntType::Unsigned(s)) => format!("{}", s.name_str()),
94                     LitKind::Int(_, LitIntType::Unsuffixed) => "".to_owned(),
95                     _ => bug!(),
96                 };
97                 let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
98                 err.span_suggestion(
99                     parent_expr.span,
100                     &"use an inclusive range instead",
101                     suggestion,
102                     Applicability::MachineApplicable,
103                 );
104                 err.emit();
105                 return true;
106             }
107         }
108     }
109
110     false
111 }
112
113 // For `isize` & `usize`, be conservative with the warnings, so that the
114 // warnings are consistent between 32- and 64-bit platforms.
115 fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) {
116     match int_ty {
117         ast::IntTy::Isize => (i64::min_value() as i128, i64::max_value() as i128),
118         ast::IntTy::I8 => (i8::min_value() as i64 as i128, i8::max_value() as i128),
119         ast::IntTy::I16 => (i16::min_value() as i64 as i128, i16::max_value() as i128),
120         ast::IntTy::I32 => (i32::min_value() as i64 as i128, i32::max_value() as i128),
121         ast::IntTy::I64 => (i64::min_value() as i128, i64::max_value() as i128),
122         ast::IntTy::I128 => (i128::min_value() as i128, i128::max_value()),
123     }
124 }
125
126 fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) {
127     match uint_ty {
128         ast::UintTy::Usize => (u64::min_value() as u128, u64::max_value() as u128),
129         ast::UintTy::U8 => (u8::min_value() as u128, u8::max_value() as u128),
130         ast::UintTy::U16 => (u16::min_value() as u128, u16::max_value() as u128),
131         ast::UintTy::U32 => (u32::min_value() as u128, u32::max_value() as u128),
132         ast::UintTy::U64 => (u64::min_value() as u128, u64::max_value() as u128),
133         ast::UintTy::U128 => (u128::min_value(), u128::max_value()),
134     }
135 }
136
137 fn get_bin_hex_repr(cx: &LateContext<'_, '_>, lit: &hir::Lit) -> Option<String> {
138     let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?;
139     let firstch = src.chars().next()?;
140
141     if firstch == '0' {
142         match src.chars().nth(1) {
143             Some('x') | Some('b') => return Some(src),
144             _ => return None,
145         }
146     }
147
148     None
149 }
150
151 fn report_bin_hex_error(
152     cx: &LateContext<'_, '_>,
153     expr: &hir::Expr<'_>,
154     ty: attr::IntType,
155     repr_str: String,
156     val: u128,
157     negative: bool,
158 ) {
159     let size = layout::Integer::from_attr(&cx.tcx, ty).size();
160     let (t, actually) = match ty {
161         attr::IntType::SignedInt(t) => {
162             let actually = sign_extend(val, size) as i128;
163             (t.name_str(), actually.to_string())
164         }
165         attr::IntType::UnsignedInt(t) => {
166             let actually = truncate(val, size);
167             (t.name_str(), actually.to_string())
168         }
169     };
170     let mut err = cx.struct_span_lint(
171         OVERFLOWING_LITERALS,
172         expr.span,
173         &format!("literal out of range for {}", t),
174     );
175     err.note(&format!(
176         "the literal `{}` (decimal `{}`) does not fit into \
177             an `{}` and will become `{}{}`",
178         repr_str, val, t, actually, t
179     ));
180     if let Some(sugg_ty) = get_type_suggestion(&cx.tables.node_type(expr.hir_id), val, negative) {
181         if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
182             let (sans_suffix, _) = repr_str.split_at(pos);
183             err.span_suggestion(
184                 expr.span,
185                 &format!("consider using `{}` instead", sugg_ty),
186                 format!("{}{}", sans_suffix, sugg_ty),
187                 Applicability::MachineApplicable,
188             );
189         } else {
190             err.help(&format!("consider using `{}` instead", sugg_ty));
191         }
192     }
193
194     err.emit();
195 }
196
197 // This function finds the next fitting type and generates a suggestion string.
198 // It searches for fitting types in the following way (`X < Y`):
199 //  - `iX`: if literal fits in `uX` => `uX`, else => `iY`
200 //  - `-iX` => `iY`
201 //  - `uX` => `uY`
202 //
203 // No suggestion for: `isize`, `usize`.
204 fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> {
205     use syntax::ast::IntTy::*;
206     use syntax::ast::UintTy::*;
207     macro_rules! find_fit {
208         ($ty:expr, $val:expr, $negative:expr,
209          $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => {
210             {
211                 let _neg = if negative { 1 } else { 0 };
212                 match $ty {
213                     $($type => {
214                         $(if !negative && val <= uint_ty_range($utypes).1 {
215                             return Some($utypes.name_str())
216                         })*
217                         $(if val <= int_ty_range($itypes).1 as u128 + _neg {
218                             return Some($itypes.name_str())
219                         })*
220                         None
221                     },)+
222                     _ => None
223                 }
224             }
225         }
226     }
227     match t.kind {
228         ty::Int(i) => find_fit!(i, val, negative,
229                       I8 => [U8] => [I16, I32, I64, I128],
230                       I16 => [U16] => [I32, I64, I128],
231                       I32 => [U32] => [I64, I128],
232                       I64 => [U64] => [I128],
233                       I128 => [U128] => []),
234         ty::Uint(u) => find_fit!(u, val, negative,
235                       U8 => [U8, U16, U32, U64, U128] => [],
236                       U16 => [U16, U32, U64, U128] => [],
237                       U32 => [U32, U64, U128] => [],
238                       U64 => [U64, U128] => [],
239                       U128 => [U128] => []),
240         _ => None,
241     }
242 }
243
244 fn lint_int_literal<'a, 'tcx>(
245     cx: &LateContext<'a, 'tcx>,
246     type_limits: &TypeLimits,
247     e: &'tcx hir::Expr<'tcx>,
248     lit: &hir::Lit,
249     t: ast::IntTy,
250     v: u128,
251 ) {
252     let int_type = t.normalize(cx.sess().target.ptr_width);
253     let (_, max) = int_ty_range(int_type);
254     let max = max as u128;
255     let negative = type_limits.negated_expr_id == e.hir_id;
256
257     // Detect literal value out of range [min, max] inclusive
258     // avoiding use of -min to prevent overflow/panic
259     if (negative && v > max + 1) || (!negative && v > max) {
260         if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
261             report_bin_hex_error(cx, e, attr::IntType::SignedInt(t), repr_str, v, negative);
262             return;
263         }
264
265         let par_id = cx.tcx.hir().get_parent_node(e.hir_id);
266         if let Node::Expr(par_e) = cx.tcx.hir().get(par_id) {
267             if let hir::ExprKind::Struct(..) = par_e.kind {
268                 if is_range_literal(cx.sess().source_map(), par_e)
269                     && lint_overflowing_range_endpoint(cx, lit, v, max, e, par_e, t.name_str())
270                 {
271                     // The overflowing literal lint was overridden.
272                     return;
273                 }
274             }
275         }
276
277         cx.span_lint(
278             OVERFLOWING_LITERALS,
279             e.span,
280             &format!("literal out of range for `{}`", t.name_str()),
281         );
282     }
283 }
284
285 fn lint_uint_literal<'a, 'tcx>(
286     cx: &LateContext<'a, 'tcx>,
287     e: &'tcx hir::Expr<'tcx>,
288     lit: &hir::Lit,
289     t: ast::UintTy,
290 ) {
291     let uint_type = t.normalize(cx.sess().target.ptr_width);
292     let (min, max) = uint_ty_range(uint_type);
293     let lit_val: u128 = match lit.node {
294         // _v is u8, within range by definition
295         ast::LitKind::Byte(_v) => return,
296         ast::LitKind::Int(v, _) => v,
297         _ => bug!(),
298     };
299     if lit_val < min || lit_val > max {
300         let parent_id = cx.tcx.hir().get_parent_node(e.hir_id);
301         if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) {
302             match par_e.kind {
303                 hir::ExprKind::Cast(..) => {
304                     if let ty::Char = cx.tables.expr_ty(par_e).kind {
305                         let mut err = cx.struct_span_lint(
306                             OVERFLOWING_LITERALS,
307                             par_e.span,
308                             "only `u8` can be cast into `char`",
309                         );
310                         err.span_suggestion(
311                             par_e.span,
312                             &"use a `char` literal instead",
313                             format!("'\\u{{{:X}}}'", lit_val),
314                             Applicability::MachineApplicable,
315                         );
316                         err.emit();
317                         return;
318                     }
319                 }
320                 hir::ExprKind::Struct(..) if is_range_literal(cx.sess().source_map(), par_e) => {
321                     let t = t.name_str();
322                     if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, par_e, t) {
323                         // The overflowing literal lint was overridden.
324                         return;
325                     }
326                 }
327                 _ => {}
328             }
329         }
330         if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
331             report_bin_hex_error(cx, e, attr::IntType::UnsignedInt(t), repr_str, lit_val, false);
332             return;
333         }
334         cx.span_lint(
335             OVERFLOWING_LITERALS,
336             e.span,
337             &format!("literal out of range for `{}`", t.name_str()),
338         );
339     }
340 }
341
342 fn lint_literal<'a, 'tcx>(
343     cx: &LateContext<'a, 'tcx>,
344     type_limits: &TypeLimits,
345     e: &'tcx hir::Expr<'tcx>,
346     lit: &hir::Lit,
347 ) {
348     match cx.tables.node_type(e.hir_id).kind {
349         ty::Int(t) => {
350             match lit.node {
351                 ast::LitKind::Int(v, ast::LitIntType::Signed(_))
352                 | ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => {
353                     lint_int_literal(cx, type_limits, e, lit, t, v)
354                 }
355                 _ => bug!(),
356             };
357         }
358         ty::Uint(t) => lint_uint_literal(cx, e, lit, t),
359         ty::Float(t) => {
360             let is_infinite = match lit.node {
361                 ast::LitKind::Float(v, _) => match t {
362                     ast::FloatTy::F32 => v.as_str().parse().map(f32::is_infinite),
363                     ast::FloatTy::F64 => v.as_str().parse().map(f64::is_infinite),
364                 },
365                 _ => bug!(),
366             };
367             if is_infinite == Ok(true) {
368                 cx.span_lint(
369                     OVERFLOWING_LITERALS,
370                     e.span,
371                     &format!("literal out of range for `{}`", t.name_str()),
372                 );
373             }
374         }
375         _ => {}
376     }
377 }
378
379 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
380     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr<'tcx>) {
381         match e.kind {
382             hir::ExprKind::Unary(hir::UnNeg, ref expr) => {
383                 // propagate negation, if the negation itself isn't negated
384                 if self.negated_expr_id != e.hir_id {
385                     self.negated_expr_id = expr.hir_id;
386                 }
387             }
388             hir::ExprKind::Binary(binop, ref l, ref r) => {
389                 if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
390                     cx.span_lint(
391                         UNUSED_COMPARISONS,
392                         e.span,
393                         "comparison is useless due to type limits",
394                     );
395                 }
396             }
397             hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit),
398             _ => {}
399         };
400
401         fn is_valid<T: cmp::PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
402             match binop.node {
403                 hir::BinOpKind::Lt => v > min && v <= max,
404                 hir::BinOpKind::Le => v >= min && v < max,
405                 hir::BinOpKind::Gt => v >= min && v < max,
406                 hir::BinOpKind::Ge => v > min && v <= max,
407                 hir::BinOpKind::Eq | hir::BinOpKind::Ne => v >= min && v <= max,
408                 _ => bug!(),
409             }
410         }
411
412         fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
413             source_map::respan(
414                 binop.span,
415                 match binop.node {
416                     hir::BinOpKind::Lt => hir::BinOpKind::Gt,
417                     hir::BinOpKind::Le => hir::BinOpKind::Ge,
418                     hir::BinOpKind::Gt => hir::BinOpKind::Lt,
419                     hir::BinOpKind::Ge => hir::BinOpKind::Le,
420                     _ => return binop,
421                 },
422             )
423         }
424
425         fn check_limits(
426             cx: &LateContext<'_, '_>,
427             binop: hir::BinOp,
428             l: &hir::Expr<'_>,
429             r: &hir::Expr<'_>,
430         ) -> bool {
431             let (lit, expr, swap) = match (&l.kind, &r.kind) {
432                 (&hir::ExprKind::Lit(_), _) => (l, r, true),
433                 (_, &hir::ExprKind::Lit(_)) => (r, l, false),
434                 _ => return true,
435             };
436             // Normalize the binop so that the literal is always on the RHS in
437             // the comparison
438             let norm_binop = if swap { rev_binop(binop) } else { binop };
439             match cx.tables.node_type(expr.hir_id).kind {
440                 ty::Int(int_ty) => {
441                     let (min, max) = int_ty_range(int_ty);
442                     let lit_val: i128 = match lit.kind {
443                         hir::ExprKind::Lit(ref li) => match li.node {
444                             ast::LitKind::Int(v, ast::LitIntType::Signed(_))
445                             | ast::LitKind::Int(v, ast::LitIntType::Unsuffixed) => v as i128,
446                             _ => return true,
447                         },
448                         _ => bug!(),
449                     };
450                     is_valid(norm_binop, lit_val, min, max)
451                 }
452                 ty::Uint(uint_ty) => {
453                     let (min, max): (u128, u128) = uint_ty_range(uint_ty);
454                     let lit_val: u128 = match lit.kind {
455                         hir::ExprKind::Lit(ref li) => match li.node {
456                             ast::LitKind::Int(v, _) => v,
457                             _ => return true,
458                         },
459                         _ => bug!(),
460                     };
461                     is_valid(norm_binop, lit_val, min, max)
462                 }
463                 _ => true,
464             }
465         }
466
467         fn is_comparison(binop: hir::BinOp) -> bool {
468             match binop.node {
469                 hir::BinOpKind::Eq
470                 | hir::BinOpKind::Lt
471                 | hir::BinOpKind::Le
472                 | hir::BinOpKind::Ne
473                 | hir::BinOpKind::Ge
474                 | hir::BinOpKind::Gt => true,
475                 _ => false,
476             }
477         }
478     }
479 }
480
481 declare_lint! {
482     IMPROPER_CTYPES,
483     Warn,
484     "proper use of libc types in foreign modules"
485 }
486
487 declare_lint_pass!(ImproperCTypes => [IMPROPER_CTYPES]);
488
489 struct ImproperCTypesVisitor<'a, 'tcx> {
490     cx: &'a LateContext<'a, 'tcx>,
491 }
492
493 enum FfiResult<'tcx> {
494     FfiSafe,
495     FfiPhantom(Ty<'tcx>),
496     FfiUnsafe { ty: Ty<'tcx>, reason: &'static str, help: Option<&'static str> },
497 }
498
499 fn is_zst<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, ty: Ty<'tcx>) -> bool {
500     tcx.layout_of(tcx.param_env(did).and(ty)).map(|layout| layout.is_zst()).unwrap_or(false)
501 }
502
503 fn ty_is_known_nonnull<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
504     match ty.kind {
505         ty::FnPtr(_) => true,
506         ty::Ref(..) => true,
507         ty::Adt(field_def, substs) if field_def.repr.transparent() && !field_def.is_union() => {
508             for field in field_def.all_fields() {
509                 let field_ty =
510                     tcx.normalize_erasing_regions(ParamEnv::reveal_all(), field.ty(tcx, substs));
511                 if is_zst(tcx, field.did, field_ty) {
512                     continue;
513                 }
514
515                 let attrs = tcx.get_attrs(field_def.did);
516                 if attrs.iter().any(|a| a.check_name(sym::rustc_nonnull_optimization_guaranteed))
517                     || ty_is_known_nonnull(tcx, field_ty)
518                 {
519                     return true;
520                 }
521             }
522
523             false
524         }
525         _ => false,
526     }
527 }
528
529 /// Check if this enum can be safely exported based on the
530 /// "nullable pointer optimization". Currently restricted
531 /// to function pointers, references, core::num::NonZero*,
532 /// core::ptr::NonNull, and #[repr(transparent)] newtypes.
533 /// FIXME: This duplicates code in codegen.
534 fn is_repr_nullable_ptr<'tcx>(
535     tcx: TyCtxt<'tcx>,
536     ty: Ty<'tcx>,
537     ty_def: &'tcx ty::AdtDef,
538     substs: SubstsRef<'tcx>,
539 ) -> bool {
540     if ty_def.variants.len() != 2 {
541         return false;
542     }
543
544     let get_variant_fields = |index| &ty_def.variants[VariantIdx::new(index)].fields;
545     let variant_fields = [get_variant_fields(0), get_variant_fields(1)];
546     let fields = if variant_fields[0].is_empty() {
547         &variant_fields[1]
548     } else if variant_fields[1].is_empty() {
549         &variant_fields[0]
550     } else {
551         return false;
552     };
553
554     if fields.len() != 1 {
555         return false;
556     }
557
558     let field_ty = fields[0].ty(tcx, substs);
559     if !ty_is_known_nonnull(tcx, field_ty) {
560         return false;
561     }
562
563     // At this point, the field's type is known to be nonnull and the parent enum is Option-like.
564     // If the computed size for the field and the enum are different, the nonnull optimization isn't
565     // being applied (and we've got a problem somewhere).
566     let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, ParamEnv::reveal_all()).unwrap();
567     if !compute_size_skeleton(ty).same_size(compute_size_skeleton(field_ty)) {
568         bug!("improper_ctypes: Option nonnull optimization not applied?");
569     }
570
571     true
572 }
573
574 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
575     /// Check if the type is array and emit an unsafe type lint.
576     fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
577         if let ty::Array(..) = ty.kind {
578             self.emit_ffi_unsafe_type_lint(
579                 ty,
580                 sp,
581                 "passing raw arrays by value is not FFI-safe",
582                 Some("consider passing a pointer to the array"),
583             );
584             true
585         } else {
586             false
587         }
588     }
589
590     /// Checks if the given type is "ffi-safe" (has a stable, well-defined
591     /// representation which can be exported to C code).
592     fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx> {
593         use FfiResult::*;
594
595         let cx = self.cx.tcx;
596
597         // Protect against infinite recursion, for example
598         // `struct S(*mut S);`.
599         // FIXME: A recursion limit is necessary as well, for irregular
600         // recursive types.
601         if !cache.insert(ty) {
602             return FfiSafe;
603         }
604
605         match ty.kind {
606             ty::Adt(def, substs) => {
607                 if def.is_phantom_data() {
608                     return FfiPhantom(ty);
609                 }
610                 match def.adt_kind() {
611                     AdtKind::Struct => {
612                         if !def.repr.c() && !def.repr.transparent() {
613                             return FfiUnsafe {
614                                 ty,
615                                 reason: "this struct has unspecified layout",
616                                 help: Some(
617                                     "consider adding a `#[repr(C)]` or \
618                                             `#[repr(transparent)]` attribute to this struct",
619                                 ),
620                             };
621                         }
622
623                         let is_non_exhaustive =
624                             def.non_enum_variant().is_field_list_non_exhaustive();
625                         if is_non_exhaustive && !def.did.is_local() {
626                             return FfiUnsafe {
627                                 ty,
628                                 reason: "this struct is non-exhaustive",
629                                 help: None,
630                             };
631                         }
632
633                         if def.non_enum_variant().fields.is_empty() {
634                             return FfiUnsafe {
635                                 ty,
636                                 reason: "this struct has no fields",
637                                 help: Some("consider adding a member to this struct"),
638                             };
639                         }
640
641                         // We can't completely trust repr(C) and repr(transparent) markings;
642                         // make sure the fields are actually safe.
643                         let mut all_phantom = true;
644                         for field in &def.non_enum_variant().fields {
645                             let field_ty = cx.normalize_erasing_regions(
646                                 ParamEnv::reveal_all(),
647                                 field.ty(cx, substs),
648                             );
649                             // repr(transparent) types are allowed to have arbitrary ZSTs, not just
650                             // PhantomData -- skip checking all ZST fields
651                             if def.repr.transparent() && is_zst(cx, field.did, field_ty) {
652                                 continue;
653                             }
654                             let r = self.check_type_for_ffi(cache, field_ty);
655                             match r {
656                                 FfiSafe => {
657                                     all_phantom = false;
658                                 }
659                                 FfiPhantom(..) => {}
660                                 FfiUnsafe { .. } => {
661                                     return r;
662                                 }
663                             }
664                         }
665
666                         if all_phantom { FfiPhantom(ty) } else { FfiSafe }
667                     }
668                     AdtKind::Union => {
669                         if !def.repr.c() && !def.repr.transparent() {
670                             return FfiUnsafe {
671                                 ty,
672                                 reason: "this union has unspecified layout",
673                                 help: Some(
674                                     "consider adding a `#[repr(C)]` or \
675                                             `#[repr(transparent)]` attribute to this union",
676                                 ),
677                             };
678                         }
679
680                         if def.non_enum_variant().fields.is_empty() {
681                             return FfiUnsafe {
682                                 ty,
683                                 reason: "this union has no fields",
684                                 help: Some("consider adding a field to this union"),
685                             };
686                         }
687
688                         let mut all_phantom = true;
689                         for field in &def.non_enum_variant().fields {
690                             let field_ty = cx.normalize_erasing_regions(
691                                 ParamEnv::reveal_all(),
692                                 field.ty(cx, substs),
693                             );
694                             // repr(transparent) types are allowed to have arbitrary ZSTs, not just
695                             // PhantomData -- skip checking all ZST fields.
696                             if def.repr.transparent() && is_zst(cx, field.did, field_ty) {
697                                 continue;
698                             }
699                             let r = self.check_type_for_ffi(cache, field_ty);
700                             match r {
701                                 FfiSafe => {
702                                     all_phantom = false;
703                                 }
704                                 FfiPhantom(..) => {}
705                                 FfiUnsafe { .. } => {
706                                     return r;
707                                 }
708                             }
709                         }
710
711                         if all_phantom { FfiPhantom(ty) } else { FfiSafe }
712                     }
713                     AdtKind::Enum => {
714                         if def.variants.is_empty() {
715                             // Empty enums are okay... although sort of useless.
716                             return FfiSafe;
717                         }
718
719                         // Check for a repr() attribute to specify the size of the
720                         // discriminant.
721                         if !def.repr.c() && !def.repr.transparent() && def.repr.int.is_none() {
722                             // Special-case types like `Option<extern fn()>`.
723                             if !is_repr_nullable_ptr(cx, ty, def, substs) {
724                                 return FfiUnsafe {
725                                     ty,
726                                     reason: "enum has no representation hint",
727                                     help: Some(
728                                         "consider adding a `#[repr(C)]`, \
729                                                 `#[repr(transparent)]`, or integer `#[repr(...)]` \
730                                                 attribute to this enum",
731                                     ),
732                                 };
733                             }
734                         }
735
736                         if def.is_variant_list_non_exhaustive() && !def.did.is_local() {
737                             return FfiUnsafe {
738                                 ty,
739                                 reason: "this enum is non-exhaustive",
740                                 help: None,
741                             };
742                         }
743
744                         // Check the contained variants.
745                         for variant in &def.variants {
746                             let is_non_exhaustive = variant.is_field_list_non_exhaustive();
747                             if is_non_exhaustive && !variant.def_id.is_local() {
748                                 return FfiUnsafe {
749                                     ty,
750                                     reason: "this enum has non-exhaustive variants",
751                                     help: None,
752                                 };
753                             }
754
755                             for field in &variant.fields {
756                                 let field_ty = cx.normalize_erasing_regions(
757                                     ParamEnv::reveal_all(),
758                                     field.ty(cx, substs),
759                                 );
760                                 // repr(transparent) types are allowed to have arbitrary ZSTs, not
761                                 // just PhantomData -- skip checking all ZST fields.
762                                 if def.repr.transparent() && is_zst(cx, field.did, field_ty) {
763                                     continue;
764                                 }
765                                 let r = self.check_type_for_ffi(cache, field_ty);
766                                 match r {
767                                     FfiSafe => {}
768                                     FfiUnsafe { .. } => {
769                                         return r;
770                                     }
771                                     FfiPhantom(..) => {
772                                         return FfiUnsafe {
773                                             ty,
774                                             reason: "this enum contains a PhantomData field",
775                                             help: None,
776                                         };
777                                     }
778                                 }
779                             }
780                         }
781                         FfiSafe
782                     }
783                 }
784             }
785
786             ty::Char => FfiUnsafe {
787                 ty,
788                 reason: "the `char` type has no C equivalent",
789                 help: Some("consider using `u32` or `libc::wchar_t` instead"),
790             },
791
792             ty::Int(ast::IntTy::I128) | ty::Uint(ast::UintTy::U128) => FfiUnsafe {
793                 ty,
794                 reason: "128-bit integers don't currently have a known stable ABI",
795                 help: None,
796             },
797
798             // Primitive types with a stable representation.
799             ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
800
801             ty::Slice(_) => FfiUnsafe {
802                 ty,
803                 reason: "slices have no C equivalent",
804                 help: Some("consider using a raw pointer instead"),
805             },
806
807             ty::Dynamic(..) => {
808                 FfiUnsafe { ty, reason: "trait objects have no C equivalent", help: None }
809             }
810
811             ty::Str => FfiUnsafe {
812                 ty,
813                 reason: "string slices have no C equivalent",
814                 help: Some("consider using `*const u8` and a length instead"),
815             },
816
817             ty::Tuple(..) => FfiUnsafe {
818                 ty,
819                 reason: "tuples have unspecified layout",
820                 help: Some("consider using a struct instead"),
821             },
822
823             ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
824                 self.check_type_for_ffi(cache, ty)
825             }
826
827             ty::Array(inner_ty, _) => self.check_type_for_ffi(cache, inner_ty),
828
829             ty::FnPtr(sig) => {
830                 match sig.abi() {
831                     Abi::Rust | Abi::RustIntrinsic | Abi::PlatformIntrinsic | Abi::RustCall => {
832                         return FfiUnsafe {
833                             ty,
834                             reason: "this function pointer has Rust-specific calling convention",
835                             help: Some(
836                                 "consider using an `extern fn(...) -> ...` \
837                                         function pointer instead",
838                             ),
839                         };
840                     }
841                     _ => {}
842                 }
843
844                 let sig = cx.erase_late_bound_regions(&sig);
845                 if !sig.output().is_unit() {
846                     let r = self.check_type_for_ffi(cache, sig.output());
847                     match r {
848                         FfiSafe => {}
849                         _ => {
850                             return r;
851                         }
852                     }
853                 }
854                 for arg in sig.inputs() {
855                     let r = self.check_type_for_ffi(cache, arg);
856                     match r {
857                         FfiSafe => {}
858                         _ => {
859                             return r;
860                         }
861                     }
862                 }
863                 FfiSafe
864             }
865
866             ty::Foreign(..) => FfiSafe,
867
868             ty::Param(..)
869             | ty::Infer(..)
870             | ty::Bound(..)
871             | ty::Error
872             | ty::Closure(..)
873             | ty::Generator(..)
874             | ty::GeneratorWitness(..)
875             | ty::Placeholder(..)
876             | ty::UnnormalizedProjection(..)
877             | ty::Projection(..)
878             | ty::Opaque(..)
879             | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
880         }
881     }
882
883     fn emit_ffi_unsafe_type_lint(
884         &mut self,
885         ty: Ty<'tcx>,
886         sp: Span,
887         note: &str,
888         help: Option<&str>,
889     ) {
890         let mut diag = self.cx.struct_span_lint(
891             IMPROPER_CTYPES,
892             sp,
893             &format!("`extern` block uses type `{}`, which is not FFI-safe", ty),
894         );
895         diag.span_label(sp, "not FFI-safe");
896         if let Some(help) = help {
897             diag.help(help);
898         }
899         diag.note(note);
900         if let ty::Adt(def, _) = ty.kind {
901             if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did) {
902                 diag.span_note(sp, "type defined here");
903             }
904         }
905         diag.emit();
906     }
907
908     fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
909         use crate::rustc::ty::TypeFoldable;
910
911         struct ProhibitOpaqueTypes<'tcx> {
912             ty: Option<Ty<'tcx>>,
913         };
914
915         impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'tcx> {
916             fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
917                 if let ty::Opaque(..) = ty.kind {
918                     self.ty = Some(ty);
919                     true
920                 } else {
921                     ty.super_visit_with(self)
922                 }
923             }
924         }
925
926         let mut visitor = ProhibitOpaqueTypes { ty: None };
927         ty.visit_with(&mut visitor);
928         if let Some(ty) = visitor.ty {
929             self.emit_ffi_unsafe_type_lint(ty, sp, "opaque types have no C equivalent", None);
930             true
931         } else {
932             false
933         }
934     }
935
936     fn check_type_for_ffi_and_report_errors(&mut self, sp: Span, ty: Ty<'tcx>, is_static: bool) {
937         // We have to check for opaque types before `normalize_erasing_regions`,
938         // which will replace opaque types with their underlying concrete type.
939         if self.check_for_opaque_ty(sp, ty) {
940             // We've already emitted an error due to an opaque type.
941             return;
942         }
943
944         // it is only OK to use this function because extern fns cannot have
945         // any generic types right now:
946         let ty = self.cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
947         // C doesn't really support passing arrays by value.
948         // The only way to pass an array by value is through a struct.
949         // So we first test that the top level isn't an array,
950         // and then recursively check the types inside.
951         if !is_static && self.check_for_array_ty(sp, ty) {
952             return;
953         }
954
955         match self.check_type_for_ffi(&mut FxHashSet::default(), ty) {
956             FfiResult::FfiSafe => {}
957             FfiResult::FfiPhantom(ty) => {
958                 self.emit_ffi_unsafe_type_lint(ty, sp, "composed only of `PhantomData`", None);
959             }
960             FfiResult::FfiUnsafe { ty, reason, help } => {
961                 self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
962             }
963         }
964     }
965
966     fn check_foreign_fn(&mut self, id: hir::HirId, decl: &hir::FnDecl<'_>) {
967         let def_id = self.cx.tcx.hir().local_def_id(id);
968         let sig = self.cx.tcx.fn_sig(def_id);
969         let sig = self.cx.tcx.erase_late_bound_regions(&sig);
970
971         for (input_ty, input_hir) in sig.inputs().iter().zip(decl.inputs) {
972             self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty, false);
973         }
974
975         if let hir::Return(ref ret_hir) = decl.output {
976             let ret_ty = sig.output();
977             if !ret_ty.is_unit() {
978                 self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, false);
979             }
980         }
981     }
982
983     fn check_foreign_static(&mut self, id: hir::HirId, span: Span) {
984         let def_id = self.cx.tcx.hir().local_def_id(id);
985         let ty = self.cx.tcx.type_of(def_id);
986         self.check_type_for_ffi_and_report_errors(span, ty, true);
987     }
988 }
989
990 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImproperCTypes {
991     fn check_foreign_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::ForeignItem<'_>) {
992         let mut vis = ImproperCTypesVisitor { cx };
993         let abi = cx.tcx.hir().get_foreign_abi(it.hir_id);
994         if let Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi {
995             // Don't worry about types in internal ABIs.
996         } else {
997             match it.kind {
998                 hir::ForeignItemKind::Fn(ref decl, _, _) => {
999                     vis.check_foreign_fn(it.hir_id, decl);
1000                 }
1001                 hir::ForeignItemKind::Static(ref ty, _) => {
1002                     vis.check_foreign_static(it.hir_id, ty.span);
1003                 }
1004                 hir::ForeignItemKind::Type => (),
1005             }
1006         }
1007     }
1008 }
1009
1010 declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]);
1011
1012 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for VariantSizeDifferences {
1013     fn check_item(&mut self, cx: &LateContext<'_, '_>, it: &hir::Item<'_>) {
1014         if let hir::ItemKind::Enum(ref enum_definition, _) = it.kind {
1015             let item_def_id = cx.tcx.hir().local_def_id(it.hir_id);
1016             let t = cx.tcx.type_of(item_def_id);
1017             let ty = cx.tcx.erase_regions(&t);
1018             let layout = match cx.layout_of(ty) {
1019                 Ok(layout) => layout,
1020                 Err(ty::layout::LayoutError::Unknown(_)) => return,
1021                 Err(err @ ty::layout::LayoutError::SizeOverflow(_)) => {
1022                     bug!("failed to get layout for `{}`: {}", t, err);
1023                 }
1024             };
1025             let (variants, tag) = match layout.variants {
1026                 layout::Variants::Multiple {
1027                     discr_kind: layout::DiscriminantKind::Tag,
1028                     ref discr,
1029                     ref variants,
1030                     ..
1031                 } => (variants, discr),
1032                 _ => return,
1033             };
1034
1035             let discr_size = tag.value.size(&cx.tcx).bytes();
1036
1037             debug!(
1038                 "enum `{}` is {} bytes large with layout:\n{:#?}",
1039                 t,
1040                 layout.size.bytes(),
1041                 layout
1042             );
1043
1044             let (largest, slargest, largest_index) = enum_definition
1045                 .variants
1046                 .iter()
1047                 .zip(variants)
1048                 .map(|(variant, variant_layout)| {
1049                     // Subtract the size of the enum discriminant.
1050                     let bytes = variant_layout.size.bytes().saturating_sub(discr_size);
1051
1052                     debug!("- variant `{}` is {} bytes large", variant.ident, bytes);
1053                     bytes
1054                 })
1055                 .enumerate()
1056                 .fold((0, 0, 0), |(l, s, li), (idx, size)| {
1057                     if size > l {
1058                         (size, l, idx)
1059                     } else if size > s {
1060                         (l, size, li)
1061                     } else {
1062                         (l, s, li)
1063                     }
1064                 });
1065
1066             // We only warn if the largest variant is at least thrice as large as
1067             // the second-largest.
1068             if largest > slargest * 3 && slargest > 0 {
1069                 cx.span_lint(
1070                     VARIANT_SIZE_DIFFERENCES,
1071                     enum_definition.variants[largest_index].span,
1072                     &format!(
1073                         "enum variant is more than three times \
1074                                           larger ({} bytes) than the next largest",
1075                         largest
1076                     ),
1077                 );
1078             }
1079         }
1080     }
1081 }