Skip to content

C++: Support printing of global and namespace variables in PrintAST #13775

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
category: breaking
---
* The `shouldPrintFunction` predicate from `PrintAstConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
* The `shouldPrintFunction` predicate from `PrintIRConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: majorAnalysis
---
* The `PrintAST` library now also prints global and namespace variables and their initializers.
8 changes: 4 additions & 4 deletions cpp/ql/lib/printAst.ql
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ external string selectedSourceFile();

class Cfg extends PrintAstConfiguration {
/**
* Holds if the AST for `func` should be printed.
* Print All functions from the selected file.
* Holds if the AST for `decl` should be printed.
* Print All declarations from the selected file.
*/
override predicate shouldPrintFunction(Function func) {
func.getFile() = getFileBySourceArchiveName(selectedSourceFile())
override predicate shouldPrintDeclaration(Declaration decl) {
decl.getFile() = getFileBySourceArchiveName(selectedSourceFile())
}
}
6 changes: 2 additions & 4 deletions cpp/ql/lib/semmle/code/cpp/Print.qll
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ private import PrintAST
* that requests that function, or no `PrintASTConfiguration` exists.
*/
private predicate shouldPrintDeclaration(Declaration decl) {
not decl instanceof Function
not (decl instanceof Function or decl instanceof GlobalOrNamespaceVariable)
or
not exists(PrintAstConfiguration c)
or
exists(PrintAstConfiguration config | config.shouldPrintFunction(decl))
exists(PrintAstConfiguration config | config.shouldPrintDeclaration(decl))
}

/**
Expand Down
6 changes: 3 additions & 3 deletions cpp/ql/lib/semmle/code/cpp/PrintAST.ql
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import cpp
import PrintAST

/**
* Temporarily tweak this class or make a copy to control which functions are
* Temporarily tweak this class or make a copy to control which declarations are
* printed.
*/
class Cfg extends PrintAstConfiguration {
/**
* TWEAK THIS PREDICATE AS NEEDED.
* Holds if the AST for `func` should be printed.
* Holds if the AST for `decl` should be printed.
*/
override predicate shouldPrintFunction(Function func) { any() }
override predicate shouldPrintDeclaration(Declaration decl) { any() }
}
123 changes: 75 additions & 48 deletions cpp/ql/lib/semmle/code/cpp/PrintAST.qll
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/**
* Provides queries to pretty-print a C++ AST as a graph.
*
* By default, this will print the AST for all functions in the database. To change this behavior,
* extend `PrintASTConfiguration` and override `shouldPrintFunction` to hold for only the functions
* you wish to view the AST for.
* By default, this will print the AST for all functions and global and namespace variables in
* the database. To change this behavior, extend `PrintASTConfiguration` and override
* `shouldPrintDeclaration` to hold for only the declarations you wish to view the AST for.
*/

import cpp
Expand All @@ -12,7 +12,7 @@ private import semmle.code.cpp.Print
private newtype TPrintAstConfiguration = MkPrintAstConfiguration()

/**
* The query can extend this class to control which functions are printed.
* The query can extend this class to control which declarations are printed.
*/
class PrintAstConfiguration extends TPrintAstConfiguration {
/**
Expand All @@ -21,14 +21,16 @@ class PrintAstConfiguration extends TPrintAstConfiguration {
string toString() { result = "PrintASTConfiguration" }

/**
* Holds if the AST for `func` should be printed. By default, holds for all
* functions.
* Holds if the AST for `decl` should be printed. By default, holds for all
* functions and global and namespace variables. Currently, does not support any
* other declaration types.
*/
predicate shouldPrintFunction(Function func) { any() }
predicate shouldPrintDeclaration(Declaration decl) { any() }
}

private predicate shouldPrintFunction(Function func) {
exists(PrintAstConfiguration config | config.shouldPrintFunction(func))
private predicate shouldPrintDeclaration(Declaration decl) {
exists(PrintAstConfiguration config | config.shouldPrintDeclaration(decl)) and
(decl instanceof Function or decl instanceof GlobalOrNamespaceVariable)
}

bindingset[s]
Expand Down Expand Up @@ -69,7 +71,7 @@ private predicate locationSortKeys(Locatable ast, string file, int line, int col
)
}

private Function getEnclosingFunction(Locatable ast) {
private Declaration getEnclosingDeclaration(Locatable ast) {
result = ast.(Expr).getEnclosingFunction()
or
result = ast.(Stmt).getEnclosingFunction()
Expand All @@ -78,6 +80,10 @@ private Function getEnclosingFunction(Locatable ast) {
or
result = ast.(Parameter).getFunction()
or
result = ast.(Expr).getEnclosingDeclaration()
or
result = ast.(Initializer).getDeclaration()
or
result = ast
}

Expand All @@ -86,21 +92,21 @@ private Function getEnclosingFunction(Locatable ast) {
* nodes for things like parameter lists and constructor init lists.
*/
private newtype TPrintAstNode =
TAstNode(Locatable ast) { shouldPrintFunction(getEnclosingFunction(ast)) } or
TAstNode(Locatable ast) { shouldPrintDeclaration(getEnclosingDeclaration(ast)) } or
TDeclarationEntryNode(DeclStmt stmt, DeclarationEntry entry) {
// We create a unique node for each pair of (stmt, entry), to avoid having one node with
// multiple parents due to extractor bug CPP-413.
stmt.getADeclarationEntry() = entry and
shouldPrintFunction(stmt.getEnclosingFunction())
shouldPrintDeclaration(stmt.getEnclosingFunction())
} or
TParametersNode(Function func) { shouldPrintFunction(func) } or
TParametersNode(Function func) { shouldPrintDeclaration(func) } or
TConstructorInitializersNode(Constructor ctor) {
ctor.hasEntryPoint() and
shouldPrintFunction(ctor)
shouldPrintDeclaration(ctor)
} or
TDestructorDestructionsNode(Destructor dtor) {
dtor.hasEntryPoint() and
shouldPrintFunction(dtor)
shouldPrintDeclaration(dtor)
}

/**
Expand Down Expand Up @@ -158,10 +164,10 @@ class PrintAstNode extends TPrintAstNode {

/**
* Holds if this node should be printed in the output. By default, all nodes
* within a function are printed, but the query can override
* `PrintASTConfiguration.shouldPrintFunction` to filter the output.
* within functions and global and namespace variables are printed, but the query
* can override `PrintASTConfiguration.shouldPrintDeclaration` to filter the output.
*/
final predicate shouldPrint() { shouldPrintFunction(this.getEnclosingFunction()) }
final predicate shouldPrint() { shouldPrintDeclaration(this.getEnclosingDeclaration()) }

/**
* Gets the children of this node.
Expand Down Expand Up @@ -229,10 +235,15 @@ class PrintAstNode extends TPrintAstNode {
abstract string getChildAccessorPredicateInternal(int childIndex);

/**
* Gets the `Function` that contains this node.
* Gets the `Declaration` that contains this node.
*/
private Declaration getEnclosingDeclaration() { result = this.getParent*().getDeclaration() }

/**
* Gets the `Declaration` this node represents.
*/
private Function getEnclosingFunction() {
result = this.getParent*().(FunctionNode).getFunction()
private Declaration getDeclaration() {
result = this.(AstNode).getAst() and shouldPrintDeclaration(result)
}
}

Expand Down Expand Up @@ -571,16 +582,53 @@ class DestructorDestructionsNode extends PrintAstNode, TDestructorDestructionsNo
final Destructor getDestructor() { result = dtor }
}

abstract private class FunctionOrGlobalOrNamespaceVariableNode extends AstNode {
override string toString() { result = qlClass(ast) + getIdentityString(ast) }

private int getOrder() {
this =
rank[result](FunctionOrGlobalOrNamespaceVariableNode node, Declaration decl, string file,
int line, int column |
node.getAst() = decl and
locationSortKeys(decl, file, line, column)
|
node order by file, line, column, getIdentityString(decl)
)
}

override string getProperty(string key) {
result = super.getProperty(key)
or
key = "semmle.order" and result = this.getOrder().toString()
}
}

/**
* A node representing a `GlobalOrNamespaceVariable`.
*/
class GlobalOrNamespaceVariableNode extends FunctionOrGlobalOrNamespaceVariableNode {
GlobalOrNamespaceVariable var;

GlobalOrNamespaceVariableNode() { var = ast }

override PrintAstNode getChildInternal(int childIndex) {
childIndex = 0 and
result.(AstNode).getAst() = var.getInitializer()
}

override string getChildAccessorPredicateInternal(int childIndex) {
childIndex = 0 and result = "getInitializer()"
}
}

/**
* A node representing a `Function`.
*/
class FunctionNode extends AstNode {
class FunctionNode extends FunctionOrGlobalOrNamespaceVariableNode {
Function func;

FunctionNode() { func = ast }

override string toString() { result = qlClass(func) + getIdentityString(func) }

override PrintAstNode getChildInternal(int childIndex) {
childIndex = 0 and
result.(ParametersNode).getFunction() = func
Expand All @@ -604,31 +652,10 @@ class FunctionNode extends AstNode {
or
childIndex = 3 and result = "<destructions>"
}

private int getOrder() {
this =
rank[result](FunctionNode node, Function function, string file, int line, int column |
node.getAst() = function and
locationSortKeys(function, file, line, column)
|
node order by file, line, column, getIdentityString(function)
)
}

override string getProperty(string key) {
result = super.getProperty(key)
or
key = "semmle.order" and result = this.getOrder().toString()
}

/**
* Gets the `Function` this node represents.
*/
final Function getFunction() { result = func }
}

private string getChildAccessorWithoutConversions(Locatable parent, Element child) {
shouldPrintFunction(getEnclosingFunction(parent)) and
shouldPrintDeclaration(getEnclosingDeclaration(parent)) and
(
exists(Stmt s | s = parent |
namedStmtChildPredicates(s, child, result)
Expand All @@ -647,7 +674,7 @@ private string getChildAccessorWithoutConversions(Locatable parent, Element chil
}

private predicate namedStmtChildPredicates(Locatable s, Element e, string pred) {
shouldPrintFunction(getEnclosingFunction(s)) and
shouldPrintDeclaration(getEnclosingDeclaration(s)) and
(
exists(int n | s.(BlockStmt).getStmt(n) = e and pred = "getStmt(" + n + ")")
or
Expand Down Expand Up @@ -735,7 +762,7 @@ private predicate namedStmtChildPredicates(Locatable s, Element e, string pred)
}

private predicate namedExprChildPredicates(Expr expr, Element ele, string pred) {
shouldPrintFunction(expr.getEnclosingFunction()) and
shouldPrintDeclaration(expr.getEnclosingDeclaration()) and
(
expr.(Access).getTarget() = ele and pred = "getTarget()"
or
Expand Down
4 changes: 2 additions & 2 deletions cpp/ql/lib/semmle/code/cpp/ir/PrintIR.qll
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* This file contains the actual implementation of `PrintIR.ql`. For test cases and very small
* databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most
* uses, however, it is better to write a query that imports `PrintIR.qll`, extends
* `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to
* dump.
* `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations
* to dump.
*/

import implementation.aliased_ssa.PrintIR
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* This file contains the actual implementation of `PrintIR.ql`. For test cases and very small
* databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most
* uses, however, it is better to write a query that imports `PrintIR.qll`, extends
* `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to
* dump.
* `PrintIRConfiguration`, and overrides `shouldPrintDeclaration()` to select a subset of declarations
* to dump.
*/

private import internal.IRInternal
Expand All @@ -16,30 +16,30 @@ import Imports::IRConfiguration
private newtype TPrintIRConfiguration = MkPrintIRConfiguration()

/**
* The query can extend this class to control which functions are printed.
* The query can extend this class to control which declarations are printed.
*/
class PrintIRConfiguration extends TPrintIRConfiguration {
/** Gets a textual representation of this configuration. */
string toString() { result = "PrintIRConfiguration" }

/**
* Holds if the IR for `func` should be printed. By default, holds for all
* functions.
* functions, global and namespace variables, and static local variables.
*/
predicate shouldPrintFunction(Language::Declaration decl) { any() }
predicate shouldPrintDeclaration(Language::Declaration decl) { any() }
}

/**
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
*/
private class FilteredIRConfiguration extends IRConfiguration {
override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) {
shouldPrintFunction(func)
shouldPrintDeclaration(func)
}
}

private predicate shouldPrintFunction(Language::Declaration decl) {
exists(PrintIRConfiguration config | config.shouldPrintFunction(decl))
private predicate shouldPrintDeclaration(Language::Declaration decl) {
exists(PrintIRConfiguration config | config.shouldPrintDeclaration(decl))
}

private predicate shouldPrintInstruction(Instruction i) {
Expand Down Expand Up @@ -90,10 +90,10 @@ private string getOperandPropertyString(Operand operand) {
}

private newtype TPrintableIRNode =
TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or
TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or
TPrintableIRFunction(IRFunction irFunc) { shouldPrintDeclaration(irFunc.getFunction()) } or
TPrintableIRBlock(IRBlock block) { shouldPrintDeclaration(block.getEnclosingFunction()) } or
TPrintableInstruction(Instruction instr) {
shouldPrintInstruction(instr) and shouldPrintFunction(instr.getEnclosingFunction())
shouldPrintInstruction(instr) and shouldPrintDeclaration(instr.getEnclosingFunction())
}

/**
Expand Down
Loading