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