Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/test/simulation/diagnosticProviders/cpp.ts
13395 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as cp from 'child_process';
7
import { TestingCacheSalts } from '../../base/salts';
8
import { CacheScope } from '../../base/simulationContext';
9
import { CLANG_DIAGNOSTICS_PROVIDER_CACHE_SALT } from '../../cacheSalt';
10
import { createTempDir } from '../stestUtil';
11
import { IFile, ITestDiagnostic } from './diagnosticsProvider';
12
import { CachingDiagnosticsProvider, findIfInstalled, setupTemporaryWorkspace } from './utils';
13
14
/**
15
* Class which finds clang diagnostics after compilation of C++ files
16
*/
17
export class CppDiagnosticsProvider extends CachingDiagnosticsProvider {
18
override readonly id = 'cpp';
19
override readonly cacheSalt = TestingCacheSalts.cppCacheSalt;
20
override readonly cacheScope = CacheScope.CPP;
21
private _isInstalled: 'local' | 'docker' | false | undefined;
22
23
protected override get cacheVersion(): number {
24
return CLANG_DIAGNOSTICS_PROVIDER_CACHE_SALT;
25
}
26
27
sources: {
28
filePath: string;
29
fileName: string;
30
fileContents: string;
31
}[] = [];
32
33
override isInstalled(): boolean {
34
if (this._isInstalled === undefined) {
35
if (findIfInstalled({ command: 'clang', arguments: ['-v'] }, /\d+\.\d+\.\d+/)) {
36
this._isInstalled = 'local';
37
} else if (findIfInstalled({ command: 'docker', arguments: ['--version'] }, /\d+\.\d+\.\d+/)) {
38
this._isInstalled = 'docker';
39
} else {
40
this._isInstalled = false;
41
}
42
}
43
return this._isInstalled !== false;
44
}
45
46
override async computeDiagnostics(_files: IFile[]): Promise<ITestDiagnostic[]> {
47
const temporaryDirectory = await this.setupWorkspace(_files);
48
const diagnostics = await this.processDiagnostics(temporaryDirectory, _files);
49
//await cleanTempDir(temporaryDirectory);
50
return diagnostics;
51
}
52
53
async setupWorkspace(_files: IFile[]): Promise<string> {
54
const temporaryDirectory = await createTempDir();
55
await setupTemporaryWorkspace(temporaryDirectory, _files);
56
return temporaryDirectory;
57
}
58
59
async processDiagnostics(temporaryDirectory: string, _files: IFile[]): Promise<ITestDiagnostic[]> {
60
// Validate that the diagnostics provider is installed
61
if (!this.isInstalled()) {
62
throw new Error('clang or docker must be available in this environment for c++ diagnostics.');
63
}
64
65
const diagnostics: ITestDiagnostic[] = [];
66
const basename = 'workspaceFolder_' + new Date().getTime();
67
for (const file of _files) {
68
69
let spawnResult;
70
if (this._isInstalled === 'docker') {
71
const args = ['run', '--rm', '-v', `${temporaryDirectory}:/${basename}`, 'mcr.microsoft.com/devcontainers/cpp:latest', 'clang++', `/${basename}/${file.fileName}`];
72
//console.log('docker ' + args.map(arg => `'${arg}'`).join(' '));
73
spawnResult = cp.spawnSync('docker', args, { shell: true, encoding: 'utf-8' });
74
} else {
75
spawnResult = cp.spawnSync('clang++', [`${temporaryDirectory}/${file.fileName}`]);
76
}
77
78
// If compilation was successful, no diagnostics are needed.
79
if (spawnResult.status === 0) {
80
return [];
81
}
82
83
// Need to capture the output of clang and convert it into diagnostics
84
// Grab the diagnostic info from the error and turn it into a diagnostic object.
85
// Example error:
86
// /workspaceFolder/main.cpp:5:10: error: expected ';' after return statement
87
// /workspaceFolder/LyraHealthComponent.cpp:3:10: fatal error: 'LyraHealthComponent.h' file not found
88
// Format:
89
// /${filePath}:${line}:${col}: ${code}: ${message}
90
const regexp = new RegExp(`^\/${basename}\/([A-Za-z_\\-\\s0-9\\.]+):(\\d+):(\\d+): ([^:]+): (.*)`);
91
let hasErrors = false;
92
const lines = spawnResult.stderr.toString().split('\n');
93
for (const line of lines) {
94
const m = line.match(regexp);
95
if (m) {
96
const [, filePath, line, col, code, message] = m;
97
// TODO: Add support for related information
98
diagnostics.push({
99
file: filePath,
100
startLine: +line - 1,
101
startCharacter: +col - 1,
102
endLine: +line - 1,
103
endCharacter: +col - 1,
104
message: message,
105
code: code,
106
relatedInformation: undefined,
107
source: this.id
108
});
109
hasErrors = true;
110
}
111
}
112
if (!hasErrors || spawnResult.error) {
113
throw new Error(`Error while running 'clang' \n\nstderr : ` + spawnResult.stderr + '\n\nerr : ' + spawnResult.error);
114
}
115
}
116
117
return diagnostics;
118
}
119
}
120
121