1 use rustc_ast
::entry
::EntryPointType
;
2 use rustc_errors
::struct_span_err
;
3 use rustc_hir
::def
::DefKind
;
4 use rustc_hir
::def_id
::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}
;
5 use rustc_hir
::{ItemId, Node, CRATE_HIR_ID}
;
6 use rustc_middle
::ty
::query
::Providers
;
7 use rustc_middle
::ty
::{DefIdTree, TyCtxt}
;
8 use rustc_session
::config
::{CrateType, EntryFnType}
;
9 use rustc_session
::parse
::feature_err
;
10 use rustc_session
::Session
;
11 use rustc_span
::symbol
::sym
;
12 use rustc_span
::{Span, DUMMY_SP}
;
14 struct EntryContext
<'tcx
> {
17 /// The function that has attribute named `main`.
18 attr_main_fn
: Option
<(LocalDefId
, Span
)>,
20 /// The function that has the attribute 'start' on it.
21 start_fn
: Option
<(LocalDefId
, Span
)>,
23 /// The functions that one might think are `main` but aren't, e.g.
24 /// main functions not defined at the top level. For diagnostics.
25 non_main_fns
: Vec
<Span
>,
28 fn entry_fn(tcx
: TyCtxt
<'_
>, (): ()) -> Option
<(DefId
, EntryFnType
)> {
29 let any_exe
= tcx
.sess
.crate_types().iter().any(|ty
| *ty
== CrateType
::Executable
);
31 // No need to find a main function.
35 // If the user wants no main function at all, then stop here.
36 if tcx
.sess
.contains_name(&tcx
.hir().attrs(CRATE_HIR_ID
), sym
::no_main
) {
41 EntryContext { tcx, attr_main_fn: None, start_fn: None, non_main_fns: Vec::new() }
;
43 for id
in tcx
.hir().items() {
44 find_item(id
, &mut ctxt
);
47 configure_main(tcx
, &ctxt
)
50 // Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs`
51 // (with `ast::Item`), so make sure to keep them in sync.
52 // A small optimization was added so that hir::Item is fetched only when needed.
53 // An equivalent optimization was not applied to the duplicated code in test_harness.rs.
54 fn entry_point_type(ctxt
: &EntryContext
<'_
>, id
: ItemId
, at_root
: bool
) -> EntryPointType
{
55 let attrs
= ctxt
.tcx
.hir().attrs(id
.hir_id());
56 if ctxt
.tcx
.sess
.contains_name(attrs
, sym
::start
) {
58 } else if ctxt
.tcx
.sess
.contains_name(attrs
, sym
::rustc_main
) {
59 EntryPointType
::RustcMainAttr
61 if let Some(name
) = ctxt
.tcx
.opt_item_name(id
.def_id
.to_def_id())
62 && name
== sym
::main
{
64 // This is a top-level function so can be `main`.
65 EntryPointType
::MainNamed
67 EntryPointType
::OtherMain
75 fn throw_attr_err(sess
: &Session
, span
: Span
, attr
: &str) {
76 sess
.struct_span_err(span
, &format
!("`{}` attribute can only be used on functions", attr
))
80 fn find_item(id
: ItemId
, ctxt
: &mut EntryContext
<'_
>) {
81 let at_root
= ctxt
.tcx
.opt_local_parent(id
.def_id
) == Some(CRATE_DEF_ID
);
83 match entry_point_type(ctxt
, id
, at_root
) {
84 EntryPointType
::None
=> (),
85 _
if !matches
!(ctxt
.tcx
.def_kind(id
.def_id
), DefKind
::Fn
) => {
86 let attrs
= ctxt
.tcx
.hir().attrs(id
.hir_id());
87 if let Some(attr
) = ctxt
.tcx
.sess
.find_by_name(attrs
, sym
::start
) {
88 throw_attr_err(&ctxt
.tcx
.sess
, attr
.span
, "start");
90 if let Some(attr
) = ctxt
.tcx
.sess
.find_by_name(attrs
, sym
::rustc_main
) {
91 throw_attr_err(&ctxt
.tcx
.sess
, attr
.span
, "rustc_main");
94 EntryPointType
::MainNamed
=> (),
95 EntryPointType
::OtherMain
=> {
96 ctxt
.non_main_fns
.push(ctxt
.tcx
.def_span(id
.def_id
));
98 EntryPointType
::RustcMainAttr
=> {
99 if ctxt
.attr_main_fn
.is_none() {
100 ctxt
.attr_main_fn
= Some((id
.def_id
, ctxt
.tcx
.def_span(id
.def_id
.to_def_id())));
104 ctxt
.tcx
.def_span(id
.def_id
.to_def_id()),
106 "multiple functions with a `#[rustc_main]` attribute"
109 ctxt
.tcx
.def_span(id
.def_id
.to_def_id()),
110 "additional `#[rustc_main]` function",
112 .span_label(ctxt
.attr_main_fn
.unwrap().1, "first `#[rustc_main]` function")
116 EntryPointType
::Start
=> {
117 if ctxt
.start_fn
.is_none() {
118 ctxt
.start_fn
= Some((id
.def_id
, ctxt
.tcx
.def_span(id
.def_id
.to_def_id())));
122 ctxt
.tcx
.def_span(id
.def_id
.to_def_id()),
124 "multiple `start` functions"
126 .span_label(ctxt
.start_fn
.unwrap().1, "previous `#[start]` function here")
127 .span_label(ctxt
.tcx
.def_span(id
.def_id
.to_def_id()), "multiple `start` functions")
134 fn configure_main(tcx
: TyCtxt
<'_
>, visitor
: &EntryContext
<'_
>) -> Option
<(DefId
, EntryFnType
)> {
135 if let Some((def_id
, _
)) = visitor
.start_fn
{
136 Some((def_id
.to_def_id(), EntryFnType
::Start
))
137 } else if let Some((def_id
, _
)) = visitor
.attr_main_fn
{
138 Some((def_id
.to_def_id(), EntryFnType
::Main
))
140 if let Some(main_def
) = tcx
.resolutions(()).main_def
&& let Some(def_id
) = main_def
.opt_fn_def_id() {
141 // non-local main imports are handled below
142 if let Some(def_id
) = def_id
.as_local() && matches
!(tcx
.hir().find_by_def_id(def_id
), Some(Node
::ForeignItem(_
))) {
145 tcx
.def_span(def_id
),
146 "the `main` function cannot be declared in an `extern` block",
152 if main_def
.is_import
&& !tcx
.features().imported_main
{
153 let span
= main_def
.span
;
155 &tcx
.sess
.parse_sess
,
158 "using an imported function as entry point `main` is experimental",
162 return Some((def_id
, EntryFnType
::Main
));
164 no_main_err(tcx
, visitor
);
169 fn no_main_err(tcx
: TyCtxt
<'_
>, visitor
: &EntryContext
<'_
>) {
170 let sp
= tcx
.def_span(CRATE_DEF_ID
);
171 if *tcx
.sess
.parse_sess
.reached_eof
.borrow() {
172 // There's an unclosed brace that made the parser reach `Eof`, we shouldn't complain about
173 // the missing `fn main()` then as it might have been hidden inside an unclosed block.
174 tcx
.sess
.delay_span_bug(sp
, "`main` not found, but expected unclosed brace error");
178 // There is no main function.
179 let mut err
= struct_span_err
!(
183 "`main` function not found in crate `{}`",
184 tcx
.crate_name(LOCAL_CRATE
)
186 let filename
= &tcx
.sess
.local_crate_source_file
;
187 let note
= if !visitor
.non_main_fns
.is_empty() {
188 for &span
in &visitor
.non_main_fns
{
189 err
.span_note(span
, "here is a function named `main`");
191 err
.note("you have one or more functions named `main` not defined at the crate level");
192 err
.help("consider moving the `main` function definitions");
193 // There were some functions named `main` though. Try to give the user a hint.
195 "the main function must be defined at the crate level{}",
196 filename
.as_ref().map(|f
| format
!(" (in `{}`)", f
.display())).unwrap_or_default()
198 } else if let Some(filename
) = filename
{
199 format
!("consider adding a `main` function to `{}`", filename
.display())
201 String
::from("consider adding a `main` function at the crate level")
203 // The file may be empty, which leads to the diagnostic machinery not emitting this
204 // note. This is a relatively simple way to detect that case and emit a span-less
206 if tcx
.sess
.source_map().lookup_line(sp
.hi()).is_ok() {
207 err
.set_span(sp
.shrink_to_hi());
208 err
.span_label(sp
.shrink_to_hi(), ¬e
);
213 if let Some(main_def
) = tcx
.resolutions(()).main_def
&& main_def
.opt_fn_def_id().is_none(){
214 // There is something at `crate::main`, but it is not a function definition.
215 err
.span_label(main_def
.span
, "non-function item at `crate::main` is found");
218 if tcx
.sess
.teach(&err
.get_code().unwrap()) {
220 "If you don't know the basics of Rust, you can go look to the Rust Book \
221 to get started: https://doc.rust-lang.org/book/",
227 pub fn provide(providers
: &mut Providers
) {
228 *providers
= Providers { entry_fn, ..*providers }
;