]>
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 | ||
7453a54e | 12 | use dep_graph::DepNode; |
54a0048b SL |
13 | use hir::map as ast_map; |
14 | use hir::def_id::{CRATE_DEF_INDEX}; | |
1a4d82fc | 15 | use session::{config, Session}; |
e9174d1e | 16 | use syntax::ast::NodeId; |
b039eaaf | 17 | use syntax::attr; |
1a4d82fc | 18 | use syntax::codemap::Span; |
e9174d1e | 19 | use syntax::entry::EntryPointType; |
54a0048b SL |
20 | use hir::{Item, ItemFn}; |
21 | use hir::intravisit::Visitor; | |
970d7e83 | 22 | |
92a42be0 | 23 | struct EntryContext<'a, 'tcx: 'a> { |
1a4d82fc | 24 | session: &'a Session, |
970d7e83 | 25 | |
92a42be0 | 26 | map: &'a ast_map::Map<'tcx>, |
970d7e83 LB |
27 | |
28 | // The top-level function called 'main' | |
1a4d82fc | 29 | main_fn: Option<(NodeId, Span)>, |
970d7e83 LB |
30 | |
31 | // The function that has attribute named 'main' | |
1a4d82fc | 32 | attr_main_fn: Option<(NodeId, Span)>, |
970d7e83 LB |
33 | |
34 | // The function that has the attribute 'start' on it | |
1a4d82fc | 35 | start_fn: Option<(NodeId, Span)>, |
970d7e83 LB |
36 | |
37 | // The functions that one might think are 'main' but aren't, e.g. | |
38 | // main functions not defined at the top level. For diagnostics. | |
1a4d82fc | 39 | non_main_fns: Vec<(NodeId, Span)> , |
970d7e83 LB |
40 | } |
41 | ||
92a42be0 SL |
42 | impl<'a, 'tcx> Visitor<'tcx> for EntryContext<'a, 'tcx> { |
43 | fn visit_item(&mut self, item: &'tcx Item) { | |
44 | let def_id = self.map.local_def_id(item.id); | |
45 | let def_key = self.map.def_key(def_id); | |
46 | let at_root = def_key.parent == Some(CRATE_DEF_INDEX); | |
47 | find_item(item, self, at_root); | |
1a4d82fc JJ |
48 | } |
49 | } | |
970d7e83 | 50 | |
1a4d82fc | 51 | pub fn find_entry_point(session: &Session, ast_map: &ast_map::Map) { |
7453a54e SL |
52 | let _task = ast_map.dep_graph.in_task(DepNode::EntryPoint); |
53 | ||
1a4d82fc JJ |
54 | let any_exe = session.crate_types.borrow().iter().any(|ty| { |
55 | *ty == config::CrateTypeExecutable | |
56 | }); | |
57 | if !any_exe { | |
970d7e83 | 58 | // No need to find a main function |
1a4d82fc | 59 | return |
970d7e83 LB |
60 | } |
61 | ||
1a4d82fc | 62 | // If the user wants no main function at all, then stop here. |
85aaf69f | 63 | if attr::contains_name(&ast_map.krate().attrs, "no_main") { |
1a4d82fc JJ |
64 | session.entry_type.set(Some(config::EntryNone)); |
65 | return | |
66 | } | |
67 | ||
68 | let mut ctxt = EntryContext { | |
970d7e83 | 69 | session: session, |
92a42be0 | 70 | map: ast_map, |
970d7e83 LB |
71 | main_fn: None, |
72 | attr_main_fn: None, | |
73 | start_fn: None, | |
1a4d82fc | 74 | non_main_fns: Vec::new(), |
970d7e83 LB |
75 | }; |
76 | ||
92a42be0 | 77 | ast_map.krate().visit_all_items(&mut ctxt); |
970d7e83 | 78 | |
1a4d82fc | 79 | configure_main(&mut ctxt); |
970d7e83 LB |
80 | } |
81 | ||
e9174d1e SL |
82 | // Beware, this is duplicated in libsyntax/entry.rs, make sure to keep |
83 | // them in sync. | |
92a42be0 | 84 | fn entry_point_type(item: &Item, at_root: bool) -> EntryPointType { |
970d7e83 | 85 | match item.node { |
1a4d82fc | 86 | ItemFn(..) => { |
85aaf69f | 87 | if attr::contains_name(&item.attrs, "start") { |
e9174d1e SL |
88 | EntryPointType::Start |
89 | } else if attr::contains_name(&item.attrs, "main") { | |
90 | EntryPointType::MainAttr | |
b039eaaf | 91 | } else if item.name.as_str() == "main" { |
92a42be0 | 92 | if at_root { |
e9174d1e SL |
93 | // This is a top-level function so can be 'main' |
94 | EntryPointType::MainNamed | |
970d7e83 | 95 | } else { |
e9174d1e | 96 | EntryPointType::OtherMain |
970d7e83 | 97 | } |
e9174d1e SL |
98 | } else { |
99 | EntryPointType::None | |
970d7e83 LB |
100 | } |
101 | } | |
e9174d1e SL |
102 | _ => EntryPointType::None, |
103 | } | |
104 | } | |
105 | ||
106 | ||
92a42be0 SL |
107 | fn find_item(item: &Item, ctxt: &mut EntryContext, at_root: bool) { |
108 | match entry_point_type(item, at_root) { | |
e9174d1e SL |
109 | EntryPointType::MainNamed => { |
110 | if ctxt.main_fn.is_none() { | |
111 | ctxt.main_fn = Some((item.id, item.span)); | |
112 | } else { | |
113 | span_err!(ctxt.session, item.span, E0136, | |
114 | "multiple 'main' functions"); | |
115 | } | |
116 | }, | |
117 | EntryPointType::OtherMain => { | |
118 | ctxt.non_main_fns.push((item.id, item.span)); | |
119 | }, | |
120 | EntryPointType::MainAttr => { | |
121 | if ctxt.attr_main_fn.is_none() { | |
122 | ctxt.attr_main_fn = Some((item.id, item.span)); | |
123 | } else { | |
124 | span_err!(ctxt.session, item.span, E0137, | |
125 | "multiple functions with a #[main] attribute"); | |
126 | } | |
127 | }, | |
128 | EntryPointType::Start => { | |
129 | if ctxt.start_fn.is_none() { | |
130 | ctxt.start_fn = Some((item.id, item.span)); | |
131 | } else { | |
132 | span_err!(ctxt.session, item.span, E0138, | |
133 | "multiple 'start' functions"); | |
134 | } | |
135 | }, | |
136 | EntryPointType::None => () | |
970d7e83 | 137 | } |
970d7e83 LB |
138 | } |
139 | ||
1a4d82fc | 140 | fn configure_main(this: &mut EntryContext) { |
970d7e83 | 141 | if this.start_fn.is_some() { |
1a4d82fc JJ |
142 | *this.session.entry_fn.borrow_mut() = this.start_fn; |
143 | this.session.entry_type.set(Some(config::EntryStart)); | |
970d7e83 | 144 | } else if this.attr_main_fn.is_some() { |
1a4d82fc JJ |
145 | *this.session.entry_fn.borrow_mut() = this.attr_main_fn; |
146 | this.session.entry_type.set(Some(config::EntryMain)); | |
970d7e83 | 147 | } else if this.main_fn.is_some() { |
1a4d82fc JJ |
148 | *this.session.entry_fn.borrow_mut() = this.main_fn; |
149 | this.session.entry_type.set(Some(config::EntryMain)); | |
970d7e83 | 150 | } else { |
1a4d82fc | 151 | // No main function |
9cc50fc6 | 152 | let mut err = this.session.struct_err("main function not found"); |
1a4d82fc JJ |
153 | if !this.non_main_fns.is_empty() { |
154 | // There were some functions named 'main' though. Try to give the user a hint. | |
9cc50fc6 SL |
155 | err.note("the main function must be defined at the crate level \ |
156 | but you have one or more functions named 'main' that are not \ | |
157 | defined at the crate level. Either move the definition or \ | |
158 | attach the `#[main]` attribute to override this behavior."); | |
85aaf69f | 159 | for &(_, span) in &this.non_main_fns { |
9cc50fc6 | 160 | err.span_note(span, "here is a function named 'main'"); |
970d7e83 | 161 | } |
9cc50fc6 | 162 | err.emit(); |
970d7e83 | 163 | this.session.abort_if_errors(); |
9cc50fc6 SL |
164 | } else { |
165 | err.emit(); | |
970d7e83 LB |
166 | } |
167 | } | |
168 | } |