]>
Commit | Line | Data |
---|---|---|
74b04a01 | 1 | use rustc_ast::entry::EntryPointType; |
dfeec247 | 2 | use rustc_errors::struct_span_err; |
f9f354fc | 3 | use rustc_hir::def_id::{CrateNum, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; |
dfeec247 XL |
4 | use rustc_hir::itemlikevisit::ItemLikeVisitor; |
5 | use rustc_hir::{HirId, ImplItem, Item, ItemKind, TraitItem}; | |
ba9703b0 XL |
6 | use rustc_middle::hir::map::Map; |
7 | use rustc_middle::ty::query::Providers; | |
8 | use rustc_middle::ty::TyCtxt; | |
f9f354fc XL |
9 | use rustc_session::config::{CrateType, EntryFnType}; |
10 | use rustc_session::Session; | |
dfeec247 XL |
11 | use rustc_span::symbol::sym; |
12 | use rustc_span::{Span, DUMMY_SP}; | |
60c5eb7d | 13 | |
dc9dc135 | 14 | struct EntryContext<'a, 'tcx> { |
1a4d82fc | 15 | session: &'a Session, |
970d7e83 | 16 | |
ba9703b0 | 17 | map: Map<'tcx>, |
970d7e83 | 18 | |
e1599b0c | 19 | /// The top-level function called `main`. |
532ac7d7 | 20 | main_fn: Option<(HirId, Span)>, |
970d7e83 | 21 | |
e1599b0c | 22 | /// The function that has attribute named `main`. |
532ac7d7 | 23 | attr_main_fn: Option<(HirId, Span)>, |
970d7e83 | 24 | |
e1599b0c | 25 | /// The function that has the attribute 'start' on it. |
532ac7d7 | 26 | start_fn: Option<(HirId, Span)>, |
970d7e83 | 27 | |
e1599b0c XL |
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. | |
dfeec247 | 30 | non_main_fns: Vec<(HirId, Span)>, |
970d7e83 LB |
31 | } |
32 | ||
476ff2be | 33 | impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> { |
dfeec247 | 34 | fn visit_item(&mut self, item: &'tcx Item<'tcx>) { |
416331ca | 35 | let def_id = self.map.local_def_id(item.hir_id); |
f9f354fc | 36 | let def_key = self.map.def_key(def_id); |
92a42be0 SL |
37 | let at_root = def_key.parent == Some(CRATE_DEF_INDEX); |
38 | find_item(item, self, at_root); | |
1a4d82fc | 39 | } |
476ff2be | 40 | |
dfeec247 | 41 | fn visit_trait_item(&mut self, _trait_item: &'tcx TraitItem<'tcx>) { |
e1599b0c | 42 | // Entry fn is never a trait item. |
32a655c1 | 43 | } |
476ff2be | 44 | |
dfeec247 | 45 | fn visit_impl_item(&mut self, _impl_item: &'tcx ImplItem<'tcx>) { |
e1599b0c | 46 | // Entry fn is never a trait item. |
476ff2be | 47 | } |
1a4d82fc | 48 | } |
970d7e83 | 49 | |
f9f354fc | 50 | fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(LocalDefId, EntryFnType)> { |
9fa01778 XL |
51 | assert_eq!(cnum, LOCAL_CRATE); |
52 | ||
f9f354fc | 53 | let any_exe = tcx.sess.crate_types().iter().any(|ty| *ty == CrateType::Executable); |
1a4d82fc | 54 | if !any_exe { |
e1599b0c | 55 | // No need to find a main function. |
9fa01778 | 56 | return None; |
970d7e83 LB |
57 | } |
58 | ||
1a4d82fc | 59 | // If the user wants no main function at all, then stop here. |
3dfed10e | 60 | if tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::no_main) { |
9fa01778 | 61 | return None; |
1a4d82fc JJ |
62 | } |
63 | ||
64 | let mut ctxt = EntryContext { | |
9fa01778 XL |
65 | session: tcx.sess, |
66 | map: tcx.hir(), | |
970d7e83 LB |
67 | main_fn: None, |
68 | attr_main_fn: None, | |
69 | start_fn: None, | |
1a4d82fc | 70 | non_main_fns: Vec::new(), |
970d7e83 LB |
71 | }; |
72 | ||
9fa01778 | 73 | tcx.hir().krate().visit_all_item_likes(&mut ctxt); |
970d7e83 | 74 | |
9fa01778 | 75 | configure_main(tcx, &ctxt) |
970d7e83 LB |
76 | } |
77 | ||
3dfed10e XL |
78 | // Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs` |
79 | // (with `ast::Item`), so make sure to keep them in sync. | |
80 | fn entry_point_type(sess: &Session, item: &Item<'_>, at_root: bool) -> EntryPointType { | |
e74abb32 | 81 | match item.kind { |
8faf50e0 | 82 | ItemKind::Fn(..) => { |
3dfed10e | 83 | if sess.contains_name(&item.attrs, sym::start) { |
e9174d1e | 84 | EntryPointType::Start |
3dfed10e | 85 | } else if sess.contains_name(&item.attrs, sym::main) { |
e9174d1e | 86 | EntryPointType::MainAttr |
48663c56 | 87 | } else if item.ident.name == sym::main { |
92a42be0 | 88 | if at_root { |
e1599b0c | 89 | // This is a top-level function so can be `main`. |
e9174d1e | 90 | EntryPointType::MainNamed |
970d7e83 | 91 | } else { |
e9174d1e | 92 | EntryPointType::OtherMain |
970d7e83 | 93 | } |
e9174d1e SL |
94 | } else { |
95 | EntryPointType::None | |
970d7e83 LB |
96 | } |
97 | } | |
e9174d1e SL |
98 | _ => EntryPointType::None, |
99 | } | |
100 | } | |
101 | ||
dfeec247 | 102 | fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) { |
3dfed10e | 103 | match entry_point_type(&ctxt.session, item, at_root) { |
e9174d1e SL |
104 | EntryPointType::MainNamed => { |
105 | if ctxt.main_fn.is_none() { | |
532ac7d7 | 106 | ctxt.main_fn = Some((item.hir_id, item.span)); |
e9174d1e | 107 | } else { |
dfeec247 XL |
108 | struct_span_err!(ctxt.session, item.span, E0136, "multiple `main` functions") |
109 | .emit(); | |
e9174d1e | 110 | } |
dfeec247 | 111 | } |
e9174d1e | 112 | EntryPointType::OtherMain => { |
532ac7d7 | 113 | ctxt.non_main_fns.push((item.hir_id, item.span)); |
dfeec247 | 114 | } |
e9174d1e SL |
115 | EntryPointType::MainAttr => { |
116 | if ctxt.attr_main_fn.is_none() { | |
532ac7d7 | 117 | ctxt.attr_main_fn = Some((item.hir_id, item.span)); |
e9174d1e | 118 | } else { |
dfeec247 XL |
119 | struct_span_err!( |
120 | ctxt.session, | |
121 | item.span, | |
122 | E0137, | |
123 | "multiple functions with a `#[main]` attribute" | |
124 | ) | |
416331ca XL |
125 | .span_label(item.span, "additional `#[main]` function") |
126 | .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[main]` function") | |
5bcae85e | 127 | .emit(); |
e9174d1e | 128 | } |
dfeec247 | 129 | } |
e9174d1e SL |
130 | EntryPointType::Start => { |
131 | if ctxt.start_fn.is_none() { | |
532ac7d7 | 132 | ctxt.start_fn = Some((item.hir_id, item.span)); |
e9174d1e | 133 | } else { |
e1599b0c | 134 | struct_span_err!(ctxt.session, item.span, E0138, "multiple `start` functions") |
dfeec247 | 135 | .span_label(ctxt.start_fn.unwrap().1, "previous `#[start]` function here") |
7cac9316 | 136 | .span_label(item.span, "multiple `start` functions") |
5bcae85e | 137 | .emit(); |
e9174d1e | 138 | } |
9fa01778 XL |
139 | } |
140 | EntryPointType::None => (), | |
970d7e83 | 141 | } |
970d7e83 LB |
142 | } |
143 | ||
f9f354fc XL |
144 | fn configure_main( |
145 | tcx: TyCtxt<'_>, | |
146 | visitor: &EntryContext<'_, '_>, | |
147 | ) -> Option<(LocalDefId, EntryFnType)> { | |
532ac7d7 | 148 | if let Some((hir_id, _)) = visitor.start_fn { |
416331ca | 149 | Some((tcx.hir().local_def_id(hir_id), EntryFnType::Start)) |
532ac7d7 | 150 | } else if let Some((hir_id, _)) = visitor.attr_main_fn { |
416331ca | 151 | Some((tcx.hir().local_def_id(hir_id), EntryFnType::Main)) |
532ac7d7 | 152 | } else if let Some((hir_id, _)) = visitor.main_fn { |
416331ca | 153 | Some((tcx.hir().local_def_id(hir_id), EntryFnType::Main)) |
970d7e83 | 154 | } else { |
e1599b0c | 155 | no_main_err(tcx, visitor); |
9fa01778 | 156 | None |
970d7e83 LB |
157 | } |
158 | } | |
9fa01778 | 159 | |
e1599b0c | 160 | fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) { |
ba9703b0 | 161 | let sp = tcx.hir().krate().item.span; |
e74abb32 XL |
162 | if *tcx.sess.parse_sess.reached_eof.borrow() { |
163 | // There's an unclosed brace that made the parser reach `Eof`, we shouldn't complain about | |
164 | // the missing `fn main()` then as it might have been hidden inside an unclosed block. | |
165 | tcx.sess.delay_span_bug(sp, "`main` not found, but expected unclosed brace error"); | |
166 | return; | |
167 | } | |
168 | ||
e1599b0c | 169 | // There is no main function. |
dfeec247 XL |
170 | let mut err = struct_span_err!( |
171 | tcx.sess, | |
172 | DUMMY_SP, | |
173 | E0601, | |
174 | "`main` function not found in crate `{}`", | |
175 | tcx.crate_name(LOCAL_CRATE) | |
176 | ); | |
e1599b0c XL |
177 | let filename = &tcx.sess.local_crate_source_file; |
178 | let note = if !visitor.non_main_fns.is_empty() { | |
179 | for &(_, span) in &visitor.non_main_fns { | |
180 | err.span_note(span, "here is a function named `main`"); | |
181 | } | |
182 | err.note("you have one or more functions named `main` not defined at the crate level"); | |
dfeec247 XL |
183 | err.help( |
184 | "either move the `main` function definitions or attach the `#[main]` attribute \ | |
185 | to one of them", | |
186 | ); | |
e1599b0c | 187 | // There were some functions named `main` though. Try to give the user a hint. |
dfeec247 XL |
188 | format!( |
189 | "the main function must be defined at the crate level{}", | |
190 | filename.as_ref().map(|f| format!(" (in `{}`)", f.display())).unwrap_or_default() | |
191 | ) | |
e1599b0c XL |
192 | } else if let Some(filename) = filename { |
193 | format!("consider adding a `main` function to `{}`", filename.display()) | |
194 | } else { | |
195 | String::from("consider adding a `main` function at the crate level") | |
196 | }; | |
e1599b0c XL |
197 | // The file may be empty, which leads to the diagnostic machinery not emitting this |
198 | // note. This is a relatively simple way to detect that case and emit a span-less | |
199 | // note instead. | |
74b04a01 | 200 | if tcx.sess.source_map().lookup_line(sp.lo()).is_ok() { |
e1599b0c XL |
201 | err.set_span(sp); |
202 | err.span_label(sp, ¬e); | |
203 | } else { | |
204 | err.note(¬e); | |
205 | } | |
206 | if tcx.sess.teach(&err.get_code().unwrap()) { | |
dfeec247 XL |
207 | err.note( |
208 | "If you don't know the basics of Rust, you can go look to the Rust Book \ | |
209 | to get started: https://doc.rust-lang.org/book/", | |
210 | ); | |
e1599b0c XL |
211 | } |
212 | err.emit(); | |
213 | } | |
214 | ||
f9f354fc | 215 | pub fn find_entry_point(tcx: TyCtxt<'_>) -> Option<(LocalDefId, EntryFnType)> { |
9fa01778 XL |
216 | tcx.entry_fn(LOCAL_CRATE) |
217 | } | |
218 | ||
f035d41b | 219 | pub fn provide(providers: &mut Providers) { |
dfeec247 | 220 | *providers = Providers { entry_fn, ..*providers }; |
9fa01778 | 221 | } |