From 62a6a2479b645ec4ff28fcdde955bebbd985f5be Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Mon, 10 Jun 2024 11:33:09 +0100 Subject: [PATCH] Types --- .../buildless/{ast_sig.qll => ASTSig.qll} | 2 + .../{compiled_ast.qll => CompiledAST.qll} | 92 +++++++++++-------- cpp/ql/lib/experimental/buildless/Model.qll | 87 ++++++++++++------ cpp/ql/lib/experimental/buildless/ast.qll | 8 +- cpp/ql/lib/experimental/buildless/checks.ql | 3 + 5 files changed, 127 insertions(+), 65 deletions(-) rename cpp/ql/lib/experimental/buildless/{ast_sig.qll => ASTSig.qll} (95%) rename cpp/ql/lib/experimental/buildless/{compiled_ast.qll => CompiledAST.qll} (76%) create mode 100644 cpp/ql/lib/experimental/buildless/checks.ql diff --git a/cpp/ql/lib/experimental/buildless/ast_sig.qll b/cpp/ql/lib/experimental/buildless/ASTSig.qll similarity index 95% rename from cpp/ql/lib/experimental/buildless/ast_sig.qll rename to cpp/ql/lib/experimental/buildless/ASTSig.qll index e11dc567f90..2db3055b6e8 100644 --- a/cpp/ql/lib/experimental/buildless/ast_sig.qll +++ b/cpp/ql/lib/experimental/buildless/ASTSig.qll @@ -26,6 +26,7 @@ signature module BuildlessASTSig predicate functionReturn(Node fn, Node returnType); predicate functionName(Node fn, string name); predicate functionParameter(Node fn, int i, Node parameterDecl); + predicate functionDefinition(Node fn); // If a definition as opposed to a declaration // Statements predicate stmt(Node node); @@ -51,6 +52,7 @@ signature module BuildlessASTSig predicate arrayType(Node type, Node element); predicate typename(Node node, string name); // Any named type, including built-in types predicate templated(Node node); + predicate typeDefinition(Node node); // If a definition as opposed to a declaration predicate classOrStructDefinition(Node node); predicate classMember(Node classOrStruct, int child, Node member); diff --git a/cpp/ql/lib/experimental/buildless/compiled_ast.qll b/cpp/ql/lib/experimental/buildless/CompiledAST.qll similarity index 76% rename from cpp/ql/lib/experimental/buildless/compiled_ast.qll rename to cpp/ql/lib/experimental/buildless/CompiledAST.qll index 5f88390256f..a283638f658 100644 --- a/cpp/ql/lib/experimental/buildless/compiled_ast.qll +++ b/cpp/ql/lib/experimental/buildless/CompiledAST.qll @@ -1,5 +1,5 @@ import cpp -import ast_sig +import ASTSig module CompiledAST implements BuildlessASTSig { private class SourceLocation extends Location { @@ -13,20 +13,24 @@ module CompiledAST implements BuildlessASTSig { result = reachableType(type).(ReferenceType).getBaseType() } + private class SourceDeclEntry extends DeclarationEntry { + SourceDeclEntry() { + not this.isAffectedByMacro() and + not this.isFromTemplateInstantiation(_) and + not this.isInMacroExpansion() and + not this.getDeclaration().isInMacroExpansion() and + this.getLocation() instanceof SourceLocation and + not this.(FunctionDeclarationEntry).getDeclaration().isCompilerGenerated() + } + } + private newtype TNode = // TFunction(SourceLocation loc) { exists(Function f | f.getLocation() = loc) } or TStatement(SourceLocation loc) { exists(Stmt s | s.getLocation() = loc) } or - TDeclaration(SourceLocation loc) { exists(DeclarationEntry decl | decl.getLocation() = loc) } or + TDeclaration(SourceDeclEntry decl) or TExpression(SourceLocation loc) { exists(Expr e | e.getLocation() = loc) } or TFunctionCallName(SourceLocation loc) { exists(FunctionCall c | c.getLocation() = loc) } or - TDeclarationType(SourceLocation loc, Type type) { - // TODO: Avoid template instantiation here - exists(DeclarationEntry decl | - decl.getLocation() = loc and not decl.isFromTemplateInstantiation(_) - | - type = reachableType(decl.getType()) - ) - } or + TDeclarationType(SourceDeclEntry decl, Type type) { type = reachableType(decl.getType()) } or TNamespaceDeclaration(NamespaceDeclarationEntry ns) { any() } class Node extends TNode { @@ -34,29 +38,28 @@ module CompiledAST implements BuildlessASTSig { SourceLocation getLocation() { this = TStatement(result) or - this = TDeclaration(result) or + result = this.getDeclaration().getLocation() or this = TExpression(result) or this = TFunctionCallName(result) or - this = TDeclarationType(result, _) or + result = this.getVariableDeclaration().getLocation() or result = this.getNamespaceDeclaration().getLocation() } Stmt getStmt() { this = TStatement(result.getLocation()) } - Function getFunction() { this = TDeclaration(result.getLocation()) and not result.isFromTemplateInstantiation(_) and not result.isCompilerGenerated()} - - DeclarationEntry getDeclaration() { - this = TDeclaration(result.getLocation()) and - not result.isFromTemplateInstantiation(_) - and not result.getDeclaration().(Function).isCompilerGenerated() - /* or this = TDeclarationType(result.getLocation(), _) */ + Function getFunction() { + result = this.getDeclaration().getDeclaration() and + not result.isFromTemplateInstantiation(_) and + not result.isCompilerGenerated() } + SourceDeclEntry getDeclaration() { this = TDeclaration(result) } + NamespaceDeclarationEntry getNamespaceDeclaration() { this = TNamespaceDeclaration(result) } Type getType() { this = TDeclarationType(_, result) } - DeclarationEntry getVariableDeclaration() { this = TDeclarationType(result.getLocation(), _) } + SourceDeclEntry getVariableDeclaration() { this = TDeclarationType(result, _) } Expr getExpr() { this = TExpression(result.getLocation()) } @@ -124,16 +127,16 @@ module CompiledAST implements BuildlessASTSig { // etc // Types predicate ptrType(Node node, Node element) { - exists(PointerType type, Location loc | - node = TDeclarationType(loc, type) and - element = TDeclarationType(loc, type.getBaseType()) + exists(PointerType type, SourceDeclEntry e | + node = TDeclarationType(e, type) and + element = TDeclarationType(e, type.getBaseType()) ) } predicate refType(Node node, Node element) { - exists(ReferenceType type, Location loc | - node = TDeclarationType(loc, type) and - element = TDeclarationType(loc, type.getBaseType()) + exists(ReferenceType type, SourceDeclEntry e | + node = TDeclarationType(e, type) and + element = TDeclarationType(e, type.getBaseType()) ) } @@ -159,6 +162,7 @@ module CompiledAST implements BuildlessASTSig { member.getDeclaration().getDeclaration() and child = 0 and classOrStruct.getLocation().getFile() = member.getLocation().getFile() // TODO: Disambiguate + // and not member.getDeclaration().getDeclaration() instanceof FriendDecl } // Templates @@ -234,9 +238,9 @@ module CompiledAST implements BuildlessASTSig { predicate type(Node node) { node = TDeclarationType(_, _) } predicate constType(Node node, Node element) { - exists(SpecifiedType type, Location loc | - node = TDeclarationType(loc, type) and - element = TDeclarationType(loc, type.getBaseType()) and + exists(SpecifiedType type, SourceDeclEntry e | + node = TDeclarationType(e, type) and + element = TDeclarationType(e, type.getBaseType()) and type.isConst() ) } @@ -247,15 +251,25 @@ module CompiledAST implements BuildlessASTSig { ns.getNamespaceDeclaration().getNamespace().getName() = name } + pragma[nomagic] + private predicate namespaceNamespace(Node ns, Node child) { + ns.getNamespaceDeclaration().getNamespace().getAChildNamespace() = + child.getNamespaceDeclaration().getNamespace() and + ns.getLocation().getFile() = child.getLocation().getFile() + } + + pragma[nomagic] + private predicate namespaceDecl(Node ns, Node child) { + child.getDeclaration().getDeclaration() = + ns.getNamespaceDeclaration().getNamespace().getADeclaration() + and + ns.getLocation().getFile() = child.getLocation().getFile() and + ns.getLocation().getStartLine() <= child.getLocation().getStartLine() + } + predicate namespaceMember(Node ns, Node member) { - ( - member.getDeclaration().getDeclaration() = - ns.getNamespaceDeclaration().getNamespace().getADeclaration() - or - ns.getNamespaceDeclaration().getNamespace().getAChildNamespace() = - member.getNamespaceDeclaration().getNamespace() - ) and - ns.getLocation().getFile() = member.getLocation().getFile() + namespaceNamespace(ns, member) or + namespaceDecl(ns, member) } predicate edge(Node parent, int index, Node child) { @@ -265,4 +279,8 @@ module CompiledAST implements BuildlessASTSig { or blockMember(parent, index, child) } + + predicate typeDefinition(Node node) { node.getDeclaration().(TypeDeclarationEntry).isDefinition() } + + predicate functionDefinition(Node fn) { fn.getDeclaration().(FunctionDeclarationEntry).isDefinition() } } diff --git a/cpp/ql/lib/experimental/buildless/Model.qll b/cpp/ql/lib/experimental/buildless/Model.qll index c7184e55b6d..eb387525e82 100644 --- a/cpp/ql/lib/experimental/buildless/Model.qll +++ b/cpp/ql/lib/experimental/buildless/Model.qll @@ -1,4 +1,4 @@ -import ast +import AST module BuildlessModel { module AST = BuildlessAST; @@ -13,7 +13,18 @@ module BuildlessModel { private newtype TElement = TNamespace(string fqn) { fqn = getQualifiedName(_) } or - TASTNode(AST::SourceElement node) { any() } + TASTNode(AST::SourceElement node) { any() } + + class Type extends string + { + Type() { exists(SourceTypeDeclaration t | t.getMangledName() = this) } + + // string toString() { result = "i am a type" } + + SourceTypeDeclaration getADeclaration() { result.getMangledName() = this } + + SourceTypeDefinition getADefinition() { result.getMangledName() = this } + } class Element extends TElement { string toString() { result = "element" } @@ -25,25 +36,31 @@ module BuildlessModel { override string toString() { result = "namespace " + this.getFullyQualifiedName() } } - class SourceElement extends Element, TASTNode - { + class SourceElement extends Element, TASTNode { AST::SourceElement node; SourceElement() { this = TASTNode(node) } + Location getLocation() { result = node.getLocation() } AST::SourceElement getSourceNode() { result = node } } - class SourceDeclaration extends SourceElement, TASTNode - { + class SourceDeclaration extends SourceElement, TASTNode { abstract SourceDeclaration getParent(); + abstract string getName(); + abstract string getMangledName(); + + abstract predicate isDefinition(); + } + + abstract class SourceDefinition extends SourceDeclaration + { } class NamespaceDeclaration extends SourceDeclaration { - AST::SourceNamespace ns; NamespaceDeclaration() { ns = node } @@ -63,6 +80,8 @@ module BuildlessModel { } override string getMangledName() { result = this.getFullyQualifiedName() } + + override predicate isDefinition() { any() } } class SourceTypeDeclaration extends SourceDeclaration { @@ -74,37 +93,42 @@ module BuildlessModel { override string toString() { result = "typename " + def.getName() } - NamespaceDeclaration getParentNamespace() { - result.getSourceNode() = def.getParent() - } + NamespaceDeclaration getParentNamespace() { result.getSourceNode() = def.getParent() } - SourceTypeDeclaration getParentType() { - result.getSourceNode() = def.getParent() - } + SourceTypeDeclaration getParentType() { result.getSourceNode() = def.getParent() } - override SourceDeclaration getParent() { - result = this.getParentNamespace() or result = this.getParentType() + override SourceDeclaration getParent() { + result = this.getParentNamespace() or result = this.getParentType() } override string getName() { result = def.getName() } // Mangled name - override string getMangledName() { - if exists(this.getParent()) then result = this.getParent().getMangledName() + - "." + this.getName() else result = this.getName() } + override string getMangledName() { + if exists(this.getParent()) + then result = this.getParent().getMangledName() + "." + this.getName() + else result = this.getName() + } + + override predicate isDefinition() { def.isDefinition() } } - class SourceFunctionDeclaration extends SourceDeclaration + class SourceTypeDefinition extends SourceTypeDeclaration, SourceDefinition { + SourceTypeDefinition() { this.isDefinition() } + } + + class SourceFunctionDeclaration extends SourceDeclaration { AST::SourceFunction fn; - SourceFunctionDeclaration() { fn=node } + SourceFunctionDeclaration() { fn = node } SourceTypeDeclaration getParentType() { result.getSourceNode() = fn.getParent() } + NamespaceDeclaration getParentNamespace() { result.getSourceNode() = fn.getParent() } - override SourceDeclaration getParent() { - result = this.getParentType() or result = this.getParentNamespace() + override SourceDeclaration getParent() { + result = this.getParentType() or result = this.getParentNamespace() } override string toString() { result = fn.getName() + "()" } @@ -113,10 +137,21 @@ module BuildlessModel { override string getName() { result = fn.getName() } - override string getMangledName() { - if exists(this.getParent()) then result = this.getParent().getMangledName() + - "." + this.getName()+"()" else result = this.getName()+"()" } -} + override string getMangledName() { + if exists(this.getParent()) + then result = this.getParent().getMangledName() + "." + this.getName() + "()" + else result = this.getName() + "()" + } + + override predicate isDefinition() { fn.isDefinition() } + } + + class SourceFunctionDefinition extends SourceFunctionDeclaration, SourceDefinition + { + SourceFunctionDefinition() { this.isDefinition() } + } + + predicate invalidParent(SourceDeclaration decl) { decl.getParent+() = decl } } // For debugging in context diff --git a/cpp/ql/lib/experimental/buildless/ast.qll b/cpp/ql/lib/experimental/buildless/ast.qll index 5d64c9b4fea..bfa9ebea458 100644 --- a/cpp/ql/lib/experimental/buildless/ast.qll +++ b/cpp/ql/lib/experimental/buildless/ast.qll @@ -1,5 +1,5 @@ -import compiled_ast -import ast_sig +import CompiledAST +import ASTSig module BuildlessAST { final class Node = AST::Node; @@ -49,6 +49,8 @@ module BuildlessAST { SourceParameter getParameter(int i) { AST::functionParameter(this, i, result) } SourceType getReturnType() { AST::functionReturn(this, result) } + + predicate isDefinition() { AST::functionDefinition(this) } } // A syntax node that declares a variable (including fields and parameters) @@ -136,6 +138,8 @@ module BuildlessAST { override string toString() { result = this.getName() } SourceElement getAMember() { AST::classMember(this, _, result) } + + predicate isDefinition() { AST::typeDefinition(this) } } // A node that contains a type of some kind diff --git a/cpp/ql/lib/experimental/buildless/checks.ql b/cpp/ql/lib/experimental/buildless/checks.ql new file mode 100644 index 00000000000..6e2bb44920e --- /dev/null +++ b/cpp/ql/lib/experimental/buildless/checks.ql @@ -0,0 +1,3 @@ +import Model + +select 1