ReScript Super Errors 快照测试体系:从运行机制到精美错误输出的完整解析
编译器编程语言开发工具【免费下载链接】rescript-compilerReScript is a robustly typed language that compiles to efficient and human-readable JavaScript.项目地址https://gitcode.com/gh_mirrors/re/rescript-compiler点击查看免费下载本文以 ReScript 编译器仓库中的tests/build_tests/super_errors测试套件为线索系统讲解该项目的超级错误super errors即精美错误展示快照测试的工作原理、运行与快照更新方法并结合仓库源码与预期快照文件剖析 ReScript 编译器在类型错误、签名不匹配、语法错误等场景下的输出格式与定位高亮机制。读完本文你将掌握如何本地运行并更新这一套件、理解其快照对比逻辑并能读懂.expected文件背后的错误渲染规范。一、Super Errors 测试是什么super_errors是 ReScript 编译器仓库中一组专门针对漂亮错误展示pretty error display的回归测试。它的定位在 tests/build_tests/super_errors/README.md 中写得很清楚Special tests for super errors (the pretty error display)。与普通编译器测试只关心是否报错不同这一套件关心的是错误到底以什么样的排版、颜色、位置标注和辅助建议呈现给开发者。任何一个细微的展示改动例如类型名着色、行号对齐、建议文案措辞都会导致快照变化因此这类测试必须依赖预期输出快照来锁定行为。套件由三部分组成见 tests/build_tests/super_errors目录/文件作用fixtures/存放故意写错的.res源文件作为触发错误的输入expected/存放每个 fixture 对应的预期错误输出快照*.expectedinput.js驱动脚本调用bsc编译所有 fixture与快照逐一比对README.md套件说明与运行方式fixtures/下是成对出现的输入文件例如 fixtures/ModuleAwait.res、fixtures/RecordInclusion.res、fixtures/OptionalImplIntf.res 等它们对应的预期快照同样命名为ModuleAwait.res.expected、RecordInclusion.res.expected等一一对应、便于追溯。二、先构建项目再运行测试README 明确给出了标准的本地操作流程先遵循 CONTRIBUTING.md 构建项目然后在仓库根目录执行测试命令。模式一检查check模式node ./tests/build_tests/super_errors/input.js该命令会逐个编译fixtures/下所有.res文件把实际错误输出与expected/下的历史快照做对比任何不一致都会打印新旧输出的差异并最终以非零退出码结束process.exit(1)从而在 CI 或本地开发中暴露回归。模式二更新update模式node ./tests/build_tests/super_errors/input.js update当你有意修改了 super errors 的展示或消息文案例如调整了某条错误建议的措辞、改变了高亮范围预期快照自然需要随之更新。此时追加update参数脚本会把当前实际输出直接写回对应的.expected文件覆盖旧快照。需要留意的是README 中的这两条命令都要求以仓库根目录为当前工作目录执行因为 input.js 中的路径expected目录、belt 库路径等均基于脚本自身位置import.meta.dirname解析但 README 约定的是根目录调用方式。三、驱动脚本源码解析快照对比的完整链路input.js 是一个带// ts-check的 JavaScript 脚本其核心逻辑非常清晰值得逐段拆解3.1 环境与依赖准备import { setup } from #dev/process; import { normalizeNewlines } from #dev/utils; const { bsc } setup(import.meta.dirname);脚本通过#dev/process的setup拿到可用的bscReScript 编译器可执行文件并通过#dev/utils的normalizeNewlines统一换行符保证跨平台Windows/Linux/macOS快照一致。3.2 fixture 发现与编译参数const fixtures readdirSync(path.join(import.meta.dirname, fixtures)) .filter(fileName path.extname(fileName) .res) .sort();只收集fixtures/下.res文件并按文件名排序保证执行顺序稳定。编译参数固定为const prefix [-w, A, -bs-jsx, 4, -I, beltLib];-w A开启全部警告便于同时覆盖警告类快照-bs-jsx 4使用 JSX v4 转换-I beltLib把仓库内的packages/rescript/belt/lib/ocaml加入搜索路径使得依赖 Belt 的 fixture如ModuleAwait.res中的Belt.Option可以正常解析类型。编译时还追加了-color always强制启用 ANSI 颜色输出这是快照能记录下高亮/着色信息的根本原因。3.3 输出规范化function postProcessErrorOutput(output) { let result output; result result.trimEnd(); result result.replace( /(?:[A-Z]:)?[\\/][^ ]?tests[\\/]build_tests[\\/]super_errors\\//g, (_match, path, _offset, _string) /.../ path.replace(\\, /), ); return normalizeNewlines(result); }快照对比前要做两处归一化trimEnd()去掉末尾空白把tests/build_tests/super_errors/之前的绝对路径前缀统一替换为/.../避免不同开发者机器上的绝对路径差异污染快照快照里你会看到诸如/.../fixtures/ModuleAwait.res这样的位置正是这一规范化产生的结果。3.4 单用例执行与比对const { stderr } await bsc([...prefix, -color, always, fullFilePath]);每个 fixture 通过bsc子进程编译错误信息含警告因为警告也走 stderr注释里特别说明了两类特殊情况warning test that actually succeeded in compiling和accidentally succeeding tests从stderr取出。随后把实际输出与expected/下对应快照比对update 模式下则直接写回快照文件if (updateTests) { await fs.writeFile(expectedFilePath, actualErrorOutput); return { fileName, failure: null }; }3.5 并行执行与结果汇总脚本注释解释了并行的动机Each fixture spawns a bsc process, so wall time is dominated by process startup; serialising the loop made the suite scale linearly with fixture count.即每个 fixture 都会拉起一个bsc进程耗时大头是进程启动因此用os.availableParallelism()决定并发数、以 worker-pool 方式并行跑完所有 fixture避免套件规模线性拖慢整体时间。任何失败用例都会把Old/New输出差异完整打印最终以退出码 1 结束。四、从快照看 Super Errors 的渲染规范expected/目录下的快照真实记录了编译器当前的美化输出格式含 ANSI 转义码。结合几个代表性文件可以总结出这套精美错误展示的稳定结构。4.1 类型不匹配 修复建议highlighting 系列以 expected/highlighting1.res.expected 为例其渲染结构为Weve found a bug for you! /.../fixtures/highlighting1.res:1:14-3:3 1 │ let a: int hel 2 │ 3 │ lo This has type: string But its expected to have type: int You can convert string to int with Int.fromString.关键要素Weve found a bug for you!作为错误标题红色加粗是 ReScript 错误输出的标志性开场文件:行:列-行:列标出错误区间随后是带行号与竖线引导的源码摘录出错区间用红色高亮This has type:/But its expected to have type:给出实际类型与期望类型类型名分别着色红色/黄色You can convert ... with ...给出可执行的修复建议此处是建议用Int.fromString做类型转换这是super errors相较普通编译错误的核心增值点。highlighting1到highlighting6六个快照文件专门用于锁定不同场景下的高亮与着色行为。4.2 标识符缺失 模块建议expected/suggest_module_for_missing_identifier.res.expected 展示了找不到值时的智能提示The value console cant be found Maybe you meant to use the module Console?当开发者写出console.log(Hello)时编译器不仅报告console未定义还会根据内置模块命名Console给出是否想使用模块的猜测。同一目录下还有suggest_module_for_missing_identifier_with_spellcheck.res.expected说明该提示还支持拼写纠错。4.3 签名不匹配include / module 约束expected/RecordInclusion.res.expected 展示了模块签名约束失败的完整诊断链Signature mismatch: ... Type declarations do not match: type ta, b, c {x: int, y: list(a, c), z: int} is not included in type ta, b, c {x: int, y: list(a, b), z: int} /.../fixtures/RecordInclusion.res:2:3-58: Expected declaration /.../fixtures/RecordInclusion.res:4:3-58: Actual declaration The types for field y are not equal.其对应的 fixtures/RecordInclusion.res 只是把y字段的类型参数从(a, b)写成(a, c)编译器便给出了Expected declaration / Actual declaration的双位置标注与字段类型不相等的精确结论。类似地expected/OptionalImplIntf.res.expected 针对可选字段x?: intvsx: int报出The optional attribute of field x is different展示了对记录字段属性的细粒度诊断。4.4 未标记变体untagged variant约束以 expected/UntaggedNonUnary1.res.expected 为例unboxed type t Tuple(int, string)会得到This untagged variant definition is invalid: Constructor Tuple has more than one argument.Untagged*系列UntaggedNonUnary1/2、UntaggedDuplicateLiteral、UntaggedDuplicatedBsAs、UntaggedAtMostOne*、UntaggedUnknown等二十余个快照构成了一套针对unboxed/untagged variant 合法性的专门测试矩阵覆盖构造参数个数、重复字面量、未知标签、as冲突等边界情况。4.5 异步上下文错误expected/ModuleAwait.res.expected 中module O: module type of Belt.Option await Belt.Option在非 async 函数体内使用await最终错误结论简洁明确Await on expression not in an async context同时错误区间精确覆盖到2:3-3:11即从module O: ...到O.forEach的整段代码。4.6 覆盖面概览从expected/目录的命名可以看到这套件的覆盖范围非常广大致可归纳为类型系统诊断apply_non_function、arity_mismatch*、function_call_mismatch、if_branch_mismatch、polyvariant_constructor_mismatch、switch_different_types等语法与构造约束syntaxErrors1-5、break_outside_loop、continue_outside_loop、misplaced_label_syntax等FFI / 属性bs_invalid_bs_int_type、bs_conflict_attributes、conflicting_ffi_attributes、bad_unboxed_attribute_*等记录 / 变体展开record_rest_*、variant_spread_*、record_type_spreads*等JSX 组件component_missing_prop、jsx_type_mismatch_option、react_component_missing_jsx_element等警告快照warning_*系列warning_33_unused_open、warning_37_unused_constructor等内置标准库相关stdlib_removed_in_error、suggest_module_for_missing_identifier*等。五、周边关联super_errors_multi 与构建测试整体布局该套件并非孤立存在。在 tests/build_tests 下还存在super_errors_multi/目录其 input.js 同样引用了super_errors命名空间用于覆盖多文件/多错误场景的快照。此外build_tests还包含case/、cycle/、source_map/、react_ppx/、uncurried-always/等三十余个面向不同编译特性的测试目录super_errors在其中专门负责错误渲染质量这一维度。六、实操小结要在本地维护或扩展这套测试推荐工作流如下按 CONTRIBUTING.md 完成项目构建得到可用的bsc在仓库根目录运行node ./tests/build_tests/super_errors/input.js确认当前输出与快照一致若你修改了错误展示逻辑先人工检查node ./tests/build_tests/super_errors/input.js输出的 diff确认每条差异都是有意为之确认无误后运行node ./tests/build_tests/super_errors/input.js update更新快照并把这批.expected变更纳入提交使新行为被后续回归测试锁定。这种fixtures expected 快照 规范化比对的组合是保证 ReScript 编译器错误信息在持续迭代中始终保持高可读性、高一致性的关键工程手段——对编译器使用者而言它决定了你在编辑器里看到的每一行红色提示的排版与措辞对贡献者而言它是修改错误输出时必须通过的质检关卡。赞分享编译器编程语言开发工具【免费下载链接】rescript-compilerReScript is a robustly typed language that compiles to efficient and human-readable JavaScript.项目地址https://gitcode.com/gh_mirrors/re/rescript-compiler点击查看免费下载相关推荐DataFusion sqllogictest 实战SQL 快照测试体系的运行机制与完整操作指南DataFusion sqllogictest 实战SQL 快照测试体系的运行机制与完整操作指南 本文以 DataFusion 仓库中的 datafusion大数据数据分析后端cheat.sh 测试体系指南单元测试与输入/输出回归测试的完整运行方法cheat.sh 测试体系指南单元测试与输入/输出回归测试的完整运行方法 导读 本文以 tests/README.md https://link.gitcod开发工具文档后端Angular Core 运行时错误机制解析从 errors.api.md 读懂 RuntimeError 与错误码体系Angular Core 运行时错误机制解析从 errors.api.md 读懂 RuntimeError 与错误码体系 导读 本文以 Angular 仓库中前端Web框架上一篇Tiny-ECS 开源项目教程下一篇Django Dynamic REST 终极指南如何构建高性能的 GraphQL 风格 API创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考