]>
Commit | Line | Data |
---|---|---|
353b0b11 | 1 | use rustc_ast::attr; |
f2b60f7d | 2 | use rustc_ast::entry::EntryPointType; |
2b03887a | 3 | use rustc_errors::error_code; |
923072b8 | 4 | use rustc_hir::def::DefKind; |
04454e1e | 5 | use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; |
923072b8 | 6 | use rustc_hir::{ItemId, Node, CRATE_HIR_ID}; |
49aad941 | 7 | use rustc_middle::query::Providers; |
353b0b11 | 8 | use rustc_middle::ty::TyCtxt; |
f2b60f7d | 9 | use rustc_session::config::{sigpipe, CrateType, EntryFnType}; |
cdc7bbd5 | 10 | use rustc_session::parse::feature_err; |
dfeec247 | 11 | use rustc_span::symbol::sym; |
2b03887a FG |
12 | use rustc_span::{Span, Symbol}; |
13 | ||
14 | use crate::errors::{ | |
15 | AttrOnlyInFunctions, AttrOnlyOnMain, AttrOnlyOnRootMain, ExternMain, MultipleRustcMain, | |
16 | MultipleStartFunctions, NoMainErr, UnixSigpipeValues, | |
17 | }; | |
60c5eb7d | 18 | |
04454e1e FG |
19 | struct EntryContext<'tcx> { |
20 | tcx: TyCtxt<'tcx>, | |
970d7e83 | 21 | |
e1599b0c | 22 | /// The function that has attribute named `main`. |
5099ac24 | 23 | attr_main_fn: Option<(LocalDefId, Span)>, |
970d7e83 | 24 | |
e1599b0c | 25 | /// The function that has the attribute 'start' on it. |
5099ac24 | 26 | start_fn: Option<(LocalDefId, 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. | |
5099ac24 | 30 | non_main_fns: Vec<Span>, |
970d7e83 LB |
31 | } |
32 | ||
17df50a5 | 33 | fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> { |
f9f354fc | 34 | let any_exe = tcx.sess.crate_types().iter().any(|ty| *ty == CrateType::Executable); |
1a4d82fc | 35 | if !any_exe { |
e1599b0c | 36 | // No need to find a main function. |
9fa01778 | 37 | return None; |
970d7e83 LB |
38 | } |
39 | ||
1a4d82fc | 40 | // If the user wants no main function at all, then stop here. |
353b0b11 | 41 | if attr::contains_name(&tcx.hir().attrs(CRATE_HIR_ID), sym::no_main) { |
9fa01778 | 42 | return None; |
1a4d82fc JJ |
43 | } |
44 | ||
04454e1e FG |
45 | let mut ctxt = |
46 | EntryContext { tcx, attr_main_fn: None, start_fn: None, non_main_fns: Vec::new() }; | |
970d7e83 | 47 | |
923072b8 FG |
48 | for id in tcx.hir().items() { |
49 | find_item(id, &mut ctxt); | |
50 | } | |
970d7e83 | 51 | |
9fa01778 | 52 | configure_main(tcx, &ctxt) |
970d7e83 LB |
53 | } |
54 | ||
3dfed10e XL |
55 | // Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs` |
56 | // (with `ast::Item`), so make sure to keep them in sync. | |
923072b8 FG |
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()); | |
353b0b11 | 61 | if attr::contains_name(attrs, sym::start) { |
29967ef6 | 62 | EntryPointType::Start |
353b0b11 | 63 | } else if attr::contains_name(attrs, sym::rustc_main) { |
923072b8 FG |
64 | EntryPointType::RustcMainAttr |
65 | } else { | |
2b03887a | 66 | if let Some(name) = ctxt.tcx.opt_item_name(id.owner_id.to_def_id()) |
923072b8 FG |
67 | && name == sym::main { |
68 | if at_root { | |
69 | // This is a top-level function so can be `main`. | |
70 | EntryPointType::MainNamed | |
71 | } else { | |
72 | EntryPointType::OtherMain | |
73 | } | |
29967ef6 | 74 | } else { |
923072b8 | 75 | EntryPointType::None |
970d7e83 | 76 | } |
e9174d1e SL |
77 | } |
78 | } | |
79 | ||
2b03887a | 80 | fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Option<Span> { |
f2b60f7d | 81 | let attrs = ctxt.tcx.hir().attrs(id.hir_id()); |
353b0b11 | 82 | attr::find_by_name(attrs, sym).map(|attr| attr.span) |
29967ef6 XL |
83 | } |
84 | ||
923072b8 | 85 | fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) { |
2b03887a | 86 | let at_root = ctxt.tcx.opt_local_parent(id.owner_id.def_id) == Some(CRATE_DEF_ID); |
923072b8 FG |
87 | |
88 | match entry_point_type(ctxt, id, at_root) { | |
f2b60f7d | 89 | EntryPointType::None => { |
2b03887a FG |
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 }); | |
92 | } | |
f2b60f7d | 93 | } |
2b03887a FG |
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 }); | |
98 | } | |
99 | } | |
dfeec247 | 100 | } |
cdc7bbd5 | 101 | EntryPointType::MainNamed => (), |
e9174d1e | 102 | EntryPointType::OtherMain => { |
2b03887a FG |
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 }); | |
105 | } | |
106 | ctxt.non_main_fns.push(ctxt.tcx.def_span(id.owner_id)); | |
dfeec247 | 107 | } |
923072b8 | 108 | EntryPointType::RustcMainAttr => { |
e9174d1e | 109 | if ctxt.attr_main_fn.is_none() { |
2b03887a | 110 | ctxt.attr_main_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id))); |
e9174d1e | 111 | } else { |
2b03887a FG |
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()), | |
116 | }); | |
e9174d1e | 117 | } |
dfeec247 | 118 | } |
e9174d1e | 119 | EntryPointType::Start => { |
2b03887a FG |
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 }); | |
122 | } | |
e9174d1e | 123 | if ctxt.start_fn.is_none() { |
2b03887a | 124 | ctxt.start_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id))); |
e9174d1e | 125 | } else { |
2b03887a FG |
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, | |
130 | }); | |
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)) | |
f2b60f7d FG |
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) })) | |
970d7e83 | 142 | } else { |
5e7ed085 FG |
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(_))) { | |
2b03887a | 146 | tcx.sess.emit_err(ExternMain { span: tcx.def_span(def_id) }); |
5e7ed085 | 147 | return None; |
136023e0 | 148 | } |
5e7ed085 FG |
149 | |
150 | if main_def.is_import && !tcx.features().imported_main { | |
151 | let span = main_def.span; | |
152 | feature_err( | |
153 | &tcx.sess.parse_sess, | |
154 | sym::imported_main, | |
155 | span, | |
156 | "using an imported function as entry point `main` is experimental", | |
157 | ) | |
158 | .emit(); | |
159 | } | |
f2b60f7d | 160 | return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) })); |
136023e0 | 161 | } |
e1599b0c | 162 | no_main_err(tcx, visitor); |
9fa01778 | 163 | None |
970d7e83 LB |
164 | } |
165 | } | |
9fa01778 | 166 | |
f2b60f7d FG |
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, | |
173 | (_, Some(_)) => { | |
174 | // Keep going so that `fn emit_malformed_attribute()` can print | |
175 | // an excellent error message | |
176 | sigpipe::DEFAULT | |
177 | } | |
178 | _ => { | |
2b03887a | 179 | tcx.sess.emit_err(UnixSigpipeValues { span: attr.span }); |
f2b60f7d FG |
180 | sigpipe::DEFAULT |
181 | } | |
182 | } | |
183 | } else { | |
184 | sigpipe::DEFAULT | |
185 | } | |
186 | } | |
187 | ||
04454e1e | 188 | fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) { |
c295e0f8 | 189 | let sp = tcx.def_span(CRATE_DEF_ID); |
353b0b11 | 190 | if tcx.sess.parse_sess.reached_eof.load(rustc_data_structures::sync::Ordering::Relaxed) { |
e74abb32 XL |
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"); | |
194 | return; | |
195 | } | |
196 | ||
e1599b0c | 197 | // There is no main function. |
2b03887a | 198 | let mut has_filename = true; |
9c376795 | 199 | let filename = tcx.sess.local_crate_source_file().unwrap_or_else(|| { |
2b03887a FG |
200 | has_filename = false; |
201 | Default::default() | |
202 | }); | |
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); | |
e1599b0c XL |
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 | |
208 | // note instead. | |
353b0b11 | 209 | let file_empty = tcx.sess.source_map().lookup_line(sp.hi()).is_err(); |
2b03887a FG |
210 | |
211 | tcx.sess.emit_err(NoMainErr { | |
212 | sp, | |
213 | crate_name: tcx.crate_name(LOCAL_CRATE), | |
214 | has_filename, | |
215 | filename, | |
216 | file_empty, | |
217 | non_main_fns: visitor.non_main_fns.clone(), | |
218 | main_def_opt, | |
219 | add_teach_note, | |
220 | }); | |
e1599b0c XL |
221 | } |
222 | ||
f035d41b | 223 | pub fn provide(providers: &mut Providers) { |
dfeec247 | 224 | *providers = Providers { entry_fn, ..*providers }; |
9fa01778 | 225 | } |