diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index 2662e70999196..22b00e09a24f7 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -481,6 +481,46 @@ fn main() {
```
"##,
+E0134: r##"
+More than one item was re-exported as `main`.
+
+Erroneous code example:
+
+```compile_fail,E0134
+#![feature(main_reexport)]
+
+mod foo {
+ pub fn bar() { }
+ pub fn baz() { }
+}
+
+use foo::bar as main;
+use foo::baz as main;
+```
+
+This error indicates that the compiler found multiple items re-exported as
+`main`. This is an error because there must be a unique entry point into a
+Rust program.
+"##,
+
+E0135: r##"
+The item re-exported as `main` is not a function.
+
+Erroneous code example:
+
+```compile_fail,E0135
+#![feature(main_reexport)]
+
+mod foo {
+ pub const bar: &'static str = "hello";
+}
+
+use foo::bar as main;
+```
+
+This is an error because the entry point of a Rust program must be a function.
+"##,
+
// This shouldn't really ever trigger since the repeated value error comes first
E0136: r##"
A binary can only have one entry point, and by default that entry point is the
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index e3b2078971439..1c705e2d68dc3 100644
--- a/src/librustc/middle/dead.rs
+++ b/src/librustc/middle/dead.rs
@@ -28,6 +28,8 @@ use syntax::{ast, codemap};
use syntax::attr;
use syntax_pos;
+use session::config::EntryFnType;
+
// Any local node that may call something in its body block should be
// explored. For example, if it's a live NodeItem that is a
// function, then we should explore its block to check for codes that
@@ -389,8 +391,18 @@ fn create_and_seed_worklist<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
// Seed entry point
- if let Some((id, _, _)) = *tcx.sess.entry_fn.borrow() {
- worklist.push(id);
+ if let Some(ref entry) = *tcx.sess.entry_fn.borrow() {
+ match entry {
+ | EntryFnType::EntryMain(id, ..)
+ | EntryFnType::EntryStart(id, ..) => worklist.push(*id),
+ | EntryFnType::EntryImported(_, def_id, _) => {
+ if let Some(node) = tcx.hir.get_if_local(*def_id) {
+ if let hir_map::Node::NodeItem(it) = node {
+ worklist.push(it.id);
+ }
+ }
+ }
+ }
}
// Seed implemented trait items
diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs
index ebc796466629c..eef600364c79b 100644
--- a/src/librustc/middle/entry.rs
+++ b/src/librustc/middle/entry.rs
@@ -10,14 +10,14 @@
use hir::map as hir_map;
-use hir::def_id::{CRATE_DEF_INDEX};
+use hir::def_id::{CRATE_DEF_INDEX, DefId};
use session::{config, Session};
use syntax::ast::NodeId;
use syntax::attr;
-use syntax::entry::EntryPointType;
use syntax_pos::Span;
-use hir::{Item, ItemFn, ImplItem, TraitItem};
+use hir::{Item, ItemFn, ImplItem, TraitItem, ItemUse, Path, def};
use hir::itemlikevisit::ItemLikeVisitor;
+use session::config::EntryFnType;
struct EntryContext<'a, 'tcx: 'a> {
session: &'a Session,
@@ -33,6 +33,9 @@ struct EntryContext<'a, 'tcx: 'a> {
// The function that has the attribute 'start' on it
start_fn: Option<(NodeId, Span)>,
+ // The function is imported and renamed as 'main'
+ imported_fn: Option<(NodeId, DefId, 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: Vec<(NodeId, Span)> ,
@@ -79,6 +82,7 @@ pub fn find_entry_point(session: &Session,
main_fn: None,
attr_main_fn: None,
start_fn: None,
+ imported_fn: None,
non_main_fns: Vec::new(),
};
@@ -87,79 +91,93 @@ pub fn find_entry_point(session: &Session,
configure_main(&mut ctxt, crate_name);
}
-// Beware, this is duplicated in libsyntax/entry.rs, make sure to keep
-// them in sync.
-fn entry_point_type(item: &Item, at_root: bool) -> EntryPointType {
+fn update_start_attr(item: &Item, ctxt: &mut EntryContext) {
+ if ctxt.start_fn.is_none() {
+ ctxt.start_fn = Some((item.id, item.span));
+ } else {
+ struct_span_err!(
+ ctxt.session, item.span, E0138,
+ "multiple 'start' functions")
+ .span_label(ctxt.start_fn.unwrap().1,
+ "previous `start` function here")
+ .span_label(item.span, "multiple `start` functions")
+ .emit();
+ }
+}
+
+fn update_main_attr(item: &Item, ctxt: &mut EntryContext) {
+ if ctxt.attr_main_fn.is_none() {
+ ctxt.attr_main_fn = Some((item.id, item.span));
+ } else {
+ struct_span_err!(ctxt.session, item.span, E0137,
+ "multiple functions with a #[main] attribute")
+ .span_label(item.span, "additional #[main] function")
+ .span_label(ctxt.attr_main_fn.unwrap().1, "first #[main] function")
+ .emit();
+ }
+}
+
+fn update_imported_fn(p: &Path, item: &Item, ctxt: &mut EntryContext) {
+ if let def::Def::Fn(def_id) = p.def {
+ if ctxt.imported_fn.is_none() {
+ ctxt.imported_fn = Some((item.id, def_id, item.span));
+ } else {
+ span_err!(ctxt.session, item.span, E0134,
+ "Re-exported multiple items as `main`");
+ }
+ } else {
+ span_err!(ctxt.session, item.span, E0135,
+ "The item re-exported as `main` is not a function");
+ }
+}
+
+fn update_main_fn(item: &Item, ctxt: &mut EntryContext) {
+ if ctxt.main_fn.is_none() {
+ ctxt.main_fn = Some((item.id, item.span));
+ } else {
+ span_err!(ctxt.session, item.span, E0136,
+ "multiple 'main' functions");
+ }
+}
+
+fn update_non_main_fns(item: &Item, ctxt: &mut EntryContext) {
+ ctxt.non_main_fns.push((item.id, item.span));
+}
+
+fn find_item(item: &Item, ctxt: &mut EntryContext, at_root: bool) {
match item.node {
ItemFn(..) => {
if attr::contains_name(&item.attrs, "start") {
- EntryPointType::Start
+ update_start_attr(item, ctxt);
} else if attr::contains_name(&item.attrs, "main") {
- EntryPointType::MainAttr
+ update_main_attr(item, ctxt);
} else if item.name == "main" {
if at_root {
// This is a top-level function so can be 'main'
- EntryPointType::MainNamed
+ update_main_fn(item, ctxt);
} else {
- EntryPointType::OtherMain
+ update_non_main_fns(item, ctxt);
}
- } else {
- EntryPointType::None
}
}
- _ => EntryPointType::None,
- }
-}
-
-
-fn find_item(item: &Item, ctxt: &mut EntryContext, at_root: bool) {
- match entry_point_type(item, at_root) {
- EntryPointType::MainNamed => {
- if ctxt.main_fn.is_none() {
- ctxt.main_fn = Some((item.id, item.span));
- } else {
- span_err!(ctxt.session, item.span, E0136,
- "multiple 'main' functions");
+ ItemUse(ref p, _) => {
+ if item.name == "main" {
+ update_imported_fn(p, item, ctxt);
}
- },
- EntryPointType::OtherMain => {
- ctxt.non_main_fns.push((item.id, item.span));
- },
- EntryPointType::MainAttr => {
- if ctxt.attr_main_fn.is_none() {
- ctxt.attr_main_fn = Some((item.id, item.span));
- } else {
- struct_span_err!(ctxt.session, item.span, E0137,
- "multiple functions with a #[main] attribute")
- .span_label(item.span, "additional #[main] function")
- .span_label(ctxt.attr_main_fn.unwrap().1, "first #[main] function")
- .emit();
- }
- },
- EntryPointType::Start => {
- if ctxt.start_fn.is_none() {
- ctxt.start_fn = Some((item.id, item.span));
- } else {
- struct_span_err!(
- ctxt.session, item.span, E0138,
- "multiple 'start' functions")
- .span_label(ctxt.start_fn.unwrap().1,
- "previous `start` function here")
- .span_label(item.span, "multiple `start` functions")
- .emit();
- }
- },
- EntryPointType::None => ()
+ }
+ _ => {}
}
}
fn configure_main(this: &mut EntryContext, crate_name: &str) {
if let Some((node_id, span)) = this.start_fn {
- this.session.entry_fn.set(Some((node_id, span, config::EntryStart)));
+ this.session.entry_fn.set(Some(EntryFnType::EntryStart(node_id, span)));
} else if let Some((node_id, span)) = this.attr_main_fn {
- this.session.entry_fn.set(Some((node_id, span, config::EntryMain)));
+ this.session.entry_fn.set(Some(EntryFnType::EntryMain(node_id, span)));
} else if let Some((node_id, span)) = this.main_fn {
- this.session.entry_fn.set(Some((node_id, span, config::EntryMain)));
+ this.session.entry_fn.set(Some(EntryFnType::EntryMain(node_id, span)));
+ } else if let Some((node_id, def_id, span)) = this.imported_fn {
+ this.session.entry_fn.set(Some(EntryFnType::EntryImported(node_id, def_id, span)));
} else {
// No main function
this.session.entry_fn.set(None);
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index b3f1b9c8e627c..685df190fb264 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -28,7 +28,7 @@ use rustc_data_structures::stable_hasher::ToStableHashKey;
use lint;
use middle::cstore;
-use syntax::ast::{self, IntTy, UintTy};
+use syntax::ast::{self, IntTy, UintTy, NodeId};
use syntax::codemap::{FileName, FilePathMapping};
use syntax::edition::{Edition, EDITION_NAME_LIST, DEFAULT_EDITION};
use syntax::parse::token;
@@ -36,6 +36,10 @@ use syntax::parse;
use syntax::symbol::Symbol;
use syntax::feature_gate::UnstableFeatures;
+use syntax_pos::Span;
+use hir::def_id::DefId;
+use hir::map::Map;
+
use errors::{ColorConfig, FatalError, Handler};
use getopts;
@@ -641,10 +645,39 @@ impl Options {
// The type of entry function, so
// users can have their own entry
// functions
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Clone, PartialEq)]
pub enum EntryFnType {
- EntryMain,
- EntryStart,
+ EntryMain(NodeId, Span),
+ EntryStart(NodeId, Span),
+ EntryImported(NodeId, DefId, Span),
+}
+
+impl EntryFnType {
+ pub fn get_local_id(&self) -> NodeId {
+ match self {
+ EntryFnType::EntryMain(node_id, ..)
+ | EntryFnType::EntryStart(node_id, ..)
+ | EntryFnType::EntryImported(node_id, ..) => *node_id,
+ }
+ }
+
+ pub fn get_def_id<'hir>(&self, map: &Map<'hir>) -> DefId {
+ match self {
+ EntryFnType::EntryMain(node_id, ..)
+ | EntryFnType::EntryStart(node_id, ..) => {
+ map.local_def_id(*node_id)
+ }
+ EntryFnType::EntryImported(_, def_id, _) => *def_id,
+ }
+ }
+
+ pub fn get_span(&self) -> Span {
+ match self {
+ EntryFnType::EntryMain(_, sp)
+ | EntryFnType::EntryStart(_, sp)
+ | EntryFnType::EntryImported(_, _, sp) => *sp
+ }
+ }
}
#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug)]
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 8df66d8d68855..2929e30533629 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -70,7 +70,7 @@ pub struct Session {
pub opts: config::Options,
pub parse_sess: ParseSess,
/// For a library crate, this is always none
- pub entry_fn: Once