1 use crate::{LateContext, LateLintPass, LintContext}
;
3 use rustc_attr
as attr
;
4 use rustc_data_structures
::fx
::FxHashSet
;
5 use rustc_errors
::Applicability
;
7 use rustc_hir
::{is_range_literal, ExprKind, Node}
;
8 use rustc_index
::vec
::Idx
;
9 use rustc_middle
::mir
::interpret
::{sign_extend, truncate}
;
10 use rustc_middle
::ty
::layout
::{IntegerExt, SizeSkeleton}
;
11 use rustc_middle
::ty
::subst
::SubstsRef
;
12 use rustc_middle
::ty
::{self, AdtKind, Ty, TyCtxt, TypeFoldable}
;
13 use rustc_span
::source_map
;
14 use rustc_span
::symbol
::sym
;
15 use rustc_span
::{Span, DUMMY_SP}
;
16 use rustc_target
::abi
::Abi
;
17 use rustc_target
::abi
::{Integer, LayoutOf, TagEncoding, VariantIdx, Variants}
;
18 use rustc_target
::spec
::abi
::Abi
as SpecAbi
;
24 /// The `unused_comparisons` lint detects comparisons made useless by
25 /// limits of the types involved.
39 /// A useless comparison may indicate a mistake, and should be fixed or
43 "comparisons made useless by limits of the types involved"
47 /// The `overflowing_literals` lint detects literal out of range for its
52 /// ```rust,compile_fail
60 /// It is usually a mistake to use a literal that overflows the type where
61 /// it is used. Either use a literal that is within range, or change the
62 /// type to be within the range of the literal.
65 "literal out of range for its type"
69 /// The `variant_size_differences` lint detects enums with widely varying
74 /// ```rust,compile_fail
75 /// #![deny(variant_size_differences)]
86 /// It can be a mistake to add a variant to an enum that is much larger
87 /// than the other variants, bloating the overall size required for all
88 /// variants. This can impact performance and memory usage. This is
89 /// triggered if one variant is more than 3 times larger than the
90 /// second-largest variant.
92 /// Consider placing the large variant's contents on the heap (for example
93 /// via [`Box`]) to keep the overall size of the enum itself down.
95 /// This lint is "allow" by default because it can be noisy, and may not be
96 /// an actual problem. Decisions about this should be guided with
97 /// profiling and benchmarking.
99 /// [`Box`]: https://doc.rust-lang.org/std/boxed/index.html
100 VARIANT_SIZE_DIFFERENCES
,
102 "detects enums with widely varying variant sizes"
105 #[derive(Copy, Clone)]
106 pub struct TypeLimits
{
107 /// Id of the last visited negated expression
108 negated_expr_id
: Option
<hir
::HirId
>,
111 impl_lint_pass
!(TypeLimits
=> [UNUSED_COMPARISONS
, OVERFLOWING_LITERALS
]);
114 pub fn new() -> TypeLimits
{
115 TypeLimits { negated_expr_id: None }
119 /// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint.
120 /// Returns `true` iff the lint was overridden.
121 fn lint_overflowing_range_endpoint
<'tcx
>(
122 cx
: &LateContext
<'tcx
>,
126 expr
: &'tcx hir
::Expr
<'tcx
>,
127 parent_expr
: &'tcx hir
::Expr
<'tcx
>,
130 // We only want to handle exclusive (`..`) ranges,
131 // which are represented as `ExprKind::Struct`.
132 let mut overwritten
= false;
133 if let ExprKind
::Struct(_
, eps
, _
) = &parent_expr
.kind
{
137 // We can suggest using an inclusive range
138 // (`..=`) instead only if it is the `end` that is
139 // overflowing and only by 1.
140 if eps
[1].expr
.hir_id
== expr
.hir_id
&& lit_val
- 1 == max
{
141 cx
.struct_span_lint(OVERFLOWING_LITERALS
, parent_expr
.span
, |lint
| {
142 let mut err
= lint
.build(&format
!("range endpoint is out of range for `{}`", ty
));
143 if let Ok(start
) = cx
.sess().source_map().span_to_snippet(eps
[0].span
) {
144 use ast
::{LitIntType, LitKind}
;
145 // We need to preserve the literal's suffix,
146 // as it may determine typing information.
147 let suffix
= match lit
.node
{
148 LitKind
::Int(_
, LitIntType
::Signed(s
)) => s
.name_str().to_string(),
149 LitKind
::Int(_
, LitIntType
::Unsigned(s
)) => s
.name_str().to_string(),
150 LitKind
::Int(_
, LitIntType
::Unsuffixed
) => "".to_string(),
153 let suggestion
= format
!("{}..={}{}", start
, lit_val
- 1, suffix
);
156 &"use an inclusive range instead",
158 Applicability
::MachineApplicable
,
169 // For `isize` & `usize`, be conservative with the warnings, so that the
170 // warnings are consistent between 32- and 64-bit platforms.
171 fn int_ty_range(int_ty
: ast
::IntTy
) -> (i128
, i128
) {
173 ast
::IntTy
::Isize
=> (i64::MIN
as i128
, i64::MAX
as i128
),
174 ast
::IntTy
::I8
=> (i8::MIN
as i64 as i128
, i8::MAX
as i128
),
175 ast
::IntTy
::I16
=> (i16::MIN
as i64 as i128
, i16::MAX
as i128
),
176 ast
::IntTy
::I32
=> (i32::MIN
as i64 as i128
, i32::MAX
as i128
),
177 ast
::IntTy
::I64
=> (i64::MIN
as i128
, i64::MAX
as i128
),
178 ast
::IntTy
::I128
=> (i128
::MIN
as i128
, i128
::MAX
),
182 fn uint_ty_range(uint_ty
: ast
::UintTy
) -> (u128
, u128
) {
184 ast
::UintTy
::Usize
=> (u64::MIN
as u128
, u64::MAX
as u128
),
185 ast
::UintTy
::U8
=> (u8::MIN
as u128
, u8::MAX
as u128
),
186 ast
::UintTy
::U16
=> (u16::MIN
as u128
, u16::MAX
as u128
),
187 ast
::UintTy
::U32
=> (u32::MIN
as u128
, u32::MAX
as u128
),
188 ast
::UintTy
::U64
=> (u64::MIN
as u128
, u64::MAX
as u128
),
189 ast
::UintTy
::U128
=> (u128
::MIN
, u128
::MAX
),
193 fn get_bin_hex_repr(cx
: &LateContext
<'_
>, lit
: &hir
::Lit
) -> Option
<String
> {
194 let src
= cx
.sess().source_map().span_to_snippet(lit
.span
).ok()?
;
195 let firstch
= src
.chars().next()?
;
198 match src
.chars().nth(1) {
199 Some('x'
| 'b'
) => return Some(src
),
207 fn report_bin_hex_error(
208 cx
: &LateContext
<'_
>,
209 expr
: &hir
::Expr
<'_
>,
215 let size
= Integer
::from_attr(&cx
.tcx
, ty
).size();
216 cx
.struct_span_lint(OVERFLOWING_LITERALS
, expr
.span
, |lint
| {
217 let (t
, actually
) = match ty
{
218 attr
::IntType
::SignedInt(t
) => {
219 let actually
= sign_extend(val
, size
) as i128
;
220 (t
.name_str(), actually
.to_string())
222 attr
::IntType
::UnsignedInt(t
) => {
223 let actually
= truncate(val
, size
);
224 (t
.name_str(), actually
.to_string())
227 let mut err
= lint
.build(&format
!("literal out of range for {}", t
));
229 "the literal `{}` (decimal `{}`) does not fit into \
230 the type `{}` and will become `{}{}`",
231 repr_str
, val
, t
, actually
, t
233 if let Some(sugg_ty
) =
234 get_type_suggestion(&cx
.typeck_results().node_type(expr
.hir_id
), val
, negative
)
236 if let Some(pos
) = repr_str
.chars().position(|c
| c
== 'i'
|| c
== 'u'
) {
237 let (sans_suffix
, _
) = repr_str
.split_at(pos
);
240 &format
!("consider using `{}` instead", sugg_ty
),
241 format
!("{}{}", sans_suffix
, sugg_ty
),
242 Applicability
::MachineApplicable
,
245 err
.help(&format
!("consider using `{}` instead", sugg_ty
));
252 // This function finds the next fitting type and generates a suggestion string.
253 // It searches for fitting types in the following way (`X < Y`):
254 // - `iX`: if literal fits in `uX` => `uX`, else => `iY`
258 // No suggestion for: `isize`, `usize`.
259 fn get_type_suggestion(t
: Ty
<'_
>, val
: u128
, negative
: bool
) -> Option
<&'
static str> {
260 use rustc_ast
::IntTy
::*;
261 use rustc_ast
::UintTy
::*;
262 macro_rules
! find_fit
{
263 ($ty
:expr
, $val
:expr
, $negative
:expr
,
264 $
($
type:ident
=> [$
($utypes
:expr
),*] => [$
($itypes
:expr
),*]),+) => {
266 let _neg
= if negative { 1 }
else { 0 }
;
269 $
(if !negative
&& val
<= uint_ty_range($utypes
).1 {
270 return Some($utypes
.name_str())
272 $
(if val
<= int_ty_range($itypes
).1 as u128
+ _neg
{
273 return Some($itypes
.name_str())
283 ty
::Int(i
) => find_fit
!(i
, val
, negative
,
284 I8
=> [U8
] => [I16
, I32
, I64
, I128
],
285 I16
=> [U16
] => [I32
, I64
, I128
],
286 I32
=> [U32
] => [I64
, I128
],
287 I64
=> [U64
] => [I128
],
288 I128
=> [U128
] => []),
289 ty
::Uint(u
) => find_fit
!(u
, val
, negative
,
290 U8
=> [U8
, U16
, U32
, U64
, U128
] => [],
291 U16
=> [U16
, U32
, U64
, U128
] => [],
292 U32
=> [U32
, U64
, U128
] => [],
293 U64
=> [U64
, U128
] => [],
294 U128
=> [U128
] => []),
299 fn lint_int_literal
<'tcx
>(
300 cx
: &LateContext
<'tcx
>,
301 type_limits
: &TypeLimits
,
302 e
: &'tcx hir
::Expr
<'tcx
>,
307 let int_type
= t
.normalize(cx
.sess().target
.ptr_width
);
308 let (min
, max
) = int_ty_range(int_type
);
309 let max
= max
as u128
;
310 let negative
= type_limits
.negated_expr_id
== Some(e
.hir_id
);
312 // Detect literal value out of range [min, max] inclusive
313 // avoiding use of -min to prevent overflow/panic
314 if (negative
&& v
> max
+ 1) || (!negative
&& v
> max
) {
315 if let Some(repr_str
) = get_bin_hex_repr(cx
, lit
) {
316 report_bin_hex_error(cx
, e
, attr
::IntType
::SignedInt(t
), repr_str
, v
, negative
);
320 let par_id
= cx
.tcx
.hir().get_parent_node(e
.hir_id
);
321 if let Node
::Expr(par_e
) = cx
.tcx
.hir().get(par_id
) {
322 if let hir
::ExprKind
::Struct(..) = par_e
.kind
{
323 if is_range_literal(par_e
)
324 && lint_overflowing_range_endpoint(cx
, lit
, v
, max
, e
, par_e
, t
.name_str())
326 // The overflowing literal lint was overridden.
332 cx
.struct_span_lint(OVERFLOWING_LITERALS
, e
.span
, |lint
| {
333 lint
.build(&format
!("literal out of range for `{}`", t
.name_str()))
335 "the literal `{}` does not fit into the type `{}` whose range is `{}..={}`",
338 .span_to_snippet(lit
.span
)
339 .expect("must get snippet from literal"),
349 fn lint_uint_literal
<'tcx
>(
350 cx
: &LateContext
<'tcx
>,
351 e
: &'tcx hir
::Expr
<'tcx
>,
355 let uint_type
= t
.normalize(cx
.sess().target
.ptr_width
);
356 let (min
, max
) = uint_ty_range(uint_type
);
357 let lit_val
: u128
= match lit
.node
{
358 // _v is u8, within range by definition
359 ast
::LitKind
::Byte(_v
) => return,
360 ast
::LitKind
::Int(v
, _
) => v
,
363 if lit_val
< min
|| lit_val
> max
{
364 let parent_id
= cx
.tcx
.hir().get_parent_node(e
.hir_id
);
365 if let Node
::Expr(par_e
) = cx
.tcx
.hir().get(parent_id
) {
367 hir
::ExprKind
::Cast(..) => {
368 if let ty
::Char
= cx
.typeck_results().expr_ty(par_e
).kind() {
369 cx
.struct_span_lint(OVERFLOWING_LITERALS
, par_e
.span
, |lint
| {
370 lint
.build("only `u8` can be cast into `char`")
373 &"use a `char` literal instead",
374 format
!("'\\u{{{:X}}}'", lit_val
),
375 Applicability
::MachineApplicable
,
382 hir
::ExprKind
::Struct(..) if is_range_literal(par_e
) => {
383 let t
= t
.name_str();
384 if lint_overflowing_range_endpoint(cx
, lit
, lit_val
, max
, e
, par_e
, t
) {
385 // The overflowing literal lint was overridden.
392 if let Some(repr_str
) = get_bin_hex_repr(cx
, lit
) {
393 report_bin_hex_error(cx
, e
, attr
::IntType
::UnsignedInt(t
), repr_str
, lit_val
, false);
396 cx
.struct_span_lint(OVERFLOWING_LITERALS
, e
.span
, |lint
| {
397 lint
.build(&format
!("literal out of range for `{}`", t
.name_str()))
399 "the literal `{}` does not fit into the type `{}` whose range is `{}..={}`",
402 .span_to_snippet(lit
.span
)
403 .expect("must get snippet from literal"),
413 fn lint_literal
<'tcx
>(
414 cx
: &LateContext
<'tcx
>,
415 type_limits
: &TypeLimits
,
416 e
: &'tcx hir
::Expr
<'tcx
>,
419 match *cx
.typeck_results().node_type(e
.hir_id
).kind() {
422 ast
::LitKind
::Int(v
, ast
::LitIntType
::Signed(_
) | ast
::LitIntType
::Unsuffixed
) => {
423 lint_int_literal(cx
, type_limits
, e
, lit
, t
, v
)
428 ty
::Uint(t
) => lint_uint_literal(cx
, e
, lit
, t
),
430 let is_infinite
= match lit
.node
{
431 ast
::LitKind
::Float(v
, _
) => match t
{
432 ast
::FloatTy
::F32
=> v
.as_str().parse().map(f32::is_infinite
),
433 ast
::FloatTy
::F64
=> v
.as_str().parse().map(f64::is_infinite
),
437 if is_infinite
== Ok(true) {
438 cx
.struct_span_lint(OVERFLOWING_LITERALS
, e
.span
, |lint
| {
439 lint
.build(&format
!("literal out of range for `{}`", t
.name_str()))
441 "the literal `{}` does not fit into the type `{}` and will be converted to `std::{}::INFINITY`",
444 .span_to_snippet(lit
.span
)
445 .expect("must get snippet from literal"),
457 impl<'tcx
> LateLintPass
<'tcx
> for TypeLimits
{
458 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, e
: &'tcx hir
::Expr
<'tcx
>) {
460 hir
::ExprKind
::Unary(hir
::UnOp
::UnNeg
, ref expr
) => {
461 // propagate negation, if the negation itself isn't negated
462 if self.negated_expr_id
!= Some(e
.hir_id
) {
463 self.negated_expr_id
= Some(expr
.hir_id
);
466 hir
::ExprKind
::Binary(binop
, ref l
, ref r
) => {
467 if is_comparison(binop
) && !check_limits(cx
, binop
, &l
, &r
) {
468 cx
.struct_span_lint(UNUSED_COMPARISONS
, e
.span
, |lint
| {
469 lint
.build("comparison is useless due to type limits").emit()
473 hir
::ExprKind
::Lit(ref lit
) => lint_literal(cx
, self, e
, lit
),
477 fn is_valid
<T
: cmp
::PartialOrd
>(binop
: hir
::BinOp
, v
: T
, min
: T
, max
: T
) -> bool
{
479 hir
::BinOpKind
::Lt
=> v
> min
&& v
<= max
,
480 hir
::BinOpKind
::Le
=> v
>= min
&& v
< max
,
481 hir
::BinOpKind
::Gt
=> v
>= min
&& v
< max
,
482 hir
::BinOpKind
::Ge
=> v
> min
&& v
<= max
,
483 hir
::BinOpKind
::Eq
| hir
::BinOpKind
::Ne
=> v
>= min
&& v
<= max
,
488 fn rev_binop(binop
: hir
::BinOp
) -> hir
::BinOp
{
492 hir
::BinOpKind
::Lt
=> hir
::BinOpKind
::Gt
,
493 hir
::BinOpKind
::Le
=> hir
::BinOpKind
::Ge
,
494 hir
::BinOpKind
::Gt
=> hir
::BinOpKind
::Lt
,
495 hir
::BinOpKind
::Ge
=> hir
::BinOpKind
::Le
,
502 cx
: &LateContext
<'_
>,
507 let (lit
, expr
, swap
) = match (&l
.kind
, &r
.kind
) {
508 (&hir
::ExprKind
::Lit(_
), _
) => (l
, r
, true),
509 (_
, &hir
::ExprKind
::Lit(_
)) => (r
, l
, false),
512 // Normalize the binop so that the literal is always on the RHS in
514 let norm_binop
= if swap { rev_binop(binop) }
else { binop }
;
515 match *cx
.typeck_results().node_type(expr
.hir_id
).kind() {
517 let (min
, max
) = int_ty_range(int_ty
);
518 let lit_val
: i128
= match lit
.kind
{
519 hir
::ExprKind
::Lit(ref li
) => match li
.node
{
522 ast
::LitIntType
::Signed(_
) | ast
::LitIntType
::Unsuffixed
,
528 is_valid(norm_binop
, lit_val
, min
, max
)
530 ty
::Uint(uint_ty
) => {
531 let (min
, max
): (u128
, u128
) = uint_ty_range(uint_ty
);
532 let lit_val
: u128
= match lit
.kind
{
533 hir
::ExprKind
::Lit(ref li
) => match li
.node
{
534 ast
::LitKind
::Int(v
, _
) => v
,
539 is_valid(norm_binop
, lit_val
, min
, max
)
545 fn is_comparison(binop
: hir
::BinOp
) -> bool
{
552 | hir
::BinOpKind
::Gt
=> true,
560 /// The `improper_ctypes` lint detects incorrect use of types in foreign
567 /// static STATIC: String;
575 /// The compiler has several checks to verify that types used in `extern`
576 /// blocks are safe and follow certain rules to ensure proper
577 /// compatibility with the foreign interfaces. This lint is issued when it
578 /// detects a probable mistake in a definition. The lint usually should
579 /// provide a description of the issue, along with possibly a hint on how
583 "proper use of libc types in foreign modules"
586 declare_lint_pass
!(ImproperCTypesDeclarations
=> [IMPROPER_CTYPES
]);
589 /// The `improper_ctypes_definitions` lint detects incorrect use of
590 /// [`extern` function] definitions.
592 /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
597 /// # #![allow(unused)]
598 /// pub extern "C" fn str_type(p: &str) { }
605 /// There are many parameter and return types that may be specified in an
606 /// `extern` function that are not compatible with the given ABI. This
607 /// lint is an alert that these types should not be used. The lint usually
608 /// should provide a description of the issue, along with possibly a hint
609 /// on how to resolve it.
610 IMPROPER_CTYPES_DEFINITIONS
,
612 "proper use of libc types in foreign item definitions"
615 declare_lint_pass
!(ImproperCTypesDefinitions
=> [IMPROPER_CTYPES_DEFINITIONS
]);
617 #[derive(Clone, Copy)]
618 crate enum CItemKind
{
623 struct ImproperCTypesVisitor
<'a
, 'tcx
> {
624 cx
: &'a LateContext
<'tcx
>,
628 enum FfiResult
<'tcx
> {
630 FfiPhantom(Ty
<'tcx
>),
631 FfiUnsafe { ty: Ty<'tcx>, reason: String, help: Option<String> }
,
634 crate fn nonnull_optimization_guaranteed
<'tcx
>(tcx
: TyCtxt
<'tcx
>, def
: &ty
::AdtDef
) -> bool
{
635 tcx
.get_attrs(def
.did
)
637 .any(|a
| tcx
.sess
.check_name(a
, sym
::rustc_nonnull_optimization_guaranteed
))
640 /// `repr(transparent)` structs can have a single non-ZST field, this function returns that
642 pub fn transparent_newtype_field
<'a
, 'tcx
>(
644 variant
: &'a ty
::VariantDef
,
645 ) -> Option
<&'a ty
::FieldDef
> {
646 let param_env
= tcx
.param_env(variant
.def_id
);
647 for field
in &variant
.fields
{
648 let field_ty
= tcx
.type_of(field
.did
);
650 tcx
.layout_of(param_env
.and(field_ty
)).map(|layout
| layout
.is_zst()).unwrap_or(false);
660 /// Is type known to be non-null?
661 crate fn ty_is_known_nonnull
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>, mode
: CItemKind
) -> bool
{
664 ty
::FnPtr(_
) => true,
666 ty
::Adt(def
, _
) if def
.is_box() && matches
!(mode
, CItemKind
::Definition
) => true,
667 ty
::Adt(def
, substs
) if def
.repr
.transparent() && !def
.is_union() => {
668 let marked_non_null
= nonnull_optimization_guaranteed(tcx
, &def
);
674 for variant
in &def
.variants
{
675 if let Some(field
) = transparent_newtype_field(cx
.tcx
, variant
) {
676 if ty_is_known_nonnull(cx
, field
.ty(tcx
, substs
), mode
) {
688 /// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type.
689 /// If the type passed in was not scalar, returns None.
690 fn get_nullable_type
<'tcx
>(cx
: &LateContext
<'tcx
>, ty
: Ty
<'tcx
>) -> Option
<Ty
<'tcx
>> {
692 Some(match *ty
.kind() {
693 ty
::Adt(field_def
, field_substs
) => {
694 let inner_field_ty
= {
695 let first_non_zst_ty
=
696 field_def
.variants
.iter().filter_map(|v
| transparent_newtype_field(cx
.tcx
, v
));
698 first_non_zst_ty
.clone().count(),
700 "Wrong number of fields for transparent type"
704 .expect("No non-zst fields in transparent type.")
705 .ty(tcx
, field_substs
)
707 return get_nullable_type(cx
, inner_field_ty
);
709 ty
::Int(ty
) => tcx
.mk_mach_int(ty
),
710 ty
::Uint(ty
) => tcx
.mk_mach_uint(ty
),
711 ty
::RawPtr(ty_mut
) => tcx
.mk_ptr(ty_mut
),
712 // As these types are always non-null, the nullable equivalent of
713 // Option<T> of these types are their raw pointer counterparts.
714 ty
::Ref(_region
, ty
, mutbl
) => tcx
.mk_ptr(ty
::TypeAndMut { ty, mutbl }
),
716 // There is no nullable equivalent for Rust's function pointers -- you
717 // must use an Option<fn(..) -> _> to represent it.
721 // We should only ever reach this case if ty_is_known_nonnull is extended
725 "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}",
733 /// Check if this enum can be safely exported based on the "nullable pointer optimization". If it
734 /// can, return the type that `ty` can be safely converted to, otherwise return `None`.
735 /// Currently restricted to function pointers, boxes, references, `core::num::NonZero*`,
736 /// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes.
737 /// FIXME: This duplicates code in codegen.
738 crate fn repr_nullable_ptr
<'tcx
>(
739 cx
: &LateContext
<'tcx
>,
742 ) -> Option
<Ty
<'tcx
>> {
743 debug
!("is_repr_nullable_ptr(cx, ty = {:?})", ty
);
744 if let ty
::Adt(ty_def
, substs
) = ty
.kind() {
745 if ty_def
.variants
.len() != 2 {
749 let get_variant_fields
= |index
| &ty_def
.variants
[VariantIdx
::new(index
)].fields
;
750 let variant_fields
= [get_variant_fields(0), get_variant_fields(1)];
751 let fields
= if variant_fields
[0].is_empty() {
753 } else if variant_fields
[1].is_empty() {
759 if fields
.len() != 1 {
763 let field_ty
= fields
[0].ty(cx
.tcx
, substs
);
764 if !ty_is_known_nonnull(cx
, field_ty
, ckind
) {
768 // At this point, the field's type is known to be nonnull and the parent enum is Option-like.
769 // If the computed size for the field and the enum are different, the nonnull optimization isn't
770 // being applied (and we've got a problem somewhere).
771 let compute_size_skeleton
= |t
| SizeSkeleton
::compute(t
, cx
.tcx
, cx
.param_env
).unwrap();
772 if !compute_size_skeleton(ty
).same_size(compute_size_skeleton(field_ty
)) {
773 bug
!("improper_ctypes: Option nonnull optimization not applied?");
776 // Return the nullable type this Option-like enum can be safely represented with.
777 let field_ty_abi
= &cx
.layout_of(field_ty
).unwrap().abi
;
778 if let Abi
::Scalar(field_ty_scalar
) = field_ty_abi
{
779 match (field_ty_scalar
.valid_range
.start(), field_ty_scalar
.valid_range
.end()) {
780 (0, _
) => unreachable
!("Non-null optimisation extended to a non-zero value."),
782 return Some(get_nullable_type(cx
, field_ty
).unwrap());
784 (start
, end
) => unreachable
!("Unhandled start and end range: ({}, {})", start
, end
),
791 impl<'a
, 'tcx
> ImproperCTypesVisitor
<'a
, 'tcx
> {
792 /// Check if the type is array and emit an unsafe type lint.
793 fn check_for_array_ty(&mut self, sp
: Span
, ty
: Ty
<'tcx
>) -> bool
{
794 if let ty
::Array(..) = ty
.kind() {
795 self.emit_ffi_unsafe_type_lint(
798 "passing raw arrays by value is not FFI-safe",
799 Some("consider passing a pointer to the array"),
807 /// Checks if the given field's type is "ffi-safe".
808 fn check_field_type_for_ffi(
810 cache
: &mut FxHashSet
<Ty
<'tcx
>>,
811 field
: &ty
::FieldDef
,
812 substs
: SubstsRef
<'tcx
>,
813 ) -> FfiResult
<'tcx
> {
814 let field_ty
= field
.ty(self.cx
.tcx
, substs
);
815 if field_ty
.has_opaque_types() {
816 self.check_type_for_ffi(cache
, field_ty
)
818 let field_ty
= self.cx
.tcx
.normalize_erasing_regions(self.cx
.param_env
, field_ty
);
819 self.check_type_for_ffi(cache
, field_ty
)
823 /// Checks if the given `VariantDef`'s field types are "ffi-safe".
824 fn check_variant_for_ffi(
826 cache
: &mut FxHashSet
<Ty
<'tcx
>>,
829 variant
: &ty
::VariantDef
,
830 substs
: SubstsRef
<'tcx
>,
831 ) -> FfiResult
<'tcx
> {
834 if def
.repr
.transparent() {
835 // Can assume that only one field is not a ZST, so only check
836 // that field's type for FFI-safety.
837 if let Some(field
) = transparent_newtype_field(self.cx
.tcx
, variant
) {
838 self.check_field_type_for_ffi(cache
, field
, substs
)
840 bug
!("malformed transparent type");
843 // We can't completely trust repr(C) markings; make sure the fields are
845 let mut all_phantom
= !variant
.fields
.is_empty();
846 for field
in &variant
.fields
{
847 match self.check_field_type_for_ffi(cache
, &field
, substs
) {
851 FfiPhantom(..) if def
.is_enum() => {
854 reason
: "this enum contains a PhantomData field".into(),
863 if all_phantom { FfiPhantom(ty) }
else { FfiSafe }
867 /// Checks if the given type is "ffi-safe" (has a stable, well-defined
868 /// representation which can be exported to C code).
869 fn check_type_for_ffi(&self, cache
: &mut FxHashSet
<Ty
<'tcx
>>, ty
: Ty
<'tcx
>) -> FfiResult
<'tcx
> {
872 let tcx
= self.cx
.tcx
;
874 // Protect against infinite recursion, for example
875 // `struct S(*mut S);`.
876 // FIXME: A recursion limit is necessary as well, for irregular
878 if !cache
.insert(ty
) {
883 ty
::Adt(def
, _
) if def
.is_box() && matches
!(self.mode
, CItemKind
::Definition
) => {
887 ty
::Adt(def
, substs
) => {
888 if def
.is_phantom_data() {
889 return FfiPhantom(ty
);
891 match def
.adt_kind() {
892 AdtKind
::Struct
| AdtKind
::Union
=> {
893 let kind
= if def
.is_struct() { "struct" }
else { "union" }
;
895 if !def
.repr
.c() && !def
.repr
.transparent() {
898 reason
: format
!("this {} has unspecified layout", kind
),
900 "consider adding a `#[repr(C)]` or \
901 `#[repr(transparent)]` attribute to this {}",
907 let is_non_exhaustive
=
908 def
.non_enum_variant().is_field_list_non_exhaustive();
909 if is_non_exhaustive
&& !def
.did
.is_local() {
912 reason
: format
!("this {} is non-exhaustive", kind
),
917 if def
.non_enum_variant().fields
.is_empty() {
920 reason
: format
!("this {} has no fields", kind
),
921 help
: Some(format
!("consider adding a member to this {}", kind
)),
925 self.check_variant_for_ffi(cache
, ty
, def
, def
.non_enum_variant(), substs
)
928 if def
.variants
.is_empty() {
929 // Empty enums are okay... although sort of useless.
933 // Check for a repr() attribute to specify the size of the
935 if !def
.repr
.c() && !def
.repr
.transparent() && def
.repr
.int
.is_none() {
936 // Special-case types like `Option<extern fn()>`.
937 if repr_nullable_ptr(self.cx
, ty
, self.mode
).is_none() {
940 reason
: "enum has no representation hint".into(),
942 "consider adding a `#[repr(C)]`, \
943 `#[repr(transparent)]`, or integer `#[repr(...)]` \
944 attribute to this enum"
951 if def
.is_variant_list_non_exhaustive() && !def
.did
.is_local() {
954 reason
: "this enum is non-exhaustive".into(),
959 // Check the contained variants.
960 for variant
in &def
.variants
{
961 let is_non_exhaustive
= variant
.is_field_list_non_exhaustive();
962 if is_non_exhaustive
&& !variant
.def_id
.is_local() {
965 reason
: "this enum has non-exhaustive variants".into(),
970 match self.check_variant_for_ffi(cache
, ty
, def
, variant
, substs
) {
981 ty
::Char
=> FfiUnsafe
{
983 reason
: "the `char` type has no C equivalent".into(),
984 help
: Some("consider using `u32` or `libc::wchar_t` instead".into()),
987 ty
::Int(ast
::IntTy
::I128
) | ty
::Uint(ast
::UintTy
::U128
) => FfiUnsafe
{
989 reason
: "128-bit integers don't currently have a known stable ABI".into(),
993 // Primitive types with a stable representation.
994 ty
::Bool
| ty
::Int(..) | ty
::Uint(..) | ty
::Float(..) | ty
::Never
=> FfiSafe
,
996 ty
::Slice(_
) => FfiUnsafe
{
998 reason
: "slices have no C equivalent".into(),
999 help
: Some("consider using a raw pointer instead".into()),
1002 ty
::Dynamic(..) => {
1003 FfiUnsafe { ty, reason: "trait objects have no C equivalent".into(), help: None }
1006 ty
::Str
=> FfiUnsafe
{
1008 reason
: "string slices have no C equivalent".into(),
1009 help
: Some("consider using `*const u8` and a length instead".into()),
1012 ty
::Tuple(..) => FfiUnsafe
{
1014 reason
: "tuples have unspecified layout".into(),
1015 help
: Some("consider using a struct instead".into()),
1018 ty
::RawPtr(ty
::TypeAndMut { ty, .. }
) | ty
::Ref(_
, ty
, _
)
1020 matches
!(self.mode
, CItemKind
::Definition
)
1021 && ty
.is_sized(self.cx
.tcx
.at(DUMMY_SP
), self.cx
.param_env
)
1027 ty
::RawPtr(ty
::TypeAndMut { ty, .. }
) | ty
::Ref(_
, ty
, _
) => {
1028 self.check_type_for_ffi(cache
, ty
)
1031 ty
::Array(inner_ty
, _
) => self.check_type_for_ffi(cache
, inner_ty
),
1034 if self.is_internal_abi(sig
.abi()) {
1037 reason
: "this function pointer has Rust-specific calling convention".into(),
1039 "consider using an `extern fn(...) -> ...` \
1040 function pointer instead"
1046 let sig
= tcx
.erase_late_bound_regions(&sig
);
1047 if !sig
.output().is_unit() {
1048 let r
= self.check_type_for_ffi(cache
, sig
.output());
1056 for arg
in sig
.inputs() {
1057 let r
= self.check_type_for_ffi(cache
, arg
);
1068 ty
::Foreign(..) => FfiSafe
,
1070 // While opaque types are checked for earlier, if a projection in a struct field
1071 // normalizes to an opaque type, then it will reach this branch.
1073 FfiUnsafe { ty, reason: "opaque types have no C equivalent".into(), help: None }
1076 // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
1077 // so they are currently ignored for the purposes of this lint.
1078 ty
::Param(..) | ty
::Projection(..) if matches
!(self.mode
, CItemKind
::Definition
) => {
1083 | ty
::Projection(..)
1089 | ty
::GeneratorWitness(..)
1090 | ty
::Placeholder(..)
1091 | ty
::FnDef(..) => bug
!("unexpected type in foreign function: {:?}", ty
),
1095 fn emit_ffi_unsafe_type_lint(
1102 let lint
= match self.mode
{
1103 CItemKind
::Declaration
=> IMPROPER_CTYPES
,
1104 CItemKind
::Definition
=> IMPROPER_CTYPES_DEFINITIONS
,
1107 self.cx
.struct_span_lint(lint
, sp
, |lint
| {
1108 let item_description
= match self.mode
{
1109 CItemKind
::Declaration
=> "block",
1110 CItemKind
::Definition
=> "fn",
1112 let mut diag
= lint
.build(&format
!(
1113 "`extern` {} uses type `{}`, which is not FFI-safe",
1114 item_description
, ty
1116 diag
.span_label(sp
, "not FFI-safe");
1117 if let Some(help
) = help
{
1121 if let ty
::Adt(def
, _
) = ty
.kind() {
1122 if let Some(sp
) = self.cx
.tcx
.hir().span_if_local(def
.did
) {
1123 diag
.span_note(sp
, "the type is defined here");
1130 fn check_for_opaque_ty(&mut self, sp
: Span
, ty
: Ty
<'tcx
>) -> bool
{
1131 struct ProhibitOpaqueTypes
<'a
, 'tcx
> {
1132 cx
: &'a LateContext
<'tcx
>,
1133 ty
: Option
<Ty
<'tcx
>>,
1136 impl<'a
, 'tcx
> ty
::fold
::TypeVisitor
<'tcx
> for ProhibitOpaqueTypes
<'a
, 'tcx
> {
1137 fn visit_ty(&mut self, ty
: Ty
<'tcx
>) -> bool
{
1143 // Consider opaque types within projections FFI-safe if they do not normalize
1144 // to more opaque types.
1145 ty
::Projection(..) => {
1146 let ty
= self.cx
.tcx
.normalize_erasing_regions(self.cx
.param_env
, ty
);
1148 // If `ty` is a opaque type directly then `super_visit_with` won't invoke
1149 // this function again.
1150 if ty
.has_opaque_types() { self.visit_ty(ty) }
else { false }
1152 _
=> ty
.super_visit_with(self),
1157 let mut visitor
= ProhibitOpaqueTypes { cx: self.cx, ty: None }
;
1158 ty
.visit_with(&mut visitor
);
1159 if let Some(ty
) = visitor
.ty
{
1160 self.emit_ffi_unsafe_type_lint(ty
, sp
, "opaque types have no C equivalent", None
);
1167 fn check_type_for_ffi_and_report_errors(
1172 is_return_type
: bool
,
1174 // We have to check for opaque types before `normalize_erasing_regions`,
1175 // which will replace opaque types with their underlying concrete type.
1176 if self.check_for_opaque_ty(sp
, ty
) {
1177 // We've already emitted an error due to an opaque type.
1181 // it is only OK to use this function because extern fns cannot have
1182 // any generic types right now:
1183 let ty
= self.cx
.tcx
.normalize_erasing_regions(self.cx
.param_env
, ty
);
1185 // C doesn't really support passing arrays by value - the only way to pass an array by value
1186 // is through a struct. So, first test that the top level isn't an array, and then
1187 // recursively check the types inside.
1188 if !is_static
&& self.check_for_array_ty(sp
, ty
) {
1192 // Don't report FFI errors for unit return types. This check exists here, and not in
1193 // `check_foreign_fn` (where it would make more sense) so that normalization has definitely
1195 if is_return_type
&& ty
.is_unit() {
1199 match self.check_type_for_ffi(&mut FxHashSet
::default(), ty
) {
1200 FfiResult
::FfiSafe
=> {}
1201 FfiResult
::FfiPhantom(ty
) => {
1202 self.emit_ffi_unsafe_type_lint(ty
, sp
, "composed only of `PhantomData`", None
);
1204 // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic
1205 // argument, which after substitution, is `()`, then this branch can be hit.
1206 FfiResult
::FfiUnsafe { ty, .. }
if is_return_type
&& ty
.is_unit() => {}
1207 FfiResult
::FfiUnsafe { ty, reason, help }
=> {
1208 self.emit_ffi_unsafe_type_lint(ty
, sp
, &reason
, help
.as_deref());
1213 fn check_foreign_fn(&mut self, id
: hir
::HirId
, decl
: &hir
::FnDecl
<'_
>) {
1214 let def_id
= self.cx
.tcx
.hir().local_def_id(id
);
1215 let sig
= self.cx
.tcx
.fn_sig(def_id
);
1216 let sig
= self.cx
.tcx
.erase_late_bound_regions(&sig
);
1218 for (input_ty
, input_hir
) in sig
.inputs().iter().zip(decl
.inputs
) {
1219 self.check_type_for_ffi_and_report_errors(input_hir
.span
, input_ty
, false, false);
1222 if let hir
::FnRetTy
::Return(ref ret_hir
) = decl
.output
{
1223 let ret_ty
= sig
.output();
1224 self.check_type_for_ffi_and_report_errors(ret_hir
.span
, ret_ty
, false, true);
1228 fn check_foreign_static(&mut self, id
: hir
::HirId
, span
: Span
) {
1229 let def_id
= self.cx
.tcx
.hir().local_def_id(id
);
1230 let ty
= self.cx
.tcx
.type_of(def_id
);
1231 self.check_type_for_ffi_and_report_errors(span
, ty
, true, false);
1234 fn is_internal_abi(&self, abi
: SpecAbi
) -> bool
{
1235 if let SpecAbi
::Rust
1237 | SpecAbi
::RustIntrinsic
1238 | SpecAbi
::PlatformIntrinsic
= abi
1247 impl<'tcx
> LateLintPass
<'tcx
> for ImproperCTypesDeclarations
{
1248 fn check_foreign_item(&mut self, cx
: &LateContext
<'_
>, it
: &hir
::ForeignItem
<'_
>) {
1249 let mut vis
= ImproperCTypesVisitor { cx, mode: CItemKind::Declaration }
;
1250 let abi
= cx
.tcx
.hir().get_foreign_abi(it
.hir_id
);
1252 if !vis
.is_internal_abi(abi
) {
1254 hir
::ForeignItemKind
::Fn(ref decl
, _
, _
) => {
1255 vis
.check_foreign_fn(it
.hir_id
, decl
);
1257 hir
::ForeignItemKind
::Static(ref ty
, _
) => {
1258 vis
.check_foreign_static(it
.hir_id
, ty
.span
);
1260 hir
::ForeignItemKind
::Type
=> (),
1266 impl<'tcx
> LateLintPass
<'tcx
> for ImproperCTypesDefinitions
{
1269 cx
: &LateContext
<'tcx
>,
1270 kind
: hir
::intravisit
::FnKind
<'tcx
>,
1271 decl
: &'tcx hir
::FnDecl
<'_
>,
1272 _
: &'tcx hir
::Body
<'_
>,
1276 use hir
::intravisit
::FnKind
;
1278 let abi
= match kind
{
1279 FnKind
::ItemFn(_
, _
, header
, ..) => header
.abi
,
1280 FnKind
::Method(_
, sig
, ..) => sig
.header
.abi
,
1284 let mut vis
= ImproperCTypesVisitor { cx, mode: CItemKind::Definition }
;
1285 if !vis
.is_internal_abi(abi
) {
1286 vis
.check_foreign_fn(hir_id
, decl
);
1291 declare_lint_pass
!(VariantSizeDifferences
=> [VARIANT_SIZE_DIFFERENCES
]);
1293 impl<'tcx
> LateLintPass
<'tcx
> for VariantSizeDifferences
{
1294 fn check_item(&mut self, cx
: &LateContext
<'_
>, it
: &hir
::Item
<'_
>) {
1295 if let hir
::ItemKind
::Enum(ref enum_definition
, _
) = it
.kind
{
1296 let item_def_id
= cx
.tcx
.hir().local_def_id(it
.hir_id
);
1297 let t
= cx
.tcx
.type_of(item_def_id
);
1298 let ty
= cx
.tcx
.erase_regions(&t
);
1299 let layout
= match cx
.layout_of(ty
) {
1300 Ok(layout
) => layout
,
1302 ty
::layout
::LayoutError
::Unknown(_
) | ty
::layout
::LayoutError
::SizeOverflow(_
),
1305 let (variants
, tag
) = match layout
.variants
{
1306 Variants
::Multiple
{
1307 tag_encoding
: TagEncoding
::Direct
,
1311 } => (variants
, tag
),
1315 let tag_size
= tag
.value
.size(&cx
.tcx
).bytes();
1318 "enum `{}` is {} bytes large with layout:\n{:#?}",
1320 layout
.size
.bytes(),
1324 let (largest
, slargest
, largest_index
) = enum_definition
1328 .map(|(variant
, variant_layout
)| {
1329 // Subtract the size of the enum tag.
1330 let bytes
= variant_layout
.size
.bytes().saturating_sub(tag_size
);
1332 debug
!("- variant `{}` is {} bytes large", variant
.ident
, bytes
);
1336 .fold((0, 0, 0), |(l
, s
, li
), (idx
, size
)| {
1339 } else if size
> s
{
1346 // We only warn if the largest variant is at least thrice as large as
1347 // the second-largest.
1348 if largest
> slargest
* 3 && slargest
> 0 {
1349 cx
.struct_span_lint(
1350 VARIANT_SIZE_DIFFERENCES
,
1351 enum_definition
.variants
[largest_index
].span
,
1353 lint
.build(&format
!(
1354 "enum variant is more than three times \
1355 larger ({} bytes) than the next largest",