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