Skip to content

Commit 349b556

Browse files
authored
Merge pull request #129 from github/erik-krogh/cartesian
various new improvements and queries
2 parents cc16fde + 38b925b commit 349b556

23 files changed

+924
-408
lines changed

ql/src/codeql_ql/ast/Ast.qll

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class AstNode extends TAstNode {
4949
/**
5050
* Gets the parent in the AST for this node.
5151
*/
52+
cached
5253
AstNode getParent() { result.getAChild(_) = this }
5354

5455
/**
@@ -74,12 +75,14 @@ class AstNode extends TAstNode {
7475
predicate hasAnnotation(string name) { this.getAnAnnotation().getName() = name }
7576

7677
/** Gets an annotation of this AST node. */
77-
Annotation getAnAnnotation() { toQL(this).getParent() = toQL(result).getParent() }
78+
Annotation getAnAnnotation() {
79+
toQL(this).getParent() = pragma[only_bind_out](toQL(result)).getParent()
80+
}
7881

7982
/**
8083
* Gets the predicate that contains this AST node.
8184
*/
82-
pragma[noinline]
85+
cached
8386
Predicate getEnclosingPredicate() { this = getANodeInPredicate(result) }
8487
}
8588

@@ -563,8 +566,10 @@ class VarDecl extends TVarDecl, VarDef, Declaration {
563566
)
564567
}
565568

566-
/** If this is a field, returns the class type that declares it. */
567-
ClassType getDeclaringType() { result.getDeclaration().getAField() = this }
569+
/** If this is declared in a field, returns the class type that declares it. */
570+
ClassType getDeclaringType() {
571+
exists(FieldDecl f | f.getVarDecl() = this and result = f.getParent().(Class).getType())
572+
}
568573

