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