Skip to content

Commit dd48d93

Browse files
committed
test(motion): 收窄位移常量读口并登记语法洞边界
R29.2 收窄(+7/−3):读口接受类型标注 / Number() 包装 / 括号 / 跨行, 负号按 Math.abs 取值(顺带关掉旧口径的负号半洞)。 R36.1④:不取 fail-closed(非字面量 _PX 的合法来源含跨文件导入与计算, 判红 = 假阳性;本仓口径「判据错杀比漏判更坏」)。剩余边界逐字登记进 文件头 ④⑤(运算式 / 三元 / 模板串 / as const / 十六进制 / 导入右值 / 标注含 => / 缺分号 ASI / 右值含分号),并写明正面口径 =「看得见的形态 必须 ≤8px」,不是「覆盖全部 TS 形态」。 变异体(每变异新解一棵树):R29.2 两形态各 1 树 ⇒ exit 1 红在 V4b 两半; 多行裸字面量阳性对照 ⇒ exit 1;反向对照(只收窄)⇒ 6/6 绿。 残洞形态 5 条 ⇒ exit 0(如实登记,不假装覆盖)。
1 parent 348f8a8 commit dd48d93

1 file changed

Lines changed: 18 additions & 3 deletions

File tree

‎app/src/motion/shift.guard.test.ts‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@
2222
* `engine.guard.test.ts` 同款用法);② **文本级扫描,不做 AST** —— 它只能证「字面量在不在」,
2323
* 不证实现是否真的遵守(评审 I-4 的那条限度);③ 未按 `*_PX` 命名的位移常量(如 `CARD_LIFT = 12`)
2424
* 本判据抓不到,只由评审兜 —— **诚实登记,不声称封死**。
25+
* ④ 🔴 **语法洞的剩余边界(R29.2 收窄后仍未关掉的面 · R36.1①)**:读口是**文本正则、不是 TS 解析器**
26+
* (见 ②)。收窄后它认得「`= 字面量 ;`」这一族(可带类型标注、可套 `Number()`、可套括号、可跨行、
27+
* 负号按 `Math.abs` 取值);**仍看不见**的形态逐字如下:**运算表达式**(`8 + 4`)· **三元**
28+
* (`x ? 12 : 4`)· **模板串**(`` `12` ``)· **`as const`**(`12 as const`)· **十六进制**(`0xc`)·
29+
* **右值来自跨文件导入或计算**(`= IMPORTED_PX` / `= BASE * 2`)。另有三条**结构性限度**:
30+
* 类型标注里含 `=`(如函数类型 `() => void`)会让读口错位 · 缺分号(ASI)的声明会把右值捕到**下一条**
31+
* 声明的 `;` · 右值里含 `;`(字符串 / 模板串内)会被截断。⇒ 这些面**由评审兜**,本判据不声称覆盖。
32+
* ⑤ **正面口径(不许读成全覆盖)**:本判据只保证「**它看得见的形态必须 ≤ 8px**」——**不是**「所有 TS
33+
* 形态都被覆盖」。⇒ **有意不做 fail-closed**:非字面量 `_PX` 的**合法**来源包括跨文件导入与计算 ⇒
34+
* 判红会把合法写法当犯规 = **假阳性**;本仓口径 **判据错杀比漏判更坏**(它会挡住正当改动)——
35+
* R36.1④ 逐字裁定「**不取 fail-closed**」。剩余边界以 ④ 为准,逐字登记,不假装全覆盖。
2536
*/
2637
import { readdirSync, readFileSync, statSync } from "node:fs";
2738
import { dirname, join } from "node:path";
@@ -75,8 +86,10 @@ function shiftViolations(text: string, file: string): string[] {
7586
.map((s) => `${file}: ${s.call} = ${s.value}px > ${SHIFT_MAX_PX}px`);
7687
}
7788

78-
/** `export const <name> = <number>` 形态的数值常量(R11.4 的「位移常量」面) */
79-
const NUMERIC_CONST = /export const ([A-Za-z_$][\w$]*)\s*=\s*(-?\d+(?:\.\d+)?)\s*;/g;
89+
/** 读口(R29.2 收窄):`export const <名>[: <标注>] = <右值>;` —— 右值**整段**交给 `LITERAL_RHS` 判形态 */
90+
const NUMERIC_CONST = /export const ([A-Za-z_$][\w$]*)\s*(?::[^=;]+)?=\s*([\s\S]*?);/g;
91+
/** 字面量右值:裸数字 / `Number(...)` 包装 / 任意层括号(跨行已折叠空白);**不匹配 ⇒ 跳过、不判红**(文件头 ④⑤) */
92+
const LITERAL_RHS = /^\(*\s*(?:Number\s*\(\s*)?(-?\d+(?:\.\d+)?)(?:\s*\))?\s*\)*$/;
8093
const PX_NAME = /_PX$/;
8194

8295
interface NumericConst {
@@ -88,7 +101,9 @@ function numericConstants(text: string, onlyPxNames: boolean): NumericConst[] {
88101
const out: NumericConst[] = [];
89102
for (const m of stripComments(text).matchAll(NUMERIC_CONST)) {
90103
if (onlyPxNames && !PX_NAME.test(m[1])) continue;
91-
out.push({ name: m[1], value: Number(m[2]) });
104+
const lit = LITERAL_RHS.exec(m[2].replace(/\s+/g, " ").trim());
105+
if (lit === null) continue; // 非字面量右值:本读口看不见 —— 剩余边界见文件头 ④(有意不 fail-closed,见 ⑤)
106+
out.push({ name: m[1], value: Math.abs(Number(lit[1])) });
92107
}
93108
return out;
94109
}

0 commit comments

Comments
 (0)