569574
/**
570575
* Holds if this is a class field that overrides the field `other`.
@@ -580,6 +585,32 @@ class VarDecl extends TVarDecl, VarDef, Declaration {
580585
override string toString() { result = this.getName() }
581586
}
582587

588+
/**
589+
* A field declaration;
590+
*/
591+
class FieldDecl extends TFieldDecl, AstNode {
592+
QL::Field f;
593+
594+
FieldDecl() { this = TFieldDecl(f) }
595+
596+
VarDecl getVarDecl() { toQL(result) = f.getChild() }
597+
598+
override AstNode getAChild(string pred) {
599+
result = super.getAChild(pred)
600+
or
601+
pred = directMember("getVarDecl") and result = this.getVarDecl()
602+
}
603+
604+
override string getAPrimaryQlClass() { result = "FieldDecl" }
605+
606+
/** Holds if this field is annotated as overriding another field. */
607+
predicate isOverride() { this.hasAnnotation("override") }
608+
609+
string getName() { result = getVarDecl().getName() }
610+
611+
override QLDoc getQLDoc() { result = any(Class c).getQLDocFor(this) }
612+
}
613+
583614
/**
584615
* A type reference, such as `DataFlow::Node`.
585616
*/
@@ -716,10 +747,7 @@ class Class extends TClass, TypeDeclaration, ModuleDeclaration {
716747
*/
717748
CharPred getCharPred() { toQL(result) = cls.getChild(_).(QL::ClassMember).getChild(_) }
718749

719-
AstNode getMember(int i) {
720-
toQL(result) = cls.getChild(i).(QL::ClassMember).getChild(_) or
721-
toQL(result) = cls.getChild(i).(QL::ClassMember).getChild(_).(QL::Field).getChild()
722-
}
750+
AstNode getMember(int i) { toQL(result) = cls.getChild(i).(QL::ClassMember).getChild(_) }
723751

724752
QLDoc getQLDocFor(AstNode m) {
725753
exists(int i | result = this.getMember(i) and m = this.getMember(i + 1))
@@ -743,9 +771,7 @@ class Class extends TClass, TypeDeclaration, ModuleDeclaration {
743771
/**
744772
* Gets a field in this class.
745773
*/
746-
VarDecl getAField() {
747-
toQL(result) = cls.getChild(_).(QL::ClassMember).getChild(_).(QL::Field).getChild()
748-
}
774+
FieldDecl getAField() { result = getMember(_) }
749775

750776
/**
751777
* Gets a super-type referenced in the `extends` part of the class declaration.
@@ -1102,9 +1128,15 @@ class Conjunction extends TConjunction, AstNode, Formula {
11021128

11031129
override string getAPrimaryQlClass() { result = "Conjunction" }
11041130

1105-
/** Gets an operand to this formula. */
1131+
/** Gets an operand to this conjunction. */
11061132
Formula getAnOperand() { toQL(result) in [conj.getLeft(), conj.getRight()] }
11071133

1134+
/** Gets the left operand to this conjunction. */
1135+
Formula getLeft() { toQL(result) = conj.getLeft() }
1136+
1137+
/** Gets the right operand to this conjunction. */
1138+
Formula getRight() { toQL(result) = conj.getRight() }
1139+
11081140
override AstNode getAChild(string pred) {
11091141
result = super.getAChild(pred)
11101142
or
@@ -1120,33 +1152,22 @@ class Disjunction extends TDisjunction, AstNode, Formula {
11201152

11211153
override string getAPrimaryQlClass() { result = "Disjunction" }
11221154

1123-
/** Gets an operand to this formula. */
1155+
/** Gets an operand to this disjunction. */
11241156
Formula getAnOperand() { toQL(result) in [disj.getLeft(), disj.getRight()] }
11251157

1158+
/** Gets the left operand to this disjunction */
1159+
Formula getLeft() { toQL(result) = disj.getLeft() }
1160+
1161+
/** Gets the right operand to this disjunction */
1162+
Formula getRight() { toQL(result) = disj.getRight() }
1163+
11261164
override AstNode getAChild(string pred) {
11271165
result = super.getAChild(pred)
11281166
or
11291167
pred = directMember("getAnOperand") and result = this.getAnOperand()
11301168
}
11311169
}
11321170

1133-
/**
1134-
* A comparison operator, such as `<` or `=`.
1135-
*/
1136-
class ComparisonOp extends TComparisonOp, AstNode {
1137-
QL::Compop op;
1138-
1139-
ComparisonOp() { this = TComparisonOp(op) }
1140-
1141-
/**
1142-
* Gets a string representing the operator.
1143-
* E.g. "<" or "=".
1144-
*/
1145-
ComparisonSymbol getSymbol() { result = op.getValue() }
1146-
1147-
override string getAPrimaryQlClass() { result = "ComparisonOp" }
1148-
}
1149-
11501171
/**
11511172
* A literal expression, such as `6` or `true` or `"foo"`.
11521173
*/
@@ -1243,10 +1264,7 @@ class ComparisonFormula extends TComparisonFormula, Formula {
12431264
Expr getAnOperand() { result in [this.getLeftOperand(), this.getRightOperand()] }
12441265

12451266
/** Gets the operator of this comparison. */
1246-
ComparisonOp getOperator() { toQL(result) = comp.getChild() }
1247-
1248-
/** Gets the symbol of this comparison (as a string). */
1249-
ComparisonSymbol getSymbol() { result = this.getOperator().getSymbol() }
1267+
ComparisonSymbol getOperator() { result = comp.getChild().getValue() }
12501268

12511269
override string getAPrimaryQlClass() { result = "ComparisonFormula" }
12521270

@@ -1256,8 +1274,6 @@ class ComparisonFormula extends TComparisonFormula, Formula {
12561274
pred = directMember("getLeftOperand") and result = this.getLeftOperand()
12571275
or
12581276
pred = directMember("getRightOperand") and result = this.getRightOperand()
1259-
or
1260-
pred = directMember("getOperator") and result = this.getOperator()
12611277
}
12621278
}
12631279

@@ -2218,6 +2234,8 @@ class Annotation extends TAnnotation, AstNode {
22182234
/** Gets the node corresponding to the field `name`. */
22192235
string getName() { result = annot.getName().getValue() }
22202236

2237+
override AstNode getParent() { result = AstNode.super.getParent() }
2238+
22212239
override AstNode getAChild(string pred) {
22222240
result = super.getAChild(pred)
22232241
or

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ newtype TAstNode =
88
TQLDoc(QL::Qldoc qldoc) or
99
TClasslessPredicate(QL::ClasslessPredicate pred) or
1010
TVarDecl(QL::VarDecl decl) or
11+
TFieldDecl(QL::Field field) or
1112
TClass(QL::Dataclass dc) or
1213
TCharPred(QL::Charpred pred) or
1314
TClassPredicate(QL::MemberPredicate pred) or
@@ -21,7 +22,6 @@ newtype TAstNode =
2122
TDisjunction(QL::Disjunction disj) or
2223
TConjunction(QL::Conjunction conj) or
2324
TComparisonFormula(QL::CompTerm comp) or
24-
TComparisonOp(QL::Compop op) or
2525
TQuantifier(QL::Quantified quant) or
2626
TFullAggregate(QL::Aggregate agg) { agg.getChild(_) instanceof QL::FullAggregateBody } or
2727
TExprAggregate(QL::Aggregate agg) { agg.getChild(_) instanceof QL::ExprAggregateBody } or
@@ -90,7 +90,6 @@ private QL::AstNode toQLFormula(AST::AstNode n) {
9090
n = TConjunction(result) or
9191
n = TDisjunction(result) or
9292
n = TComparisonFormula(result) or
93-
n = TComparisonOp(result) or
9493
n = TQuantifier(result) or
9594
n = TFullAggregate(result) or
9695
n = TIdentifier(result) or
@@ -127,6 +126,7 @@ private QL::AstNode toGenerateYAML(AST::AstNode n) {
127126
/**
128127
* Gets the underlying TreeSitter entity for a given AST node.
129128
*/
129+
cached
130130
QL::AstNode toQL(AST::AstNode n) {
131131
result = toQLExpr(n)
132132
or
@@ -150,6 +150,8 @@ QL::AstNode toQL(AST::AstNode n) {
150150
or
151151
n = TVarDecl(result)
152152
or
153+
n = TFieldDecl(result)
154+
or
153155
n = TClass(result)
154156
or
155157
n = TCharPred(result)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ predicate predOverrides(ClassPredicate sub, ClassPredicate sup) {
134134
}
135135

136136
private VarDecl declaredField(ClassType ty, string name) {
137-
result = ty.getDeclaration().getAField() and
137+
result = ty.getDeclaration().getAField().getVarDecl() and
138138
result.getName() = name
139139
}
140140

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class VariableScope extends TScope, AstNode {
3737
predicate containsField(VarDef decl, string name) {
3838
name = decl.getName() and
3939
(
40-
decl = this.(Class).getAField()
40+
decl = this.(Class).getAField().getVarDecl()
4141
or
4242
this.getOuterScope().containsField(decl, name) and
4343
not exists(this.getADefinition(name))

ql/src/queries/performance/PrefixSuffixEquality.ql renamed to ql/src/codeql_ql/performance/InefficientStringComparisonQuery.qll

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
/**
2-
* @name Prefix or suffix predicate calls when comparing with literal
3-
* @description Using 'myString.prefix(n) = "..."' instead of 'myString.matches("...%")'
4-
* @kind problem
5-
* @problem.severity error
6-
* @id ql/prefix-or-suffix-equality-check
7-
* @tags performance
8-
* @precision high
9-
*/
10-
111
import ql
122
import codeql_ql.ast.internal.Predicate
133
import codeql_ql.ast.internal.Builtins
@@ -29,7 +19,7 @@ class SuffixPredicateCall extends Call {
2919
}
3020

3121
class EqFormula extends ComparisonFormula {
32-
EqFormula() { this.getSymbol() = "=" }
22+
EqFormula() { this.getOperator() = "=" }
3323
}
3424

3525
bindingset[s]
@@ -48,6 +38,17 @@ class FixPredicateCall extends Call {
4838
FixPredicateCall() { this instanceof PrefixPredicateCall or this instanceof SuffixPredicateCall }
4939
}
5040

51-
from EqFormula eq, FixPredicateCall call, String literal
52-
where eq.getAnOperand() = call and eq.getAnOperand() = literal
53-
select eq, "Use " + getMessage(call, literal) + " instead."
41+
class RegexpMatchPredicate extends BuiltinPredicate {
42+
RegexpMatchPredicate() { this = any(StringClass sc).getClassPredicate("regexpMatch", 1) }
43+
}
44+
45+
predicate canUseMatchInsteadOfRegexpMatch(Call c, string matchesStr) {
46+
c.getTarget() instanceof RegexpMatchPredicate and
47+
exists(string raw | raw = c.getArgument(0).(String).getValue() |
48+
matchesStr = "%" + raw.regexpCapture("^\\.\\*(\\w+)$", _)
49+
or
50+
matchesStr = raw.regexpCapture("^(\\w+)\\.\\*$", _) + "%"
51+
or
52+
matchesStr = "%" + raw.regexpCapture("^\\.\\*(\\w+)\\.\\*$", _) + "%"
53+
)
54+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import ql
2+
3+
MemberCall explicitThisCallInFile(File f) {
4+
result.getLocation().getFile() = f and
5+
result.getBase() instanceof ThisAccess and
6+
// Exclude `this.(Type).whatever(...)`, as some files have that as their only instance of `this`.
7+
not result = any(InlineCast c).getBase()
8+
}
9+
10+
PredicateCall implicitThisCallInFile(File f) {
11+
result.getLocation().getFile() = f and
12+
exists(result.getTarget().getDeclaringType().getASuperType()) and
13+
// Exclude `SomeModule::whatever(...)`
14+
not exists(result.getQualifier())
15+
}
16+
17+
PredicateCall confusingImplicitThisCall(File f) {
18+
result = implicitThisCallInFile(f) and
19+
exists(explicitThisCallInFile(f))
20+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import ql
2+
3+
class RedundantInlineCast extends AstNode instanceof InlineCast {
4+
Type t;
5+
6+
RedundantInlineCast() {
7+
t = unique( | | super.getType()) and
8+
(
9+
// The cast is to the type the base expression already has
10+
t = unique( | | super.getBase().getType())
11+
or
12+
// The cast is to the same type as the other expression in an equality comparison
13+
exists(ComparisonFormula comp, Expr other | comp.getOperator() = "=" |
14+
this = comp.getAnOperand() and
15+
other = comp.getAnOperand() and
16+
this != other and
17+
t = unique( | | other.getType()) and
18+
not other instanceof InlineCast // we don't want to risk both sides being "redundant"
19+
)
20+
or
21+
exists(Call call, int i, Predicate target |
22+
this = call.getArgument(i) and
23+
target = unique( | | call.getTarget()) and
24+
t = unique( | | target.getParameterType(i))
25+
)
26+
) and
27+
// noopt can require explicit casts
28+
not exists(Annotation annon | annon = this.getEnclosingPredicate().getAnAnnotation() |
29+
annon.getName() = "pragma" and
30+
annon.getArgs(0).getValue() = "noopt"
31+
)
32+
}
33+
34+
TypeExpr getTypeExpr() { result = super.getTypeExpr() }
35+
}

0 commit comments

Comments
 (0)