Skip to content

Comments

Add the AST Viewer#492

Merged
aeisenberg merged 11 commits intogithub:mainfrom
aeisenberg:aeisenberg/ast-viewer
Jul 23, 2020
Merged

Add the AST Viewer#492
aeisenberg merged 11 commits intogithub:mainfrom
aeisenberg:aeisenberg/ast-viewer

Conversation

@aeisenberg
Copy link
Contributor

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 github/codeql#3931 for the C/C++ query to be available in the QL sources. Other languages will need similar queries.

Replace this with a description of the changes your pull request makes.

Checklist

  • CHANGELOG.md has been updated to incorporate all user visible changes made by this pull request.
  • Issues have been created for any UI or other user-facing changes made by this pull request.
  • @github/product-docs-dsp has been cc'd in all issues for UI or other user-facing changes made by this pull request.

@aeisenberg aeisenberg requested review from dbartol and jcreedcmu July 9, 2020 20:10
@aeisenberg aeisenberg force-pushed the aeisenberg/ast-viewer branch from 121f8ea to d0053e7 Compare July 10, 2020 14:46
@aeisenberg
Copy link
Contributor Author

Note: Now rebaased on #494. Let's get that one in first.

@aeisenberg aeisenberg force-pushed the aeisenberg/ast-viewer branch from d0053e7 to c7c27fe Compare July 10, 2020 15:05
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 github/codeql#3931 for the C/C++ query to be available in the QL sources. Other languages will need similar queries.
* Fix command-linting tests.
* Fix failing windows test and Use Uri.parse(_, true)
* Use  Uri.parse(_, true). That is the preferred API.
* Reformat comments.
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 github/codeql#3931 for the C/C++ query to be available in the QL sources. Other languages will need similar queries.
* Fix command-linting tests.
* Fix failing windows test and Use Uri.parse(_, true)
* Use  Uri.parse(_, true). That is the preferred API.
* Reformat comments.
Set `codeQL.experimentalAstViewer` to true in settings
in order for component to be enabled.
async bqrsDecode(bqrsPath: string, resultSet: string, pageSize?: number, offset?: number): Promise<DecodedBqrsChunk> {
const subcommandArgs = [
'--entities=url,string',
'--entities=id,url,string',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't suppose there's any performance penalty for including more things than we need for any particular query here, is there? Just idle speculation as to whether we might want to parameterize over the set of entities, since the other bqrs decoding code paths didn't need id until now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally implemented this as optionally including the id field. I could add it back.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do whatever you think is reasonable; I can see the side of the argument for it being awkward to make it optional too, since that makes the type vary.

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())) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file -> uri replacement is slightly scary to me since I don't have this fully paged into my brain. I would have thought those would have different behavior. Did this not work before? Does it definitely continue working after this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh...I had forgotten that I did this. We are comparing 2 strings to see if they are a match and if so, then we mark the tuple as a match. Earlier on, we call getDefinitions and getReferences with document.uri.toString(). So, I am not sure how this worked in the past. I'll go try this out now and see.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I extracted fileRangeFromURI to its own file and I made it return a vscode.Location object. It's the same as before, except it is returning a vscode api type. file and uri are both vscode.Uri instances.


// Build up the parent-child relationships
edgeTuples.tuples.forEach(tuple => {
const from = tuple[0] as EntityValue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a huge deal, but I'd have a slight preference for establishing what type we expect tuple to have right away, like

    // Build up the parent-child relationships
    edgeTuples.tuples.forEach(tuple => {
      const [src, tgt, tupleType, orderValue] = tuple as [EntityValue, EntityValue, string, number];
      const toId = src.id!;
      const fromId = tgt.id!;

      switch (tupleType) {
        case 'semmle.order':
          astOrder.set(toId, Number(orderValue));
          break;
        case 'semmle.label': {
          childToParent.set(toId, fromId);
          let children = parentToChildren.get(fromId);
          if (!children) {
            parentToChildren.set(fromId, children = []);
          }
          children.push(toId);
          break;
        }
        default:
          throw new Error(`unexpected tuple type ${tupleType}`);
      }
    });

I don't know if the names I provided are the most appropriate for the fields of the tuples but that's sort of my point --- I'm a little uncertain reading this code what the expected schema/interpretation is.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(and if the interpretation of some columns depends on the values of others, which looks like it might be the case here with respect to tuple[3] depending on tuple[2], it would be similarly nice to clarify that)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point. I will clarify.


// populate parents and children
nodeTuples.tuples.forEach(tuple => {
const entity = tuple[0] as EntityValue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above about deconstructing tuple here

});

// find the roots and add the order
for(const [, item] of idToItem) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know you didn't have to write an explicit undefined to avoid binding a tuple deconstructor field! Nice.

switch (keyType) {
case KeyType.DefinitionQuery: return 'ide-contextual-queries/local-definitions';
case KeyType.ReferenceQuery: return 'ide-contextual-queries/local-references';
case KeyType.DefinitionQuery:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like a bunch of these definitions should be hoisted out of definitions.ts into some more general file in contextual/ but that needn't happen in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I agree that it's getting a bit messy. I'll work on that next.

@aeisenberg aeisenberg force-pushed the aeisenberg/ast-viewer branch from 572e38c to 0045891 Compare July 22, 2020 20:34
children.push(toId);

default:
// ignore other tupleTypes since they are not needed by the ast viewer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, ok, great --- I didn't know that other values of tupleType existed, and it's great to clarify here that we're explicitly ignoring them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for suggesting that change. I think it makes the code clearer.

@aeisenberg aeisenberg merged commit 1017741 into github:main Jul 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants