Merge pull request #407 from jcreedcmu/jcreed/view-sarif

Allow viewing SARIF from query history view
This commit is contained in:
jcreedcmu
2020-05-28 08:08:56 -04:00
committed by GitHub
6 changed files with 51 additions and 16 deletions

View File

@@ -4,6 +4,7 @@
- Better formatting and autoindentation when adding QLDoc comments to `.ql` and `.qll` files.
- Allow for more flexibility when opening a database in the workspace. A user can now choose the actual database folder, or the nested `db-*` folder.
- Add query history menu command for viewing corresponding SARIF file.
## 1.2.0 - 19 May 2020

View File

@@ -284,6 +284,10 @@
"command": "codeQLQueryHistory.showQueryText",
"title": "Show Query Text"
},
{
"command": "codeQLQueryHistory.viewSarif",
"title": "View SARIF"
},
{
"command": "codeQLQueryResults.nextPathStep",
"title": "CodeQL: Show Next Step on Path"
@@ -388,6 +392,11 @@
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory"
},
{
"command": "codeQLQueryHistory.viewSarif",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory && viewItem == interpretedResultsItem"
},
{
"command": "codeQLTests.showOutputDifferences",
"group": "qltest@1",
@@ -484,6 +493,10 @@
"command": "codeQLQueryHistory.showQueryText",
"when": "false"
},
{
"command": "codeQLQueryHistory.viewSarif",
"when": "false"
},
{
"command": "codeQLQueryHistory.setLabel",
"when": "false"

View File

@@ -402,7 +402,7 @@ export class InterfaceManager extends DisposableObject {
const sarif = await interpretResults(
this.cliServer,
metadata,
resultsPaths.resultsPath,
resultsPaths,
sourceInfo
);
// For performance reasons, limit the number of results we try
@@ -440,7 +440,7 @@ export class InterfaceManager extends DisposableObject {
): Promise<Interpretation | undefined> {
let interpretation: Interpretation | undefined = undefined;
if (
(await query.hasInterpretedResults()) &&
(await query.canHaveInterpretedResults()) &&
query.quickEvalPosition === undefined // never do results interpretation if quickEval
) {
try {

View File

@@ -74,7 +74,7 @@ class HistoryTreeDataProvider implements vscode.TreeDataProvider<CompletedQuery>
constructor(private ctx: ExtensionContext) {
}
getTreeItem(element: CompletedQuery): vscode.TreeItem {
async getTreeItem(element: CompletedQuery): Promise<vscode.TreeItem> {
const it = new vscode.TreeItem(element.toString());
it.command = {
@@ -83,6 +83,11 @@ class HistoryTreeDataProvider implements vscode.TreeDataProvider<CompletedQuery>
arguments: [element],
};
// Mark this query history item according to whether it has a
// SARIF file so that we can make context menu items conditionally
// available.
it.contextValue = await element.query.hasInterpretedResults() ? 'interpretedResultsItem' : 'rawResultsItem';
if (!element.didRunSuccessfully) {
it.iconPath = path.join(this.ctx.extensionPath, FAILED_QUERY_HISTORY_ITEM_ICON);
}
@@ -257,6 +262,22 @@ export class QueryHistoryManager {
}
}
async handleViewSarif(queryHistoryItem: CompletedQuery) {
try {
const hasInterpretedResults = await queryHistoryItem.query.canHaveInterpretedResults();
if (hasInterpretedResults) {
const textDocument = await vscode.workspace.openTextDocument(vscode.Uri.file(queryHistoryItem.query.resultsPaths.interpretedResultsPath));
await vscode.window.showTextDocument(textDocument, vscode.ViewColumn.One);
}
else {
const label = queryHistoryItem.getLabel();
helpers.showAndLogInformationMessage(`Query ${label} has no interpreted results.`);
}
} catch (e) {
helpers.showAndLogErrorMessage(e.message);
}
}
async getQueryText(queryHistoryItem: CompletedQuery): Promise<string> {
if (queryHistoryItem.options.queryText) {
return queryHistoryItem.options.queryText;
@@ -296,6 +317,7 @@ export class QueryHistoryManager {
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.setLabel', this.handleSetLabel.bind(this)));
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryLog', this.handleShowQueryLog.bind(this)));
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryText', this.handleShowQueryText.bind(this)));
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.viewSarif', this.handleViewSarif.bind(this)));
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.itemClicked', async (item) => {
return this.handleItemClicked(item);
}));

View File

@@ -5,7 +5,7 @@ import * as cli from './cli';
import * as sarif from 'sarif';
import * as fs from 'fs-extra';
import * as path from 'path';
import { RawResultsSortState, SortedResultSetInfo, DatabaseInfo, QueryMetadata, InterpretedResultsSortState } from "./interface-types";
import { RawResultsSortState, SortedResultSetInfo, DatabaseInfo, QueryMetadata, InterpretedResultsSortState, ResultsPaths } from "./interface-types";
import { QueryHistoryConfig } from "./config";
import { QueryHistoryItemOptions } from "./query-history";
@@ -54,13 +54,6 @@ export class CompletedQuery implements QueryWithResults {
return helpers.getQueryName(this.query);
}
/**
* Holds if this query should produce interpreted results.
*/
canInterpretedResults(): Promise<boolean> {
return this.query.dbItem.hasMetadataFile();
}
get statusString(): string {
switch (this.result.resultType) {
case messages.QueryResultType.CANCELLATION:
@@ -130,9 +123,8 @@ export class CompletedQuery implements QueryWithResults {
/**
* Call cli command to interpret results.
*/
export async function interpretResults(server: cli.CodeQLCliServer, metadata: QueryMetadata | undefined, resultsPath: string, sourceInfo?: cli.SourceInfo): Promise<sarif.Log> {
const interpretedResultsPath = resultsPath + ".interpreted.sarif";
export async function interpretResults(server: cli.CodeQLCliServer, metadata: QueryMetadata | undefined, resultsPaths: ResultsPaths, sourceInfo?: cli.SourceInfo): Promise<sarif.Log> {
const { resultsPath, interpretedResultsPath } = resultsPaths;
if (await fs.pathExists(interpretedResultsPath)) {
return JSON.parse(await fs.readFile(interpretedResultsPath, 'utf8'));
}

View File

@@ -157,15 +157,22 @@ export class QueryInfo {
}
/**
* Holds if this query should produce interpreted results.
* Holds if this query can in principle produce interpreted results.
*/
async hasInterpretedResults(): Promise<boolean> {
async canHaveInterpretedResults(): Promise<boolean> {
const hasMetadataFile = await this.dbItem.hasMetadataFile();
if (!hasMetadataFile) {
logger.log("Cannot produce interpreted results since the database does not have a .dbinfo or codeql-database.yml file.");
}
return hasMetadataFile;
}
/**
* Holds if this query actually has produced interpreted results.
*/
async hasInterpretedResults(): Promise<boolean> {
return fs.pathExists(this.resultsPaths.interpretedResultsPath);
}
}
export interface QueryWithResults {