1use std::iter;
2use std::ops::ControlFlow;
3
4use rustc_abi::ExternAbi;
5use rustc_attr_ir::find_attr;
6use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
7use rustc_hir as hir;
8use rustc_hir::def::DefKind;
9use rustc_hir::def_id::{DefId, LocalDefId};
10use rustc_hir::intravisit::{self, Visitor};
11use rustc_middle::hir::nested_filter;
12use rustc_middle::middle::privacy::{EffectiveVisibility, Level};
13use rustc_middle::query::{LocalCrate, Providers};
14use rustc_middle::ty::{
15 self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Unnormalized, Visibility,
16};
17use rustc_span::Span;
18use rustc_structures::CrateType;
19
20use crate::diagnostics::UnexportableItem;
21
22struct ExportableItemCollector<'tcx> {
23 tcx: TyCtxt<'tcx>,
24 exportable_items: FxIndexSet<DefId>,
25 in_exportable_mod: bool,
26 seen_exportable_in_mod: bool,
27}
28
29impl<'tcx> ExportableItemCollector<'tcx> {
30 fn new(tcx: TyCtxt<'tcx>) -> ExportableItemCollector<'tcx> {
31 ExportableItemCollector {
32 tcx,
33 exportable_items: Default::default(),
34 in_exportable_mod: false,
35 seen_exportable_in_mod: false,
36 }
37 }
38
39 fn report_wrong_site(&self, def_id: LocalDefId) {
40 let def_descr = self.tcx.def_descr(def_id.to_def_id());
41 self.tcx.dcx().emit_err(UnexportableItem::Item {
42 descr: &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}", def_descr))
})format!("{}", def_descr),
43 span: self.tcx.def_span(def_id),
44 });
45 }
46
47 fn item_is_exportable(&self, def_id: LocalDefId) -> bool {
48 let has_attr = {
{
'done:
{
for i in
::rustc_attr_ir::HasAttrs::get_attrs(def_id, &self.tcx) {
#[allow(unused_imports)]
use ::rustc_attr_ir::AttributeKind::*;
let i: &::rustc_attr_ir::Attribute = i;
match i {
::rustc_attr_ir::Attribute::Parsed(ExportStable) => {
break 'done Some(());
}
::rustc_attr_ir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}.is_some()find_attr!(self.tcx, def_id, ExportStable);
49 if !self.in_exportable_mod && !has_attr {
50 return false;
51 }
52
53 let visibilities = self.tcx.effective_visibilities(());
54 let is_pub = visibilities.is_directly_public(def_id);
55
56 if has_attr && !is_pub {
57 let vis = visibilities.effective_vis(def_id).cloned().unwrap_or_else(|| {
58 EffectiveVisibility::from_vis(Visibility::Restricted(
59 self.tcx.parent_module_from_def_id(def_id),
60 ))
61 });
62 let vis = vis.at_level(Level::Direct);
63 let span = self.tcx.def_span(def_id);
64
65 self.tcx.dcx().emit_err(UnexportableItem::PrivItem {
66 vis_note: span,
67 vis_descr: &vis.to_string(def_id, self.tcx),
68 span,
69 });
70 return false;
71 }
72
73 is_pub && (has_attr || self.in_exportable_mod)
74 }
75
76 fn add_exportable(&mut self, def_id: LocalDefId) {
77 self.seen_exportable_in_mod = true;
78 self.exportable_items.insert(def_id.to_def_id());
79 }
80
81 fn walk_item_with_mod(&mut self, item: &'tcx hir::Item<'tcx>) {
82 let def_id = item.hir_id().owner.def_id;
83 let old_exportable_mod = self.in_exportable_mod;
84 if {
{
'done:
{
for i in
::rustc_attr_ir::HasAttrs::get_attrs(def_id, &self.tcx) {
#[allow(unused_imports)]
use ::rustc_attr_ir::AttributeKind::*;
let i: &::rustc_attr_ir::Attribute = i;
match i {
::rustc_attr_ir::Attribute::Parsed(ExportStable) => {
break 'done Some(());
}
::rustc_attr_ir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}.is_some()find_attr!(self.tcx, def_id, ExportStable) {
85 self.in_exportable_mod = true;
86 }
87 let old_seen_exportable_in_mod = std::mem::replace(&mut self.seen_exportable_in_mod, false);
88
89 intravisit::walk_item(self, item);
90
91 if self.seen_exportable_in_mod || self.in_exportable_mod {
92 self.exportable_items.insert(def_id.to_def_id());
93 }
94
95 self.seen_exportable_in_mod = old_seen_exportable_in_mod;
96 self.in_exportable_mod = old_exportable_mod;
97 }
98}
99
100impl<'tcx> Visitor<'tcx> for ExportableItemCollector<'tcx> {
101 type NestedFilter = nested_filter::All;
102
103 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
104 self.tcx
105 }
106
107 fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
108 let def_id = item.hir_id().owner.def_id;
109 match item.kind {
112 hir::ItemKind::Mod(..) => {
113 self.walk_item_with_mod(item);
114 return;
115 }
116 hir::ItemKind::Impl(impl_) if impl_.of_trait.is_none() => {
117 self.walk_item_with_mod(item);
118 return;
119 }
120 _ => {}
121 }
122
123 if !self.item_is_exportable(def_id) {
124 return;
125 }
126
127 match item.kind {
128 hir::ItemKind::Fn { .. }
129 | hir::ItemKind::Struct(..)
130 | hir::ItemKind::Enum(..)
131 | hir::ItemKind::Union(..)
132 | hir::ItemKind::TyAlias(..) => {
133 self.add_exportable(def_id);
134 }
135 hir::ItemKind::Use(tree) => {
136 for res in tree.resolutions() {
137 for res in res.present_items() {
138 if let Some(res_id) = res.opt_def_id()
140 && let Some(res_id) = res_id.as_local()
141 {
142 self.add_exportable(res_id);
143 }
144 }
145 }
146 }
147 hir::ItemKind::Mod(..) => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
149 hir::ItemKind::Impl(impl_) if impl_.of_trait.is_none() => {
150 ::core::panicking::panic("internal error: entered unreachable code");unreachable!();
151 }
152 _ => {
153 self.tcx.dcx().delayed_bug("Target is checked by attribute parser");
154 }
155 }
156 }
157
158 fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem<'tcx>) {
159 let def_id = item.hir_id().owner.def_id;
160 if !self.item_is_exportable(def_id) {
161 return;
162 }
163 match item.kind {
164 hir::ImplItemKind::Fn(..) | hir::ImplItemKind::Type(..) => {
165 self.add_exportable(def_id);
166 }
167 _ => self.report_wrong_site(def_id),
168 }
169 }
170
171 fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
172 let def_id = item.hir_id().owner.def_id;
173 if !self.item_is_exportable(def_id) {
174 self.report_wrong_site(def_id);
175 }
176 }
177
178 fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem<'tcx>) {
179 let def_id = item.hir_id().owner.def_id;
180 if !self.item_is_exportable(def_id) {
181 self.report_wrong_site(def_id);
182 }
183 }
184}
185
186struct ExportableItemsChecker<'tcx, 'a> {
187 tcx: TyCtxt<'tcx>,
188 exportable_items: &'a FxIndexSet<DefId>,
189 item_id: DefId,
190}
191
192impl<'tcx, 'a> ExportableItemsChecker<'tcx, 'a> {
193 fn check(&mut self) {
194 match self.tcx.def_kind(self.item_id) {
195 DefKind::Fn | DefKind::AssocFn => self.check_fn(),
196 DefKind::Enum | DefKind::Struct | DefKind::Union => self.check_ty(),
197 _ => {}
198 }
199 }
200
201 fn check_fn(&mut self) {
202 let def_id = self.item_id.expect_local();
203 let span = self.tcx.def_span(def_id);
204
205 if self.tcx.generics_of(def_id).requires_monomorphization(self.tcx) {
206 self.tcx.dcx().emit_err(UnexportableItem::GenericFn(span));
207 return;
208 }
209
210 let sig = self.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
211 if !#[allow(non_exhaustive_omitted_patterns)] match sig.abi() {
ExternAbi::C { .. } => true,
_ => false,
}matches!(sig.abi(), ExternAbi::C { .. }) {
212 self.tcx.dcx().emit_err(UnexportableItem::FnAbi(span));
213 return;
214 }
215
216 let sig = self
217 .tcx
218 .try_normalize_erasing_regions(
219 ty::TypingEnv::non_body_analysis(self.tcx, def_id),
220 Unnormalized::new_wip(sig),
221 )
222 .unwrap_or(sig);
223
224 let hir_id = self.tcx.local_def_id_to_hir_id(def_id);
225 let decl = self.tcx.hir_fn_decl_by_hir_id(hir_id).unwrap();
226
227 for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
228 self.check_nested_types_are_exportable(*input_ty, input_hir.span);
229 }
230
231 if let hir::FnRetTy::Return(ret_hir) = decl.output {
232 self.check_nested_types_are_exportable(sig.output(), ret_hir.span);
233 }
234 }
235
236 fn check_ty(&mut self) {
237 let ty = self.tcx.type_of(self.item_id).skip_binder();
238 if let ty::Adt(adt_def, _) = ty.kind() {
239 if !adt_def.repr().inhibit_struct_field_reordering() {
240 self.tcx
241 .dcx()
242 .emit_err(UnexportableItem::TypeRepr(self.tcx.def_span(self.item_id)));
243 }
244
245 for variant in adt_def.variants() {
247 for field in &variant.fields {
248 if !field.vis.is_public() {
249 self.tcx.dcx().emit_err(UnexportableItem::AdtWithPrivFields {
250 span: self.tcx.def_span(self.item_id),
251 vis_note: self.tcx.def_span(field.did),
252 field_name: field.name.as_str(),
253 });
254 }
255 }
256 }
257 }
258 }
259
260 fn check_nested_types_are_exportable(&mut self, ty: Ty<'tcx>, ty_span: Span) {
261 let res = ty.visit_with(self);
262 if let Some(err_cause) = res.break_value() {
263 self.tcx.dcx().emit_err(UnexportableItem::TypeInInterface {
264 span: self.tcx.def_span(self.item_id),
265 desc: self.tcx.def_descr(self.item_id),
266 ty: &::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}", err_cause))
})format!("{}", err_cause),
267 ty_span,
268 });
269 }
270 }
271}
272
273impl<'tcx, 'a> TypeVisitor<TyCtxt<'tcx>> for ExportableItemsChecker<'tcx, 'a> {
274 type Result = ControlFlow<Ty<'tcx>>;
275
276 fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
277 match ty.kind() {
278 ty::Adt(adt_def, _) => {
279 let did = adt_def.did();
280 let exportable = if did.is_local() {
281 self.exportable_items.contains(&did)
282 } else {
283 self.tcx.is_exportable(did)
284 };
285 if !exportable {
286 return ControlFlow::Break(ty);
287 }
288 for variant in adt_def.variants() {
289 for field in &variant.fields {
290 let field_ty =
291 self.tcx.type_of(field.did).instantiate_identity().skip_norm_wip();
292 field_ty.visit_with(self)?;
293 }
294 }
295
296 return ty.super_visit_with(self);
297 }
298
299 ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Bool | ty::Char | ty::Error(_) => {}
300
301 ty::Array(_, _)
302 | ty::Ref(_, _, _)
303 | ty::Param(_)
304 | ty::Closure(_, _)
305 | ty::Dynamic(_, _)
306 | ty::Coroutine(_, _)
307 | ty::Foreign(_)
308 | ty::Str
309 | ty::Tuple(_)
310 | ty::Pat(..)
311 | ty::Slice(_)
312 | ty::RawPtr(_, _)
313 | ty::FnDef(_, _)
314 | ty::FnPtr(_, _)
315 | ty::CoroutineClosure(_, _)
316 | ty::CoroutineWitness(_, _)
317 | ty::Never
318 | ty::UnsafeBinder(_)
319 | ty::Alias(_, ty::AliasTy { kind: ty::AliasTyKind::Opaque { .. }, .. }) => {
320 return ControlFlow::Break(ty);
321 }
322
323 ty::Alias(..) | ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
324 }
325 ControlFlow::Continue(())
326 }
327}
328
329fn exportable_items_provider_local<'tcx>(tcx: TyCtxt<'tcx>, _: LocalCrate) -> &'tcx [DefId] {
336 if !tcx.crate_types().contains(&CrateType::Sdylib) && !tcx.is_sdylib_interface_build() {
337 return &[];
338 }
339
340 let mut visitor = ExportableItemCollector::new(tcx);
341 tcx.hir_walk_toplevel_module(&mut visitor);
342 let exportable_items = visitor.exportable_items;
343 for item_id in exportable_items.iter() {
344 let mut validator =
345 ExportableItemsChecker { tcx, exportable_items: &exportable_items, item_id: *item_id };
346 validator.check();
347 }
348
349 tcx.arena.alloc_from_iter(exportable_items.into_iter())
350}
351
352struct ImplsOrderVisitor<'tcx> {
353 tcx: TyCtxt<'tcx>,
354 order: FxIndexMap<DefId, usize>,
355}
356
357impl<'tcx> ImplsOrderVisitor<'tcx> {
358 fn new(tcx: TyCtxt<'tcx>) -> ImplsOrderVisitor<'tcx> {
359 ImplsOrderVisitor { tcx, order: Default::default() }
360 }
361}
362
363impl<'tcx> Visitor<'tcx> for ImplsOrderVisitor<'tcx> {
364 type NestedFilter = nested_filter::All;
365
366 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
367 self.tcx
368 }
369
370 fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
371 if let hir::ItemKind::Impl(impl_) = item.kind
372 && impl_.of_trait.is_none()
373 && self.tcx.is_exportable(item.owner_id.def_id.to_def_id())
374 {
375 self.order.insert(item.owner_id.def_id.to_def_id(), self.order.len());
376 }
377 intravisit::walk_item(self, item);
378 }
379}
380
381fn stable_order_of_exportable_impls<'tcx>(
389 tcx: TyCtxt<'tcx>,
390 _: LocalCrate,
391) -> &'tcx FxIndexMap<DefId, usize> {
392 if !tcx.crate_types().contains(&CrateType::Sdylib) && !tcx.is_sdylib_interface_build() {
393 return tcx.arena.alloc(FxIndexMap::<DefId, usize>::default());
394 }
395
396 let mut vis = ImplsOrderVisitor::new(tcx);
397 tcx.hir_walk_toplevel_module(&mut vis);
398 tcx.arena.alloc(vis.order)
399}
400
401pub(crate) fn provide(providers: &mut Providers) {
402 *providers = Providers {
403 exportable_items: exportable_items_provider_local,
404 stable_order_of_exportable_impls,
405 ..*providers
406 };
407}