Skip to content

Commit 98f5c6d

Browse files
committed
rustc: Only accept main functions at the crate level. #4433
1 parent be8dc61 commit 98f5c6d

File tree

8 files changed

+93
-63
lines changed

8 files changed

+93
-63
lines changed

src/librustc/driver/driver.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ pub fn compile_rest(sess: Session,
225225
time(time_passes, ~"resolution", ||
226226
middle::resolve::resolve_crate(sess, lang_items, crate));
227227

228-
time(time_passes, ~"looking for entry point", || middle::entry::find_entry_point(sess, crate));
228+
time(time_passes, ~"looking for entry point",
229+
|| middle::entry::find_entry_point(sess, crate, ast_map));
229230

230231
let freevars = time(time_passes, ~"freevar finding", ||
231232
freevars::annotate_freevars(def_map, crate));

src/librustc/middle/entry.rs

+77-52
Original file line numberDiff line numberDiff line change
@@ -15,73 +15,97 @@ use syntax::ast::{crate, node_id, item, item_fn};
1515
use syntax::codemap::span;
1616
use syntax::visit::{default_visitor, mk_vt, vt, Visitor, visit_crate, visit_item};
1717
use syntax::attr::{attrs_contains_name};
18+
use syntax::ast_map;
19+
use core::util;
1820

1921
struct EntryContext {
2022
session: Session,
2123

24+
ast_map: ast_map::map,
25+
26+
// The top-level function called 'main'
27+
main_fn: Option<(node_id, span)>,
28+
2229
// The function that has attribute named 'main'
2330
attr_main_fn: Option<(node_id, span)>,
2431

25-
// The functions that could be main functions
26-
main_fns: ~[Option<(node_id, span)>],
27-
2832
// The function that has the attribute 'start' on it
2933
start_fn: Option<(node_id, span)>,
34+
35+
// The functions that one might think are 'main' but aren't, e.g.
36+
// main functions not defined at the top level. For diagnostics.
37+
non_main_fns: ~[(node_id, span)],
3038
}
3139

3240
type EntryVisitor = vt<@mut EntryContext>;
3341

34-
pub fn find_entry_point(session: Session, crate: @crate) {
42+
pub fn find_entry_point(session: Session, crate: @crate, ast_map: ast_map::map) {
43+
44+
// FIXME #4404 android JNI hacks
45+
if *session.building_library ||
46+
session.targ_cfg.os == session::os_android {
47+
// No need to find a main function
48+
return;
49+
}
3550

3651
let ctxt = @mut EntryContext {
3752
session: session,
53+
ast_map: ast_map,
54+
main_fn: None,
3855
attr_main_fn: None,
39-
main_fns: ~[],
4056
start_fn: None,
57+
non_main_fns: ~[],
4158
};
4259

4360
visit_crate(crate, ctxt, mk_vt(@Visitor {
4461
visit_item: |item, ctxt, visitor| find_item(item, ctxt, visitor),
4562
.. *default_visitor()
4663
}));
4764

48-
check_duplicate_main(ctxt);
65+
configure_main(ctxt);
4966
}
5067

5168
fn find_item(item: @item, ctxt: @mut EntryContext, visitor: EntryVisitor) {
5269
match item.node {
5370
item_fn(*) => {
54-
// If this is the main function, we must record it in the
55-
// session.
56-
57-
// FIXME #4404 android JNI hacks
58-
if !*ctxt.session.building_library ||
59-
ctxt.session.targ_cfg.os == session::os_android {
60-
61-
if ctxt.attr_main_fn.is_none() &&
62-
item.ident == special_idents::main {
63-
64-
ctxt.main_fns.push(Some((item.id, item.span)));
71+
if item.ident == special_idents::main {
72+
match ctxt.ast_map.find(&item.id) {
73+
Some(&ast_map::node_item(_, path)) => {
74+
if path.len() == 0 {
75+
// This is a top-level function so can be 'main'
76+
if ctxt.main_fn.is_none() {
77+
ctxt.main_fn = Some((item.id, item.span));
78+
} else {
79+
ctxt.session.span_err(
80+
item.span,
81+
~"multiple 'main' functions");
82+
}
83+
} else {
84+
// This isn't main
85+
ctxt.non_main_fns.push((item.id, item.span));
86+
}
87+
}
88+
_ => util::unreachable()
6589
}
90+
}
6691

67-
if attrs_contains_name(item.attrs, ~"main") {
68-
if ctxt.attr_main_fn.is_none() {
69-
ctxt.attr_main_fn = Some((item.id, item.span));
70-
} else {
71-
ctxt.session.span_err(
72-
item.span,
73-
~"multiple 'main' functions");
74-
}
92+
if attrs_contains_name(item.attrs, ~"main") {
93+
if ctxt.attr_main_fn.is_none() {
94+
ctxt.attr_main_fn = Some((item.id, item.span));
95+
} else {
96+
ctxt.session.span_err(
97+
item.span,
98+
~"multiple 'main' functions");
7599
}
100+
}
76101

77-
if attrs_contains_name(item.attrs, ~"start") {
78-
if ctxt.start_fn.is_none() {
79-
ctxt.start_fn = Some((item.id, item.span));
80-
} else {
81-
ctxt.session.span_err(
82-
item.span,
83-
~"multiple 'start' functions");
84-
}
102+
if attrs_contains_name(item.attrs, ~"start") {
103+
if ctxt.start_fn.is_none() {
104+
ctxt.start_fn = Some((item.id, item.span));
105+
} else {
106+
ctxt.session.span_err(
107+
item.span,
108+
~"multiple 'start' functions");
85109
}
86110
}
87111
}
@@ -91,29 +115,30 @@ fn find_item(item: @item, ctxt: @mut EntryContext, visitor: EntryVisitor) {
91115
visit_item(item, ctxt, visitor);
92116
}
93117

94-
// main function checking
95-
//
96-
// be sure that there is only one main function
97-
fn check_duplicate_main(ctxt: @mut EntryContext) {
118+
fn configure_main(ctxt: @mut EntryContext) {
98119
let this = &mut *ctxt;
99-
if this.attr_main_fn.is_none() && this.start_fn.is_none() {
100-
if this.main_fns.len() >= 1u {
101-
let mut i = 1u;
102-
while i < this.main_fns.len() {
103-
let (_, dup_main_span) = this.main_fns[i].unwrap();
104-
this.session.span_err(
105-
dup_main_span,
106-
~"multiple 'main' functions");
107-
i += 1;
108-
}
109-
*this.session.entry_fn = this.main_fns[0];
110-
*this.session.entry_type = Some(session::EntryMain);
111-
}
112-
} else if !this.start_fn.is_none() {
120+
if this.start_fn.is_some() {
113121
*this.session.entry_fn = this.start_fn;
114122
*this.session.entry_type = Some(session::EntryStart);
115-
} else {
123+
} else if this.attr_main_fn.is_some() {
116124
*this.session.entry_fn = this.attr_main_fn;
117125
*this.session.entry_type = Some(session::EntryMain);
126+
} else if this.main_fn.is_some() {
127+
*this.session.entry_fn = this.main_fn;
128+
*this.session.entry_type = Some(session::EntryMain);
129+
} else {
130+
// No main function
131+
this.session.err(~"main function not found");
132+
if !this.non_main_fns.is_empty() {
133+
// There were some functions named 'main' though. Try to give the user a hint.
134+
this.session.note(~"the main function must be defined at the crate level \
135+
but you have one or more functions named 'main' that are not \
136+
defined at the crate level. Either move the definition or attach \
137+
the `#[main]` attribute to override this behavior.");
138+
for this.non_main_fns.each |&(_, span)| {
139+
this.session.span_note(span, ~"here is a function named 'main'");
140+
}
141+
}
142+
this.session.abort_if_errors();
118143
}
119144
}

src/librustc/middle/typeck/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ fn check_for_entry_fn(ccx: @mut CrateCtxt) {
387387
Some(session::EntryStart) => check_start_fn_ty(ccx, id, sp),
388388
None => tcx.sess.bug(~"entry function without a type")
389389
},
390-
None => tcx.sess.err(~"entry function not found")
390+
None => tcx.sess.bug(~"type checking without entry function")
391391
}
392392
}
393393
}

src/test/compile-fail/elided-test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// error-pattern: entry function not found
11+
// error-pattern: main function not found
1212

1313
// Since we're not compiling a test runner this function should be elided
1414
// and the build will fail because main doesn't exist

src/test/compile-fail/multiple-main.rs renamed to src/test/compile-fail/main-wrong-location.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
fn main() {
12-
}
13-
14-
mod foo {
15-
fn main() { //~ ERROR multiple 'main' functions
16-
}
17-
}
11+
mod m {
12+
// An inferred main entry point (that doesn't use #[main])
13+
// must appear at the top of the crate
14+
fn main() { } //~ NOTE here is a function named 'main'
15+
}

src/test/compile-fail/missing-main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// error-pattern:entry function not found
11+
// error-pattern:main function not found
1212
fn mian() { }

src/test/run-pass/dupe-first-attr.rc

+2
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ mod hello;
2525

2626
#[cfg(target_os = "android")]
2727
mod hello;
28+
29+
fn main() { }

src/test/run-pass/intrinsic-alignment.rs

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod rusti {
2222
#[cfg(target_os = "macos")]
2323
#[cfg(target_os = "freebsd")]
2424
mod m {
25+
#[main]
2526
#[cfg(target_arch = "x86")]
2627
pub fn main() {
2728
unsafe {
@@ -30,6 +31,7 @@ mod m {
3031
}
3132
}
3233

34+
#[main]
3335
#[cfg(target_arch = "x86_64")]
3436
pub fn main() {
3537
unsafe {
@@ -41,6 +43,7 @@ mod m {
4143

4244
#[cfg(target_os = "win32")]
4345
mod m {
46+
#[main]
4447
#[cfg(target_arch = "x86")]
4548
pub fn main() {
4649
unsafe {
@@ -52,6 +55,7 @@ mod m {
5255

5356
#[cfg(target_os = "android")]
5457
mod m {
58+
#[main]
5559
#[cfg(target_arch = "arm")]
5660
pub fn main() {
5761
unsafe {

0 commit comments

Comments
 (0)