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