修复: 样本的**标签可以撒谎** —— 覆盖表声称覆盖箭头函数,而那份样本根本不是箭头函数

我自己变异出来的(M47),是 M45 的下一层。M45 修的是"样本**少了**要红",
但**名字对不上内容**当时无人管。

## 一、缺陷:标签是散文,内容没人核

实测:把 `['箭头函数', 'const fooSelfTest = () => {};']` 的**内容**换成
`'function fooSelfTest() {}'`(**标签不动**、样本个数不变)
⇒ 默认跑 **0 条 RED**、`red` 仍是 9(与干净树逐字相同)。

★ 后果我实测过,而且它**让报警自己少说话**(不只是少覆盖):
在上面那个撒谎标签的前提下,把 `const` 那条正则删掉(⇒ **箭头写法真的认不出来了**),
报警只点名 `const = async (…`,**"箭头函数"一个字都不提**(`grep -c` = **0**)。

⇒ 覆盖表**声称**覆盖了箭头函数,而报警里那一格**消失了**。
**该说话的地方没说话,而账面上看不出少了什么** —— 我们这一路最怕的那个形状。

★ 与前面几层的关系(**每修一层,缺口换一层**):
· `957ec5e`:发现规则**看不见某种写法**;
· `8691940`:样本表**可以静默缩水**(M45);
· 本次:样本表**可以撒谎**(M47)—— 表上的名字与内容**没有任何关系**。

## 二、修法:给每条样本配一条**见证**(结构,不是散文)

每个样本从 `[名字, 内容]` 变成 `[名字, 内容, 见证]`,见证是"这条样本**凭什么**算那种写法"的
**机械**判据,并判两个方向:
① 内容必须满足自己的见证(⇒ **标签与内容不符 = 红**);
② 见证必须**能区分**:至少要能否掉另一条样本(否则 `/./` 这种空见证也算"覆盖")。
⇒ 把"这条样本是哪一种写法"从**散文**挪进**结构**。

## 三、变异验证(都已还原)

| 变异 | 结果 |
|---|---|
| **M47** 箭头样本内容换成 function 声明(标签不动) | **红**:`这些样本标签与内容不符:箭头函数 ⇒ 覆盖表在说谎,而报警会因此少说话` |
| **M48** 见证退化成 `/./`(对谁都成立) | **红**:`这些见证区分不了任何东西:箭头函数 ⇒ 见证表自己退化成"永远通过"` |
| 复合:**M47 + 箭头支持真坏** | 两条红都出:先抓撒谎,再点名 `const = async (…`(⇒ 撒谎不再能掩盖覆盖) |
| 回归 **M45** 删样本 | 仍 **1 条报警** |
| 回归 **M43** 退化正则 | 仍 **1 条认不出** |

## 四、验证与状态

· 五个自检单独跑仍全 rc=0;默认跑 `checks=459 pass=455 fail=4 skip=0 red=9 … verdict=red`、
  `mutants=48 ran=47 skipped=1 on_new_criteria=35 diag=none baseline=7/7✓`;
  自检段现为 `…发现规则在 5 种写法上都有对照(要求 5 种,缺一即红;每条样本都带能区分它自己的见证)`。
· 残留全清;`git status` 仅本文件;`HEAD=8691940`。
· pi 的 `cc8beb7`(它插件那例)我已复核;它目录一行未动(那两处实验都在 `/tmp` 做)。
This commit is contained in:
2026-09-18 07:29:32 +08:00
parent 8691940505
commit 16cbc02421

View File

