From 45de9de1e99c3d6a38055835b0fe6c65e1ddac73 Mon Sep 17 00:00:00 2001 From: William Throwe Date: Sun, 23 Aug 2015 14:12:39 -0400 Subject: [PATCH 1/4] Move entry point identification logic to libsyntax Identifying entry points will be useful in --test mode, which is handled in libsyntax. --- src/librustc/middle/entry.rs | 81 ++++++++++++++++-------------------- src/libsyntax/entry.rs | 42 +++++++++++++++++++ src/libsyntax/lib.rs | 1 + 3 files changed, 79 insertions(+), 45 deletions(-) create mode 100644 src/libsyntax/entry.rs diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index c6e5b654f9a5c..8cdd4f7fe74c5 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -11,20 +11,19 @@ use ast_map; use session::{config, Session}; -use syntax::ast::{Name, NodeId, Item, ItemFn}; +use syntax; +use syntax::ast::{NodeId, Item}; use syntax::attr; use syntax::codemap::Span; -use syntax::parse::token; +use syntax::entry::EntryPointType; use syntax::visit; use syntax::visit::Visitor; -struct EntryContext<'a, 'ast: 'a> { +struct EntryContext<'a> { session: &'a Session, - ast_map: &'a ast_map::Map<'ast>, - - // The interned Name for "main". - main_name: Name, + // The current depth in the ast + depth: usize, // The top-level function called 'main' main_fn: Option<(NodeId, Span)>, @@ -40,9 +39,11 @@ struct EntryContext<'a, 'ast: 'a> { non_main_fns: Vec<(NodeId, Span)> , } -impl<'a, 'ast, 'v> Visitor<'v> for EntryContext<'a, 'ast> { +impl<'a, 'v> Visitor<'v> for EntryContext<'a> { fn visit_item(&mut self, item: &Item) { + self.depth += 1; find_item(item, self); + self.depth -= 1; } } @@ -63,8 +64,7 @@ pub fn find_entry_point(session: &Session, ast_map: &ast_map::Map) { let mut ctxt = EntryContext { session: session, - main_name: token::intern("main"), - ast_map: ast_map, + depth: 0, main_fn: None, attr_main_fn: None, start_fn: None, @@ -77,44 +77,35 @@ pub fn find_entry_point(session: &Session, ast_map: &ast_map::Map) { } fn find_item(item: &Item, ctxt: &mut EntryContext) { - match item.node { - ItemFn(..) => { - if item.ident.name == ctxt.main_name { - ctxt.ast_map.with_path(item.id, |path| { - if path.count() == 1 { - // 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 { - span_err!(ctxt.session, item.span, E0136, - "multiple 'main' functions"); - } - } else { - // This isn't main - ctxt.non_main_fns.push((item.id, item.span)); - } - }); + match syntax::entry::entry_point_type(item, ctxt.depth) { + 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"); } - - if attr::contains_name(&item.attrs, "main") { - if ctxt.attr_main_fn.is_none() { - ctxt.attr_main_fn = Some((item.id, item.span)); - } else { - span_err!(ctxt.session, item.span, E0137, - "multiple functions with a #[main] attribute"); - } + }, + 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 { + span_err!(ctxt.session, item.span, E0137, + "multiple functions with a #[main] attribute"); } - - if attr::contains_name(&item.attrs, "start") { - if ctxt.start_fn.is_none() { - ctxt.start_fn = Some((item.id, item.span)); - } else { - span_err!(ctxt.session, item.span, E0138, - "multiple 'start' functions"); - } + }, + EntryPointType::Start => { + if ctxt.start_fn.is_none() { + ctxt.start_fn = Some((item.id, item.span)); + } else { + span_err!(ctxt.session, item.span, E0138, + "multiple 'start' functions"); } - } - _ => () + }, + EntryPointType::None => () } visit::walk_item(ctxt, item); diff --git a/src/libsyntax/entry.rs b/src/libsyntax/entry.rs new file mode 100644 index 0000000000000..b6c5d0066a233 --- /dev/null +++ b/src/libsyntax/entry.rs @@ -0,0 +1,42 @@ +// Copyright 2012-2015 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. + +use attr; +use ast::{Item, ItemFn}; + +pub enum EntryPointType { + None, + MainNamed, + MainAttr, + Start, + OtherMain, // Not an entry point, but some other function named main +} + +pub fn entry_point_type(item: &Item, depth: usize) -> 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 item.ident.name == "main" { + if depth == 1 { + // This is a top-level function so can be 'main' + EntryPointType::MainNamed + } else { + EntryPointType::OtherMain + } + } else { + EntryPointType::None + } + } + _ => EntryPointType::None, + } +} diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 0d1fa6dd7265a..d1c862ad40b25 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -90,6 +90,7 @@ pub mod attr; pub mod codemap; pub mod config; pub mod diagnostic; +pub mod entry; pub mod feature_gate; pub mod fold; pub mod owned_slice; From 15d6837a16d727a3d37a703eaedf48e62c260290 Mon Sep 17 00:00:00 2001 From: William Throwe Date: Sun, 23 Aug 2015 21:46:10 -0400 Subject: [PATCH 2/4] Mark main-like functions allow(dead_code) in tests Fixes #12327. --- src/libsyntax/test.rs | 49 +++++++++++++------ src/test/compile-fail/test-warns-dead-code.rs | 17 +++++++ src/test/run-pass/test-main-not-dead-attr.rs | 18 +++++++ src/test/run-pass/test-main-not-dead.rs | 15 ++++++ 4 files changed, 84 insertions(+), 15 deletions(-) create mode 100644 src/test/compile-fail/test-warns-dead-code.rs create mode 100644 src/test/run-pass/test-main-not-dead-attr.rs create mode 100644 src/test/run-pass/test-main-not-dead.rs diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 26fb287ce35d1..3fbcbd728aabc 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -14,6 +14,7 @@ #![allow(unused_imports)] use self::HasTestSignature::*; +use std::iter; use std::slice; use std::mem; use std::vec; @@ -24,6 +25,7 @@ use codemap::{DUMMY_SP, Span, ExpnInfo, NameAndSpan, MacroAttribute}; use codemap; use diagnostic; use config; +use entry::{self, EntryPointType}; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::expand::ExpansionConfig; @@ -177,22 +179,39 @@ impl<'a> fold::Folder for TestHarnessGenerator<'a> { // the one we're going to add. Only if compiling an executable. mod_folded.items = mem::replace(&mut mod_folded.items, vec![]).move_map(|item| { - item.map(|ast::Item {id, ident, attrs, node, vis, span}| { - ast::Item { - id: id, - ident: ident, - attrs: attrs.into_iter().filter_map(|attr| { - if !attr.check_name("main") { - Some(attr) - } else { - None + match entry::entry_point_type(&item, self.cx.path.len() + 1) { + EntryPointType::MainNamed | + EntryPointType::MainAttr | + EntryPointType::Start => + item.map(|ast::Item {id, ident, attrs, node, vis, span}| { + let allow_str = InternedString::new("allow"); + let dead_code_str = InternedString::new("dead_code"); + let allow_dead_code_item = + attr::mk_list_item(allow_str, + vec![attr::mk_word_item(dead_code_str)]); + let allow_dead_code = attr::mk_attr_outer(attr::mk_attr_id(), + allow_dead_code_item); + + ast::Item { + id: id, + ident: ident, + attrs: attrs.into_iter().filter_map(|attr| { + if !attr.check_name("main") { + Some(attr) + } else { + None + } + }) + .chain(iter::once(allow_dead_code)) + .collect(), + node: node, + vis: vis, + span: span } - }).collect(), - node: node, - vis: vis, - span: span - } - }) + }), + EntryPointType::None | + EntryPointType::OtherMain => item, + } }); if !tests.is_empty() || !tested_submods.is_empty() { diff --git a/src/test/compile-fail/test-warns-dead-code.rs b/src/test/compile-fail/test-warns-dead-code.rs new file mode 100644 index 0000000000000..0e25f1e965ab9 --- /dev/null +++ b/src/test/compile-fail/test-warns-dead-code.rs @@ -0,0 +1,17 @@ +// Copyright 2015 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. + +// compile-flags: --test + +#![deny(dead_code)] + +fn dead() {} //~ error: function is never used: `dead` + +fn main() {} diff --git a/src/test/run-pass/test-main-not-dead-attr.rs b/src/test/run-pass/test-main-not-dead-attr.rs new file mode 100644 index 0000000000000..295559b6ddb6f --- /dev/null +++ b/src/test/run-pass/test-main-not-dead-attr.rs @@ -0,0 +1,18 @@ +// Copyright 2015 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. + +// compile-flags: --test + +#![feature(main)] + +#![deny(dead_code)] + +#[main] +fn foo() { panic!(); } diff --git a/src/test/run-pass/test-main-not-dead.rs b/src/test/run-pass/test-main-not-dead.rs new file mode 100644 index 0000000000000..7de3ca7479659 --- /dev/null +++ b/src/test/run-pass/test-main-not-dead.rs @@ -0,0 +1,15 @@ +// Copyright 2015 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. + +// compile-flags: --test + +#![deny(dead_code)] + +fn main() { panic!(); } From 0112e7bd159968d2ad2e5ea6727564c200b7c35f Mon Sep 17 00:00:00 2001 From: William Throwe Date: Mon, 24 Aug 2015 11:34:04 -0400 Subject: [PATCH 3/4] Move main removal to its own pass in --test mode This handles the case where the #[main] function is buried deeper in the ast than we search for #[test] functions. I'm not sure why one would want to do that, but since it works in standard compilation it should also work for tests. --- src/libsyntax/test.rs | 95 +++++++++++-------- .../run-pass/test-runner-hides-buried-main.rs | 24 +++++ 2 files changed, 80 insertions(+), 39 deletions(-) create mode 100644 src/test/run-pass/test-runner-hides-buried-main.rs diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 3fbcbd728aabc..9975e25f4930d 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -175,45 +175,6 @@ impl<'a> fold::Folder for TestHarnessGenerator<'a> { let tests = mem::replace(&mut self.tests, tests); let tested_submods = mem::replace(&mut self.tested_submods, tested_submods); - // Remove any #[main] from the AST so it doesn't clash with - // the one we're going to add. Only if compiling an executable. - - mod_folded.items = mem::replace(&mut mod_folded.items, vec![]).move_map(|item| { - match entry::entry_point_type(&item, self.cx.path.len() + 1) { - EntryPointType::MainNamed | - EntryPointType::MainAttr | - EntryPointType::Start => - item.map(|ast::Item {id, ident, attrs, node, vis, span}| { - let allow_str = InternedString::new("allow"); - let dead_code_str = InternedString::new("dead_code"); - let allow_dead_code_item = - attr::mk_list_item(allow_str, - vec![attr::mk_word_item(dead_code_str)]); - let allow_dead_code = attr::mk_attr_outer(attr::mk_attr_id(), - allow_dead_code_item); - - ast::Item { - id: id, - ident: ident, - attrs: attrs.into_iter().filter_map(|attr| { - if !attr.check_name("main") { - Some(attr) - } else { - None - } - }) - .chain(iter::once(allow_dead_code)) - .collect(), - node: node, - vis: vis, - span: span - } - }), - EntryPointType::None | - EntryPointType::OtherMain => item, - } - }); - if !tests.is_empty() || !tested_submods.is_empty() { let (it, sym) = mk_reexport_mod(&mut self.cx, tests, tested_submods); mod_folded.items.push(it); @@ -230,6 +191,58 @@ impl<'a> fold::Folder for TestHarnessGenerator<'a> { } } +struct EntryPointCleaner { + // Current depth in the ast + depth: usize, +} + +impl fold::Folder for EntryPointCleaner { + fn fold_item(&mut self, i: P) -> SmallVector> { + self.depth += 1; + let folded = fold::noop_fold_item(i, self).expect_one("noop did something"); + self.depth -= 1; + + // Remove any #[main] 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) { + EntryPointType::MainNamed | + EntryPointType::MainAttr | + EntryPointType::Start => + folded.map(|ast::Item {id, ident, attrs, node, vis, span}| { + let allow_str = InternedString::new("allow"); + let dead_code_str = InternedString::new("dead_code"); + let allow_dead_code_item = + attr::mk_list_item(allow_str, + vec![attr::mk_word_item(dead_code_str)]); + let allow_dead_code = attr::mk_attr_outer(attr::mk_attr_id(), + allow_dead_code_item); + + ast::Item { + id: id, + ident: ident, + attrs: attrs.into_iter().filter_map(|attr| { + if !attr.check_name("main") { + Some(attr) + } else { + None + } + }) + .chain(iter::once(allow_dead_code)) + .collect(), + node: node, + vis: vis, + span: span + } + }), + EntryPointType::None | + EntryPointType::OtherMain => folded, + }; + + SmallVector::one(folded) + } +} + fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec, tested_submods: Vec<(ast::Ident, ast::Ident)>) -> (P, ast::Ident) { let super_ = token::str_to_ident("super"); @@ -265,6 +278,10 @@ fn generate_test_harness(sess: &ParseSess, krate: ast::Crate, cfg: &ast::CrateConfig, sd: &diagnostic::SpanHandler) -> ast::Crate { + // Remove the entry points + let mut cleaner = EntryPointCleaner { depth: 0 }; + let krate = cleaner.fold_crate(krate); + let mut feature_gated_cfgs = vec![]; let mut cx: TestCtxt = TestCtxt { sess: sess, diff --git a/src/test/run-pass/test-runner-hides-buried-main.rs b/src/test/run-pass/test-runner-hides-buried-main.rs new file mode 100644 index 0000000000000..7ba10850403e0 --- /dev/null +++ b/src/test/run-pass/test-runner-hides-buried-main.rs @@ -0,0 +1,24 @@ +// Copyright 2015 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. + +// compile-flags: --test + +#![feature(main)] + +#![allow(dead_code)] + +mod a { + fn b() { + || { + #[main] + fn c() { panic!(); } + }; + } +} From 8320a3a048717f2a09ba5e5cddb2b634047da647 Mon Sep 17 00:00:00 2001 From: William Throwe Date: Mon, 24 Aug 2015 14:33:22 -0400 Subject: [PATCH 4/4] Remove #[start] as well as #[main] in --test Fixes #11766. --- src/libsyntax/test.rs | 15 ++++++--------- src/test/run-pass/test-runner-hides-start.rs | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 src/test/run-pass/test-runner-hides-start.rs diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 9975e25f4930d..7fb8cdde31170 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -202,8 +202,8 @@ impl fold::Folder for EntryPointCleaner { let folded = fold::noop_fold_item(i, self).expect_one("noop did something"); self.depth -= 1; - // Remove any #[main] from the AST so it doesn't clash with - // the one we're going to add, but mark it as + // 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) { EntryPointType::MainNamed | @@ -221,13 +221,10 @@ impl fold::Folder for EntryPointCleaner { ast::Item { id: id, ident: ident, - attrs: attrs.into_iter().filter_map(|attr| { - if !attr.check_name("main") { - Some(attr) - } else { - None - } - }) + attrs: attrs.into_iter() + .filter(|attr| { + !attr.check_name("main") && !attr.check_name("start") + }) .chain(iter::once(allow_dead_code)) .collect(), node: node, diff --git a/src/test/run-pass/test-runner-hides-start.rs b/src/test/run-pass/test-runner-hides-start.rs new file mode 100644 index 0000000000000..fc94b19ada1fb --- /dev/null +++ b/src/test/run-pass/test-runner-hides-start.rs @@ -0,0 +1,16 @@ +// Copyright 2015 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. + +// compile-flags: --test + +#![feature(start)] + +#[start] +fn start(_: isize, _: *const *const u8) -> isize { panic!(); }