rustc_mir_transform/
check_inline_always_target_features.rs1use rustc_hir::attrs::InlineAttr;
2use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind};
3use rustc_middle::mir::{Body, TerminatorKind};
4use rustc_middle::ty::{self, TyCtxt};
5
6use crate::pass_manager::MirLint;
7
8pub(super) struct CheckInlineAlwaysTargetFeature;
9
10impl<'tcx> MirLint<'tcx> for CheckInlineAlwaysTargetFeature {
11 fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
12 check_inline_always_target_features(tcx, body)
13 }
14}
15
16#[inline]
25fn check_inline_always_target_features<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
26 let caller_def_id = body.source.def_id().expect_local();
27 if !tcx.def_kind(caller_def_id).has_codegen_attrs() {
28 return;
29 }
30
31 let caller_codegen_fn_attrs = tcx.codegen_fn_attrs(caller_def_id);
32
33 for bb in body.basic_blocks.iter() {
34 let terminator = bb.terminator();
35 match &terminator.kind {
36 TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => {
37 let fn_ty = func.ty(body, tcx);
38 let ty::FnDef(callee_def_id, _) = *fn_ty.kind() else {
39 continue;
40 };
41
42 if !tcx.def_kind(callee_def_id).has_codegen_attrs() {
43 continue;
44 }
45 let callee_codegen_fn_attrs = tcx.codegen_fn_attrs(callee_def_id);
46 if callee_codegen_fn_attrs.inline != InlineAttr::Always
47 || callee_codegen_fn_attrs.target_features.is_empty()
48 {
49 continue;
50 }
51
52 if tcx.is_target_feature_call_safe(
55 &callee_codegen_fn_attrs.target_features,
56 &caller_codegen_fn_attrs
57 .target_features
58 .iter()
59 .cloned()
60 .chain(tcx.sess.target_features.iter().map(|feat| TargetFeature {
61 name: *feat,
62 kind: TargetFeatureKind::Implied,
63 }))
64 .collect::<Vec<_>>(),
65 ) {
66 continue;
67 }
68
69 let callee_only: Vec<_> = callee_codegen_fn_attrs
70 .target_features
71 .iter()
72 .filter(|it| !caller_codegen_fn_attrs.target_features.contains(it))
73 .filter(|it| !matches!(it.kind, TargetFeatureKind::Implied))
74 .map(|it| it.name.as_str())
75 .collect();
76
77 crate::errors::emit_inline_always_target_feature_diagnostic(
78 tcx,
79 terminator.source_info.span,
80 callee_def_id,
81 caller_def_id.into(),
82 &callee_only,
83 );
84 }
85 _ => (),
86 }
87 }
88}