@ -2364,11 +2364,11 @@ let selfCheckRuns = 0;
* ⇒ 样本与生产扫描调的是**同一个** `declaredSelfTests`(不是抄一份)。
*/
const FORM_SAMPLES = [
['function 声明', 'function fooSelfTest() {}\n'],
['箭头函数', 'const fooSelfTest = () => {};\n'],
['async function', 'async function fooSelfTest() {}\n'],
['名与括号间有空格', 'function fooSelfTest (a) {}\n'],
['const = async (…', 'const fooSelfTest = async (a) => {};\n'],
['function 声明', 'function fooSelfTest() {}\n', /^function fooSelfTest\(\)/],
['箭头函数', 'const fooSelfTest = () => {};\n', /^const fooSelfTest = \(\) =>/],
['async function', 'async function fooSelfTest() {}\n', /^async function fooSelfTest\(\)/],
['名与括号间有空格', 'function fooSelfTest (a) {}\n', /^function fooSelfTest \(/],
['const = async (…', 'const fooSelfTest = async (a) => {};\n', /^const fooSelfTest = async \(/],
];
/*
* ★★ **样本表本身也必须被钉住**(我自己变异出来的 M45)。
@ -2404,6 +2404,40 @@ let selfCheckRuns = 0;
console.log(`RED 这些样本不在要求清单里(加了样本却没说是为了守哪种写法):${stray.join('、')}`);
reds.push(`(自检)\`FORM_SAMPLES\` 有多余样本未登记进 \`REQUIRED_FORMS\`:${stray.join('、')}`);
}
/*
* ★★ **标签不许撒谎**(我自己变异出来的 M47,比 M45 更深一层)。
*
* 实测:把 `['箭头函数', 'const fooSelfTest = () => {};']` 的**内容**换成
* `'function fooSelfTest() {}'`(**标签不动**、样本个数不变)⇒ 默认跑 **0 条 RED**、`red` 仍是 9。
* ⇒ M45 修的是"样本**个数/名字**少了要红",但**名字对不上内容**当时无人管:
* 表上写着覆盖"箭头函数",而那份样本**根本不是箭头函数**。
*
* ★ 后果我实测过,而且它**让报警自己少说话**(不只是少覆盖):
* 在上面那个撒谎标签的前提下,把 `const` 那条正则删掉(⇒ **箭头写法真的认不出来了**),
* 报警只点名 `const = async (…`,**"箭头函数"一个字都不提**(`grep -c` = **0**)。
* ⇒ 覆盖表**声称**覆盖了箭头函数,而报警里那一格**消失了**。
* 这正是我们这一路最怕的形状:**该说话的地方没说话,而账面上看不出少了什么。**
*
* ⇒ 修法:每个样本再带一条**见证**(witness)——"这条样本为什么算那种写法"的**机械**判据:
* ① 样本内容必须满足自己的见证(⇒ 标签与内容不符 = 红);
* ② 见证必须**能区分**:至少要能否掉另一条样本(否则 `/./` 这种空见证也算"覆盖")。
* ⇒ 标签是**散文**,见证是**结构**;把"这条样本是哪一种写法"从散文挪进结构。
*/
const liar = FORM_SAMPLES.filter(([, src, w]) => !w.test(src)).map(([name]) => name);
if (liar.length) {
console.log(`RED 这些样本**标签与内容不符**(内容不满足它自己的见证):${liar.join('、')}`
+ ' ⇒ 覆盖表在说谎,而报警会因此**少说话**');
reds.push(`(自检)\`FORM_SAMPLES\` 标签与内容不符:${liar.join('、')}`
+ ' —— 声称覆盖了那种写法,而那份样本不是它');
}
const vacuous = FORM_SAMPLES.filter(([name, , w]) =>
!FORM_SAMPLES.some(([other, otherSrc]) => other !== name && !w.test(otherSrc))).map(([n]) => n);
if (vacuous.length) {
console.log(`RED 这些见证**区分不了任何东西**(对别的样本也成立):${vacuous.join('、')}`
+ ' ⇒ 见证表自己退化成"永远通过"');
reds.push(`(自检)\`FORM_SAMPLES\` 的见证对别的样本也成立:${vacuous.join('、')}`
+ ' —— 空见证等于没覆盖');
}
const missed = FORM_SAMPLES.filter(([, src]) => !declaredSelfTests(src).includes('fooSelfTest'));
if (missed.length) {
console.log(`RED 发现规则认不出这些写法(⇒ 那种写法写了自检也不会被接线防线看见):`
@ -2438,7 +2472,8 @@ let selfCheckRuns = 0;
console.log(`自检段:${SELFTESTS.length} 个自检已在**默认路径**内跑过`
+ `(${SELFTESTS.map(([n]) => `--${n}`).join('、')});`
+ `源码里 ${declared.length} 个 \`*SelfTest\` 函数**全部**在册(无"写了没接线");`
+ `发现规则在 ${FORM_SAMPLES.length} 种写法上都有对照(要求 ${REQUIRED_FORMS.length} 种,缺一即红)`);
+ `发现规则在 ${FORM_SAMPLES.length} 种写法上都有对照(要求 ${REQUIRED_FORMS.length} 种,缺一即红;`
+ '每条样本都带能区分它自己的见证)');
}
}