rustc: Only accept main functions at the crate level. #4433

This commit is contained in:
Brian Anderson 2013-04-29 16:16:58 -07:00
parent be8dc615c5
commit 98f5c6d5b6
8 changed files with 94 additions and 64 deletions

View File

@ -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));

View File

@ -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();
}
}

View File

@ -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")
}
}
}

View File

@ -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

View File

@ -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'
}

View File

@ -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() { }

View File

@ -25,3 +25,5 @@ mod hello;
#[cfg(target_os = "android")]
mod hello;
fn main() { }

View File

@ -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 {