Python: implement printAst for Python#4461
Conversation
|
The tests failed in CI, but not on my machine. My best guess was that every |
|
Thank you very much! I hope one of us finds time to look at it soon :-) |
|
Thanks for looking into print AST support in Python. Trying it out now. I am using https://lgtm.com/projects/g/AtsushiSakai/PythonRobotics/. I mostly looked at the
|
That is the location of
I changed the order 👍
There is a bunch of those synthetic nodes made by the extractor. For example all the positional arguments of a call are aggregated into an
I removed the location from
That was a bug, I had accidentally used the wrong method.
👍 |
|
Thanks for addressing everything. Ah...I see how |
|
This looks great. Only issue is that I am seeing errors when I click on synthetic nodes like Similar to what I was seeing in JavaScript. |
And similar to JavaScript I don't see what to do about it - none of those synthetic nodes have a result for |
|
Will be fixed here: github/vscode-codeql#620 Thanks for looking into this. I'm happy with the implementation, but someone on the python team should approve. |
/cc @github/codeql-python |
yoff
left a comment
There was a problem hiding this comment.
Thanks again for doing this.
There is some extractor-induced funkiness (like how decorators are represented, although it makes great semantic sense), but the feature is certainly useful.
Co-authored-by: yoff <lerchedahl@gmail.com>
tausbn
left a comment
There was a problem hiding this comment.
Good stuff! Thank you for stepping out of your comfort zone and writing some Python. 😉
I’ve made a couple of suggestions, all related to the documentation.
|
|
||
| /** | ||
| * Controls whether the `Element` should be considered for AST printing. | ||
| * By default it checks whether the `Element` `e` belongs to `Location` `l`. |
There was a problem hiding this comment.
This says “Element” where it should perhaps say “AstNode”?
| * is `key`. | ||
| */ | ||
| string getProperty(string key) { | ||
| key = "semmle.label" and |
There was a problem hiding this comment.
I’m just going to assume that this is some sort of deep magic that is meaningful to someone.
| /** | ||
| * An `AstNode` printed in the print-viewer. | ||
| * | ||
| * This class can be overwriten to define more specific behavior for some `AstNode`s. |
There was a problem hiding this comment.
| * This class can be overwriten to define more specific behavior for some `AstNode`s. | |
| * This class can be overwritten to define more specific behavior for some `AstNode`s. |
... unless you actually meant to use “overridden“.
| * | ||
| * This class can be overwriten to define more specific behavior for some `AstNode`s. | ||
| * The `getChildNode` and `getStmtList` methods can be overwritten to easily set up a child-parent relation between different `AstElementNode`s. | ||
| * Be very carefull about overriding `getChild`, as `getChildNode` and `getStmtList` depends on the default beavior of `getChild`. |
There was a problem hiding this comment.
| * Be very carefull about overriding `getChild`, as `getChildNode` and `getStmtList` depends on the default beavior of `getChild`. | |
| * Be very careful about overriding `getChild`, as `getChildNode` and `getStmtList` depend on the default behavior of `getChild`. |
|
|
||
| /** | ||
| * A print node for `StmtList`. | ||
| * A `StmtListNode` is always a child of a `AstElementNode`, |
There was a problem hiding this comment.
| * A `StmtListNode` is always a child of a `AstElementNode`, | |
| * A `StmtListNode` is always a child of an `AstElementNode`, |
| /** | ||
| * A print node for `StmtList`. | ||
| * A `StmtListNode` is always a child of a `AstElementNode`, | ||
| * and the child-parent relation is decided by the `getStmtList` predicate in `AstElementNode. |
There was a problem hiding this comment.
| * and the child-parent relation is decided by the `getStmtList` predicate in `AstElementNode. | |
| * and the child-parent relation is defined by the `getStmtList` predicate in `AstElementNode`. |
| */ | ||
| private module PrettyPrinting { | ||
| /** | ||
| * Gets the QL class for `a`. |
There was a problem hiding this comment.
| * Gets the QL class for `a`. | |
| * Gets the QL class for the `AstNode` `a`. |
| } | ||
|
|
||
| /** | ||
| * Gets the QL class for `a` for the `AstNode`s where the toString method does not print the QL class. |
There was a problem hiding this comment.
| * Gets the QL class for `a` for the `AstNode`s where the toString method does not print the QL class. | |
| * Gets the QL class for `AstNode`s where the `toString` method does not print the QL class. |
| /** | ||
| * Gets a human-readable representation of the `AstNode` `a`, or the empty string. | ||
| * | ||
| * Has one and only result for every AstNode. |
There was a problem hiding this comment.
| * Has one and only result for every AstNode. | |
| * Has exactly one result for every `AstNode`. |
|
|
||
| /** | ||
| * Gets a human-readable representation of the given `AstNode`. | ||
| * Is only defined for the an `AstNode` where a human-readable representation can be created without using recursion. |
There was a problem hiding this comment.
| * Is only defined for the an `AstNode` where a human-readable representation can be created without using recursion. | |
| * Is only defined for `AstNode`s for which a human-readable representation can be created without using recursion. |
|
I'm somewhat surprised that you didn't just batch my suggestions and commit them directly, but who am I to argue. 🙂 |

(Creating a Python PR feels weird).
I recommend trying out the printAst feature (see next section) before reviewing the code.
This PR adds a
printAst.qlquery that can be used for viewing the AST of Python files.Recommended usage instructions:
"codeQL.experimentalAstViewer": trueto your settings in VSCode(Ctrl + Shift + P:
Open Settings (JSON)).View ASTbutton in the bottom.The printAst.ql query produces a tree, and there are 3 basic kinds of nodes in this tree (most of the other languages have more kinds of nodes, JS has 15):
AstElementNode: This node represents anAstNode. The children of this node can be any kind of node.FunctionParamsNode: This node represents the parameters of a function. The children are alwaysAstElementNode.StmtListNode: This node represents aStmtList, and is e.g. used as a child of an if-statement. The children of aStmtListNodeare alwaysAstElementNode.The
StmtListthat this node represents must be a result fromAstElementNode::getStmtList(...). This is used to figure out where theStmtListbelongs and printing a label.I could have made a
CallArgumentsNodefor aggregating all the arguments in a call, but I decided against this, as the result already looks pretty good as it is. (I've done that differently in JavaScript. But JavaScript have both arguments and type-arguments, so not splitting them up would look bad).The
AstElementNodeimplements some default behavior that works forAstNodes, and for some it actually looks OK (it determines children based on theAstNode::getAChildNode()predicate).This default behavior is overwritten in subclasses to get more specific/prettier behavior, for example:
FunctionParamsNode.StmtListbecomes a child, instead of eachStmtinside theStmtLists.The pretty-printing is quick and dirty and far from complete.
But it looks pretty good to me.
Maybe the
PrettyPrintingmodule should be moved somewhere else?