Slang 编译器诊断系统深入解析:DiagnosticSink、Lua 驱动诊断定义与富诊断渲染
发布时间:2026/9/18 8:58:43 锦皓数字建站

Slang 编译器诊断系统深入解析DiagnosticSink、Lua 驱动诊断定义与富诊断渲染【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang本文聚焦 Shader 编译器 Slang 内部贯穿所有编译管线阶段的诊断Diagnostics系统从中央接收接口DiagnosticSink到以 Lua 表声明诊断、构建期生成 C 枚举与消息表的完整链路再到严重级别、源码位置渲染、错误码命名空间与内部编译器错误ICE处理。读者读完可掌握如何新增一条诊断、如何把 Slang 集成进消费其诊断的工具链以及如何定制错误格式化所需的全部知识。DiagnosticSink所有管线阶段共用的诊断中枢Slang 编译器从词法/预处理、解析、语义检查、AST 到 IR 的降级直到最终代码生成每个阶段都可能产生错误error、警告warning与附注note。这些信息统一汇入一个中心接口DiagnosticSink其声明位于 source/compiler-core/slang-diagnostic-sink.h。子系统的 sink 获取与贯穿方式从源码结构看各编译子系统通过以下方式获得 sink编译请求编译入口持有自己的 sink贯穿到解析器、语义检查器、IR 优化等各级对象sink 之间可以通过setParentSink()/getParentSink()slang-diagnostic-sink.h建立父子关系子 sink 会把格式化后的诊断转发给父 sink新创建的 sink 可以从父 sink 拷贝显示与设置状态flags、颜色模式、Unicode、已启用的警告分组、严重级别覆盖表见构造函数 slang-diagnostic-sink.h。sink 的输出有两种去向若设置了ISlangWriter* writer诊断写入该 writer否则写入内部的StringBuilder outputBufferslang-diagnostic-sink.h。工具可通过getBlobIfNeeded()把累积的诊断取为 blobgetErrorCount()则随时给出当前累计错误数编译流程常用它做提前退出或最终成败判定。diagnose 模板与参数打印sink-diagnose(pos, info, args...)是核心入口模板版本见 slang-diagnostic-sink.h。它把参数打包成DiagnosticArg数组并依据是否设置AlwaysGenerateRichDiagnostics标志分流到旧式diagnoseImpl或富诊断diagnoseRichImpl。参数通过一组printDiagnosticArg重载完成格式化覆盖int32/uint32/int64/uint64/double、String、UnownedStringSlice、Name*、TokenType、Token、IRInst*、Modifier*等类型slang-diagnostic-sink.h因此诊断消息里可以安全地嵌入各类编译器对象。此外diagnoseRaw(severity, message)用于直接以纯文本追加诊断典型场景是转发下游编译器报错diagnoseWithoutSourceView()则用于给已有诊断追加 note 时避免重复打印同一行源码。行为控制 Flagssink-setFlag/resetFlag/isFlagSet控制的Flags位slang-diagnostic-sink.h包括Flag作用VerbosePath显示更详细规范/绝对路径SourceLocationLine有源码时显示定位行HumaneLoc显示 file/line 形式的人类可读位置TreatWarningsAsErrors把覆盖后的警告提升为错误LanguageServer以适合语言服务器的格式输出AlwaysGenerateRichDiagnostics强制旧式诊断走新式富诊断路径MachineReadableDiagnostics以机器可读 TSV 格式输出另有若干渲染选项setSourceLineMaxLength默认 120 字符0 表示不限、setDiagnosticColorModeSLANG_DIAGNOSTIC_COLOR_AUTO/ALWAYS/NEVERAUTO 时依据 writer 是否控制台决定是否着色、setEnableUnicode未显式设置时按控制台输出自动探测。诊断定义Lua 表驱动的声明式体系Slang 的全部诊断集中声明在 Lua 文件中构建期经代码生成转为 C 结构。主文件是 source/slang/slang-diagnostics.lua约 6000 行按错误码段组织0xxxx命令行/宿主平台、15xxx预处理、2xxxx解析、3xxxx语义等另有独立子文件如类型错误集中放在 source/slang/diagnostics/type-errors.lua。表结构schema每条诊断在 Lua 中用辅助函数声明核心字段为name唯一小驼峰/连字符名称如cannot-open-file、function-redefinitioncodeid整数错误码构建期映射为 C 枚举值severityerror、warning、note、internal、fatal之一message消息模板其中~param为插值参数。以 slang-diagnostics.lua 头部注释中的示例为准err( function return type mismatch, 30007, expression type ~expression.type does not match functions return type ~returnType:Type, span({loc expression:Expr, message expression type}), span({loc function:Decl, message function return type}) )插值语法有三类~param默认 String 参数~param:Type带类型的参数Type、Decl、Expr、Stmt、Val、Name、int等~param.member成员访问如~expression.type会自动生成expression-typeDecl直接插值时自动使用.name。位置参数语法为location普通SourceLoc变量或location:Type类型化位置如Decl-getNameLoc()、Expr-loc类型化位置也可作为插值参数复用。辅助函数span、note 与变长结构span(loc, message?)标记一个主/次跨度message可选默认为空note(message, span...)附注必须至少带一个 span第一个为主跨度、其余为次跨度不可嵌套variadic_span(struct_name, loc, message)生成嵌套结构体与ListError errors形式的变长跨度列表见multiple type errors示例variadic_note(struct_name, message, span...)变长附注如候选列表ListCandidate candidatesstandalone_note无主诊断的独立 note如编译耗时提示。位置函数err(name, code, message, [primary_span], ...)与warning(...)的 primary_span 是可选的——无源码位置的诊断如命令行参数错误可以不挂跨度。构建期代码生成整个链路记录在 slang-diagnostics.lua 头部注释slang-diagnostics.lua 用err()/warning()等辅助函数定义诊断source/slang/slang-diagnostics-helpers.lua 处理这些定义先提取span()/note()中的位置再从~插值提取参数自动去重成员访问按位置类型解析slang-rich-diagnostics.h.lua 加载处理结果FIDDLE 模板在 slang-rich-diagnostics.h 中生成每个诊断的 C 结构体直接参数成为成员变量位置成为SourceLoc或类型化指针并在slang-rich-diagnostics.cpp中生成toGenericDiagnostic()展开成员访问、插值参数拼装消息、设置主跨度/次跨度与附注。消费侧头文件是 source/slang/slang-diagnostics.h它明确注释所有诊断现在都由slang-diagnostics.lua定义并经slang-rich-diagnostics.h生成旧的slang-diagnostic-defs.h已移除。该头文件还导出findDiagnosticByName()、getDiagnosticsLookup()与overrideDiagnostic(s)供按名称查询和覆盖诊断严重级别使用。运行时查找由DiagnosticsLookup承担slang-diagnostic-sink.h按精确/宽松名称查找、按 id 查找允许同 id 多条返回先加入者、addAlias别名注册并用MemoryArena管理名称生命周期。新一代声明式语法source/slang/diagnostics/type-errors.lua 展示了一种更结构化的声明形式guinea pig原型诊断以diagnostic ...块描述完整结构diagnostic argument_type_mismatch { code E30019, severity error, flag type-mismatch, message cannot convert argument of type {found} to parameter of type {expected}, params { { name func_name, type String }, { name param_name, type String }, { name param_index, type int }, { name expected, type Type }, { name found, type Type }, }, primary_label { loc arg_loc, message expected {expected}, found {found}, }, secondary_labels { { loc param_loc, message parameter {param_name} declared as {expected} here }, { loc func_loc, message in call to function {func_name} }, }, notes { no implicit conversion exists from {found} to {expected} }, helps { add explicit cast: ({expected}){found_expr} }, }与旧式err(...)相比它显式区分主标签、次标签、notes 与 helps语义更接近面向 IDE 与 LLM 消费的富诊断模型也印证了富诊断的内部表示GenericDiagnostic、toGenericDiagnostic()、getInfo()见 slang-rich-diagnostics.h。严重级别诊断系统的严重级别在Severity枚举中定义slang-diagnostic-sink.h并通过static_assert与公开 API 的SLANG_SEVERITY_*常量保持同步slang-diagnostic-sink.h级别名称getSeverityName输出语义Disableignored被禁用/覆盖掉的诊断Notenote附注提供上下文Warningwarning可编译但有隐患Errorerror常规编译错误Fatalfatal error致命错误通常直接终止Internalinternal error内部编译器错误ICELua 侧与之对应helper 的add_diagnostic校验 severity 只能是error、warning、note、internal、fatal之一slang-diagnostics-helpers.lua并提供internal()/fatal()便捷函数负数或特殊码如-1、99999internal-compiler-error catch-all是内部哨兵不参与编号校验。警告分组与覆盖警告还带有WarningLevel分组仿 clang/gcc 的-Wall/-Wextra/-Wpedantic分组相互独立、非嵌套slang-diagnostic-sink.hDefault始终输出无需开启Extra默认开启m_enabledWarningLevels初始只置该位slang-diagnostic-sink.hAll、Pedantic默认关闭需显式enableWarningLevel()开启。enableWarningLevel()对位索引做边界检查防止越界移位slang-diagnostic-sink.hoverrideDiagnosticSeverity(id, severity, info)支持按诊断 id 单独覆盖例如-Wno-xxx抑制、-werror提升覆盖表m_severityOverrides可按get/setSeverityOverrides在父子 sink 间拷贝。源码位置与消息渲染诊断必须能定位到file:line:column。Slang 用SourceLoc表示抽象源码位置声明于 source/compiler-core/slang-source-loc.h由SourceManager负责映射SourceManager::getHumaneLoc(loc, type)返回HumaneSourceLocslang-source-loc.h把名义位置SourceLocType::Nominal解析为文件路径、行、列。sink通过setSourceManager()绑定SourceManagerslang-diagnostic-sink.h渲染时结合SourceLocationLexer可选回调用于对定位行的 token 做词法高亮未设置时只在SourceLoc处显示单字符 caret与SourceLineMaxLength等选项输出类似 clang 风格的行内定位。设置Flag::SourceLocationLine会显示带 caret 的源码行Flag::HumaneLoc控制是否显示 file/line 人类可读位置。富诊断是更完整的渲染层声明于 source/slang/slang-rich-diagnostics.h每个生成的诊断结构体通过toGenericDiagnostic()转成GenericDiagnostic携带主跨度、次跨度、notes、helps 与消息sink-diagnoseRichImpl()依据覆盖表与位置做最终渲染并支持颜色SlangDiagnosticColor、UnicodeshouldEnableUnicode()与setEnableUnicode()、源行长度上限等选项。SLANG_INTERNAL_ERROR/SLANG_UNIMPLEMENTED/SLANG_DIAGNOSE_UNEXPECTED宏slang-diagnostics.h直接调用富诊断debug 构建下先在诊断前追加note: internal error triggered at 文件:行的原始 note因为主诊断可能中止编译再输出Diagnostics::InternalCompilerError等富诊断对象。错误码命名空间诊断 id 的来源即 slang-diagnostics.lua 各条目的code字段构建期生成 C 枚举Diagnostics::*与消息表同一 id 可被DiagnosticsLookup::getDiagnosticById反查。代码按段分配命名空间0xxxx命令行与宿主平台交互、15xxx预处理、2xxxx解析、3xxxx语义/类型子段内再细分如150xx条件、151xx指令解析、153xxinclude、154xx宏定义、155xx宏展开、156xxpragma。关于诊断的书写规范与文档化约定仓库提供了 docs/diagnostic-guidelines.md完整诊断采用error[E00000]: 主消息-- file.slang:LL:CC 源码片段 主/次标签 note/ help的 Rust/Clang 风格结构错误码建议字母前缀加 5 位数字如E0308、W00001消息要求小写开头、无句末标点、代码元素用反引号、主动语态与牛津逗号并提供-error-formatjson、-show-error-codes、-explain E00001、-max-errorsN、-show-type-aliasesalways|helpful|never、-colorauto|always|never、-verbose-diagnostics等命令行选项说明以及错误级联抑制、优先级排序语法错误 模块错误 类型定义 接口实现 类型不匹配 其他语义 警告 remark与 IDE/LSP 集成DiagnosticRelatedInformation、CodeAction、severity 映射的指导。内部编译器错误ICE编译器自身的断言失败会以Severity::Internalinternal error上报最终落到 catch-all 诊断internal-compiler-errorLua 侧码99999。断言宏与 sink 的交互核心断言宏定义在 source/core/slang-common.hSLANG_ASSERT(VALUE)debug 构建_DEBUG下调用::Slang::handleAssert(#VALUE, __FILE__, __LINE__, false)release 构建下退化为SLANG_ASSUME(VALUE)对编译器提示假设不执行检查SLANG_RELEASE_ASSERT(VALUE)任何构建下都调用handleAssert(..., true)。SLANG_ASSERT_FAILURE(msg)宏则定义在 source/core/slang-signal.h同样转发到handleAssert。handleAssert的实现位于 source/core/slang-signal.cpp其关键行为读取环境变量SLANG_ASSERTgetenv(SLANG_ASSERT)见 slang-signal.cpp来调节行为读取时刻意不构建StringBuilder避免断言输出路径内部再次触发断言导致重入依据SLANG_ASSERT的值决定是触发调试器断点、打印失败消息还是直接exit(-1)slang-signal.cpp。与 sink 的正式衔接则是通过 source/slang/slang-diagnostics.h 的宏完成SLANG_INTERNAL_ERROR(sink, pos)在 debug 下先diagnoseRaw(Severity::Note, note: internal error triggered at 文件:行)再sink-diagnose(Diagnostics::InternalCompilerError{...})release 下只输出富诊断。SLANG_UNIMPLEMENTED(sink, pos, what)、SLANG_DIAGNOSE_UNEXPECTED(sink, pos, message)结构相同。从源码结构看SLANG_INTERNAL_ERROR等宏要求包含 slang-rich-diagnostics.h 以获得完整的Diagnostics::*结构体定义。新增一个诊断操作检查清单在 Slang 中新增诊断的标准路径结合 slang-diagnostics.lua 头部示例与 docs/diagnostic-guidelines.md 的最佳实践选定 Lua 文件与编号段通用诊断写入 source/slang/slang-diagnostics.lua类型相关诊断写入 source/slang/diagnostics/type-errors.lua从对应错误码段如3xxxx取未占用的 id避免与既有码冲突可用DiagnosticsLookup::getDiagnosticById验证写消息文本小写开头、无句末标点、代码元素用反引号通过~param、~param:Type、~param.member描述参数Decl直接插值会自动取.name声明参数与位置能用span({loc expr:Expr, message ...})声明主跨度用note(...)/variadic_span/variadic_note补充次跨度与附注primary_span 可选无位置的命令行类诊断可省略选择严重级别err()或warning()需要分组管控的警告可传extra/pedantic哨兵使其默认受-W组开关控制特殊场景用internal()/fatal()/standalone_note()在合适位置调用 sink找到持有DiagnosticSink的编译站点调用sink-diagnose(pos, Diagnostics::YourDiagnostic{...})富诊断结构体或sink-diagnose(pos, info, args...)旧式路径无源码位置的用diagnoseRaw考虑覆盖与级联如需支持-Wno-xxx抑制确保诊断注册进DiagnosticsLookup富诊断由getRichDiagnosticsInfo()自动注册遵循错误级联抑制原则标记受污染符号、限制传播补测试按 docs/diagnostic-guidelines.md 的约定为诊断编写至少一个测试正例与负例验证消息文本、跨度与 fix-it 建议。延伸阅读docs/diagnostic-guidelines.md诊断消息书写规范、错误码格式、JSON 输出与 IDE 集成约定source/compiler-core/slang-diagnostic-sink.hsink 完整接口、DiagnosticInfo、DiagnosticsLookupsource/slang/slang-diagnostics.lua 与 source/slang/slang-diagnostics-helpers.lua诊断定义 DSL 与构建期处理逻辑source/slang/slang-rich-diagnostics.h富诊断结构体的 FIDDLE 生成模板与消费接口source/core/slang-common.h、source/core/slang-signal.cpp断言与 ICE 上报的底层实现。【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。