1 #![allow(non_snake_case)]
3 use rustc
::hir
::{ExprKind, Node}
;
4 use crate::hir
::def_id
::DefId
;
5 use rustc
::hir
::lowering
::is_range_literal
;
6 use rustc
::ty
::subst
::SubstsRef
;
7 use rustc
::ty
::{self, AdtKind, ParamEnv, Ty, TyCtxt}
;
8 use rustc
::ty
::layout
::{self, IntegerExt, LayoutOf, VariantIdx, SizeSkeleton}
;
9 use rustc
::{lint, util}
;
10 use rustc_index
::vec
::Idx
;
11 use util
::nodemap
::FxHashSet
;
12 use lint
::{LateContext, LintContext, LintArray}
;
13 use lint
::{LintPass, LateLintPass}
;
16 use std
::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64}
;
18 use syntax
::{ast, attr, source_map}
;
19 use syntax
::errors
::Applicability
;
20 use syntax
::symbol
::sym
;
21 use rustc_target
::spec
::abi
::Abi
;
26 use rustc
::mir
::interpret
::{sign_extend, truncate}
;
33 "comparisons made useless by limits of the types involved"
39 "literal out of range for its type"
43 VARIANT_SIZE_DIFFERENCES
,
45 "detects enums with widely varying variant sizes"
48 #[derive(Copy, Clone)]
49 pub struct TypeLimits
{
50 /// Id of the last visited negated expression
51 negated_expr_id
: hir
::HirId
,
54 impl_lint_pass
!(TypeLimits
=> [UNUSED_COMPARISONS
, OVERFLOWING_LITERALS
]);
57 pub fn new() -> TypeLimits
{
58 TypeLimits { negated_expr_id: hir::DUMMY_HIR_ID }
62 /// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint.
63 /// Returns `true` iff the lint was overridden.
64 fn lint_overflowing_range_endpoint
<'a
, 'tcx
>(
65 cx
: &LateContext
<'a
, 'tcx
>,
69 expr
: &'tcx hir
::Expr
,
70 parent_expr
: &'tcx hir
::Expr
,
73 // We only want to handle exclusive (`..`) ranges,
74 // which are represented as `ExprKind::Struct`.
75 if let ExprKind
::Struct(_
, eps
, _
) = &parent_expr
.kind
{
79 // We can suggest using an inclusive range
80 // (`..=`) instead only if it is the `end` that is
81 // overflowing and only by 1.
82 if eps
[1].expr
.hir_id
== expr
.hir_id
&& lit_val
- 1 == max
{
83 let mut err
= cx
.struct_span_lint(
86 &format
!("range endpoint is out of range for `{}`", ty
),
88 if let Ok(start
) = cx
.sess().source_map().span_to_snippet(eps
[0].span
) {
89 use ast
::{LitKind, LitIntType}
;
90 // We need to preserve the literal's suffix,
91 // as it may determine typing information.
92 let suffix
= match lit
.node
{
93 LitKind
::Int(_
, LitIntType
::Signed(s
)) => format
!("{}", s
.name_str()),
94 LitKind
::Int(_
, LitIntType
::Unsigned(s
)) => format
!("{}", s
.name_str()),
95 LitKind
::Int(_
, LitIntType
::Unsuffixed
) => "".to_owned(),
98 let suggestion
= format
!("{}..={}{}", start
, lit_val
- 1, suffix
);
101 &"use an inclusive range instead",
103 Applicability
::MachineApplicable
,
114 // For `isize` & `usize`, be conservative with the warnings, so that the
115 // warnings are consistent between 32- and 64-bit platforms.
116 fn int_ty_range(int_ty
: ast
::IntTy
) -> (i128
, i128
) {
118 ast
::IntTy
::Isize
=> (i64::min_value() as i128
, i64::max_value() as i128
),
119 ast
::IntTy
::I8
=> (i8::min_value() as i64 as i128
, i8::max_value() as i128
),
120 ast
::IntTy
::I16
=> (i16::min_value() as i64 as i128
, i16::max_value() as i128
),
121 ast
::IntTy
::I32
=> (i32::min_value() as i64 as i128
, i32::max_value() as i128
),
122 ast
::IntTy
::I64
=> (i64::min_value() as i128
, i64::max_value() as i128
),
123 ast
::IntTy
::I128
=>(i128
::min_value() as i128
, i128
::max_value()),
127 fn uint_ty_range(uint_ty
: ast
::UintTy
) -> (u128
, u128
) {
129 ast
::UintTy
::Usize
=> (u64::min_value() as u128
, u64::max_value() as u128
),
130 ast
::UintTy
::U8
=> (u8::min_value() as u128
, u8::max_value() as u128
),
131 ast
::UintTy
::U16
=> (u16::min_value() as u128
, u16::max_value() as u128
),
132 ast
::UintTy
::U32
=> (u32::min_value() as u128
, u32::max_value() as u128
),
133 ast
::UintTy
::U64
=> (u64::min_value() as u128
, u64::max_value() as u128
),
134 ast
::UintTy
::U128
=> (u128
::min_value(), u128
::max_value()),
138 fn get_bin_hex_repr(cx
: &LateContext
<'_
, '_
>, lit
: &hir
::Lit
) -> Option
<String
> {
139 let src
= cx
.sess().source_map().span_to_snippet(lit
.span
).ok()?
;
140 let firstch
= src
.chars().next()?
;
143 match src
.chars().nth(1) {
144 Some('x'
) | Some('b'
) => return Some(src
),
152 fn report_bin_hex_error(
153 cx
: &LateContext
<'_
, '_
>,
160 let size
= layout
::Integer
::from_attr(&cx
.tcx
, ty
).size();
161 let (t
, actually
) = match ty
{
162 attr
::IntType
::SignedInt(t
) => {
163 let actually
= sign_extend(val
, size
) as i128
;
164 (t
.name_str(), actually
.to_string())
166 attr
::IntType
::UnsignedInt(t
) => {
167 let actually
= truncate(val
, size
);
168 (t
.name_str(), actually
.to_string())
171 let mut err
= cx
.struct_span_lint(
172 OVERFLOWING_LITERALS
,
174 &format
!("literal out of range for {}", t
),
177 "the literal `{}` (decimal `{}`) does not fit into \
178 an `{}` and will become `{}{}`",
179 repr_str
, val
, t
, actually
, t
181 if let Some(sugg_ty
) =
182 get_type_suggestion(&cx
.tables
.node_type(expr
.hir_id
), val
, negative
)
184 if let Some(pos
) = repr_str
.chars().position(|c
| c
== 'i'
|| c
== 'u'
) {
185 let (sans_suffix
, _
) = repr_str
.split_at(pos
);
188 &format
!("consider using `{}` instead", sugg_ty
),
189 format
!("{}{}", sans_suffix
, sugg_ty
),
190 Applicability
::MachineApplicable
193 err
.help(&format
!("consider using `{}` instead", sugg_ty
));
200 // This function finds the next fitting type and generates a suggestion string.
201 // It searches for fitting types in the following way (`X < Y`):
202 // - `iX`: if literal fits in `uX` => `uX`, else => `iY`
206 // No suggestion for: `isize`, `usize`.
207 fn get_type_suggestion(t
: Ty
<'_
>, val
: u128
, negative
: bool
) -> Option
<&'
static str> {
208 use syntax
::ast
::IntTy
::*;
209 use syntax
::ast
::UintTy
::*;
210 macro_rules
! find_fit
{
211 ($ty
:expr
, $val
:expr
, $negative
:expr
,
212 $
($
type:ident
=> [$
($utypes
:expr
),*] => [$
($itypes
:expr
),*]),+) => {
214 let _neg
= if negative { 1 }
else { 0 }
;
217 $
(if !negative
&& val
<= uint_ty_range($utypes
).1 {
218 return Some($utypes
.name_str())
220 $
(if val
<= int_ty_range($itypes
).1 as u128
+ _neg
{
221 return Some($itypes
.name_str())
231 ty
::Int(i
) => find_fit
!(i
, val
, negative
,
232 I8
=> [U8
] => [I16
, I32
, I64
, I128
],
233 I16
=> [U16
] => [I32
, I64
, I128
],
234 I32
=> [U32
] => [I64
, I128
],
235 I64
=> [U64
] => [I128
],
236 I128
=> [U128
] => []),
237 ty
::Uint(u
) => find_fit
!(u
, val
, negative
,
238 U8
=> [U8
, U16
, U32
, U64
, U128
] => [],
239 U16
=> [U16
, U32
, U64
, U128
] => [],
240 U32
=> [U32
, U64
, U128
] => [],
241 U64
=> [U64
, U128
] => [],
242 U128
=> [U128
] => []),
247 fn lint_int_literal
<'a
, 'tcx
>(
248 cx
: &LateContext
<'a
, 'tcx
>,
249 type_limits
: &TypeLimits
,
255 let int_type
= t
.normalize(cx
.sess().target
.ptr_width
);
256 let (_
, max
) = int_ty_range(int_type
);
257 let max
= max
as u128
;
258 let negative
= type_limits
.negated_expr_id
== e
.hir_id
;
260 // Detect literal value out of range [min, max] inclusive
261 // avoiding use of -min to prevent overflow/panic
262 if (negative
&& v
> max
+ 1) || (!negative
&& v
> max
) {
263 if let Some(repr_str
) = get_bin_hex_repr(cx
, lit
) {
264 report_bin_hex_error(
267 attr
::IntType
::SignedInt(t
),
275 let par_id
= cx
.tcx
.hir().get_parent_node(e
.hir_id
);
276 if let Node
::Expr(par_e
) = cx
.tcx
.hir().get(par_id
) {
277 if let hir
::ExprKind
::Struct(..) = par_e
.kind
{
278 if is_range_literal(cx
.sess(), par_e
)
279 && lint_overflowing_range_endpoint(cx
, lit
, v
, max
, e
, par_e
, t
.name_str())
281 // The overflowing literal lint was overridden.
288 OVERFLOWING_LITERALS
,
290 &format
!("literal out of range for `{}`", t
.name_str()),
295 fn lint_uint_literal
<'a
, 'tcx
>(
296 cx
: &LateContext
<'a
, 'tcx
>,
301 let uint_type
= t
.normalize(cx
.sess().target
.ptr_width
);
302 let (min
, max
) = uint_ty_range(uint_type
);
303 let lit_val
: u128
= match lit
.node
{
304 // _v is u8, within range by definition
305 ast
::LitKind
::Byte(_v
) => return,
306 ast
::LitKind
::Int(v
, _
) => v
,
309 if lit_val
< min
|| lit_val
> max
{
310 let parent_id
= cx
.tcx
.hir().get_parent_node(e
.hir_id
);
311 if let Node
::Expr(par_e
) = cx
.tcx
.hir().get(parent_id
) {
313 hir
::ExprKind
::Cast(..) => {
314 if let ty
::Char
= cx
.tables
.expr_ty(par_e
).kind
{
315 let mut err
= cx
.struct_span_lint(
316 OVERFLOWING_LITERALS
,
318 "only `u8` can be cast into `char`",
322 &"use a `char` literal instead",
323 format
!("'\\u{{{:X}}}'", lit_val
),
324 Applicability
::MachineApplicable
,
330 hir
::ExprKind
::Struct(..)
331 if is_range_literal(cx
.sess(), par_e
) => {
332 let t
= t
.name_str();
333 if lint_overflowing_range_endpoint(cx
, lit
, lit_val
, max
, e
, par_e
, t
) {
334 // The overflowing literal lint was overridden.
341 if let Some(repr_str
) = get_bin_hex_repr(cx
, lit
) {
342 report_bin_hex_error(cx
, e
, attr
::IntType
::UnsignedInt(t
), repr_str
, lit_val
, false);
346 OVERFLOWING_LITERALS
,
348 &format
!("literal out of range for `{}`", t
.name_str()),
353 fn lint_literal
<'a
, 'tcx
>(
354 cx
: &LateContext
<'a
, 'tcx
>,
355 type_limits
: &TypeLimits
,
359 match cx
.tables
.node_type(e
.hir_id
).kind
{
362 ast
::LitKind
::Int(v
, ast
::LitIntType
::Signed(_
)) |
363 ast
::LitKind
::Int(v
, ast
::LitIntType
::Unsuffixed
) => {
364 lint_int_literal(cx
, type_limits
, e
, lit
, t
, v
)
370 lint_uint_literal(cx
, e
, lit
, t
)
373 let is_infinite
= match lit
.node
{
374 ast
::LitKind
::Float(v
, _
) => {
376 ast
::FloatTy
::F32
=> v
.as_str().parse().map(f32::is_infinite
),
377 ast
::FloatTy
::F64
=> v
.as_str().parse().map(f64::is_infinite
),
382 if is_infinite
== Ok(true) {
384 OVERFLOWING_LITERALS
,
386 &format
!("literal out of range for `{}`", t
.name_str()),
394 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for TypeLimits
{
395 fn check_expr(&mut self, cx
: &LateContext
<'a
, 'tcx
>, e
: &'tcx hir
::Expr
) {
397 hir
::ExprKind
::Unary(hir
::UnNeg
, ref expr
) => {
398 // propagate negation, if the negation itself isn't negated
399 if self.negated_expr_id
!= e
.hir_id
{
400 self.negated_expr_id
= expr
.hir_id
;
403 hir
::ExprKind
::Binary(binop
, ref l
, ref r
) => {
404 if is_comparison(binop
) && !check_limits(cx
, binop
, &l
, &r
) {
405 cx
.span_lint(UNUSED_COMPARISONS
,
407 "comparison is useless due to type limits");
410 hir
::ExprKind
::Lit(ref lit
) => lint_literal(cx
, self, e
, lit
),
414 fn is_valid
<T
: cmp
::PartialOrd
>(binop
: hir
::BinOp
, v
: T
, min
: T
, max
: T
) -> bool
{
416 hir
::BinOpKind
::Lt
=> v
> min
&& v
<= max
,
417 hir
::BinOpKind
::Le
=> v
>= min
&& v
< max
,
418 hir
::BinOpKind
::Gt
=> v
>= min
&& v
< max
,
419 hir
::BinOpKind
::Ge
=> v
> min
&& v
<= max
,
420 hir
::BinOpKind
::Eq
| hir
::BinOpKind
::Ne
=> v
>= min
&& v
<= max
,
425 fn rev_binop(binop
: hir
::BinOp
) -> hir
::BinOp
{
426 source_map
::respan(binop
.span
,
428 hir
::BinOpKind
::Lt
=> hir
::BinOpKind
::Gt
,
429 hir
::BinOpKind
::Le
=> hir
::BinOpKind
::Ge
,
430 hir
::BinOpKind
::Gt
=> hir
::BinOpKind
::Lt
,
431 hir
::BinOpKind
::Ge
=> hir
::BinOpKind
::Le
,
436 fn check_limits(cx
: &LateContext
<'_
, '_
>,
441 let (lit
, expr
, swap
) = match (&l
.kind
, &r
.kind
) {
442 (&hir
::ExprKind
::Lit(_
), _
) => (l
, r
, true),
443 (_
, &hir
::ExprKind
::Lit(_
)) => (r
, l
, false),
446 // Normalize the binop so that the literal is always on the RHS in
448 let norm_binop
= if swap { rev_binop(binop) }
else { binop }
;
449 match cx
.tables
.node_type(expr
.hir_id
).kind
{
451 let (min
, max
) = int_ty_range(int_ty
);
452 let lit_val
: i128
= match lit
.kind
{
453 hir
::ExprKind
::Lit(ref li
) => {
455 ast
::LitKind
::Int(v
, ast
::LitIntType
::Signed(_
)) |
456 ast
::LitKind
::Int(v
, ast
::LitIntType
::Unsuffixed
) => v
as i128
,
462 is_valid(norm_binop
, lit_val
, min
, max
)
464 ty
::Uint(uint_ty
) => {
465 let (min
, max
) :(u128
, u128
) = uint_ty_range(uint_ty
);
466 let lit_val
: u128
= match lit
.kind
{
467 hir
::ExprKind
::Lit(ref li
) => {
469 ast
::LitKind
::Int(v
, _
) => v
,
475 is_valid(norm_binop
, lit_val
, min
, max
)
481 fn is_comparison(binop
: hir
::BinOp
) -> bool
{
488 hir
::BinOpKind
::Gt
=> true,
498 "proper use of libc types in foreign modules"
501 declare_lint_pass
!(ImproperCTypes
=> [IMPROPER_CTYPES
]);
503 struct ImproperCTypesVisitor
<'a
, 'tcx
> {
504 cx
: &'a LateContext
<'a
, 'tcx
>,
507 enum FfiResult
<'tcx
> {
509 FfiPhantom(Ty
<'tcx
>),
512 reason
: &'
static str,
513 help
: Option
<&'
static str>,
517 fn is_zst
<'tcx
>(tcx
: TyCtxt
<'tcx
>, did
: DefId
, ty
: Ty
<'tcx
>) -> bool
{
518 tcx
.layout_of(tcx
.param_env(did
).and(ty
)).map(|layout
| layout
.is_zst()).unwrap_or(false)
521 fn ty_is_known_nonnull
<'tcx
>(tcx
: TyCtxt
<'tcx
>, ty
: Ty
<'tcx
>) -> bool
{
523 ty
::FnPtr(_
) => true,
525 ty
::Adt(field_def
, substs
) if field_def
.repr
.transparent() && !field_def
.is_union() => {
526 for field
in field_def
.all_fields() {
527 let field_ty
= tcx
.normalize_erasing_regions(
528 ParamEnv
::reveal_all(),
529 field
.ty(tcx
, substs
),
531 if is_zst(tcx
, field
.did
, field_ty
) {
535 let attrs
= tcx
.get_attrs(field_def
.did
);
536 if attrs
.iter().any(|a
| a
.check_name(sym
::rustc_nonnull_optimization_guaranteed
)) ||
537 ty_is_known_nonnull(tcx
, field_ty
) {
548 /// Check if this enum can be safely exported based on the
549 /// "nullable pointer optimization". Currently restricted
550 /// to function pointers, references, core::num::NonZero*,
551 /// core::ptr::NonNull, and #[repr(transparent)] newtypes.
552 /// FIXME: This duplicates code in codegen.
553 fn is_repr_nullable_ptr
<'tcx
>(
556 ty_def
: &'tcx ty
::AdtDef
,
557 substs
: SubstsRef
<'tcx
>,
559 if ty_def
.variants
.len() != 2 {
563 let get_variant_fields
= |index
| &ty_def
.variants
[VariantIdx
::new(index
)].fields
;
564 let variant_fields
= [get_variant_fields(0), get_variant_fields(1)];
565 let fields
= if variant_fields
[0].is_empty() {
567 } else if variant_fields
[1].is_empty() {
573 if fields
.len() != 1 {
577 let field_ty
= fields
[0].ty(tcx
, substs
);
578 if !ty_is_known_nonnull(tcx
, field_ty
) {
582 // At this point, the field's type is known to be nonnull and the parent enum is Option-like.
583 // If the computed size for the field and the enum are different, the nonnull optimization isn't
584 // being applied (and we've got a problem somewhere).
585 let compute_size_skeleton
= |t
| SizeSkeleton
::compute(t
, tcx
, ParamEnv
::reveal_all()).unwrap();
586 if !compute_size_skeleton(ty
).same_size(compute_size_skeleton(field_ty
)) {
587 bug
!("improper_ctypes: Option nonnull optimization not applied?");
593 impl<'a
, 'tcx
> ImproperCTypesVisitor
<'a
, 'tcx
> {
595 /// Check if the type is array and emit an unsafe type lint.
596 fn check_for_array_ty(&mut self, sp
: Span
, ty
: Ty
<'tcx
>) -> bool
{
597 if let ty
::Array(..) = ty
.kind
{
598 self.emit_ffi_unsafe_type_lint(
601 "passing raw arrays by value is not FFI-safe",
602 Some("consider passing a pointer to the array"),
611 /// Checks if the given type is "ffi-safe" (has a stable, well-defined
612 /// representation which can be exported to C code).
613 fn check_type_for_ffi(&self,
614 cache
: &mut FxHashSet
<Ty
<'tcx
>>,
615 ty
: Ty
<'tcx
>) -> FfiResult
<'tcx
> {
618 let cx
= self.cx
.tcx
;
620 // Protect against infinite recursion, for example
621 // `struct S(*mut S);`.
622 // FIXME: A recursion limit is necessary as well, for irregular
624 if !cache
.insert(ty
) {
629 ty
::Adt(def
, substs
) => {
630 if def
.is_phantom_data() {
631 return FfiPhantom(ty
);
633 match def
.adt_kind() {
635 if !def
.repr
.c() && !def
.repr
.transparent() {
638 reason
: "this struct has unspecified layout",
639 help
: Some("consider adding a `#[repr(C)]` or \
640 `#[repr(transparent)]` attribute to this struct"),
644 let is_non_exhaustive
=
645 def
.non_enum_variant().is_field_list_non_exhaustive();
646 if is_non_exhaustive
&& !def
.did
.is_local() {
649 reason
: "this struct is non-exhaustive",
654 if def
.non_enum_variant().fields
.is_empty() {
657 reason
: "this struct has no fields",
658 help
: Some("consider adding a member to this struct"),
662 // We can't completely trust repr(C) and repr(transparent) markings;
663 // make sure the fields are actually safe.
664 let mut all_phantom
= true;
665 for field
in &def
.non_enum_variant().fields
{
666 let field_ty
= cx
.normalize_erasing_regions(
667 ParamEnv
::reveal_all(),
668 field
.ty(cx
, substs
),
670 // repr(transparent) types are allowed to have arbitrary ZSTs, not just
671 // PhantomData -- skip checking all ZST fields
672 if def
.repr
.transparent() && is_zst(cx
, field
.did
, field_ty
) {
675 let r
= self.check_type_for_ffi(cache
, field_ty
);
681 FfiUnsafe { .. }
=> {
687 if all_phantom { FfiPhantom(ty) }
else { FfiSafe }
690 if !def
.repr
.c() && !def
.repr
.transparent() {
693 reason
: "this union has unspecified layout",
694 help
: Some("consider adding a `#[repr(C)]` or \
695 `#[repr(transparent)]` attribute to this union"),
699 if def
.non_enum_variant().fields
.is_empty() {
702 reason
: "this union has no fields",
703 help
: Some("consider adding a field to this union"),
707 let mut all_phantom
= true;
708 for field
in &def
.non_enum_variant().fields
{
709 let field_ty
= cx
.normalize_erasing_regions(
710 ParamEnv
::reveal_all(),
711 field
.ty(cx
, substs
),
713 // repr(transparent) types are allowed to have arbitrary ZSTs, not just
714 // PhantomData -- skip checking all ZST fields.
715 if def
.repr
.transparent() && is_zst(cx
, field
.did
, field_ty
) {
718 let r
= self.check_type_for_ffi(cache
, field_ty
);
724 FfiUnsafe { .. }
=> {
730 if all_phantom { FfiPhantom(ty) }
else { FfiSafe }
733 if def
.variants
.is_empty() {
734 // Empty enums are okay... although sort of useless.
738 // Check for a repr() attribute to specify the size of the
740 if !def
.repr
.c() && !def
.repr
.transparent() && def
.repr
.int
.is_none() {
741 // Special-case types like `Option<extern fn()>`.
742 if !is_repr_nullable_ptr(cx
, ty
, def
, substs
) {
745 reason
: "enum has no representation hint",
746 help
: Some("consider adding a `#[repr(C)]`, \
747 `#[repr(transparent)]`, or integer `#[repr(...)]` \
748 attribute to this enum"),
753 if def
.is_variant_list_non_exhaustive() && !def
.did
.is_local() {
756 reason
: "this enum is non-exhaustive",
761 // Check the contained variants.
762 for variant
in &def
.variants
{
763 let is_non_exhaustive
= variant
.is_field_list_non_exhaustive();
764 if is_non_exhaustive
&& !variant
.def_id
.is_local() {
767 reason
: "this enum has non-exhaustive variants",
772 for field
in &variant
.fields
{
773 let field_ty
= cx
.normalize_erasing_regions(
774 ParamEnv
::reveal_all(),
775 field
.ty(cx
, substs
),
777 // repr(transparent) types are allowed to have arbitrary ZSTs, not
778 // just PhantomData -- skip checking all ZST fields.
779 if def
.repr
.transparent() && is_zst(cx
, field
.did
, field_ty
) {
782 let r
= self.check_type_for_ffi(cache
, field_ty
);
785 FfiUnsafe { .. }
=> {
791 reason
: "this enum contains a PhantomData field",
803 ty
::Char
=> FfiUnsafe
{
805 reason
: "the `char` type has no C equivalent",
806 help
: Some("consider using `u32` or `libc::wchar_t` instead"),
809 ty
::Int(ast
::IntTy
::I128
) | ty
::Uint(ast
::UintTy
::U128
) => FfiUnsafe
{
811 reason
: "128-bit integers don't currently have a known stable ABI",
815 // Primitive types with a stable representation.
816 ty
::Bool
| ty
::Int(..) | ty
::Uint(..) | ty
::Float(..) | ty
::Never
=> FfiSafe
,
818 ty
::Slice(_
) => FfiUnsafe
{
820 reason
: "slices have no C equivalent",
821 help
: Some("consider using a raw pointer instead"),
824 ty
::Dynamic(..) => FfiUnsafe
{
826 reason
: "trait objects have no C equivalent",
830 ty
::Str
=> FfiUnsafe
{
832 reason
: "string slices have no C equivalent",
833 help
: Some("consider using `*const u8` and a length instead"),
836 ty
::Tuple(..) => FfiUnsafe
{
838 reason
: "tuples have unspecified layout",
839 help
: Some("consider using a struct instead"),
842 ty
::RawPtr(ty
::TypeAndMut { ty, .. }
) |
843 ty
::Ref(_
, ty
, _
) => self.check_type_for_ffi(cache
, ty
),
845 ty
::Array(inner_ty
, _
) => self.check_type_for_ffi(cache
, inner_ty
),
849 Abi
::Rust
| Abi
::RustIntrinsic
| Abi
::PlatformIntrinsic
| Abi
::RustCall
=> {
852 reason
: "this function pointer has Rust-specific calling convention",
853 help
: Some("consider using an `extern fn(...) -> ...` \
854 function pointer instead"),
860 let sig
= cx
.erase_late_bound_regions(&sig
);
861 if !sig
.output().is_unit() {
862 let r
= self.check_type_for_ffi(cache
, sig
.output());
870 for arg
in sig
.inputs() {
871 let r
= self.check_type_for_ffi(cache
, arg
);
882 ty
::Foreign(..) => FfiSafe
,
890 ty
::GeneratorWitness(..) |
891 ty
::Placeholder(..) |
892 ty
::UnnormalizedProjection(..) |
895 ty
::FnDef(..) => bug
!("unexpected type in foreign function: {:?}", ty
),
899 fn emit_ffi_unsafe_type_lint(
906 let mut diag
= self.cx
.struct_span_lint(
909 &format
!("`extern` block uses type `{}`, which is not FFI-safe", ty
),
911 diag
.span_label(sp
, "not FFI-safe");
912 if let Some(help
) = help
{
916 if let ty
::Adt(def
, _
) = ty
.kind
{
917 if let Some(sp
) = self.cx
.tcx
.hir().span_if_local(def
.did
) {
918 diag
.span_note(sp
, "type defined here");
924 fn check_for_opaque_ty(&mut self, sp
: Span
, ty
: Ty
<'tcx
>) -> bool
{
925 use crate::rustc
::ty
::TypeFoldable
;
927 struct ProhibitOpaqueTypes
<'tcx
> {
928 ty
: Option
<Ty
<'tcx
>>,
931 impl<'tcx
> ty
::fold
::TypeVisitor
<'tcx
> for ProhibitOpaqueTypes
<'tcx
> {
932 fn visit_ty(&mut self, ty
: Ty
<'tcx
>) -> bool
{
933 if let ty
::Opaque(..) = ty
.kind
{
937 ty
.super_visit_with(self)
942 let mut visitor
= ProhibitOpaqueTypes { ty: None }
;
943 ty
.visit_with(&mut visitor
);
944 if let Some(ty
) = visitor
.ty
{
945 self.emit_ffi_unsafe_type_lint(
948 "opaque types have no C equivalent",
957 fn check_type_for_ffi_and_report_errors(&mut self, sp
: Span
, ty
: Ty
<'tcx
>, is_static
: bool
) {
958 // We have to check for opaque types before `normalize_erasing_regions`,
959 // which will replace opaque types with their underlying concrete type.
960 if self.check_for_opaque_ty(sp
, ty
) {
961 // We've already emitted an error due to an opaque type.
965 // it is only OK to use this function because extern fns cannot have
966 // any generic types right now:
967 let ty
= self.cx
.tcx
.normalize_erasing_regions(ParamEnv
::reveal_all(), ty
);
968 // C doesn't really support passing arrays by value.
969 // The only way to pass an array by value is through a struct.
970 // So we first test that the top level isn't an array,
971 // and then recursively check the types inside.
972 if !is_static
&& self.check_for_array_ty(sp
, ty
) {
976 match self.check_type_for_ffi(&mut FxHashSet
::default(), ty
) {
977 FfiResult
::FfiSafe
=> {}
978 FfiResult
::FfiPhantom(ty
) => {
979 self.emit_ffi_unsafe_type_lint(ty
, sp
, "composed only of `PhantomData`", None
);
981 FfiResult
::FfiUnsafe { ty, reason, help }
=> {
982 self.emit_ffi_unsafe_type_lint(ty
, sp
, reason
, help
);
987 fn check_foreign_fn(&mut self, id
: hir
::HirId
, decl
: &hir
::FnDecl
) {
988 let def_id
= self.cx
.tcx
.hir().local_def_id(id
);
989 let sig
= self.cx
.tcx
.fn_sig(def_id
);
990 let sig
= self.cx
.tcx
.erase_late_bound_regions(&sig
);
992 for (input_ty
, input_hir
) in sig
.inputs().iter().zip(&decl
.inputs
) {
993 self.check_type_for_ffi_and_report_errors(input_hir
.span
, input_ty
, false);
996 if let hir
::Return(ref ret_hir
) = decl
.output
{
997 let ret_ty
= sig
.output();
998 if !ret_ty
.is_unit() {
999 self.check_type_for_ffi_and_report_errors(ret_hir
.span
, ret_ty
, false);
1004 fn check_foreign_static(&mut self, id
: hir
::HirId
, span
: Span
) {
1005 let def_id
= self.cx
.tcx
.hir().local_def_id(id
);
1006 let ty
= self.cx
.tcx
.type_of(def_id
);
1007 self.check_type_for_ffi_and_report_errors(span
, ty
, true);
1011 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for ImproperCTypes
{
1012 fn check_foreign_item(&mut self, cx
: &LateContext
<'_
, '_
>, it
: &hir
::ForeignItem
) {
1013 let mut vis
= ImproperCTypesVisitor { cx }
;
1014 let abi
= cx
.tcx
.hir().get_foreign_abi(it
.hir_id
);
1015 if let Abi
::Rust
| Abi
::RustCall
| Abi
::RustIntrinsic
| Abi
::PlatformIntrinsic
= abi
{
1016 // Don't worry about types in internal ABIs.
1019 hir
::ForeignItemKind
::Fn(ref decl
, _
, _
) => {
1020 vis
.check_foreign_fn(it
.hir_id
, decl
);
1022 hir
::ForeignItemKind
::Static(ref ty
, _
) => {
1023 vis
.check_foreign_static(it
.hir_id
, ty
.span
);
1025 hir
::ForeignItemKind
::Type
=> ()
1031 declare_lint_pass
!(VariantSizeDifferences
=> [VARIANT_SIZE_DIFFERENCES
]);
1033 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for VariantSizeDifferences
{
1034 fn check_item(&mut self, cx
: &LateContext
<'_
, '_
>, it
: &hir
::Item
) {
1035 if let hir
::ItemKind
::Enum(ref enum_definition
, _
) = it
.kind
{
1036 let item_def_id
= cx
.tcx
.hir().local_def_id(it
.hir_id
);
1037 let t
= cx
.tcx
.type_of(item_def_id
);
1038 let ty
= cx
.tcx
.erase_regions(&t
);
1039 let layout
= match cx
.layout_of(ty
) {
1040 Ok(layout
) => layout
,
1041 Err(ty
::layout
::LayoutError
::Unknown(_
)) => return,
1042 Err(err @ ty
::layout
::LayoutError
::SizeOverflow(_
)) => {
1043 bug
!("failed to get layout for `{}`: {}", t
, err
);
1046 let (variants
, tag
) = match layout
.variants
{
1047 layout
::Variants
::Multiple
{
1048 discr_kind
: layout
::DiscriminantKind
::Tag
,
1052 } => (variants
, discr
),
1056 let discr_size
= tag
.value
.size(&cx
.tcx
).bytes();
1058 debug
!("enum `{}` is {} bytes large with layout:\n{:#?}",
1059 t
, layout
.size
.bytes(), layout
);
1061 let (largest
, slargest
, largest_index
) = enum_definition
.variants
1064 .map(|(variant
, variant_layout
)| {
1065 // Subtract the size of the enum discriminant.
1066 let bytes
= variant_layout
.size
.bytes().saturating_sub(discr_size
);
1068 debug
!("- variant `{}` is {} bytes large",
1074 .fold((0, 0, 0), |(l
, s
, li
), (idx
, size
)| if size
> l
{
1076 } else if size
> s
{
1082 // We only warn if the largest variant is at least thrice as large as
1083 // the second-largest.
1084 if largest
> slargest
* 3 && slargest
> 0 {
1085 cx
.span_lint(VARIANT_SIZE_DIFFERENCES
,
1086 enum_definition
.variants
[largest_index
].span
,
1087 &format
!("enum variant is more than three times \
1088 larger ({} bytes) than the next largest",