Skip to content
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

Introduce Custom Test Frameworks #53410

Merged
merged 4 commits into from
Sep 5, 2018
Merged
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
1 change: 1 addition & 0 deletions src/Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2739,6 +2739,7 @@ name = "syntax_ext"
version = "0.0.0"
dependencies = [
"fmt_macros 0.0.0",
"log 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"proc_macro 0.0.0",
"rustc_data_structures 0.0.0",
"rustc_errors 0.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# `custom_test_frameworks`

The tracking issue for this feature is: [#50297]

[#50297]: https://github.com/rust-lang/rust/issues/50297

------------------------

The `custom_test_frameworks` feature allows the use of `#[test_case]` and `#![test_runner]`.
Any function, const, or static can be annotated with `#[test_case]` causing it to be aggregated (like `#[test]`)
and be passed to the test runner determined by the `#![test_runner]` crate attribute.

```rust
#![feature(custom_test_frameworks)]
#![test_runner(my_runner)]

fn my_runner(tests: &[&i32]) {
for t in tests {
if **t == 0 {
println!("PASSED");
} else {
println!("FAILED");
}
}
}

#[test_case]
const WILL_PASS: i32 = 0;

#[test_case]
const WILL_FAIL: i32 = 4;
```

1 change: 0 additions & 1 deletion src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,6 @@ where
let (mut krate, features) = syntax::config::features(
krate,
&sess.parse_sess,
sess.opts.test,
sess.edition(),
);
// these need to be set "early" so that expansion sees `quote` if enabled.
Expand Down
67 changes: 40 additions & 27 deletions src/librustc_lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1835,43 +1835,56 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
}

declare_lint! {
UNNAMEABLE_TEST_FUNCTIONS,
UNNAMEABLE_TEST_ITEMS,
Warn,
"detects an function that cannot be named being marked as #[test]"
"detects an item that cannot be named being marked as #[test_case]",
report_in_external_macro: true
}

pub struct UnnameableTestItems {
boundary: ast::NodeId, // NodeId of the item under which things are not nameable
items_nameable: bool,
}

pub struct UnnameableTestFunctions;
impl UnnameableTestItems {
pub fn new() -> Self {
Self {
boundary: ast::DUMMY_NODE_ID,
items_nameable: true
}
}
}

impl LintPass for UnnameableTestFunctions {
impl LintPass for UnnameableTestItems {
fn get_lints(&self) -> LintArray {
lint_array!(UNNAMEABLE_TEST_FUNCTIONS)
lint_array!(UNNAMEABLE_TEST_ITEMS)
}
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestFunctions {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnameableTestItems {
fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
match it.node {
hir::ItemKind::Fn(..) => {
for attr in &it.attrs {
if attr.name() == "test" {
let parent = cx.tcx.hir.get_parent(it.id);
match cx.tcx.hir.find(parent) {
Some(Node::Item(hir::Item {node: hir::ItemKind::Mod(_), ..})) |
None => {}
_ => {
cx.struct_span_lint(
UNNAMEABLE_TEST_FUNCTIONS,
attr.span,
"cannot test inner function",
).emit();
}
}
break;
}
}
if self.items_nameable {
if let hir::ItemKind::Mod(..) = it.node {}
else {
self.items_nameable = false;
self.boundary = it.id;
}
_ => return,
};
return;
}

if let Some(attr) = attr::find_by_name(&it.attrs, "rustc_test_marker") {
cx.struct_span_lint(
UNNAMEABLE_TEST_ITEMS,
attr.span,
"cannot test inner items",
).emit();
}
}

fn check_item_post(&mut self, _cx: &LateContext, it: &hir::Item) {
if !self.items_nameable && self.boundary == it.id {
self.items_nameable = true;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc_lint/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
MutableTransmutes: MutableTransmutes,
UnionsWithDropFields: UnionsWithDropFields,
UnreachablePub: UnreachablePub,
UnnameableTestFunctions: UnnameableTestFunctions,
UnnameableTestItems: UnnameableTestItems::new(),
TypeAliasBounds: TypeAliasBounds,
UnusedBrokenConst: UnusedBrokenConst,
TrivialConstraints: TrivialConstraints,
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,7 @@ pub struct Resolver<'a, 'b: 'a> {
crate_loader: &'a mut CrateLoader<'b>,
macro_names: FxHashSet<Ident>,
macro_prelude: FxHashMap<Name, &'a NameBinding<'a>>,
unshadowable_attrs: FxHashMap<Name, &'a NameBinding<'a>>,
pub all_macros: FxHashMap<Name, Def>,
macro_map: FxHashMap<DefId, Lrc<SyntaxExtension>>,
macro_defs: FxHashMap<Mark, DefId>,
Expand Down Expand Up @@ -1729,6 +1730,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
crate_loader,
macro_names: FxHashSet(),
macro_prelude: FxHashMap(),
unshadowable_attrs: FxHashMap(),
all_macros: FxHashMap(),
macro_map: FxHashMap(),
invocations,
Expand Down
23 changes: 23 additions & 0 deletions src/librustc_resolve/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,23 @@ impl<'a, 'crateloader: 'a> base::Resolver for Resolver<'a, 'crateloader> {
self.macro_prelude.insert(ident.name, binding);
}

fn add_unshadowable_attr(&mut self, ident: ast::Ident, ext: Lrc<SyntaxExtension>) {
let def_id = DefId {
krate: BUILTIN_MACROS_CRATE,
index: DefIndex::from_array_index(self.macro_map.len(),
DefIndexAddressSpace::Low),
};
let kind = ext.kind();
self.macro_map.insert(def_id, ext);
let binding = self.arenas.alloc_name_binding(NameBinding {
kind: NameBindingKind::Def(Def::Macro(def_id, kind), false),
span: DUMMY_SP,
vis: ty::Visibility::Invisible,
expansion: Mark::root(),
});
self.unshadowable_attrs.insert(ident.name, binding);
}

fn resolve_imports(&mut self) {
ImportResolver { resolver: self }.resolve_imports()
}
Expand Down Expand Up @@ -462,6 +479,12 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
return def;
}

if kind == MacroKind::Attr {
if let Some(ext) = self.unshadowable_attrs.get(&path[0].name) {
return Ok(ext.def());
}
}

let legacy_resolution = self.resolve_legacy_scope(&invocation.legacy_scope, path[0], false);
let result = if let Some((legacy_binding, _)) = legacy_resolution {
Ok(legacy_binding.def())
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1587,7 +1587,7 @@ impl TyKind {
if let TyKind::ImplicitSelf = *self { true } else { false }
}

crate fn is_unit(&self) -> bool {
pub fn is_unit(&self) -> bool {
if let TyKind::Tup(ref tys) = *self { tys.is_empty() } else { false }
}
}
Expand Down
15 changes: 2 additions & 13 deletions src/libsyntax/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,16 @@ use ptr::P;

/// A folder that strips out items that do not belong in the current configuration.
pub struct StripUnconfigured<'a> {
pub should_test: bool,
pub sess: &'a ParseSess,
pub features: Option<&'a Features>,
}

// `cfg_attr`-process the crate's attributes and compute the crate's features.
pub fn features(mut krate: ast::Crate, sess: &ParseSess, should_test: bool, edition: Edition)
pub fn features(mut krate: ast::Crate, sess: &ParseSess, edition: Edition)
-> (ast::Crate, Features) {
let features;
{
let mut strip_unconfigured = StripUnconfigured {
should_test,
sess,
features: None,
};
Expand Down Expand Up @@ -118,11 +116,6 @@ impl<'a> StripUnconfigured<'a> {
// Determine if a node with the given attributes should be included in this configuration.
pub fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
attrs.iter().all(|attr| {
// When not compiling with --test we should not compile the #[test] functions
if !self.should_test && is_test_or_bench(attr) {
return false;
}

let mis = if !is_cfg(attr) {
return true;
} else if let Some(mis) = attr.meta_item_list() {
Expand Down Expand Up @@ -249,7 +242,7 @@ impl<'a> StripUnconfigured<'a> {
//
// NB: This is intentionally not part of the fold_expr() function
// in order for fold_opt_expr() to be able to avoid this check
if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a) || is_test_or_bench(a)) {
if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
let msg = "removing an expression is not supported in this position";
self.sess.span_diagnostic.span_err(attr.span, msg);
}
Expand Down Expand Up @@ -352,7 +345,3 @@ impl<'a> fold::Folder for StripUnconfigured<'a> {
fn is_cfg(attr: &ast::Attribute) -> bool {
attr.check_name("cfg")
}

pub fn is_test_or_bench(attr: &ast::Attribute) -> bool {
attr.check_name("test") || attr.check_name("bench")
}
3 changes: 3 additions & 0 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,7 @@ pub trait Resolver {
fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment,
derives: &[Mark]);
fn add_builtin(&mut self, ident: ast::Ident, ext: Lrc<SyntaxExtension>);
fn add_unshadowable_attr(&mut self, ident: ast::Ident, ext: Lrc<SyntaxExtension>);

fn resolve_imports(&mut self);
// Resolves attribute and derive legacy macros from `#![plugin(..)]`.
Expand All @@ -729,6 +730,7 @@ pub trait Resolver {

fn resolve_macro_invocation(&mut self, invoc: &Invocation, scope: Mark, force: bool)
-> Result<Option<Lrc<SyntaxExtension>>, Determinacy>;

fn resolve_macro_path(&mut self, path: &ast::Path, kind: MacroKind, scope: Mark,
derives_in_scope: &[ast::Path], force: bool)
-> Result<Lrc<SyntaxExtension>, Determinacy>;
Expand Down Expand Up @@ -759,6 +761,7 @@ impl Resolver for DummyResolver {
fn visit_ast_fragment_with_placeholders(&mut self, _invoc: Mark, _fragment: &AstFragment,
_derives: &[Mark]) {}
fn add_builtin(&mut self, _ident: ast::Ident, _ext: Lrc<SyntaxExtension>) {}
fn add_unshadowable_attr(&mut self, _ident: ast::Ident, _ext: Lrc<SyntaxExtension>) {}

fn resolve_imports(&mut self) {}
fn find_legacy_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>, _allow_derive: bool)
Expand Down
Loading