diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index e421246da8d..a9454b595a5 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -225,7 +225,8 @@ pub fn compile_rest(sess: Session, time(time_passes, ~"resolution", || middle::resolve::resolve_crate(sess, lang_items, crate)); - time(time_passes, ~"looking for entry point", || middle::entry::find_entry_point(sess, crate)); + time(time_passes, ~"looking for entry point", + || middle::entry::find_entry_point(sess, crate, ast_map)); let freevars = time(time_passes, ~"freevar finding", || freevars::annotate_freevars(def_map, crate)); diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index 18f4a5a3072..47873b3fa1f 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -15,29 +15,46 @@ use syntax::ast::{crate, node_id, item, item_fn}; use syntax::codemap::span; use syntax::visit::{default_visitor, mk_vt, vt, Visitor, visit_crate, visit_item}; use syntax::attr::{attrs_contains_name}; +use syntax::ast_map; +use core::util; struct EntryContext { session: Session, + ast_map: ast_map::map, + + // The top-level function called 'main' + main_fn: Option<(node_id, span)>, + // The function that has attribute named 'main' attr_main_fn: Option<(node_id, span)>, - // The functions that could be main functions - main_fns: ~[Option<(node_id, span)>], - // The function that has the attribute 'start' on it start_fn: Option<(node_id, span)>, + + // The functions that one might think are 'main' but aren't, e.g. + // main functions not defined at the top level. For diagnostics. + non_main_fns: ~[(node_id, span)], } type EntryVisitor = vt<@mut EntryContext>; -pub fn find_entry_point(session: Session, crate: @crate) { +pub fn find_entry_point(session: Session, crate: @crate, ast_map: ast_map::map) { + + // FIXME #4404 android JNI hacks + if *session.building_library || + session.targ_cfg.os == session::os_android { + // No need to find a main function + return; + } let ctxt = @mut EntryContext { session: session, + ast_map: ast_map, + main_fn: None, attr_main_fn: None, - main_fns: ~[], start_fn: None, + non_main_fns: ~[], }; visit_crate(crate, ctxt, mk_vt(@Visitor { @@ -45,43 +62,50 @@ pub fn find_entry_point(session: Session, crate: @crate) { .. *default_visitor() })); - check_duplicate_main(ctxt); + configure_main(ctxt); } fn find_item(item: @item, ctxt: @mut EntryContext, visitor: EntryVisitor) { match item.node { item_fn(*) => { - // If this is the main function, we must record it in the - // session. - - // FIXME #4404 android JNI hacks - if !*ctxt.session.building_library || - ctxt.session.targ_cfg.os == session::os_android { - - if ctxt.attr_main_fn.is_none() && - item.ident == special_idents::main { - - ctxt.main_fns.push(Some((item.id, item.span))); - } - - if attrs_contains_name(item.attrs, ~"main") { - if ctxt.attr_main_fn.is_none() { - ctxt.attr_main_fn = Some((item.id, item.span)); - } else { - ctxt.session.span_err( - item.span, - ~"multiple 'main' functions"); + if item.ident == special_idents::main { + match ctxt.ast_map.find(&item.id) { + Some(&ast_map::node_item(_, path)) => { + if path.len() == 0 { + // This is a top-level function so can be 'main' + if ctxt.main_fn.is_none() { + ctxt.main_fn = Some((item.id, item.span)); + } else { + ctxt.session.span_err( + item.span, + ~"multiple 'main' functions"); + } + } else { + // This isn't main + ctxt.non_main_fns.push((item.id, item.span)); + } } + _ => util::unreachable() } + } - if attrs_contains_name(item.attrs, ~"start") { - if ctxt.start_fn.is_none() { - ctxt.start_fn = Some((item.id, item.span)); - } else { - ctxt.session.span_err( - item.span, - ~"multiple 'start' functions"); - } + if attrs_contains_name(item.attrs, ~"main") { + if ctxt.attr_main_fn.is_none() { + ctxt.attr_main_fn = Some((item.id, item.span)); + } else { + ctxt.session.span_err( + item.span, + ~"multiple 'main' functions"); + } + } + + if attrs_contains_name(item.attrs, ~"start") { + if ctxt.start_fn.is_none() { + ctxt.start_fn = Some((item.id, item.span)); + } else { + ctxt.session.span_err( + item.span, + ~"multiple 'start' functions"); } } } @@ -91,29 +115,30 @@ fn find_item(item: @item, ctxt: @mut EntryContext, visitor: EntryVisitor) { visit_item(item, ctxt, visitor); } -// main function checking -// -// be sure that there is only one main function -fn check_duplicate_main(ctxt: @mut EntryContext) { +fn configure_main(ctxt: @mut EntryContext) { let this = &mut *ctxt; - if this.attr_main_fn.is_none() && this.start_fn.is_none() { - if this.main_fns.len() >= 1u { - let mut i = 1u; - while i < this.main_fns.len() { - let (_, dup_main_span) = this.main_fns[i].unwrap(); - this.session.span_err( - dup_main_span, - ~"multiple 'main' functions"); - i += 1; - } - *this.session.entry_fn = this.main_fns[0]; - *this.session.entry_type = Some(session::EntryMain); - } - } else if !this.start_fn.is_none() { + if this.start_fn.is_some() { *this.session.entry_fn = this.start_fn; *this.session.entry_type = Some(session::EntryStart); - } else { + } else if this.attr_main_fn.is_some() { *this.session.entry_fn = this.attr_main_fn; *this.session.entry_type = Some(session::EntryMain); + } else if this.main_fn.is_some() { + *this.session.entry_fn = this.main_fn; + *this.session.entry_type = Some(session::EntryMain); + } else { + // No main function + this.session.err(~"main function not found"); + if !this.non_main_fns.is_empty() { + // There were some functions named 'main' though. Try to give the user a hint. + this.session.note(~"the main function must be defined at the crate level \ + but you have one or more functions named 'main' that are not \ + defined at the crate level. Either move the definition or attach \ + the `#[main]` attribute to override this behavior."); + for this.non_main_fns.each |&(_, span)| { + this.session.span_note(span, ~"here is a function named 'main'"); + } + } + this.session.abort_if_errors(); } } diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 14ef3feb7d5..3ea53aaa976 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -387,7 +387,7 @@ fn check_for_entry_fn(ccx: @mut CrateCtxt) { Some(session::EntryStart) => check_start_fn_ty(ccx, id, sp), None => tcx.sess.bug(~"entry function without a type") }, - None => tcx.sess.err(~"entry function not found") + None => tcx.sess.bug(~"type checking without entry function") } } } diff --git a/src/test/compile-fail/elided-test.rs b/src/test/compile-fail/elided-test.rs index eaae721e0e5..b62214b12f9 100644 --- a/src/test/compile-fail/elided-test.rs +++ b/src/test/compile-fail/elided-test.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: entry function not found +// error-pattern: main function not found // Since we're not compiling a test runner this function should be elided // and the build will fail because main doesn't exist diff --git a/src/test/compile-fail/multiple-main.rs b/src/test/compile-fail/main-wrong-location.rs similarity index 72% rename from src/test/compile-fail/multiple-main.rs rename to src/test/compile-fail/main-wrong-location.rs index ef8cd58abf9..90ef7843d4b 100644 --- a/src/test/compile-fail/multiple-main.rs +++ b/src/test/compile-fail/main-wrong-location.rs @@ -8,10 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -fn main() { -} - -mod foo { - fn main() { //~ ERROR multiple 'main' functions - } -} +mod m { + // An inferred main entry point (that doesn't use #[main]) + // must appear at the top of the crate + fn main() { } //~ NOTE here is a function named 'main' +} \ No newline at end of file diff --git a/src/test/compile-fail/missing-main.rs b/src/test/compile-fail/missing-main.rs index 4f1b604b507..4bfdaf69480 100644 --- a/src/test/compile-fail/missing-main.rs +++ b/src/test/compile-fail/missing-main.rs @@ -8,5 +8,5 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:entry function not found +// error-pattern:main function not found fn mian() { } diff --git a/src/test/run-pass/dupe-first-attr.rc b/src/test/run-pass/dupe-first-attr.rc index d39a2aa4476..9bd63a8d646 100644 --- a/src/test/run-pass/dupe-first-attr.rc +++ b/src/test/run-pass/dupe-first-attr.rc @@ -25,3 +25,5 @@ mod hello; #[cfg(target_os = "android")] mod hello; + +fn main() { } \ No newline at end of file diff --git a/src/test/run-pass/intrinsic-alignment.rs b/src/test/run-pass/intrinsic-alignment.rs index adc085d2108..cce3d8066ec 100644 --- a/src/test/run-pass/intrinsic-alignment.rs +++ b/src/test/run-pass/intrinsic-alignment.rs @@ -22,6 +22,7 @@ mod rusti { #[cfg(target_os = "macos")] #[cfg(target_os = "freebsd")] mod m { + #[main] #[cfg(target_arch = "x86")] pub fn main() { unsafe { @@ -30,6 +31,7 @@ mod m { } } + #[main] #[cfg(target_arch = "x86_64")] pub fn main() { unsafe { @@ -41,6 +43,7 @@ mod m { #[cfg(target_os = "win32")] mod m { + #[main] #[cfg(target_arch = "x86")] pub fn main() { unsafe { @@ -52,6 +55,7 @@ mod m { #[cfg(target_os = "android")] mod m { + #[main] #[cfg(target_arch = "arm")] pub fn main() { unsafe {