Skip to content

Commit 225c975

Browse files
committed
fix: complete clangd release validation
1 parent 74bb0af commit 225c975

8 files changed

Lines changed: 78 additions & 23 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
## 0.2.4
44

55
- 修复 hermetic mcpp 编译数据库被插件追加 `--query-driver` 后导致的 clangd 标准库和
6-
模块误诊断;仅在编译命令未自带完整 sysroot、标准库和 no-default-config 配置时补充
7-
query driver。
6+
模块误诊断;仅在编译命令未自带 no-default-config 和显式标准库配置时补充 query
7+
driver。
88
- 模块检查优先选择项目源码,避免误选 `.mcpp` 依赖缓存或 `target` 生成源码,改善补全、
99
跳转和诊断稳定性。
1010
-`build.mcpp` 作为独立的语法高亮语言处理,不再让 clangd 把 mcpp 构建脚本当作普通

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ TextMate 语法规则提供。
105105
2. 从显式设置、编译器目录、匹配的 xlings `llvm-tools` 目录和 `PATH` 中查找 clangd。
106106
3. 执行编译器和 clangd 的版本命令,比较 LLVM 版本与 revision。
107107
4. 保留用户已有的 `clangd.arguments`,展开 `${workspaceFolder}`
108-
`${workspaceRoot}`;只有在 CDB 未提供完整 sysroot、标准库和 no-default-config
109-
参数时,才添加精确的 `--query-driver`
108+
`${workspaceRoot}`;只有在 CDB 未提供 no-default-config 和显式标准库配置时,才添加
109+
精确的 `--query-driver`
110110
5. 根据 CDB 中是否已有显式 PCM 映射以及 `mcpp.modulesSupport` 设置,决定是否增加
111111
clangd 实验模块参数。
112112
6. 必要时重启官方 clangd 扩展,并执行一次最长 60 秒的 `clangd --check`
@@ -381,8 +381,8 @@ API、状态栏、任务和 clangd 集成。
381381
版本完全一致的 tag:
382382

383383
```sh
384-
git tag -a v0.2.3 -m "mcpp-vscode 0.2.3"
385-
git push origin v0.2.3
384+
git tag -a v0.2.4 -m "mcpp-vscode 0.2.4"
385+
git push origin v0.2.4
386386
```
387387

388388
`.github/workflows/release.yml` 会校验 tag,执行测试和打包,生成 VSIX 与 SHA-256 文件,

docs/superpowers/plans/2026-08-05-clangd-hermetic-cdb-build-script.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,10 @@ Create annotated tag `v0.2.4` on the verified repair commit. Do not push it or c
138138
Run: `code --install-extension /Users/cltx/projects/mcpp/mcpp-vscode/mcpp-vscode-0.2.4.vsix --force`
139139

140140
Then run `code --list-extensions --show-versions` and verify `mcpp-community.mcpp-vscode@0.2.4`. A VS Code window reload is required before the new language registration and clangd arguments take effect.
141+
142+
### Review follow-up
143+
144+
- [x] Extend module grammar injection to `source.mcpp-build` and cover the import capture scope.
145+
- [x] Resolve relative compilation-database source paths from each command directory.
146+
- [x] Treat explicit libc++ and no-default-config commands as hermetic without requiring a sysroot.
147+
- [x] Keep the release instructions synchronized with the extension version.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@
178178
{
179179
"scopeName": "source.cpp.mcpp-modules",
180180
"injectTo": [
181-
"source.cpp"
181+
"source.cpp",
182+
"source.mcpp-build"
182183
],
183184
"path": "./syntaxes/mcpp-modules.tmLanguage.json"
184185
}

