1use std::ops::ControlFlow;
12
13use rustc_hir::def_id::{DefId, LocalDefId};
14use rustc_hir::{HirId, LangItem, OwnerId};
15use rustc_middle::thir::visit::Visitor as _;
16use rustc_middle::thir::{self, Thir};
17use rustc_middle::ty::adjustment::PointerCoercion;
18use rustc_middle::ty::{
19 self, Binder, ExistentialTraitRef, GenericArgs, Instance, Ty, TyCtxt, TypeSuperVisitable as _,
20 TypeVisitable as _, TypeVisitor, TypingEnv,
21};
22use rustc_span::Span;
23use tracing::{debug, info};
24
25use crate::ferrocene::{InstantiateResult, LintState, UnvalidatedImplCause, Use, UseKind};
26
27pub(super) struct LintThir<'thir, 'tcx> {
28 thir: &'thir Thir<'tcx>,
29 linter: LintState<'tcx>,
30 owner: OwnerId,
31}
32
33impl<'thir, 'tcx: 'thir> thir::visit::Visitor<'thir, 'tcx> for LintThir<'thir, 'tcx> {
34 fn thir(&self) -> &'thir Thir<'tcx> {
35 self.thir
36 }
37
38 fn visit_expr(&mut self, expr: &'thir thir::Expr<'tcx>) {
39 let use_ = match self.find_unvalidated_use(expr) {
40 None => return,
41 Some(Use {
44 kind: UseKind::TraitObjectCast(UnvalidatedImplCause::UnresolvedGenericImpl(..), _),
45 ..
46 }) => return,
47 Some(use_) => use_,
48 };
49 let hir_id = HirId { owner: self.owner, local_id: expr.temp_scope_id };
50 {
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:50",
"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(50u32),
::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);
51 self.linter.check_use(hir_id, use_);
52 }
53}
54
55impl<'thir, 'tcx: 'thir> LintThir<'thir, 'tcx> {
56 pub(super) fn check_item(tcx: TyCtxt<'tcx>, owner: OwnerId, item: LocalDefId) -> Option<()> {
61 {
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:61",
"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(61u32),
::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:?}");
62
63 if tcx.sess.opts.test
64 && tcx.entry_fn(()).and_then(|(id, _)| id.as_local()) == Some(owner.def_id)
65 {
66 {
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:67",
"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(67u32),
::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");
68 return None;
69 }
70
71 let linter = LintState::new(tcx, item)?;
72 let body = tcx.thir_body(item).ok();
74 if body.is_none() {
75 {
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:75",
"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(75u32),
::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");
76 }
77 let thir = &body?.0.borrow();
78 let mut visitor = LintThir { linter, thir, owner };
79 for expr in &*thir.exprs {
80 visitor.visit_expr(expr);
81 }
82
83 Some(())
84 }
85
86 fn find_unvalidated_use(&mut self, expr: &thir::Expr<'tcx>) -> Option<Use<'tcx>> {
87 let tcx = self.linter.tcx;
88 let mut span = expr.span;
89
90 let mut try_instantiate = |def_id, args| self.try_instantiate(def_id, args, span);
91
92 let use_kind = match expr.kind {
93 thir::ExprKind::NamedConst { def_id, .. }
94 | thir::ExprKind::StaticRef { def_id, .. } => {
95 let unknown_fn = contains_unknown_fn(expr.ty)?;
101 UseKind::ContainsFnPtr(def_id, unknown_fn)
105 }
106 thir::ExprKind::Call { ty, .. } => {
107 let instance = self.instance_of_ty_ignoring_validated(ty, expr.span)?;
108 span = tcx.sess.source_map().span_until_char(expr.span, '(');
111
112 {
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:112",
"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(112u32),
::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:?}");
113 UseKind::Called(instance)
114 }
115 thir::ExprKind::Closure(ref expr) => {
119 LintThir::check_item(tcx, self.owner, expr.closure_id);
122 return None;
123 }
124 thir::ExprKind::PointerCoercion {
125 cast: PointerCoercion::ReifyFnPointer(_),
127 source,
128 ..
129 } => {
130 let source_ty = self.thir[source].ty;
131 let fn_ptr_trait = tcx.require_lang_item(LangItem::FnPtrTrait, expr.span);
132 let trait_ref = Binder::dummy(ExistentialTraitRef::new_from_args(
133 tcx,
134 fn_ptr_trait,
135 GenericArgs::empty(),
136 ));
137 self.linter.check_fn_ptr_coercion(
138 source_ty,
139 trait_ref.with_self_ty(tcx, source_ty),
140 &mut try_instantiate,
141 expr.span,
142 )?
143 }
144 thir::ExprKind::PointerCoercion { cast: PointerCoercion::Unsize, source, .. } => {
145 let source_ty = self.thir[source].ty;
146 self.linter.check_dyn_trait_coercion(
147 expr.ty,
148 source_ty,
149 self.typing_env(),
150 &mut try_instantiate,
151 span,
152 )?
153 }
154 _ => return None,
156 };
157
158 Some(Use { kind: use_kind, span, from_instantiation: None })
159 }
160
161 fn instance_of_ty_ignoring_validated(
162 &self,
163 ty: Ty<'tcx>,
164 span: Span,
165 ) -> Option<Instance<'tcx>> {
166 let mut try_instantiate = |def_id, args| self.try_instantiate(def_id, args, span);
167
168 self.linter.instance_of_ty(ty, None, &mut try_instantiate, span).filter(|instance| {
169 if #[allow(non_exhaustive_omitted_patterns)] match instance.def {
ty::InstanceKind::Virtual(..) => true,
_ => false,
}matches!(instance.def, ty::InstanceKind::Virtual(..)) {
172 {
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:172",
"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(172u32),
::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:?}");
173 false
174 } else {
175 true
176 }
177 })
178 }
179
180 fn try_instantiate(
181 &self,
182 def_id: DefId,
183 args: &'tcx GenericArgs<'tcx>,
184 span: Span,
185 ) -> InstantiateResult<'tcx> {
186 let tcx = self.linter.tcx;
187 match Instance::try_resolve(tcx, self.typing_env(), def_id, args) {
188 Err(_) => {
189 tcx.dcx().span_delayed_bug(
192 span,
193 ::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:?})"),
194 );
195 InstantiateResult::Err
196 }
197 Ok(None) => InstantiateResult::Indeterminate,
198 Ok(Some(instance)) => InstantiateResult::Resolved(instance),
199 }
200 }
201
202 fn typing_env(&self) -> TypingEnv<'tcx> {
203 use rustc_middle::ty::TypingMode;
204 let tcx = self.linter.tcx;
205
206 let typing_mode = TypingMode::typeck_for_body(tcx, self.linter.item);
207 let param_env = tcx.param_env(self.linter.item);
208 TypingEnv::new(param_env, typing_mode)
209 }
210}
211
212fn contains_unknown_fn<'tcx>(ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
216 struct ContainsUnknownFnVisitor;
217
218 impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsUnknownFnVisitor {
219 type Result = ControlFlow<Ty<'tcx>>;
220
221 fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
222 match t.kind() {
223 ty::Dynamic(..) | ty::FnPtr(_, _) => ControlFlow::Break(t),
224 _ => t.super_visit_with(self),
225 }
226 }
227 }
228
229 let cf = ty.visit_with(&mut ContainsUnknownFnVisitor);
230 cf.break_value()
231}