QL: Merge branch 'main' into tausbn/update-extractor-generator

This commit is contained in:
Taus
2021-10-15 12:28:01 +02:00
committed by GitHub
16 changed files with 474 additions and 64 deletions

View File

@@ -1,4 +1,4 @@
name: "CodeQL"
name: "CodeQL with bleeding edge queries and extractor"
on:
workflow_dispatch:

View File

@@ -0,0 +1,53 @@
name: "CodeQL with published queries and extractor"
on:
workflow_dispatch:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Download pack
run: |
# adjust this line to make the workflow work in other repositories
# the ql-qlpack.zip file can be downloaded at:
# - https://github.com/github/codeql-ql/releases
# - https://github.com/github/codeql-ql/actions/workflows/bleeding-codeql-analysis.yml
gh release download latest --pattern ql-qlpack.zip
unzip ql-qlpack.zip -d "${PACK}"
env:
PACK: ${{ runner.temp }}/ql-qlpack
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Hack codeql-action options
run: |
JSON=$(jq -nc --arg pack "${PACK}" '.resolve.queries=["--search-path", $pack] | .resolve.extractor=["--search-path", $pack] | .database.init=["--search-path", $pack]')
echo "CODEQL_ACTION_EXTRA_OPTIONS=${JSON}" >> ${GITHUB_ENV}
env:
PACK: ${{ runner.temp }}/ql-qlpack
- name: Initialize CodeQL
uses: github/codeql-action/init@esbena/ql
with:
languages: ql
db-location: ${{ runner.temp }}/db
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@esbena/ql

View File

@@ -0,0 +1 @@
import codeql_ql.ast.internal.Builtins::BuildinsConsistency

View File

@@ -9,10 +9,10 @@ abstract class Container extends @container {
Container getAChildContainer() { this = result.getParentContainer() }
/** Gets a file in this container. */
File getAFile() { result = getAChildContainer() }
File getAFile() { result = this.getAChildContainer() }
/** Gets a sub-folder in this container. */
Folder getAFolder() { result = getAChildContainer() }
Folder getAFolder() { result = this.getAChildContainer() }
/**
* Gets the absolute, canonical path of this container, using forward slashes
@@ -58,7 +58,7 @@ abstract class Container extends @container {
* </table>
*/
string getBaseName() {
result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1)
result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1)
}
/**
@@ -84,17 +84,19 @@ abstract class Container extends @container {
* <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr>
* </table>
*/
string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) }
string getExtension() {
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3)
}
/** Gets the file in this container that has the given `baseName`, if any. */
File getFile(string baseName) {
result = getAFile() and
result = this.getAFile() and
result.getBaseName() = baseName
}
/** Gets the sub-folder in this container that has the given `baseName`, if any. */
Folder getFolder(string baseName) {
result = getAFolder() and
result = this.getAFolder() and
result.getBaseName() = baseName
}
@@ -111,7 +113,7 @@ abstract class Container extends @container {
*/
string getRelativePath() {
exists(string absPath, string pref |
absPath = getAbsolutePath() and sourceLocationPrefix(pref)
absPath = this.getAbsolutePath() and sourceLocationPrefix(pref)
|
absPath = pref and result = ""
or
@@ -137,7 +139,9 @@ abstract class Container extends @container {
* <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr>
* </table>
*/
string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) }
string getStem() {
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1)
}
/**
* Gets a URL representing the location of this container.
@@ -151,7 +155,7 @@ abstract class Container extends @container {
*
* This is the absolute path of the container.
*/
string toString() { result = getAbsolutePath() }
string toString() { result = this.getAbsolutePath() }
}
/** A folder. */
@@ -159,7 +163,7 @@ class Folder extends Container, @folder {
override string getAbsolutePath() { folders(this, result) }
/** Gets the URL of this folder. */
override string getURL() { result = "folder://" + getAbsolutePath() }
override string getURL() { result = "folder://" + this.getAbsolutePath() }
}
/** A file. */

