Skip to content

Commit 0d1b0c0

Browse files
committed
test: self-verifying ASSERT_FILTER mode for ItemFilter
The integration test now validates at the data-structure level instead of grepping rendered output. When ASSERT_FILTER=1 is set alongside SKIP_LANG_START=1, apply_all() asserts two things: 1. The filter's exclusion set is non-empty (precondition: lang_start items were present to begin with). 2. No matching items survive in the filtered output (postcondition: the filter actually worked). The precondition check is pedantically complete: std::rt::lang_start is the runtime wrapper rustc generates for every crate with a fn main, so it will always appear in monomorphized output. Every one of the 29 test programs has a main function and every expected output contains exactly 11 lang_start references. It would take a fundamental change to rustc's runtime initialization for this to stop being true; but if it does, the assertion will tell us the test data is stale rather than silently passing. The more realistic failure mode: someone adds a library crate (no main) to the test suite. The assert message spells this out and gives two actionable options (skip lib crates in the test target, or gate the filter on the presence of main).
1 parent 474f0b7 commit 0d1b0c0

2 files changed

Lines changed: 46 additions & 5 deletions

File tree

Makefile

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,8 @@ test-skip-lang-start:
5454
name=$$(basename $${rust} .rs); \
5555
d2=$${dir}/$${name}.smir.d2; \
5656
echo "$$rust"; \
57-
SKIP_LANG_START=1 ${SMIR} --out-dir $${dir} $${rust} \
57+
SKIP_LANG_START=1 ASSERT_FILTER=1 ${SMIR} --out-dir $${dir} $${rust} \
5858
|| { report "$$rust" "Conversion failed"; continue; }; \
59-
if grep -q 'lang_start' $${d2} 2>/dev/null; then \
60-
report "$$rust" "Output still contains lang_start"; \
61-
fi; \
6259
rm -f $${d2}; \
6360
done; \
6461
[ -z "$$errors" ] || (echo "===============\nFAILING TESTS:$$errors"; exit 1)

src/mk_graph/mod.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub use util::GraphLabelString;
2929

3030
/// A predicate that identifies items to exclude from graph output.
3131
/// Each variant corresponds to an environment variable that enables it.
32+
#[derive(Debug)]
3233
pub(crate) enum ItemFilter {
3334
/// Exclude `std::rt::lang_start` and items only reachable through it.
3435
/// Enabled by `SKIP_LANG_START=1`.
@@ -58,17 +59,60 @@ impl ItemFilter {
5859
///
5960
/// After this call, `ctx.resolve_call_target()` returns `None` for any
6061
/// excluded function, so renderers don't need a separate exclusion set.
62+
///
63+
/// When `ASSERT_FILTER=1` is set (intended for integration tests), this
64+
/// asserts that each filter actually matched something and that no
65+
/// matching items survive after filtering.
6166
pub fn apply_all(items: &mut Vec<Item>, ctx: &mut GraphContext) {
6267
let filters = Self::enabled();
6368
if filters.is_empty() {
6469
return;
6570
}
71+
let assert_mode = std::env::var("ASSERT_FILTER").is_ok();
6672
let mut excluded = HashSet::new();
6773
for filter in &filters {
68-
excluded.extend(filter.compute_exclusions(items, ctx));
74+
let filter_excluded = filter.compute_exclusions(items, ctx);
75+
// The precondition assert checks that the test input actually
76+
// contains items this filter targets. For LangStart, this holds
77+
// for any crate with `fn main` because rustc always emits
78+
// `std::rt::lang_start` as the runtime entry wrapper. If a test
79+
// program is a library crate (no main), lang_start won't be
80+
// present and this assert will fire; in that case, either skip
81+
// the lib crate in test-skip-lang-start or gate LangStart on
82+
// the presence of a main function.
83+
if assert_mode {
84+
assert!(
85+
!filter_excluded.is_empty(),
86+
"ASSERT_FILTER: {:?} matched no items. \
87+
If the test input is a library crate (no fn main), \
88+
std::rt::lang_start won't be present; either exclude \
89+
lib crates from test-skip-lang-start or adjust the \
90+
filter precondition.",
91+
filter
92+
);
93+
}
94+
excluded.extend(filter_excluded);
6995
}
7096
items.retain(|i| !excluded.contains(&i.symbol_name));
7197
ctx.functions.retain(|_, name| !excluded.contains(name));
98+
if assert_mode {
99+
for filter in &filters {
100+
assert!(
101+
!filter.survives(items),
102+
"ASSERT_FILTER: {:?} items survived filtering",
103+
filter
104+
);
105+
}
106+
}
107+
}
108+
109+
/// Check whether any items matching this filter remain after filtering.
110+
fn survives(&self, items: &[Item]) -> bool {
111+
match self {
112+
ItemFilter::LangStart => items
113+
.iter()
114+
.any(|i| is_std_rt_lang_start(&i.mono_item_kind)),
115+
}
72116
}
73117
}
74118

0 commit comments

Comments
 (0)