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