2 use rustc_ast
::entry
::EntryPointType
;
3 use rustc_errors
::error_code
;
4 use rustc_hir
::def
::DefKind
;
5 use rustc_hir
::def_id
::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}
;
6 use rustc_hir
::{ItemId, Node, CRATE_HIR_ID}
;
7 use rustc_middle
::ty
::query
::Providers
;
8 use rustc_middle
::ty
::TyCtxt
;
9 use rustc_session
::config
::{sigpipe, CrateType, EntryFnType}
;
10 use rustc_session
::parse
::feature_err
;
11 use rustc_span
::symbol
::sym
;
12 use rustc_span
::{Span, Symbol}
;
15 AttrOnlyInFunctions
, AttrOnlyOnMain
, AttrOnlyOnRootMain
, ExternMain
, MultipleRustcMain
,
16 MultipleStartFunctions
, NoMainErr
, UnixSigpipeValues
,
19 struct EntryContext
<'tcx
> {
22 /// The function that has attribute named `main`.
23 attr_main_fn
: Option
<(LocalDefId
, Span
)>,
25 /// The function that has the attribute 'start' on it.
26 start_fn
: Option
<(LocalDefId
, Span
)>,
28 /// The functions that one might think are `main` but aren't, e.g.
29 /// main functions not defined at the top level. For diagnostics.
30 non_main_fns
: Vec
<Span
>,
33 fn entry_fn(tcx
: TyCtxt
<'_
>, (): ()) -> Option
<(DefId
, EntryFnType
)> {
34 let any_exe
= tcx
.sess
.crate_types().iter().any(|ty
| *ty
== CrateType
::Executable
);
36 // No need to find a main function.
40 // If the user wants no main function at all, then stop here.
41 if attr
::contains_name(&tcx
.hir().attrs(CRATE_HIR_ID
), sym
::no_main
) {
46 EntryContext { tcx, attr_main_fn: None, start_fn: None, non_main_fns: Vec::new() }
;
48 for id
in tcx
.hir().items() {
49 find_item(id
, &mut ctxt
);
52 configure_main(tcx
, &ctxt
)
55 // Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs`
56 // (with `ast::Item`), so make sure to keep them in sync.
57 // A small optimization was added so that hir::Item is fetched only when needed.
58 // An equivalent optimization was not applied to the duplicated code in test_harness.rs.
59 fn entry_point_type(ctxt
: &EntryContext
<'_
>, id
: ItemId
, at_root
: bool
) -> EntryPointType
{
60 let attrs
= ctxt
.tcx
.hir().attrs(id
.hir_id());
61 if attr
::contains_name(attrs
, sym
::start
) {
63 } else if attr
::contains_name(attrs
, sym
::rustc_main
) {
64 EntryPointType
::RustcMainAttr
66 if let Some(name
) = ctxt
.tcx
.opt_item_name(id
.owner_id
.to_def_id())
67 && name
== sym
::main
{
69 // This is a top-level function so can be `main`.
70 EntryPointType
::MainNamed
72 EntryPointType
::OtherMain
80 fn attr_span_by_symbol(ctxt
: &EntryContext
<'_
>, id
: ItemId
, sym
: Symbol
) -> Option
<Span
> {
81 let attrs
= ctxt
.tcx
.hir().attrs(id
.hir_id());
82 attr
::find_by_name(attrs
, sym
).map(|attr
| attr
.span
)
85 fn find_item(id
: ItemId
, ctxt
: &mut EntryContext
<'_
>) {
86 let at_root
= ctxt
.tcx
.opt_local_parent(id
.owner_id
.def_id
) == Some(CRATE_DEF_ID
);
88 match entry_point_type(ctxt
, id
, at_root
) {
89 EntryPointType
::None
=> {
90 if let Some(span
) = attr_span_by_symbol(ctxt
, id
, sym
::unix_sigpipe
) {
91 ctxt
.tcx
.sess
.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe }
);
94 _
if !matches
!(ctxt
.tcx
.def_kind(id
.owner_id
), DefKind
::Fn
) => {
95 for attr
in [sym
::start
, sym
::rustc_main
] {
96 if let Some(span
) = attr_span_by_symbol(ctxt
, id
, attr
) {
97 ctxt
.tcx
.sess
.emit_err(AttrOnlyInFunctions { span, attr }
);
101 EntryPointType
::MainNamed
=> (),
102 EntryPointType
::OtherMain
=> {
103 if let Some(span
) = attr_span_by_symbol(ctxt
, id
, sym
::unix_sigpipe
) {
104 ctxt
.tcx
.sess
.emit_err(AttrOnlyOnRootMain { span, attr: sym::unix_sigpipe }
);
106 ctxt
.non_main_fns
.push(ctxt
.tcx
.def_span(id
.owner_id
));
108 EntryPointType
::RustcMainAttr
=> {
109 if ctxt
.attr_main_fn
.is_none() {
110 ctxt
.attr_main_fn
= Some((id
.owner_id
.def_id
, ctxt
.tcx
.def_span(id
.owner_id
)));
112 ctxt
.tcx
.sess
.emit_err(MultipleRustcMain
{
113 span
: ctxt
.tcx
.def_span(id
.owner_id
.to_def_id()),
114 first
: ctxt
.attr_main_fn
.unwrap().1,
115 additional
: ctxt
.tcx
.def_span(id
.owner_id
.to_def_id()),
119 EntryPointType
::Start
=> {
120 if let Some(span
) = attr_span_by_symbol(ctxt
, id
, sym
::unix_sigpipe
) {
121 ctxt
.tcx
.sess
.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe }
);
123 if ctxt
.start_fn
.is_none() {
124 ctxt
.start_fn
= Some((id
.owner_id
.def_id
, ctxt
.tcx
.def_span(id
.owner_id
)));
126 ctxt
.tcx
.sess
.emit_err(MultipleStartFunctions
{
127 span
: ctxt
.tcx
.def_span(id
.owner_id
),
128 labeled
: ctxt
.tcx
.def_span(id
.owner_id
.to_def_id()),
129 previous
: ctxt
.start_fn
.unwrap().1,
136 fn configure_main(tcx
: TyCtxt
<'_
>, visitor
: &EntryContext
<'_
>) -> Option
<(DefId
, EntryFnType
)> {
137 if let Some((def_id
, _
)) = visitor
.start_fn
{
138 Some((def_id
.to_def_id(), EntryFnType
::Start
))
139 } else if let Some((local_def_id
, _
)) = visitor
.attr_main_fn
{
140 let def_id
= local_def_id
.to_def_id();
141 Some((def_id
, EntryFnType
::Main { sigpipe: sigpipe(tcx, def_id) }
))
143 if let Some(main_def
) = tcx
.resolutions(()).main_def
&& let Some(def_id
) = main_def
.opt_fn_def_id() {
144 // non-local main imports are handled below
145 if let Some(def_id
) = def_id
.as_local() && matches
!(tcx
.hir().find_by_def_id(def_id
), Some(Node
::ForeignItem(_
))) {
146 tcx
.sess
.emit_err(ExternMain { span: tcx.def_span(def_id) }
);
150 if main_def
.is_import
&& !tcx
.features().imported_main
{
151 let span
= main_def
.span
;
153 &tcx
.sess
.parse_sess
,
156 "using an imported function as entry point `main` is experimental",
160 return Some((def_id
, EntryFnType
::Main { sigpipe: sigpipe(tcx, def_id) }
));
162 no_main_err(tcx
, visitor
);
167 fn sigpipe(tcx
: TyCtxt
<'_
>, def_id
: DefId
) -> u8 {
168 if let Some(attr
) = tcx
.get_attr(def_id
, sym
::unix_sigpipe
) {
169 match (attr
.value_str(), attr
.meta_item_list()) {
170 (Some(sym
::inherit
), None
) => sigpipe
::INHERIT
,
171 (Some(sym
::sig_ign
), None
) => sigpipe
::SIG_IGN
,
172 (Some(sym
::sig_dfl
), None
) => sigpipe
::SIG_DFL
,
174 // Keep going so that `fn emit_malformed_attribute()` can print
175 // an excellent error message
179 tcx
.sess
.emit_err(UnixSigpipeValues { span: attr.span }
);
188 fn no_main_err(tcx
: TyCtxt
<'_
>, visitor
: &EntryContext
<'_
>) {
189 let sp
= tcx
.def_span(CRATE_DEF_ID
);
190 if tcx
.sess
.parse_sess
.reached_eof
.load(rustc_data_structures
::sync
::Ordering
::Relaxed
) {
191 // There's an unclosed brace that made the parser reach `Eof`, we shouldn't complain about
192 // the missing `fn main()` then as it might have been hidden inside an unclosed block.
193 tcx
.sess
.delay_span_bug(sp
, "`main` not found, but expected unclosed brace error");
197 // There is no main function.
198 let mut has_filename
= true;
199 let filename
= tcx
.sess
.local_crate_source_file().unwrap_or_else(|| {
200 has_filename
= false;
203 let main_def_opt
= tcx
.resolutions(()).main_def
;
204 let diagnostic_id
= error_code
!(E0601
);
205 let add_teach_note
= tcx
.sess
.teach(&diagnostic_id
);
206 // The file may be empty, which leads to the diagnostic machinery not emitting this
207 // note. This is a relatively simple way to detect that case and emit a span-less
209 let file_empty
= tcx
.sess
.source_map().lookup_line(sp
.hi()).is_err();
211 tcx
.sess
.emit_err(NoMainErr
{
213 crate_name
: tcx
.crate_name(LOCAL_CRATE
),
217 non_main_fns
: visitor
.non_main_fns
.clone(),
223 pub fn provide(providers
: &mut Providers
) {
224 *providers
= Providers { entry_fn, ..*providers }
;