1use std::iter;
2use std::ops::ControlFlow;
3
4use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, VariantIdx, Variants, WrappingRange};
5use rustc_data_structures::fx::FxHashSet;
6use rustc_errors::DiagMessage;
7use rustc_hir::intravisit::VisitorExt;
8use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem};
9use rustc_middle::bug;
10use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton};
11use rustc_middle::ty::{
12 self, Adt, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
13 TypeVisitableExt,
14};
15use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
16use rustc_span::def_id::LocalDefId;
17use rustc_span::{Span, Symbol, source_map, sym};
18use tracing::debug;
19use {rustc_ast as ast, rustc_hir as hir};
20
21mod improper_ctypes;
22
23use crate::lints::{
24 AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
25 AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
26 AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
27 InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
28 UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, UsesPowerAlignment,
29 VariantSizeDifferencesDiag,
30};
31use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
32
33mod literal;
34
35use literal::{int_ty_range, lint_literal, uint_ty_range};
36
37declare_lint! {
38 UNUSED_COMPARISONS,
56 Warn,
57 "comparisons made useless by limits of the types involved"
58}
59
60declare_lint! {
61 OVERFLOWING_LITERALS,
78 Deny,
79 "literal out of range for its type"
80}
81
82declare_lint! {
83 VARIANT_SIZE_DIFFERENCES,
115 Allow,
116 "detects enums with widely varying variant sizes"
117}
118
119declare_lint! {
120 INVALID_NAN_COMPARISONS,
137 Warn,
138 "detects invalid floating point NaN comparisons"
139}
140
141declare_lint! {
142 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
168 Warn,
169 "detects ambiguous wide pointer comparisons"
170}
171
172declare_lint! {
173 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
197 Warn,
198 "detects unpredictable function pointer comparisons"
199}
200
201#[derive(Copy, Clone, Default)]
202pub(crate) struct TypeLimits {
203 negated_expr_id: Option<hir::HirId>,
205 negated_expr_span: Option<Span>,
207}
208
209impl_lint_pass!(TypeLimits => [
210 UNUSED_COMPARISONS,
211 OVERFLOWING_LITERALS,
212 INVALID_NAN_COMPARISONS,
213 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
214 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS
215]);
216
217impl TypeLimits {
218 pub(crate) fn new() -> TypeLimits {
219 TypeLimits { negated_expr_id: None, negated_expr_span: None }
220 }
221}
222
223fn lint_nan<'tcx>(
224 cx: &LateContext<'tcx>,
225 e: &'tcx hir::Expr<'tcx>,
226 binop: hir::BinOp,
227 l: &'tcx hir::Expr<'tcx>,
228 r: &'tcx hir::Expr<'tcx>,
229) {
230 fn is_nan(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
231 let expr = expr.peel_blocks().peel_borrows();
232 match expr.kind {
233 ExprKind::Path(qpath) => {
234 let Some(def_id) = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id()
235 else {
236 return false;
237 };
238
239 matches!(
240 cx.tcx.get_diagnostic_name(def_id),
241 Some(sym::f16_nan | sym::f32_nan | sym::f64_nan | sym::f128_nan)
242 )
243 }
244 _ => false,
245 }
246 }
247
248 fn eq_ne(
249 e: &hir::Expr<'_>,
250 l: &hir::Expr<'_>,
251 r: &hir::Expr<'_>,
252 f: impl FnOnce(Span, Span) -> InvalidNanComparisonsSuggestion,
253 ) -> InvalidNanComparisons {
254 let suggestion = if let Some(l_span) = l.span.find_ancestor_inside(e.span)
255 && let Some(r_span) = r.span.find_ancestor_inside(e.span)
256 {
257 f(l_span, r_span)
258 } else {
259 InvalidNanComparisonsSuggestion::Spanless
260 };
261
262 InvalidNanComparisons::EqNe { suggestion }
263 }
264
265 let lint = match binop.node {
266 hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
267 eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
268 nan_plus_binop: l_span.until(r_span),
269 float: r_span.shrink_to_hi(),
270 neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
271 })
272 }
273 hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => {
274 eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
275 nan_plus_binop: l_span.shrink_to_hi().to(r_span),
276 float: l_span.shrink_to_hi(),
277 neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
278 })
279 }
280 hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge
281 if is_nan(cx, l) || is_nan(cx, r) =>
282 {
283 InvalidNanComparisons::LtLeGtGe
284 }
285 _ => return,
286 };
287
288 cx.emit_span_lint(INVALID_NAN_COMPARISONS, e.span, lint);
289}
290
291#[derive(Debug, PartialEq, Copy, Clone)]
292enum ComparisonOp {
293 BinOp(hir::BinOpKind),
294 Other,
295}
296
297fn lint_wide_pointer<'tcx>(
298 cx: &LateContext<'tcx>,
299 e: &'tcx hir::Expr<'tcx>,
300 cmpop: ComparisonOp,
301 l: &'tcx hir::Expr<'tcx>,
302 r: &'tcx hir::Expr<'tcx>,
303) {
304 let ptr_unsized = |mut ty: Ty<'tcx>| -> Option<(
305 usize,
306 String,
307 bool,
308 )> {
309 let mut refs = 0;
310 while let ty::Ref(_, inner_ty, _) = ty.kind() {
313 ty = *inner_ty;
314 refs += 1;
315 }
316
317 let mut modifiers = String::new();
319 ty = match ty.kind() {
320 ty::RawPtr(ty, _) => *ty,
321 ty::Adt(def, args) if cx.tcx.is_diagnostic_item(sym::NonNull, def.did()) => {
322 modifiers.push_str(".as_ptr()");
323 args.type_at(0)
324 }
325 _ => return None,
326 };
327
328 (!ty.is_sized(cx.tcx, cx.typing_env()))
329 .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn))))
330 };
331
332 let l = l.peel_borrows();
334 let r = r.peel_borrows();
335
336 let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else {
337 return;
338 };
339 let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else {
340 return;
341 };
342
343 let Some((l_ty_refs, l_modifiers, l_inner_ty_is_dyn)) = ptr_unsized(l_ty) else {
344 return;
345 };
346 let Some((r_ty_refs, r_modifiers, r_inner_ty_is_dyn)) = ptr_unsized(r_ty) else {
347 return;
348 };
349
350 let (Some(l_span), Some(r_span)) =
351 (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
352 else {
353 return cx.emit_span_lint(
354 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
355 e.span,
356 AmbiguousWidePointerComparisons::Spanless,
357 );
358 };
359
360 let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
361 let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne));
362 let is_dyn_comparison = l_inner_ty_is_dyn && r_inner_ty_is_dyn;
363
364 let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
365 let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
366 let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
367
368 let deref_left = &*"*".repeat(l_ty_refs);
369 let deref_right = &*"*".repeat(r_ty_refs);
370
371 let l_modifiers = &*l_modifiers;
372 let r_modifiers = &*r_modifiers;
373
374 cx.emit_span_lint(
375 AMBIGUOUS_WIDE_POINTER_COMPARISONS,
376 e.span,
377 AmbiguousWidePointerComparisons::Spanful {
378 addr_metadata_suggestion: (is_eq_ne && !is_dyn_comparison).then(|| {
379 AmbiguousWidePointerComparisonsAddrMetadataSuggestion {
380 ne,
381 deref_left,
382 deref_right,
383 l_modifiers,
384 r_modifiers,
385 left,
386 middle,
387 right,
388 }
389 }),
390 addr_suggestion: if is_eq_ne {
391 AmbiguousWidePointerComparisonsAddrSuggestion::AddrEq {
392 ne,
393 deref_left,
394 deref_right,
395 l_modifiers,
396 r_modifiers,
397 left,
398 middle,
399 right,
400 }
401 } else {
402 AmbiguousWidePointerComparisonsAddrSuggestion::Cast {
403 deref_left,
404 deref_right,
405 l_modifiers,
406 r_modifiers,
407 paren_left: if l_ty_refs != 0 { ")" } else { "" },
408 paren_right: if r_ty_refs != 0 { ")" } else { "" },
409 left_before: (l_ty_refs != 0).then_some(l_span.shrink_to_lo()),
410 left_after: l_span.shrink_to_hi(),
411 right_before: (r_ty_refs != 0).then_some(r_span.shrink_to_lo()),
412 right_after: r_span.shrink_to_hi(),
413 }
414 },
415 },
416 );
417}
418
419fn lint_fn_pointer<'tcx>(
420 cx: &LateContext<'tcx>,
421 e: &'tcx hir::Expr<'tcx>,
422 cmpop: ComparisonOp,
423 l: &'tcx hir::Expr<'tcx>,
424 r: &'tcx hir::Expr<'tcx>,
425) {
426 let peel_refs = |mut ty: Ty<'tcx>| -> (Ty<'tcx>, usize) {
427 let mut refs = 0;
428
429 while let ty::Ref(_, inner_ty, _) = ty.kind() {
430 ty = *inner_ty;
431 refs += 1;
432 }
433
434 (ty, refs)
435 };
436
437 let l = l.peel_borrows();
439 let r = r.peel_borrows();
440
441 let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else { return };
442 let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else { return };
443
444 let (l_ty, l_ty_refs) = peel_refs(l_ty);
447 let (r_ty, r_ty_refs) = peel_refs(r_ty);
448
449 if l_ty.is_fn() && r_ty.is_fn() {
450 } else if let ty::Adt(l_def, l_args) = l_ty.kind()
452 && let ty::Adt(r_def, r_args) = r_ty.kind()
453 && cx.tcx.is_lang_item(l_def.did(), LangItem::Option)
454 && cx.tcx.is_lang_item(r_def.did(), LangItem::Option)
455 && let Some(l_some_arg) = l_args.get(0)
456 && let Some(r_some_arg) = r_args.get(0)
457 && l_some_arg.expect_ty().is_fn()
458 && r_some_arg.expect_ty().is_fn()
459 {
460 return cx.emit_span_lint(
462 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
463 e.span,
464 UnpredictableFunctionPointerComparisons::Warn,
465 );
466 } else {
467 return;
469 }
470
471 let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne));
474
475 if !is_eq_ne {
476 return cx.emit_span_lint(
478 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
479 e.span,
480 UnpredictableFunctionPointerComparisons::Warn,
481 );
482 }
483
484 let (Some(l_span), Some(r_span)) =
485 (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
486 else {
487 return cx.emit_span_lint(
489 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
490 e.span,
491 UnpredictableFunctionPointerComparisons::Warn,
492 );
493 };
494
495 let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
496
497 let deref_left = &*"*".repeat(l_ty_refs);
499 let deref_right = &*"*".repeat(r_ty_refs);
500
501 let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
502 let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
503 let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
504
505 let sugg =
506 if !r_ty.is_fn_ptr() {
509 let fn_sig = r_ty.fn_sig(cx.tcx);
510
511 UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEqWithCast {
512 ne,
513 fn_sig,
514 deref_left,
515 deref_right,
516 left,
517 middle,
518 right,
519 }
520 } else {
521 UnpredictableFunctionPointerComparisonsSuggestion::FnAddrEq {
522 ne,
523 deref_left,
524 deref_right,
525 left,
526 middle,
527 right,
528 }
529 };
530
531 cx.emit_span_lint(
532 UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
533 e.span,
534 UnpredictableFunctionPointerComparisons::Suggestion { sugg },
535 );
536}
537
538impl<'tcx> LateLintPass<'tcx> for TypeLimits {
539 fn check_lit(
540 &mut self,
541 cx: &LateContext<'tcx>,
542 hir_id: HirId,
543 lit: &'tcx hir::Lit,
544 negated: bool,
545 ) {
546 if negated {
547 self.negated_expr_id = Some(hir_id);
548 self.negated_expr_span = Some(lit.span);
549 }
550 lint_literal(cx, self, hir_id, lit.span, lit, negated);
551 }
552
553 fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
554 match e.kind {
555 hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
556 if self.negated_expr_id != Some(e.hir_id) {
558 self.negated_expr_id = Some(expr.hir_id);
559 self.negated_expr_span = Some(e.span);
560 }
561 }
562 hir::ExprKind::Binary(binop, ref l, ref r) => {
563 if is_comparison(binop) {
564 if !check_limits(cx, binop, l, r) {
565 cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
566 } else {
567 lint_nan(cx, e, binop, l, r);
568 let cmpop = ComparisonOp::BinOp(binop.node);
569 lint_wide_pointer(cx, e, cmpop, l, r);
570 lint_fn_pointer(cx, e, cmpop, l, r);
571 }
572 }
573 }
574 hir::ExprKind::Call(path, [l, r])
575 if let ExprKind::Path(ref qpath) = path.kind
576 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
577 && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
578 && let Some(cmpop) = diag_item_cmpop(diag_item) =>
579 {
580 lint_wide_pointer(cx, e, cmpop, l, r);
581 lint_fn_pointer(cx, e, cmpop, l, r);
582 }
583 hir::ExprKind::MethodCall(_, l, [r], _)
584 if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
585 && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
586 && let Some(cmpop) = diag_item_cmpop(diag_item) =>
587 {
588 lint_wide_pointer(cx, e, cmpop, l, r);
589 lint_fn_pointer(cx, e, cmpop, l, r);
590 }
591 _ => {}
592 };
593
594 fn is_valid<T: PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
595 match binop.node {
596 hir::BinOpKind::Lt => v > min && v <= max,
597 hir::BinOpKind::Le => v >= min && v < max,
598 hir::BinOpKind::Gt => v >= min && v < max,
599 hir::BinOpKind::Ge => v > min && v <= max,
600 hir::BinOpKind::Eq | hir::BinOpKind::Ne => v >= min && v <= max,
601 _ => bug!(),
602 }
603 }
604
605 fn rev_binop(binop: hir::BinOp) -> hir::BinOp {
606 source_map::respan(
607 binop.span,
608 match binop.node {
609 hir::BinOpKind::Lt => hir::BinOpKind::Gt,
610 hir::BinOpKind::Le => hir::BinOpKind::Ge,
611 hir::BinOpKind::Gt => hir::BinOpKind::Lt,
612 hir::BinOpKind::Ge => hir::BinOpKind::Le,
613 _ => return binop,
614 },
615 )
616 }
617
618 fn check_limits(
619 cx: &LateContext<'_>,
620 binop: hir::BinOp,
621 l: &hir::Expr<'_>,
622 r: &hir::Expr<'_>,
623 ) -> bool {
624 let (lit, expr, swap) = match (&l.kind, &r.kind) {
625 (&hir::ExprKind::Lit(_), _) => (l, r, true),
626 (_, &hir::ExprKind::Lit(_)) => (r, l, false),
627 _ => return true,
628 };
629 let norm_binop = if swap { rev_binop(binop) } else { binop };
632 match *cx.typeck_results().node_type(expr.hir_id).kind() {
633 ty::Int(int_ty) => {
634 let (min, max) = int_ty_range(int_ty);
635 let lit_val: i128 = match lit.kind {
636 hir::ExprKind::Lit(li) => match li.node {
637 ast::LitKind::Int(
638 v,
639 ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed,
640 ) => v.get() as i128,
641 _ => return true,
642 },
643 _ => bug!(),
644 };
645 is_valid(norm_binop, lit_val, min, max)
646 }
647 ty::Uint(uint_ty) => {
648 let (min, max): (u128, u128) = uint_ty_range(uint_ty);
649 let lit_val: u128 = match lit.kind {
650 hir::ExprKind::Lit(li) => match li.node {
651 ast::LitKind::Int(v, _) => v.get(),
652 _ => return true,
653 },
654 _ => bug!(),
655 };
656 is_valid(norm_binop, lit_val, min, max)
657 }
658 _ => true,
659 }
660 }
661
662 fn is_comparison(binop: hir::BinOp) -> bool {
663 matches!(
664 binop.node,
665 hir::BinOpKind::Eq
666 | hir::BinOpKind::Lt
667 | hir::BinOpKind::Le
668 | hir::BinOpKind::Ne
669 | hir::BinOpKind::Ge
670 | hir::BinOpKind::Gt
671 )
672 }
673
674 fn diag_item_cmpop(diag_item: Symbol) -> Option<ComparisonOp> {
675 Some(match diag_item {
676 sym::cmp_ord_max => ComparisonOp::Other,
677 sym::cmp_ord_min => ComparisonOp::Other,
678 sym::ord_cmp_method => ComparisonOp::Other,
679 sym::cmp_partialeq_eq => ComparisonOp::BinOp(hir::BinOpKind::Eq),
680 sym::cmp_partialeq_ne => ComparisonOp::BinOp(hir::BinOpKind::Ne),
681 sym::cmp_partialord_cmp => ComparisonOp::Other,
682 sym::cmp_partialord_ge => ComparisonOp::BinOp(hir::BinOpKind::Ge),
683 sym::cmp_partialord_gt => ComparisonOp::BinOp(hir::BinOpKind::Gt),
684 sym::cmp_partialord_le => ComparisonOp::BinOp(hir::BinOpKind::Le),
685 sym::cmp_partialord_lt => ComparisonOp::BinOp(hir::BinOpKind::Lt),
686 _ => return None,
687 })
688 }
689 }
690}
691
692declare_lint! {
693 IMPROPER_CTYPES,
715 Warn,
716 "proper use of libc types in foreign modules"
717}
718
719declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]);
720
721declare_lint! {
722 IMPROPER_CTYPES_DEFINITIONS,
744 Warn,
745 "proper use of libc types in foreign item definitions"
746}
747
748declare_lint! {
749 USES_POWER_ALIGNMENT,
797 Warn,
798 "Structs do not follow the power alignment rule under repr(C)"
799}
800
801declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS, USES_POWER_ALIGNMENT]);
802
803#[derive(Clone, Copy)]
804pub(crate) enum CItemKind {
805 Declaration,
806 Definition,
807}
808
809struct ImproperCTypesVisitor<'a, 'tcx> {
810 cx: &'a LateContext<'tcx>,
811 mode: CItemKind,
812}
813
814struct CTypesVisitorState<'tcx> {
816 cache: FxHashSet<Ty<'tcx>>,
817 base_ty: Ty<'tcx>,
820}
821
822enum FfiResult<'tcx> {
823 FfiSafe,
824 FfiPhantom(Ty<'tcx>),
825 FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> },
826}
827
828pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
829 tcx: TyCtxt<'tcx>,
830 def: ty::AdtDef<'tcx>,
831) -> bool {
832 tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed)
833}
834
835pub(crate) fn transparent_newtype_field<'a, 'tcx>(
838 tcx: TyCtxt<'tcx>,
839 variant: &'a ty::VariantDef,
840) -> Option<&'a ty::FieldDef> {
841 let typing_env = ty::TypingEnv::non_body_analysis(tcx, variant.def_id);
842 variant.fields.iter().find(|field| {
843 let field_ty = tcx.type_of(field.did).instantiate_identity();
844 let is_1zst =
845 tcx.layout_of(typing_env.as_query_input(field_ty)).is_ok_and(|layout| layout.is_1zst());
846 !is_1zst
847 })
848}
849
850fn ty_is_known_nonnull<'tcx>(
852 tcx: TyCtxt<'tcx>,
853 typing_env: ty::TypingEnv<'tcx>,
854 ty: Ty<'tcx>,
855 mode: CItemKind,
856) -> bool {
857 let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
858
859 match ty.kind() {
860 ty::FnPtr(..) => true,
861 ty::Ref(..) => true,
862 ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true,
863 ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => {
864 let marked_non_null = nonnull_optimization_guaranteed(tcx, *def);
865
866 if marked_non_null {
867 return true;
868 }
869
870 if def.is_unsafe_cell() {
872 return false;
873 }
874
875 def.variants()
876 .iter()
877 .filter_map(|variant| transparent_newtype_field(tcx, variant))
878 .any(|field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args), mode))
879 }
880 ty::Pat(base, pat) => {
881 ty_is_known_nonnull(tcx, typing_env, *base, mode)
882 || Option::unwrap_or_default(
883 try {
884 match **pat {
885 ty::PatternKind::Range { start, end, include_end } => {
886 match (start, end) {
887 (Some(start), None) => {
888 start.try_to_value()?.try_to_bits(tcx, typing_env)? > 0
889 }
890 (Some(start), Some(end)) => {
891 let start =
892 start.try_to_value()?.try_to_bits(tcx, typing_env)?;
893 let end =
894 end.try_to_value()?.try_to_bits(tcx, typing_env)?;
895
896 if include_end {
897 start > 0 && end >= start
900 } else {
901 start > 0 && end > start
902 }
903 }
904 _ => false,
905 }
906 }
907 }
908 },
909 )
910 }
911 _ => false,
912 }
913}
914
915fn get_nullable_type<'tcx>(
918 tcx: TyCtxt<'tcx>,
919 typing_env: ty::TypingEnv<'tcx>,
920 ty: Ty<'tcx>,
921) -> Option<Ty<'tcx>> {
922 let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
923
924 Some(match *ty.kind() {
925 ty::Adt(field_def, field_args) => {
926 let inner_field_ty = {
927 let mut first_non_zst_ty =
928 field_def.variants().iter().filter_map(|v| transparent_newtype_field(tcx, v));
929 debug_assert_eq!(
930 first_non_zst_ty.clone().count(),
931 1,
932 "Wrong number of fields for transparent type"
933 );
934 first_non_zst_ty
935 .next_back()
936 .expect("No non-zst fields in transparent type.")
937 .ty(tcx, field_args)
938 };
939 return get_nullable_type(tcx, typing_env, inner_field_ty);
940 }
941 ty::Pat(base, ..) => return get_nullable_type(tcx, typing_env, base),
942 ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => ty,
943 ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl),
946 ty::FnPtr(..) => ty,
949 ref unhandled => {
952 debug!(
953 "get_nullable_type: Unhandled scalar kind: {:?} while checking {:?}",
954 unhandled, ty
955 );
956 return None;
957 }
958 })
959}
960
961fn is_niche_optimization_candidate<'tcx>(
966 tcx: TyCtxt<'tcx>,
967 typing_env: ty::TypingEnv<'tcx>,
968 ty: Ty<'tcx>,
969) -> bool {
970 if tcx.layout_of(typing_env.as_query_input(ty)).is_ok_and(|layout| !layout.is_1zst()) {
971 return false;
972 }
973
974 match ty.kind() {
975 ty::Adt(ty_def, _) => {
976 let non_exhaustive = ty_def.is_variant_list_non_exhaustive();
977 let empty = (ty_def.is_struct() && ty_def.all_fields().next().is_none())
978 || (ty_def.is_enum() && ty_def.variants().is_empty());
979
980 !non_exhaustive && empty
981 }
982 ty::Tuple(tys) => tys.is_empty(),
983 _ => false,
984 }
985}
986
987pub(crate) fn repr_nullable_ptr<'tcx>(
992 tcx: TyCtxt<'tcx>,
993 typing_env: ty::TypingEnv<'tcx>,
994 ty: Ty<'tcx>,
995 ckind: CItemKind,
996) -> Option<Ty<'tcx>> {
997 debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty);
998 match ty.kind() {
999 ty::Adt(ty_def, args) => {
1000 let field_ty = match &ty_def.variants().raw[..] {
1001 [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
1002 ([], [field]) | ([field], []) => field.ty(tcx, args),
1003 ([field1], [field2]) => {
1004 let ty1 = field1.ty(tcx, args);
1005 let ty2 = field2.ty(tcx, args);
1006
1007 if is_niche_optimization_candidate(tcx, typing_env, ty1) {
1008 ty2
1009 } else if is_niche_optimization_candidate(tcx, typing_env, ty2) {
1010 ty1
1011 } else {
1012 return None;
1013 }
1014 }
1015 _ => return None,
1016 },
1017 _ => return None,
1018 };
1019
1020 if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) {
1021 return None;
1022 }
1023
1024 let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env).ok();
1028 if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) {
1029 bug!("improper_ctypes: Option nonnull optimization not applied?");
1030 }
1031
1032 let field_ty_layout = tcx.layout_of(typing_env.as_query_input(field_ty));
1034 if field_ty_layout.is_err() && !field_ty.has_non_region_param() {
1035 bug!("should be able to compute the layout of non-polymorphic type");
1036 }
1037
1038 let field_ty_abi = &field_ty_layout.ok()?.backend_repr;
1039 if let BackendRepr::Scalar(field_ty_scalar) = field_ty_abi {
1040 match field_ty_scalar.valid_range(&tcx) {
1041 WrappingRange { start: 0, end }
1042 if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 =>
1043 {
1044 return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap());
1045 }
1046 WrappingRange { start: 1, .. } => {
1047 return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap());
1048 }
1049 WrappingRange { start, end } => {
1050 unreachable!("Unhandled start and end range: ({}, {})", start, end)
1051 }
1052 };
1053 }
1054 None
1055 }
1056 ty::Pat(base, pat) => match **pat {
1057 ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, *base),
1058 },
1059 _ => None,
1060 }
1061}
1062
1063impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
1064 fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
1066 if let ty::Array(..) = ty.kind() {
1067 self.emit_ffi_unsafe_type_lint(
1068 ty,
1069 sp,
1070 fluent::lint_improper_ctypes_array_reason,
1071 Some(fluent::lint_improper_ctypes_array_help),
1072 );
1073 true
1074 } else {
1075 false
1076 }
1077 }
1078
1079 fn check_field_type_for_ffi(
1081 &self,
1082 acc: &mut CTypesVisitorState<'tcx>,
1083 field: &ty::FieldDef,
1084 args: GenericArgsRef<'tcx>,
1085 ) -> FfiResult<'tcx> {
1086 let field_ty = field.ty(self.cx.tcx, args);
1087 let field_ty = self
1088 .cx
1089 .tcx
1090 .try_normalize_erasing_regions(self.cx.typing_env(), field_ty)
1091 .unwrap_or(field_ty);
1092 self.check_type_for_ffi(acc, field_ty)
1093 }
1094
1095 fn check_variant_for_ffi(
1097 &self,
1098 acc: &mut CTypesVisitorState<'tcx>,
1099 ty: Ty<'tcx>,
1100 def: ty::AdtDef<'tcx>,
1101 variant: &ty::VariantDef,
1102 args: GenericArgsRef<'tcx>,
1103 ) -> FfiResult<'tcx> {
1104 use FfiResult::*;
1105 let transparent_with_all_zst_fields = if def.repr().transparent() {
1106 if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
1107 match self.check_field_type_for_ffi(acc, field, args) {
1109 FfiUnsafe { ty, .. } if ty.is_unit() => (),
1110 r => return r,
1111 }
1112
1113 false
1114 } else {
1115 true
1118 }
1119 } else {
1120 false
1121 };
1122
1123 let mut all_phantom = !variant.fields.is_empty();
1125 for field in &variant.fields {
1126 all_phantom &= match self.check_field_type_for_ffi(acc, field, args) {
1127 FfiSafe => false,
1128 FfiUnsafe { ty, .. } if ty.is_unit() => false,
1130 FfiPhantom(..) => true,
1131 r @ FfiUnsafe { .. } => return r,
1132 }
1133 }
1134
1135 if all_phantom {
1136 FfiPhantom(ty)
1137 } else if transparent_with_all_zst_fields {
1138 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
1139 } else {
1140 FfiSafe
1141 }
1142 }
1143
1144 fn check_type_for_ffi(
1147 &self,
1148 acc: &mut CTypesVisitorState<'tcx>,
1149 ty: Ty<'tcx>,
1150 ) -> FfiResult<'tcx> {
1151 use FfiResult::*;
1152
1153 let tcx = self.cx.tcx;
1154
1155 if !acc.cache.insert(ty) {
1160 return FfiSafe;
1161 }
1162
1163 match *ty.kind() {
1164 ty::Adt(def, args) => {
1165 if let Some(boxed) = ty.boxed_ty()
1166 && matches!(self.mode, CItemKind::Definition)
1167 {
1168 if boxed.is_sized(tcx, self.cx.typing_env()) {
1169 return FfiSafe;
1170 } else {
1171 return FfiUnsafe {
1172 ty,
1173 reason: fluent::lint_improper_ctypes_box,
1174 help: None,
1175 };
1176 }
1177 }
1178 if def.is_phantom_data() {
1179 return FfiPhantom(ty);
1180 }
1181 match def.adt_kind() {
1182 AdtKind::Struct | AdtKind::Union => {
1183 if let Some(sym::cstring_type | sym::cstr_type) =
1184 tcx.get_diagnostic_name(def.did())
1185 && !acc.base_ty.is_mutable_ptr()
1186 {
1187 return FfiUnsafe {
1188 ty,
1189 reason: fluent::lint_improper_ctypes_cstr_reason,
1190 help: Some(fluent::lint_improper_ctypes_cstr_help),
1191 };
1192 }
1193
1194 if !def.repr().c() && !def.repr().transparent() {
1195 return FfiUnsafe {
1196 ty,
1197 reason: if def.is_struct() {
1198 fluent::lint_improper_ctypes_struct_layout_reason
1199 } else {
1200 fluent::lint_improper_ctypes_union_layout_reason
1201 },
1202 help: if def.is_struct() {
1203 Some(fluent::lint_improper_ctypes_struct_layout_help)
1204 } else {
1205 Some(fluent::lint_improper_ctypes_union_layout_help)
1206 },
1207 };
1208 }
1209
1210 let is_non_exhaustive =
1211 def.non_enum_variant().is_field_list_non_exhaustive();
1212 if is_non_exhaustive && !def.did().is_local() {
1213 return FfiUnsafe {
1214 ty,
1215 reason: if def.is_struct() {
1216 fluent::lint_improper_ctypes_struct_non_exhaustive
1217 } else {
1218 fluent::lint_improper_ctypes_union_non_exhaustive
1219 },
1220 help: None,
1221 };
1222 }
1223
1224 if def.non_enum_variant().fields.is_empty() {
1225 return FfiUnsafe {
1226 ty,
1227 reason: if def.is_struct() {
1228 fluent::lint_improper_ctypes_struct_fieldless_reason
1229 } else {
1230 fluent::lint_improper_ctypes_union_fieldless_reason
1231 },
1232 help: if def.is_struct() {
1233 Some(fluent::lint_improper_ctypes_struct_fieldless_help)
1234 } else {
1235 Some(fluent::lint_improper_ctypes_union_fieldless_help)
1236 },
1237 };
1238 }
1239
1240 self.check_variant_for_ffi(acc, ty, def, def.non_enum_variant(), args)
1241 }
1242 AdtKind::Enum => {
1243 if def.variants().is_empty() {
1244 return FfiSafe;
1246 }
1247 if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
1250 {
1251 if let Some(ty) =
1253 repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty, self.mode)
1254 {
1255 return self.check_type_for_ffi(acc, ty);
1256 }
1257
1258 return FfiUnsafe {
1259 ty,
1260 reason: fluent::lint_improper_ctypes_enum_repr_reason,
1261 help: Some(fluent::lint_improper_ctypes_enum_repr_help),
1262 };
1263 }
1264
1265 use improper_ctypes::{
1266 check_non_exhaustive_variant, non_local_and_non_exhaustive,
1267 };
1268
1269 let non_local_def = non_local_and_non_exhaustive(def);
1270 let ret = def.variants().iter().try_for_each(|variant| {
1272 check_non_exhaustive_variant(non_local_def, variant)
1273 .map_break(|reason| FfiUnsafe { ty, reason, help: None })?;
1274
1275 match self.check_variant_for_ffi(acc, ty, def, variant, args) {
1276 FfiSafe => ControlFlow::Continue(()),
1277 r => ControlFlow::Break(r),
1278 }
1279 });
1280 if let ControlFlow::Break(result) = ret {
1281 return result;
1282 }
1283
1284 FfiSafe
1285 }
1286 }
1287 }
1288
1289 ty::Char => FfiUnsafe {
1290 ty,
1291 reason: fluent::lint_improper_ctypes_char_reason,
1292 help: Some(fluent::lint_improper_ctypes_char_help),
1293 },
1294
1295 ty::Pat(base, ..) => self.check_type_for_ffi(acc, base),
1298
1299 ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => {
1300 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None }
1301 }
1302
1303 ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
1305
1306 ty::Slice(_) => FfiUnsafe {
1307 ty,
1308 reason: fluent::lint_improper_ctypes_slice_reason,
1309 help: Some(fluent::lint_improper_ctypes_slice_help),
1310 },
1311
1312 ty::Dynamic(..) => {
1313 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None }
1314 }
1315
1316 ty::Str => FfiUnsafe {
1317 ty,
1318 reason: fluent::lint_improper_ctypes_str_reason,
1319 help: Some(fluent::lint_improper_ctypes_str_help),
1320 },
1321
1322 ty::Tuple(..) => FfiUnsafe {
1323 ty,
1324 reason: fluent::lint_improper_ctypes_tuple_reason,
1325 help: Some(fluent::lint_improper_ctypes_tuple_help),
1326 },
1327
1328 ty::RawPtr(ty, _) | ty::Ref(_, ty, _)
1329 if {
1330 matches!(self.mode, CItemKind::Definition)
1331 && ty.is_sized(self.cx.tcx, self.cx.typing_env())
1332 } =>
1333 {
1334 FfiSafe
1335 }
1336
1337 ty::RawPtr(ty, _)
1338 if match ty.kind() {
1339 ty::Tuple(tuple) => tuple.is_empty(),
1340 _ => false,
1341 } =>
1342 {
1343 FfiSafe
1344 }
1345
1346 ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty),
1347
1348 ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty),
1349
1350 ty::FnPtr(sig_tys, hdr) => {
1351 let sig = sig_tys.with(hdr);
1352 if self.is_internal_abi(sig.abi()) {
1353 return FfiUnsafe {
1354 ty,
1355 reason: fluent::lint_improper_ctypes_fnptr_reason,
1356 help: Some(fluent::lint_improper_ctypes_fnptr_help),
1357 };
1358 }
1359
1360 let sig = tcx.instantiate_bound_regions_with_erased(sig);
1361 for arg in sig.inputs() {
1362 match self.check_type_for_ffi(acc, *arg) {
1363 FfiSafe => {}
1364 r => return r,
1365 }
1366 }
1367
1368 let ret_ty = sig.output();
1369 if ret_ty.is_unit() {
1370 return FfiSafe;
1371 }
1372
1373 self.check_type_for_ffi(acc, ret_ty)
1374 }
1375
1376 ty::Foreign(..) => FfiSafe,
1377
1378 ty::Alias(ty::Opaque, ..) => {
1381 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None }
1382 }
1383
1384 ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
1387 if matches!(self.mode, CItemKind::Definition) =>
1388 {
1389 FfiSafe
1390 }
1391
1392 ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
1393
1394 ty::Param(..)
1395 | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
1396 | ty::Infer(..)
1397 | ty::Bound(..)
1398 | ty::Error(_)
1399 | ty::Closure(..)
1400 | ty::CoroutineClosure(..)
1401 | ty::Coroutine(..)
1402 | ty::CoroutineWitness(..)
1403 | ty::Placeholder(..)
1404 | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
1405 }
1406 }
1407
1408 fn emit_ffi_unsafe_type_lint(
1409 &mut self,
1410 ty: Ty<'tcx>,
1411 sp: Span,
1412 note: DiagMessage,
1413 help: Option<DiagMessage>,
1414 ) {
1415 let lint = match self.mode {
1416 CItemKind::Declaration => IMPROPER_CTYPES,
1417 CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
1418 };
1419 let desc = match self.mode {
1420 CItemKind::Declaration => "block",
1421 CItemKind::Definition => "fn",
1422 };
1423 let span_note = if let ty::Adt(def, _) = ty.kind()
1424 && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did())
1425 {
1426 Some(sp)
1427 } else {
1428 None
1429 };
1430 self.cx.emit_span_lint(
1431 lint,
1432 sp,
1433 ImproperCTypes { ty, desc, label: sp, help, note, span_note },
1434 );
1435 }
1436
1437 fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
1438 struct ProhibitOpaqueTypes;
1439 impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
1440 type Result = ControlFlow<Ty<'tcx>>;
1441
1442 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
1443 if !ty.has_opaque_types() {
1444 return ControlFlow::Continue(());
1445 }
1446
1447 if let ty::Alias(ty::Opaque, ..) = ty.kind() {
1448 ControlFlow::Break(ty)
1449 } else {
1450 ty.super_visit_with(self)
1451 }
1452 }
1453 }
1454
1455 if let Some(ty) = self
1456 .cx
1457 .tcx
1458 .try_normalize_erasing_regions(self.cx.typing_env(), ty)
1459 .unwrap_or(ty)
1460 .visit_with(&mut ProhibitOpaqueTypes)
1461 .break_value()
1462 {
1463 self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None);
1464 true
1465 } else {
1466 false
1467 }
1468 }
1469
1470 fn check_type_for_ffi_and_report_errors(
1471 &mut self,
1472 sp: Span,
1473 ty: Ty<'tcx>,
1474 is_static: bool,
1475 is_return_type: bool,
1476 ) {
1477 if self.check_for_opaque_ty(sp, ty) {
1478 return;
1480 }
1481
1482 let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty);
1483
1484 if !is_static && self.check_for_array_ty(sp, ty) {
1488 return;
1489 }
1490
1491 if is_return_type && ty.is_unit() {
1495 return;
1496 }
1497
1498 let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty };
1499 match self.check_type_for_ffi(&mut acc, ty) {
1500 FfiResult::FfiSafe => {}
1501 FfiResult::FfiPhantom(ty) => {
1502 self.emit_ffi_unsafe_type_lint(
1503 ty,
1504 sp,
1505 fluent::lint_improper_ctypes_only_phantomdata,
1506 None,
1507 );
1508 }
1509 FfiResult::FfiUnsafe { ty, reason, help } => {
1510 self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
1511 }
1512 }
1513 }
1514
1515 fn check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
1520 let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity();
1521 let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig);
1522
1523 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1524 for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) {
1525 self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, false);
1526 }
1527 }
1528
1529 if let hir::FnRetTy::Return(ret_hir) = decl.output {
1530 for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(ret_hir, sig.output()) {
1531 self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, true);
1532 }
1533 }
1534 }
1535
1536 fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
1538 let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity();
1539 let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig);
1540
1541 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1542 self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false);
1543 }
1544
1545 if let hir::FnRetTy::Return(ret_hir) = decl.output {
1546 self.check_type_for_ffi_and_report_errors(ret_hir.span, sig.output(), false, true);
1547 }
1548 }
1549
1550 fn check_foreign_static(&mut self, id: hir::OwnerId, span: Span) {
1551 let ty = self.cx.tcx.type_of(id).instantiate_identity();
1552 self.check_type_for_ffi_and_report_errors(span, ty, true, false);
1553 }
1554
1555 fn is_internal_abi(&self, abi: ExternAbi) -> bool {
1556 matches!(
1557 abi,
1558 ExternAbi::Rust | ExternAbi::RustCall | ExternAbi::RustCold | ExternAbi::RustIntrinsic
1559 )
1560 }
1561
1562 fn find_fn_ptr_ty_with_external_abi(
1566 &self,
1567 hir_ty: &hir::Ty<'tcx>,
1568 ty: Ty<'tcx>,
1569 ) -> Vec<(Ty<'tcx>, Span)> {
1570 struct FnPtrFinder<'a, 'b, 'tcx> {
1571 visitor: &'a ImproperCTypesVisitor<'b, 'tcx>,
1572 spans: Vec<Span>,
1573 tys: Vec<Ty<'tcx>>,
1574 }
1575
1576 impl<'a, 'b, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'a, 'b, 'tcx> {
1577 fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) {
1578 debug!(?ty);
1579 if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind
1580 && !self.visitor.is_internal_abi(*abi)
1581 {
1582 self.spans.push(ty.span);
1583 }
1584
1585 hir::intravisit::walk_ty(self, ty)
1586 }
1587 }
1588
1589 impl<'a, 'b, 'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'a, 'b, 'tcx> {
1590 type Result = ControlFlow<Ty<'tcx>>;
1591
1592 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
1593 if let ty::FnPtr(_, hdr) = ty.kind()
1594 && !self.visitor.is_internal_abi(hdr.abi)
1595 {
1596 self.tys.push(ty);
1597 }
1598
1599 ty.super_visit_with(self)
1600 }
1601 }
1602
1603 let mut visitor = FnPtrFinder { visitor: self, spans: Vec::new(), tys: Vec::new() };
1604 ty.visit_with(&mut visitor);
1605 visitor.visit_ty_unambig(hir_ty);
1606
1607 iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect()
1608 }
1609}
1610
1611impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
1612 fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
1613 let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration };
1614 let abi = cx.tcx.hir().get_foreign_abi(it.hir_id());
1615
1616 match it.kind {
1617 hir::ForeignItemKind::Fn(sig, _, _) => {
1618 if vis.is_internal_abi(abi) {
1619 vis.check_fn(it.owner_id.def_id, sig.decl)
1620 } else {
1621 vis.check_foreign_fn(it.owner_id.def_id, sig.decl);
1622 }
1623 }
1624 hir::ForeignItemKind::Static(ty, _, _) if !vis.is_internal_abi(abi) => {
1625 vis.check_foreign_static(it.owner_id, ty.span);
1626 }
1627 hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
1628 }
1629 }
1630}
1631
1632impl ImproperCTypesDefinitions {
1633 fn check_ty_maybe_containing_foreign_fnptr<'tcx>(
1634 &mut self,
1635 cx: &LateContext<'tcx>,
1636 hir_ty: &'tcx hir::Ty<'_>,
1637 ty: Ty<'tcx>,
1638 ) {
1639 let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1640 for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) {
1641 vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false);
1642 }
1643 }
1644
1645 fn check_arg_for_power_alignment<'tcx>(
1646 &mut self,
1647 cx: &LateContext<'tcx>,
1648 ty: Ty<'tcx>,
1649 ) -> bool {
1650 if cx.tcx.sess.target.os != "aix" {
1657 return false;
1658 }
1659 if ty.is_floating_point() && ty.primitive_size(cx.tcx).bytes() > 4 {
1660 return true;
1661 } else if let Adt(adt_def, _) = ty.kind()
1662 && adt_def.is_struct()
1663 {
1664 let struct_variant = adt_def.variant(VariantIdx::ZERO);
1665 for struct_field in &struct_variant.fields {
1669 let field_ty = cx.tcx.type_of(struct_field.did).instantiate_identity();
1670 if self.check_arg_for_power_alignment(cx, field_ty) {
1671 return true;
1672 }
1673 }
1674 }
1675 return false;
1676 }
1677
1678 fn check_struct_for_power_alignment<'tcx>(
1679 &mut self,
1680 cx: &LateContext<'tcx>,
1681 item: &'tcx hir::Item<'tcx>,
1682 ) {
1683 let adt_def = cx.tcx.adt_def(item.owner_id.to_def_id());
1684 if adt_def.repr().c()
1685 && !adt_def.repr().packed()
1686 && cx.tcx.sess.target.os == "aix"
1687 && !adt_def.all_fields().next().is_none()
1688 {
1689 let struct_variant_data = item.expect_struct().0;
1690 for (index, ..) in struct_variant_data.fields().iter().enumerate() {
1691 if index != 0 {
1695 let first_field_def = struct_variant_data.fields()[index];
1696 let def_id = first_field_def.def_id;
1697 let ty = cx.tcx.type_of(def_id).instantiate_identity();
1698 if self.check_arg_for_power_alignment(cx, ty) {
1699 cx.emit_span_lint(
1700 USES_POWER_ALIGNMENT,
1701 first_field_def.span,
1702 UsesPowerAlignment,
1703 );
1704 }
1705 }
1706 }
1707 }
1708 }
1709}
1710
1711impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
1719 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
1720 match item.kind {
1721 hir::ItemKind::Static(ty, ..)
1722 | hir::ItemKind::Const(ty, ..)
1723 | hir::ItemKind::TyAlias(ty, ..) => {
1724 self.check_ty_maybe_containing_foreign_fnptr(
1725 cx,
1726 ty,
1727 cx.tcx.type_of(item.owner_id).instantiate_identity(),
1728 );
1729 }
1730 hir::ItemKind::Fn { .. } => {}
1732 hir::ItemKind::Struct(..) => {
1735 self.check_struct_for_power_alignment(cx, item);
1736 }
1737 hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {}
1739 hir::ItemKind::Impl(..)
1741 | hir::ItemKind::TraitAlias(..)
1742 | hir::ItemKind::Trait(..)
1743 | hir::ItemKind::GlobalAsm(..)
1744 | hir::ItemKind::ForeignMod { .. }
1745 | hir::ItemKind::Mod(..)
1746 | hir::ItemKind::Macro(..)
1747 | hir::ItemKind::Use(..)
1748 | hir::ItemKind::ExternCrate(..) => {}
1749 }
1750 }
1751
1752 fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
1753 self.check_ty_maybe_containing_foreign_fnptr(
1754 cx,
1755 field.ty,
1756 cx.tcx.type_of(field.def_id).instantiate_identity(),
1757 );
1758 }
1759
1760 fn check_fn(
1761 &mut self,
1762 cx: &LateContext<'tcx>,
1763 kind: hir::intravisit::FnKind<'tcx>,
1764 decl: &'tcx hir::FnDecl<'_>,
1765 _: &'tcx hir::Body<'_>,
1766 _: Span,
1767 id: LocalDefId,
1768 ) {
1769 use hir::intravisit::FnKind;
1770
1771 let abi = match kind {
1772 FnKind::ItemFn(_, _, header, ..) => header.abi,
1773 FnKind::Method(_, sig, ..) => sig.header.abi,
1774 _ => return,
1775 };
1776
1777 let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1778 if vis.is_internal_abi(abi) {
1779 vis.check_fn(id, decl);
1780 } else {
1781 vis.check_foreign_fn(id, decl);
1782 }
1783 }
1784}
1785
1786declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]);
1787
1788impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
1789 fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
1790 if let hir::ItemKind::Enum(ref enum_definition, _) = it.kind {
1791 let t = cx.tcx.type_of(it.owner_id).instantiate_identity();
1792 let ty = cx.tcx.erase_regions(t);
1793 let Ok(layout) = cx.layout_of(ty) else { return };
1794 let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, ref variants, .. } =
1795 &layout.variants
1796 else {
1797 return;
1798 };
1799
1800 let tag_size = tag.size(&cx.tcx).bytes();
1801
1802 debug!(
1803 "enum `{}` is {} bytes large with layout:\n{:#?}",
1804 t,
1805 layout.size.bytes(),
1806 layout
1807 );
1808
1809 let (largest, slargest, largest_index) = iter::zip(enum_definition.variants, variants)
1810 .map(|(variant, variant_layout)| {
1811 let bytes = variant_layout.size.bytes().saturating_sub(tag_size);
1813
1814 debug!("- variant `{}` is {} bytes large", variant.ident, bytes);
1815 bytes
1816 })
1817 .enumerate()
1818 .fold((0, 0, 0), |(l, s, li), (idx, size)| {
1819 if size > l {
1820 (size, l, idx)
1821 } else if size > s {
1822 (l, size, li)
1823 } else {
1824 (l, s, li)
1825 }
1826 });
1827
1828 if largest > slargest * 3 && slargest > 0 {
1831 cx.emit_span_lint(
1832 VARIANT_SIZE_DIFFERENCES,
1833 enum_definition.variants[largest_index].span,
1834 VariantSizeDifferencesDiag { largest },
1835 );
1836 }
1837 }
1838 }
1839}
1840
1841declare_lint! {
1842 INVALID_ATOMIC_ORDERING,
1879 Deny,
1880 "usage of invalid atomic ordering in atomic operations and memory fences"
1881}
1882
1883declare_lint_pass!(InvalidAtomicOrdering => [INVALID_ATOMIC_ORDERING]);
1884
1885impl InvalidAtomicOrdering {
1886 fn inherent_atomic_method_call<'hir>(
1887 cx: &LateContext<'_>,
1888 expr: &Expr<'hir>,
1889 recognized_names: &[Symbol], ) -> Option<(Symbol, &'hir [Expr<'hir>])> {
1891 const ATOMIC_TYPES: &[Symbol] = &[
1892 sym::AtomicBool,
1893 sym::AtomicPtr,
1894 sym::AtomicUsize,
1895 sym::AtomicU8,
1896 sym::AtomicU16,
1897 sym::AtomicU32,
1898 sym::AtomicU64,
1899 sym::AtomicU128,
1900 sym::AtomicIsize,
1901 sym::AtomicI8,
1902 sym::AtomicI16,
1903 sym::AtomicI32,
1904 sym::AtomicI64,
1905 sym::AtomicI128,
1906 ];
1907 if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind
1908 && recognized_names.contains(&method_path.ident.name)
1909 && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
1910 && let Some(impl_did) = cx.tcx.impl_of_method(m_def_id)
1911 && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
1912 && cx.tcx.trait_id_of_impl(impl_did).is_none()
1914 && let parent = cx.tcx.parent(adt.did())
1915 && cx.tcx.is_diagnostic_item(sym::atomic_mod, parent)
1916 && ATOMIC_TYPES.contains(&cx.tcx.item_name(adt.did()))
1917 {
1918 return Some((method_path.ident.name, args));
1919 }
1920 None
1921 }
1922
1923 fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> {
1924 let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None };
1925 let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?;
1926 let tcx = cx.tcx;
1927 let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
1928 let name = tcx.item_name(did);
1929 let parent = tcx.parent(did);
1930 [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find(
1931 |&ordering| {
1932 name == ordering
1933 && (Some(parent) == atomic_ordering
1934 || tcx.opt_parent(parent) == atomic_ordering)
1936 },
1937 )
1938 }
1939
1940 fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
1941 if let Some((method, args)) =
1942 Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
1943 && let Some((ordering_arg, invalid_ordering)) = match method {
1944 sym::load => Some((&args[0], sym::Release)),
1945 sym::store => Some((&args[1], sym::Acquire)),
1946 _ => None,
1947 }
1948 && let Some(ordering) = Self::match_ordering(cx, ordering_arg)
1949 && (ordering == invalid_ordering || ordering == sym::AcqRel)
1950 {
1951 if method == sym::load {
1952 cx.emit_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad);
1953 } else {
1954 cx.emit_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore);
1955 };
1956 }
1957 }
1958
1959 fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
1960 if let ExprKind::Call(func, args) = expr.kind
1961 && let ExprKind::Path(ref func_qpath) = func.kind
1962 && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
1963 && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
1964 && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
1965 {
1966 cx.emit_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence);
1967 }
1968 }
1969
1970 fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
1971 let Some((method, args)) = Self::inherent_atomic_method_call(
1972 cx,
1973 expr,
1974 &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak],
1975 ) else {
1976 return;
1977 };
1978
1979 let fail_order_arg = match method {
1980 sym::fetch_update => &args[1],
1981 sym::compare_exchange | sym::compare_exchange_weak => &args[3],
1982 _ => return,
1983 };
1984
1985 let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
1986
1987 if matches!(fail_ordering, sym::Release | sym::AcqRel) {
1988 cx.emit_span_lint(
1989 INVALID_ATOMIC_ORDERING,
1990 fail_order_arg.span,
1991 InvalidAtomicOrderingDiag { method, fail_order_arg_span: fail_order_arg.span },
1992 );
1993 }
1994 }
1995}
1996
1997impl<'tcx> LateLintPass<'tcx> for InvalidAtomicOrdering {
1998 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1999 Self::check_atomic_load_store(cx, expr);
2000 Self::check_memory_fence(cx, expr);
2001 Self::check_atomic_compare_exchange(cx, expr);
2002 }
2003}