Skip to content

Commit e552840

Browse files
committed
Document test harness generation
Also ensure that we're consistently using the def-site span when appropriate.
1 parent c8cf9f5 commit e552840

File tree

1 file changed

+55
-27
lines changed

1 file changed

+55
-27
lines changed

src/libsyntax_ext/test_harness.rs

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct Test {
2525

2626
struct TestCtxt<'a> {
2727
ext_cx: ExtCtxt<'a>,
28+
def_site: Span,
2829
test_cases: Vec<Test>,
2930
reexport_test_harness_main: Option<Symbol>,
3031
test_runner: Option<ast::Path>,
@@ -125,6 +126,7 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
125126
struct EntryPointCleaner {
126127
// Current depth in the ast
127128
depth: usize,
129+
def_site: Span,
128130
}
129131

130132
impl MutVisitor for EntryPointCleaner {
@@ -141,8 +143,10 @@ impl MutVisitor for EntryPointCleaner {
141143
EntryPointType::MainAttr |
142144
EntryPointType::Start =>
143145
item.map(|ast::Item {id, ident, attrs, node, vis, span, tokens}| {
144-
let allow_ident = Ident::with_dummy_span(sym::allow);
145-
let dc_nested = attr::mk_nested_word_item(Ident::from_str("dead_code"));
146+
let allow_ident = Ident::new(sym::allow, self.def_site);
147+
let dc_nested = attr::mk_nested_word_item(
148+
Ident::from_str_and_span("dead_code", self.def_site),
149+
);
146150
let allow_dead_code_item = attr::mk_list_item(allow_ident, vec![dc_nested]);
147151
let allow_dead_code = attr::mk_attr_outer(allow_dead_code_item);
148152

@@ -180,15 +184,26 @@ fn generate_test_harness(sess: &ParseSess,
180184
krate: &mut ast::Crate,
181185
features: &Features,
182186
test_runner: Option<ast::Path>) {
183-
// Remove the entry points
184-
let mut cleaner = EntryPointCleaner { depth: 0 };
185-
cleaner.visit_crate(krate);
186-
187187
let mut econfig = ExpansionConfig::default("test".to_string());
188188
econfig.features = Some(features);
189189

190+
let ext_cx = ExtCtxt::new(sess, econfig, resolver);
191+
192+
let expn_id = ext_cx.resolver.expansion_for_ast_pass(
193+
DUMMY_SP,
194+
AstPass::TestHarness,
195+
&[sym::main, sym::test, sym::rustc_attrs],
196+
None,
197+
);
198+
let def_site = DUMMY_SP.with_def_site_ctxt(expn_id);
199+
200+
// Remove the entry points
201+
let mut cleaner = EntryPointCleaner { depth: 0, def_site };
202+
cleaner.visit_crate(krate);
203+
190204
let cx = TestCtxt {
191-
ext_cx: ExtCtxt::new(sess, econfig, resolver),
205+
ext_cx,
206+
def_site,
192207
test_cases: Vec::new(),
193208
reexport_test_harness_main,
194209
test_runner
@@ -202,27 +217,40 @@ fn generate_test_harness(sess: &ParseSess,
202217

203218
/// Creates a function item for use as the main function of a test build.
204219
/// This function will call the `test_runner` as specified by the crate attribute
220+
///
221+
/// By default this expands to
222+
///
223+
/// #[main]
224+
/// pub fn main() {
225+
/// extern crate test;
226+
/// test::test_main_static(&[
227+
/// &test_const1,
228+
/// &test_const2,
229+
/// &test_const3,
230+
/// ]);
231+
/// }
232+
///
233+
/// Most of the Ident have the usual def-site hygiene for the AST pass. The
234+
/// exception is the `test_const`s. These have a syntax context that has two
235+
/// opaque marks: one from the expansion of `test` or `test_case`, and one
236+
/// generated in `TestHarnessGenerator::flat_map_item`. When resolving this
237+
/// identifier after failing to find a matching identifier in the root module
238+
/// we remove the outer mark, and try resolving at its def-site, which will
239+
/// then resolve to `test_const`.
240+
///
241+
/// The expansion here can be controlled by two attributes:
242+
///
243+
/// `reexport_test_harness_main` provides a different name for the `main`
244+
/// function and `test_runner` provides a path that replaces
245+
/// `test::test_main_static`.
205246
fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
206-
// Writing this out by hand:
207-
// pub fn main() {
208-
// #![main]
209-
// test::test_main_static(&[..tests]);
210-
// }
211-
let expn_id = cx.ext_cx.resolver.expansion_for_ast_pass(
212-
DUMMY_SP,
213-
AstPass::TestHarness,
214-
&[sym::main, sym::test, sym::rustc_attrs],
215-
None,
216-
);
217-
let sp = DUMMY_SP.with_def_site_ctxt(expn_id);
247+
let sp = cx.def_site;
218248
let ecx = &cx.ext_cx;
219249
let test_id = Ident::new(sym::test, sp);
220250

221251
// test::test_main_static(...)
222252
let mut test_runner = cx.test_runner.clone().unwrap_or(
223-
ecx.path(sp, vec![
224-
test_id, ecx.ident_of("test_main_static")
225-
]));
253+
ecx.path(sp, vec![test_id, Ident::from_str_and_span("test_main_static", sp)]));
226254

227255
test_runner.span = sp;
228256

@@ -231,17 +259,17 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
231259
vec![mk_tests_slice(cx, sp)]);
232260
let call_test_main = ecx.stmt_expr(call_test_main);
233261

234-
// #![main]
235-
let main_meta = ecx.meta_word(sp, sym::main);
236-
let main_attr = ecx.attribute(main_meta);
237-
238262
// extern crate test
239263
let test_extern_stmt = ecx.stmt_item(sp, ecx.item(sp,
240264
test_id,
241265
vec![],
242266
ast::ItemKind::ExternCrate(None)
243267
));
244268

269+
// #[main]
270+
let main_meta = ecx.meta_word(sp, sym::main);
271+
let main_attr = ecx.attribute(main_meta);
272+
245273
// pub fn main() { ... }
246274
let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(vec![]));
247275

@@ -279,7 +307,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
279307
}
280308

281309
/// Creates a slice containing every test like so:
282-
/// &[test1, test2]
310+
/// &[&test1, &test2]
283311
fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> {
284312
debug!("building test vector from {} tests", cx.test_cases.len());
285313
let ref ecx = cx.ext_cx;

0 commit comments

Comments
 (0)