1use rustc_ast::entry::EntryPointType;
2use rustc_errors::codes::*;
3use rustc_hir::attrs::AttributeKind;
4use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId};
5use rustc_hir::{CRATE_HIR_ID, ItemId, Node, find_attr};
6use rustc_middle::query::Providers;
7use rustc_middle::ty::TyCtxt;
8use rustc_session::config::{CrateType, EntryFnType, sigpipe};
9use rustc_span::{RemapPathScopeComponents, Span};
10
11use crate::errors::{ExternMain, MultipleRustcMain, NoMainErr};
12
13struct EntryContext<'tcx> {
14 tcx: TyCtxt<'tcx>,
15
16 rustc_main_fn: Option<(LocalDefId, Span)>,
18
19 non_main_fns: Vec<Span>,
22}
23
24fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> {
25 let any_exe = tcx.crate_types().contains(&CrateType::Executable);
26 if !any_exe {
27 return None;
29 }
30
31 if {
{
'done:
{
for i in tcx.hir_attrs(CRATE_HIR_ID) {
let i: &rustc_hir::Attribute = i;
match i {
rustc_hir::Attribute::Parsed(AttributeKind::NoMain) => {
break 'done Some(());
}
_ => {}
}
}
None
}
}.is_some()
}find_attr!(tcx.hir_attrs(CRATE_HIR_ID), AttributeKind::NoMain) {
33 return None;
34 }
35
36 let mut ctxt = EntryContext { tcx, rustc_main_fn: None, non_main_fns: Vec::new() };
37
38 for id in tcx.hir_free_items() {
39 check_and_search_item(id, &mut ctxt);
40 }
41
42 configure_main(tcx, &ctxt)
43}
44
45fn check_and_search_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
46 let at_root = ctxt.tcx.opt_local_parent(id.owner_id.def_id) == Some(CRATE_DEF_ID);
47
48 let attrs = ctxt.tcx.hir_attrs(id.hir_id());
49 let entry_point_type = rustc_ast::entry::entry_point_type(
50 {
{
'done:
{
for i in attrs {
let i: &rustc_hir::Attribute = i;
match i {
rustc_hir::Attribute::Parsed(AttributeKind::RustcMain) => {
break 'done Some(());
}
_ => {}
}
}
None
}
}.is_some()
}find_attr!(attrs, AttributeKind::RustcMain),
51 at_root,
52 ctxt.tcx.opt_item_name(id.owner_id.to_def_id()),
53 );
54
55 match entry_point_type {
56 EntryPointType::None => {}
57 EntryPointType::MainNamed => {}
58 EntryPointType::OtherMain => {
59 ctxt.non_main_fns.push(ctxt.tcx.def_span(id.owner_id));
60 }
61 EntryPointType::RustcMainAttr => {
62 if ctxt.rustc_main_fn.is_none() {
63 ctxt.rustc_main_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id)));
64 } else {
65 ctxt.tcx.dcx().emit_err(MultipleRustcMain {
66 span: ctxt.tcx.def_span(id.owner_id.to_def_id()),
67 first: ctxt.rustc_main_fn.unwrap().1,
68 additional: ctxt.tcx.def_span(id.owner_id.to_def_id()),
69 });
70 }
71 }
72 }
73}
74
75fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> {
76 if let Some((local_def_id, _)) = visitor.rustc_main_fn {
77 let def_id = local_def_id.to_def_id();
78 Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx) }))
79 } else {
80 if let Some(main_def) = tcx.resolutions(()).main_def
82 && let Some(def_id) = main_def.opt_fn_def_id()
83 {
84 if let Some(def_id) = def_id.as_local()
86 && #[allow(non_exhaustive_omitted_patterns)] match tcx.hir_node_by_def_id(def_id)
{
Node::ForeignItem(_) => true,
_ => false,
}matches!(tcx.hir_node_by_def_id(def_id), Node::ForeignItem(_))
87 {
88 tcx.dcx().emit_err(ExternMain { span: tcx.def_span(def_id) });
89 return None;
90 }
91
92 return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx) }));
93 }
94 no_main_err(tcx, visitor);
95 None
96 }
97}
98
99fn sigpipe(tcx: TyCtxt<'_>) -> u8 {
100 match tcx.sess.opts.unstable_opts.on_broken_pipe {
101 rustc_target::spec::OnBrokenPipe::Default => sigpipe::DEFAULT,
102 rustc_target::spec::OnBrokenPipe::Kill => sigpipe::SIG_DFL,
103 rustc_target::spec::OnBrokenPipe::Error => sigpipe::SIG_IGN,
104 rustc_target::spec::OnBrokenPipe::Inherit => sigpipe::INHERIT,
105 }
106}
107
108fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) {
109 let sp = tcx.def_span(CRATE_DEF_ID);
110
111 let mut has_filename = true;
113 let filename = tcx
114 .sess
115 .local_crate_source_file()
116 .map(|src| src.path(RemapPathScopeComponents::DIAGNOSTICS).to_path_buf())
117 .unwrap_or_else(|| {
118 has_filename = false;
119 Default::default()
120 });
121 let main_def_opt = tcx.resolutions(()).main_def;
122 let code = E0601;
123 let add_teach_note = tcx.sess.teach(code);
124 let file_empty = tcx.sess.source_map().lookup_line(sp.hi()).is_err();
128
129 tcx.dcx().emit_err(NoMainErr {
130 sp,
131 crate_name: tcx.crate_name(LOCAL_CRATE),
132 has_filename,
133 filename,
134 file_empty,
135 non_main_fns: visitor.non_main_fns.clone(),
136 main_def_opt,
137 add_teach_note,
138 });
139}
140
141pub fn provide(providers: &mut Providers) {
142 *providers = Providers { entry_fn, ..*providers };
143}