1use std::ops::ControlFlow;
12
13use rustc_hir::attrs::LangItem;
14use rustc_hir::def_id::{DefId, LocalDefId};
15use rustc_hir::{HirId, OwnerId};
16use rustc_middle::thir::visit::Visitor as _;
17use rustc_middle::thir::{self, Thir};
18use rustc_middle::ty::adjustment::PointerCoercion;
19use rustc_middle::ty::{
20 self, Binder, ExistentialTraitRef, GenericArgs, Instance, Ty, TyCtxt, TypeSuperVisitable as _,
21 TypeVisitable as _, TypeVisitor, TypingEnv,
22};
23use rustc_span::Span;
24use tracing::{debug, info};
25
26use crate::ferrocene::{InstantiateResult, LintState, UnvalidatedImplCause, Use, UseKind};
27
28pub(super) struct LintThir<'thir, 'tcx> {
29 thir: &'thir Thir<'tcx>,
30 linter: LintState<'tcx>,
31 owner: OwnerId,
33}
34
35impl<'thir, 'tcx: 'thir> thir::visit::Visitor<'thir, 'tcx> for LintThir<'thir, 'tcx> {
36 fn thir(&self) -> &'thir Thir<'tcx> {
37 self.thir
38 }
39
40 fn visit_expr(&mut self, expr: &'thir thir::Expr<'tcx>) {
41 let use_ = match self.find_unvalidated_use(expr) {
42 None => return,
43 Some(Use {
46 kind: UseKind::TraitObjectCast(UnvalidatedImplCause::UnresolvedGenericImpl(..), _),
47 ..
48 }) => return,
49 Some(use_) => use_,
50 };
51 let hir_id = HirId { owner: self.owner, local_id: expr.temp_scope_id };
52 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/ferrocene/thir.rs:52",
"rustc_lint::ferrocene::thir", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/ferrocene/thir.rs"),
::tracing_core::__macro_support::Option::Some(52u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::ferrocene::thir"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("id={1:?}, kind={0:?}",
expr.kind, hir_id) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("id={hir_id:?}, kind={:?}", expr.kind);
53 self.linter.check_use(hir_id, use_);
54 }
55}
56
57impl<'thir, 'tcx: 'thir> LintThir<'thir, 'tcx> {
58 pub(super) fn check_item(tcx: TyCtxt<'tcx>, owner: OwnerId, item: LocalDefId) -> Option<()> {
63 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/ferrocene/thir.rs:63",
"rustc_lint::ferrocene::thir", ::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/ferrocene/thir.rs"),
::tracing_core::__macro_support::Option::Some(63u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::ferrocene::thir"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("checking {0:?}",
item) as &dyn ::tracing::field::Value))])
});
} else { ; }
};tracing::trace!("checking {item:?}");
64
65 if tcx.sess.opts.test
66 && tcx.entry_fn(()).and_then(|(id, _)| id.as_local()) == Some(owner.def_id)
67 {
68 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/ferrocene/thir.rs:69",
"rustc_lint::ferrocene::thir", ::tracing::Level::INFO,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/ferrocene/thir.rs"),
::tracing_core::__macro_support::Option::Some(69u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::ferrocene::thir"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("treating libtest main function as unvalidated")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};info!("treating libtest main function as unvalidated");
70 return None;
71 }
72
73 let linter = LintState::new(tcx, item)?;
74 let body = tcx.thir_body(item).ok();
76 if body.is_none() {
77 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/ferrocene/thir.rs:77",
"rustc_lint::ferrocene::thir", ::tracing::Level::INFO,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/ferrocene/thir.rs"),
::tracing_core::__macro_support::Option::Some(77u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::ferrocene::thir"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("skipping item {0:?} without body",
item) as &dyn ::tracing::field::Value))])
});
} else { ; }
};info!("skipping item {item:?} without body");
78 }
79 let thir = &body?.0.borrow();
80 let mut visitor = LintThir { linter, thir, owner };
81 for expr in &*thir.exprs {
82 visitor.visit_expr(expr);
83 }
84
85 Some(())
86 }
87
88 fn find_unvalidated_use(&mut self, expr: &thir::Expr<'tcx>) -> Option<Use<'tcx>> {
89 let tcx = self.linter.tcx;
90 let mut span = expr.span;
91
92 let mut try_instantiate = |def_id, args| self.try_instantiate(def_id, args, span);
93
94 let use_kind = match expr.kind {
95 thir::ExprKind::NamedConst { def_id, .. }
96 | thir::ExprKind::StaticRef { def_id, .. } => {
97 let unknown_fn = contains_unknown_fn(expr.ty)?;
103 UseKind::ContainsFnPtr(def_id, unknown_fn)
107 }
108 thir::ExprKind::Call { ty, .. } => {
109 let instance = self.instance_of_ty_ignoring_validated(ty, expr.span)?;
110 span = tcx.sess.source_map().span_until_char(expr.span, '(');
113
114 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/ferrocene/thir.rs:114",
"rustc_lint::ferrocene::thir", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/ferrocene/thir.rs"),
::tracing_core::__macro_support::Option::Some(114u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::ferrocene::thir"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("saw call to {0:?}",
instance) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("saw call to {instance:?}");
115 UseKind::Called(instance)
116 }
117 thir::ExprKind::Closure(ref expr) => {
121 LintThir::check_item(tcx, self.owner, expr.closure_id);
124 return None;
125 }
126 thir::ExprKind::PointerCoercion {
127 cast: PointerCoercion::ReifyFnPointer(_),
129 source,
130 ..
131 } => {
132 let source_ty = self.thir[source].ty;
133 let fn_ptr_trait = tcx.require_lang_item(LangItem::FnPtrTrait, expr.span);
134 let trait_ref = Binder::dummy(ExistentialTraitRef::new_from_args(
135 tcx,
136 fn_ptr_trait,
137 GenericArgs::empty(),
138 ));
139 self.linter.check_fn_ptr_coercion(
140 source_ty,
141 trait_ref.with_self_ty(tcx, source_ty),
142 &mut try_instantiate,
143 expr.span,
144 )?
145 }
146 thir::ExprKind::PointerCoercion { cast: PointerCoercion::Unsize, source, .. } => {
147 let source_ty = self.thir[source].ty;
148 self.linter.check_dyn_trait_coercion(
149 expr.ty,
150 source_ty,
151 self.typing_env(),
152 &mut try_instantiate,
153 span,
154 )?
155 }
156 _ => return None,
158 };
159
160 Some(Use { kind: use_kind, span, from_instantiation: None })
161 }
162
163 fn instance_of_ty_ignoring_validated(
164 &self,
165 ty: Ty<'tcx>,
166 span: Span,
167 ) -> Option<Instance<'tcx>> {
168 let mut try_instantiate = |def_id, args| self.try_instantiate(def_id, args, span);
169
170 self.linter.instance_of_ty(ty, None, &mut try_instantiate, span).filter(|instance| {
171 if #[allow(non_exhaustive_omitted_patterns)] match instance.def {
ty::InstanceKind::Virtual(..) => true,
_ => false,
}matches!(instance.def, ty::InstanceKind::Virtual(..)) {
174 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/ferrocene/thir.rs:174",
"rustc_lint::ferrocene::thir", ::tracing::Level::INFO,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/ferrocene/thir.rs"),
::tracing_core::__macro_support::Option::Some(174u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::ferrocene::thir"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::INFO <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("skipping dyn assoc item {0:?}",
instance) as &dyn ::tracing::field::Value))])
});
} else { ; }
};info!("skipping dyn assoc item {instance:?}");
175 false
176 } else {
177 true
178 }
179 })
180 }
181
182 fn try_instantiate(
183 &self,
184 def_id: DefId,
185 args: &'tcx GenericArgs<'tcx>,
186 span: Span,
187 ) -> InstantiateResult<'tcx> {
188 let tcx = self.linter.tcx;
189 match Instance::try_resolve(tcx, self.typing_env(), def_id, args) {
190 Err(_) => {
191 tcx.dcx().span_delayed_bug(
194 span,
195 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("could not resolve instance ({0:?}, {1:?})",
def_id, args))
})format!("could not resolve instance ({def_id:?}, {args:?})"),
196 );
197 InstantiateResult::Err
198 }
199 Ok(None) => InstantiateResult::Indeterminate,
200 Ok(Some(instance)) => InstantiateResult::Resolved(instance),
201 }
202 }
203
204 fn typing_env(&self) -> TypingEnv<'tcx> {
205 use rustc_middle::ty::TypingMode;
206 let tcx = self.linter.tcx;
207
208 let typing_mode = TypingMode::typeck_for_body(tcx, self.linter.item);
209 let param_env = tcx.param_env(self.linter.item);
210 TypingEnv::new(param_env, typing_mode)
211 }
212}
213
214fn contains_unknown_fn<'tcx>(ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
218 struct ContainsUnknownFnVisitor;
219
220 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsUnknownFnVisitor {
221 type Result = ControlFlow<Ty<'tcx>>;
222
223 fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
224 match t.kind() {
225 ty::Dynamic(..) | ty::FnPtr(_, _) => ControlFlow::Break(t),
226 _ => t.super_visit_with(self),
227 }
228 }
229 }
230
231 let cf = ty.visit_with(&mut ContainsUnknownFnVisitor);
232 cf.break_value()
233}