mirror of
https://github.com/github/codeql.git
synced 2026-08-02 16:32:58 +02:00
900 lines
30 KiB
Plaintext
900 lines
30 KiB
Plaintext
/**
|
|
* Provides classes for working with scopes and declared objects.
|
|
*/
|
|
overlay[local?]
|
|
module;
|
|
|
|
import go
|
|
|
|
/**
|
|
* A scope.
|
|
*/
|
|
class Scope extends @scope {
|
|
/** Gets the enclosing scope of this scope, if any. */
|
|
Scope getOuterScope() { scopenesting(this, result) }
|
|
|
|
/** Gets a scope nested inside this scope. */
|
|
Scope getAnInnerScope() { this = result.getOuterScope() }
|
|
|
|
/** Looks up the entity with the given name in this scope. */
|
|
Entity getEntity(string name) {
|
|
result.getName() = name and
|
|
result.getScope() = this
|
|
}
|
|
|
|
/** Gets a textual representation of this scope. */
|
|
string toString() { result = "scope" }
|
|
}
|
|
|
|
/** Provides helper predicates for working with scopes. */
|
|
module Scope {
|
|
/** Gets the universe scope. */
|
|
UniverseScope universe() { any() }
|
|
}
|
|
|
|
/**
|
|
* The universe scope.
|
|
*/
|
|
class UniverseScope extends @universescope, Scope {
|
|
override string toString() { result = "universe scope" }
|
|
}
|
|
|
|
/** A package scope. */
|
|
class PackageScope extends @packagescope, Scope {
|
|
/** Gets the package whose scope this is. */
|
|
Package getPackage() { this = result.getScope() }
|
|
|
|
override string toString() { result = "package scope" }
|
|
}
|
|
|
|
/** A local scope. */
|
|
class LocalScope extends @localscope, Scope, Locatable {
|
|
/** Gets the AST node inducing this scope. */
|
|
ScopeNode getNode() { this = result.getScope() }
|
|
|
|
/**
|
|
* Gets the function scope in which this scope is nested.
|
|
*
|
|
* For function scopes, this is the scope itself.
|
|
*/
|
|
FunctionScope getEnclosingFunctionScope() {
|
|
result = this.getOuterScope().(LocalScope).getEnclosingFunctionScope()
|
|
}
|
|
|
|
override string toString() { result = "local scope" }
|
|
}
|
|
|
|
/** A local scope induced by a file. */
|
|
class FileScope extends LocalScope {
|
|
FileScope() { this.getNode() instanceof File }
|
|
}
|
|
|
|
/** A local scope induced by a function definition. */
|
|
class FunctionScope extends LocalScope {
|
|
FuncDef f;
|
|
|
|
FunctionScope() { this.getNode() = f.getTypeExpr() }
|
|
|
|
/** Gets the function inducing this scope. */
|
|
FuncDef getFunction() { result = f }
|
|
|
|
override FunctionScope getEnclosingFunctionScope() { result = this }
|
|
|
|
override string toString() { result = "function scope" }
|
|
}
|
|
|
|
/**
|
|
* A declared or built-in entity (that is, package, type, constant, variable, function or label)
|
|
*/
|
|
class Entity extends @object {
|
|
/**
|
|
* Gets the name of this entity.
|
|
*
|
|
* Anonymous entities (such as the receiver variables of interface methods) have the empty string as their name.
|
|
*/
|
|
string getName() { objects(this, _, result) }
|
|
|
|
/** Gets the package in which this entity is declared, if any. */
|
|
Package getPackage() { result.getScope() = this.getScope() }
|
|
|
|
/**
|
|
* Holds if this entity is declared in a package with path `pkg` and has the given `name`.
|
|
*
|
|
* Note that for methods `pkg` is the package path followed by `.` followed
|
|
* by the name of the receiver type, for example `io.Writer`.
|
|
*/
|
|
pragma[nomagic]
|
|
predicate hasQualifiedName(string pkg, string name) {
|
|
(
|
|
pkg = this.getPackage().getPath()
|
|
or
|
|
not exists(this.getPackage()) and pkg = ""
|
|
) and
|
|
name = this.getName()
|
|
}
|
|
|
|
/** Gets the qualified name of this entity, if any. */
|
|
string getQualifiedName() {
|
|
exists(string pkg, string name | this.hasQualifiedName(pkg, name) |
|
|
if pkg = "" then result = name else result = pkg + "." + name
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the scope in which this entity is declared, if any.
|
|
*
|
|
* Entities corresponding to fields and methods do not have a scope.
|
|
*/
|
|
Scope getScope() { objectscopes(this, result) }
|
|
|
|
/**
|
|
* Gets the declaring identifier for this entity, if any.
|
|
*
|
|
* Note that type switch statements which declare a new variable in the guard
|
|
* actually have a new variable (of the right type) implicitly declared at
|
|
* the beginning of each case clause, and these do not have a syntactic
|
|
* declaration.
|
|
*/
|
|
Ident getDeclaration() { result.declares(this) }
|
|
|
|
/** Gets a reference to this entity. */
|
|
Name getAReference() { result.getTarget() = this }
|
|
|
|
/** Gets the type of this entity. */
|
|
Type getType() { objecttypes(this, result) }
|
|
|
|
/** Gets a textual representation of this entity. */
|
|
string toString() { result = this.getName() }
|
|
|
|
/** Gets the location of this entity. */
|
|
Location getLocation() {
|
|
result = this.getDeclaration().getLocation()
|
|
or
|
|
result = any(CaseClause cc | this = cc.getImplicitlyDeclaredVariable()).getLocation()
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED: Use `getLocation()` instead.
|
|
*
|
|
* Holds if this element is at the specified location.
|
|
* The location spans column `startcolumn` of line `startline` to
|
|
* column `endcolumn` of line `endline` in file `filepath`.
|
|
* For more information, see
|
|
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
|
*/
|
|
deprecated predicate hasLocationInfo(
|
|
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
|
) {
|
|
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
|
or
|
|
// otherwise fall back on dummy location
|
|
not exists(this.getLocation()) and
|
|
filepath = "" and
|
|
startline = 0 and
|
|
startcolumn = 0 and
|
|
endline = 0 and
|
|
endcolumn = 0
|
|
}
|
|
}
|
|
|
|
/** A declared entity (that is, type, constant, variable or function). */
|
|
class DeclaredEntity extends Entity, @declobject {
|
|
/** Gets the expression to which this entity is initialized, if any. */
|
|
Expr getInit() {
|
|
exists(ValueSpec spec, int i |
|
|
spec.getNameExpr(i) = this.getDeclaration() and
|
|
spec.getInit(i) = result
|
|
)
|
|
}
|
|
}
|
|
|
|
/** A built-in entity (that is, type, constant or function). */
|
|
class BuiltinEntity extends Entity, @builtinobject { }
|
|
|
|
/** An imported package. */
|
|
class PackageEntity extends Entity, @pkgobject { }
|
|
|
|
/**
|
|
* A named type: predeclared types, defined types, type parameters, and type
|
|
* aliases.
|
|
*/
|
|
class TypeEntity extends Entity, @typeobject { }
|
|
|
|
/** The parent of a type parameter type, either a declared type or a declared function. */
|
|
class TypeParamParentEntity extends Entity, @typeparamparentobject { }
|
|
|
|
/** A named type which has a declaration. */
|
|
class DeclaredTypeEntity extends TypeEntity, DeclaredEntity, TypeParamParentEntity, @decltypeobject {
|
|
/** Gets the declaration specifier declaring this type. */
|
|
TypeSpec getSpec() { result.getNameExpr() = this.getDeclaration() }
|
|
}
|
|
|
|
/** DEPRECATED: Use `DeclaredTypeEntity` instead. */
|
|
deprecated class DeclaredType = DeclaredTypeEntity;
|
|
|
|
/** A built-in type. */
|
|
class BuiltinTypeEntity extends TypeEntity, BuiltinEntity, @builtintypeobject { }
|
|
|
|
/** DEPRECATED: Use `BuiltinTypeEntity` instead. */
|
|
deprecated class BuiltinType = BuiltinTypeEntity;
|
|
|
|
/** A built-in or declared constant, variable, field, method or function. */
|
|
class ValueEntity extends Entity, @valueobject {
|
|
/** Gets a data-flow node that reads the value of this entity. */
|
|
Read getARead() { result.reads(this) }
|
|
|
|
/** Gets a control-flow node that updates the value of this entity. */
|
|
Write getAWrite() { result.writes(this, _) }
|
|
}
|
|
|
|
/** A built-in or declared constant. */
|
|
class Constant extends ValueEntity, @constobject { }
|
|
|
|
/** A declared constant. */
|
|
class DeclaredConstant extends Constant, DeclaredEntity, @declconstobject {
|
|
/** Gets the declaration specifier declaring this constant. */
|
|
ValueSpec getSpec() { result.getANameExpr() = this.getDeclaration() }
|
|
}
|
|
|
|
/** A built-in constant. */
|
|
class BuiltinConstant extends Constant, BuiltinEntity, @builtinconstobject { }
|
|
|
|
/**
|
|
* A built-in or declared variable.
|
|
*
|
|
* Note that Go currently does not have any built-in variables, so this class is effectively
|
|
* an alias for `DeclaredVariable`.
|
|
*/
|
|
class Variable extends ValueEntity, @varobject { }
|
|
|
|
/** A declared variable. */
|
|
class DeclaredVariable extends Variable, DeclaredEntity, @declvarobject {
|
|
/** Gets the declaration specifier declaring this variable. */
|
|
ValueSpec getSpec() { result.getANameExpr() = this.getDeclaration() }
|
|
}
|
|
|
|
/** A variable declared in a local scope (as opposed to a package scope or the universal scope). */
|
|
class LocalVariable extends DeclaredVariable {
|
|
LocalVariable() { this.getScope() instanceof LocalScope }
|
|
|
|
/** Gets the innermost function containing the scope of this variable, if any. */
|
|
FuncDef getDeclaringFunction() {
|
|
result = this.getScope().(LocalScope).getEnclosingFunctionScope().getFunction()
|
|
}
|
|
|
|
/** Holds if this variable is referenced inside a nested function. */
|
|
predicate isCaptured() {
|
|
this.getDeclaringFunction() != this.getAReference().getEnclosingFunction()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A (named) function parameter.
|
|
*
|
|
* Note that receiver variables are considered parameters.
|
|
*/
|
|
class Parameter extends DeclaredVariable {
|
|
FuncDef f;
|
|
int index;
|
|
|
|
Parameter() {
|
|
f.(MethodDecl).getReceiverDecl().getNameExpr() = this.getDeclaration() and
|
|
index = -1
|
|
or
|
|
exists(FuncTypeExpr tp | tp = f.getTypeExpr() |
|
|
this =
|
|
rank[index + 1](DeclaredVariable parm, int j, int k |
|
|
parm.getDeclaration() = tp.getParameterDecl(j).getNameExpr(k)
|
|
|
|
|
parm order by j, k
|
|
)
|
|
)
|
|
}
|
|
|
|
/** Gets the function to which this parameter belongs. */
|
|
FuncDef getFunction() { result = f }
|
|
|
|
/**
|
|
* Gets the index of this parameter among all parameters of the function.
|
|
*
|
|
* The receiver is considered to have index -1.
|
|
*/
|
|
int getIndex() { result = index }
|
|
|
|
/** Holds if this is the `i`th parameter of function `fd`. */
|
|
predicate isParameterOf(FuncDef fd, int i) { fd = f and i = index }
|
|
}
|
|
|
|
/** The receiver variable of a method. */
|
|
class ReceiverVariable extends Parameter {
|
|
override MethodDecl f;
|
|
|
|
ReceiverVariable() { index = -1 }
|
|
|
|
/** Holds if this is the receiver variable of method `m`. */
|
|
predicate isReceiverOf(MethodDecl m) { m = f }
|
|
}
|
|
|
|
/** A (named) function result variable. */
|
|
class ResultVariable extends DeclaredVariable {
|
|
FuncDef f;
|
|
int index;
|
|
|
|
ResultVariable() {
|
|
exists(FuncTypeExpr tp | tp = f.getTypeExpr() |
|
|
this =
|
|
rank[index + 1](DeclaredVariable parm, int j, int k |
|
|
parm.getDeclaration() = tp.getResultDecl(j).getNameExpr(k)
|
|
|
|
|
parm order by j, k
|
|
)
|
|
)
|
|
}
|
|
|
|
/** Gets the function to which this result variable belongs. */
|
|
FuncDef getFunction() { result = f }
|
|
|
|
/** Gets the index of this result among all results of the function. */
|
|
int getIndex() { result = index }
|
|
|
|
/** Holds if this is the `i`th result of function `fd`. */
|
|
predicate isResultOf(FuncDef fd, int i) { fd = f and i = index }
|
|
}
|
|
|
|
/**
|
|
* A struct field.
|
|
*
|
|
* Note that field identity is determined by type identity: if two struct types are identical in
|
|
* the sense of the Go language specification (https://golang.org/ref/spec#Type_identity), then
|
|
* any of their fields that have the same name are also identical. This, in turn, means that a
|
|
* field can have two or more declarations.
|
|
*
|
|
* For example, consider the following two type declarations:
|
|
*
|
|
* ```go
|
|
* type T1 struct { x int }
|
|
* type T2 struct { x int }
|
|
* ```
|
|
*
|
|
* Types `T1` and `T2` are different, but their underlying struct types are identical. Hence
|
|
* the two declarations of `x` refer to the same field.
|
|
*/
|
|
class Field extends Variable {
|
|
StructType declaringType;
|
|
|
|
Field() { fieldstructs(this, declaringType) }
|
|
|
|
/** Gets the struct type declaring this field. */
|
|
StructType getDeclaringType() { result = declaringType }
|
|
|
|
override Package getPackage() {
|
|
exists(Type tp | tp.getUnderlyingType() = declaringType | result = tp.getPackage())
|
|
}
|
|
|
|
/**
|
|
* Holds if this field has name `f` and it belongs to a type with qualified name `tp`.
|
|
*
|
|
* Note that due to field embedding the same field may have multiple qualified names.
|
|
*/
|
|
override predicate hasQualifiedName(string tp, string f) {
|
|
exists(Type base |
|
|
tp = base.getQualifiedName() and
|
|
this = base.getField(f)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if this field has name `f` and it belongs to a type `tp` declared in package `pkg`.
|
|
*
|
|
* Note that due to field embedding the same field may belong to multiple types.
|
|
*/
|
|
predicate hasQualifiedName(string pkg, string tp, string f) {
|
|
exists(Type base |
|
|
base.hasQualifiedName(pkg, tp) and
|
|
this = base.getField(f)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the tag associated with this field, or the empty string if this field has no tag.
|
|
*/
|
|
string getTag() { declaringType.hasOwnFieldWithTag(_, this.getName(), this.getType(), _, result) }
|
|
}
|
|
|
|
/**
|
|
* A field that belongs to a struct that may be embedded within another struct.
|
|
*
|
|
* When a selector addresses such a field, it is possible it is implicitly addressing a nested struct.
|
|
*/
|
|
class PromotedField extends Field {
|
|
PromotedField() { this = any(StructType t).getFieldOfEmbedded(_, _, _, _) }
|
|
}
|
|
|
|
/** A built-in or declared function. */
|
|
class Function extends ValueEntity, @functionobject {
|
|
/**
|
|
* Gets a call to this function.
|
|
*
|
|
* This includes calls that target this function indirectly, by calling an
|
|
* interface method that this function implements.
|
|
*/
|
|
overlay[global]
|
|
pragma[nomagic]
|
|
DataFlow::CallNode getACall() { this = result.getACalleeIncludingExternals().asFunction() }
|
|
|
|
/** Gets the declaration of this function, if any. */
|
|
FuncDecl getFuncDecl() { none() }
|
|
|
|
/** Holds if this function is variadic. */
|
|
predicate isVariadic() { none() }
|
|
|
|
/** Holds if this function has no observable side effects. */
|
|
predicate mayHaveSideEffects() { none() }
|
|
|
|
/**
|
|
* Holds if this function may return without panicking, exiting the process, or looping forever.
|
|
*
|
|
* This predicate is an over-approximation: it may hold for functions that can never
|
|
* return normally, but it never fails to hold for functions that can.
|
|
*
|
|
* Note this is declared here and not in `DeclaredFunction` so that library models can override this
|
|
* by extending `Function` rather than having to remember to extend `DeclaredFunction`.
|
|
*/
|
|
predicate mayReturnNormally() {
|
|
not this.mustPanic() and
|
|
(ControlFlow::mayReturnNormally(this.getFuncDecl()) or not exists(this.getBody()))
|
|
}
|
|
|
|
/**
|
|
* Holds if calling this function may cause a runtime panic.
|
|
*
|
|
* This predicate is an over-approximation: it may hold for functions that can never
|
|
* cause a runtime panic, but it never fails to hold for functions that can.
|
|
*/
|
|
predicate mayPanic() { any() }
|
|
|
|
/**
|
|
* Holds if calling this function always causes a runtime panic.
|
|
*
|
|
* This predicate is an over-approximation: it may not hold for functions that do
|
|
* cause a runtime panic, but it never holds for functions that do not.
|
|
*/
|
|
predicate mustPanic() { none() }
|
|
|
|
/** Gets the number of parameters of this function. */
|
|
int getNumParameter() { result = this.getType().(SignatureType).getNumParameter() }
|
|
|
|
/** Gets the type of the `i`th parameter of this function. */
|
|
Type getParameterType(int i) { result = this.getType().(SignatureType).getParameterType(i) }
|
|
|
|
/** Gets the number of results of this function. */
|
|
int getNumResult() { result = this.getType().(SignatureType).getNumResult() }
|
|
|
|
/** Gets the type of the `i`th result of this function. */
|
|
Type getResultType(int i) { result = this.getType().(SignatureType).getResultType(i) }
|
|
|
|
/** Gets the body of this function, if any. */
|
|
BlockStmt getBody() { result = this.getFuncDecl().getBody() }
|
|
|
|
/** Gets the `i`th parameter of this function. */
|
|
Parameter getParameter(int i) { result.isParameterOf(this.getFuncDecl(), i) }
|
|
|
|
/** Gets a parameter of this function. */
|
|
Parameter getAParameter() { result = this.getParameter(_) }
|
|
|
|
/** Gets the `i`th result variable of this function. */
|
|
ResultVariable getResult(int i) { result.isResultOf(this.getFuncDecl(), i) }
|
|
|
|
/** Gets a result variable of this function. */
|
|
ResultVariable getAResult() { result = this.getResult(_) }
|
|
}
|
|
|
|
bindingset[m]
|
|
pragma[inline_late]
|
|
private Type implementsIncludingInterfaceMethodsCand(Method m, string mname) {
|
|
result.implements(m.getReceiverType().getUnderlyingType()) and
|
|
mname = m.getName()
|
|
}
|
|
|
|
/**
|
|
* A method, that is, a function with a receiver variable, or a function declared in an interface.
|
|
*
|
|
* Note that method identity is determined by receiver type identity: if two methods have the same
|
|
* name and their receiver types are identical in the sense of the Go language specification
|
|
* (https://golang.org/ref/spec#Type_identity), then the two methods are identical as well.
|
|
*/
|
|
class Method extends Function {
|
|
Variable receiver;
|
|
|
|
Method() { methodreceivers(this, receiver) }
|
|
|
|
override Package getPackage() {
|
|
// a method doesn't have a scope, so manually associate it with its receiver's
|
|
// package.
|
|
result = this.getReceiverType().getPackage()
|
|
}
|
|
|
|
/** Holds if this method is declared in an interface. */
|
|
predicate isInterfaceMethod() {
|
|
this.getReceiverType().getUnderlyingType() instanceof InterfaceType
|
|
}
|
|
|
|
/** Gets the receiver variable of this method. */
|
|
Variable getReceiver() { result = receiver }
|
|
|
|
/** Gets the type of the receiver variable of this method. */
|
|
Type getReceiverType() { result = receiver.getType() }
|
|
|
|
/**
|
|
* Gets the receiver base type of this method, that is, either the base type of the receiver type
|
|
* if it is a pointer type, or the receiver type itself if it is not a pointer type.
|
|
*/
|
|
Type getReceiverBaseType() { result = lookThroughPointerType(this.getReceiverType()) }
|
|
|
|
/** Holds if this method has name `m` and belongs to the method set of type `tp` or `*tp`. */
|
|
private predicate isIn(DefinedType tp, string m) {
|
|
this = tp.getMethod(m) or
|
|
this = tp.getPointerType().getMethod(m)
|
|
}
|
|
|
|
/**
|
|
* Holds if this method has name `m` and belongs to the method set of a type `T` or `*T` where
|
|
* `T` has qualified name `tp`.
|
|
*
|
|
* Note that `meth.hasQualifiedName(tp, m)` is almost, but not quite, equivalent to
|
|
* `exists(Type t | tp = t.getQualifiedName() and meth = t.getMethod(m))`: the latter
|
|
* distinguishes between the method sets of `T` and `*T`, while the former does not.
|
|
*/
|
|
override predicate hasQualifiedName(string tp, string m) {
|
|
exists(DefinedType t |
|
|
this.isIn(t, m) and
|
|
tp = t.getQualifiedName()
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if this method has name `m` and belongs to the method set of a type `T` or `*T` where
|
|
* `T` is declared in package `pkg` and has name `tp`.
|
|
*
|
|
* Note that `meth.hasQualifiedName(pkg, tp, m)` is almost, but not quite, equivalent to
|
|
* `exists(Type t | t.hasQualifiedName(pkg, tp) and meth = t.getMethod(m))`: the latter
|
|
* distinguishes between the method sets of `T` and `*T`, while the former does not.
|
|
*/
|
|
pragma[nomagic]
|
|
predicate hasQualifiedName(string pkg, string tp, string m) {
|
|
exists(DefinedType t |
|
|
this.isIn(t, m) and
|
|
t.hasQualifiedName(pkg, tp)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if this method implements the method `m`, that is, if `m` is a method
|
|
* on an interface, and this is a method with the same name on a type that
|
|
* implements that interface.
|
|
*
|
|
* Note that all methods implement themselves, and interface methods _only_
|
|
* implement themselves.
|
|
*/
|
|
predicate implements(Method m) {
|
|
if this.isInterfaceMethod() then this = m else this.implementsIncludingInterfaceMethods(m)
|
|
}
|
|
|
|
/**
|
|
* Holds if this method implements the method `m`, that is, if `m` is a method
|
|
* on an interface, and this is a method with the same name on a type that
|
|
* implements that interface.
|
|
*
|
|
* Note that all methods implement themselves, and that unlike the predicate `implements`
|
|
* this does allow interface methods to implement other interfaces.
|
|
*/
|
|
predicate implementsIncludingInterfaceMethods(Method m) {
|
|
this = m
|
|
or
|
|
exists(Type t, string mname |
|
|
t = implementsIncludingInterfaceMethodsCand(m, mname) and
|
|
this = t.getMethod(mname)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if this method implements the method that has qualified name `pkg.tp.name`, that is, if
|
|
* `pkg.tp.name` is a method on an interface, and this is a method with the same name on a type
|
|
* that implements that interface.
|
|
*/
|
|
predicate implements(string pkg, string tp, string name) {
|
|
exists(Method m | m.hasQualifiedName(pkg, tp, name) | this.implements(m))
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A method whose receiver may be embedded within a struct.
|
|
*
|
|
* When a selector addresses such a method, it is possible it is implicitly addressing a nested struct.
|
|
*/
|
|
class PromotedMethod extends Method {
|
|
PromotedMethod() { this = any(StructType t).getMethodOfEmbedded(_, _, _) }
|
|
}
|
|
|
|
/** A declared function. */
|
|
class DeclaredFunction extends Function, DeclaredEntity, TypeParamParentEntity, @declfunctionobject {
|
|
override FuncDecl getFuncDecl() { result.getNameExpr() = this.getDeclaration() }
|
|
|
|
override predicate mayHaveSideEffects() {
|
|
not exists(this.getBody())
|
|
or
|
|
exists(BlockStmt body | body = this.getBody() |
|
|
body.mayHaveSideEffects()
|
|
or
|
|
// functions declared in files with build constraints may be defined differently
|
|
// for different platforms, so allow them to avoid false positives
|
|
body.getFile().hasBuildConstraints()
|
|
)
|
|
}
|
|
|
|
override predicate isVariadic() { this.getType().(SignatureType).isVariadic() }
|
|
}
|
|
|
|
/** A built-in function. */
|
|
class BuiltinFunction extends Function, BuiltinEntity, @builtinfunctionobject {
|
|
override predicate mayHaveSideEffects() { builtinFunction(this.getName(), false, _, _, _) }
|
|
|
|
override predicate mayPanic() { builtinFunction(this.getName(), _, true, _, _) }
|
|
|
|
override predicate mustPanic() { builtinFunction(this.getName(), _, _, true, _) }
|
|
|
|
override predicate isVariadic() { builtinFunction(this.getName(), _, _, _, true) }
|
|
|
|
/**
|
|
* Holds if this function is pure, that is, it has no observable side effects and
|
|
* no non-determinism.
|
|
*/
|
|
predicate isPure() { not this.mayHaveSideEffects() }
|
|
}
|
|
|
|
private newtype TCallable =
|
|
TFunctionCallable(Function f) or
|
|
TFuncLitCallable(FuncLit l)
|
|
|
|
/**
|
|
* A `Function` or a `FuncLit`. We do it this way because of limitations of both
|
|
* `Function` and `FuncDef`:
|
|
* - `Function` is an entity, and therefore does not include function literals, and
|
|
* - `FuncDef` is an AST node, and so is not extracted for functions from external libraries.
|
|
*/
|
|
class Callable extends TCallable {
|
|
/** Gets a textual representation of this callable. */
|
|
string toString() { result = [this.asFunction().toString(), this.asFuncLit().toString()] }
|
|
|
|
/** Gets this callable as a function, if it is one. */
|
|
Function asFunction() { this = TFunctionCallable(result) }
|
|
|
|
/** Gets this callable as a function literal, if it is one. */
|
|
FuncLit asFuncLit() { this = TFuncLitCallable(result) }
|
|
|
|
/** Gets this function's definition, if it exists. */
|
|
FuncDef getFuncDef() { result = [this.asFuncLit().(FuncDef), this.asFunction().getFuncDecl()] }
|
|
|
|
/** Gets the type of this callable. */
|
|
SignatureType getType() {
|
|
result = this.asFunction().getType() or
|
|
result = this.asFuncLit().getType()
|
|
}
|
|
|
|
/** Gets the name of this callable. */
|
|
string getName() {
|
|
result = this.asFunction().getName() or
|
|
result = this.asFuncLit().getName()
|
|
}
|
|
|
|
/** Gets the location of this callable. */
|
|
Location getLocation() {
|
|
result = this.asFunction().getLocation() or result = this.asFuncLit().getLocation()
|
|
}
|
|
|
|
/**
|
|
* DEPRECATED: Use `getLocation()` instead.
|
|
*
|
|
* Holds if this element is at the specified location.
|
|
* The location spans column `sc` of line `sl` to
|
|
* column `ec` of line `el` in file `fp`.
|
|
* For more information, see
|
|
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
|
*/
|
|
deprecated predicate hasLocationInfo(string fp, int sl, int sc, int el, int ec) {
|
|
this.getLocation().hasLocationInfo(fp, sl, sc, el, ec)
|
|
}
|
|
}
|
|
|
|
/** A statement label. */
|
|
class Label extends Entity, @labelobject { }
|
|
|
|
/**
|
|
* Holds if `name` is a built-in function, where
|
|
*
|
|
* - `pure` is true if the function has no observable side effects, and false otherwise;
|
|
* - `mayPanic` is true if calling this function may cause a panic, and false otherwise;
|
|
* - `mustPanic` is true if calling this function always causes a panic, and false otherwise;
|
|
* - `variadic` is true if this function is variadic, and false otherwise.
|
|
*
|
|
* Allocating memory is not considered an observable side effect.
|
|
*/
|
|
private predicate builtinFunction(
|
|
string name, boolean pure, boolean mayPanic, boolean mustPanic, boolean variadic
|
|
) {
|
|
name = "append" and pure = false and mayPanic = false and mustPanic = false and variadic = true
|
|
or
|
|
name = "cap" and pure = true and mayPanic = false and mustPanic = false and variadic = false
|
|
or
|
|
name = "clear" and pure = false and mayPanic = false and mustPanic = false and variadic = false
|
|
or
|
|
name = "close" and pure = false and mayPanic = true and mustPanic = false and variadic = false
|
|
or
|
|
name = "complex" and pure = true and mayPanic = true and mustPanic = false and variadic = false
|
|
or
|
|
name = "copy" and pure = false and mayPanic = true and mustPanic = false and variadic = false
|
|
or
|
|
name = "delete" and pure = false and mayPanic = false and mustPanic = false and variadic = false
|
|
or
|
|
name = "imag" and pure = true and mayPanic = false and mustPanic = false and variadic = false
|
|
or
|
|
name = "len" and pure = true and mayPanic = false and mustPanic = false and variadic = false
|
|
or
|
|
name = "make" and pure = true and mayPanic = true and mustPanic = false and variadic = true
|
|
or
|
|
name = "max" and pure = true and mayPanic = false and mustPanic = false and variadic = true
|
|
or
|
|
name = "min" and pure = true and mayPanic = false and mustPanic = false and variadic = true
|
|
or
|
|
name = "new" and pure = true and mayPanic = false and mustPanic = false and variadic = false
|
|
or
|
|
name = "panic" and pure = false and mayPanic = true and mustPanic = true and variadic = false
|
|
or
|
|
name = "print" and pure = false and mayPanic = false and mustPanic = false and variadic = true
|
|
or
|
|
name = "println" and pure = false and mayPanic = false and mustPanic = false and variadic = true
|
|
or
|
|
name = "real" and pure = true and mayPanic = false and mustPanic = false and variadic = false
|
|
or
|
|
name = "recover" and pure = false and mayPanic = false and mustPanic = false and variadic = false
|
|
}
|
|
|
|
/** Provides helper predicates for working with built-in objects from the universe scope. */
|
|
module Builtin {
|
|
// built-in types
|
|
/** Gets the built-in type `bool`. */
|
|
BuiltinTypeEntity bool() { result.getName() = "bool" }
|
|
|
|
/** Gets the built-in type `byte`. */
|
|
BuiltinTypeEntity byte() { result.getName() = "byte" }
|
|
|
|
/** Gets the built-in type `complex64`. */
|
|
BuiltinTypeEntity complex64() { result.getName() = "complex64" }
|
|
|
|
/** Gets the built-in type `complex128`. */
|
|
BuiltinTypeEntity complex128() { result.getName() = "complex128" }
|
|
|
|
/** Gets the built-in type `error`. */
|
|
BuiltinTypeEntity error() { result.getName() = "error" }
|
|
|
|
/** Gets the built-in type `float32`. */
|
|
BuiltinTypeEntity float32() { result.getName() = "float32" }
|
|
|
|
/** Gets the built-in type `float64`. */
|
|
BuiltinTypeEntity float64() { result.getName() = "float64" }
|
|
|
|
/** Gets the built-in type `int`. */
|
|
BuiltinTypeEntity int_() { result.getName() = "int" }
|
|
|
|
/** Gets the built-in type `int8`. */
|
|
BuiltinTypeEntity int8() { result.getName() = "int8" }
|
|
|
|
/** Gets the built-in type `int16`. */
|
|
BuiltinTypeEntity int16() { result.getName() = "int16" }
|
|
|
|
/** Gets the built-in type `int32`. */
|
|
BuiltinTypeEntity int32() { result.getName() = "int32" }
|
|
|
|
/** Gets the built-in type `int64`. */
|
|
BuiltinTypeEntity int64() { result.getName() = "int64" }
|
|
|
|
/** Gets the built-in type `rune`. */
|
|
BuiltinTypeEntity rune() { result.getName() = "rune" }
|
|
|
|
/** Gets the built-in type `string`. */
|
|
BuiltinTypeEntity string_() { result.getName() = "string" }
|
|
|
|
/** Gets the built-in type `uint`. */
|
|
BuiltinTypeEntity uint() { result.getName() = "uint" }
|
|
|
|
/** Gets the built-in type `uint8`. */
|
|
BuiltinTypeEntity uint8() { result.getName() = "uint8" }
|
|
|
|
/** Gets the built-in type `uint16`. */
|
|
BuiltinTypeEntity uint16() { result.getName() = "uint16" }
|
|
|
|
/** Gets the built-in type `uint32`. */
|
|
BuiltinTypeEntity uint32() { result.getName() = "uint32" }
|
|
|
|
/** Gets the built-in type `uint64`. */
|
|
BuiltinTypeEntity uint64() { result.getName() = "uint64" }
|
|
|
|
/** Gets the built-in type `uintptr`. */
|
|
BuiltinTypeEntity uintptr() { result.getName() = "uintptr" }
|
|
|
|
// built-in constants
|
|
/** Gets the built-in constant `true`. */
|
|
BuiltinConstant true_() { result.getName() = "true" }
|
|
|
|
/** Gets the built-in constant `false`. */
|
|
BuiltinConstant false_() { result.getName() = "false" }
|
|
|
|
/** Gets the built-in constant corresponding to `b`. */
|
|
BuiltinConstant bool(boolean b) {
|
|
b = true and result = true_()
|
|
or
|
|
b = false and result = false_()
|
|
}
|
|
|
|
/** Gets the built-in constant `iota`. */
|
|
BuiltinConstant iota() { result.getName() = "iota" }
|
|
|
|
// built-in zero value
|
|
/** Gets the built-in zero-value `nil`. */
|
|
BuiltinConstant nil() { result.getName() = "nil" }
|
|
|
|
/** Gets the built-in function `append`. */
|
|
BuiltinFunction append() { result.getName() = "append" }
|
|
|
|
/** Gets the built-in function `cap`. */
|
|
BuiltinFunction cap() { result.getName() = "cap" }
|
|
|
|
/** Gets the built-in function `clear`. */
|
|
BuiltinFunction clear() { result.getName() = "clear" }
|
|
|
|
/** Gets the built-in function `close`. */
|
|
BuiltinFunction close() { result.getName() = "close" }
|
|
|
|
/** Gets the built-in function `complex`. */
|
|
BuiltinFunction complex() { result.getName() = "complex" }
|
|
|
|
/** Gets the built-in function `copy`. */
|
|
BuiltinFunction copy() { result.getName() = "copy" }
|
|
|
|
/** Gets the built-in function `delete`. */
|
|
BuiltinFunction delete() { result.getName() = "delete" }
|
|
|
|
/** Gets the built-in function `imag`. */
|
|
BuiltinFunction imag() { result.getName() = "imag" }
|
|
|
|
/** Gets the built-in function `len`. */
|
|
BuiltinFunction len() { result.getName() = "len" }
|
|
|
|
/** Gets the built-in function `make`. */
|
|
BuiltinFunction make() { result.getName() = "make" }
|
|
|
|
/** Gets the built-in function `max`. */
|
|
BuiltinFunction max_() { result.getName() = "max" }
|
|
|
|
/** Gets the built-in function `min`. */
|
|
BuiltinFunction min_() { result.getName() = "min" }
|
|
|
|
/** Gets the built-in function `new`. */
|
|
BuiltinFunction new() { result.getName() = "new" }
|
|
|
|
/** Gets the built-in function `panic`. */
|
|
BuiltinFunction panic() { result.getName() = "panic" }
|
|
|
|
/** Gets the built-in function `print`. */
|
|
BuiltinFunction print() { result.getName() = "print" }
|
|
|
|
/** Gets the built-in function `println`. */
|
|
BuiltinFunction println() { result.getName() = "println" }
|
|
|
|
/** Gets the built-in function `real`. */
|
|
BuiltinFunction real() { result.getName() = "real" }
|
|
|
|
/** Gets the built-in function `recover`. */
|
|
BuiltinFunction recover() { result.getName() = "recover" }
|
|
}
|