Add the AST Viewer

This commit adds the AST Viewer for viewing the QL AST of a file in a
database.

The different components are as follows:

1. There is a new view `codeQLAstViewer`, which displays the AST
2. This view is backed by the `AstViewerDataProvider` and `AstViewer` classes in astView.ts
3. To generate an AST, we use contextual queries, similar to how Find references/declarations are implemented. In particular, in `definitions.ts` there is `TemplatePrintAstProvider` which provides an AST for a given source buffer.
  - Similar to the other queries, we first determine which database the buffer belongs to.
  - Based on that, we generate a synthetic qlpack and run the templatized `printAst.ql` query
  - We plug in the archive-relative path name of the source file.
  - After the query is run, we wrap the results in an `AstBuilder` instance.
  - When requested, the `AstBuilder` will generate the full AST of the file from the BQRS results.
  - The AST roots (all top-level elements, functions, variable declarations, etc, are roots) are passed to the `AstViewer` instance, which handles the display lifecycle and other VS Code-specific functions.

There are a few unrelated pieces here, which can be pulled out to another PR if required:

- The `codeQLQueryHistory` view now has a _welcome_ message to make it more obvious to users how to start.
- `definitions.ts` is moved to the `contextual` subfolder.
- `fileRangeFromURI` is extracted from `definitions.ts` to its own file so it can be reused.

Also, note that this relies on https://github.com/github/codeql/pull/3931 for the C/C++ query to be available in the QL sources. Other languages will need similar queries.
This commit is contained in:
Andrew Eisenberg
2020-07-01 07:31:26 -07:00
parent 546ec2eb1c
commit eaa26e5ef7
16 changed files with 1678 additions and 68 deletions

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12.6L10.7 13.3L12.3 11.7L13.9 13.3L14.7 12.6L13 11L14.7 9.40005L13.9 8.60005L12.3 10.3L10.7 8.60005L10 9.40005L11.6 11L10 12.6Z" fill="#C5C5C5"/>
<path d="M1 4L15 4L15 3L1 3L1 4Z" fill="#C5C5C5"/>
<path d="M1 7L15 7L15 6L1 6L1 7Z" fill="#C5C5C5"/>
<path d="M9 9.5L9 9L1 9L1 10L9 10L9 9.5Z" fill="#C5C5C5"/>
<path d="M9 13L9 12.5L9 12L1 12L1 13L9 13Z" fill="#C5C5C5"/>
</svg>

After

Width:  |  Height:  |  Size: 483 B

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.0001 12.6L10.7001 13.3L12.3001 11.7L13.9001 13.3L14.7001 12.6L13.0001 11L14.7001 9.40005L13.9001 8.60005L12.3001 10.3L10.7001 8.60005L10.0001 9.40005L11.6001 11L10.0001 12.6Z" fill="#424242"/>
<path d="M1.00006 4L15.0001 4L15.0001 3L1.00006 3L1.00006 4Z" fill="#424242"/>
<path d="M1.00006 7L15.0001 7L15.0001 6L1.00006 6L1.00006 7Z" fill="#424242"/>
<path d="M9.00006 9.5L9.00006 9L1.00006 9L1.00006 10L9.00006 10L9.00006 9.5Z" fill="#424242"/>
<path d="M9.00006 13L9.00006 12.5L9.00006 12L1.00006 12L1.00006 13L9.00006 13Z" fill="#424242"/>
</svg>

After

Width:  |  Height:  |  Size: 658 B

View File

