mirror of
https://github.com/github/codeql.git
synced 2026-04-25 08:45:14 +02:00
QL: Merge branch 'main' into tausbn/update-extractor-generator
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
name: "CodeQL"
|
||||
name: "CodeQL with bleeding edge queries and extractor"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
53
.github/workflows/published-codeql-analysis.yml
vendored
Normal file
53
.github/workflows/published-codeql-analysis.yml
vendored
Normal 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
|
||||
1
ql/consistency-queries/BuildinsConsistency.ql
Normal file
1
ql/consistency-queries/BuildinsConsistency.ql
Normal file
@@ -0,0 +1 @@
|
||||
import codeql_ql.ast.internal.Builtins::BuildinsConsistency
|
||||
@@ -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. */
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
30
ql/src/queries/performance/NonInitialStdLibImport.ql
Normal file
30
ql/src/queries/performance/NonInitialStdLibImport.ql
Normal 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"
|
||||
129
ql/src/queries/style/UseSetLiteral.ql
Normal file
129
ql/src/queries/style/UseSetLiteral.ql
Normal 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
|
||||
25
ql/src/queries/style/docs/MissingQLDoc.ql
Normal file
25
ql/src/queries/style/docs/MissingQLDoc.ql
Normal 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."
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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. |
|
||||
1
ql/test/queries/style/UseSetLiteral/UseSetLiteral.qlref
Normal file
1
ql/test/queries/style/UseSetLiteral/UseSetLiteral.qlref
Normal file
@@ -0,0 +1 @@
|
||||
queries/style/UseSetLiteral.ql
|
||||
135
ql/test/queries/style/UseSetLiteral/test.qll
Normal file
135
ql/test/queries/style/UseSetLiteral/test.qll
Normal 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
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user