Skip to content

Commit cc16fde

Browse files
authored
Merge pull request #130 from github/erik-krogh/more-types
Better type resolution
2 parents 7214d70 + 631a503 commit cc16fde

File tree

15 files changed

+270
-65
lines changed

15 files changed

+270
-65
lines changed
Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1 @@
1-
import ql
2-
private import codeql_ql.ast.internal.AstNodes as AstNodes
3-
4-
query AstNode nonTotalGetParent() {
5-
exists(AstNodes::toQL(result).getParent()) and
6-
not exists(result.getParent()) and
7-
not result.getLocation().getStartColumn() = 1 and // startcolumn = 1 <=> top level in file <=> fine to have no parent
8-
not result instanceof YAML::YAMLNode and // parents for YAML doens't work
9-
not (result instanceof QLDoc and result.getLocation().getFile().getExtension() = "dbscheme") // qldoc in dbschemes are not hooked up
10-
}
1+
private import codeql_ql.ast.internal.AstNodes::AstConsistency

ql/src/codeql_ql/ast/Ast.qll

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,7 @@ class NewType extends TNewType, TypeDeclaration, ModuleDeclaration {
819819
* A branch in a `newtype`.
820820
* E.g. `Bar()` or `Baz()` in `newtype Foo = Bar() or Baz()`.
821821
*/
822-
class NewTypeBranch extends TNewTypeBranch, PredicateOrBuiltin, TypeDeclaration {
822+
class NewTypeBranch extends TNewTypeBranch, Predicate, TypeDeclaration {
823823
QL::DatatypeBranch branch;
824824

825825
NewTypeBranch() { this = TNewTypeBranch(branch) }
@@ -835,7 +835,7 @@ class NewTypeBranch extends TNewTypeBranch, PredicateOrBuiltin, TypeDeclaration
835835
}
836836

837837
/** Gets the body of this branch. */
838-
Formula getBody() { toQL(result) = branch.getChild(_).(QL::Body).getChild() }
838+
override Formula getBody() { toQL(result) = branch.getChild(_).(QL::Body).getChild() }
839839

840840
override NewTypeBranchType getReturnType() { result.getDeclaration() = this }
841841

@@ -1551,7 +1551,7 @@ class ExprAggregate extends TExprAggregate, Aggregate {
15511551

15521552
override Type getType() {
15531553
exists(PrimitiveType prim | prim = result |
1554-
kind.regexpMatch("(strict)?count|sum|min|max|rank") and
1554+
kind.regexpMatch("(strict)?count") and
15551555
result.getName() = "int"
15561556
or
15571557
kind.regexpMatch("(strict)?concat") and
@@ -1615,16 +1615,16 @@ class FullAggregate extends TFullAggregate, Aggregate {
16151615

16161616
override Type getType() {
16171617
exists(PrimitiveType prim | prim = result |
1618-
kind.regexpMatch("(strict)?(count|sum|min|max|rank)") and
1618+
kind = ["count", "strictcount"] and
16191619
result.getName() = "int"
16201620
or
16211621
kind.regexpMatch("(strict)?concat") and
16221622
result.getName() = "string"
16231623
)
16241624
or
1625-
kind = ["any", "min", "max", "unique"] and
1625+
kind = ["any", "min", "max", "unique", "rank", "sum", "strictsum"] and
16261626
not exists(this.getExpr(_)) and
1627-
result = this.getArgument(0).getTypeExpr().getResolvedType()
1627+
result = this.getArgument(0).getType()
16281628
or
16291629
not kind = ["count", "strictcount"] and
16301630
result = this.getExpr(0).getType()
@@ -1761,6 +1761,10 @@ class Super extends TSuper, Expr {
17611761
Super() { this = TSuper(_) }
17621762

17631763
override string getAPrimaryQlClass() { result = "Super" }
1764+
1765+
override Type getType() {
1766+
exists(MemberCall call | call.getBase() = this | result = call.getTarget().getDeclaringType())
1767+
}
17641768
}
17651769

17661770
/** An access to `result`. */
@@ -1794,6 +1798,7 @@ class Negation extends TNegation, Formula {
17941798

17951799
/** An expression, such as `x+4`. */
17961800
class Expr extends TExpr, AstNode {
1801+
cached
17971802
Type getType() { none() }
17981803
}
17991804

@@ -1874,15 +1879,14 @@ class AddSubExpr extends TAddSubExpr, BinOpExpr {
18741879
result = this.getLeftOperand().getType() and
18751880
result = this.getRightOperand().getType()
18761881
or
1877-
// Both operands are subtypes of `int`
1878-
result.getName() = "int" and
1879-
result = this.getLeftOperand().getType().getASuperType*() and
1880-
result = this.getRightOperand().getType().getASuperType*()
1882+
// Both operands are subtypes of `int` / `string` / `float`
1883+
exprOfPrimitiveAddType(result) = this.getLeftOperand() and
1884+
exprOfPrimitiveAddType(result) = this.getRightOperand()
18811885
or
18821886
// Coercion to from `int` to `float`
18831887
exists(PrimitiveType i | result.getName() = "float" and i.getName() = "int" |
18841888
this.getAnOperand().getType() = result and
1885-
this.getAnOperand().getType().getASuperType*() = i
1889+
this.getAnOperand() = exprOfPrimitiveAddType(i)
18861890
)
18871891
or
18881892
// Coercion to `string`
@@ -1899,6 +1903,25 @@ class AddSubExpr extends TAddSubExpr, BinOpExpr {
18991903
}
19001904
}
19011905

1906+
pragma[noinline]
1907+
private Expr exprOfPrimitiveAddType(PrimitiveType t) {
1908+
result.getType() = getASubTypeOfAddPrimitive(t)
1909+
}
1910+
1911+
/**
1912+
* Gets a subtype of the given primitive type `prim`.
1913+
* This predicate does not consider float to be a supertype of int.
1914+
*/
1915+
private Type getASubTypeOfAddPrimitive(PrimitiveType prim) {
1916+
result = prim and
1917+
result.getName() = ["int", "string", "float"]
1918+
or
1919+
exists(Type superType | superType = getASubTypeOfAddPrimitive(prim) |
1920+
result.getASuperType() = superType and
1921+
not (result.getName() = "int" and superType.getName() = "float")
1922+
)
1923+
}
1924+
19021925
/**
19031926
* An addition expression, such as `x + y`.
19041927
*/
@@ -1939,15 +1962,18 @@ class MulDivModExpr extends TMulDivModExpr, BinOpExpr {
19391962
this.getLeftOperand().getType() = result and
19401963
this.getRightOperand().getType() = result
19411964
or
1942-
// Both operands are subtypes of `int`
1943-
result.getName() = "int" and
1944-
result = this.getLeftOperand().getType().getASuperType*() and
1945-
result = this.getRightOperand().getType().getASuperType*()
1965+
// Both operands are subtypes of `int`/`float`
1966+
result.getName() = ["int", "float"] and
1967+
exprOfPrimitiveAddType(result) = this.getLeftOperand() and
1968+
exprOfPrimitiveAddType(result) = this.getRightOperand()
19461969
or
19471970
// Coercion from `int` to `float`
19481971
exists(PrimitiveType i | result.getName() = "float" and i.getName() = "int" |
1949-
this.getAnOperand().getType() = result and
1950-
this.getAnOperand().getType().getASuperType*() = i
1972+
this.getLeftOperand() = exprOfPrimitiveAddType(result) and
1973+
this.getRightOperand() = exprOfPrimitiveAddType(i)
1974+
or
1975+
this.getRightOperand() = exprOfPrimitiveAddType(result) and
1976+
this.getLeftOperand() = exprOfPrimitiveAddType(i)
19511977
)
19521978
}
19531979

@@ -2281,6 +2307,8 @@ module YAML {
22812307
)
22822308
}
22832309

2310+
YAMLListItem getListItem() { toQL(result).getParent() = yamle }
2311+
22842312
/** Gets the value of this YAML entry. */
22852313
YAMLValue getValue() {
22862314
exists(QL::YamlKeyvaluepair pair |
@@ -2393,7 +2421,7 @@ module YAML {
23932421
deps.getLocation().getFile() = file and entry.getLocation().getFile() = file
23942422
|
23952423
deps.isRoot() and
2396-
deps.getKey().getQualifiedName() = "dependencies" and
2424+
deps.getKey().getQualifiedName() = ["dependencies", "libraryPathDependencies"] and
23972425
entry.getLocation().getStartLine() = 1 + deps.getLocation().getStartLine() and
23982426
entry.getLocation().getStartColumn() > deps.getLocation().getStartColumn()
23992427
)
@@ -2408,8 +2436,11 @@ module YAML {
24082436

24092437
predicate hasDependency(string name, string version) {
24102438
exists(YAMLEntry entry | this.isADependency(entry) |
2411-
entry.getKey().getQualifiedName() = name and
2439+
entry.getKey().getQualifiedName().trim() = name and
24122440
entry.getValue().getValue() = version
2441+
or
2442+
name = entry.getListItem().getValue().getValue().trim() and
2443+
version = "\"*\""
24132444
)
24142445
}
24152446

@@ -2431,7 +2462,7 @@ module YAML {
24312462
*/
24322463
QLPack getADependency() {
24332464
exists(string name | this.hasDependency(name, _) |
2434-
result.getName().replaceAll("-", "/") = name
2465+
result.getName().replaceAll("-", "/") = name.replaceAll("-", "/")
24352466
)
24362467
}
24372468

ql/src/codeql_ql/ast/internal/AstNodes.qll

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ QL::AstNode toQL(AST::AstNode n) {
193193
n = TAnnotationArg(result)
194194
}
195195

196-
class TPredicate = TCharPred or TClasslessPredicate or TClassPredicate or TDBRelation;
196+
class TPredicate =
197+
TCharPred or TClasslessPredicate or TClassPredicate or TDBRelation or TNewTypeBranch;
197198

198199
class TPredOrBuiltin = TPredicate or TNewTypeBranch or TBuiltin;
199200

@@ -208,3 +209,16 @@ class TTypeDeclaration = TClass or TNewType or TNewTypeBranch;
208209
class TModuleDeclaration = TClasslessPredicate or TModule or TClass or TNewType;
209210

210211
class TVarDef = TVarDecl or TAsExpr;
212+
213+
module AstConsistency {
214+
import ql
215+
216+
query predicate nonTotalGetParent(AstNode node) {
217+
exists(toQL(node).getParent()) and
218+
not exists(node.getParent()) and
219+
not node.getLocation().getStartColumn() = 1 and // startcolumn = 1 <=> top level in file <=> fine to have no parent
220+
exists(node.toString()) and // <- there are a few parse errors in "global-data-flow-java-1.ql", this way we filter them out.
221+
not node instanceof YAML::YAMLNode and // parents for YAML doens't work
222+
not (node instanceof QLDoc and node.getLocation().getFile().getExtension() = "dbscheme") // qldoc in dbschemes are not hooked up
223+
}
224+
}

ql/src/codeql_ql/ast/internal/Module.qll

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ private predicate resolveQualifiedName(Import imp, ContainerOrModule m, int i) {
139139
}
140140

141141
private predicate resolveSelectionName(Import imp, ContainerOrModule m, int i) {
142+
(m.(File_).getFile().getExtension() = "qll" or not m instanceof File_) and
142143
exists(int last |
143144
resolveQualifiedName(imp, m, last) and
144145
last = count(int j | exists(imp.getQualifiedName(j))) - 1
@@ -281,7 +282,11 @@ module ModConsistency {
281282
query predicate multipleResolve(Import imp, int c, ContainerOrModule m) {
282283
c = strictcount(ContainerOrModule m0 | resolve(imp, m0)) and
283284
c > 1 and
284-
resolve(imp, m)
285+
resolve(imp, m) and
286+
not imp.getLocation()
287+
.getFile()
288+
.getAbsolutePath()
289+
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
285290
}
286291

287292
query predicate multipleResolveModuleExpr(ModuleExpr me, int c, ContainerOrModule m) {

ql/src/codeql_ql/ast/internal/Predicate.qll

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,43 @@ private import Builtins
33
private import codeql_ql.ast.internal.Module
44
private import codeql_ql.ast.internal.AstNodes
55

6-
private class TClasslessPredicateOrNewTypeBranch = TClasslessPredicate or TNewTypeBranch;
7-
8-
private string getPredicateName(TClasslessPredicateOrNewTypeBranch p) {
9-
result = p.(ClasslessPredicate).getName() or
10-
result = p.(NewTypeBranch).getName()
11-
}
12-
136
private predicate definesPredicate(
14-
FileOrModule m, string name, int arity, TClasslessPredicateOrNewTypeBranch p, boolean public
7+
FileOrModule m, string name, int arity, Predicate p, boolean public
158
) {
16-
m = getEnclosingModule(p) and
17-
name = getPredicateName(p) and
18-
public = getPublicBool(p) and
19-
arity = [p.(ClasslessPredicate).getArity(), count(p.(NewTypeBranch).getField(_))]
20-
or
21-
// import X
22-
exists(Import imp, FileOrModule m0 |
23-
m = getEnclosingModule(imp) and
24-
m0 = imp.getResolvedModule() and
25-
not exists(imp.importedAs()) and
26-
definesPredicate(m0, name, arity, p, true) and
27-
public = getPublicBool(imp)
28-
)
29-
or
30-
// predicate X = Y
31-
exists(ClasslessPredicate alias |
32-
m = getEnclosingModule(alias) and
33-
name = alias.getName() and
34-
resolvePredicateExpr(alias.getAlias(), p) and
35-
public = getPublicBool(alias) and
36-
arity = alias.getArity()
9+
(
10+
p instanceof NewTypeBranch or
11+
p instanceof ClasslessPredicate
12+
) and
13+
(
14+
m = getEnclosingModule(p) and
15+
name = p.getName() and
16+
public = getPublicBool(p) and
17+
arity = p.getArity()
18+
or
19+
// import X
20+
exists(Import imp, FileOrModule m0 |
21+
m = getEnclosingModule(imp) and
22+
m0 = imp.getResolvedModule() and
23+
not exists(imp.importedAs()) and
24+
definesPredicate(m0, name, arity, p, true) and
25+
public = getPublicBool(imp)
26+
)
27+
or
28+
// predicate X = Y
29+
exists(ClasslessPredicate alias |
30+
m = getEnclosingModule(alias) and
31+
name = alias.getName() and
32+
resolvePredicateExpr(alias.getAlias(), p) and
33+
public = getPublicBool(alias) and
34+
arity = alias.getArity()
35+
)
3736
)
3837
}
3938

4039
cached
4140
private module Cached {
4241
cached
43-
predicate resolvePredicateExpr(PredicateExpr pe, ClasslessPredicate p) {
42+
predicate resolvePredicateExpr(PredicateExpr pe, Predicate p) {
4443
exists(FileOrModule m, boolean public |
4544
not exists(pe.getQualifier()) and
4645
m = getEnclosingModule(pe).getEnclosing*() and
@@ -49,7 +48,7 @@ private module Cached {
4948
m = pe.getQualifier().getResolvedModule() and
5049
public = true
5150
|
52-
definesPredicate(m, pe.getName(), count(p.getParameter(_)), p, public)
51+
definesPredicate(m, pe.getName(), p.getArity(), p, public)
5352
)
5453
}
5554

ql/src/codeql_ql/ast/internal/Type.qll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,12 @@ module TyConsistency {
340340
resolveTypeExpr(te, t)
341341
}
342342

343+
query predicate multiplePrimitives(TypeExpr te, int c, PrimitiveType t) {
344+
c = strictcount(PrimitiveType t0 | resolveTypeExpr(te, t0)) and
345+
c > 1 and
346+
resolveTypeExpr(te, t)
347+
}
348+
343349
query predicate varDefNoType(VarDef def) {
344350
not exists(def.getType()) and
345351
not def.getLocation()
@@ -360,4 +366,10 @@ module TyConsistency {
360366
.getAbsolutePath()
361367
.regexpMatch(".*/(test|examples|ql-training|recorded-call-graph-metrics)/.*")
362368
}
369+
370+
query predicate multiplePrimitivesExpr(Expr e, int c, PrimitiveType t) {
371+
c = strictcount(PrimitiveType t0 | t0 = e.getType()) and
372+
c > 1 and
373+
t = e.getType()
374+
}
363375
}

ql/src/codeql_ql/ast/internal/Variable.qll

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import ql
2-
import codeql_ql.ast.internal.AstNodes
2+
private import codeql_ql.ast.internal.AstNodes
33

44
private class TScope =
55
TClass or TAggregate or TQuantifier or TSelect or TPredicate or TNewTypeBranch;
@@ -89,4 +89,8 @@ module VarConsistency {
8989
decl = f.getDeclaration() and
9090
strictcount(f.getDeclaration()) > 1
9191
}
92+
93+
query predicate noFieldDef(FieldAccess f) { not exists(f.getDeclaration()) }
94+
95+
query predicate noVarDef(VarAccess v) { not exists(v.getDeclaration()) }
9296
}

ql/src/codeql_ql/printAstGenerated.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* to hold for only the AST nodes you wish to view.
99
*/
1010

11-
import ast.internal.TreeSitter::Generated
11+
import ast.internal.TreeSitter::QL
1212
private import codeql.Locations
1313

1414
/**

0 commit comments

Comments
 (0)