Skip to content

Implement RFC #1260 #38312

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/librustc/middle/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -418,6 +418,7 @@ fn get_struct_ctor_id(item: &hir::Item) -> Option<ast::NodeId> {
struct DeadVisitor<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
live_symbols: Box<FxHashSet<ast::NodeId>>,
defined_as_main: Option<DefId>,
}

impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<DefId>) {
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);
}
29 changes: 24 additions & 5 deletions src/librustc/middle/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<DefId>,

// The function that imported to root namespace named 'main'.
imported_main_fn: Option<(NodeId, Span)>,
}

impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> {
Expand All @@ -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<DefId>) {
let _task = ast_map.dep_graph.in_task(DepNode::EntryPoint);

let any_exe = session.crate_types.borrow().iter().any(|ty| {
Expand All @@ -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);
Expand All @@ -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<F: Fn() -> 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'
Expand All @@ -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));
Expand Down Expand Up @@ -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 => ()
}
}
Expand All @@ -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");
Expand Down
16 changes: 13 additions & 3 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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| {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<DefId>,
}

/// Run the "early phases" of the compiler: initial `cfg` processing,
Expand Down Expand Up @@ -791,6 +799,7 @@ pub fn phase_2_configure_and_expand<F>(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,
Expand All @@ -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<DefId>,
f: F)
-> Result<R, usize>
where F: for<'a> FnOnce(TyCtxt<'a, 'tcx, 'tcx>,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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", || {
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_driver/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ impl PpSourceMode {
resolutions.clone(),
arenas,
id,
None,
|tcx, _, _, _| {
let annotation = TypedAnnotation { tcx: tcx };
let _ignore = tcx.dep_graph.in_ignore();
Expand Down Expand Up @@ -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 => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_resolve/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_resolve/check_unused.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
7 changes: 7 additions & 0 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,11 @@ pub struct Resolver<'a> {

// Avoid duplicated errors for "name already defined".
name_already_seen: FxHashMap<Name, Span>,

// Track function imported as main in root namespace
pub defined_as_main: Option<DefId>,

main_directive_id: Option<NodeId>,
}

pub struct ResolverArenas<'a> {
Expand Down Expand Up @@ -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,
}
}

Expand Down
19 changes: 13 additions & 6 deletions src/librustc_resolve/resolve_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<T>(&mut self, module: Module<'a>, name: Name, ns: Namespace, binding: T)
pub fn try_define<T>(&mut self, directive_id: Option<NodeId>, 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() {
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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());
});
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
2 changes: 2 additions & 0 deletions src/libsyntax/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(..) => {
Expand Down
3 changes: 2 additions & 1 deletion src/libsyntax/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =>
Expand Down
13 changes: 13 additions & 0 deletions src/test/compile-fail/rfc1260/non-fn-item.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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;
12 changes: 12 additions & 0 deletions src/test/compile-fail/rfc1260/privacy.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
13 changes: 13 additions & 0 deletions src/test/compile-fail/rfc1260/reexport-to-non-root.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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`

Loading