Repository navigation
踩坑记录
这个项目里反复出现同一类缺陷。这一页把它们归类——
不是为了记录"我们修了 31 个 bug",而是为了指出: 它们几乎全是同一个形状。
几乎每一个坑都是:
同一个概念在两个地方各写了一份规则,然后两份漂移了。
它们的共同特征是:
- ❌ 不发生异常
- ❌ 不报错
- ✅ 只是行为不对(静默错误)
赋值"成功"了,不报错,只是没效果。
程序"跑完"了,不报错,只是答案不对。
所以不能靠"跑起来没报错"判断正确性。
累计已 12 次以上。
#12 / #13
早期解码失败时会"返回 0"兜底。于是:
$([1, [2, [3]]]) → [1, 0, [0]] 内层数据全丢,但不报错
$(["a,b", "c"]) → 被切成 3 个元素
根因:列表原先用 "," 拼接、str.split(",") 切分。
内层列表编码后本身含逗号(L:[N:2,L:[N:3]]),
被外层一刀切开就成了残缺片段,正则匹配失败,
于是又落进那个"返回 0"的兜底分支——同一个兜底分支连犯两案。
修法:改用长度/个数前缀编码,元素边界由类型自己界定, 不依赖任何分隔符。见 ADR-0003。
教训:兜底分支如果不区分"合法的空值"和"我解析失败了", 它就会静默地把数据变成 0。
#22
全局「0」(在函数外写)报的是:
这里需要一个缩进的语句块(if 后面必须跟缩进)
而实际错误跟 if 毫无关系。
根因:这条信息是加 while/for/def/class 之前写的,
那时能开块的只有 if,所以直接写死了 "if 后面"。
#31
未知 > 1 抛出的是 Python 的裸异常:
TypeError: '>' not supported between instances of 'str' and 'int'
用户看到的是宿主实现的报错,而不是带位置的「炑」。
教训:错误信息里的具体名字会过期。加新语法时要回头改旧提示。
#23
空行会让整段缩进比较被跳过,DEDENT 全丢,
块永远不结束,第二个顶层 def 被当成还在 class 体内。
#26
break 写在函数里的循环外,直到运行到那一行才报错。
根因:最初只管解析、不管上下文,循环合法性留到运行期由异常冒泡发现。
#27
循环体里定义函数、函数体里写 break,会把调用方的循环打断:
while i < 3
def f()
break ← 这个 break 干掉了外层的 while
f()
$(i) → i 停在 0,而不是 3
根因:loop_depth 是解析器的一个字段,进入函数体时没有清零。
教训:作用域类状态(
loop_depth、缩进栈)在跨界时要显式重置, 不能靠"反正会自己恢复"。
#19
self.x「1」 报 实例上没有属性 'x'。
根因:把整个 AttrNode 传给了赋值函数,于是它去求值 self.x——
那是在读这个属性,而此时属性还不存在,所以报错。
赋值要做的是"求值左边的主体(self),然后往它上面写属性名"。
#30
m[0][1]「55」 写不进去,读回来还是老值,而且不报错。
根因(两层):_postfix_tail 把 m[0][1] 解析成嵌套的
IndexNode(IndexNode(m, 0), 1),写入路径没处理嵌套。
教训:读和写是两条路径。只测读不测写, 写入路径的 bug 会一直躲着。
#29
改正则加 {} 和 : 时把 - 漏掉了,于是所有减法/负数都报错。
教训:正则里每加一个字符都要想"原来能过的那些还过得去吗"。
IDLE 原设计是"每种语言一个目录",所以关键字表散落在各模块里。 本项目继承了这种散落,于是同一个概念有 9 份副本。
漏改一处的症状:
| 漏改哪张表 | 症状 |
|---|---|
colorizer |
新关键字不高亮 |
autocomplete |
Tab 补不出来 |
pyparse 块开启者 |
写完按回车多缩进一级 |
STATEMENT_KEYWORDS |
if 新词 语法坏了
|
全都是静默失效,不报错。
所以 tests/test_keyword_sync.py 逐项检查,
包括反向断言("某某不该在哪张表里")。
教训:反向断言比正向断言重要。只说"该有的都有", 挡不住"不该有的混进去了"。
第四版加 未知 时发现:关键字表不只一份,而且它们的"归属"不一样。
-
and/or/not要高亮补全,但绝不能进STATEMENT_KEYWORDS -
break/continue要高亮补全,但绝不能进"块开启者", 反而必须进pyparse._closere -
未知要高亮补全,但绝不能进STATEMENT_KEYWORDS(它是值不是语句,加进去会破坏if 未知)
所以"同步关键字表"不是无脑复制——得先想清楚每张表的语义。
#34
写三值逻辑测试时,有一项挂了,查下来是测试写错了:
check("每次静默观测都带提示", all("提示" in obs.hint ...))恒为假。因为 hint 字段存的是建议正文("若要确定分支……"),
而 提示: 这个标签是渲染时才加的。查错了地方。
ADR-0008 第三条记过同一类事:报告失败之前, 先确认测试测的是不是你以为的那件事。
这些坑有一个共同点:都是"看起来没问题"的代码。
所以本项目的验证纪律是:
- 改动后必须跑实际程序,不是只看代码像对的
- 自测要验证最终效果,不是中间步骤
- 一个测不出失败的测试比没有测试更坏——它会给你虚假的信心
- 验收要覆盖每一条进入路径(读路径、写路径、嵌套路径、空值路径)
本项目连"输出正确"都不追求,但每条记录都要求"已验证"。