diff --git a/ql/consistency-queries/ModuleResolution.ql b/ql/consistency-queries/ModuleResolution.ql index dbf7b0789f0..49347558d8e 100644 --- a/ql/consistency-queries/ModuleResolution.ql +++ b/ql/consistency-queries/ModuleResolution.ql @@ -1 +1 @@ -import codeql_ql.ast.internal.Module::Consistency +import codeql_ql.ast.internal.Module::ModConsistency diff --git a/ql/consistency-queries/TypeResolution.ql b/ql/consistency-queries/TypeResolution.ql new file mode 100644 index 00000000000..f709a28910f --- /dev/null +++ b/ql/consistency-queries/TypeResolution.ql @@ -0,0 +1 @@ +import codeql_ql.ast.internal.Type::TyConsistency diff --git a/ql/src/codeql_ql/ast/Ast.qll b/ql/src/codeql_ql/ast/Ast.qll index 0afedafbced..34cd0f3c552 100644 --- a/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/src/codeql_ql/ast/Ast.qll @@ -1,6 +1,7 @@ import ql private import codeql_ql.ast.internal.AstNodes private import codeql_ql.ast.internal.Module +private import codeql_ql.ast.internal.Type /** An AST node of a QL program */ class AstNode extends TAstNode { @@ -169,16 +170,16 @@ class VarDecl extends TVarDecl, AstNode { result.(Quantifier).getAnArgument() = this } - Type getType() { toGenerated(result) = var.getChild(0) } + TypeExpr getType() { toGenerated(result) = var.getChild(0) } } /** - * A type, such as `DataFlow::Node`. + * A type reference, such as `DataFlow::Node`. */ -class Type extends TType, AstNode { +class TypeExpr extends TType, AstNode { Generated::TypeExpr type; - Type() { this = TType(type) } + TypeExpr() { this = TType(type) } override string getAPrimaryQlClass() { result = "Type" } @@ -221,6 +222,8 @@ class Type extends TType, AstNode { or result.(Class).getUnionMember() = this } + + Type getResolvedType() { resolveTypeExpr(this, result) } } /** @@ -320,19 +323,15 @@ class Class extends TClass, AstNode, ModuleMember { cls.getChild(_).(Generated::ClassMember).getChild(_).(Generated::Field).getChild() } - /** - * Gets a super-type for this class. - * That is: a type after the `extends` keyword. - */ - Type getASuperType() { toGenerated(result) = cls.getChild(_) } + TypeExpr getASuperType() { toGenerated(result) = cls.getChild(_) } /** Gets the type that this class is defined to be an alias of. */ - Type getAliasType() { + TypeExpr getAliasType() { toGenerated(result) = cls.getChild(_).(Generated::TypeAliasBody).getChild() } /** Gets the type of one of the members that this class is defined to be a union of. */ - Type getUnionMember() { + TypeExpr getUnionMember() { toGenerated(result) = cls.getChild(_).(Generated::TypeUnionBody).getChild(_) } } @@ -466,7 +465,9 @@ class InlineCast extends TInlineCast, Expr { override string getAPrimaryQlClass() { result = "InlineCast" } - Type getType() { toGenerated(result) = expr.getChild(_).(Generated::QualifiedRhs).getChild(_) } + TypeExpr getType() { + toGenerated(result) = expr.getChild(_).(Generated::QualifiedRhs).getChild(_) + } Expr getBase() { toGenerated(result) = expr.getChild(0) } } @@ -746,10 +747,10 @@ class InstanceOf extends TInstanceOf, Formula { Expr getExpr() { toGenerated(result) = inst.getChild(0) } /** Gets the reference to the type being checked. */ - Type getType() { toGenerated(result) = inst.getChild(1) } + TypeExpr getType() { toGenerated(result) = inst.getChild(1) } /** Gets the type being checked. */ - //QLType getType() { result = getTypeRef().getType() } + //QLTypeExpr getType() { result = getTypeRef().getType() } override string getAPrimaryQlClass() { result = "InstanceOf" } } diff --git a/ql/src/codeql_ql/ast/internal/Module.qll b/ql/src/codeql_ql/ast/internal/Module.qll index cf87d268e6c..8cb6d9f322b 100644 --- a/ql/src/codeql_ql/ast/internal/Module.qll +++ b/ql/src/codeql_ql/ast/internal/Module.qll @@ -80,7 +80,7 @@ private Module getEnclosingModule0(AstNode n) { AstNodes::toGenerated(result) = parent*(AstNodes::toGenerated(n).getParent()) } -private ContainerOrModule getEnclosingModule(AstNode n) { +ContainerOrModule getEnclosingModule(AstNode n) { result = TModule(getEnclosingModule0(n)) or not exists(getEnclosingModule0(n)) and @@ -209,7 +209,7 @@ private predicate defines( ) } -module Consistency { +module ModConsistency { query predicate noResolve(Import imp) { not resolve(imp, _) and not imp.getLocation().getFile().getAbsolutePath().regexpMatch(".*/(test|examples)/.*") diff --git a/ql/src/codeql_ql/ast/internal/Type.qll b/ql/src/codeql_ql/ast/internal/Type.qll new file mode 100644 index 00000000000..a47f0c6fdd7 --- /dev/null +++ b/ql/src/codeql_ql/ast/internal/Type.qll @@ -0,0 +1,264 @@ +import ql +private import codeql_ql.ast.internal.AstNodes as AstNodes +private import codeql_ql.ast.internal.TreeSitter +private import codeql_ql.ast.internal.Module + +private newtype TType = + TClass(Class c) { isActualClass(c) } or + TNewType(NewType n) or + TNewTypeBranch(NewTypeBranch b) or + TPrimitive(string s) { primTypeName(s) } or + TUnion(Class c) { exists(c.getUnionMember()) } or + TDontCare() or + TClassChar(Class c) { isActualClass(c) } or + TClassDomain(Class c) { isActualClass(c) } or + TDatabase(string s) { exists(TypeExpr t | t.isDBType() and s = t.getClassName()) } + +private predicate primTypeName(string s) { s = ["int", "float", "string", "boolean", "date"] } + +private predicate isActualClass(Class c) { + not exists(c.getAliasType()) and + not exists(c.getUnionMember()) +} + +/** + * A type, such as `int` or `Node`. + */ +class Type extends TType { + string toString() { result = getName() } + + string getName() { result = "???" } + + /** + * Gets a supertype of this type. This follows the user-visible type heirarchy, + * and doesn't include internal types like thecharacteristic and domain types of classes. + */ + Type getASuperType() { none() } + + /** + * Gets a supertype of this type in the internal heirarchy, + * which includes the characteristic and domain types of classes. + */ + Type getAnInternalSuperType() { result = TDontCare() } + + AstNode getDeclaration() { none() } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + if exists(getDeclaration()) + then + getDeclaration() + .getLocation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + else ( + filepath = "" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + ) + } +} + +class ClassType extends Type, TClass { + Class decl; + + ClassType() { this = TClass(decl) } + + override string getName() { result = decl.getName() } + + override Class getDeclaration() { result = decl } + + override Type getASuperType() { result = decl.getASuperType().getResolvedType() } + + override Type getAnInternalSuperType() { + result.(ClassCharType).getClassType() = this + or + result = super.getAnInternalSuperType() + } +} + +class ClassCharType extends Type, TClassChar { + Class decl; + + ClassCharType() { this = TClassChar(decl) } + + override string getName() { exists(string n | n = decl.getName() | result = n + "." + n) } + + override Class getDeclaration() { result = decl } + + ClassType getClassType() { result = TClass(decl) } + + override Type getAnInternalSuperType() { + result.(ClassDomainType).getClassType() = this.getClassType() + } +} + +class ClassDomainType extends Type, TClassDomain { + Class decl; + + ClassDomainType() { this = TClassDomain(decl) } + + override string getName() { result = decl.getName() + ".extends" } + + override Class getDeclaration() { result = decl } + + ClassType getClassType() { result = TClass(decl) } + + override Type getAnInternalSuperType() { result = getClassType().getASuperType() } +} + +class PrimitiveType extends Type, TPrimitive { + string name; + + PrimitiveType() { this = TPrimitive(name) } + + override string getName() { result = name } + + override Type getASuperType() { name = "int" and result.(PrimitiveType).getName() = "float" } + + override Type getAnInternalSuperType() { + result = getASuperType() + or + result = super.getAnInternalSuperType() + } +} + +class DontCareType extends Type, TDontCare { + override string getName() { result = "_" } + + override Type getAnInternalSuperType() { none() } +} + +class NewTypeType extends Type, TNewType { + NewType decl; + + NewTypeType() { this = TNewType(decl) } + + override NewType getDeclaration() { result = decl } + + NewTypeBranchType getABranch() { result = TNewTypeBranch(decl.getABranch()) } + + override string getName() { result = decl.getName() } +} + +class NewTypeBranchType extends Type, TNewTypeBranch { + NewTypeBranch decl; + + NewTypeBranchType() { this = TNewTypeBranch(decl) } + + override NewTypeBranch getDeclaration() { result = decl } + + override string getName() { result = decl.getName() } + + override Type getASuperType() { + result = TNewType(decl.getParent()) + or + result.(UnionType).getUnionMember() = this + } + + override Type getAnInternalSuperType() { + result = getASuperType() + or + result = super.getAnInternalSuperType() + } +} + +class UnionType extends Type, TUnion { + Class decl; + + UnionType() { this = TUnion(decl) } + + override Class getDeclaration() { result = decl } + + override string getName() { result = decl.getName() } + + Type getUnionMember() { result = decl.getUnionMember().getResolvedType() } +} + +class DatabaseType extends Type, TDatabase { + string name; + + DatabaseType() { this = TDatabase(name) } + + override string getName() { result = name } +} + +predicate resolveTypeExpr(TypeExpr te, Type t) { + if te.isDBType() + then t = TDatabase(te.getClassName()) + else + if primTypeName(te.getClassName()) + then t = TPrimitive(te.getClassName()) + else + exists(FileOrModule m, boolean public | qualifier(te, m, public) | + defines(m, te.getClassName(), t, public) + ) +} + +private predicate qualifier(TypeExpr te, FileOrModule m, boolean public) { + if exists(te.getModule()) + then ( + public = true and m = te.getModule().getResolvedModule() + ) else ( + (public = true or public = false) and + m = getEnclosingModule(te).getEnclosing*() + ) +} + +private boolean getPublicBool(ModuleMember m) { + if m.isPrivate() then result = false else result = true +} + +private predicate defines(FileOrModule m, string name, Type t, boolean public) { + exists(Class ty | t = TClass(ty) | + getEnclosingModule(ty) = m and + ty.getName() = name and + public = getPublicBool(ty) + ) + or + exists(NewType ty | t = TNewType(ty) | + getEnclosingModule(ty) = m and + ty.getName() = name and + public = getPublicBool(ty) + ) + or + exists(NewTypeBranch ty | t = TNewTypeBranch(ty) | + getEnclosingModule(ty) = m and + ty.getName() = name and + public = getPublicBool(ty.getParent()) + ) + or + exists(Class ty | t = TUnion(ty) | + getEnclosingModule(ty) = m and + ty.getName() = name and + public = getPublicBool(ty) + ) + or + exists(Class ty | t = ty.getAliasType().getResolvedType() | + getEnclosingModule(ty) = m and + ty.getName() = name and + public = getPublicBool(ty) + ) + or + exists(Import im | + getEnclosingModule(im) = m and + not exists(im.importedAs()) and + public = getPublicBool(im) and + defines(im.getResolvedModule(), name, t, true) + ) +} + +module TyConsistency { + query predicate noResolve(TypeExpr te) { + not resolveTypeExpr(te, _) and + not te.getLocation().getFile().getAbsolutePath().regexpMatch(".*/(test|examples)/.*") + } + + query predicate multipleResolve(TypeExpr te, int c, Type t) { + c = strictcount(Type t0 | resolveTypeExpr(te, t0)) and + c > 1 and + resolveTypeExpr(te, t) + } +} diff --git a/ql/src/ide-contextual-queries/localDefinitions.ql b/ql/src/ide-contextual-queries/localDefinitions.ql index c9c6b279bea..99049df8305 100644 --- a/ql/src/ide-contextual-queries/localDefinitions.ql +++ b/ql/src/ide-contextual-queries/localDefinitions.ql @@ -9,13 +9,53 @@ import ql import codeql_ql.ast.internal.Module +import codeql_ql.ast.internal.Type import codeql.IDEContextual external string selectedSourceFile(); -from ModuleRef ref, FileOrModule target, string kind -where +newtype TLoc = + TAst(AstNode n) or + TFileOrModule(FileOrModule m) + +class Loc extends TLoc { + string toString() { result = "" } + + AstNode asAst() { this = TAst(result) } + + FileOrModule asMod() { this = TFileOrModule(result) } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(AstNode n | this = TAst(n) | + n.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + ) + or + exists(FileOrModule m | this = TFileOrModule(m) | + m.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + ) + } +} + +predicate resolveModule(ModuleRef ref, FileOrModule target, string kind) { target = ref.getResolvedModule() and kind = "module" and ref.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile()) +} + +predicate resolveType(TypeExpr ref, AstNode target, string kind) { + target = ref.getResolvedType().getDeclaration() and + kind = "type" and + ref.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile()) +} + +predicate resolve(Loc ref, Loc target, string kind) { + resolveModule(ref.asAst(), target.asMod(), kind) + or + resolveType(ref.asAst(), target.asAst(), kind) +} + +from Loc ref, Loc target, string kind +where resolve(ref, target, kind) select ref, target, kind diff --git a/ql/src/ide-contextual-queries/localReferences.ql b/ql/src/ide-contextual-queries/localReferences.ql index 572a7d4e7a6..ecaed17ef7d 100644 --- a/ql/src/ide-contextual-queries/localReferences.ql +++ b/ql/src/ide-contextual-queries/localReferences.ql @@ -13,9 +13,48 @@ import codeql.IDEContextual external string selectedSourceFile(); -from ModuleRef ref, FileOrModule target, string kind -where +newtype TLoc = + TAst(AstNode n) or + TFileOrModule(FileOrModule m) + +class Loc extends TLoc { + string toString() { result = "" } + + AstNode asAst() { this = TAst(result) } + + FileOrModule asMod() { this = TFileOrModule(result) } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(AstNode n | this = TAst(n) | + n.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + ) + or + exists(FileOrModule m | this = TFileOrModule(m) | + m.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + ) + } +} + +predicate resolveModule(ModuleRef ref, FileOrModule target, string kind) { target = ref.getResolvedModule() and kind = "module" and ref.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile()) +} + +predicate resolveType(TypeExpr ref, AstNode target, string kind) { + target = ref.getResolvedType().getDeclaration() and + kind = "type" and + ref.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile()) +} + +predicate resolve(Loc ref, Loc target, string kind) { + resolveModule(ref.asAst(), target.asMod(), kind) + or + resolveType(ref.asAst(), target.asAst(), kind) +} + +from Loc ref, Loc target, string kind +where resolve(ref, target, kind) select ref, target, kind