From 19af3e5e305c4f13895a02db96ce4284aef929fe Mon Sep 17 00:00:00 2001 From: Joe Date: Thu, 10 Sep 2020 10:47:47 +0100 Subject: [PATCH] Java: Add PrintAST --- java/ql/src/printAst.ql | 28 ++ java/ql/src/semmle/code/Location.qll | 13 + java/ql/src/semmle/code/java/Annotation.qll | 2 + .../src/semmle/code/java/CompilationUnit.qll | 2 + java/ql/src/semmle/code/java/Expr.qll | 148 +++++- java/ql/src/semmle/code/java/Import.qll | 10 + java/ql/src/semmle/code/java/Member.qll | 6 + java/ql/src/semmle/code/java/PrintAst.qll | 432 ++++++++++++++++++ java/ql/src/semmle/code/java/Statement.qll | 52 +++ java/ql/src/semmle/code/java/Type.qll | 8 + java/ql/src/semmle/code/java/Variable.qll | 2 + 11 files changed, 698 insertions(+), 5 deletions(-) create mode 100644 java/ql/src/printAst.ql create mode 100644 java/ql/src/semmle/code/java/PrintAst.qll diff --git a/java/ql/src/printAst.ql b/java/ql/src/printAst.ql new file mode 100644 index 00000000000..8be94cb1d87 --- /dev/null +++ b/java/ql/src/printAst.ql @@ -0,0 +1,28 @@ +/** + * @name Print AST + * @description Outputs a representation of a file's Abstract Syntax Tree. This + * query is used by the VS Code extension. + * @id csharp/print-ast + * @kind graph + * @tags ide-contextual-queries/print-ast + */ + + import java + import semmle.code.java.PrintAst + import definitions + + /** + * The source file to generate an AST from. + */ +external string selectedSourceFile(); + +class PrintAstConfigurationOverride extends PrintAstConfiguration { + /** + * Holds if the location matches the selected file in the VS Code extension and + * the element is `fromSource`. + */ + override predicate shouldPrint(Element e, Location l) { + super.shouldPrint(e, l) and + l.getFile() = getEncodedFile(selectedSourceFile()) + } +} \ No newline at end of file diff --git a/java/ql/src/semmle/code/Location.qll b/java/ql/src/semmle/code/Location.qll index 5cb97a39979..110450d374f 100755 --- a/java/ql/src/semmle/code/Location.qll +++ b/java/ql/src/semmle/code/Location.qll @@ -86,6 +86,19 @@ class Top extends @top { /** Gets a textual representation of this element. */ cached string toString() { hasName(this, result) } + + /** + * Gets the name of a primary CodeQL class to which this element belongs. + * + * For most elements, this is simply the most precise syntactic category to + * which they belong; for example, `AddExpr` is a primary class, but + * `BinaryExpr` is not. + * + * This predicate always has a result. If no primary class can be + * determined, the result is `"???"`. If multiple primary classes match, + * this predicate can have multiple results. + */ + string getAPrimaryQlClass() { result = "???" } } /** A location maps language elements to positions in source files. */ diff --git a/java/ql/src/semmle/code/java/Annotation.qll b/java/ql/src/semmle/code/java/Annotation.qll index 42a855487d9..e93e412e028 100755 --- a/java/ql/src/semmle/code/java/Annotation.qll +++ b/java/ql/src/semmle/code/java/Annotation.qll @@ -72,6 +72,8 @@ class Annotation extends @annotation, Expr { if value instanceof ArrayInit then result = value.(ArrayInit).getAnInit() else result = value ) } + + override string getAPrimaryQlClass() { result = "Annotation" } } /** An `Annotation` that applies to a declaration. */ diff --git a/java/ql/src/semmle/code/java/CompilationUnit.qll b/java/ql/src/semmle/code/java/CompilationUnit.qll index a9e3ee8ac7f..9b4b58e9a9b 100755 --- a/java/ql/src/semmle/code/java/CompilationUnit.qll +++ b/java/ql/src/semmle/code/java/CompilationUnit.qll @@ -30,4 +30,6 @@ class CompilationUnit extends Element, File { * Gets the module associated with this compilation unit, if any. */ Module getModule() { cumodule(this, result) } + + override string getAPrimaryQlClass() { result = "CompilationUnit" } } diff --git a/java/ql/src/semmle/code/java/Expr.qll b/java/ql/src/semmle/code/java/Expr.qll index 070cb45783c..7641535aaab 100755 --- a/java/ql/src/semmle/code/java/Expr.qll +++ b/java/ql/src/semmle/code/java/Expr.qll @@ -413,6 +413,8 @@ class ArrayAccess extends Expr, @arrayaccess { Expr getIndexExpr() { result.isNthChildOf(this, 1) } override string toString() { result = "...[...]" } + + override string getAPrimaryQlClass() { result = "ArrayAccess" } } /** @@ -450,6 +452,8 @@ class ArrayCreationExpr extends Expr, @arraycreationexpr { /** Gets a printable representation of this expression. */ override string toString() { result = "new " + this.getType().toString() } + + override string getAPrimaryQlClass() { result = "ArrayCreationExpr" } } /** An array initializer occurs in an array creation expression. */ @@ -467,6 +471,8 @@ class ArrayInit extends Expr, @arrayinit { /** Gets a printable representation of this expression. */ override string toString() { result = "{...}" } + + override string getAPrimaryQlClass() { result = "ArrayInit" } } /** A common super-class that represents all varieties of assignments. */ @@ -494,7 +500,9 @@ class Assignment extends Expr, @assignment { * * For example, `x = 23`. */ -class AssignExpr extends Assignment, @assignexpr { } +class AssignExpr extends Assignment, @assignexpr { + override string getAPrimaryQlClass() { result = "AssignExpr" } +} /** * A common super-class to represent compound assignments, which include an implicit operator. @@ -519,56 +527,78 @@ class AssignOp extends Assignment, @assignop { /** A compound assignment expression using the `+=` operator. */ class AssignAddExpr extends AssignOp, @assignaddexpr { override string getOp() { result = "+=" } + + override string getAPrimaryQlClass() { result = "AssignAddExpr" } } /** A compound assignment expression using the `-=` operator. */ class AssignSubExpr extends AssignOp, @assignsubexpr { override string getOp() { result = "-=" } + + override string getAPrimaryQlClass() { result = "AssignSubExpr" } } /** A compound assignment expression using the `*=` operator. */ class AssignMulExpr extends AssignOp, @assignmulexpr { override string getOp() { result = "*=" } + + override string getAPrimaryQlClass() { result = "AssignMulExpr" } } /** A compound assignment expression using the `/=` operator. */ class AssignDivExpr extends AssignOp, @assigndivexpr { override string getOp() { result = "/=" } + + override string getAPrimaryQlClass() { result = "AssignDivExpr" } } /** A compound assignment expression using the `%=` operator. */ class AssignRemExpr extends AssignOp, @assignremexpr { override string getOp() { result = "%=" } + + override string getAPrimaryQlClass() { result = "AssignRemExpr" } } /** A compound assignment expression using the `&=` operator. */ class AssignAndExpr extends AssignOp, @assignandexpr { override string getOp() { result = "&=" } + + override string getAPrimaryQlClass() { result = "AssignAndExpr" } } /** A compound assignment expression using the `|=` operator. */ class AssignOrExpr extends AssignOp, @assignorexpr { override string getOp() { result = "|=" } + + override string getAPrimaryQlClass() { result = "AssignOrExpr" } } /** A compound assignment expression using the `^=` operator. */ class AssignXorExpr extends AssignOp, @assignxorexpr { override string getOp() { result = "^=" } + + override string getAPrimaryQlClass() { result = "AssignXorExpr" } } /** A compound assignment expression using the `<<=` operator. */ class AssignLShiftExpr extends AssignOp, @assignlshiftexpr { override string getOp() { result = "<<=" } + + override string getAPrimaryQlClass() { result = "AssignLShiftExpr" } } /** A compound assignment expression using the `>>=` operator. */ class AssignRShiftExpr extends AssignOp, @assignrshiftexpr { override string getOp() { result = ">>=" } + + override string getAPrimaryQlClass() { result = "AssignRShiftExpr" } } /** A compound assignment expression using the `>>>=` operator. */ class AssignURShiftExpr extends AssignOp, @assignurshiftexpr { override string getOp() { result = ">>>=" } + + override string getAPrimaryQlClass() { result = "AssignURShiftExpr" } } /** A common super-class to represent constant literals. */ @@ -597,25 +627,37 @@ class BooleanLiteral extends Literal, @booleanliteral { or result = false and getLiteral() = "false" } + + override string getAPrimaryQlClass() { result = "BooleanLiteral" } } /** An integer literal. For example, `23`. */ class IntegerLiteral extends Literal, @integerliteral { /** Gets the int representation of this literal. */ int getIntValue() { result = getValue().toInt() } + + override string getAPrimaryQlClass() { result = "IntegerLiteral" } } /** A long literal. For example, `23l`. */ -class LongLiteral extends Literal, @longliteral { } +class LongLiteral extends Literal, @longliteral { + override string getAPrimaryQlClass() { result = "LongLiteral" } +} /** A floating point literal. For example, `4.2f`. */ -class FloatingPointLiteral extends Literal, @floatingpointliteral { } +class FloatingPointLiteral extends Literal, @floatingpointliteral { + override string getAPrimaryQlClass() { result = "FloatingPointLiteral" } +} /** A double literal. For example, `4.2`. */ -class DoubleLiteral extends Literal, @doubleliteral { } +class DoubleLiteral extends Literal, @doubleliteral { + override string getAPrimaryQlClass() { result = "DoubleLiteral" } +} /** A character literal. For example, `'\n'`. */ -class CharacterLiteral extends Literal, @characterliteral { } +class CharacterLiteral extends Literal, @characterliteral { + override string getAPrimaryQlClass() { result = "CharacterLiteral" } +} /** A string literal. For example, `"hello world"`. */ class StringLiteral extends Literal, @stringliteral { @@ -623,6 +665,8 @@ class StringLiteral extends Literal, @stringliteral { * Gets the literal string without the quotes. */ string getRepresentedString() { result = getValue() } + + override string getAPrimaryQlClass() { result = "StringLiteral" } } /** The null literal, written `null`. */ @@ -630,6 +674,8 @@ class NullLiteral extends Literal, @nullliteral { override string getLiteral() { result = "null" } override string getValue() { result = "null" } + + override string getAPrimaryQlClass() { result = "NullLiteral" } } /** A common super-class to represent binary operator expressions. */ @@ -661,96 +707,134 @@ class BinaryExpr extends Expr, @binaryexpr { /** A binary expression using the `*` operator. */ class MulExpr extends BinaryExpr, @mulexpr { override string getOp() { result = " * " } + + override string getAPrimaryQlClass() { result = "MulExpr" } } /** A binary expression using the `/` operator. */ class DivExpr extends BinaryExpr, @divexpr { override string getOp() { result = " / " } + + override string getAPrimaryQlClass() { result = "DivExpr" } } /** A binary expression using the `%` operator. */ class RemExpr extends BinaryExpr, @remexpr { override string getOp() { result = " % " } + + override string getAPrimaryQlClass() { result = "RemExpr" } } /** A binary expression using the `+` operator. */ class AddExpr extends BinaryExpr, @addexpr { override string getOp() { result = " + " } + + override string getAPrimaryQlClass() { result = "AddExpr" } } /** A binary expression using the `-` operator. */ class SubExpr extends BinaryExpr, @subexpr { override string getOp() { result = " - " } + + override string getAPrimaryQlClass() { result = "SubExpr" } } /** A binary expression using the `<<` operator. */ class LShiftExpr extends BinaryExpr, @lshiftexpr { override string getOp() { result = " << " } + + override string getAPrimaryQlClass() { result = "LShiftExpr" } } /** A binary expression using the `>>` operator. */ class RShiftExpr extends BinaryExpr, @rshiftexpr { override string getOp() { result = " >> " } + + override string getAPrimaryQlClass() { result = "RShiftExpr" } } /** A binary expression using the `>>>` operator. */ class URShiftExpr extends BinaryExpr, @urshiftexpr { override string getOp() { result = " >>> " } + + override string getAPrimaryQlClass() { result = "URShiftExpr" } } /** A binary expression using the `&` operator. */ class AndBitwiseExpr extends BinaryExpr, @andbitexpr { override string getOp() { result = " & " } + + override string getAPrimaryQlClass() { result = "AddBitwiseExpr" } } /** A binary expression using the `|` operator. */ class OrBitwiseExpr extends BinaryExpr, @orbitexpr { override string getOp() { result = " | " } + + override string getAPrimaryQlClass() { result = "OrBitwiseExpr" } } /** A binary expression using the `^` operator. */ class XorBitwiseExpr extends BinaryExpr, @xorbitexpr { override string getOp() { result = " ^ " } + + override string getAPrimaryQlClass() { result = "?XorBitwiseExpr" } } /** A binary expression using the `&&` operator. */ class AndLogicalExpr extends BinaryExpr, @andlogicalexpr { override string getOp() { result = " && " } + + override string getAPrimaryQlClass() { result = "AndLogicalExpr" } } /** A binary expression using the `||` operator. */ class OrLogicalExpr extends BinaryExpr, @orlogicalexpr { override string getOp() { result = " || " } + + override string getAPrimaryQlClass() { result = "OrLogicalExpr" } } /** A binary expression using the `<` operator. */ class LTExpr extends BinaryExpr, @ltexpr { override string getOp() { result = " < " } + + override string getAPrimaryQlClass() { result = "LTExpr" } } /** A binary expression using the `>` operator. */ class GTExpr extends BinaryExpr, @gtexpr { override string getOp() { result = " > " } + + override string getAPrimaryQlClass() { result = "GTExpr" } } /** A binary expression using the `<=` operator. */ class LEExpr extends BinaryExpr, @leexpr { override string getOp() { result = " <= " } + + override string getAPrimaryQlClass() { result = "LEExpr" } } /** A binary expression using the `>=` operator. */ class GEExpr extends BinaryExpr, @geexpr { override string getOp() { result = " >= " } + + override string getAPrimaryQlClass() { result = "GEExpr" } } /** A binary expression using the `==` operator. */ class EQExpr extends BinaryExpr, @eqexpr { override string getOp() { result = " == " } + + override string getAPrimaryQlClass() { result = "EQExpr" } } /** A binary expression using the `!=` operator. */ class NEExpr extends BinaryExpr, @neexpr { override string getOp() { result = " != " } + + override string getAPrimaryQlClass() { result = "NEExpr" } } /** @@ -872,41 +956,57 @@ class UnaryAssignExpr extends UnaryExpr, @unaryassignment { } /** A post-increment expression. For example, `i++`. */ class PostIncExpr extends UnaryAssignExpr, @postincexpr { override string toString() { result = "...++" } + + override string getAPrimaryQlClass() { result = "PostIncExpr" } } /** A post-decrement expression. For example, `i--`. */ class PostDecExpr extends UnaryAssignExpr, @postdecexpr { override string toString() { result = "...--" } + + override string getAPrimaryQlClass() { result = "PostDecExpr" } } /** A pre-increment expression. For example, `++i`. */ class PreIncExpr extends UnaryAssignExpr, @preincexpr { override string toString() { result = "++..." } + + override string getAPrimaryQlClass() { result = "PreIncExpr" } } /** A pre-decrement expression. For example, `--i`. */ class PreDecExpr extends UnaryAssignExpr, @predecexpr { override string toString() { result = "--..." } + + override string getAPrimaryQlClass() { result = "PreDecExpr" } } /** A unary minus expression. For example, `-i`. */ class MinusExpr extends UnaryExpr, @minusexpr { override string toString() { result = "-..." } + + override string getAPrimaryQlClass() { result = "MinusExpr" } } /** A unary plus expression. For example, `+i`. */ class PlusExpr extends UnaryExpr, @plusexpr { override string toString() { result = "+..." } + + override string getAPrimaryQlClass() { result = "PlusExpr" } } /** A bit negation expression. For example, `~x`. */ class BitNotExpr extends UnaryExpr, @bitnotexpr { override string toString() { result = "~..." } + + override string getAPrimaryQlClass() { result = "BitNotExpr" } } /** A logical negation expression. For example, `!b`. */ class LogNotExpr extends UnaryExpr, @lognotexpr { override string toString() { result = "!..." } + + override string getAPrimaryQlClass() { result = "LogNotExpr" } } /** A cast expression. */ @@ -919,6 +1019,8 @@ class CastExpr extends Expr, @castexpr { /** Gets a printable representation of this expression. */ override string toString() { result = "(...)..." } + + override string getAPrimaryQlClass() { result = "CastExpr" } } /** A class instance creation expression. */ @@ -985,6 +1087,8 @@ class ClassInstanceExpr extends Expr, ConstructorCall, @classinstancexpr { /** Gets a printable representation of this expression. */ override string toString() { result = "new " + this.getConstructor().getName() + "(...)" } + + override string getAPrimaryQlClass() { result = "ClassInstanceExpr" } } /** A functional expression is either a lambda expression or a member reference expression. */ @@ -1026,6 +1130,8 @@ class LambdaExpr extends FunctionalExpr, @lambdaexpr { /** Gets a printable representation of this expression. */ override string toString() { result = "...->..." } + + override string getAPrimaryQlClass() { result = "LambdaExpr" } } /** @@ -1054,6 +1160,8 @@ class MemberRefExpr extends FunctionalExpr, @memberref { /** Gets a printable representation of this expression. */ override string toString() { result = "...::..." } + + override string getAPrimaryQlClass() { result = "MemberRefExpr" } } /** A conditional expression or a `switch` expression. */ @@ -1091,6 +1199,8 @@ class ConditionalExpr extends Expr, @conditionalexpr { /** Gets a printable representation of this expression. */ override string toString() { result = "...?...:..." } + + override string getAPrimaryQlClass() { result = "ConditionalExpr" } } /** @@ -1130,6 +1240,8 @@ class SwitchExpr extends Expr, @switchexpr { /** Gets a printable representation of this expression. */ override string toString() { result = "switch (...)" } + + override string getAPrimaryQlClass() { result = "SwitchExpr" } } /** @@ -1173,6 +1285,8 @@ class InstanceOfExpr extends Expr, @instanceofexpr { /** Gets a printable representation of this expression. */ override string toString() { result = "...instanceof..." } + + override string getAPrimaryQlClass() { result = "InstanceOfExpr" } } /** @@ -1215,6 +1329,8 @@ class LocalVariableDeclExpr extends Expr, @localvariabledeclexpr { /** Gets a printable representation of this expression. */ override string toString() { result = this.getName() } + + override string getAPrimaryQlClass() { result = "LocalVariableDeclExpr" } } /** An update of a variable or an initialization of the variable. */ @@ -1261,6 +1377,8 @@ class TypeLiteral extends Expr, @typeliteral { /** Gets a printable representation of this expression. */ override string toString() { result = this.getTypeName().toString() + ".class" } + + override string getAPrimaryQlClass() { result = "TypeLiteral" } } /** @@ -1306,6 +1424,8 @@ class ThisAccess extends InstanceAccess, @thisaccess { override string toString() { if exists(this.getQualifier()) then result = this.getQualifier() + ".this" else result = "this" } + + override string getAPrimaryQlClass() { result = "ThisAccess" } } /** @@ -1321,6 +1441,8 @@ class SuperAccess extends InstanceAccess, @superaccess { then result = this.getQualifier() + ".super" else result = "super" } + + override string getAPrimaryQlClass() { result = "SuperAccess" } } /** @@ -1380,6 +1502,8 @@ class VarAccess extends Expr, @varaccess { // the qualifier is either `super` or `A.super`, where `A` is the enclosing type. getQualifier().(InstanceAccess).isOwnInstanceAccess() } + + override string getAPrimaryQlClass() { result = "VarAccess" } } /** @@ -1472,6 +1596,8 @@ class MethodAccess extends Expr, Call, @methodaccess { * `t`-qualified `this` or `super`. */ predicate isEnclosingMethodAccess(RefType t) { Qualifier::enclosingMemberAccess(this, t) } + + override string getAPrimaryQlClass() { result = "MethodAccess" } } /** A type access is a (possibly qualified) reference to a type. */ @@ -1503,6 +1629,8 @@ class TypeAccess extends Expr, Annotatable, @typeaccess { or not this.hasQualifier() and result = this.getType().toString() } + + override string getAPrimaryQlClass() { result = "TypeAccess" } } /** An array type access is a type access of the form `String[]`. */ @@ -1518,6 +1646,8 @@ class ArrayTypeAccess extends Expr, @arraytypeaccess { /** Gets a printable representation of this expression. */ override string toString() { result = "...[]" } + + override string getAPrimaryQlClass() { result = "ArrayTypeAccss" } } /** @@ -1531,6 +1661,8 @@ class UnionTypeAccess extends Expr, @uniontypeaccess { /** Gets a printable representation of this expression. */ override string toString() { result = "...|..." } + + override string getAPrimaryQlClass() { result = "UnionTypeAccess" } } /** @@ -1567,12 +1699,16 @@ class IntersectionTypeAccess extends Expr, @intersectiontypeaccess { /** Gets a printable representation of this expression. */ override string toString() { result = "...&..." } + + override string getAPrimaryQlClass() { result = "IntersectionTypeAccess" } } /** A package access. */ class PackageAccess extends Expr, @packageaccess { /** Gets a printable representation of this expression. */ override string toString() { result = "package" } + + override string getAPrimaryQlClass() { result = "PackageAccess" } } /** A wildcard type access, which may have either a lower or an upper bound. */ @@ -1588,6 +1724,8 @@ class WildcardTypeAccess extends Expr, @wildcardtypeaccess { /** Gets a printable representation of this expression. */ override string toString() { result = "? ..." } + + override string getAPrimaryQlClass() { result = "WildcardTypeAccess" } } /** diff --git a/java/ql/src/semmle/code/java/Import.qll b/java/ql/src/semmle/code/java/Import.qll index 81823e7e2ac..8f00e52245f 100755 --- a/java/ql/src/semmle/code/java/Import.qll +++ b/java/ql/src/semmle/code/java/Import.qll @@ -28,6 +28,8 @@ class ImportType extends Import { RefType getImportedType() { imports(this, result, _, _) } override string toString() { result = "import " + this.getImportedType().toString() } + + override string getAPrimaryQlClass() { result = "ImportType" } } /** @@ -48,6 +50,8 @@ class ImportOnDemandFromType extends Import { NestedType getAnImport() { result.getEnclosingType() = this.getTypeHoldingImport() } override string toString() { result = "import " + this.getTypeHoldingImport().toString() + ".*" } + + override string getAPrimaryQlClass() { result = "ImportOnDemandFromTypw" } } /** @@ -69,6 +73,8 @@ class ImportOnDemandFromPackage extends Import { override string toString() { result = "import " + this.getPackageHoldingImport().toString() + ".*" } + + override string getAPrimaryQlClass() { result = "ImportOnDemandFromPackage" } } /** @@ -96,6 +102,8 @@ class ImportStaticOnDemand extends Import { override string toString() { result = "import static " + this.getTypeHoldingImport().toString() + ".*" } + + override string getAPrimaryQlClass() { result = "ImportStaticOnDemand" } } /** @@ -135,4 +143,6 @@ class ImportStaticTypeMember extends Import { override string toString() { result = "import static " + this.getTypeHoldingImport().toString() + "." + this.getName() } + + override string getAPrimaryQlClass() { result = "ImportStaticTypeMember" } } diff --git a/java/ql/src/semmle/code/java/Member.qll b/java/ql/src/semmle/code/java/Member.qll index a5fa37b7e1f..2808562cc08 100755 --- a/java/ql/src/semmle/code/java/Member.qll +++ b/java/ql/src/semmle/code/java/Member.qll @@ -411,6 +411,8 @@ class Method extends Callable, @method { not isFinal() and not getDeclaringType().isFinal() } + + override string getAPrimaryQlClass() { result = "Method" } } /** A method that is the same as its source declaration. */ @@ -504,6 +506,8 @@ class Constructor extends Callable, @constructor { override Constructor getSourceDeclaration() { constrs(this, _, _, _, _, result) } override string getSignature() { constrs(this, _, result, _, _, _) } + + override string getAPrimaryQlClass() { result = "Constructor" } } /** @@ -548,6 +552,8 @@ class FieldDeclaration extends ExprParent, @fielddecl, Annotatable { then result = this.getTypeAccess() + " " + this.getField(0) + ";" else result = this.getTypeAccess() + " " + this.getField(0) + ", ...;" } + + override string getAPrimaryQlClass() { result = "FieldDeclaration" } } /** A class or instance field. */ diff --git a/java/ql/src/semmle/code/java/PrintAst.qll b/java/ql/src/semmle/code/java/PrintAst.qll new file mode 100644 index 00000000000..3e36af7dd67 --- /dev/null +++ b/java/ql/src/semmle/code/java/PrintAst.qll @@ -0,0 +1,432 @@ +/** + * Provides queries to pretty-print a Java AST as a graph. + * + * By default, this will print the AST for all elements in the database. To change this behavior, + * extend `PrintAstConfiguration` and override `shouldPrint` to hold for only the elements + * you wish to view the AST for. + */ + +import java + +private newtype TPrintAstConfiguration = MkPrintAstConfiguration() + +/** + * The query can extend this class to control which elements are printed. + */ +class PrintAstConfiguration extends TPrintAstConfiguration { + /** + * Gets a textual representation of this `PrintAstConfiguration`. + */ + string toString() { result = "PrintAstConfiguration" } + + /** + * Controls whether the `Element` should be considered for AST printing. + * By default it checks whether the `Element` `e` belongs to `Location` `l`. + */ + predicate shouldPrint(Element e, Location l) { e.fromSource() and l = e.getLocation() } +} + +private predicate shouldPrint(Element e, Location l) { + exists(PrintAstConfiguration config | config.shouldPrint(e, l)) +} + +private class ExprOrStmt extends Element { + ExprOrStmt() { this instanceof Expr or this instanceof Stmt } + + ExprOrStmt getParent() { + result = this.(Expr).getParent() + or + result = this.(Stmt).getParent() + } + + Callable getEnclosingCallable() { + result = this.(Expr).getEnclosingCallable() + or + result = this.(Stmt).getEnclosingCallable() + } +} + +/** Holds if the given element does not need to be rendered in the AST, due to being compiler-generated. */ +private predicate isNotNeeded(Element el) { + exists(InitializerMethod im | + el = im + or + exists(ExprOrStmt e | e = el | + e.getEnclosingCallable() = im and + not e.getParent*() = any(Field f).getInitializer() and + not isInitBlock(im.getDeclaringType(), e.getParent*()) + ) + ) +} + +/** + * Retrieves the canonical QL class(es) for entity `el` + */ +private string getQlClass(Element el) { + result = "[" + concat(el.getAPrimaryQlClass(), ",") + "] " + // Alternative implementation -- do not delete. It is useful for QL class discovery. + // result = "[" + concat(el.getAQlClass(), ",") + "] " +} + +private predicate locationSortKeys(Element ast, string file, int line, int column) { + exists(Location loc | + loc = ast.getLocation() and + file = loc.getFile().toString() and + line = loc.getStartLine() and + column = loc.getStartColumn() + ) + or + not exists(ast.getLocation()) and + file = "" and + line = 0 and + column = 0 +} + +/** + * Printed AST nodes are mostly `Element`s of the underlying AST. + */ +private newtype TPrintAstNode = + TElementNode(Element el) { shouldPrint(el, _) } or + TAnnotationsNode(Annotatable ann) { shouldPrint(ann, _) and ann.hasAnnotation() } or + TParametersNode(Callable c) { shouldPrint(c, _) and not c.hasNoParameters() } or + TBaseTypesNode(ClassOrInterface ty) { shouldPrint(ty, _) } + +/** + * A node in the output tree. + */ +class PrintAstNode extends TPrintAstNode { + /** + * Gets a textual representation of this node in the PrintAst output tree. + */ + string toString() { none() } + + /** + * Gets the child node at index `childIndex`. Child indices must be unique, + * but need not be contiguous. + */ + PrintAstNode getChild(int childIndex) { none() } + + /** + * Gets a child of this node. + */ + final PrintAstNode getAChild() { result = getChild(_) } + + /** + * Gets the parent of this node, if any. + */ + final PrintAstNode getParent() { result.getAChild() = this } + + /** + * Gets the location of this node in the source code. + */ + Location getLocation() { none() } + + /** + * Gets the value of the property of this node, where the name of the property + * is `key`. + */ + string getProperty(string key) { + key = "semmle.label" and + result = toString() + } + + /** + * Gets the label for the edge from this node to the specified child. By + * default, this is just the index of the child, but subclasses can override + * this. + */ + string getChildEdgeLabel(int childIndex) { + exists(getChild(childIndex)) and + result = childIndex.toString() + } +} + +/** A top-level AST node. */ +class TopLevelPrintAstNode extends PrintAstNode { + TopLevelPrintAstNode() { not exists(this.getParent()) } + + private int getOrder() { + this = + rank[result](TopLevelPrintAstNode n, Location l | + l = n.getLocation() + | + n + order by + l.getFile().getRelativePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(), + l.getEndColumn() + ) + } + + override string getProperty(string key) { + result = super.getProperty(key) + or + key = "semmle.order" and + result = this.getOrder().toString() + } +} + +/** + * A node representing an AST node with an underlying `Element`. + */ +abstract class ElementNode extends PrintAstNode, TElementNode { + Element element; + + ElementNode() { this = TElementNode(element) and not isNotNeeded(element) } + + override string toString() { result = getQlClass(element) + element.toString() } + + override Location getLocation() { result = element.getLocation() } + + /** + * Gets the `Element` represented by this node. + */ + final Element getElement() { result = element } +} + +/** + * An node representing an `Expr` or a `Stmt`. + */ +final class ExprStmtNode extends ElementNode { + ExprStmtNode() { element instanceof ExprOrStmt } + + override PrintAstNode getChild(int childIndex) { + exists(Element el | result.(ElementNode).getElement() = el | + el.(Expr).isNthChildOf(element, childIndex) + or + el.(Stmt).isNthChildOf(element, childIndex) + or + childIndex = -4 and + el = element.(ClassInstanceExpr).getAnonymousClass() + or + childIndex = 0 and + el = element.(LocalClassDeclStmt).getLocalClass() + ) + or + childIndex = -2 and + result.(AnnotationsNode).getAnnotated() = element.(LocalVariableDeclExpr).getVariable() + } +} + +/** + * A node representing a `Callable`, such as method declaration. + */ +final class CallableNode extends ElementNode { + Callable callable; + + CallableNode() { callable = element } + + override PrintAstNode getChild(int childIndex) { + // TODO: javadoc + childIndex = 0 and + result.(AnnotationsNode).getAnnotated() = callable + or + childIndex = 1 and + result.(ElementNode).getElement().(Expr).isNthChildOf(callable, -1) // return type + or + childIndex = 2 and + result.(ParametersNode).getCallable() = callable + or + childIndex = 3 and + result.(ElementNode).getElement() = callable.getBody() + } +} + +/** + * A node representing a `Parameter` of a `Callable`. + */ +final class ParameterNode extends ElementNode { + Parameter p; + + ParameterNode() { p = element } + + override PrintAstNode getChild(int childIndex) { + childIndex = -1 and + result.(AnnotationsNode).getAnnotated() = p + or + childIndex = 0 and + result.(ElementNode).getElement().(Expr).getParent() = p + } +} + +private predicate isInitBlock(Class c, Block b) { + exists(InitializerMethod m | b.getParent() = m.getBody() and m.getDeclaringType() = c) +} + +/** + * A node representing a `Class` or an `Interface`. + */ +final class ClassInterfaceNode extends ElementNode { + ClassOrInterface ty; + + ClassInterfaceNode() { ty = element } + + private Element getADeclaration() { + result.(Callable).getDeclaringType() = ty + or + result.(FieldDeclaration).getAField().getDeclaringType() = ty + or + result.(NestedType).getEnclosingType().getSourceDeclaration() = ty and + not result instanceof AnonymousClass and + not result instanceof LocalClass + or + isInitBlock(ty, result) + } + + override PrintAstNode getChild(int childIndex) { + // TODO: generic params, javadoc? + childIndex = -2 and + result.(AnnotationsNode).getAnnotated() = ty + or + childIndex = -1 and + result.(BaseTypesNode).getClassOrInterface() = ty + or + childIndex >= 0 and + result.(ElementNode).getElement() = + rank[childIndex](Element e, string file, int line, int column | + e = getADeclaration() and locationSortKeys(e, file, line, column) + | + e order by file, line, column + ) + } +} + +/** + * A node representing a `FieldDeclaration`. + */ +final class FieldDeclNode extends ElementNode { + FieldDeclaration decl; + + FieldDeclNode() { decl = element } + + override PrintAstNode getChild(int childIndex) { + childIndex = -2 and + result.(AnnotationsNode).getAnnotated() = decl.getAField() + or + childIndex = -1 and + result.(ElementNode).getElement() = decl.getTypeAccess() + or + childIndex >= 0 and + result.(ElementNode).getElement() = decl.getField(childIndex).getInitializer() + } +} + +/** + * A node representing a `CompilationUnit`. + */ +final class CompilationUnitNode extends ElementNode { + CompilationUnit cu; + + CompilationUnitNode() { cu = element } + + private Element getADeclaration() { + cu.hasChildElement(result) + or + result.(Import).getCompilationUnit() = cu + } + + override PrintAstNode getChild(int childIndex) { + result.(ElementNode).getElement() = + rank[childIndex](Element e, string file, int line, int column | + e = getADeclaration() and locationSortKeys(e, file, line, column) + | + e order by file, line, column + ) + } +} + +/** + * A node representing an `Import`. + */ +final class ImportNode extends ElementNode { + ImportNode() { element instanceof Import } +} + +/** + * A node representing the annotations of an `Annotatable`. + * Only rendered if there is at least one annotation. + */ +final class AnnotationsNode extends PrintAstNode, TAnnotationsNode { + Annotatable ann; + + AnnotationsNode() { this = TAnnotationsNode(ann) } + + override string toString() { result = "(Annotations)" } + + override Location getLocation() { result = ann.getLocation() } + + override ElementNode getChild(int childIndex) { + result.getElement() = + rank[childIndex](Element e, string file, int line, int column | + e = ann.getAnAnnotation() and locationSortKeys(e, file, line, column) + | + e order by file, line, column + ) + } + + /** + * Gets the underlying `Annotatable`. + */ + Annotatable getAnnotated() { result = ann } +} + +/** + * A node representing the parameters of a `Callable`. + * Only rendered if there is at least one parameter. + */ +final class ParametersNode extends PrintAstNode, TParametersNode { + Callable c; + + ParametersNode() { this = TParametersNode(c) } + + override string toString() { result = "(Parameters)" } + + override Location getLocation() { result = c.getLocation() } + + override ElementNode getChild(int childIndex) { result.getElement() = c.getParameter(childIndex) } + + /** + * Gets the underlying `Callable`. + */ + Callable getCallable() { result = c } +} + +/** + * A node representing the base types of a `Class` or `Interface` that it extends or implements. + * Only redered if there is at least one such type. + */ +final class BaseTypesNode extends PrintAstNode, TBaseTypesNode { + ClassOrInterface ty; + + BaseTypesNode() { this = TBaseTypesNode(ty) and exists(TypeAccess ta | ta.getParent() = ty) } + + override string toString() { result = "(Base Types)" } + + override ElementNode getChild(int childIndex) { + result.getElement().(TypeAccess).isNthChildOf(ty, -2 - childIndex) + } + + ClassOrInterface getClassOrInterface() { result = ty } +} + +/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */ +query predicate nodes(PrintAstNode node, string key, string value) { value = node.getProperty(key) } + +/** + * Holds if `target` is a child of `source` in the AST, and property `key` of the edge has the + * given `value`. + */ +query predicate edges(PrintAstNode source, PrintAstNode target, string key, string value) { + exists(int childIndex | + target = source.getChild(childIndex) and + ( + key = "semmle.label" and value = source.getChildEdgeLabel(childIndex) + or + key = "semmle.order" and value = childIndex.toString() + ) + ) +} + +/** Holds if property `key` of the graph has the given `value`. */ +query predicate graphProperties(string key, string value) { + key = "semmle.graphKind" and value = "tree" +} diff --git a/java/ql/src/semmle/code/java/Statement.qll b/java/ql/src/semmle/code/java/Statement.qll index fbc86440788..b37cc9c6d1f 100755 --- a/java/ql/src/semmle/code/java/Statement.qll +++ b/java/ql/src/semmle/code/java/Statement.qll @@ -77,7 +77,10 @@ class BlockStmt extends Stmt, @block { override string pp() { result = "{ ... }" } /** This statement's Halstead ID (used to compute Halstead metrics). */ + override string getHalsteadID() { result = "BlockStmt" } + + override string getAPrimaryQlClass() { result = "BlockStmt" } } /** @@ -137,6 +140,8 @@ class IfStmt extends ConditionalStmt, @ifstmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "IfStmt" } + + override string getAPrimaryQlClass() { result = "IfStmt" } } /** A `for` loop. */ @@ -202,6 +207,8 @@ class ForStmt extends ConditionalStmt, @forstmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "ForStmt" } + + override string getAPrimaryQlClass() { result = "ForStmt" } } /** An enhanced `for` loop. (Introduced in Java 5.) */ @@ -220,6 +227,8 @@ class EnhancedForStmt extends Stmt, @enhancedforstmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "EnhancedForStmt" } + + override string getAPrimaryQlClass() { result = "EnhancedForStmt" } } /** A `while` loop. */ @@ -241,6 +250,8 @@ class WhileStmt extends ConditionalStmt, @whilestmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "WhileStmt" } + + override string getAPrimaryQlClass() { result = "WhileStmt" } } /** A `do` loop. */ @@ -262,6 +273,8 @@ class DoStmt extends ConditionalStmt, @dostmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "DoStmt" } + + override string getAPrimaryQlClass() { result = "DoStmt" } } /** @@ -349,6 +362,8 @@ class TryStmt extends Stmt, @trystmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "TryStmt" } + + override string getAPrimaryQlClass() { result = "TryStmt" } } /** A `catch` clause in a `try` statement. */ @@ -378,6 +393,9 @@ class CatchClause extends Stmt, @catchclause { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "CatchClause" } + + + override string getAPrimaryQlClass() { result = "CatchClause" } } /** A `switch` statement. */ @@ -411,6 +429,8 @@ class SwitchStmt extends Stmt, @switchstmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "SwitchStmt" } + + override string getAPrimaryQlClass() { result = "SwitchStmt" } } /** @@ -472,6 +492,8 @@ class ConstCase extends SwitchCase { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "ConstCase" } + + override string getAPrimaryQlClass() { result = "ConstCase" } } /** A `default` case of a `switch` statement */ @@ -483,6 +505,8 @@ class DefaultCase extends SwitchCase { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "DefaultCase" } + + override string getAPrimaryQlClass() { result = "DefaultCase" } } /** A `synchronized` statement. */ @@ -498,6 +522,8 @@ class SynchronizedStmt extends Stmt, @synchronizedstmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "SynchronizedStmt" } + + override string getAPrimaryQlClass() { result = "SynchonizedStmt" } } /** A `return` statement. */ @@ -510,6 +536,8 @@ class ReturnStmt extends Stmt, @returnstmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "ReturnStmt" } + + override string getAPrimaryQlClass() { result = "ReturnStmt" } } /** A `throw` statement. */ @@ -552,6 +580,8 @@ class ThrowStmt extends Stmt, @throwstmt { getExpr().getType().(RefType).hasSupertype*(result.getVariable().getType().(RefType)) and not this.getEnclosingStmt+() = result } + + override string getAPrimaryQlClass() { result = "ThrowStmt" } } /** A `break`, `yield` or `continue` statement. */ @@ -617,6 +647,8 @@ class BreakStmt extends Stmt, @breakstmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "BreakStmt" } + + override string getAPrimaryQlClass() { result = "BreakStmt" } } /** @@ -631,6 +663,8 @@ class YieldStmt extends Stmt, @yieldstmt { override string pp() { result = "yield ..." } override string getHalsteadID() { result = "YieldStmt" } + + override string getAPrimaryQlClass() { result = "YieldStmt" } } /** A `continue` statement. */ @@ -648,6 +682,8 @@ class ContinueStmt extends Stmt, @continuestmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "ContinueStmt" } + + override string getAPrimaryQlClass() { result = "ContinusStmt" } } /** The empty statement. */ @@ -657,6 +693,8 @@ class EmptyStmt extends Stmt, @emptystmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "EmptyStmt" } + + override string getAPrimaryQlClass() { result = "EmptyStmt" } } /** @@ -685,6 +723,8 @@ class ExprStmt extends Stmt, @exprstmt { fdl.getStartColumn() = sl.getStartColumn() ) } + + override string getAPrimaryQlClass() { result = "ExprStmt" } } /** A labeled statement. */ @@ -700,6 +740,8 @@ class LabeledStmt extends Stmt, @labeledstmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = this.getLabel() + ":" } + + override string getAPrimaryQlClass() { result = "LabelStmt" } } /** An `assert` statement. */ @@ -717,6 +759,8 @@ class AssertStmt extends Stmt, @assertstmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "AssertStmt" } + + override string getAPrimaryQlClass() { result = "AssertStmt" } } /** A statement that declares one or more local variables. */ @@ -738,6 +782,8 @@ class LocalVariableDeclStmt extends Stmt, @localvariabledeclstmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "LocalVariableDeclStmt" } + + override string getAPrimaryQlClass() { result = "LocalVariableDeclStmt" } } /** A statement that declares a local class. */ @@ -750,6 +796,8 @@ class LocalClassDeclStmt extends Stmt, @localclassdeclstmt { /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "LocalClassDeclStmt" } + + override string getAPrimaryQlClass() { result = "LocalClassDeclStmt" } } /** An explicit `this(...)` constructor invocation. */ @@ -791,6 +839,8 @@ class ThisConstructorInvocationStmt extends Stmt, ConstructorCall, @constructori /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "ConstructorInvocationStmt" } + + override string getAPrimaryQlClass() { result = "ThisConstructorInvocationStmt" } } /** An explicit `super(...)` constructor invocation. */ @@ -833,4 +883,6 @@ class SuperConstructorInvocationStmt extends Stmt, ConstructorCall, @superconstr /** This statement's Halstead ID (used to compute Halstead metrics). */ override string getHalsteadID() { result = "SuperConstructorInvocationStmt" } + + override string getAPrimaryQlClass() { result = "SuperConstructorInvocationStmt" } } diff --git a/java/ql/src/semmle/code/java/Type.qll b/java/ql/src/semmle/code/java/Type.qll index 4f29ea94bd7..f4250ef425e 100755 --- a/java/ql/src/semmle/code/java/Type.qll +++ b/java/ql/src/semmle/code/java/Type.qll @@ -613,6 +613,8 @@ class Class extends RefType, @class { result = this.getASupertype().(Class).getAnAnnotation() ) } + + override string getAPrimaryQlClass() { result = "Class" } } /** @@ -696,6 +698,8 @@ class AnonymousClass extends NestedClass { * the string `""` as a placeholder. */ override string getQualifiedName() { result = "" } + + override string getAPrimaryQlClass() { result = "AnonymousClass" } } /** A local class. */ @@ -704,6 +708,8 @@ class LocalClass extends NestedClass { /** Gets the statement that declares this local class. */ LocalClassDeclStmt getLocalClassDeclStmt() { isLocalClass(this, result) } + + override string getAPrimaryQlClass() { result = "LocalClass" } } /** A top-level type. */ @@ -807,6 +813,8 @@ class Interface extends RefType, @interface { // JLS 9.1.1.1: "Every interface is implicitly abstract" any() } + + override string getAPrimaryQlClass() { result = "Interface" } } /** A class or interface. */ diff --git a/java/ql/src/semmle/code/java/Variable.qll b/java/ql/src/semmle/code/java/Variable.qll index 44c7e617d3f..e038f98beaf 100755 --- a/java/ql/src/semmle/code/java/Variable.qll +++ b/java/ql/src/semmle/code/java/Variable.qll @@ -96,4 +96,6 @@ class Parameter extends Element, @param, LocalScopeVariable { call.getCallee().getSourceDeclaration().getAParameter() = this ) } + + override string getAPrimaryQlClass() { result = "Parameter" } }