Skip to content

台账 AST 计数器把 strictObject(...).passthrough() 读成 strict —— postureOf() 在 helper 惯用法上提前返回、不走链 #5072

Description

@xuyushun441-sys

发现于 #4001 批 18(ui/view.zod.ts),范围外,未在该 PR 修。

缺陷

packages/spec/scripts/lib/strictness-ledger.tspostureOf() 第一行就对 campaign 自己的 helper 惯用法短路:

function postureOf(call: ts.CallExpression, idiom: string): Posture {
  if (idiom === 'strictObject' || idiom === 'z.strictObject') return 'strict';
  // ...只有走到这里才会遍历 .strict() / .passthrough() / .catchall() 链

z.object(...) 它会正确地走链;对 strictObject(...)直接返回 strict,不看后面挂了什么。于是 strictObject(...).passthrough() —— 一个运行期开放的形状 —— 在台账里被记为 strict

实测(非阅读推断)

GanttConfigSchema.parse({ startDateField:'a', endDateField:'b', titleField:'c', lockField:'x' })
  => { startDateField:'a', endDateField:'b', titleField:'c', lockField:'x' }   // lockField 原样通过
TreeConfigSchema.parse({ labelField:'a', parentField:'b', zzz:1 })
  => { parentField:'b', labelField:'a', zzz:1 }                                 // zzz 原样通过

两者的 .passthrough() 都是刻意的(注释写明:让 renderer-ahead 的 config 旋钮到达 plugin-gantt / plugin-tree,不必等 spec 发版),objectui 侧也有对应记录(types/src/zod/objectql.zod.ts 的注释直接引用「the same reason the spec puts it on GanttConfigSchema/TreeConfigSchema」)。问题不在这两个 schema,在读它们的尺。

影响面(AST 精确统计,全 packages/spec/src)

恰好 2 处,都在 ui/view.zod.ts:GanttConfigSchemaTreeConfigSchema

⚠️ 统计这个数字本身踩了一次坑,值得记:用「站点行往后扫 N 行找 }).passthrough()」的正则法得到 11,因为窗口会捞到后面别的站点的链尾。改成真正沿 parent 走链的 AST 判定后是 2别用行窗口复核这条,会得到一个吓人的假数。

为什么值得修(而不是「只差 2 个,无所谓」)

  • 虚增 strict 计数:ui/ 的 strict 读数里有 2 个其实是 passthrough。战役的排期与「还剩多少」都读这个数。
  • 方向更糟的那一半:它让一个开放形状在地图上长得像已关闭。战役的整个判据是「未声明键会不会被静默吃掉」,而这把尺对 helper 惯用法给出的答案与运行期相反。
  • 这是台账自己的第 N 次「计数器返回一个错误的读数,遍历正确地尊重了那个读数」——与 fix(spec): #4001 重测 —— 实测剩余站点清单,并修好测量它的那把尺 #4852 修掉的那次(time-relative-trigger 折行写法计成 0、零站点文件被覆盖检查跳过)是同一个形状,只是这次盲区落在 posture 而不是 count 上。fix(spec): #4001 重测 —— 实测剩余站点清单,并修好测量它的那把尺 #4852idiomOf/nameOf 都重建在 AST 上了,postureOf 是同一次重建里唯一保留了短路的那个。

处方

postureOf()strictObject / z.strictObject 应当以 'strict'初值继续走链,而不是提前返回 —— 与 z.looseObject 同理(它也是提前返回 'passthrough',同样挡住了 .strict() 覆写,虽然目前全仓无实例)。

let posture: Posture =
  idiom === 'strictObject' || idiom === 'z.strictObject' ? 'strict'
  : idiom === 'z.looseObject' ? 'passthrough'
  : 'strip';
// …然后一律走链,让链上最后一个显式调用赢

修的时候请先证红:把 GanttConfigSchema 的期望姿态钉成 passthrough,确认 scripts/strictness-ledger.test.ts 在修之前是红的。改完 ui/ 的 strict 计数会 −2、passthrough 计数 +2,台账表头需要同步(strip 计数不变,所以 remaining-strip 那张双向表不受影响)。

顺带值得判一下:这两处是否该在台账里单列一类。它们既不是 authorable(已关)也不是留开的 strip —— 是刻意开放的父 + 已关闭的子块(批 18 关掉了 GanttConfig.tooltipFields,父仍开放)。现有四类(real door / no gate / no door / wire)都不完全贴。

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions