]>
Commit | Line | Data |
---|---|---|
970d7e83 LB |
1 | // Copyright 2012 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | ||
32a655c1 | 12 | use hir::map as hir_map; |
54a0048b | 13 | use hir::def_id::{CRATE_DEF_INDEX}; |
1a4d82fc | 14 | use session::{config, Session}; |
e9174d1e | 15 | use syntax::ast::NodeId; |
b039eaaf | 16 | use syntax::attr; |
e9174d1e | 17 | use syntax::entry::EntryPointType; |
3157f602 | 18 | use syntax_pos::Span; |
32a655c1 | 19 | use hir::{Item, ItemFn, ImplItem, TraitItem}; |
476ff2be | 20 | use hir::itemlikevisit::ItemLikeVisitor; |
970d7e83 | 21 | |
92a42be0 | 22 | struct EntryContext<'a, 'tcx: 'a> { |
1a4d82fc | 23 | session: &'a Session, |
970d7e83 | 24 | |
32a655c1 | 25 | map: &'a hir_map::Map<'tcx>, |
970d7e83 LB |
26 | |
27 | // The top-level function called 'main' | |
1a4d82fc | 28 | main_fn: Option<(NodeId, Span)>, |
970d7e83 LB |
29 | |
30 | // The function that has attribute named 'main' | |
1a4d82fc | 31 | attr_main_fn: Option<(NodeId, Span)>, |
970d7e83 LB |
32 | |
33 | // The function that has the attribute 'start' on it | |
1a4d82fc | 34 | start_fn: Option<(NodeId, Span)>, |
970d7e83 LB |
35 | |
36 | // The functions that one might think are 'main' but aren't, e.g. | |
37 | // main functions not defined at the top level. For diagnostics. | |
1a4d82fc | 38 | non_main_fns: Vec<(NodeId, Span)> , |
970d7e83 LB |
39 | } |
40 | ||
476ff2be | 41 | impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> { |
92a42be0 SL |
42 | fn visit_item(&mut self, item: &'tcx Item) { |
43 | let def_id = self.map.local_def_id(item.id); | |
44 | let def_key = self.map.def_key(def_id); | |
45 | let at_root = def_key.parent == Some(CRATE_DEF_INDEX); | |
46 | find_item(item, self, at_root); | |
1a4d82fc | 47 | } |
476ff2be | 48 | |
32a655c1 SL |
49 | fn visit_trait_item(&mut self, _trait_item: &'tcx TraitItem) { |
50 | // entry fn is never a trait item | |
51 | } | |
476ff2be SL |
52 | |
53 | fn visit_impl_item(&mut self, _impl_item: &'tcx ImplItem) { | |
54 | // entry fn is never an impl item | |
55 | } | |
1a4d82fc | 56 | } |
970d7e83 | 57 | |
0531ce1d XL |
58 | pub fn find_entry_point(session: &Session, |
59 | hir_map: &hir_map::Map, | |
60 | crate_name: &str) { | |
1a4d82fc JJ |
61 | let any_exe = session.crate_types.borrow().iter().any(|ty| { |
62 | *ty == config::CrateTypeExecutable | |
63 | }); | |
64 | if !any_exe { | |
970d7e83 | 65 | // No need to find a main function |
83c7162d | 66 | session.entry_fn.set(None); |
1a4d82fc | 67 | return |
970d7e83 LB |
68 | } |
69 | ||
1a4d82fc | 70 | // If the user wants no main function at all, then stop here. |
32a655c1 | 71 | if attr::contains_name(&hir_map.krate().attrs, "no_main") { |
83c7162d | 72 | session.entry_fn.set(None); |
1a4d82fc JJ |
73 | return |
74 | } | |
75 | ||
76 | let mut ctxt = EntryContext { | |
041b39d2 | 77 | session, |
32a655c1 | 78 | map: hir_map, |
970d7e83 LB |
79 | main_fn: None, |
80 | attr_main_fn: None, | |
81 | start_fn: None, | |
1a4d82fc | 82 | non_main_fns: Vec::new(), |
970d7e83 LB |
83 | }; |
84 | ||
32a655c1 | 85 | hir_map.krate().visit_all_item_likes(&mut ctxt); |
970d7e83 | 86 | |
0531ce1d | 87 | configure_main(&mut ctxt, crate_name); |
970d7e83 LB |
88 | } |
89 | ||
e9174d1e SL |
90 | // Beware, this is duplicated in libsyntax/entry.rs, make sure to keep |
91 | // them in sync. | |
92a42be0 | 92 | fn entry_point_type(item: &Item, at_root: bool) -> EntryPointType { |
970d7e83 | 93 | match item.node { |
1a4d82fc | 94 | ItemFn(..) => { |
85aaf69f | 95 | if attr::contains_name(&item.attrs, "start") { |
e9174d1e SL |
96 | EntryPointType::Start |
97 | } else if attr::contains_name(&item.attrs, "main") { | |
98 | EntryPointType::MainAttr | |
476ff2be | 99 | } else if item.name == "main" { |
92a42be0 | 100 | if at_root { |
e9174d1e SL |
101 | // This is a top-level function so can be 'main' |
102 | EntryPointType::MainNamed | |
970d7e83 | 103 | } else { |
e9174d1e | 104 | EntryPointType::OtherMain |
970d7e83 | 105 | } |
e9174d1e SL |
106 | } else { |
107 | EntryPointType::None | |
970d7e83 LB |
108 | } |
109 | } | |
e9174d1e SL |
110 | _ => EntryPointType::None, |
111 | } | |
112 | } | |
113 | ||
114 | ||
92a42be0 SL |
115 | fn find_item(item: &Item, ctxt: &mut EntryContext, at_root: bool) { |
116 | match entry_point_type(item, at_root) { | |
e9174d1e SL |
117 | EntryPointType::MainNamed => { |
118 | if ctxt.main_fn.is_none() { | |
119 | ctxt.main_fn = Some((item.id, item.span)); | |
120 | } else { | |
121 | span_err!(ctxt.session, item.span, E0136, | |
122 | "multiple 'main' functions"); | |
123 | } | |
124 | }, | |
125 | EntryPointType::OtherMain => { | |
126 | ctxt.non_main_fns.push((item.id, item.span)); | |
127 | }, | |
128 | EntryPointType::MainAttr => { | |
129 | if ctxt.attr_main_fn.is_none() { | |
130 | ctxt.attr_main_fn = Some((item.id, item.span)); | |
131 | } else { | |
5bcae85e SL |
132 | struct_span_err!(ctxt.session, item.span, E0137, |
133 | "multiple functions with a #[main] attribute") | |
7cac9316 XL |
134 | .span_label(item.span, "additional #[main] function") |
135 | .span_label(ctxt.attr_main_fn.unwrap().1, "first #[main] function") | |
5bcae85e | 136 | .emit(); |
e9174d1e SL |
137 | } |
138 | }, | |
139 | EntryPointType::Start => { | |
140 | if ctxt.start_fn.is_none() { | |
141 | ctxt.start_fn = Some((item.id, item.span)); | |
142 | } else { | |
5bcae85e SL |
143 | struct_span_err!( |
144 | ctxt.session, item.span, E0138, | |
145 | "multiple 'start' functions") | |
146 | .span_label(ctxt.start_fn.unwrap().1, | |
7cac9316 XL |
147 | "previous `start` function here") |
148 | .span_label(item.span, "multiple `start` functions") | |
5bcae85e | 149 | .emit(); |
e9174d1e SL |
150 | } |
151 | }, | |
152 | EntryPointType::None => () | |
970d7e83 | 153 | } |
970d7e83 LB |
154 | } |
155 | ||
0531ce1d | 156 | fn configure_main(this: &mut EntryContext, crate_name: &str) { |
83c7162d XL |
157 | if let Some((node_id, span)) = this.start_fn { |
158 | this.session.entry_fn.set(Some((node_id, span, config::EntryStart))); | |
159 | } else if let Some((node_id, span)) = this.attr_main_fn { | |
160 | this.session.entry_fn.set(Some((node_id, span, config::EntryMain))); | |
161 | } else if let Some((node_id, span)) = this.main_fn { | |
162 | this.session.entry_fn.set(Some((node_id, span, config::EntryMain))); | |
970d7e83 | 163 | } else { |
1a4d82fc | 164 | // No main function |
83c7162d | 165 | this.session.entry_fn.set(None); |
0531ce1d XL |
166 | let mut err = struct_err!(this.session, E0601, |
167 | "`main` function not found in crate `{}`", crate_name); | |
1a4d82fc JJ |
168 | if !this.non_main_fns.is_empty() { |
169 | // There were some functions named 'main' though. Try to give the user a hint. | |
9cc50fc6 SL |
170 | err.note("the main function must be defined at the crate level \ |
171 | but you have one or more functions named 'main' that are not \ | |
172 | defined at the crate level. Either move the definition or \ | |
173 | attach the `#[main]` attribute to override this behavior."); | |
85aaf69f | 174 | for &(_, span) in &this.non_main_fns { |
9cc50fc6 | 175 | err.span_note(span, "here is a function named 'main'"); |
970d7e83 | 176 | } |
9cc50fc6 | 177 | err.emit(); |
970d7e83 | 178 | this.session.abort_if_errors(); |
9cc50fc6 | 179 | } else { |
0531ce1d XL |
180 | if let Some(ref filename) = this.session.local_crate_source_file { |
181 | err.note(&format!("consider adding a `main` function to `{}`", filename.display())); | |
182 | } | |
2c00a5a8 XL |
183 | if this.session.teach(&err.get_code().unwrap()) { |
184 | err.note("If you don't know the basics of Rust, you can go look to the Rust Book \ | |
185 | to get started: https://doc.rust-lang.org/book/"); | |
186 | } | |
9cc50fc6 | 187 | err.emit(); |
970d7e83 LB |
188 | } |
189 | } | |
190 | } |