@@ -25,6 +25,7 @@
"onLanguage:ql",
"onView:codeQLDatabases",
"onView:codeQLQueryHistory",
"onView:codeQLAstViewer",
"onView:test-explorer",
"onCommand:codeQL.checkForUpdatesToCLI",
"onCommand:codeQLDatabases.chooseDatabaseFolder",
@@ -32,6 +33,7 @@
"onCommand:codeQLDatabases.chooseDatabaseInternet",
"onCommand:codeQLDatabases.chooseDatabaseLgtm",
"onCommand:codeQL.setCurrentDatabase",
"onCommand:codeQL.viewAst",
"onCommand:codeQL.chooseDatabaseFolder",
"onCommand:codeQL.chooseDatabaseArchive",
"onCommand:codeQL.chooseDatabaseInternet",
@@ -218,6 +220,10 @@
"command": "codeQL.setCurrentDatabase",
"title": "CodeQL: Set Current Database"
},
{
"command": "codeQL.viewAst",
"title": "CodeQL: View AST"
},
{
"command": "codeQL.upgradeCurrentDatabase",
"title": "CodeQL: Upgrade Current Database"
@@ -333,6 +339,18 @@
{
"command": "codeQLTests.acceptOutput",
"title": "CodeQL: Accept Test Output"
},
{
"command": "codeQLAstViewer.gotoCode",
"title": "Go To Code"
},
{
"command": "codeQLAstViewer.clear",
"title": "Clear AST",
"icon": {
"light": "media/light/clear-all.svg",
"dark": "media/dark/clear-all.svg"
}
}
],
"menus": {
@@ -366,6 +384,11 @@
"command": "codeQLDatabases.chooseDatabaseLgtm",
"when": "view == codeQLDatabases",
"group": "navigation"
},
{
"command": "codeQLAstViewer.clear",
"when": "view == codeQLAstViewer",
"group": "navigation"
}
],
"view/item/context": [
@@ -446,6 +469,11 @@
"group": "9_qlCommands",
"when": "resourceScheme == codeql-zip-archive || explorerResourceIsFolder || resourceExtname == .zip"
},
{
"command": "codeQL.viewAst",
"group": "9_qlCommands",
"when": "resourceScheme == codeql-zip-archive"
},
{
"command": "codeQL.runQueries",
"group": "9_qlCommands"
@@ -468,6 +496,10 @@
"command": "codeQL.setCurrentDatabase",
"when": "false"
},
{
"command": "codeQL.viewAst",
"when": "resourceScheme == codeql-zip-archive"
},
{
"command": "codeQLDatabases.setCurrentDatabase",
"when": "false"
@@ -543,6 +575,14 @@
{
"command": "codeQLQueryHistory.compareWith",
"when": "false"
},
{
"command": "codeQLAstViewer.gotoCode",
"when": "false"
},
{
"command": "codeQLAstViewer.clear",
"when": "false"
}
],
"editor/context": [
@@ -574,9 +614,23 @@
{
"id": "codeQLQueryHistory",
"name": "Query History"
},
{
"id": "codeQLAstViewer",
"name": "AST Viewer"
}
]
}
},
"viewsWelcome": [
{
"view": "codeQLAstViewer",
"contents": "Run the 'CodeQL: View AST' command on an open source file from a Code QL database.\n[View AST](command:codeQL.viewAst)"
},
{
"view": "codeQLQueryHistory",
"contents": "Run the 'CodeQL: Run Query' command on QL query.\n[Run Query](command:codeQL.runQuery)"
}
]
},
"scripts": {
"build": "gulp",

View File

@@ -0,0 +1,102 @@
import * as vscode from 'vscode';
import { DatabaseItem } from './databases';
import { UrlValue, BqrsId } from './bqrs-cli-types';
import fileRangeFromURI from './contextual/fileRangeFromURI';
import { showLocation } from './interface-utils';
export interface AstItem {
id: BqrsId;
label?: string;
location?: UrlValue;
parent: AstItem | RootAstItem;
children: AstItem[];
order: number;
}
export type RootAstItem = Omit<AstItem, 'parent'>;
class AstViewerDataProvider implements vscode.TreeDataProvider<AstItem | RootAstItem> {
public roots: RootAstItem[] = [];
public db: DatabaseItem | undefined;
private _onDidChangeTreeData =
new vscode.EventEmitter<AstItem | undefined>();
readonly onDidChangeTreeData: vscode.Event<AstItem | undefined> =
this._onDidChangeTreeData.event;
constructor() {
vscode.commands.registerCommand('codeQLAstViewer.gotoCode',
async (location: UrlValue, db: DatabaseItem) => {
if (location) {
await showLocation(fileRangeFromURI(location, db));
}
});
}
refresh(): void {
this._onDidChangeTreeData.fire();
}
getChildren(item?: AstItem): vscode.ProviderResult<(AstItem | RootAstItem)[]> {
const children = item ? item.children : this.roots;
return children.sort((c1, c2) => (c1.order - c2.order));
}
getParent(item: AstItem): vscode.ProviderResult<AstItem> {
return item.parent as AstItem;
}
getTreeItem(item: AstItem): vscode.TreeItem {
const line = typeof item.location === 'string'
? item.location
: item.location?.startLine;
const state = item.children.length
? vscode.TreeItemCollapsibleState.Collapsed
: vscode.TreeItemCollapsibleState.None;
const treeItem = new vscode.TreeItem(item.label || '', state);
treeItem.description = line ? `Line ${line}` : '';
treeItem.id = String(item.id);
treeItem.tooltip = `${treeItem.description} ${treeItem.label}`;
treeItem.command = {
command: 'codeQLAstViewer.gotoCode',
title: 'Go To Code',
tooltip: `Go To ${item.location}`,
arguments: [item.location, this.db]
};
return treeItem;
}
}
export class AstViewer {
private treeView: vscode.TreeView<AstItem | RootAstItem>;
private treeDataProvider: AstViewerDataProvider;
constructor() {
this.treeDataProvider = new AstViewerDataProvider();
this.treeView = vscode.window.createTreeView('codeQLAstViewer', {
treeDataProvider: this.treeDataProvider,
showCollapseAll: true
});
vscode.commands.registerCommand('codeQLAstViewer.clear', () => {
this.clear();
});
}
updateRoots(roots: RootAstItem[], db: DatabaseItem, fileName: string) {
this.treeDataProvider.roots = roots;
this.treeDataProvider.db = db;
this.treeDataProvider.refresh();
this.treeView.message = `AST for ${fileName}`;
this.treeView.reveal(roots[0], { focus: true });
}
private clear() {
this.treeDataProvider.roots = [];
this.treeDataProvider.db = undefined;
this.treeDataProvider.refresh();
this.treeView.message = undefined;
}
}

View File

@@ -53,9 +53,12 @@ export interface BQRSInfo {
'result-sets': ResultSetSchema[];
}
export type BqrsId = number;
export interface EntityValue {
url?: UrlValue;
label?: string;
id?: BqrsId;
}
export interface LineColumnLocation {

View File

@@ -499,7 +499,7 @@ export class CodeQLCliServer implements Disposable {
*/
async bqrsDecode(bqrsPath: string, resultSet: string, pageSize?: number, offset?: number): Promise<DecodedBqrsChunk> {
const subcommandArgs = [
'--entities=url,string',
'--entities=id,url,string',
'--result-set', resultSet,
].concat(
pageSize ? ['--rows', pageSize.toString()] : []

View File

@@ -0,0 +1,121 @@
import { QueryWithResults } from '../run-queries';
import { CodeQLCliServer } from '../cli';
import { DecodedBqrsChunk, BqrsId, EntityValue } from '../bqrs-cli-types';
import { DatabaseItem } from '../databases';
import { AstItem, RootAstItem } from '../astViewer';
/**
* A class that wraps a tree of QL results from a query that
* has an @kind of graph
*/
// RENAME to ASTParser / ASTCreator
export default class AstBuilder {
private roots: RootAstItem[] | undefined;
private bqrsPath: string;
constructor(
queryResults: QueryWithResults,
private cli: CodeQLCliServer,
public db: DatabaseItem,
public fileName: string
) {
this.bqrsPath = queryResults.query.resultsPaths.resultsPath;
}
async getRoots(): Promise<RootAstItem[]> {
if (!this.roots) {
this.roots = await this.parseRoots();
}
return this.roots;
}
private async parseRoots(): Promise<RootAstItem[]> {
const [nodeTuples, edgeTuples, graphProperties] = await Promise.all([
await this.cli.bqrsDecode(this.bqrsPath, 'nodes'),
await this.cli.bqrsDecode(this.bqrsPath, 'edges'),
await this.cli.bqrsDecode(this.bqrsPath, 'graphProperties'),
]);
if (!this.isValidGraph(graphProperties)) {
throw new Error('AST is invalid');
}
const idToItem = new Map<BqrsId, AstItem | RootAstItem>();
const parentToChildren = new Map<BqrsId, BqrsId[]>();
const childToParent = new Map<BqrsId, BqrsId>();
const astOrder = new Map<BqrsId, number>();
const roots = [];
// Build up the parent-child relationships
edgeTuples.tuples.forEach(tuple => {
const from = tuple[0] as EntityValue;
const to = tuple[1] as EntityValue;
const toId = to.id!;
const fromId = from.id!;
if (tuple[2] === 'semmle.order') {
astOrder.set(toId, Number(tuple[3]));
} else if (tuple[2] === 'semmle.label') {
childToParent.set(toId, fromId);
let children = parentToChildren.get(fromId);
if (!children) {
parentToChildren.set(fromId, children = []);
}
children.push(toId);
}
});
// populate parents and children
nodeTuples.tuples.forEach(tuple => {
const entity = tuple[0] as EntityValue;
const id = entity.id!;
if (tuple[1] === 'semmle.order') {
astOrder.set(id, Number(tuple[2]));
} else if (tuple[1] === 'semmle.label') {
const item = {
id,
label: entity.label,
location: entity.url,
children: [] as AstItem[],
order: Number.MAX_SAFE_INTEGER
};
idToItem.set(id, item as RootAstItem);
const parent = idToItem.get(childToParent.get(id) || -1);
if (parent) {
const astItem = item as AstItem;
astItem.parent = parent;
parent.children.push(astItem);
}
const children = parentToChildren.get(id) || [];
children.forEach(childId => {
const child = idToItem.get(childId) as AstItem | undefined;
if (child) {
child.parent = item;
item.children.push(child);
}
});
}
});
// find the roots and add the order
for(const [, item] of idToItem) {
item.order = astOrder.has(item.id)
? astOrder.get(item.id)!
: Number.MAX_SAFE_INTEGER;
if (!('parent' in item)) {
roots.push(item);
}
}
return roots;
}
private isValidGraph(graphProperties: DecodedBqrsChunk) {
const tuple = graphProperties?.tuples?.find(t => t[0] === 'semmle.graphKind');
return tuple?.[1] === 'tree';
}
}

View File

@@ -2,15 +2,19 @@ import * as fs from 'fs-extra';
import * as yaml from 'js-yaml';
import * as tmp from 'tmp-promise';
import * as vscode from 'vscode';
import { decodeSourceArchiveUri, zipArchiveScheme } from './archive-filesystem-provider';
import { ColumnKindCode, EntityValue, getResultSetSchema, LineColumnLocation, UrlValue } from './bqrs-cli-types';
import { CodeQLCliServer } from './cli';
import { DatabaseItem, DatabaseManager } from './databases';
import * as helpers from './helpers';
import { CachedOperation } from './helpers';
import * as messages from './messages';
import { QueryServerClient } from './queryserver-client';
import { compileAndRunQueryAgainstDatabase, QueryWithResults } from './run-queries';
import * as path from 'path';
import { decodeSourceArchiveUri, zipArchiveScheme } from '../archive-filesystem-provider';
import { ColumnKindCode, EntityValue, getResultSetSchema } from '../bqrs-cli-types';
import { CodeQLCliServer } from '../cli';
import { DatabaseItem, DatabaseManager } from '../databases';
import * as helpers from '../helpers';
import { CachedOperation } from '../helpers';
import * as messages from '../messages';
import { QueryServerClient } from '../queryserver-client';
import { compileAndRunQueryAgainstDatabase, QueryWithResults } from '../run-queries';
import AstBuilder from './astBuilder';
import fileRangeFromURI from './fileRangeFromURI';
/**
* Run templated CodeQL queries to find definitions and references in
@@ -25,19 +29,38 @@ const SELECT_QUERY_NAME = '#select';
enum KeyType {
DefinitionQuery = 'DefinitionQuery',
ReferenceQuery = 'ReferenceQuery',
PrintAstQuery = 'PrintAstQuery',
}
function tagOfKeyType(keyType: KeyType): string {
switch (keyType) {
case KeyType.DefinitionQuery: return 'ide-contextual-queries/local-definitions';
case KeyType.ReferenceQuery: return 'ide-contextual-queries/local-references';
case KeyType.DefinitionQuery:
return 'ide-contextual-queries/local-definitions';
case KeyType.ReferenceQuery:
return 'ide-contextual-queries/local-references';
case KeyType.PrintAstQuery:
return 'ide-contextual-queries/print-ast';
}
}
function nameOfKeyType(keyType: KeyType): string {
switch (keyType) {
case KeyType.DefinitionQuery: return 'definitions';
case KeyType.ReferenceQuery: return 'references';
case KeyType.DefinitionQuery:
return 'definitions';
case KeyType.ReferenceQuery:
return 'references';
case KeyType.PrintAstQuery:
return 'print AST';
}
}
function kindOfKeyType(keyType: KeyType): string {
switch (keyType) {
case KeyType.DefinitionQuery:
case KeyType.ReferenceQuery:
return 'definitions';
case KeyType.PrintAstQuery:
return 'graph';
}
}
@@ -45,7 +68,7 @@ async function resolveQueries(cli: CodeQLCliServer, qlpack: string, keyType: Key
const suiteFile = (await tmp.file({
postfix: '.qls'
})).path;
const suiteYaml = { qlpack, include: { kind: 'definitions', 'tags contain': tagOfKeyType(keyType) } };
const suiteYaml = { qlpack, include: { kind: kindOfKeyType(keyType), 'tags contain': tagOfKeyType(keyType) } };
await fs.writeFile(suiteFile, yaml.safeDump(suiteYaml), 'utf8');
const queries = await cli.resolveQueriesInSuite(suiteFile, helpers.getOnDiskWorkspaceFolders());
@@ -81,10 +104,6 @@ export class TemplateQueryDefinitionProvider implements vscode.DefinitionProvide
this.cache = new CachedOperation<vscode.LocationLink[]>(this.getDefinitions.bind(this));
}
async getDefinitions(uriString: string): Promise<vscode.LocationLink[]> {
return getLinksForUriString(this.cli, this.qs, this.dbm, uriString, KeyType.DefinitionQuery, (src, _dest) => src === uriString);
}
async provideDefinition(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise<vscode.LocationLink[]> {
const fileLinks = await this.cache.get(document.uri.toString());
const locLinks: vscode.LocationLink[] = [];
@@ -95,6 +114,17 @@ export class TemplateQueryDefinitionProvider implements vscode.DefinitionProvide
}
return locLinks;
}
private async getDefinitions(uriString: string): Promise<vscode.LocationLink[]> {
return getLinksForUriString(
this.cli,
this.qs,
this.dbm,
uriString,
KeyType.DefinitionQuery,
(src, _dest) => src === uriString
);
}
}
export class TemplateQueryReferenceProvider implements vscode.ReferenceProvider {
@@ -108,11 +138,12 @@ export class TemplateQueryReferenceProvider implements vscode.ReferenceProvider
this.cache = new CachedOperation<FullLocationLink[]>(this.getReferences.bind(this));
}
async getReferences(uriString: string): Promise<FullLocationLink[]> {
return getLinksForUriString(this.cli, this.qs, this.dbm, uriString, KeyType.ReferenceQuery, (_src, dest) => dest === uriString);
}
async provideReferences(document: vscode.TextDocument, position: vscode.Position, _context: vscode.ReferenceContext, _token: vscode.CancellationToken): Promise<vscode.Location[]> {
async provideReferences(
document: vscode.TextDocument,
position: vscode.Position,
_context: vscode.ReferenceContext,
_token: vscode.CancellationToken
): Promise<vscode.Location[]> {
const fileLinks = await this.cache.get(document.uri.toString());
const locLinks: vscode.Location[] = [];
for (const link of fileLinks) {
@@ -122,14 +153,99 @@ export class TemplateQueryReferenceProvider implements vscode.ReferenceProvider
}
return locLinks;
}
private async getReferences(uriString: string): Promise<FullLocationLink[]> {
return getLinksForUriString(
this.cli,
this.qs,
this.dbm,
uriString,
KeyType.ReferenceQuery,
(_src, dest) => dest === uriString
);
}
}
interface FileRange {
file: vscode.Uri;
range: vscode.Range;
export class TemplatePrintAstProvider {
private cache: CachedOperation<QueryWithResults | undefined>;
constructor(
private cli: CodeQLCliServer,
private qs: QueryServerClient,
private dbm: DatabaseManager,
) {
this.cache = new CachedOperation<QueryWithResults | undefined>(this.getAst.bind(this));
}
async provideAst(document?: vscode.TextDocument): Promise<AstBuilder | undefined> {
if (!document) {
return;
}
const queryResults = await this.cache.get(document.uri.toString());
if (!queryResults) {
return;
}
return new AstBuilder(
queryResults, this.cli,
this.dbm.findDatabaseItem(vscode.Uri.parse(queryResults.database.databaseUri!))!,
path.basename(document.fileName)
);
}
private async getAst(uriString: string): Promise<QueryWithResults> {
const uri = vscode.Uri.parse(uriString, true);
if (uri.scheme !== zipArchiveScheme) {
throw new Error('AST Viewing is only available for databases with zipped source archives.');
}
const zippedArchive = decodeSourceArchiveUri(uri);
const sourceArchiveUri = vscode.Uri.file(zippedArchive.sourceArchiveZipPath).with({ scheme: zipArchiveScheme });
const db = this.dbm.findDatabaseItemBySourceArchive(sourceArchiveUri);
if (!db) {
throw new Error('Can\'t infer database from the provided source.');
}
const qlpack = await qlpackOfDatabase(this.cli, db);
if (!qlpack) {
throw new Error('Can\'t infer qlpack from database source archive');
}
const queries = await resolveQueries(this.cli, qlpack, KeyType.PrintAstQuery);
if (queries.length > 1) {
throw new Error('Found multiple Print AST queries. Can\'t continue');
}
if (queries.length === 0) {
throw new Error('Did not find any Print AST queries. Can\'t continue');
}
const query = queries[0];
const templates: messages.TemplateDefinitions = {
[TEMPLATE_NAME]: {
values: {
tuples: [[{
stringValue: zippedArchive.pathWithinSourceArchive
}]]
}
}
};
return await compileAndRunQueryAgainstDatabase(
this.cli,
this.qs,
db,
false,
vscode.Uri.file(query),
templates
);
}
}
async function getLinksFromResults(results: QueryWithResults, cli: CodeQLCliServer, db: DatabaseItem, filter: (srcFile: string, destFile: string) => boolean): Promise<FullLocationLink[]> {
async function getLinksFromResults(
results: QueryWithResults,
cli: CodeQLCliServer,
db: DatabaseItem,
filter: (srcFile: string, destFile: string) => boolean
): Promise<FullLocationLink[]> {
const localLinks: FullLocationLink[] = [];
const bqrsPath = results.query.resultsPaths.resultsPath;
const info = await cli.bqrsInfo(bqrsPath);
@@ -145,8 +261,8 @@ async function getLinksFromResults(results: QueryWithResults, cli: CodeQLCliServ
const dest = tuple[1] as EntityValue;
const srcFile = src.url && fileRangeFromURI(src.url, db);
const destFile = dest.url && fileRangeFromURI(dest.url, db);
if (srcFile && destFile && filter(srcFile.file.toString(), destFile.file.toString())) {
localLinks.push({ targetRange: destFile.range, targetUri: destFile.file, originSelectionRange: srcFile.range, originUri: srcFile.file });
if (srcFile && destFile && filter(srcFile.uri.toString(), destFile.uri.toString())) {
localLinks.push({ targetRange: destFile.range, targetUri: destFile.uri, originSelectionRange: srcFile.range, originUri: srcFile.uri });
}
}
}
@@ -191,26 +307,3 @@ async function getLinksForUriString(
return [];
}
}
function fileRangeFromURI(uri: UrlValue, db: DatabaseItem): FileRange | undefined {
if (typeof uri === 'string') {
return undefined;
} else if ('startOffset' in uri) {
return undefined;
} else {
const loc = uri as LineColumnLocation;
const range = new vscode.Range(Math.max(0, loc.startLine - 1),
Math.max(0, loc.startColumn - 1),
Math.max(0, loc.endLine - 1),
Math.max(0, loc.endColumn));
try {
const parsed = vscode.Uri.parse(uri.uri, true);
if (parsed.scheme === 'file') {
return { file: db.resolveSourceFile(parsed.fsPath), range };
}
return undefined;
} catch (e) {
return undefined;
}
}
}

View File

@@ -0,0 +1,28 @@
import * as vscode from 'vscode';
import { UrlValue, LineColumnLocation } from '../bqrs-cli-types';
import { DatabaseItem } from '../databases';
export default function fileRangeFromURI(uri: UrlValue, db: DatabaseItem): vscode.Location | undefined {
if (typeof uri === 'string') {
return undefined;
} else if ('startOffset' in uri) {
return undefined;
} else {
const loc = uri as LineColumnLocation;
const range = new vscode.Range(Math.max(0, (loc.startLine || 0) - 1),
Math.max(0, (loc.startColumn || 0) - 1),
Math.max(0, (loc.endLine || 0) - 1),
Math.max(0, (loc.endColumn || 0)));
try {
const parsed = vscode.Uri.parse(uri.uri, true);
if (parsed.scheme === 'file') {
return new vscode.Location(db.resolveSourceFile(parsed.fsPath), range);
}
return undefined;
} catch (e) {
return undefined;
}
}
}

View File

@@ -1,14 +1,32 @@
import { commands, Disposable, ExtensionContext, extensions, languages, ProgressLocation, ProgressOptions, Uri, window as Window, env } from 'vscode';
import {
commands,
Disposable,
ExtensionContext,
extensions,
languages,
ProgressLocation,
ProgressOptions,
Uri,
window as Window,
env,
window
} from 'vscode';
import { LanguageClient } from 'vscode-languageclient';
import * as path from 'path';
import { testExplorerExtensionId, TestHub } from 'vscode-test-adapter-api';
import { AstViewer } from './astViewer';
import * as archiveFilesystemProvider from './archive-filesystem-provider';
import { CodeQLCliServer } from './cli';
import { DistributionConfigListener, QueryHistoryConfigListener, QueryServerConfigListener } from './config';
import * as languageSupport from './languageSupport';
import { DatabaseManager } from './databases';
import { DatabaseUI } from './databases-ui';
import { TemplateQueryDefinitionProvider, TemplateQueryReferenceProvider } from './definitions';
import {
TemplateQueryDefinitionProvider,
TemplateQueryReferenceProvider,
TemplatePrintAstProvider
} from './contextual/definitions';
import {
DEFAULT_DISTRIBUTION_VERSION_RANGE,
DistributionKind,
@@ -523,6 +541,15 @@ async function activateWithInstalledDistribution(
new TemplateQueryReferenceProvider(cliServer, qs, dbm)
);
const astViewer = new AstViewer();
ctx.subscriptions.push(commands.registerCommand('codeQL.viewAst', async () => {
const ast = await new TemplatePrintAstProvider(cliServer, qs, dbm)
.provideAst(window.activeTextEditor?.document);
if (ast) {
astViewer.updateRoots(await ast.getRoots(), ast.db, ast.fileName);
}
}));
logger.log('Successfully finished extension initialization.');
}

View File

@@ -149,13 +149,16 @@ export function getHtmlForWebview(
</html>`;
}
export async function showLocation(
export async function showResolvableLocation(
loc: ResolvableLocationValue,
databaseItem: DatabaseItem
): Promise<void> {
const resolvedLocation = tryResolveLocation(loc, databaseItem);
if (resolvedLocation) {
const doc = await workspace.openTextDocument(resolvedLocation.uri);
await showLocation(tryResolveLocation(loc, databaseItem));
}
export async function showLocation(location?: Location) {
if (location) {
const doc = await workspace.openTextDocument(location.uri);
const editorsWithDoc = Window.visibleTextEditors.filter(
(e) => e.document === doc
);
@@ -163,7 +166,7 @@ export async function showLocation(
editorsWithDoc.length > 0
? editorsWithDoc[0]
: await Window.showTextDocument(doc, ViewColumn.One);
const range = resolvedLocation.range;
const range = location.range;
// When highlighting the range, vscode's occurrence-match and bracket-match highlighting will
// trigger based on where we place the cursor/selection, and will compete for the user's attention.
// For reference:
@@ -188,6 +191,7 @@ const findRangeHighlightBackground = new ThemeColor(
'editor.findRangeHighlightBackground'
);
export const shownLocationDecoration = Window.createTextEditorDecorationType({
backgroundColor: findMatchBackground,
});
@@ -209,7 +213,7 @@ export async function jumpToLocation(
);
if (databaseItem !== undefined) {
try {
await showLocation(msg.loc, databaseItem);
await showResolvableLocation(msg.loc, databaseItem);
} catch (e) {
if (e instanceof Error) {
if (e.message.match(/File not found/)) {

View File

@@ -105,11 +105,7 @@ class HistoryTreeDataProvider
getChildren(
element?: CompletedQuery
): vscode.ProviderResult<CompletedQuery[]> {
if (element == undefined) {
return this.history;
} else {
return [];
}
return element ? [] : this.history;
}
getParent(_element: CompletedQuery): vscode.ProviderResult<CompletedQuery> {

View File

@@ -0,0 +1,150 @@
import * as fs from 'fs-extra';
// import * as sinonChai from 'sinon-chai';
import * as chai from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import * as sinon from 'sinon';
import AstBuilder from '../../../contextual/astBuilder';
import { QueryWithResults } from '../../../run-queries';
import { CodeQLCliServer } from '../../../cli';
import { DatabaseItem } from '../../../databases';
chai.use(chaiAsPromised);
const expect = chai.expect;
/**
*
This test uses an AST generated from this file (already BQRS-decoded in ../data/astBuilder.json):
#include <common.h>
int interrupt_init(void)
{
return 0;
}
void enable_interrupts(void)
{
return;
}
int disable_interrupts(void)
{
return 0;
}
*/
describe('AstBuilder', () => {
let mockCli: CodeQLCliServer;
let overrides: Record<string, object | undefined>;
beforeEach(() => {
mockCli = {
bqrsDecode: sinon.stub().callsFake((_: string, resultSet: 'nodes' | 'edges' | 'graphProperties') => {
return mockDecode(resultSet);
})
} as unknown as CodeQLCliServer;
overrides = {
nodes: undefined,
edges: undefined,
graphProperties: undefined
};
});
it('should build the AST roots', async () => {
const astBuilder = createAstBuilder();
const roots = await astBuilder.getRoots();
expect(mockCli.bqrsDecode).to.have.been.calledWith('/a/b/c', 'nodes');
expect(mockCli.bqrsDecode).to.have.been.calledWith('/a/b/c', 'edges');
expect(mockCli.bqrsDecode).to.have.been.calledWith('/a/b/c', 'graphProperties');
expect(roots.map(
r => ({ ...r, children: undefined })
)).to.deep.eq(expectedRoots);
});
it('should fail when graphProperties are not correct', async () => {
overrides.graphProperties = {
tuples: [
[
'semmle.graphKind',
'hucairz'
]
]
};
const astBuilder = createAstBuilder();
expect(astBuilder.getRoots()).to.be.rejectedWith('AST is invalid');
});
function createAstBuilder() {
return new AstBuilder({
query: {
resultsPaths: {
resultsPath: '/a/b/c'
}
}
} as QueryWithResults, mockCli, {} as DatabaseItem, '');
}
function mockDecode(resultSet: 'nodes' | 'edges' | 'graphProperties') {
if (overrides[resultSet]) {
return overrides[resultSet];
}
const mapper = {
nodes: 0,
edges: 1,
graphProperties: 2
};
const index = mapper[resultSet] as number;
if (index >= 0 && index <= 2) {
return JSON.parse(fs.readFileSync(`${__dirname}/../data/astBuilder.json`, 'utf8'))[index];
} else {
throw new Error(`Invalid resultSet: ${resultSet}`);
}
}
});
const expectedRoots = [
{
id: 26362,
label: '[TopLevelFunction] int disable_interrupts()',
location: {
uri: 'file:/opt/src/arch/sandbox/lib/interrupts.c',
startLine: 19,
startColumn: 5,
endLine: 19,
endColumn: 22
},
order: 3,
children: undefined
},
{
id: 26363,
label: '[TopLevelFunction] void enable_interrupts()',
location: {
uri: 'file:/opt/src/arch/sandbox/lib/interrupts.c',
startLine: 15,
startColumn: 6,
endLine: 15,
endColumn: 22
},
order: 2,
children: undefined
},
{
id: 26364,
label: '[TopLevelFunction] int interrupt_init()',
location: {
uri: 'file:/opt/src/arch/sandbox/lib/interrupts.c',
startLine: 10,
startColumn: 5,
endLine: 10,
endColumn: 18
},
order: 1,
children: undefined
}
];

View File

@@ -0,0 +1,42 @@
import 'vscode-test';
import 'mocha';
import { expect } from 'chai';
import { Uri, Range } from 'vscode';
import fileRangeFromURI from '../../../contextual/fileRangeFromURI';
import { DatabaseItem } from '../../../databases';
import { WholeFileLocation, LineColumnLocation } from '../../../bqrs-cli-types';
describe('fileRangeFromURI', () => {
it('should return undefined when value is a string', () => {
expect(fileRangeFromURI('hucairz', createMockDatabaseItem())).to.be.undefined;
});
it('should return a range for a WholeFileLocation', () => {
expect(fileRangeFromURI({
uri: 'file:///hucairz',
} as WholeFileLocation, createMockDatabaseItem())).to.deep.eq({
uri: Uri.parse('file:///hucairz', true),
range: new Range(0, 0, 0, 0)
});
});
it('should return a range for a LineColumnLocation', () => {
expect(fileRangeFromURI({
uri: 'file:///hucairz',
startLine: 1,
startColumn: 2,
endLine: 3,
endColumn: 4,
} as LineColumnLocation, createMockDatabaseItem())).to.deep.eq({
uri: Uri.parse('file:///hucairz', true),
range: new Range(0, 1, 2, 4)
});
});
function createMockDatabaseItem(): DatabaseItem {
return {
resolveSourceFile: (uri: string) => Uri.parse('file://' + uri, true)
} as DatabaseItem;
}
});

View File

@@ -0,0 +1,977 @@
[
{
"columns": [
{
"name": "node",
"kind": "Entity"
},
{
"name": "key",
"kind": "String"
},
{
"name": "value",
"kind": "String"
}
],
"tuples": [
[
{
"id": 26359,
"label": "",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 19,
"startColumn": 5,
"endLine": 19,
"endColumn": 22
}
},
"semmle.label",
""
],
[
{
"id": 26360,
"label": "",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 15,
"startColumn": 6,
"endLine": 15,
"endColumn": 22
}
},
"semmle.label",
""
],
[
{
"id": 26361,
"label": "",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 10,
"startColumn": 5,
"endLine": 10,
"endColumn": 18
}
},
"semmle.label",
""
],
[
{
"id": 26362,
"label": "[TopLevelFunction] int disable_interrupts()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 19,
"startColumn": 5,
"endLine": 19,
"endColumn": 22
}
},
"semmle.label",
"[TopLevelFunction] int disable_interrupts()"
],
[
{
"id": 26362,
"label": "[TopLevelFunction] int disable_interrupts()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 19,
"startColumn": 5,
"endLine": 19,
"endColumn": 22
}
},
"semmle.order",
"3"
],
[
{
"id": 26363,
"label": "[TopLevelFunction] void enable_interrupts()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 15,
"startColumn": 6,
"endLine": 15,
"endColumn": 22
}
},
"semmle.label",
"[TopLevelFunction] void enable_interrupts()"
],
[
{
"id": 26363,
"label": "[TopLevelFunction] void enable_interrupts()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 15,
"startColumn": 6,
"endLine": 15,
"endColumn": 22
}
},
"semmle.order",
"2"
],
[
{
"id": 26364,
"label": "[TopLevelFunction] int interrupt_init()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 10,
"startColumn": 5,
"endLine": 10,
"endColumn": 18
}
},
"semmle.label",
"[TopLevelFunction] int interrupt_init()"
],
[
{
"id": 26364,
"label": "[TopLevelFunction] int interrupt_init()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 10,
"startColumn": 5,
"endLine": 10,
"endColumn": 18
}
},
"semmle.order",
"1"
],
[
{
"id": 26365,
"label": "[Literal] 0",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 21,
"startColumn": 9,
"endLine": 21,
"endColumn": 9
}
},
"Type",
"[IntType] int"
],
[
{
"id": 26365,
"label": "[Literal] 0",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 21,
"startColumn": 9,
"endLine": 21,
"endColumn": 9
}
},
"semmle.label",
"[Literal] 0"
],
[
{
"id": 26365,
"label": "[Literal] 0",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 21,
"startColumn": 9,
"endLine": 21,
"endColumn": 9
}
},
"Value",
"[Literal] 0"
],
[
{
"id": 26365,
"label": "[Literal] 0",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 21,
"startColumn": 9,
"endLine": 21,
"endColumn": 9
}
},
"ValueCategory",
"prvalue"
],
[
{
"id": 26366,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 21,
"startColumn": 2,
"endLine": 21,
"endColumn": 10
}
},
"semmle.label",
"[ReturnStmt] return ..."
],
[
{
"id": 26367,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 20,
"startColumn": 1,
"endLine": 22,
"endColumn": 1
}
},
"semmle.label",
"[Block] { ... }"
],
[
{
"id": 26368,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 17,
"startColumn": 2,
"endLine": 17,
"endColumn": 8
}
},
"semmle.label",
"[ReturnStmt] return ..."
],
[
{
"id": 26369,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 16,
"startColumn": 1,
"endLine": 18,
"endColumn": 1
}
},
"semmle.label",
"[Block] { ... }"
],
[
{
"id": 26370,
"label": "[Literal] 0",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 12,
"startColumn": 9,
"endLine": 12,
"endColumn": 9
}
},
"Type",
"[IntType] int"
],
[
{
"id": 26370,
"label": "[Literal] 0",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 12,
"startColumn": 9,
"endLine": 12,
"endColumn": 9
}
},
"semmle.label",
"[Literal] 0"
],
[
{
"id": 26370,
"label": "[Literal] 0",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 12,
"startColumn": 9,
"endLine": 12,
"endColumn": 9
}
},
"Value",
"[Literal] 0"
],
[
{
"id": 26370,
"label": "[Literal] 0",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 12,
"startColumn": 9,
"endLine": 12,
"endColumn": 9
}
},
"ValueCategory",
"prvalue"
],
[
{
"id": 26371,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 12,
"startColumn": 2,
"endLine": 12,
"endColumn": 10
}
},
"semmle.label",
"[ReturnStmt] return ..."
],
[
{
"id": 26372,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 11,
"startColumn": 1,
"endLine": 13,
"endColumn": 1
}
},
"semmle.label",
"[Block] { ... }"
]
]
},
{
"columns": [
{
"name": "source",
"kind": "Entity"
},
{
"name": "target",
"kind": "Entity"
},
{
"name": "key",
"kind": "String"
},
{
"name": "value",
"kind": "String"
}
],
"tuples": [
[
{
"id": 26362,
"label": "[TopLevelFunction] int disable_interrupts()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 19,
"startColumn": 5,
"endLine": 19,
"endColumn": 22
}
},
{
"id": 26359,
"label": "",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 19,
"startColumn": 5,
"endLine": 19,
"endColumn": 22
}
},
"semmle.label",
"params"
],
[
{
"id": 26362,
"label": "[TopLevelFunction] int disable_interrupts()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 19,
"startColumn": 5,
"endLine": 19,
"endColumn": 22
}
},
{
"id": 26359,
"label": "",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 19,
"startColumn": 5,
"endLine": 19,
"endColumn": 22
}
},
"semmle.order",
"0"
],
[
{
"id": 26362,
"label": "[TopLevelFunction] int disable_interrupts()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 19,
"startColumn": 5,
"endLine": 19,
"endColumn": 22
}
},
{
"id": 26367,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 20,
"startColumn": 1,
"endLine": 22,
"endColumn": 1
}
},
"semmle.label",
"body"
],
[
{
"id": 26362,
"label": "[TopLevelFunction] int disable_interrupts()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 19,
"startColumn": 5,
"endLine": 19,
"endColumn": 22
}
},
{
"id": 26367,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 20,
"startColumn": 1,
"endLine": 22,
"endColumn": 1
}
},
"semmle.order",
"2"
],
[
{
"id": 26363,
"label": "[TopLevelFunction] void enable_interrupts()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 15,
"startColumn": 6,
"endLine": 15,
"endColumn": 22
}
},
{
"id": 26360,
"label": "",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 15,
"startColumn": 6,
"endLine": 15,
"endColumn": 22
}
},
"semmle.label",
"params"
],
[
{
"id": 26363,
"label": "[TopLevelFunction] void enable_interrupts()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 15,
"startColumn": 6,
"endLine": 15,
"endColumn": 22
}
},
{
"id": 26360,
"label": "",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 15,
"startColumn": 6,
"endLine": 15,
"endColumn": 22
}
},
"semmle.order",
"0"
],
[
{
"id": 26363,
"label": "[TopLevelFunction] void enable_interrupts()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 15,
"startColumn": 6,
"endLine": 15,
"endColumn": 22
}
},
{
"id": 26369,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 16,
"startColumn": 1,
"endLine": 18,
"endColumn": 1
}
},
"semmle.label",
"body"
],
[
{
"id": 26363,
"label": "[TopLevelFunction] void enable_interrupts()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 15,
"startColumn": 6,
"endLine": 15,
"endColumn": 22
}
},
{
"id": 26369,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 16,
"startColumn": 1,
"endLine": 18,
"endColumn": 1
}
},
"semmle.order",
"2"
],
[
{
"id": 26364,
"label": "[TopLevelFunction] int interrupt_init()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 10,
"startColumn": 5,
"endLine": 10,
"endColumn": 18
}
},
{
"id": 26361,
"label": "",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 10,
"startColumn": 5,
"endLine": 10,
"endColumn": 18
}
},
"semmle.label",
"params"
],
[
{
"id": 26364,
"label": "[TopLevelFunction] int interrupt_init()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 10,
"startColumn": 5,
"endLine": 10,
"endColumn": 18
}
},
{
"id": 26361,
"label": "",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 10,
"startColumn": 5,
"endLine": 10,
"endColumn": 18
}
},
"semmle.order",
"0"
],
[
{
"id": 26364,
"label": "[TopLevelFunction] int interrupt_init()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 10,
"startColumn": 5,
"endLine": 10,
"endColumn": 18
}
},
{
"id": 26372,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 11,
"startColumn": 1,
"endLine": 13,
"endColumn": 1
}
},
"semmle.label",
"body"
],
[
{
"id": 26364,
"label": "[TopLevelFunction] int interrupt_init()",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 10,
"startColumn": 5,
"endLine": 10,
"endColumn": 18
}
},
{
"id": 26372,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 11,
"startColumn": 1,
"endLine": 13,
"endColumn": 1
}
},
"semmle.order",
"2"
],
[
{
"id": 26366,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 21,
"startColumn": 2,
"endLine": 21,
"endColumn": 10
}
},
{
"id": 26365,
"label": "[Literal] 0",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 21,
"startColumn": 9,
"endLine": 21,
"endColumn": 9
}
},
"semmle.label",
"0"
],
[
{
"id": 26366,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 21,
"startColumn": 2,
"endLine": 21,
"endColumn": 10
}
},
{
"id": 26365,
"label": "[Literal] 0",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 21,
"startColumn": 9,
"endLine": 21,
"endColumn": 9
}
},
"semmle.order",
"0"
],
[
{
"id": 26367,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 20,
"startColumn": 1,
"endLine": 22,
"endColumn": 1
}
},
{
"id": 26366,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 21,
"startColumn": 2,
"endLine": 21,
"endColumn": 10
}
},
"semmle.label",
"0"
],
[
{
"id": 26367,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 20,
"startColumn": 1,
"endLine": 22,
"endColumn": 1
}
},
{
"id": 26366,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 21,
"startColumn": 2,
"endLine": 21,
"endColumn": 10
}
},
"semmle.order",
"0"
],
[
{
"id": 26369,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 16,
"startColumn": 1,
"endLine": 18,
"endColumn": 1
}
},
{
"id": 26368,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 17,
"startColumn": 2,
"endLine": 17,
"endColumn": 8
}
},
"semmle.label",
"0"
],
[
{
"id": 26369,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 16,
"startColumn": 1,
"endLine": 18,
"endColumn": 1
}
},
{
"id": 26368,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 17,
"startColumn": 2,
"endLine": 17,
"endColumn": 8
}
},
"semmle.order",
"0"
],
[
{
"id": 26371,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 12,
"startColumn": 2,
"endLine": 12,
"endColumn": 10
}
},
{
"id": 26370,
"label": "[Literal] 0",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 12,
"startColumn": 9,
"endLine": 12,
"endColumn": 9
}
},
"semmle.label",
"0"
],
[
{
"id": 26371,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 12,
"startColumn": 2,
"endLine": 12,
"endColumn": 10
}
},
{
"id": 26370,
"label": "[Literal] 0",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 12,
"startColumn": 9,
"endLine": 12,
"endColumn": 9
}
},
"semmle.order",
"0"
],
[
{
"id": 26372,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 11,
"startColumn": 1,
"endLine": 13,
"endColumn": 1
}
},
{
"id": 26371,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 12,
"startColumn": 2,
"endLine": 12,
"endColumn": 10
}
},
"semmle.label",
"0"
],
[
{
"id": 26372,
"label": "[Block] { ... }",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 11,
"startColumn": 1,
"endLine": 13,
"endColumn": 1
}
},
{
"id": 26371,
"label": "[ReturnStmt] return ...",
"url": {
"uri": "file:/opt/src/arch/sandbox/lib/interrupts.c",
"startLine": 12,
"startColumn": 2,
"endLine": 12,
"endColumn": 10
}
},
"semmle.order",
"0"
]
]
},
{
"columns": [
{
"name": "key",
"kind": "String"
},
{
"name": "value",
"kind": "String"
}
],
"tuples": [
[
"semmle.graphKind",
"tree"
]
]
}
]

View File

@@ -2,7 +2,6 @@ import 'vscode-test';
import 'mocha';
import * as chaiAsPromised from 'chai-as-promised';
import * as sinon from 'sinon';
// import * as sinonChai from 'sinon-chai';
import * as path from 'path';
import * as fs from 'fs-extra';
import * as tmp from 'tmp';