View File

@@ -19,7 +19,7 @@ private string stringIndexedMember(string name, string index) {
/** An AST node of a QL program */
class AstNode extends TAstNode {
string toString() { result = getAPrimaryQlClass() }
string toString() { result = this.getAPrimaryQlClass() }
/**
* Gets the location of the AST node.
@@ -35,8 +35,8 @@ class AstNode extends TAstNode {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
if exists(getLocation())
then getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
if exists(this.getLocation())
then this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
else (
filepath = "" and
startline = 0 and
@@ -92,7 +92,7 @@ class TopLevel extends TTopLevel, AstNode {
* Gets a member from contained in this top-level module.
* Includes private members.
*/
ModuleMember getAMember() { result = getMember(_) }
ModuleMember getAMember() { result = this.getMember(_) }
/** Gets the `i`'th member of this top-level module. */
ModuleMember getMember(int i) { toQL(result) = file.getChild(i).(QL::ModuleMember).getChild(_) }
@@ -197,13 +197,13 @@ class PredicateOrBuiltin extends TPredOrBuiltin, AstNode {
Type getReturnType() { none() }
int getArity() { result = count(getParameterType(_)) }
int getArity() { result = count(int i | exists(this.getParameterType(i))) }
predicate isPrivate() { none() }
}
class BuiltinPredicate extends PredicateOrBuiltin, TBuiltin {
override string toString() { result = getName() }
override string toString() { result = this.getName() }
override string getAPrimaryQlClass() { result = "BuiltinPredicate" }
}
@@ -264,7 +264,7 @@ class Predicate extends TPredicate, AstNode, PredicateOrBuiltin, Declaration {
*/
override int getArity() {
not this.(ClasslessPredicate).getAlias() instanceof PredicateExpr and
result = count(getParameter(_))
result = count(this.getParameter(_))
or
exists(PredicateExpr alias | alias = this.(ClasslessPredicate).getAlias() |
result = alias.getArity()
@@ -274,7 +274,7 @@ class Predicate extends TPredicate, AstNode, PredicateOrBuiltin, Declaration {
/**
* Holds if this predicate is private.
*/
override predicate isPrivate() { hasAnnotation("private") }
override predicate isPrivate() { this.hasAnnotation("private") }
/**
* Gets the return type (if any) of the predicate.
@@ -321,18 +321,18 @@ class Relation extends TDBRelation, AstNode, Declaration {
}
/** Gets the `i`th parameter name */
string getParameterName(int i) { result = getColumn(i).getColName().getValue() }
string getParameterName(int i) { result = this.getColumn(i).getColName().getValue() }
/** Gets the `i`th parameter type */
string getParameterType(int i) {
// TODO: This is just using the name of the type, not the actual type. Checkout Type.qll
result = getColumn(i).getColType().getChild().(QL::Token).getValue()
result = this.getColumn(i).getColType().getChild().(QL::Token).getValue()
}
/**
* Gets the number of parameters.
*/
int getArity() { result = count(getColumn(_)) }
int getArity() { result = count(this.getColumn(_)) }
override string getAPrimaryQlClass() { result = "Relation" }
}
@@ -464,7 +464,7 @@ class ClassPredicate extends TClassPredicate, Predicate {
/**
* Holds if this predicate is annotated as overriding another predicate.
*/
predicate isOverride() { hasAnnotation("override") }
predicate isOverride() { this.hasAnnotation("override") }
override VarDecl getParameter(int i) {
toQL(result) =
@@ -474,7 +474,7 @@ class ClassPredicate extends TClassPredicate, Predicate {
/**
* Gets the type representing this class.
*/
override ClassType getDeclaringType() { result.getDeclaration() = getParent() }
override ClassType getDeclaringType() { result.getDeclaration() = this.getParent() }
predicate overrides(ClassPredicate other) { predOverrides(this, other) }
@@ -503,7 +503,7 @@ class CharPred extends TCharPred, Predicate {
override Formula getBody() { toQL(result) = pred.getBody() }
override string getName() { result = getParent().(Class).getName() }
override string getName() { result = this.getParent().(Class).getName() }
override AstNode getAChild(string pred_name) {
result = super.getAChild(pred_name)
@@ -511,7 +511,7 @@ class CharPred extends TCharPred, Predicate {
pred_name = directMember("getBody") and result = this.getBody()
}
override ClassType getDeclaringType() { result.getDeclaration() = getParent() }
override ClassType getDeclaringType() { result.getDeclaration() = this.getParent() }
}
/**
@@ -667,7 +667,7 @@ class Module extends TModule, ModuleDeclaration {
*/
class ModuleMember extends TModuleMember, AstNode {
/** Holds if this member is declared as `private`. */
predicate isPrivate() { hasAnnotation("private") }
predicate isPrivate() { this.hasAnnotation("private") }
}
/** A declaration. E.g. a class, type, predicate, newtype... */
@@ -735,7 +735,7 @@ class Class extends TClass, TypeDeclaration, ModuleDeclaration {
* Gets predicate `name` implemented in this class.
*/
ClassPredicate getClassPredicate(string name) {
result = getAClassPredicate() and
result = this.getAClassPredicate() and
result.getName() = name
}
@@ -864,7 +864,7 @@ class Call extends TCall, Expr, Formula {
}
/** Gets an argument of this call, if any. */
final Expr getAnArgument() { result = getArgument(_) }
final Expr getAnArgument() { result = this.getArgument(_) }
PredicateOrBuiltin getTarget() { resolveCall(this, result) }
@@ -1231,7 +1231,7 @@ class ComparisonFormula extends TComparisonFormula, Formula {
Expr getRightOperand() { toQL(result) = comp.getRight() }
/** Gets an operand of this comparison. */
Expr getAnOperand() { result in [getLeftOperand(), getRightOperand()] }
Expr getAnOperand() { result in [this.getLeftOperand(), this.getRightOperand()] }
/** Gets the operator of this comparison. */
ComparisonOp getOperator() { toQL(result) = comp.getChild() }
@@ -1289,7 +1289,7 @@ class Quantifier extends TQuantifier, Formula {
* Holds if this is the "expression only" form of an exists quantifier.
* In other words, the quantifier is of the form `exists( expr )`.
*/
predicate hasExpr() { exists(getExpr()) }
predicate hasExpr() { exists(this.getExpr()) }
override string getAPrimaryQlClass() { result = "Quantifier" }
@@ -1461,7 +1461,7 @@ class HigherOrderFormula extends THigherOrderFormula, Formula {
* Gets the `i`th argument to this higher-order formula.
* E.g. for `fastTC(pathSucc/2)(n1, n2)` the result is `n1` and `n2`.
*/
Expr getArgument(int i) { toQL(result) = hop.getChild(i + getNumInputs()) }
Expr getArgument(int i) { toQL(result) = hop.getChild(i + this.getNumInputs()) }
/**
* Gets the name of this higher-order predicate.
@@ -1550,7 +1550,7 @@ class ExprAggregate extends TExprAggregate, Aggregate {
)
or
not kind = ["count", "strictcount"] and
result = getExpr(0).getType()
result = this.getExpr(0).getType()
}
}
@@ -1614,11 +1614,11 @@ class FullAggregate extends TFullAggregate, Aggregate {
)
or
kind = ["any", "min", "max", "unique"] and
not exists(getExpr(_)) and
result = getArgument(0).getTypeExpr().getResolvedType()
not exists(this.getExpr(_)) and
result = this.getArgument(0).getTypeExpr().getResolvedType()
or
not kind = ["count", "strictcount"] and
result = getExpr(0).getType()
result = this.getExpr(0).getType()
}
override AstNode getAChild(string pred) {
@@ -1842,7 +1842,7 @@ class BinOpExpr extends TBinOpExpr, Expr {
FunctionSymbol getOperator() { none() } // overriden in subclasses
/* Gets an operand of the binary expression. */
final Expr getAnOperand() { result = getLeftOperand() or result = getRightOperand() }
final Expr getAnOperand() { result = this.getLeftOperand() or result = this.getRightOperand() }
}
/**
@@ -1999,7 +1999,7 @@ class Range extends TRange, Expr {
/**
* Gets the lower and upper bounds of the range.
*/
Expr getAnEndpoint() { result = [getLowEndpoint(), getHighEndpoint()] }
Expr getAnEndpoint() { result = [this.getLowEndpoint(), this.getHighEndpoint()] }
override PrimitiveType getType() { result.getName() = "int" }
@@ -2030,12 +2030,12 @@ class Set extends TSet, Expr {
/**
* Gets an element in this set literal expression, if any.
*/
Expr getAnElement() { result = getElement(_) }
Expr getAnElement() { result = this.getElement(_) }
/**
* Gets the number of elements in this set literal expression.
*/
int getNumberOfElements() { result = count(getAnElement()) }
int getNumberOfElements() { result = count(this.getAnElement()) }
override Type getType() { result = this.getElement(0).getType() }
@@ -2044,7 +2044,7 @@ class Set extends TSet, Expr {
override AstNode getAChild(string pred) {
result = super.getAChild(pred)
or
exists(int i | pred = indexedMember("getElement", i) and result = getElement(i))
exists(int i | pred = indexedMember("getElement", i) and result = this.getElement(i))
}
}
@@ -2227,10 +2227,10 @@ class BindingSet extends Annotation {
string getBoundName(int index) { result = this.getArgs(index).getValue() }
/** Gets a name bound by this bindingset, if any. */
string getABoundName() { result = getBoundName(_) }
string getABoundName() { result = this.getBoundName(_) }
/** Gets the number of names bound by this bindingset. */
int getNumberOfBoundNames() { result = count(getABoundName()) }
int getNumberOfBoundNames() { result = count(this.getABoundName()) }
}
/**
@@ -2240,7 +2240,7 @@ module YAML {
/** A node in a YAML file */
class YAMLNode extends TYAMLNode, AstNode {
/** Holds if the predicate is a root node (has no parent) */
predicate isRoot() { not exists(getParent()) }
predicate isRoot() { not exists(this.getParent()) }
}
/** A YAML comment. */
@@ -2309,7 +2309,7 @@ module YAML {
* Dashes are replaced with `/` (because we don't have that information in the generated AST).
*/
string getQualifiedName() {
result = concat(string part, int i | part = getNamePart(i) | part, "/" order by i)
result = concat(string part, int i | part = this.getNamePart(i) | part, "/" order by i)
}
}
@@ -2362,15 +2362,15 @@ module YAML {
}
/** Gets the name of this qlpack */
string getName() { result = getProperty("name") }
string getName() { result = this.getProperty("name") }
/** Gets the version of this qlpack */
string getVersion() { result = getProperty("version") }
string getVersion() { result = this.getProperty("version") }
/** Gets the extractor of this qlpack */
string getExtractor() { result = getProperty("extractor") }
string getExtractor() { result = this.getProperty("extractor") }
string toString() { result = getName() }
string toString() { result = this.getName() }
/** Gets the file that this `QLPack` represents. */
File getFile() { result = file }
@@ -2385,7 +2385,7 @@ module YAML {
entry.getLocation().getStartColumn() > deps.getLocation().getStartColumn()
)
or
exists(YAMLEntry prev | isADependency(prev) |
exists(YAMLEntry prev | this.isADependency(prev) |
prev.getLocation().getFile() = file and
entry.getLocation().getFile() = file and
entry.getLocation().getStartLine() = 1 + prev.getLocation().getStartLine() and
@@ -2394,7 +2394,7 @@ module YAML {
}
predicate hasDependency(string name, string version) {
exists(YAMLEntry entry | isADependency(entry) |
exists(YAMLEntry entry | this.isADependency(entry) |
entry.getKey().getQualifiedName() = name and
entry.getValue().getValue() = version
)
@@ -2402,7 +2402,7 @@ module YAML {
/** Gets the database scheme of this qlpack */
File getDBScheme() {
result.getBaseName() = getProperty("dbscheme") and
result.getBaseName() = this.getProperty("dbscheme") and
result = file.getParentContainer().getFile(any(string s | s.matches("%.dbscheme")))
}
@@ -2410,14 +2410,16 @@ module YAML {
Container getAFileInPack() {
result.getParentContainer() = file.getParentContainer()
or
result = getAFileInPack().(Folder).getAChildContainer()
result = this.getAFileInPack().(Folder).getAChildContainer()
}
/**
* Gets a QLPack that this QLPack depends on.
*/
QLPack getADependency() {
exists(string name | hasDependency(name, _) | result.getName().replaceAll("-", "/") = name)
exists(string name | this.hasDependency(name, _) |
result.getName().replaceAll("-", "/") = name
)
}
Location getLocation() {

View File

@@ -35,8 +35,8 @@ predicate isBuiltinMember(string sig) {
"string int.toString()", "string string.charAt(int)", "int string.indexOf(string)",
"int string.indexOf(string, int, int)", "predicate string.isLowercase()",
"predicate string.isUppercase()", "int string.length()", "predicate string.matches(string)",
"string string.prefix(int)", "string regexpCapture(string, int)",
"string regexpFind(string, int, int)", "predicate string.regexpMatch(string)",
"string string.prefix(int)", "string string.regexpCapture(string, int)",
"string string.regexpFind(string, int, int)", "predicate string.regexpMatch(string)",
"string string.regexpReplaceAll(string, string)", "string string.replaceAll(string, string)",
"string string.splitAt(string)", "string string.splitAt(string, int)",
"string string.substring(int, int)", "string string.suffix(int)", "date string.toDate()",
@@ -58,6 +58,18 @@ predicate isBuiltinMember(string qual, string ret, string name, string args) {
)
}
module BuildinsConsistency {
query predicate noBuildinParse(string sig) {
isBuiltinMember(sig) and
not exists(sig.regexpCapture("(\\w+) (\\w+)\\.(\\w+)\\(([\\w, ]*)\\)", _))
}
query predicate noBuildinClasslessParse(string sig) {
isBuiltinClassless(sig) and
not exists(sig.regexpCapture("(\\w+) (\\w+)\\(([\\w, ]*)\\)", _))
}
}
bindingset[args]
string getArgType(string args, int i) { result = args.splitAt(",", i).trim() }

View File

@@ -27,7 +27,7 @@ private predicate isActualClass(Class c) {
* A type, such as `int` or `Node`.
*/
class Type extends TType {
string toString() { result = getName() }
string toString() { result = this.getName() }
string getName() { result = "???" }
@@ -48,9 +48,9 @@ class Type extends TType {
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
if exists(getDeclaration())
if exists(this.getDeclaration())
then
getDeclaration()
this.getDeclaration()
.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
else (
@@ -72,14 +72,14 @@ class Type extends TType {
private predicate getClassPredicate1(
string name, int arity, PredicateOrBuiltin p1, PredicateOrBuiltin p2
) {
getClassPredicate0(name, arity, p1, p2.getDeclaringType()) and
this.getClassPredicate0(name, arity, p1, p2.getDeclaringType()) and
p2 = classPredCandidate(this, name, arity)
}
cached
PredicateOrBuiltin getClassPredicate(string name, int arity) {
result = classPredCandidate(this, name, arity) and
not getClassPredicate1(name, arity, _, result)
not this.getClassPredicate1(name, arity, _, result)
}
}
@@ -179,7 +179,7 @@ class ClassDomainType extends Type, TClassDomain {
ClassType getClassType() { result = TClass(decl) }
override Type getAnInternalSuperType() { result = getClassType().getASuperType() }
override Type getAnInternalSuperType() { result = this.getClassType().getASuperType() }
}
class PrimitiveType extends Type, TPrimitive {
@@ -192,7 +192,7 @@ class PrimitiveType extends Type, TPrimitive {
override Type getASuperType() { name = "int" and result.(PrimitiveType).getName() = "float" }
override Type getAnInternalSuperType() {
result = getASuperType()
result = this.getASuperType()
or
result = super.getAnInternalSuperType()
}
@@ -232,7 +232,7 @@ class NewTypeBranchType extends Type, TNewTypeBranch {
}
override Type getAnInternalSuperType() {
result = getASuperType()
result = this.getASuperType()
or
result = super.getAnInternalSuperType()
}

View File

@@ -0,0 +1,30 @@
/**
* @name Standard library is not the first import
* @description Importing other libraries before the standard library can cause a change in
* evaluation order and may lead to performance errors.
* @kind problem
* @problem.severity error
* @id ql/noninitial-stdlib-import
* @tags performance
* @precision high
*/
import ql
predicate isStdLibImport(Import i, string name) {
name = i.getQualifiedName(0) and
i.getLocation().getFile().getRelativePath().matches(name + "%") and
not exists(i.getQualifiedName(1))
}
Import importBefore(Import i) {
exists(Module m, int bi, int ii |
result = m.getMember(bi) and
i = m.getMember(ii) and
bi < ii
)
}
from Import i
where isStdLibImport(i, _) and exists(importBefore(i))
select i, "This import may cause reevaluation to occur, as there are other imports preceding it"

View File

@@ -0,0 +1,129 @@
/**
* @name Use a set literal in place of `or`
* @description A chain of `or`s can be replaced with a set literal, improving readability.
* @kind problem
* @problem.severity recommendation
* @id ql/use-set-literal
* @tags maintainability
* @precision high
*/
import ql
/**
* A chain of disjunctions treated as one object. For example the following is
* a chain of disjunctions with three operands:
* ```
* a or b or c
* ```
*/
class DisjunctionChain extends Disjunction {
DisjunctionChain() { not exists(Disjunction parent | parent.getAnOperand() = this) }
/**
* Gets any operand of the chain.
*/
Formula getAnOperandRec() {
result = getAnOperand*() and
not result instanceof Disjunction
}
}
/**
* An equality comparison with a `Literal`. For example:
* ```
* x = 4
* ```
*/
class EqualsLiteral extends ComparisonFormula {
EqualsLiteral() {
getSymbol() = "=" and
getAnOperand() instanceof Literal
}
}
/**
* A chain of disjunctions where each operand is an equality comparison between
* the same thing and various `Literal`s. For example:
* ```
* x = 4 or
* x = 5 or
* x = 6
* ```
*/
class DisjunctionEqualsLiteral extends DisjunctionChain {
DisjunctionEqualsLiteral() {
// VarAccess on the same variable
exists(VarDef v |
forex(Formula f | f = getAnOperandRec() |
f.(EqualsLiteral).getAnOperand().(VarAccess).getDeclaration() = v
)
)
or
// FieldAccess on the same variable
exists(VarDecl v |
forex(Formula f | f = getAnOperandRec() |
f.(EqualsLiteral).getAnOperand().(FieldAccess).getDeclaration() = v
)
)
or
// ThisAccess
forex(Formula f | f = getAnOperandRec() |
f.(EqualsLiteral).getAnOperand() instanceof ThisAccess
)
or
// ResultAccess
forex(Formula f | f = getAnOperandRec() |
f.(EqualsLiteral).getAnOperand() instanceof ResultAccess
)
// (in principle something like GlobalValueNumbering could be used to generalize this)
}
}
/**
* A call with a single `Literal` argument. For example:
* ```
* myPredicate(4)
* ```
*/
class CallLiteral extends Call {
CallLiteral() {
getNumberOfArguments() = 1 and
getArgument(0) instanceof Literal
}
}
/**
* A chain of disjunctions where each operand is a call to the same predicate
* using various `Literal`s. For example:
* ```
* myPredicate(4) or
* myPredicate(5) or
* myPredicate(6)
* ```
*/
class DisjunctionPredicateLiteral extends DisjunctionChain {
DisjunctionPredicateLiteral() {
// Call to the same target
exists(PredicateOrBuiltin target |
forex(Formula f | f = getAnOperandRec() | f.(CallLiteral).getTarget() = target)
)
}
}
from DisjunctionChain d, string msg, int c
where
(
d instanceof DisjunctionEqualsLiteral and
msg =
"This formula of " + c.toString() +
" comparisons can be replaced with a single equality on a set literal, improving readability."
or
d instanceof DisjunctionPredicateLiteral and
msg =
"This formula of " + c.toString() +
" predicate calls can be replaced with a single call on a set literal, improving readability."
) and
c = count(d.getAnOperandRec()) and
c >= 4
select d, msg

View File

@@ -0,0 +1,25 @@
/**
* @name Missing QLDoc.
* @description Library classes should have QLDoc.
* @kind problem
* @problem.severity recommendation
* @id ql/missing-qldoc
* @tags maintainability
* @precision high
*/
import ql
from File f, Class c
where
f = c.getLocation().getFile() and
not exists(c.getQLDoc()) and // no QLDoc
f.getExtension() = "qll" and // in a library
not c.isPrivate() and // class is public
not exists(Module m |
m.getAMember*() = c and
m.isPrivate() // modules containing the class are public
) and
not exists(c.getAliasType()) and // class is not just an alias
not f.getParentContainer*().getBaseName().toLowerCase() = ["internal", "experimental", "test"] // exclusions
select c, "This library class should have QLDoc."

View File

@@ -31,3 +31,9 @@ module Aliases {
alias0() // <- works
}
}
module Buildins {
predicate replaceAll(string s) { "foo".replaceAll("foo", "bar") = s }
predicate regexpCapture(string s) { "foo".regexpCapture("\\w", 1) = s }
}

View File

@@ -6,3 +6,5 @@
| Foo.qll:29:5:29:16 | PredicateCall | Foo.qll:26:3:26:32 | ClasslessPredicate alias2 |
| Foo.qll:31:5:31:12 | PredicateCall | Foo.qll:22:3:22:32 | ClasslessPredicate myThing0 |
| Foo.qll:31:5:31:12 | PredicateCall | Foo.qll:24:3:24:32 | ClasslessPredicate alias0 |
| Foo.qll:36:36:36:65 | MemberCall | file://:0:0:0:0 | replaceAll |
| Foo.qll:38:39:38:67 | MemberCall | file://:0:0:0:0 | regexpCapture |

View File

@@ -220,6 +220,8 @@ nodes
| file://:0:0:0:0 | none | semmle.label | [BuiltinPredicate] none |
| file://:0:0:0:0 | pow | semmle.label | [BuiltinPredicate] pow |
| file://:0:0:0:0 | prefix | semmle.label | [BuiltinPredicate] prefix |
| file://:0:0:0:0 | regexpCapture | semmle.label | [BuiltinPredicate] regexpCapture |
| file://:0:0:0:0 | regexpFind | semmle.label | [BuiltinPredicate] regexpFind |
| file://:0:0:0:0 | regexpMatch | semmle.label | [BuiltinPredicate] regexpMatch |
| file://:0:0:0:0 | regexpReplaceAll | semmle.label | [BuiltinPredicate] regexpReplaceAll |
| file://:0:0:0:0 | replaceAll | semmle.label | [BuiltinPredicate] replaceAll |

View File

@@ -0,0 +1,8 @@
| test.qll:4:3:7:7 | Disjunction | This formula of 4 comparisons can be replaced with a single equality on a set literal, improving readability. |
| test.qll:30:3:33:10 | Disjunction | This formula of 4 predicate calls can be replaced with a single call on a set literal, improving readability. |
| test.qll:44:3:47:12 | Disjunction | This formula of 4 comparisons can be replaced with a single equality on a set literal, improving readability. |
| test.qll:62:7:65:14 | Disjunction | This formula of 4 comparisons can be replaced with a single equality on a set literal, improving readability. |
| test.qll:68:7:71:13 | Disjunction | This formula of 4 comparisons can be replaced with a single equality on a set literal, improving readability. |
| test.qll:74:7:77:13 | Disjunction | This formula of 4 comparisons can be replaced with a single equality on a set literal, improving readability. |
| test.qll:87:3:90:9 | Disjunction | This formula of 4 predicate calls can be replaced with a single call on a set literal, improving readability. |
| test.qll:128:3:134:3 | Disjunction | This formula of 4 comparisons can be replaced with a single equality on a set literal, improving readability. |

View File

@@ -0,0 +1 @@
queries/style/UseSetLiteral.ql

View File

@@ -0,0 +1,135 @@
import ql
predicate test1(int a) {
a = 1 or // BAD
a = 2 or
a = 3 or
a = 4
}
predicate test2(int a) {
a = [1, 2, 3, 4] // GOOD
}
predicate test3(int a) {
a = 1 and // GOOD (for the purposes of this query)
a = 2 and
a = 3 and
a = 4
}
bindingset[a]
predicate test4(int a) {
a < 1 or // GOOD (for the purposes of this query)
a = 2 or
a >= 3 or
a > 4
}
predicate test5() {
test1(1) or // BAD
test1(2) or
test1(3) or
test1(4)
}
predicate test6() {
test1(1) or // GOOD
test2(2) or
test3(3) or
test4(4)
}
int test7() {
1 = result or // BAD
2 = result or
3 = result or
4 = result
}
predicate test8() {
test7() = 1 or // BAD [NOT DETECTED]
test7() = 2 or
test7() = 3 or
test7() = 4
}
class MyTest8Class extends int {
string s;
MyTest8Class() {
(
this = 1 or // BAD
this = 2 or
this = 3 or
this = 4
) and
(
s = "1" or // BAD
s = "2" or
s = "3" or
s = "4"
) and
exists(float f |
f = 1.0 or // BAD
f = 1.5 or
f = 2.0 or
f = 2.5
)
}
predicate is(int x) { x = this }
int get() { result = this }
}
predicate test9(MyTest8Class c) {
c.is(1) or // BAD
c.is(2) or
c.is(3) or
c.is(4)
}
predicate test10(MyTest8Class c) {
c.get() = 1 or // BAD [NOT DETECTED]
c.get() = 2 or
c.get() = 3 or
c.get() = 4
}
bindingset[a, b, c, d]
predicate test11(int a, int b, int c, int d) {
a = 1 or // GOOD
b = 2 or
c = 3 or
d = 4
}
bindingset[a, b]
predicate test12(int a, int b) {
a = 1 or // BAD [NOT DETECTED]
a = 2 or
a = 3 or
a = 4 or
b = 0
}
predicate test13(int a, int b) {
a = 1 and b = 1 // GOOD
or
a = 2 and b = 4
or
a = 3 and b = 9
or
a = 4 and b = 16
}
predicate test14(int a) {
a = 1 // BAD
or
(
(a = 2 or a = 3)
or
a = 4
)
}