src/analysis.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,16 @@ export function analyzeCompilationDatabase(contents: string): CompilationDatabas
179179
argument.startsWith("-fmodule-file=")
180180
|| argument.startsWith("-fprebuilt-module-path=")
181181
));
182+
const directory = typeof command.directory === "string" ? command.directory : undefined;
183+
const sourceFile = typeof command.file === "string"
184+
? resolveCompilationSourceFile(directory, command.file)
185+
: undefined;
182186
candidates.push({
183187
kind,
184188
capability: kind === "llvm" ? "full" : "syntax-only",
185189
compilerPath: args[0],
186-
sourceFile: typeof command.file === "string" ? command.file : undefined,
187-
directory: typeof command.directory === "string" ? command.directory : undefined,
190+
sourceFile,
191+
directory,
188192
arguments: args,
189193
hasPrebuiltModules,
190194
reason: kind === "llvm"
@@ -219,6 +223,16 @@ function isWithinDirectory(directory: string, file: string): boolean {
219223
|| (relative !== ".." && !relative.startsWith(`..${pathApi.sep}`) && !pathApi.isAbsolute(relative));
220224
}
221225

226+
function resolveCompilationSourceFile(directory: string | undefined, file: string): string {
227+
if (directory === undefined) {
228+
return file;
229+
}
230+
const windows = /^[A-Za-z]:[\\/]/.test(directory) || directory.includes("\\")
231+
|| /^[A-Za-z]:[\\/]/.test(file) || file.includes("\\");
232+
const pathApi = windows ? path.win32 : path.posix;
233+
return pathApi.isAbsolute(file) ? file : pathApi.resolve(directory, file);
234+
}
235+
222236
function isProjectSource(directory: string, file: string): boolean {
223237
if (!isWithinDirectory(directory, file)) {
224238
return false;
@@ -252,13 +266,6 @@ function removeManagedArguments(arguments_: readonly string[]): string[] {
252266
return result;
253267
}
254268

255-
function hasValueFlag(arguments_: readonly string[], flag: string): boolean {
256-
return arguments_.some((argument, index) => (
257-
(argument === flag && index + 1 < arguments_.length)
258-
|| argument.startsWith(`${flag}=`)
259-
));
260-
}
261-
262269
function hasExplicitLibcxxPath(arguments_: readonly string[]): boolean {
263270
return arguments_.some((argument, index) => {
264271
const value = argument === "-isystem"
@@ -276,8 +283,7 @@ function isHermeticClangCommand(arguments_: readonly string[] | undefined): bool
276283
}
277284
return arguments_.includes("--no-default-config")
278285
&& arguments_.includes("-nostdinc++")
279-
&& hasExplicitLibcxxPath(arguments_)
280-
&& hasValueFlag(arguments_, "--sysroot");
286+
&& hasExplicitLibcxxPath(arguments_);
281287
}
282288

283289
function expandWorkspaceVariables(argument: string, workspaceFolder?: string): string {

syntaxes/mcpp-modules.tmLanguage.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
33
"name": "mcpp C++ Modules",
44
"scopeName": "source.cpp.mcpp-modules",
5-
"injectionSelector": "L:source.cpp",
5+
"injectionSelector": "L:source.cpp, L:source.mcpp-build",
66
"patterns": [
77
{
88
"include": "#module-declaration"

test/analysis.test.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,35 @@ test("prefers a project source over an mcpp dependency cache source", () => {
115115
assert.equal(result.sourceFile, "/work/app/src/main.cpp");
116116
});
117117

118+
test("resolves relative CDB source files from their command directory", () => {
119+
const result = analyzeCompilationDatabase(JSON.stringify([
120+
{
121+
directory: "/work/app",
122+
file: "/work/app/.mcpp/packages/redis/src/transaction.cpp",
123+
arguments: [
124+
"/tools/clang++",
125+
"-std=c++23",
126+
"-fprebuilt-module-path=/work/app/target/pcm.cache",
127+
"-c",
128+
"/work/app/.mcpp/packages/redis/src/transaction.cpp",
129+
],
130+
},
131+
{
132+
directory: "/work/app",
133+
file: "src/main.cpp",
134+
arguments: [
135+
"/tools/clang++",
136+
"-std=c++23",
137+
"-fprebuilt-module-path=/work/app/target/pcm.cache",
138+
"-c",
139+
"src/main.cpp",
140+
],
141+
},
142+
]));
143+
144+
assert.equal(result.sourceFile, "/work/app/src/main.cpp");
145+
});
146+
118147
test("keeps Windows separators when reading a command-form CDB entry", () => {
119148
const result = analyzeCompilationDatabase(JSON.stringify([
120149
{
@@ -203,8 +232,10 @@ test("hermetic mcpp commands do not use query-driver discovery", () => {
203232
"/tools/clang++",
204233
"--no-default-config",
205234
"-nostdinc++",
206-
"-isystem/tools/llvm/include/c++/v1",
207-
"--sysroot=/platform/sdk",
235+
"-isystem",
236+
"/tools/llvm/include/c++/v1",
237+
"-isystem/tools/llvm/include/x86_64-unknown-linux-gnu",
238+
"-isystem/tools/llvm/include",
208239
"-c",
209240
"/work/app/src/main.cpp",
210241
],

test/artifacts.test.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,16 @@ test("ships an injection grammar with module-specific scopes", () => {
137137
const grammar = manifest.contributes?.grammars?.find((item) => item.scopeName === "source.cpp.mcpp-modules");
138138
assert.deepEqual(grammar, {
139139
scopeName: "source.cpp.mcpp-modules",
140-
injectTo: ["source.cpp"],
140+
injectTo: ["source.cpp", "source.mcpp-build"],
141141
path: "./syntaxes/mcpp-modules.tmLanguage.json",
142142
});
143143

144144
const grammarText = readFileSync(path.join(root, "syntaxes/mcpp-modules.tmLanguage.json"), "utf8");
145-
const grammarDocument = JSON.parse(grammarText) as { repository?: Record<string, unknown> };
145+
const grammarDocument = JSON.parse(grammarText) as {
146+
injectionSelector?: string;
147+
repository?: Record<string, unknown>;
148+
};
149+
assert.match(grammarDocument.injectionSelector ?? "", /source\.mcpp-build/);
146150
for (const key of ["module-declaration", "import-declaration", "module-name"]) {
147151
assert.ok(grammarDocument.repository?.[key], `missing grammar rule: ${key}`);
148152
}
@@ -155,6 +159,12 @@ test("ships an injection grammar with module-specific scopes", () => {
155159
const importPattern = new RegExp(javascriptPattern);
156160
assert.match("import xxx", importPattern);
157161
assert.match("export import foo.bar;", importPattern);
162+
163+
const importRuleWithCaptures = importRule as {
164+
captures?: Record<string, { name?: string }>;
165+
};
166+
assert.equal(importRuleWithCaptures.captures?.["2"]?.name, "keyword.control.import.cpp");
167+
assert.match("import mcpp;", importPattern);
158168
});
159169

160170
test("设置全局默认后先释放工具链锁再提供立即构建", () => {

0 commit comments

Comments
 (0)