GSoC 2026:改进 clangd 对 HLSL 的支持

💡 原文英文,约1700词,阅读约需7分钟。
📝

内容提要

GSoC 2026 项目改进 clangd 对 HLSL 的支持。作者通过差距分析发现,语义标注、资源绑定、out/inout 参数、向量矩阵 swizzle 等 HLSL 构造在 clangd 的悬停、补全和诊断中表现不佳。经 RFC 社区讨论后,工作拆分为 11 个 GitHub issue 并逐一提交 PR,修复了 out 参数悬停显示、语义标注、swizzle 补全等问题。此外还研究了多入口点问题,为现代 HLSL 实现库模式回退,并原型验证了旧式 HLSL 编译上下文切换。

🔎

延伸解读

问题根源:Clang 与 clangd 的职责分离

文章指出,Clang 能解析 HLSL 并在 AST 中表示许多构造,但 clangd 复用 C/C++ 基础设施提供 IDE 功能,因此不一定知道如何暴露这些信息。例如语义标注、资源绑定、out/inout 参数等 HLSL 特有构造在标准 C++ 中没有直接对应,导致悬停、补全和诊断表现不佳。理解这一分层是定位修复位置的关键。

修复策略:优先扩展现有基础设施

作者通过差距分析区分了问题类型:有些构造已在 AST 中正确表示但 clangd 未暴露,有些涉及配置,还有些源于编译器前端。对于前者,正确做法是扩展现有 clangd 基础设施,而非添加 HLSL 专用解析器或编译器特性。例如语义标注悬停问题通过让 HLSL 属性提供通用悬停基础设施所需的拼写来解决,避免了 HLSL 专用变通。

社区反馈如何影响实现细节

RFC 讨论中,社区反馈直接改变了两个设计:语义补全最初考虑无条件建议 HLSL 语义,后改为依赖已输入前缀;向量和矩阵 swizzle 补全最初考虑在每次输入点号后自动触发,后因可能干扰正常编辑而改为可配置。这表明在大型开源项目中,设计讨论能帮助细化行为,避免过度自动化带来的副作用。

多入口点:现代与旧式 HLSL 的不同挑战

现代 HLSL 可用 [shader("vertex")] 等注解在源码中标记入口点,作者实现了库模式回退,为无编译命令的文件提供通用编译上下文。旧式 HLSL 通过编译器标志选择入口点,不同入口点可能需要不同编译命令,clangd 无法用单一命令正确表示所有入口点。作者原型验证了在活动编译上下文间切换的机制,但尚未形成完整上游方案。

Q&A

GSoC 2026 中改进 clangd 对 HLSL 支持的项目目标是什么?

该项目的目标是识别 clangd 对 HLSL 支持中的差距,并改进这些支持,以提供更好的 IDE 功能,如悬停、代码补全、诊断和导航。

为什么 clangd 对 HLSL 的支持不够好?

因为 clangd 重用了 C/C++ 基础设施来提供 IDE 功能,而 HLSL 有语言特定的构造(如语义标注、资源绑定、out/inout 参数、着色器属性、向量和矩阵 swizzle),这些构造在标准 C++ 中没有直接等价物,因此现有 clangd 基础设施无法自动提供预期的编辑器体验。

项目是如何进行差距分析的?

作者创建了隔离的 HLSL 测试用例,覆盖不同类别的语言构造,并使用 clangd 的悬停、代码补全、跳转定义和诊断功能进行测试。当行为不明确时,还检查了 Clang 的 AST 以确定所需信息是否已可用。目的是区分问题类型:有些构造已在 AST 中正确表示但 clangd 未暴露,有些涉及配置,有些则源于编译器管道更早的阶段。

项目最终创建了多少个 GitHub issue 和 PR?

项目创建了 11 个 GitHub issue,每个 issue 对应一个独立的 pull request,分为悬停和代码补全两个主要领域。

在悬停功能方面,项目修复了哪些 HLSL 构造?

修复了语义标注(如 SV_Target 和 SV_Position)、循环和分支控制属性(如 [unroll] 和 [loop])、out 和 inout 参数限定符、向量 swizzle 和矩阵元素访问、RootSignature 悬停以及 register(...) 悬停范围。

在代码补全方面,项目实现了哪些改进?

实现了属性补全(在 [...] 内)、register(...) 补全、HLSL 注解(在 : 后)、向量 swizzle 补全和矩阵 swizzle 补全。

项目如何处理 HLSL 中的 out 和 inout 参数悬停显示问题?

HLSL 的 out 和 inout 参数在内部被降低为 C++ 引用类型,导致悬停时显示内部表示(如 float &__restrict)而不是 HLSL 类型。解决方案是添加 getHLSLParamTypeAsWritten() 辅助函数,使 clangd 能够恢复原始 HLSL 类型进行显示。

项目对多入口点问题进行了哪些研究?

研究了包含多个着色器入口点的 HLSL 文件。对于现代 HLSL,使用 HLSL 库目标可以让 Clang 构建包含不同入口点的单个 AST,并实现了在没有编译命令时自动使用库目标的回退。对于旧式 HLSL,入口点通过编译器标志选择,需要不同的编译上下文,因此构建了一个原型,允许 clangd 发现入口点并在其编译上下文之间切换,而无需关闭和重新打开文件。

项目未来的工作方向是什么?

主要剩余工作是旧式多入口点问题,需要将原型发展为可上游的解决方案,并配以客户端 UX 来选择活动入口点。此外还有较小的后续机会,如更丰富的 RootSignature 悬停信息、为 .hlsl 添加默认 files.associations 条目以及额外的 HLSL 特定配置改进。

🏷️

标签

➡️

继续阅读