Skip to content

Commit 6efb5ce

Browse files
Include "clang" diagnostics from SourceKit-LSP (#1062)
* Include "clang" diagnostics from SourceKit-LSP * Include "clang" in the list of diagnostics sources * Fix regex to not include "[-<code>]" that compiler outputs for C/C++ diagnostics * Clean up the "(fix available)" SourceKit includes in clang messages so that merging works proper * Add new tests to watch for clang diagnostics Issue: #1028 * Add some tests for SourceKit-LSP * Make sure we're getting diagnostics * Realized was an issue where "instanceof" does not work when the extension under test is using source under "dist/" but the tests use source under "out" so if test instantiated an object, instanceof may not work in the extension code * Fix pretest hook for clean repo * fixed missing diagnostics from clangd - fixed #1028 - diagnostic sources other than "swiftc" all regarded as SourceKit * Prevent new launch configurations when running integration tests --------- Co-authored-by: Kai Lau <[email protected]>
1 parent b6b258c commit 6efb5ce

File tree

15 files changed

+424
-177
lines changed

15 files changed

+424
-177
lines changed

assets/test/.vscode/settings.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"swift.disableAutoResolve": true,
3+
"swift.autoGenerateLaunchConfigurations": false,
34
"swift.additionalTestArguments": [
45
"-Xswiftc",
56
"-DTEST_ARGUMENT_SET_VIA_TEST_BUILD_ARGUMENTS_SETTING"
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// swift-tools-version:5.6
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "MyPoint",
6+
products: [
7+
.library(name: "MyPoint", targets: ["MyPoint"]),
8+
],
9+
targets: [
10+
.target(name: "MyPoint"),
11+
]
12+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include "MyPoint.h"
2+
3+
int func() {
4+
struct MyPoint p;
5+
p.x = 1;
6+
p.y = bar;
7+
p.z = 2
8+
return p.x;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
struct MyPoint {
2+
int x;
3+
int y;
4+
};
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// swift-tools-version:5.6
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "MyPoint",
6+
products: [
7+
.library(name: "MyPoint", targets: ["MyPoint"]),
8+
],
9+
targets: [
10+
.target(name: "MyPoint"),
11+
]
12+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include "MyPoint.h"
2+
3+
int func() {
4+
MyPoint* p = new MyPoint2();
5+
p->x = 1;
6+
p->y = bar;
7+
p.z = 2
8+
return p.x;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class MyPoint {
2+
public:
3+
int x;
4+
int y;
5+
};

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1275,7 +1275,7 @@
12751275
"watch": "tsc --watch",
12761276
"lint": "eslint ./ --ext ts && tsc --noEmit",
12771277
"format": "prettier --check *.json src test",
1278-
"pretest": "npm run compile && del-cli ./assets/test/**/.build && tsc -p ./",
1278+
"pretest": "npm run compile-tests && del-cli ./assets/test/**/.build && tsc -p ./",
12791279
"test": "vscode-test",
12801280
"integration-test": "npm test -- --label integrationTests",
12811281
"unit-test": "npm test -- --label unitTests",

src/DiagnosticsManager.ts

+46-47
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,22 @@ interface ParsedDiagnostic {
2626

2727
type DiagnosticsMap = Map<string, vscode.Diagnostic[]>;
2828

29+
type SourcePredicate = (source: string) => boolean;
30+
31+
type DiagnosticPredicate = (diagnostic: vscode.Diagnostic) => boolean;
32+
2933
const isEqual = (d1: vscode.Diagnostic, d2: vscode.Diagnostic) =>
3034
d1.range.start.isEqual(d2.range.start) && d1.message === d2.message;
3135

36+
const isSource = (diagnostic: vscode.Diagnostic, sourcesPredicate: SourcePredicate) =>
37+
sourcesPredicate(diagnostic.source ?? "");
38+
39+
const isSwiftc: DiagnosticPredicate = diagnostic =>
40+
isSource(diagnostic, DiagnosticsManager.isSwiftc);
41+
42+
const isSourceKit: DiagnosticPredicate = diagnostic =>
43+
isSource(diagnostic, DiagnosticsManager.isSourcekit);
44+
3245
/**
3346
* Handles the collection and deduplication of diagnostics from
3447
* various {@link vscode.Diagnostic.source | Diagnostic sources}.
@@ -38,9 +51,9 @@ const isEqual = (d1: vscode.Diagnostic, d2: vscode.Diagnostic) =>
3851
* thier own diagnostics.
3952
*/
4053
export class DiagnosticsManager implements vscode.Disposable {
41-
// Prior to Swift 6 "sourcekitd" was the source
42-
static sourcekit: string[] = ["SourceKit", "sourcekitd"];
43-
static swiftc: string[] = ["swiftc"];
54+
private static swiftc: string = "swiftc";
55+
static isSourcekit: SourcePredicate = source => this.swiftc !== source;
56+
static isSwiftc: SourcePredicate = source => this.swiftc === source;
4457

4558
private diagnosticCollection: vscode.DiagnosticCollection =
4659
vscode.languages.createDiagnosticCollection("swift");
@@ -57,15 +70,15 @@ export class DiagnosticsManager implements vscode.Disposable {
5770
});
5871
this.onDidStartTaskDisposible = vscode.tasks.onDidStartTask(event => {
5972
// Will only try to provide diagnostics for `swift` tasks
60-
const execution = event.execution.task.execution;
61-
if (!(execution && execution instanceof SwiftExecution)) {
73+
const task = event.execution.task;
74+
if (task.definition.type !== "swift") {
6275
return;
6376
}
6477
if (!this.includeSwiftcDiagnostics()) {
6578
return;
6679
}
6780
// Provide new list of diagnostics
68-
const swiftExecution = execution as SwiftExecution;
81+
const swiftExecution = task.execution as SwiftExecution;
6982
const provideDiagnostics: Promise<DiagnosticsMap> =
7083
this.parseDiagnostics(swiftExecution);
7184

@@ -76,7 +89,7 @@ export class DiagnosticsManager implements vscode.Disposable {
7689
map.forEach((diagnostics, uri) =>
7790
this.handleDiagnostics(
7891
vscode.Uri.file(uri),
79-
DiagnosticsManager.swiftc,
92+
DiagnosticsManager.isSwiftc,
8093
diagnostics
8194
)
8295
);
@@ -91,25 +104,25 @@ export class DiagnosticsManager implements vscode.Disposable {
91104
* Provide a new list of diagnostics for a given file
92105
*
93106
* @param uri {@link vscode.Uri Uri} of the file these diagonstics apply to
94-
* @param sources The source of the diagnostics which will apply for cleaning
95-
* up diagnostics that have been removed. See {@link swiftc} and {@link sourcekit}
96-
* @param newDiagnostics Array of {@link vscode.Diagnostic}. This can be empty to remove old diagnostics for the specified `sources`.
107+
* @param sourcePredicate Diagnostics of a source that satisfies the predicate will apply for cleaning
108+
* up diagnostics that have been removed. See {@link isSwiftc} and {@link isSourceKit}
109+
* @param newDiagnostics Array of {@link vscode.Diagnostic}. This can be empty to remove old diagnostics satisfying `sourcePredicate`.
97110
*/
98111
handleDiagnostics(
99112
uri: vscode.Uri,
100-
sources: string[],
113+
sourcePredicate: SourcePredicate,
101114
newDiagnostics: vscode.Diagnostic[]
102115
): void {
103-
const isFromSourceKit = !!DiagnosticsManager.sourcekit.find(s => sources.includes(s));
116+
const isFromSourceKit = !sourcePredicate(DiagnosticsManager.swiftc);
104117
// Is a descrepency between SourceKit-LSP and older versions
105118
// of Swift as to whether the first letter is capitalized or not,
106119
// so we'll always display messages capitalized to user and this
107120
// also will allow comparing messages when merging
108-
newDiagnostics = newDiagnostics.map(this.capitalizeMessage);
121+
newDiagnostics = newDiagnostics.map(this.capitalizeMessage).map(this.cleanMessage);
109122
const allDiagnostics = this.allDiagnostics.get(uri.fsPath)?.slice() || [];
110123
// Remove the old set of diagnostics from this source
111124
const removedDiagnostics = this.removeDiagnostics(allDiagnostics, d =>
112-
this.isSource(d, sources)
125+
isSource(d, sourcePredicate)
113126
);
114127
// Clean up any "fixed" swiftc diagnostics
115128
if (isFromSourceKit) {
@@ -119,7 +132,7 @@ export class DiagnosticsManager implements vscode.Disposable {
119132
);
120133
this.removeDiagnostics(
121134
allDiagnostics,
122-
d1 => this.isSwiftc(d1) && !!removedDiagnostics.find(d2 => isEqual(d1, d2))
135+
d1 => isSwiftc(d1) && !!removedDiagnostics.find(d2 => isEqual(d1, d2))
123136
);
124137
}
125138
// Append the new diagnostics we just received
@@ -131,25 +144,17 @@ export class DiagnosticsManager implements vscode.Disposable {
131144

132145
private updateDiagnosticsCollection(uri: vscode.Uri): void {
133146
const diagnostics = this.allDiagnostics.get(uri.fsPath) ?? [];
134-
const swiftcDiagnostics = diagnostics.filter(d => this.isSwiftc(d));
135-
const sourceKitDiagnostics = diagnostics.filter(d => this.isSourceKit(d));
147+
const swiftcDiagnostics = diagnostics.filter(isSwiftc);
148+
const sourceKitDiagnostics = diagnostics.filter(isSourceKit);
136149
const mergedDiagnostics: vscode.Diagnostic[] = [];
137150
switch (configuration.diagnosticsCollection) {
138151
case "keepSourceKit":
139152
mergedDiagnostics.push(...swiftcDiagnostics);
140-
this.mergeDiagnostics(
141-
mergedDiagnostics,
142-
sourceKitDiagnostics,
143-
DiagnosticsManager.sourcekit
144-
);
153+
this.mergeDiagnostics(mergedDiagnostics, sourceKitDiagnostics, isSourceKit);
145154
break;
146155
case "keepSwiftc":
147156
mergedDiagnostics.push(...sourceKitDiagnostics);
148-
this.mergeDiagnostics(
149-
mergedDiagnostics,
150-
swiftcDiagnostics,
151-
DiagnosticsManager.swiftc
152-
);
157+
this.mergeDiagnostics(mergedDiagnostics, swiftcDiagnostics, isSwiftc);
153158
break;
154159
case "onlySourceKit":
155160
mergedDiagnostics.push(...sourceKitDiagnostics);
@@ -168,7 +173,7 @@ export class DiagnosticsManager implements vscode.Disposable {
168173
private mergeDiagnostics(
169174
mergedDiagnostics: vscode.Diagnostic[],
170175
newDiagnostics: vscode.Diagnostic[],
171-
precedence: string[]
176+
precedencePredicate: DiagnosticPredicate
172177
): void {
173178
for (const diagnostic of newDiagnostics) {
174179
// See if a duplicate diagnostic exists
@@ -178,11 +183,11 @@ export class DiagnosticsManager implements vscode.Disposable {
178183
}
179184

180185
// Perform de-duplication
181-
if (precedence.includes(diagnostic.source || "")) {
186+
if (precedencePredicate(diagnostic)) {
182187
mergedDiagnostics.push(diagnostic);
183188
continue;
184189
}
185-
if (!currentDiagnostic || !precedence.includes(currentDiagnostic.source || "")) {
190+
if (!currentDiagnostic || !precedencePredicate(currentDiagnostic)) {
186191
mergedDiagnostics.push(diagnostic);
187192
continue;
188193
}
@@ -193,29 +198,17 @@ export class DiagnosticsManager implements vscode.Disposable {
193198
private removeSwiftcDiagnostics() {
194199
this.allDiagnostics.forEach((diagnostics, path) => {
195200
const newDiagnostics = diagnostics.slice();
196-
this.removeDiagnostics(newDiagnostics, d => this.isSwiftc(d));
201+
this.removeDiagnostics(newDiagnostics, isSwiftc);
197202
if (diagnostics.length !== newDiagnostics.length) {
198203
this.allDiagnostics.set(path, newDiagnostics);
199204
}
200205
this.updateDiagnosticsCollection(vscode.Uri.file(path));
201206
});
202207
}
203208

204-
private isSource(diagnostic: vscode.Diagnostic, sources: string[]): boolean {
205-
return sources.includes(diagnostic.source || "");
206-
}
207-
208-
private isSwiftc(diagnostic: vscode.Diagnostic): boolean {
209-
return this.isSource(diagnostic, DiagnosticsManager.swiftc);
210-
}
211-
212-
private isSourceKit(diagnostic: vscode.Diagnostic): boolean {
213-
return this.isSource(diagnostic, DiagnosticsManager.sourcekit);
214-
}
215-
216209
private removeDiagnostics(
217210
diagnostics: vscode.Diagnostic[],
218-
matches: (d: vscode.Diagnostic) => boolean
211+
matches: DiagnosticPredicate
219212
): vscode.Diagnostic[] {
220213
const removed: vscode.Diagnostic[] = [];
221214
let i = diagnostics.length;
@@ -323,13 +316,13 @@ export class DiagnosticsManager implements vscode.Disposable {
323316
private parseDiagnostic(
324317
line: string
325318
): ParsedDiagnostic | vscode.DiagnosticRelatedInformation | undefined {
326-
const diagnosticRegex = /^(.*?):(\d+)(?::(\d+))?:\s+(warning|error|note):\s+(.*)$/g;
319+
const diagnosticRegex = /^(.*?):(\d+)(?::(\d+))?:\s+(warning|error|note):\s+([^\\[]*)/g;
327320
const match = diagnosticRegex.exec(line);
328321
if (!match) {
329322
return;
330323
}
331324
const uri = match[1];
332-
const message = this.capitalize(match[5]);
325+
const message = this.capitalize(match[5]).trim();
333326
const range = this.range(match[2], match[3]);
334327
const severity = this.severity(match[4]);
335328
if (severity === vscode.DiagnosticSeverity.Information) {
@@ -339,7 +332,7 @@ export class DiagnosticsManager implements vscode.Disposable {
339332
);
340333
}
341334
const diagnostic = new vscode.Diagnostic(range, message, severity);
342-
diagnostic.source = DiagnosticsManager.swiftc[0];
335+
diagnostic.source = DiagnosticsManager.swiftc;
343336
return { uri, diagnostic };
344337
}
345338

@@ -377,6 +370,12 @@ export class DiagnosticsManager implements vscode.Disposable {
377370
return diagnostic;
378371
};
379372

373+
private cleanMessage = (diagnostic: vscode.Diagnostic) => {
374+
diagnostic = { ...diagnostic };
375+
diagnostic.message = diagnostic.message.replace("(fix available)", "").trim();
376+
return diagnostic;
377+
};
378+
380379
private onDidStartTaskDisposible: vscode.Disposable;
381380
private onDidChangeConfigurationDisposible: vscode.Disposable;
382381
}

src/sourcekit-lsp/LanguageClientManager.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -545,15 +545,15 @@ export class LanguageClientManager {
545545
const document = uri as vscode.TextDocument;
546546
this.workspaceContext.diagnostics.handleDiagnostics(
547547
document.uri ?? uri,
548-
DiagnosticsManager.sourcekit,
548+
DiagnosticsManager.isSourcekit,
549549
result?.items ?? []
550550
);
551551
return undefined;
552552
},
553553
handleDiagnostics: (uri, diagnostics) => {
554554
this.workspaceContext.diagnostics.handleDiagnostics(
555555
uri,
556-
DiagnosticsManager.sourcekit,
556+
DiagnosticsManager.isSourcekit,
557557
diagnostics
558558
);
559559
},

src/ui/SwiftBuildStatus.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class SwiftBuildStatus implements vscode.Disposable {
5252

5353
private handleTaskStatus(task: vscode.Task): void {
5454
// Only care about swift tasks
55-
if (!(task.execution && task.execution instanceof SwiftExecution)) {
55+
if (task.definition.type !== "swift") {
5656
return;
5757
}
5858
// Default to setting if task doesn't overwrite

0 commit comments

Comments
 (0)