Skip to content
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
11 changes: 11 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,14 @@ impl SingleAttributeParser for RustcTestMarkerParser {
Some(AttributeKind::RustcTestMarker(value_str))
}
}

pub(crate) struct RustcTestEntrypointMarkerParser;

impl NoArgsAttributeParser for RustcTestEntrypointMarkerParser {
const PATH: &[Symbol] = &[sym::rustc_test_entrypoint_marker];
const ALLOWED_TARGETS: AllowedTargets<'_> =
AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::Closure)]);
const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
const STABILITY: AttributeStability = unstable!(rustc_attrs);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTestEntrypointMarker;
}
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ attribute_parsers!(
Single<WithoutArgs<RustcSpecializationTraitParser>>,
Single<WithoutArgs<RustcStdInternalSymbolParser>>,
Single<WithoutArgs<RustcStrictCoherenceParser>>,
Single<WithoutArgs<RustcTestEntrypointMarkerParser>>,
Single<WithoutArgs<RustcTrivialFieldReadsParser>>,
Single<WithoutArgs<RustcUnsafeSpecializationMarkerParser>>,
Single<WithoutArgs<SplatParser>>,
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_builtin_macros/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
///
/// We mark item with an inert attribute "rustc_test_marker" which the test generation
/// logic will pick up on.
///
/// The test function also gains a `#[rustc_test_entrypoint_marker]` attribute for tools to pick up
/// on. This behavior is *unstable*.
pub(crate) fn expand_test_case(
ecx: &mut ExtCtxt<'_>,
attr_sp: Span,
Expand Down Expand Up @@ -377,6 +380,12 @@ pub(crate) fn expand_test_or_bench(
let test_extern =
cx.item(sp, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, test_ident));

let item = {
let mut item = item;
item.attrs.push(cx.attr_word(sym::rustc_test_entrypoint_marker, attr_sp));
item
};

debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));

if is_stmt {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[
sym::rustc_paren_sugar,
sym::rustc_inherit_overflow_checks,
sym::rustc_reservation_impl,
sym::rustc_test_entrypoint_marker,
sym::rustc_test_marker,
sym::rustc_unsafe_specialization_marker,
sym::rustc_specialization_trait,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_strict_coherence]`.
RustcStrictCoherence(Span),

/// Represents `#[rustc_test_entrypoint_marker]`
RustcTestEntrypointMarker,

/// Represents `#[rustc_test_marker]`
RustcTestMarker(Symbol),

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ impl AttributeKind {
RustcSpecializationTrait => No,
RustcStdInternalSymbol => No,
RustcStrictCoherence(..) => Yes,
RustcTestEntrypointMarker => No,
RustcTestMarker(..) => No,
RustcThenThisWouldNeed(..) => No,
RustcTrivialFieldReads => Yes,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
AttributeKind::RustcSpecializationTrait => (),
AttributeKind::RustcStdInternalSymbol => (),
AttributeKind::RustcStrictCoherence(..) => (),
AttributeKind::RustcTestEntrypointMarker => (),
AttributeKind::RustcTestMarker(..) => (),
AttributeKind::RustcThenThisWouldNeed(..) => (),
AttributeKind::RustcTrivialFieldReads => (),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1856,6 +1856,7 @@ symbols! {
rustc_specialization_trait,
rustc_std_internal_symbol,
rustc_strict_coherence,
rustc_test_entrypoint_marker,
rustc_test_marker,
rustc_then_this_would_need,
rustc_trivial_field_reads,
Expand Down
3 changes: 3 additions & 0 deletions tests/pretty/tests-are-sorted.pp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
testfn: test::StaticTestFn(#[coverage(off)] ||
test::assert_test_result(m_test())),
};
#[rustc_test_entrypoint_marker]
fn m_test() {}

extern crate test;
Expand All @@ -55,6 +56,7 @@
test::assert_test_result(z_test())),
};
#[ignore = "not yet implemented"]
#[rustc_test_entrypoint_marker]
fn z_test() {}

extern crate test;
Expand All @@ -79,6 +81,7 @@
testfn: test::StaticTestFn(#[coverage(off)] ||
test::assert_test_result(a_test())),
};
#[rustc_test_entrypoint_marker]
fn a_test() {}
#[rustc_main]
#[coverage(off)]
Expand Down
84 changes: 84 additions & 0 deletions tests/ui-fulldeps/test_entrypoint_attrs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//@ run-pass
//@ ignore-cross-compile
//@ ignore-remote
//@ edition: 2024
//@ ignore-stage1
//! Uses a rustc driver to check that test entrypoints get a `#[rustc_test_entrypoint_marker]`
//! and can be found using that attribute in rustc drivers (the main use for this attribute).

#![feature(rustc_private)]

extern crate rustc_driver;
extern crate rustc_interface;
extern crate rustc_middle;
#[macro_use]
extern crate rustc_hir;

use interface::Compiler;
use rustc_driver::Compilation;
use rustc_interface::interface;
use rustc_middle::ty::TyCtxt;
use std::io::Write;

const CRATE_NAME: &str = "input";

struct TestAttr {
expected_tests: usize,
}

impl rustc_driver::Callbacks for TestAttr {
fn after_analysis<'tcx>(&mut self, _compiler: &Compiler, tcx: TyCtxt<'tcx>) -> Compilation {
let mut tests = Vec::new();
for did in tcx.hir_crate_items(()).definitions() {
if find_attr!(tcx, did, RustcTestEntrypointMarker) {
tests.push(did);
}
}

// the file contains one test, so we should find one entrypoint marker.
assert_eq!(tests.len(), self.expected_tests);

Compilation::Stop
}
}

fn count_tests(src: &str, expected_tests: usize) {
let path = "test_input.rs";
let mut file = std::fs::File::create(path).unwrap();
file.write_all(src.as_bytes()).unwrap();

let args = [
"rustc".to_string(),
"--test".to_string(),
"--crate-type=lib".to_string(),
"--crate-name".to_string(),
CRATE_NAME.to_string(),
path.to_string(),
];
rustc_driver::catch_fatal_errors(|| -> interface::Result<()> {
rustc_driver::run_compiler(&args, &mut TestAttr { expected_tests });
Comment thread
jdonszelmann marked this conversation as resolved.
Ok(())
})
.unwrap()
.unwrap();
}

fn main() {
count_tests(
r#"
#[test]
fn meow() {{ }}
"#,
1,
);
count_tests(
r#"
#[test]
fn one() {{ }}

#[test]
fn two() {{ }}
"#,
2,
);
}
Loading