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