1 #![allow(non_snake_case)]
3 use crate::{LateContext, LateLintPass, LintContext}
;
5 use rustc_attr
as attr
;
6 use rustc_data_structures
::fx
::FxHashSet
;
7 use rustc_errors
::Applicability
;
9 use rustc_hir
::{is_range_literal, ExprKind, Node}
;
10 use rustc_index
::vec
::Idx
;
11 use rustc_middle
::mir
::interpret
::{sign_extend, truncate}
;
12 use rustc_middle
::ty
::layout
::{IntegerExt, SizeSkeleton}
;
13 use rustc_middle
::ty
::subst
::SubstsRef
;
14 use rustc_middle
::ty
::{self, AdtKind, Ty, TypeFoldable}
;
15 use rustc_span
::source_map
;
16 use rustc_span
::symbol
::sym
;
17 use rustc_span
::{Span, DUMMY_SP}
;
18 use rustc_target
::abi
::Abi
;
19 use rustc_target
::abi
::{Integer, LayoutOf, TagEncoding, VariantIdx, Variants}
;
20 use rustc_target
::spec
::abi
::Abi
as SpecAbi
;
28 "comparisons made useless by limits of the types involved"
34 "literal out of range for its type"
38 VARIANT_SIZE_DIFFERENCES
,
40 "detects enums with widely varying variant sizes"
43 #[derive(Copy, Clone)]
44 pub struct TypeLimits
{
45 /// Id of the last visited negated expression
46 negated_expr_id
: Option
<hir
::HirId
>,
49 impl_lint_pass
!(TypeLimits
=> [UNUSED_COMPARISONS
, OVERFLOWING_LITERALS
]);
52 pub fn new() -> TypeLimits
{
53 TypeLimits { negated_expr_id: None }
57 /// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint.
58 /// Returns `true` iff the lint was overridden.
59 fn lint_overflowing_range_endpoint
<'tcx
>(
60 cx
: &LateContext
<'tcx
>,
64 expr
: &'tcx hir
::Expr
<'tcx
>,
65 parent_expr
: &'tcx hir
::Expr
<'tcx
>,
68 // We only want to handle exclusive (`..`) ranges,
69 // which are represented as `ExprKind::Struct`.
70 let mut overwritten
= false;
71 if let ExprKind
::Struct(_
, eps
, _
) = &parent_expr
.kind
{
75 // We can suggest using an inclusive range
76 // (`..=`) instead only if it is the `end` that is
77 // overflowing and only by 1.
78 if eps
[1].expr
.hir_id
== expr
.hir_id
&& lit_val
- 1 == max
{
79 cx
.struct_span_lint(OVERFLOWING_LITERALS
, parent_expr
.span
, |lint
| {
80 let mut err
= lint
.build(&format
!("range endpoint is out of range for `{}`", ty
));
81 if let Ok(start
) = cx
.sess().source_map().span_to_snippet(eps
[0].span
) {
82 use ast
::{LitIntType, LitKind}
;
83 // We need to preserve the literal's suffix,
84 // as it may determine typing information.
85 let suffix
= match lit
.node
{
86 LitKind
::Int(_
, LitIntType
::Signed(s
)) => s
.name_str().to_string(),
87 LitKind
::Int(_
, LitIntType
::Unsigned(s
)) => s
.name_str().to_string(),
88 LitKind
::Int(_
, LitIntType
::Unsuffixed
) => "".to_string(),
91 let suggestion
= format
!("{}..={}{}", start
, lit_val
- 1, suffix
);
94 &"use an inclusive range instead",
96 Applicability
::MachineApplicable
,
107 // For `isize` & `usize`, be conservative with the warnings, so that the
108 // warnings are consistent between 32- and 64-bit platforms.
109 fn int_ty_range(int_ty
: ast
::IntTy
) -> (i128
, i128
) {
111 ast
::IntTy
::Isize
=> (i64::MIN
as i128
, i64::MAX
as i128
),
112 ast
::IntTy
::I8
=> (i8::MIN
as i64 as i128
, i8::MAX
as i128
),
113 ast
::IntTy
::I16
=> (i16::MIN
as i64 as i128
, i16::MAX
as i128
),
114 ast
::IntTy
::I32
=> (i32::MIN
as i64 as i128
, i32::MAX
as i128
),
115 ast
::IntTy
::I64
=> (i64::MIN
as i128
, i64::MAX
as i128
),
116 ast
::IntTy
::I128
=> (i128
::MIN
as i128
, i128
::MAX
),
120 fn uint_ty_range(uint_ty
: ast
::UintTy
) -> (u128
, u128
) {
122 ast
::UintTy
::Usize
=> (u64::MIN
as u128
, u64::MAX
as u128
),
123 ast
::UintTy
::U8
=> (u8::MIN
as u128
, u8::MAX
as u128
),
124 ast
::UintTy
::U16
=> (u16::MIN
as u128
, u16::MAX
as u128
),
125 ast
::UintTy
::U32
=> (u32::MIN
as u128
, u32::MAX
as u128
),
126 ast
::UintTy
::U64
=> (u64::MIN
as u128
, u64::MAX
as u128
),
127 ast
::UintTy
::U128
=> (u128
::MIN
, u128
::MAX
),
131 fn get_bin_hex_repr(cx
: &LateContext
<'_
>, lit
: &hir
::Lit
) -> Option
<String
> {
132 let src
= cx
.sess().source_map().span_to_snippet(lit
.span
).ok()?
;
133 let firstch
= src
.chars().next()?
;
136 match src
.chars().nth(1) {
137 Some('x'
| 'b'
) => return Some(src
),
145 fn report_bin_hex_error(
146 cx
: &LateContext
<'_
>,
147 expr
: &hir
::Expr
<'_
>,
153 let size
= Integer
::from_attr(&cx
.tcx
, ty
).size();
154 cx
.struct_span_lint(OVERFLOWING_LITERALS
, expr
.span
, |lint
| {
155 let (t
, actually
) = match ty
{
156 attr
::IntType
::SignedInt(t
) => {
157 let actually
= sign_extend(val
, size
) as i128
;
158 (t
.name_str(), actually
.to_string())
160 attr
::IntType
::UnsignedInt(t
) => {
161 let actually
= truncate(val
, size
);
162 (t
.name_str(), actually
.to_string())
165 let mut err
= lint
.build(&format
!("literal out of range for {}", t
));
167 "the literal `{}` (decimal `{}`) does not fit into \
168 the type `{}` and will become `{}{}`",
169 repr_str
, val
, t
, actually
, t
171 if let Some(sugg_ty
) =
172 get_type_suggestion(&cx
.typeck_results().node_type(expr
.hir_id
), val
, negative
)
174 if let Some(pos
) = repr_str
.chars().position(|c
| c
== 'i'
|| c
== 'u'
) {
175 let (sans_suffix
, _
) = repr_str
.split_at(pos
);
178 &format
!("consider using `{}` instead", sugg_ty
),
179 format
!("{}{}", sans_suffix
, sugg_ty
),
180 Applicability
::MachineApplicable
,
183 err
.help(&format
!("consider using `{}` instead", sugg_ty
));
190 // This function finds the next fitting type and generates a suggestion string.
191 // It searches for fitting types in the following way (`X < Y`):
192 // - `iX`: if literal fits in `uX` => `uX`, else => `iY`
196 // No suggestion for: `isize`, `usize`.
197 fn get_type_suggestion(t
: Ty
<'_
>, val
: u128
, negative
: bool
) -> Option
<&'
static str> {
198 use rustc_ast
::IntTy
::*;
199 use rustc_ast
::UintTy
::*;
200 macro_rules
! find_fit
{
201 ($ty
:expr
, $val
:expr
, $negative
:expr
,
202 $
($
type:ident
=> [$
($utypes
:expr
),*] => [$
($itypes
:expr
),*]),+) => {
204 let _neg
= if negative { 1 }
else { 0 }
;
207 $
(if !negative
&& val
<= uint_ty_range($utypes
).1 {
208 return Some($utypes
.name_str())
210 $
(if val
<= int_ty_range($itypes
).1 as u128
+ _neg
{
211 return Some($itypes
.name_str())
221 ty
::Int(i
) => find_fit
!(i
, val
, negative
,
222 I8
=> [U8
] => [I16
, I32
, I64
, I128
],
223 I16
=> [U16
] => [I32
, I64
, I128
],
224 I32
=> [U32
] => [I64
, I128
],
225 I64
=> [U64
] => [I128
],
226 I128
=> [U128
] => []),
227 ty
::Uint(u
) => find_fit
!(u
, val
, negative
,
228 U8
=> [U8
, U16
, U32
, U64
, U128
] => [],
229 U16
=> [U16
, U32
, U64
, U128
] => [],
230 U32
=> [U32
, U64
, U128
] => [],
231 U64
=> [U64
, U128
] => [],
232 U128
=> [U128
] => []),
237 fn lint_int_literal
<'tcx
>(
238 cx
: &LateContext
<'tcx
>,
239 type_limits
: &TypeLimits
,
240 e
: &'tcx hir
::Expr
<'tcx
>,
245 let int_type
= t
.normalize(cx
.sess().target
.ptr_width
);
246 let (min
, max
) = int_ty_range(int_type
);
247 let max
= max
as u128
;
248 let negative
= type_limits
.negated_expr_id
== Some(e
.hir_id
);
250 // Detect literal value out of range [min, max] inclusive
251 // avoiding use of -min to prevent overflow/panic
252 if (negative
&& v
> max
+ 1) || (!negative
&& v
> max
) {
253 if let Some(repr_str
) = get_bin_hex_repr(cx
, lit
) {
254 report_bin_hex_error(cx
, e
, attr
::IntType
::SignedInt(t
), repr_str
, v
, negative
);
258 let par_id
= cx
.tcx
.hir().get_parent_node(e
.hir_id
);
259 if let Node
::Expr(par_e
) = cx
.tcx
.hir().get(par_id
) {
260 if let hir
::ExprKind
::Struct(..) = par_e
.kind
{
261 if is_range_literal(par_e
)
262 && lint_overflowing_range_endpoint(cx
, lit
, v
, max
, e
, par_e
, t
.name_str())
264 // The overflowing literal lint was overridden.
270 cx
.struct_span_lint(OVERFLOWING_LITERALS
, e
.span
, |lint
| {
271 lint
.build(&format
!("literal out of range for `{}`", t
.name_str()))
273 "the literal `{}` does not fit into the type `{}` whose range is `{}..={}`",
276 .span_to_snippet(lit
.span
)
277 .expect("must get snippet from literal"),
287 fn lint_uint_literal
<'tcx
>(
288 cx
: &LateContext
<'tcx
>,
289 e
: &'tcx hir
::Expr
<'tcx
>,
293 let uint_type
= t
.normalize(cx
.sess().target
.ptr_width
);
294 let (min
, max
) = uint_ty_range(uint_type
);
295 let lit_val
: u128
= match lit
.node
{
296 // _v is u8, within range by definition
297 ast
::LitKind
::Byte(_v
) => return,
298 ast
::LitKind
::Int(v
, _
) => v
,
301 if lit_val
< min
|| lit_val
> max
{
302 let parent_id
= cx
.tcx
.hir().get_parent_node(e
.hir_id
);
303 if let Node
::Expr(par_e
) = cx
.tcx
.hir().get(parent_id
) {
305 hir
::ExprKind
::Cast(..) => {
306 if let ty
::Char
= cx
.typeck_results().expr_ty(par_e
).kind
{
307 cx
.struct_span_lint(OVERFLOWING_LITERALS
, par_e
.span
, |lint
| {
308 lint
.build("only `u8` can be cast into `char`")
311 &"use a `char` literal instead",
312 format
!("'\\u{{{:X}}}'", lit_val
),
313 Applicability
::MachineApplicable
,
320 hir
::ExprKind
::Struct(..) if is_range_literal(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.
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);
334 cx
.struct_span_lint(OVERFLOWING_LITERALS
, e
.span
, |lint
| {
335 lint
.build(&format
!("literal out of range for `{}`", t
.name_str()))
337 "the literal `{}` does not fit into the type `{}` whose range is `{}..={}`",
340 .span_to_snippet(lit
.span
)
341 .expect("must get snippet from literal"),
351 fn lint_literal
<'tcx
>(
352 cx
: &LateContext
<'tcx
>,
353 type_limits
: &TypeLimits
,
354 e
: &'tcx hir
::Expr
<'tcx
>,
357 match cx
.typeck_results().node_type(e
.hir_id
).kind
{
360 ast
::LitKind
::Int(v
, ast
::LitIntType
::Signed(_
) | ast
::LitIntType
::Unsuffixed
) => {
361 lint_int_literal(cx
, type_limits
, e
, lit
, t
, v
)
366 ty
::Uint(t
) => lint_uint_literal(cx
, e
, lit
, t
),
368 let is_infinite
= match lit
.node
{
369 ast
::LitKind
::Float(v
, _
) => match t
{
370 ast
::FloatTy
::F32
=> v
.as_str().parse().map(f32::is_infinite
),
371 ast
::FloatTy
::F64
=> v
.as_str().parse().map(f64::is_infinite
),
375 if is_infinite
== Ok(true) {
376 cx
.struct_span_lint(OVERFLOWING_LITERALS
, e
.span
, |lint
| {
377 lint
.build(&format
!("literal out of range for `{}`", t
.name_str()))
379 "the literal `{}` does not fit into the type `{}` and will be converted to `std::{}::INFINITY`",
382 .span_to_snippet(lit
.span
)
383 .expect("must get snippet from literal"),
395 impl<'tcx
> LateLintPass
<'tcx
> for TypeLimits
{
396 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, e
: &'tcx hir
::Expr
<'tcx
>) {
398 hir
::ExprKind
::Unary(hir
::UnOp
::UnNeg
, ref expr
) => {
399 // propagate negation, if the negation itself isn't negated
400 if self.negated_expr_id
!= Some(e
.hir_id
) {
401 self.negated_expr_id
= Some(expr
.hir_id
);
404 hir
::ExprKind
::Binary(binop
, ref l
, ref r
) => {
405 if is_comparison(binop
) && !check_limits(cx
, binop
, &l
, &r
) {
406 cx
.struct_span_lint(UNUSED_COMPARISONS
, e
.span
, |lint
| {
407 lint
.build("comparison is useless due to type limits").emit()
411 hir
::ExprKind
::Lit(ref lit
) => lint_literal(cx
, self, e
, lit
),
415 fn is_valid
<T
: cmp
::PartialOrd
>(binop
: hir
::BinOp
, v
: T
, min
: T
, max
: T
) -> bool
{
417 hir
::BinOpKind
::Lt
=> v
> min
&& v
<= max
,
418 hir
::BinOpKind
::Le
=> v
>= min
&& v
< max
,
419 hir
::BinOpKind
::Gt
=> v
>= min
&& v
< max
,
420 hir
::BinOpKind
::Ge
=> v
> min
&& v
<= max
,
421 hir
::BinOpKind
::Eq
| hir
::BinOpKind
::Ne
=> v
>= min
&& v
<= max
,
426 fn rev_binop(binop
: hir
::BinOp
) -> hir
::BinOp
{
430 hir
::BinOpKind
::Lt
=> hir
::BinOpKind
::Gt
,
431 hir
::BinOpKind
::Le
=> hir
::BinOpKind
::Ge
,
432 hir
::BinOpKind
::Gt
=> hir
::BinOpKind
::Lt
,
433 hir
::BinOpKind
::Ge
=> hir
::BinOpKind
::Le
,
440 cx
: &LateContext
<'_
>,
445 let (lit
, expr
, swap
) = match (&l
.kind
, &r
.kind
) {
446 (&hir
::ExprKind
::Lit(_
), _
) => (l
, r
, true),
447 (_
, &hir
::ExprKind
::Lit(_
)) => (r
, l
, false),
450 // Normalize the binop so that the literal is always on the RHS in
452 let norm_binop
= if swap { rev_binop(binop) }
else { binop }
;
453 match cx
.typeck_results().node_type(expr
.hir_id
).kind
{
455 let (min
, max
) = int_ty_range(int_ty
);
456 let lit_val
: i128
= match lit
.kind
{
457 hir
::ExprKind
::Lit(ref li
) => match li
.node
{
460 ast
::LitIntType
::Signed(_
) | ast
::LitIntType
::Unsuffixed
,
466 is_valid(norm_binop
, lit_val
, min
, max
)
468 ty
::Uint(uint_ty
) => {
469 let (min
, max
): (u128
, u128
) = uint_ty_range(uint_ty
);
470 let lit_val
: u128
= match lit
.kind
{
471 hir
::ExprKind
::Lit(ref li
) => match li
.node
{
472 ast
::LitKind
::Int(v
, _
) => v
,
477 is_valid(norm_binop
, lit_val
, min
, max
)
483 fn is_comparison(binop
: hir
::BinOp
) -> bool
{
490 | hir
::BinOpKind
::Gt
=> true,
500 "proper use of libc types in foreign modules"
503 declare_lint_pass
!(ImproperCTypesDeclarations
=> [IMPROPER_CTYPES
]);
506 IMPROPER_CTYPES_DEFINITIONS
,
508 "proper use of libc types in foreign item definitions"
511 declare_lint_pass
!(ImproperCTypesDefinitions
=> [IMPROPER_CTYPES_DEFINITIONS
]);
513 #[derive(Clone, Copy)]
514 crate enum CItemKind
{
519 struct ImproperCTypesVisitor
<'a
, 'tcx
> {
520 cx
: &'a LateContext
<'tcx
>,
524 enum FfiResult
<'tcx
> {
526 FfiPhantom(Ty
<'tcx
>),
527 FfiUnsafe { ty: Ty<'tcx>, reason: String, help: Option<String> }
,
530 /// Is type known to be non-null?
531 fn ty_is_known_nonnull
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>, mode
: CItemKind
) -> bool
{
534 ty
::FnPtr(_
) => true,
536 ty
::Adt(def
, _
) if def
.is_box() && matches
!(mode
, CItemKind
::Definition
) => true,
537 ty
::Adt(def
, substs
) if def
.repr
.transparent() && !def
.is_union() => {
538 let guaranteed_nonnull_optimization
= tcx
541 .any(|a
| tcx
.sess
.check_name(a
, sym
::rustc_nonnull_optimization_guaranteed
));
543 if guaranteed_nonnull_optimization
{
546 for variant
in &def
.variants
{
547 if let Some(field
) = variant
.transparent_newtype_field(tcx
) {
548 if ty_is_known_nonnull(cx
, field
.ty(tcx
, substs
), mode
) {
560 /// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type.
561 /// If the type passed in was not scalar, returns None.
562 fn get_nullable_type
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> Option
<Ty
<'tcx
>> {
565 ty
::Adt(field_def
, field_substs
) => {
566 let inner_field_ty
= {
567 let first_non_zst_ty
=
568 field_def
.variants
.iter().filter_map(|v
| v
.transparent_newtype_field(tcx
));
570 first_non_zst_ty
.clone().count(),
572 "Wrong number of fields for transparent type"
576 .expect("No non-zst fields in transparent type.")
577 .ty(tcx
, field_substs
)
579 return get_nullable_type(cx
, inner_field_ty
);
581 ty
::Int(ty
) => tcx
.mk_mach_int(ty
),
582 ty
::Uint(ty
) => tcx
.mk_mach_uint(ty
),
583 ty
::RawPtr(ty_mut
) => tcx
.mk_ptr(ty_mut
),
584 // As these types are always non-null, the nullable equivalent of
585 // Option<T> of these types are their raw pointer counterparts.
586 ty
::Ref(_region
, ty
, mutbl
) => tcx
.mk_ptr(ty
::TypeAndMut { ty, mutbl }
),
588 // There is no nullable equivalent for Rust's function pointers -- you
589 // must use an Option<fn(..) -> _> to represent it.
593 // We should only ever reach this case if ty_is_known_nonnull is extended
597 "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}",
605 /// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
606 /// can, return the the type that `ty` can be safely converted to, otherwise return `None`.
607 /// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`,
608 /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes.
609 /// FIXME: This duplicates code in codegen.
610 crate fn repr_nullable_ptr
<'tcx
>(
611 cx
: &LateContext
<'tcx
>,
614 ) -> Option
<Ty
<'tcx
>> {
615 debug
!("is_repr_nullable_ptr(cx, ty = {:?})", ty
);
616 if let ty
::Adt(ty_def
, substs
) = ty
.kind
{
617 if ty_def
.variants
.len() != 2 {
621 let get_variant_fields
= |index
| &ty_def
.variants
[VariantIdx
::new(index
)].fields
;
622 let variant_fields
= [get_variant_fields(0), get_variant_fields(1)];
623 let fields
= if variant_fields
[0].is_empty() {
625 } else if variant_fields
[1].is_empty() {
631 if fields
.len() != 1 {
635 let field_ty
= fields
[0].ty(cx
.tcx
, substs
);
636 if !ty_is_known_nonnull(cx
, field_ty
, ckind
) {
640 // At this point, the field's type is known to be nonnull and the parent enum is Option-like.
641 // If the computed size for the field and the enum are different, the nonnull optimization isn't
642 // being applied (and we've got a problem somewhere).
643 let compute_size_skeleton
= |t
| SizeSkeleton
::compute(t
, cx
.tcx
, cx
.param_env
).unwrap();
644 if !compute_size_skeleton(ty
).same_size(compute_size_skeleton(field_ty
)) {
645 bug
!("improper_ctypes: Option nonnull optimization not applied?");
648 // Return the nullable type this Option-like enum can be safely represented with.
649 let field_ty_abi
= &cx
.layout_of(field_ty
).unwrap().abi
;
650 if let Abi
::Scalar(field_ty_scalar
) = field_ty_abi
{
651 match (field_ty_scalar
.valid_range
.start(), field_ty_scalar
.valid_range
.end()) {
652 (0, _
) => unreachable
!("Non-null optimisation extended to a non-zero value."),
654 return Some(get_nullable_type(cx
, field_ty
).unwrap());
656 (start
, end
) => unreachable
!("Unhandled start and end range: ({}, {})", start
, end
),
663 impl<'a
, 'tcx
> ImproperCTypesVisitor
<'a
, 'tcx
> {
664 /// Check if the type is array and emit an unsafe type lint.
665 fn check_for_array_ty(&mut self, sp
: Span
, ty
: Ty
<'tcx
>) -> bool
{
666 if let ty
::Array(..) = ty
.kind
{
667 self.emit_ffi_unsafe_type_lint(
670 "passing raw arrays by value is not FFI-safe",
671 Some("consider passing a pointer to the array"),
679 /// Checks if the given field's type is "ffi-safe".
680 fn check_field_type_for_ffi(
682 cache
: &mut FxHashSet
<Ty
<'tcx
>>,
683 field
: &ty
::FieldDef
,
684 substs
: SubstsRef
<'tcx
>,
685 ) -> FfiResult
<'tcx
> {
686 let field_ty
= field
.ty(self.cx
.tcx
, substs
);
687 if field_ty
.has_opaque_types() {
688 self.check_type_for_ffi(cache
, field_ty
)
690 let field_ty
= self.cx
.tcx
.normalize_erasing_regions(self.cx
.param_env
, field_ty
);
691 self.check_type_for_ffi(cache
, field_ty
)
695 /// Checks if the given `VariantDef`'s field types are "ffi-safe".
696 fn check_variant_for_ffi(
698 cache
: &mut FxHashSet
<Ty
<'tcx
>>,
701 variant
: &ty
::VariantDef
,
702 substs
: SubstsRef
<'tcx
>,
703 ) -> FfiResult
<'tcx
> {
706 if def
.repr
.transparent() {
707 // Can assume that only one field is not a ZST, so only check
708 // that field's type for FFI-safety.
709 if let Some(field
) = variant
.transparent_newtype_field(self.cx
.tcx
) {
710 self.check_field_type_for_ffi(cache
, field
, substs
)
712 bug
!("malformed transparent type");
715 // We can't completely trust repr(C) markings; make sure the fields are
717 let mut all_phantom
= !variant
.fields
.is_empty();
718 for field
in &variant
.fields
{
719 match self.check_field_type_for_ffi(cache
, &field
, substs
) {
723 FfiPhantom(..) if def
.is_enum() => {
726 reason
: "this enum contains a PhantomData field".into(),
735 if all_phantom { FfiPhantom(ty) }
else { FfiSafe }
739 /// Checks if the given type is "ffi-safe" (has a stable, well-defined
740 /// representation which can be exported to C code).
741 fn check_type_for_ffi(&self, cache
: &mut FxHashSet
<Ty
<'tcx
>>, ty
: Ty
<'tcx
>) -> FfiResult
<'tcx
> {
744 let tcx
= self.cx
.tcx
;
746 // Protect against infinite recursion, for example
747 // `struct S(*mut S);`.
748 // FIXME: A recursion limit is necessary as well, for irregular
750 if !cache
.insert(ty
) {
755 ty
::Adt(def
, _
) if def
.is_box() && matches
!(self.mode
, CItemKind
::Definition
) => {
759 ty
::Adt(def
, substs
) => {
760 if def
.is_phantom_data() {
761 return FfiPhantom(ty
);
763 match def
.adt_kind() {
764 AdtKind
::Struct
| AdtKind
::Union
=> {
765 let kind
= if def
.is_struct() { "struct" }
else { "union" }
;
767 if !def
.repr
.c() && !def
.repr
.transparent() {
770 reason
: format
!("this {} has unspecified layout", kind
),
772 "consider adding a `#[repr(C)]` or \
773 `#[repr(transparent)]` attribute to this {}",
779 let is_non_exhaustive
=
780 def
.non_enum_variant().is_field_list_non_exhaustive();
781 if is_non_exhaustive
&& !def
.did
.is_local() {
784 reason
: format
!("this {} is non-exhaustive", kind
),
789 if def
.non_enum_variant().fields
.is_empty() {
792 reason
: format
!("this {} has no fields", kind
),
793 help
: Some(format
!("consider adding a member to this {}", kind
)),
797 self.check_variant_for_ffi(cache
, ty
, def
, def
.non_enum_variant(), substs
)
800 if def
.variants
.is_empty() {
801 // Empty enums are okay... although sort of useless.
805 // Check for a repr() attribute to specify the size of the
807 if !def
.repr
.c() && !def
.repr
.transparent() && def
.repr
.int
.is_none() {
808 // Special-case types like `Option<extern fn()>`.
809 if repr_nullable_ptr(self.cx
, ty
, self.mode
).is_none() {
812 reason
: "enum has no representation hint".into(),
814 "consider adding a `#[repr(C)]`, \
815 `#[repr(transparent)]`, or integer `#[repr(...)]` \
816 attribute to this enum"
823 if def
.is_variant_list_non_exhaustive() && !def
.did
.is_local() {
826 reason
: "this enum is non-exhaustive".into(),
831 // Check the contained variants.
832 for variant
in &def
.variants
{
833 let is_non_exhaustive
= variant
.is_field_list_non_exhaustive();
834 if is_non_exhaustive
&& !variant
.def_id
.is_local() {
837 reason
: "this enum has non-exhaustive variants".into(),
842 match self.check_variant_for_ffi(cache
, ty
, def
, variant
, substs
) {
853 ty
::Char
=> FfiUnsafe
{
855 reason
: "the `char` type has no C equivalent".into(),
856 help
: Some("consider using `u32` or `libc::wchar_t` instead".into()),
859 ty
::Int(ast
::IntTy
::I128
) | ty
::Uint(ast
::UintTy
::U128
) => FfiUnsafe
{
861 reason
: "128-bit integers don't currently have a known stable ABI".into(),
865 // Primitive types with a stable representation.
866 ty
::Bool
| ty
::Int(..) | ty
::Uint(..) | ty
::Float(..) | ty
::Never
=> FfiSafe
,
868 ty
::Slice(_
) => FfiUnsafe
{
870 reason
: "slices have no C equivalent".into(),
871 help
: Some("consider using a raw pointer instead".into()),
875 FfiUnsafe { ty, reason: "trait objects have no C equivalent".into(), help: None }
878 ty
::Str
=> FfiUnsafe
{
880 reason
: "string slices have no C equivalent".into(),
881 help
: Some("consider using `*const u8` and a length instead".into()),
884 ty
::Tuple(..) => FfiUnsafe
{
886 reason
: "tuples have unspecified layout".into(),
887 help
: Some("consider using a struct instead".into()),
890 ty
::RawPtr(ty
::TypeAndMut { ty, .. }
) | ty
::Ref(_
, ty
, _
)
892 matches
!(self.mode
, CItemKind
::Definition
)
893 && ty
.is_sized(self.cx
.tcx
.at(DUMMY_SP
), self.cx
.param_env
)
899 ty
::RawPtr(ty
::TypeAndMut { ty, .. }
) | ty
::Ref(_
, ty
, _
) => {
900 self.check_type_for_ffi(cache
, ty
)
903 ty
::Array(inner_ty
, _
) => self.check_type_for_ffi(cache
, inner_ty
),
906 if self.is_internal_abi(sig
.abi()) {
909 reason
: "this function pointer has Rust-specific calling convention".into(),
911 "consider using an `extern fn(...) -> ...` \
912 function pointer instead"
918 let sig
= tcx
.erase_late_bound_regions(&sig
);
919 if !sig
.output().is_unit() {
920 let r
= self.check_type_for_ffi(cache
, sig
.output());
928 for arg
in sig
.inputs() {
929 let r
= self.check_type_for_ffi(cache
, arg
);
940 ty
::Foreign(..) => FfiSafe
,
942 // While opaque types are checked for earlier, if a projection in a struct field
943 // normalizes to an opaque type, then it will reach this branch.
945 FfiUnsafe { ty, reason: "opaque types have no C equivalent".into(), help: None }
948 // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
949 // so they are currently ignored for the purposes of this lint.
950 ty
::Param(..) | ty
::Projection(..) if matches
!(self.mode
, CItemKind
::Definition
) => {
961 | ty
::GeneratorWitness(..)
962 | ty
::Placeholder(..)
963 | ty
::FnDef(..) => bug
!("unexpected type in foreign function: {:?}", ty
),
967 fn emit_ffi_unsafe_type_lint(
974 let lint
= match self.mode
{
975 CItemKind
::Declaration
=> IMPROPER_CTYPES
,
976 CItemKind
::Definition
=> IMPROPER_CTYPES_DEFINITIONS
,
979 self.cx
.struct_span_lint(lint
, sp
, |lint
| {
980 let item_description
= match self.mode
{
981 CItemKind
::Declaration
=> "block",
982 CItemKind
::Definition
=> "fn",
984 let mut diag
= lint
.build(&format
!(
985 "`extern` {} uses type `{}`, which is not FFI-safe",
988 diag
.span_label(sp
, "not FFI-safe");
989 if let Some(help
) = help
{
993 if let ty
::Adt(def
, _
) = ty
.kind
{
994 if let Some(sp
) = self.cx
.tcx
.hir().span_if_local(def
.did
) {
995 diag
.span_note(sp
, "the type is defined here");
1002 fn check_for_opaque_ty(&mut self, sp
: Span
, ty
: Ty
<'tcx
>) -> bool
{
1003 struct ProhibitOpaqueTypes
<'a
, 'tcx
> {
1004 cx
: &'a LateContext
<'tcx
>,
1005 ty
: Option
<Ty
<'tcx
>>,
1008 impl<'a
, 'tcx
> ty
::fold
::TypeVisitor
<'tcx
> for ProhibitOpaqueTypes
<'a
, 'tcx
> {
1009 fn visit_ty(&mut self, ty
: Ty
<'tcx
>) -> bool
{
1015 // Consider opaque types within projections FFI-safe if they do not normalize
1016 // to more opaque types.
1017 ty
::Projection(..) => {
1018 let ty
= self.cx
.tcx
.normalize_erasing_regions(self.cx
.param_env
, ty
);
1020 // If `ty` is a opaque type directly then `super_visit_with` won't invoke
1021 // this function again.
1022 if ty
.has_opaque_types() { self.visit_ty(ty) }
else { false }
1024 _
=> ty
.super_visit_with(self),
1029 let mut visitor
= ProhibitOpaqueTypes { cx: self.cx, ty: None }
;
1030 ty
.visit_with(&mut visitor
);
1031 if let Some(ty
) = visitor
.ty
{
1032 self.emit_ffi_unsafe_type_lint(ty
, sp
, "opaque types have no C equivalent", None
);
1039 fn check_type_for_ffi_and_report_errors(
1044 is_return_type
: bool
,
1046 // We have to check for opaque types before `normalize_erasing_regions`,
1047 // which will replace opaque types with their underlying concrete type.
1048 if self.check_for_opaque_ty(sp
, ty
) {
1049 // We've already emitted an error due to an opaque type.
1053 // it is only OK to use this function because extern fns cannot have
1054 // any generic types right now:
1055 let ty
= self.cx
.tcx
.normalize_erasing_regions(self.cx
.param_env
, ty
);
1057 // C doesn't really support passing arrays by value - the only way to pass an array by value
1058 // is through a struct. So, first test that the top level isn't an array, and then
1059 // recursively check the types inside.
1060 if !is_static
&& self.check_for_array_ty(sp
, ty
) {
1064 // Don't report FFI errors for unit return types. This check exists here, and not in
1065 // `check_foreign_fn` (where it would make more sense) so that normalization has definitely
1067 if is_return_type
&& ty
.is_unit() {
1071 match self.check_type_for_ffi(&mut FxHashSet
::default(), ty
) {
1072 FfiResult
::FfiSafe
=> {}
1073 FfiResult
::FfiPhantom(ty
) => {
1074 self.emit_ffi_unsafe_type_lint(ty
, sp
, "composed only of `PhantomData`", None
);
1076 // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic
1077 // argument, which after substitution, is `()`, then this branch can be hit.
1078 FfiResult
::FfiUnsafe { ty, .. }
if is_return_type
&& ty
.is_unit() => {}
1079 FfiResult
::FfiUnsafe { ty, reason, help }
=> {
1080 self.emit_ffi_unsafe_type_lint(ty
, sp
, &reason
, help
.as_deref());
1085 fn check_foreign_fn(&mut self, id
: hir
::HirId
, decl
: &hir
::FnDecl
<'_
>) {
1086 let def_id
= self.cx
.tcx
.hir().local_def_id(id
);
1087 let sig
= self.cx
.tcx
.fn_sig(def_id
);
1088 let sig
= self.cx
.tcx
.erase_late_bound_regions(&sig
);
1090 for (input_ty
, input_hir
) in sig
.inputs().iter().zip(decl
.inputs
) {
1091 self.check_type_for_ffi_and_report_errors(input_hir
.span
, input_ty
, false, false);
1094 if let hir
::FnRetTy
::Return(ref ret_hir
) = decl
.output
{
1095 let ret_ty
= sig
.output();
1096 self.check_type_for_ffi_and_report_errors(ret_hir
.span
, ret_ty
, false, true);
1100 fn check_foreign_static(&mut self, id
: hir
::HirId
, span
: Span
) {
1101 let def_id
= self.cx
.tcx
.hir().local_def_id(id
);
1102 let ty
= self.cx
.tcx
.type_of(def_id
);
1103 self.check_type_for_ffi_and_report_errors(span
, ty
, true, false);
1106 fn is_internal_abi(&self, abi
: SpecAbi
) -> bool
{
1107 if let SpecAbi
::Rust
1109 | SpecAbi
::RustIntrinsic
1110 | SpecAbi
::PlatformIntrinsic
= abi
1119 impl<'tcx
> LateLintPass
<'tcx
> for ImproperCTypesDeclarations
{
1120 fn check_foreign_item(&mut self, cx
: &LateContext
<'_
>, it
: &hir
::ForeignItem
<'_
>) {
1121 let mut vis
= ImproperCTypesVisitor { cx, mode: CItemKind::Declaration }
;
1122 let abi
= cx
.tcx
.hir().get_foreign_abi(it
.hir_id
);
1124 if !vis
.is_internal_abi(abi
) {
1126 hir
::ForeignItemKind
::Fn(ref decl
, _
, _
) => {
1127 vis
.check_foreign_fn(it
.hir_id
, decl
);
1129 hir
::ForeignItemKind
::Static(ref ty
, _
) => {
1130 vis
.check_foreign_static(it
.hir_id
, ty
.span
);
1132 hir
::ForeignItemKind
::Type
=> (),
1138 impl<'tcx
> LateLintPass
<'tcx
> for ImproperCTypesDefinitions
{
1141 cx
: &LateContext
<'tcx
>,
1142 kind
: hir
::intravisit
::FnKind
<'tcx
>,
1143 decl
: &'tcx hir
::FnDecl
<'_
>,
1144 _
: &'tcx hir
::Body
<'_
>,
1148 use hir
::intravisit
::FnKind
;
1150 let abi
= match kind
{
1151 FnKind
::ItemFn(_
, _
, header
, ..) => header
.abi
,
1152 FnKind
::Method(_
, sig
, ..) => sig
.header
.abi
,
1156 let mut vis
= ImproperCTypesVisitor { cx, mode: CItemKind::Definition }
;
1157 if !vis
.is_internal_abi(abi
) {
1158 vis
.check_foreign_fn(hir_id
, decl
);
1163 declare_lint_pass
!(VariantSizeDifferences
=> [VARIANT_SIZE_DIFFERENCES
]);
1165 impl<'tcx
> LateLintPass
<'tcx
> for VariantSizeDifferences
{
1166 fn check_item(&mut self, cx
: &LateContext
<'_
>, it
: &hir
::Item
<'_
>) {
1167 if let hir
::ItemKind
::Enum(ref enum_definition
, _
) = it
.kind
{
1168 let item_def_id
= cx
.tcx
.hir().local_def_id(it
.hir_id
);
1169 let t
= cx
.tcx
.type_of(item_def_id
);
1170 let ty
= cx
.tcx
.erase_regions(&t
);
1171 let layout
= match cx
.layout_of(ty
) {
1172 Ok(layout
) => layout
,
1174 ty
::layout
::LayoutError
::Unknown(_
) | ty
::layout
::LayoutError
::SizeOverflow(_
),
1177 let (variants
, tag
) = match layout
.variants
{
1178 Variants
::Multiple
{
1179 tag_encoding
: TagEncoding
::Direct
,
1183 } => (variants
, tag
),
1187 let tag_size
= tag
.value
.size(&cx
.tcx
).bytes();
1190 "enum `{}` is {} bytes large with layout:\n{:#?}",
1192 layout
.size
.bytes(),
1196 let (largest
, slargest
, largest_index
) = enum_definition
1200 .map(|(variant
, variant_layout
)| {
1201 // Subtract the size of the enum tag.
1202 let bytes
= variant_layout
.size
.bytes().saturating_sub(tag_size
);
1204 debug
!("- variant `{}` is {} bytes large", variant
.ident
, bytes
);
1208 .fold((0, 0, 0), |(l
, s
, li
), (idx
, size
)| {
1211 } else if size
> s
{
1218 // We only warn if the largest variant is at least thrice as large as
1219 // the second-largest.
1220 if largest
> slargest
* 3 && slargest
> 0 {
1221 cx
.struct_span_lint(
1222 VARIANT_SIZE_DIFFERENCES
,
1223 enum_definition
.variants
[largest_index
].span
,
1225 lint
.build(&format
!(
1226 "enum variant is more than three times \
1227 larger ({} bytes) than the next largest",