]>
Commit | Line | Data |
---|---|---|
74b04a01 | 1 | use rustc_ast::entry::EntryPointType; |
dfeec247 | 2 | use rustc_errors::struct_span_err; |
923072b8 | 3 | use rustc_hir::def::DefKind; |
04454e1e | 4 | use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; |
923072b8 | 5 | use rustc_hir::{ItemId, Node, 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 | ||
17df50a5 | 28 | fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> { |
f9f354fc | 29 | let any_exe = tcx.sess.crate_types().iter().any(|ty| *ty == CrateType::Executable); |
1a4d82fc | 30 | if !any_exe { |
e1599b0c | 31 | // No need to find a main function. |
9fa01778 | 32 | return None; |
970d7e83 LB |
33 | } |
34 | ||
1a4d82fc | 35 | // If the user wants no main function at all, then stop here. |
6a06907d | 36 | if tcx.sess.contains_name(&tcx.hir().attrs(CRATE_HIR_ID), sym::no_main) { |
9fa01778 | 37 | return None; |
1a4d82fc JJ |
38 | } |
39 | ||
04454e1e FG |
40 | let mut ctxt = |
41 | EntryContext { tcx, attr_main_fn: None, start_fn: None, non_main_fns: Vec::new() }; | |
970d7e83 | 42 | |
923072b8 FG |
43 | for id in tcx.hir().items() { |
44 | find_item(id, &mut ctxt); | |
45 | } | |
970d7e83 | 46 | |
9fa01778 | 47 | configure_main(tcx, &ctxt) |
970d7e83 LB |
48 | } |
49 | ||
3dfed10e XL |
50 | // Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs` |
51 | // (with `ast::Item`), so make sure to keep them in sync. | |
923072b8 FG |
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()); | |
04454e1e | 56 | if ctxt.tcx.sess.contains_name(attrs, sym::start) { |
29967ef6 | 57 | EntryPointType::Start |
04454e1e | 58 | } else if ctxt.tcx.sess.contains_name(attrs, sym::rustc_main) { |
923072b8 FG |
59 | EntryPointType::RustcMainAttr |
60 | } else { | |
61 | if let Some(name) = ctxt.tcx.opt_item_name(id.def_id.to_def_id()) | |
62 | && name == sym::main { | |
63 | if at_root { | |
64 | // This is a top-level function so can be `main`. | |
65 | EntryPointType::MainNamed | |
66 | } else { | |
67 | EntryPointType::OtherMain | |
68 | } | |
29967ef6 | 69 | } else { |
923072b8 | 70 | EntryPointType::None |
970d7e83 | 71 | } |
e9174d1e SL |
72 | } |
73 | } | |
74 | ||
29967ef6 XL |
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)) | |
77 | .emit(); | |
78 | } | |
79 | ||
923072b8 FG |
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); | |
82 | ||
83 | match entry_point_type(ctxt, id, at_root) { | |
29967ef6 | 84 | EntryPointType::None => (), |
923072b8 FG |
85 | _ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => { |
86 | let attrs = ctxt.tcx.hir().attrs(id.hir_id()); | |
04454e1e FG |
87 | if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym::start) { |
88 | throw_attr_err(&ctxt.tcx.sess, attr.span, "start"); | |
29967ef6 | 89 | } |
04454e1e FG |
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"); | |
e9174d1e | 92 | } |
dfeec247 | 93 | } |
cdc7bbd5 | 94 | EntryPointType::MainNamed => (), |
e9174d1e | 95 | EntryPointType::OtherMain => { |
923072b8 | 96 | ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id)); |
dfeec247 | 97 | } |
923072b8 | 98 | EntryPointType::RustcMainAttr => { |
e9174d1e | 99 | if ctxt.attr_main_fn.is_none() { |
923072b8 | 100 | ctxt.attr_main_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id()))); |
e9174d1e | 101 | } else { |
dfeec247 | 102 | struct_span_err!( |
04454e1e | 103 | ctxt.tcx.sess, |
923072b8 | 104 | ctxt.tcx.def_span(id.def_id.to_def_id()), |
dfeec247 | 105 | E0137, |
923072b8 FG |
106 | "multiple functions with a `#[rustc_main]` attribute" |
107 | ) | |
108 | .span_label( | |
109 | ctxt.tcx.def_span(id.def_id.to_def_id()), | |
110 | "additional `#[rustc_main]` function", | |
dfeec247 | 111 | ) |
923072b8 | 112 | .span_label(ctxt.attr_main_fn.unwrap().1, "first `#[rustc_main]` function") |
5bcae85e | 113 | .emit(); |
e9174d1e | 114 | } |
dfeec247 | 115 | } |
e9174d1e SL |
116 | EntryPointType::Start => { |
117 | if ctxt.start_fn.is_none() { | |
923072b8 | 118 | ctxt.start_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id()))); |
e9174d1e | 119 | } else { |
923072b8 FG |
120 | struct_span_err!( |
121 | ctxt.tcx.sess, | |
122 | ctxt.tcx.def_span(id.def_id.to_def_id()), | |
123 | E0138, | |
124 | "multiple `start` functions" | |
125 | ) | |
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") | |
128 | .emit(); | |
e9174d1e | 129 | } |
9fa01778 | 130 | } |
970d7e83 | 131 | } |
970d7e83 LB |
132 | } |
133 | ||
04454e1e | 134 | fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> { |
5099ac24 FG |
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)) | |
970d7e83 | 139 | } else { |
5e7ed085 FG |
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(_))) { | |
143 | tcx.sess | |
144 | .struct_span_err( | |
145 | tcx.def_span(def_id), | |
146 | "the `main` function cannot be declared in an `extern` block", | |
136023e0 XL |
147 | ) |
148 | .emit(); | |
5e7ed085 | 149 | return None; |
136023e0 | 150 | } |
5e7ed085 FG |
151 | |
152 | if main_def.is_import && !tcx.features().imported_main { | |
153 | let span = main_def.span; | |
154 | feature_err( | |
155 | &tcx.sess.parse_sess, | |
156 | sym::imported_main, | |
157 | span, | |
158 | "using an imported function as entry point `main` is experimental", | |
159 | ) | |
160 | .emit(); | |
161 | } | |
162 | return Some((def_id, EntryFnType::Main)); | |
136023e0 | 163 | } |
e1599b0c | 164 | no_main_err(tcx, visitor); |
9fa01778 | 165 | None |
970d7e83 LB |
166 | } |
167 | } | |
9fa01778 | 168 | |
04454e1e | 169 | fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) { |
c295e0f8 | 170 | let sp = tcx.def_span(CRATE_DEF_ID); |
e74abb32 XL |
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"); | |
175 | return; | |
176 | } | |
177 | ||
e1599b0c | 178 | // There is no main function. |
dfeec247 XL |
179 | let mut err = struct_span_err!( |
180 | tcx.sess, | |
181 | DUMMY_SP, | |
182 | E0601, | |
183 | "`main` function not found in crate `{}`", | |
184 | tcx.crate_name(LOCAL_CRATE) | |
185 | ); | |
e1599b0c XL |
186 | let filename = &tcx.sess.local_crate_source_file; |
187 | let note = if !visitor.non_main_fns.is_empty() { | |
5099ac24 | 188 | for &span in &visitor.non_main_fns { |
e1599b0c XL |
189 | err.span_note(span, "here is a function named `main`"); |
190 | } | |
191 | err.note("you have one or more functions named `main` not defined at the crate level"); | |
cdc7bbd5 | 192 | err.help("consider moving the `main` function definitions"); |
e1599b0c | 193 | // There were some functions named `main` though. Try to give the user a hint. |
dfeec247 XL |
194 | format!( |
195 | "the main function must be defined at the crate level{}", | |
196 | filename.as_ref().map(|f| format!(" (in `{}`)", f.display())).unwrap_or_default() | |
197 | ) | |
e1599b0c XL |
198 | } else if let Some(filename) = filename { |
199 | format!("consider adding a `main` function to `{}`", filename.display()) | |
200 | } else { | |
201 | String::from("consider adding a `main` function at the crate level") | |
202 | }; | |
e1599b0c XL |
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 | |
205 | // note instead. | |
5e7ed085 FG |
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); | |
e1599b0c XL |
209 | } else { |
210 | err.note(¬e); | |
211 | } | |
cdc7bbd5 | 212 | |
5e7ed085 FG |
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"); | |
cdc7bbd5 XL |
216 | } |
217 | ||
e1599b0c | 218 | if tcx.sess.teach(&err.get_code().unwrap()) { |
dfeec247 XL |
219 | err.note( |
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/", | |
222 | ); | |
e1599b0c XL |
223 | } |
224 | err.emit(); | |
225 | } | |
226 | ||
f035d41b | 227 | pub fn provide(providers: &mut Providers) { |
dfeec247 | 228 | *providers = Providers { entry_fn, ..*providers }; |
9fa01778 | 229 | } |