diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 1bf6b837fd998..bf93961877e42 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -21,7 +21,7 @@ use hir::itemlikevisit::ItemLikeVisitor; use middle::privacy; use ty::{self, TyCtxt}; use hir::def::Def; -use hir::def_id::{DefId}; +use hir::def_id::DefId; use lint; use util::nodemap::FxHashSet; @@ -418,6 +418,7 @@ fn get_struct_ctor_id(item: &hir::Item) -> Option { struct DeadVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, live_symbols: Box>, + defined_as_main: Option, } impl<'a, 'tcx> DeadVisitor<'a, 'tcx> { @@ -515,7 +516,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DeadVisitor<'a, 'tcx> { } fn visit_item(&mut self, item: &'tcx hir::Item) { - if self.should_warn_about_item(item) { + if self.should_warn_about_item(item) && + self.defined_as_main.map_or(true, |x| x != self.tcx.map.local_def_id(item.id)) { self.warn_dead_code( item.id, item.span, @@ -593,10 +595,13 @@ impl<'a, 'tcx> Visitor<'tcx> for DeadVisitor<'a, 'tcx> { } pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - access_levels: &privacy::AccessLevels) { + access_levels: &privacy::AccessLevels, + defined_as_main: Option) { let _task = tcx.dep_graph.in_task(DepNode::DeadCheck); let krate = tcx.map.krate(); let live_symbols = find_live(tcx, access_levels, krate); - let mut visitor = DeadVisitor { tcx: tcx, live_symbols: live_symbols }; + let mut visitor = DeadVisitor { tcx: tcx, + live_symbols: live_symbols, + defined_as_main: defined_as_main, }; intravisit::walk_crate(&mut visitor, krate); } diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index e927843a984b8..c6050061eb962 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -11,7 +11,7 @@ use dep_graph::DepNode; use hir::map as ast_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; @@ -36,7 +36,14 @@ struct EntryContext<'a, 'tcx: 'a> { // 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)> , + non_main_fns: Vec<(NodeId, Span)>, + + // Function item that imported to root namespace named 'main'. + // It was collected in resolve phase + defined_as_main: Option, + + // The function that imported to root namespace named 'main'. + imported_main_fn: Option<(NodeId, Span)>, } impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> { @@ -53,7 +60,7 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> { } } -pub fn find_entry_point(session: &Session, ast_map: &ast_map::Map) { +pub fn find_entry_point(session: &Session, ast_map: &ast_map::Map, defined_as_main: Option) { let _task = ast_map.dep_graph.in_task(DepNode::EntryPoint); let any_exe = session.crate_types.borrow().iter().any(|ty| { @@ -77,6 +84,8 @@ pub fn find_entry_point(session: &Session, ast_map: &ast_map::Map) { attr_main_fn: None, start_fn: None, non_main_fns: Vec::new(), + defined_as_main: defined_as_main, + imported_main_fn: None, }; ast_map.krate().visit_all_item_likes(&mut ctxt); @@ -86,13 +95,15 @@ pub fn find_entry_point(session: &Session, ast_map: &ast_map::Map) { // 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 entry_point_type bool>(item: &Item, at_root: bool, defined_as_main: F) -> EntryPointType { match item.node { ItemFn(..) => { if attr::contains_name(&item.attrs, "start") { EntryPointType::Start } else if attr::contains_name(&item.attrs, "main") { EntryPointType::MainAttr + } else if defined_as_main() { + EntryPointType::ImportedMain } else if item.name == "main" { if at_root { // This is a top-level function so can be 'main' @@ -110,7 +121,9 @@ fn entry_point_type(item: &Item, at_root: bool) -> EntryPointType { fn find_item(item: &Item, ctxt: &mut EntryContext, at_root: bool) { - match entry_point_type(item, at_root) { + match entry_point_type(item, + at_root, + || ctxt.defined_as_main == Some(ctxt.map.local_def_id(item.id))) { EntryPointType::MainNamed => { if ctxt.main_fn.is_none() { ctxt.main_fn = Some((item.id, item.span)); @@ -146,6 +159,9 @@ fn find_item(item: &Item, ctxt: &mut EntryContext, at_root: bool) { .emit(); } }, + EntryPointType::ImportedMain => { + ctxt.imported_main_fn = Some((item.id, item.span)); + }, EntryPointType::None => () } } @@ -160,6 +176,9 @@ fn configure_main(this: &mut EntryContext) { } else if this.main_fn.is_some() { *this.session.entry_fn.borrow_mut() = this.main_fn; this.session.entry_type.set(Some(config::EntryMain)); + } else if this.imported_main_fn.is_some() { + *this.session.entry_fn.borrow_mut() = this.imported_main_fn; + this.session.entry_type.set(Some(config::EntryMain)); } else { // No main function let mut err = this.session.struct_err("main function not found"); diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 8d995e97b95b3..051a1d3bceaa5 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -11,6 +11,7 @@ use rustc::hir; use rustc::hir::{map as hir_map, FreevarMap, TraitMap}; use rustc::hir::lowering::lower_crate; +use rustc::hir::def_id::DefId; use rustc_data_structures::blake2b::Blake2bHasher; use rustc_data_structures::fmt_wrap::FmtWrap; use rustc::ty::util::ArchIndependentHasher; @@ -117,7 +118,12 @@ pub fn compile_input(sess: &Session, let outputs = build_output_filenames(input, outdir, output, &krate.attrs, sess); let crate_name = link::find_crate_name(Some(sess), &krate.attrs, input); - let ExpansionResult { expanded_crate, defs, analysis, resolutions, mut hir_forest } = { + let ExpansionResult { expanded_crate, + defs, + analysis, + resolutions, + defined_as_main, + mut hir_forest } = { phase_2_configure_and_expand( sess, &cstore, krate, registry, &crate_name, addl_plugins, control.make_glob_map, |expanded_crate| { @@ -175,6 +181,7 @@ pub fn compile_input(sess: &Session, resolutions, &arenas, &crate_name, + defined_as_main, |tcx, analysis, incremental_hashes_map, result| { { // Eventually, we will want to track plugins. @@ -536,6 +543,7 @@ pub struct ExpansionResult { pub analysis: ty::CrateAnalysis<'static>, pub resolutions: Resolutions, pub hir_forest: hir_map::Forest, + pub defined_as_main: Option, } /// Run the "early phases" of the compiler: initial `cfg` processing, @@ -791,6 +799,7 @@ pub fn phase_2_configure_and_expand(sess: &Session, glob_map: if resolver.make_glob_map { Some(resolver.glob_map) } else { None }, hir_ty_to_ty: NodeMap(), }, + defined_as_main: resolver.defined_as_main, resolutions: Resolutions { freevars: resolver.freevars, trait_map: resolver.trait_map, @@ -809,6 +818,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, resolutions: Resolutions, arenas: &'tcx ty::CtxtArenas<'tcx>, name: &str, + defined_as_main: Option, f: F) -> Result where F: for<'a> FnOnce(TyCtxt<'a, 'tcx, 'tcx>, @@ -842,7 +852,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, time(time_passes, "looking for entry point", - || middle::entry::find_entry_point(sess, &hir_map)); + || middle::entry::find_entry_point(sess, &hir_map, defined_as_main)); sess.plugin_registrar_fn.set(time(time_passes, "looking for plugin registrar", || { plugin::build::find_plugin_registrar(sess.diagnostic(), &hir_map) @@ -973,7 +983,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, || reachable::find_reachable(tcx, &analysis.access_levels)); time(time_passes, "death checking", || { - middle::dead::check_crate(tcx, &analysis.access_levels); + middle::dead::check_crate(tcx, &analysis.access_levels, defined_as_main); }); time(time_passes, "unused lib feature checking", || { diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index b055b043723e4..db96e1501bfe4 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -232,6 +232,7 @@ impl PpSourceMode { resolutions.clone(), arenas, id, + None, |tcx, _, _, _| { let annotation = TypedAnnotation { tcx: tcx }; let _ignore = tcx.dep_graph.in_ignore(); @@ -960,6 +961,7 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session, resolutions.clone(), arenas, crate_name, + None, |tcx, _, _, _| { match ppm { PpmMir | PpmMirCFG => { diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 25a37931ba31f..d99e36e0dfd12 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -80,7 +80,7 @@ impl<'b> Resolver<'b> { where T: ToNameBinding<'b>, { let binding = def.to_name_binding(); - if let Err(old_binding) = self.try_define(parent, name, ns, binding.clone()) { + if let Err(old_binding) = self.try_define(None, parent, name, ns, binding.clone()) { self.report_conflict(parent, name, ns, old_binding, &binding); } } diff --git a/src/librustc_resolve/check_unused.rs b/src/librustc_resolve/check_unused.rs index 41391c65a128d..331cc98c1b40d 100644 --- a/src/librustc_resolve/check_unused.rs +++ b/src/librustc_resolve/check_unused.rs @@ -55,7 +55,8 @@ impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> { // We have information about whether `use` (import) directives are actually // used now. If an import is not used at all, we signal a lint error. fn check_import(&mut self, item_id: ast::NodeId, id: ast::NodeId, span: Span) { - let mut used = false; + // Take it as used if it's importing main function + let mut used = Some(id) == self.resolver.main_directive_id; self.per_ns(|this, ns| used |= this.used_imports.contains(&(id, ns))); if !used { if self.maybe_unused_trait_imports.contains(&id) { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index f7aaf2475f65e..8a916ce61dbd4 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1114,6 +1114,11 @@ pub struct Resolver<'a> { // Avoid duplicated errors for "name already defined". name_already_seen: FxHashMap, + + // Track function imported as main in root namespace + pub defined_as_main: Option, + + main_directive_id: Option, } pub struct ResolverArenas<'a> { @@ -1289,6 +1294,8 @@ impl<'a> Resolver<'a> { macro_exports: Vec::new(), invocations: invocations, name_already_seen: FxHashMap(), + defined_as_main: None, + main_directive_id: None, } } diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index b634d57a842f6..4dcd966dfb423 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -11,7 +11,7 @@ use self::ImportDirectiveSubclass::*; use {AmbiguityError, Module, PerNS}; -use Namespace::{self, TypeNS, MacroNS}; +use Namespace::{self, TypeNS, MacroNS, ValueNS}; use {NameBinding, NameBindingKind, PathResult, PathScope, PrivacyError, ToNameBinding}; use Resolver; use {names_to_string, module_to_string}; @@ -301,11 +301,17 @@ impl<'a> Resolver<'a> { } // Define the name or return the existing binding if there is a collision. - pub fn try_define(&mut self, module: Module<'a>, name: Name, ns: Namespace, binding: T) + pub fn try_define(&mut self, directive_id: Option, module: Module<'a>, name: Name, ns: Namespace, binding: T) -> Result<(), &'a NameBinding<'a>> where T: ToNameBinding<'a> { let binding = self.arenas.alloc_name_binding(binding.to_name_binding()); + if ns == ValueNS && module.parent.is_none() && name == Name::intern("main") { + if let Def::Fn(def_id) = binding.def() { + self.defined_as_main = Some(def_id); + self.main_directive_id = directive_id; + } + } self.update_resolution(module, name, ns, |this, resolution| { if let Some(old_binding) = resolution.binding { if binding.is_glob_import() { @@ -374,7 +380,7 @@ impl<'a> Resolver<'a> { for directive in module.glob_importers.borrow_mut().iter() { if self.is_accessible_from(binding.vis, directive.parent) { let imported_binding = self.import(binding, directive); - let _ = self.try_define(directive.parent, name, ns, imported_binding); + let _ = self.try_define(Some(directive.id), directive.parent, name, ns, imported_binding); } } @@ -388,7 +394,7 @@ impl<'a> Resolver<'a> { let dummy_binding = self.dummy_binding; let dummy_binding = self.import(dummy_binding, directive); self.per_ns(|this, ns| { - let _ = this.try_define(directive.parent, target, ns, dummy_binding.clone()); + let _ = this.try_define(Some(directive.id), directive.parent, target, ns, dummy_binding.clone()); }); } } @@ -533,7 +539,8 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { } Ok(binding) => { let imported_binding = this.import(binding, directive); - let conflict = this.try_define(directive.parent, target, ns, imported_binding); + let conflict = this.try_define(Some(directive.id), directive.parent, target, ns, + imported_binding); if let Err(old_binding) = conflict { let binding = &this.import(binding, directive); this.report_conflict(directive.parent, target, ns, binding, old_binding); @@ -705,7 +712,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { for ((name, ns), binding) in bindings { if binding.pseudo_vis() == ty::Visibility::Public || self.is_accessible(binding.vis) { let imported_binding = self.import(binding, directive); - let _ = self.try_define(directive.parent, name, ns, imported_binding); + let _ = self.try_define(Some(directive.id), directive.parent, name, ns, imported_binding); } } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index df25473ddd916..8da504e4108bd 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -169,6 +169,7 @@ pub fn run_core(search_paths: SearchPaths, resolutions, &arenas, &name, + None, |tcx, analysis, _, result| { if let Err(_) = result { sess.fatal("Compilation failed, aborting rustdoc"); diff --git a/src/libsyntax/entry.rs b/src/libsyntax/entry.rs index 93ca1948ed84b..1bc50fcb97ee0 100644 --- a/src/libsyntax/entry.rs +++ b/src/libsyntax/entry.rs @@ -17,10 +17,12 @@ pub enum EntryPointType { MainAttr, Start, OtherMain, // Not an entry point, but some other function named main + ImportedMain, // function imported as "main" in root namespace } // Beware, this is duplicated in librustc/middle/entry.rs, make sure to keep // them in sync. +// FIXME: get it sync or find other way to keep sane in --test mode pub fn entry_point_type(item: &Item, depth: usize) -> EntryPointType { match item.node { ItemKind::Fn(..) => { diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index fca89e265e4ed..b8bb19217ae92 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -186,7 +186,8 @@ impl fold::Folder for EntryPointCleaner { // Remove any #[main] or #[start] from the AST so it doesn't // clash with the one we're going to add, but mark it as // #[allow(dead_code)] to avoid printing warnings. - let folded = match entry::entry_point_type(&folded, self.depth) { + let folded = match entry::entry_point_type(&folded, self.depth) { + EntryPointType::ImportedMain => unimplemented!(), // FIXME EntryPointType::MainNamed | EntryPointType::MainAttr | EntryPointType::Start => diff --git a/src/test/compile-fail/rfc1260/non-fn-item.rs b/src/test/compile-fail/rfc1260/non-fn-item.rs new file mode 100644 index 0000000000000..a6811b15176eb --- /dev/null +++ b/src/test/compile-fail/rfc1260/non-fn-item.rs @@ -0,0 +1,13 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:main function not found +mod ns { pub const foo: u8 = 1; } +use ns::foo as main; diff --git a/src/test/compile-fail/rfc1260/privacy.rs b/src/test/compile-fail/rfc1260/privacy.rs new file mode 100644 index 0000000000000..4a63a88208a61 --- /dev/null +++ b/src/test/compile-fail/rfc1260/privacy.rs @@ -0,0 +1,12 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod ns { fn foo() {} } +use ns::foo as main; //~ error: function `foo` is private diff --git a/src/test/compile-fail/rfc1260/reexport-to-non-root.rs b/src/test/compile-fail/rfc1260/reexport-to-non-root.rs new file mode 100644 index 0000000000000..48ac2f60b27c6 --- /dev/null +++ b/src/test/compile-fail/rfc1260/reexport-to-non-root.rs @@ -0,0 +1,13 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:main function not found +mod ns { pub fn foo() {} use foo as main; } //~ ERROR unresolved import `foo` + diff --git a/src/test/compile-fail/rfc1260/wrong-signature.rs b/src/test/compile-fail/rfc1260/wrong-signature.rs new file mode 100644 index 0000000000000..6a30d75b5531f --- /dev/null +++ b/src/test/compile-fail/rfc1260/wrong-signature.rs @@ -0,0 +1,13 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +mod ns { pub fn foo(_: ()) {} } //~ ERROR: main function has wrong type +use ns::foo as main; diff --git a/src/test/run-pass/rfc1260.rs b/src/test/run-pass/rfc1260.rs new file mode 100644 index 0000000000000..aad25fea40007 --- /dev/null +++ b/src/test/run-pass/rfc1260.rs @@ -0,0 +1,12 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod ns { pub fn foo() {} } +use ns::foo as main;