1
1
import * as crypto from 'crypto' ;
2
2
import * as path from 'path' ;
3
- import * as bqrs from 'semmle-bqrs' ;
4
- import { CustomResultSets , FivePartLocation , LocationStyle , LocationValue , PathProblemQueryResults , ProblemQueryResults , ResolvableLocationValue , tryGetResolvableLocation , WholeFileLocation } from 'semmle-bqrs' ;
5
- import { FileReader } from 'semmle-io-node' ;
3
+ import * as cli from './cli' ;
4
+ import * as Sarif from 'sarif' ;
5
+ import { parseSarifLocation , parseSarifPlainTextMessage } from './sarif-utils' ;
6
+ import { FivePartLocation , LocationValue , ResolvableLocationValue , WholeFileLocation , tryGetResolvableLocation , LocationStyle } from 'semmle-bqrs' ;
6
7
import { DisposableObject } from 'semmle-vscode-utils' ;
7
8
import * as vscode from 'vscode' ;
8
9
import { Diagnostic , DiagnosticRelatedInformation , DiagnosticSeverity , languages , Location , Position , Range , Uri , window as Window , workspace } from 'vscode' ;
@@ -11,7 +12,7 @@ import { DatabaseItem, DatabaseManager } from './databases';
11
12
import * as helpers from './helpers' ;
12
13
import { showAndLogErrorMessage } from './helpers' ;
13
14
import { assertNever } from './helpers-pure' ;
14
- import { FromResultsViewMsg , Interpretation , IntoResultsViewMsg , ResultsInfo , SortedResultSetInfo , SortedResultsMap , INTERPRETED_RESULTS_PER_RUN_LIMIT } from './interface-types' ;
15
+ import { FromResultsViewMsg , Interpretation , IntoResultsViewMsg , ResultsInfo , SortedResultSetInfo , SortedResultsMap , INTERPRETED_RESULTS_PER_RUN_LIMIT , QueryMetadata } from './interface-types' ;
15
16
import { Logger } from './logging' ;
16
17
import * as messages from './messages' ;
17
18
import { EvaluationInfo , interpretResults , QueryInfo , tmpDir } from './queries' ;
@@ -159,7 +160,7 @@ export class InterfaceManager extends DisposableObject {
159
160
if ( msg . visible ) {
160
161
const databaseItem = this . databaseManager . findDatabaseItem ( Uri . parse ( msg . databaseUri ) ) ;
161
162
if ( databaseItem !== undefined ) {
162
- await this . showResultsAsDiagnostics ( msg . resultsPath , msg . kind , databaseItem ) ;
163
+ await this . showResultsAsDiagnostics ( msg . resultsPath , msg . metadata , databaseItem ) ;
163
164
}
164
165
} else {
165
166
// TODO: Only clear diagnostics on the same database.
@@ -256,7 +257,7 @@ export class InterfaceManager extends DisposableObject {
256
257
sortedResultsMap,
257
258
database : info . database ,
258
259
shouldKeepOldResultsWhileRendering,
259
- kind : info . query . metadata ? info . query . metadata . kind : undefined
260
+ metadata : info . query . metadata
260
261
} ) ;
261
262
}
262
263
@@ -271,7 +272,7 @@ export class InterfaceManager extends DisposableObject {
271
272
const sourceInfo = sourceArchiveUri === undefined ?
272
273
undefined :
273
274
{ sourceArchive : sourceArchiveUri . fsPath , sourceLocationPrefix } ;
274
- const sarif = await interpretResults ( this . cliServer , query , resultsInfo , sourceInfo ) ;
275
+ const sarif = await interpretResults ( this . cliServer , query . metadata , query . resultsInfo . resultsPath , sourceInfo ) ;
275
276
// For performance reasons, limit the number of results we try
276
277
// to serialize and send to the webview. TODO: possibly also
277
278
// limit number of paths per result, number of steps per path,
@@ -295,90 +296,98 @@ export class InterfaceManager extends DisposableObject {
295
296
this . logger . log ( `Exception during results interpretation: ${ e . message } . Will show raw results instead.` ) ;
296
297
}
297
298
}
298
-
299
299
return interpretation ;
300
300
}
301
301
302
- private async showResultsAsDiagnostics ( resultsPath : string , kind : string | undefined ,
303
- database : DatabaseItem ) {
304
-
302
+ private async showResultsAsDiagnostics ( resultsPath : string , metadata : QueryMetadata | undefined , database : DatabaseItem ) {
305
303
// URIs from the webview have the vscode-resource scheme, so convert into a filesystem URI first.
306
304
const resultsPathOnDisk = webviewUriToFileUri ( resultsPath ) . fsPath ;
307
- const fileReader = await FileReader . open ( resultsPathOnDisk ) ;
308
- try {
309
- const resultSets = await bqrs . open ( fileReader ) ;
310
- try {
311
- switch ( kind || 'problem' ) {
312
- case 'problem' : {
313
- const customResults = bqrs . createCustomResultSets < ProblemQueryResults > ( resultSets , ProblemQueryResults ) ;
314
- await this . showProblemResultsAsDiagnostics ( customResults , database ) ;
315
- }
316
- break ;
305
+ const sourceLocationPrefix = await database . getSourceLocationPrefix ( this . cliServer ) ;
306
+ const sourceArchiveUri = database . sourceArchive ;
307
+ const sourceInfo = sourceArchiveUri === undefined ?
308
+ undefined :
309
+ { sourceArchive : sourceArchiveUri . fsPath , sourceLocationPrefix } ;
310
+ const sarif = await interpretResults ( this . cliServer , metadata , resultsPathOnDisk , sourceInfo ) ;
317
311
318
- case 'path-problem' : {
319
- const customResults = bqrs . createCustomResultSets < PathProblemQueryResults > ( resultSets , PathProblemQueryResults ) ;
320
- await this . showProblemResultsAsDiagnostics ( customResults , database ) ;
321
- }
322
- break ;
323
-
324
- default :
325
- throw new Error ( `Unrecognized query kind '${ kind } '.` ) ;
326
- }
327
- }
328
- catch ( e ) {
329
- const msg = e instanceof Error ? e . message : e . toString ( ) ;
330
- this . logger . log ( `Exception while computing problem results as diagnostics: ${ msg } ` ) ;
331
- this . _diagnosticCollection . clear ( ) ;
332
- }
312
+ try {
313
+ await this . showProblemResultsAsDiagnostics ( sarif , database ) ;
333
314
}
334
- finally {
335
- fileReader . dispose ( ) ;
315
+ catch ( e ) {
316
+ const msg = e instanceof Error ? e . message : e . toString ( ) ;
317
+ this . logger . log ( `Exception while computing problem results as diagnostics: ${ msg } ` ) ;
318
+ this . _diagnosticCollection . clear ( ) ;
336
319
}
320
+
337
321
}
338
322
339
- private async showProblemResultsAsDiagnostics ( results : CustomResultSets < ProblemQueryResults > ,
340
- databaseItem : DatabaseItem ) : Promise < void > {
323
+ private async showProblemResultsAsDiagnostics ( results : Sarif . Log , databaseItem : DatabaseItem ) : Promise < void > {
324
+ const sourceLocationPrefix = await databaseItem . getSourceLocationPrefix ( this . cliServer ) ;
325
+
326
+
327
+ if ( ! results . runs || ! results . runs [ 0 ] . results ) {
328
+ this . logger . log ( "Didn't find a run in the sarif results. Error processing sarif?" )
329
+ return ;
330
+ }
341
331
342
332
const diagnostics : [ Uri , ReadonlyArray < Diagnostic > ] [ ] = [ ] ;
343
- for await ( const problemRow of results . problems . readTuples ( ) ) {
344
- const codeLocation = resolveLocation ( problemRow . element . location , databaseItem ) ;
345
- let message : string ;
346
- const references = problemRow . references ;
347
- if ( references ) {
348
- let referenceIndex = 0 ;
349
- message = problemRow . message . replace ( / \$ \@ / g, sub => {
350
- if ( referenceIndex < references . length ) {
351
- const replacement = references [ referenceIndex ] . text ;
352
- referenceIndex ++ ;
353
- return replacement ;
354
- }
355
- else {
356
- return sub ;
357
- }
358
- } ) ;
333
+
334
+ for ( const result of results . runs [ 0 ] . results ) {
335
+ const message = result . message . text ;
336
+ if ( message === undefined ) {
337
+ this . logger . log ( "Sarif had result without plaintext message" )
338
+ continue ;
359
339
}
360
- else {
361
- message = problemRow . message ;
340
+ if ( ! result . locations ) {
341
+ this . logger . log ( "Sarif had result without location" )
342
+ continue ;
362
343
}
363
- const diagnostic = new Diagnostic ( codeLocation . range , message , DiagnosticSeverity . Warning ) ;
364
- if ( problemRow . references ) {
365
- const relatedInformation : DiagnosticRelatedInformation [ ] = [ ] ;
366
- for ( const reference of problemRow . references ) {
367
- const referenceLocation = tryResolveLocation ( reference . element . location , databaseItem ) ;
344
+
345
+ const sarifLoc = parseSarifLocation ( result . locations [ 0 ] , sourceLocationPrefix ) ;
346
+ if ( sarifLoc . t == "NoLocation" ) {
347
+ continue ;
348
+ }
349
+ const resultLocation = tryResolveLocation ( sarifLoc , databaseItem )
350
+ if ( ! resultLocation ) {
351
+ this . logger . log ( "Sarif location was not resolvable " + sarifLoc )
352
+ continue ;
353
+ }
354
+ const parsedMessage = parseSarifPlainTextMessage ( message ) ;
355
+ const relatedInformation : DiagnosticRelatedInformation [ ] = [ ] ;
356
+ const relatedLocationsById : { [ k : number ] : Sarif . Location } = { } ;
357
+
358
+
359
+ for ( let loc of result . relatedLocations || [ ] ) {
360
+ relatedLocationsById [ loc . id ! ] = loc ;
361
+ }
362
+ let resultMessageChunks : string [ ] = [ ] ;
363
+ for ( const section of parsedMessage ) {
364
+ if ( typeof section === "string" ) {
365
+ resultMessageChunks . push ( section ) ;
366
+ } else {
367
+ resultMessageChunks . push ( section . text ) ;
368
+ const sarifChunkLoc = parseSarifLocation ( relatedLocationsById [ section . dest ] , sourceLocationPrefix ) ;
369
+ if ( sarifChunkLoc . t == "NoLocation" ) {
370
+ continue ;
371
+ }
372
+ const referenceLocation = tryResolveLocation ( sarifChunkLoc , databaseItem ) ;
373
+
374
+
368
375
if ( referenceLocation ) {
369
376
const related = new DiagnosticRelatedInformation ( referenceLocation ,
370
- reference . text ) ;
377
+ section . text ) ;
371
378
relatedInformation . push ( related ) ;
372
379
}
373
380
}
374
- diagnostic . relatedInformation = relatedInformation ;
375
381
}
382
+ const diagnostic = new Diagnostic ( resultLocation . range , resultMessageChunks . join ( "" ) , DiagnosticSeverity . Warning ) ;
383
+ diagnostic . relatedInformation = relatedInformation ;
384
+
376
385
diagnostics . push ( [
377
- codeLocation . uri ,
386
+ resultLocation . uri ,
378
387
[ diagnostic ]
379
388
] ) ;
380
- }
381
389
390
+ }
382
391
this . _diagnosticCollection . set ( diagnostics ) ;
383
392
}
384
393
0 commit comments