mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
1073 lines
37 KiB
Plaintext
1073 lines
37 KiB
Plaintext
/**
|
|
* Public API for "objects"
|
|
* A `Value` is a static approximation to a set of runtime objects.
|
|
*/
|
|
|
|
import python
|
|
private import TObject
|
|
private import semmle.python.objects.ObjectInternal
|
|
private import semmle.python.pointsto.PointsTo
|
|
private import semmle.python.pointsto.PointsToContext
|
|
private import semmle.python.pointsto.MRO
|
|
private import semmle.python.types.Builtins
|
|
|
|
/*
|
|
* Use the term `ObjectSource` to refer to DB entity. Either a CFG node
|
|
* for Python objects, or `@py_cobject` entity for built-in objects.
|
|
*/
|
|
|
|
class ObjectSource = Object;
|
|
|
|
/** An alias for Function used for scopes */
|
|
class FunctionScope = Function;
|
|
|
|
class ClassScope = Class;
|
|
|
|
class ModuleScope = Module;
|
|
|
|
/**
|
|
* A value in the Python program.
|
|
* Each `Value` is a static approximation to a set of one or more real objects.
|
|
*/
|
|
class Value extends TObject {
|
|
Value() {
|
|
this != ObjectInternal::unknown() and
|
|
this != ObjectInternal::unknownClass() and
|
|
this != ObjectInternal::undefined()
|
|
}
|
|
|
|
/** Gets a textual representation of this element. */
|
|
string toString() { result = this.(ObjectInternal).toString() }
|
|
|
|
/** Gets a `ControlFlowNode` that refers to this object. */
|
|
ControlFlowNode getAReference() { PointsToInternal::pointsTo(result, _, this, _) }
|
|
|
|
/** Gets the origin CFG node for this value. */
|
|
ControlFlowNode getOrigin() { result = this.(ObjectInternal).getOrigin() }
|
|
|
|
/**
|
|
* Gets the class of this object.
|
|
* Strictly, the `Value` representing the class of the objects
|
|
* represented by this Value.
|
|
*/
|
|
ClassValue getClass() { result = this.(ObjectInternal).getClass() }
|
|
|
|
/** Gets a call to this object */
|
|
CallNode getACall() { result = this.getACall(_) }
|
|
|
|
/** Gets a call to this object with the given `caller` context. */
|
|
CallNode getACall(PointsToContext caller) {
|
|
PointsToInternal::pointsTo(result.getFunction(), caller, this, _)
|
|
or
|
|
exists(BoundMethodObjectInternal bm |
|
|
PointsToInternal::pointsTo(result.getFunction(), caller, bm, _) and
|
|
bm.getFunction() = this
|
|
)
|
|
}
|
|
|
|
/** Gets a `Value` that represents the attribute `name` of this object. */
|
|
Value attr(string name) { this.(ObjectInternal).attribute(name, result, _) }
|
|
|
|
/**
|
|
* Holds if this value is builtin. Applies to built-in functions and methods,
|
|
* but also integers and strings.
|
|
*/
|
|
predicate isBuiltin() { this.(ObjectInternal).isBuiltin() }
|
|
|
|
/**
|
|
* 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/).
|
|
*/
|
|
predicate hasLocationInfo(
|
|
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
|
) {
|
|
this.(ObjectInternal)
|
|
.getOrigin()
|
|
.getLocation()
|
|
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
|
or
|
|
not exists(this.(ObjectInternal).getOrigin()) and
|
|
filepath = "" and
|
|
startline = 0 and
|
|
startcolumn = 0 and
|
|
endline = 0 and
|
|
endcolumn = 0
|
|
}
|
|
|
|
/**
|
|
* Gets the name of this value, if it has one.
|
|
* Note this is the innate name of the
|
|
* object, not necessarily all the names by which it can be called.
|
|
*/
|
|
final string getName() { result = this.(ObjectInternal).getName() }
|
|
|
|
/** Holds if this value has the attribute `name` */
|
|
predicate hasAttribute(string name) { this.(ObjectInternal).hasAttribute(name) }
|
|
|
|
/** Whether this value is absent from the database, but has been inferred to likely exist */
|
|
predicate isAbsent() {
|
|
this instanceof AbsentModuleObjectInternal
|
|
or
|
|
this instanceof AbsentModuleAttributeObjectInternal
|
|
}
|
|
|
|
/**
|
|
* Whether this overrides v. In this context, "overrides" means that this object
|
|
* is a named attribute of a some class C and `v` is a named attribute of another
|
|
* class S, both attributes having the same name, and S is a super class of C.
|
|
*/
|
|
predicate overrides(Value v) {
|
|
exists(ClassValue my_class, ClassValue other_class, string name |
|
|
my_class.declaredAttribute(name) = this and
|
|
other_class.declaredAttribute(name) = v and
|
|
my_class.getABaseType+() = other_class
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the boolean interpretation of this value.
|
|
* Could be both `true` and `false`, if we can't determine the result more precisely.
|
|
*/
|
|
boolean getABooleanValue() { result = this.(ObjectInternal).booleanValue() }
|
|
|
|
/**
|
|
* Gets the boolean interpretation of this value, only if we can determine the result precisely.
|
|
* The result can be `none()`, but never both `true` and `false`.
|
|
*/
|
|
boolean getDefiniteBooleanValue() {
|
|
result = this.getABooleanValue() and
|
|
not (this.getABooleanValue() = true and this.getABooleanValue() = false)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A module in the Python program.
|
|
* Each `ModuleValue` represents a module object in the Python program.
|
|
*/
|
|
class ModuleValue extends Value instanceof ModuleObjectInternal {
|
|
/**
|
|
* Holds if this module "exports" name.
|
|
* That is, does it define `name` in `__all__` or is
|
|
* `__all__` not defined and `name` a global variable that does not start with "_"
|
|
* This is the set of names imported by `from ... import *`.
|
|
*/
|
|
predicate exports(string name) { PointsTo::moduleExports(this, name) }
|
|
|
|
/** Gets the scope for this module, provided that it is a Python module. */
|
|
ModuleScope getScope() { result = super.getSourceModule() }
|
|
|
|
/**
|
|
* Gets the container path for this module. Will be the file for a Python module,
|
|
* the folder for a package and no result for a builtin module.
|
|
*/
|
|
Container getPath() {
|
|
result = this.(PackageObjectInternal).getFolder()
|
|
or
|
|
result = this.(PythonModuleObjectInternal).getSourceModule().getFile()
|
|
}
|
|
|
|
/**
|
|
* Whether this module is imported by 'import name'. For example on a linux system,
|
|
* the module 'posixpath' is imported as 'os.path' or as 'posixpath'
|
|
*/
|
|
predicate importedAs(string name) { PointsToInternal::module_imported_as(this, name) }
|
|
|
|
/** Whether this module is a package. */
|
|
predicate isPackage() { this instanceof PackageObjectInternal }
|
|
|
|
/** Whether the complete set of names "exported" by this module can be accurately determined */
|
|
predicate hasCompleteExportInfo() { super.hasCompleteExportInfo() }
|
|
|
|
/** Get a module that this module imports */
|
|
ModuleValue getAnImportedModule() { result.importedAs(this.getScope().getAnImportedModuleName()) }
|
|
|
|
/** When used as a normal module (for example, imported and used by other modules) */
|
|
predicate isUsedAsModule() {
|
|
this.isBuiltin()
|
|
or
|
|
this.isPackage()
|
|
or
|
|
exists(ImportingStmt i | this.importedAs(i.getAnImportedModuleName()))
|
|
or
|
|
this.getPath().getBaseName() = "__init__.py"
|
|
}
|
|
|
|
/** When used (exclusively) as a script (will not include normal modules that can also be run as a script) */
|
|
predicate isUsedAsScript() {
|
|
not this.isUsedAsModule() and
|
|
(
|
|
not this.getPath().getExtension() = "py"
|
|
or
|
|
exists(If i, Name name, StringLiteral main, Cmpop op |
|
|
i.getScope() = this.getScope() and
|
|
op instanceof Eq and
|
|
i.getTest().(Compare).compares(name, op, main) and
|
|
name.getId() = "__name__" and
|
|
main.getText() = "__main__"
|
|
)
|
|
or
|
|
exists(Comment c |
|
|
c.getLocation().getFile() = this.getPath() and
|
|
c.getLocation().getStartLine() = 1 and
|
|
c.getText().regexpMatch("^#!/.*python(2|3)?[ \\\\t]*$")
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
module Module {
|
|
/**
|
|
* Gets the `ModuleValue` named `name`.
|
|
*
|
|
* Note that the name used to refer to a module is not
|
|
* necessarily its name. For example,
|
|
* there are modules referred to by the name `os.path`,
|
|
* but that are not named `os.path`, for example the module `posixpath`.
|
|
* Such that the following is true:
|
|
* `Module::named("os.path").getName() = "posixpath"
|
|
*/
|
|
ModuleValue named(string name) {
|
|
result.getName() = name
|
|
or
|
|
result = named(name, _)
|
|
}
|
|
|
|
/* Prevent runaway recursion when a module has itself as an attribute. */
|
|
private ModuleValue named(string name, int dots) {
|
|
dots = 0 and
|
|
not name.charAt(_) = "." and
|
|
result.getName() = name
|
|
or
|
|
dots <= 3 and
|
|
exists(string modname, string attrname | name = modname + "." + attrname |
|
|
result = named(modname, dots - 1).attr(attrname)
|
|
)
|
|
}
|
|
|
|
/** Get the `ModuleValue` for the `builtin` module. */
|
|
ModuleValue builtinModule() { result = TBuiltinModuleObject(Builtin::builtinModule()) }
|
|
}
|
|
|
|
module Value {
|
|
/**
|
|
* Gets the `Value` named `name`.
|
|
* If there is at least one '.' in `name`, then the part of
|
|
* the name to the left of the rightmost '.' is interpreted as a module name
|
|
* and the part after the rightmost '.' as an attribute of that module.
|
|
* For example, `Value::named("os.path.join")` is the `Value` representing the
|
|
* `join` function of the `os.path` module.
|
|
* If there is no '.' in `name`, then the `Value` returned is the builtin
|
|
* object of that name.
|
|
* For example `Value::named("len")` is the `Value` representing the `len` built-in function.
|
|
*/
|
|
Value named(string name) {
|
|
exists(string modname, string attrname | name = modname + "." + attrname |
|
|
result = Module::named(modname).attr(attrname)
|
|
)
|
|
or
|
|
result = ObjectInternal::builtin(name)
|
|
or
|
|
name = "None" and result = ObjectInternal::none_()
|
|
or
|
|
name = "True" and result = TTrue()
|
|
or
|
|
name = "False" and result = TFalse()
|
|
}
|
|
|
|
/**
|
|
* Gets the `NumericValue` for the integer constant `i`, if it exists.
|
|
* There will be no `NumericValue` for most integers, but the following are
|
|
* guaranteed to exist:
|
|
* * From zero to 511 inclusive.
|
|
* * All powers of 2 (up to 2**30)
|
|
* * Any integer explicitly mentioned in the source program.
|
|
*/
|
|
NumericValue forInt(int i) { result.(IntObjectInternal).intValue() = i }
|
|
|
|
/**
|
|
* Gets the `Value` for the bytes constant `bytes`, if it exists.
|
|
* There will be no `Value` for most byte strings, unless it is explicitly
|
|
* declared in the source program.
|
|
*/
|
|
StringValue forBytes(string bytes) { result.(BytesObjectInternal).strValue() = bytes }
|
|
|
|
/**
|
|
* Gets the `Value` for the unicode constant `text`, if it exists.
|
|
* There will be no `Value` for most text strings, unless it is explicitly
|
|
* declared in the source program.
|
|
*/
|
|
StringValue forUnicode(string text) { result.(UnicodeObjectInternal).strValue() = text }
|
|
|
|
/**
|
|
* Gets a `Value` for the string `text`. May be a bytes or unicode string for Python 2.
|
|
* There will be no `Value` for most strings, unless it is explicitly
|
|
* declared in the source program.
|
|
*/
|
|
StringValue forString(string text) {
|
|
result.(UnicodeObjectInternal).strValue() = text
|
|
or
|
|
major_version() = 2 and
|
|
result.(BytesObjectInternal).strValue() = text
|
|
}
|
|
|
|
/** Gets the `Value` for the bool constant `b`. */
|
|
Value forBool(boolean b) {
|
|
b = true and result = TTrue()
|
|
or
|
|
b = false and result = TFalse()
|
|
}
|
|
|
|
/** Gets the `Value` for `None`. */
|
|
Value none_() { result = ObjectInternal::none_() }
|
|
|
|
/**
|
|
* Shortcuts added by the `site` module to exit your interactive session.
|
|
*
|
|
* see https://docs.python.org/3/library/constants.html#constants-added-by-the-site-module
|
|
*/
|
|
Value siteQuitter(string name) {
|
|
(
|
|
name = "exit"
|
|
or
|
|
name = "quit"
|
|
) and
|
|
result = Value::named(name)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A callable in the Python program.
|
|
* Callables include Python functions, built-in functions and bound-methods,
|
|
* but not classes.
|
|
*/
|
|
class CallableValue extends Value {
|
|
CallableValue() { this instanceof CallableObjectInternal }
|
|
|
|
/**
|
|
* Holds if this callable never returns once called.
|
|
* For example, `sys.exit`
|
|
*/
|
|
predicate neverReturns() { this.(CallableObjectInternal).neverReturns() }
|
|
|
|
/** Gets the scope for this function, provided that it is a Python function. */
|
|
FunctionScope getScope() { result = this.(PythonFunctionObjectInternal).getScope() }
|
|
|
|
/** Gets the `n`th parameter node of this callable. */
|
|
NameNode getParameter(int n) { result = this.(CallableObjectInternal).getParameter(n) }
|
|
|
|
/** Gets the `name`d parameter node of this callable. */
|
|
NameNode getParameterByName(string name) {
|
|
result = this.(CallableObjectInternal).getParameterByName(name)
|
|
}
|
|
|
|
/**
|
|
* Gets the argument in `call` corresponding to the `n`'th positional parameter of this callable.
|
|
*
|
|
* Use this method instead of `call.getArg(n)` to handle the fact that this function might be used as
|
|
* a bound-method, such that argument `n` of the call corresponds to the `n+1` parameter of the callable.
|
|
*
|
|
* This method also gives results when the argument is passed as a keyword argument in `call`, as long
|
|
* as `this` is not a builtin function or a builtin method.
|
|
*
|
|
* Examples:
|
|
*
|
|
* - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents
|
|
* `func(10, 20)`, then `getArgumentForCall(call, 0)` will give the `ControlFlowNode` for `10`.
|
|
*
|
|
* - with `call` representing `func(b=20, a=10)`, `getArgumentForCall(call, 0)` will give
|
|
* the `ControlFlowNode` for `10`.
|
|
*
|
|
* - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call`
|
|
* represents `foo.func(10, 20)`, then `getArgumentForCall(call, 1)` will give the
|
|
* `ControlFlowNode` for `10`.
|
|
* Note: There will also exist a `BoundMethodValue bm` where `bm.getArgumentForCall(call, 0)`
|
|
* will give the `ControlFlowNode` for `10` (notice the shift in index used).
|
|
*/
|
|
cached
|
|
ControlFlowNode getArgumentForCall(CallNode call, int n) {
|
|
exists(ObjectInternal called, int offset |
|
|
PointsToInternal::pointsTo(call.getFunction(), _, called, _) and
|
|
called.functionAndOffset(this, offset)
|
|
|
|
|
call.getArg(n - offset) = result
|
|
or
|
|
exists(string name |
|
|
call.getArgByName(name) = result and
|
|
this.getParameter(n).getId() = name
|
|
)
|
|
or
|
|
called instanceof BoundMethodObjectInternal and
|
|
offset = 1 and
|
|
n = 0 and
|
|
result = call.getFunction().(AttrNode).getObject()
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the argument in `call` corresponding to the `name`d keyword parameter of this callable.
|
|
*
|
|
* This method also gives results when the argument is passed as a positional argument in `call`, as long
|
|
* as `this` is not a builtin function or a builtin method.
|
|
*
|
|
* Examples:
|
|
*
|
|
* - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents
|
|
* `func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the `ControlFlowNode` for `10`.
|
|
*
|
|
* - with `call` representing `func(b=20, a=10)`, `getNamedArgumentForCall(call, "a")` will give
|
|
* the `ControlFlowNode` for `10`.
|
|
*
|
|
* - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call`
|
|
* represents `foo.func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the
|
|
* `ControlFlowNode` for `10`.
|
|
*/
|
|
cached
|
|
ControlFlowNode getNamedArgumentForCall(CallNode call, string name) {
|
|
exists(CallableObjectInternal called, int offset |
|
|
PointsToInternal::pointsTo(call.getFunction(), _, called, _) and
|
|
called.functionAndOffset(this, offset)
|
|
|
|
|
call.getArgByName(name) = result
|
|
or
|
|
exists(int n |
|
|
call.getArg(n) = result and
|
|
this.getParameter(n + offset).getId() = name
|
|
// TODO: and not positional only argument (Python 3.8+)
|
|
)
|
|
or
|
|
called instanceof BoundMethodObjectInternal and
|
|
offset = 1 and
|
|
name = "self" and
|
|
result = call.getFunction().(AttrNode).getObject()
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A bound-method, such as `o.func`, where `o` is an instance
|
|
* of a class that has a callable attribute `func`.
|
|
*/
|
|
class BoundMethodValue extends CallableValue instanceof BoundMethodObjectInternal {
|
|
/**
|
|
* Gets the callable that will be used when `this` is called.
|
|
* The actual callable for `func` in `o.func`.
|
|
*/
|
|
CallableValue getFunction() { result = super.getFunction() }
|
|
|
|
/**
|
|
* Gets the value that will be used for the `self` parameter when `this` is called.
|
|
* The value for `o` in `o.func`.
|
|
*/
|
|
Value getSelf() { result = super.getSelf() }
|
|
|
|
/** Gets the parameter node that will be used for `self`. */
|
|
NameNode getSelfParameter() { result = super.getSelfParameter() }
|
|
}
|
|
|
|
/**
|
|
* A class in the Python program, both Python and built-in.
|
|
*/
|
|
class ClassValue extends Value {
|
|
ClassValue() { this.(ObjectInternal).isClass() = true }
|
|
|
|
/** Gets an improper super type of this class. */
|
|
ClassValue getASuperType() { result = this.getABaseType*() }
|
|
|
|
/**
|
|
* Looks up the attribute `name` on this class.
|
|
* Note that this may be different from `this.attr(name)`.
|
|
* For example given the class:
|
|
* ```class C:
|
|
* @classmethod
|
|
* def f(cls): pass
|
|
* ```
|
|
* `this.lookup("f")` is equivalent to `C.__dict__['f']`, which is the class-method
|
|
* whereas
|
|
* `this.attr("f")` is equivalent to `C.f`, which is a bound-method.
|
|
*/
|
|
Value lookup(string name) { this.(ClassObjectInternal).lookup(name, result, _) }
|
|
|
|
predicate isCallable() { this.(ClassObjectInternal).lookup("__call__", _, _) }
|
|
|
|
/** Holds if this class is an iterable. */
|
|
predicate isIterable() {
|
|
this.hasAttribute("__iter__")
|
|
or
|
|
this.hasAttribute("__aiter__")
|
|
or
|
|
this.hasAttribute("__getitem__")
|
|
}
|
|
|
|
/** Holds if this class is an iterator. */
|
|
predicate isIterator() {
|
|
this.hasAttribute("__iter__") and
|
|
(
|
|
major_version() = 3 and this.hasAttribute("__next__")
|
|
or
|
|
/*
|
|
* Because 'next' is a common method name we need to check that an __iter__
|
|
* method actually returns this class. This is not needed for Py3 as the
|
|
* '__next__' method exists to define a class as an iterator.
|
|
*/
|
|
|
|
major_version() = 2 and
|
|
this.hasAttribute("next") and
|
|
exists(ClassValue other, FunctionValue iter | other.declaredAttribute("__iter__") = iter |
|
|
iter.getAnInferredReturnType() = this
|
|
)
|
|
)
|
|
or
|
|
/* This will be redundant when we have C class information */
|
|
this = ClassValue::generator()
|
|
}
|
|
|
|
/** Holds if this class is a container(). That is, does it have a __getitem__ method. */
|
|
predicate isContainer() { exists(this.lookup("__getitem__")) }
|
|
|
|
/**
|
|
* Holds if this class is a sequence. Mutually exclusive with `isMapping()`.
|
|
*
|
|
* Following the definition from
|
|
* https://docs.python.org/3/glossary.html#term-sequence.
|
|
* We don't look at the keys accepted by `__getitem__, but default to treating a class
|
|
* as a sequence (so might treat some mappings as sequences).
|
|
*/
|
|
predicate isSequence() {
|
|
/*
|
|
* To determine whether something is a sequence or a mapping is not entirely clear,
|
|
* so we need to guess a bit.
|
|
*/
|
|
|
|
this.getASuperType() = ClassValue::tuple()
|
|
or
|
|
this.getASuperType() = ClassValue::list()
|
|
or
|
|
this.getASuperType() = ClassValue::range()
|
|
or
|
|
this.getASuperType() = ClassValue::bytes()
|
|
or
|
|
this.getASuperType() = ClassValue::unicode()
|
|
or
|
|
major_version() = 2 and this.getASuperType() = Value::named("collections.Sequence")
|
|
or
|
|
major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Sequence")
|
|
or
|
|
this.hasAttribute("__getitem__") and
|
|
this.hasAttribute("__len__") and
|
|
not this.getASuperType() = ClassValue::dict() and
|
|
not this.getASuperType() = Value::named("collections.Mapping") and
|
|
not this.getASuperType() = Value::named("collections.abc.Mapping")
|
|
}
|
|
|
|
/**
|
|
* Holds if this class is a mapping. Mutually exclusive with `isSequence()`.
|
|
*
|
|
* Although a class will satisfy the requirement by the definition in
|
|
* https://docs.python.org/3.8/glossary.html#term-mapping, we don't look at the keys
|
|
* accepted by `__getitem__, but default to treating a class as a sequence (so might
|
|
* treat some mappings as sequences).
|
|
*/
|
|
predicate isMapping() {
|
|
major_version() = 2 and this.getASuperType() = Value::named("collections.Mapping")
|
|
or
|
|
major_version() = 3 and this.getASuperType() = Value::named("collections.abc.Mapping")
|
|
or
|
|
this.hasAttribute("__getitem__") and
|
|
not this.isSequence()
|
|
}
|
|
|
|
/** Holds if this class is a descriptor. */
|
|
predicate isDescriptorType() { this.hasAttribute("__get__") }
|
|
|
|
/** Holds if this class is a context manager. */
|
|
predicate isContextManager() {
|
|
this.hasAttribute("__enter__") and
|
|
this.hasAttribute("__exit__")
|
|
}
|
|
|
|
/**
|
|
* Gets the qualified name for this class.
|
|
* Should return the same name as the `__qualname__` attribute on classes in Python 3.
|
|
*/
|
|
string getQualifiedName() {
|
|
result = this.(ClassObjectInternal).getBuiltin().getName()
|
|
or
|
|
result = this.(PythonClassObjectInternal).getScope().getQualifiedName()
|
|
}
|
|
|
|
/** Gets the MRO for this class */
|
|
MRO getMro() { result = Types::getMro(this) }
|
|
|
|
predicate failedInference(string reason) { Types::failedInference(this, reason) }
|
|
|
|
/** Gets the nth immediate base type of this class. */
|
|
ClassValue getBaseType(int n) { result = Types::getBase(this, n) }
|
|
|
|
/** Gets an immediate base type of this class. */
|
|
ClassValue getABaseType() { result = Types::getBase(this, _) }
|
|
|
|
/**
|
|
* Holds if this class is a new style class.
|
|
* A new style class is one that implicitly or explicitly inherits from `object`.
|
|
*/
|
|
predicate isNewStyle() { Types::isNewStyle(this) }
|
|
|
|
/**
|
|
* Holds if this class is an old style class.
|
|
* An old style class is one that does not inherit from `object`.
|
|
*/
|
|
predicate isOldStyle() { Types::isOldStyle(this) }
|
|
|
|
/** Gets the scope associated with this class, if it is not a builtin class */
|
|
ClassScope getScope() { result = this.(PythonClassObjectInternal).getScope() }
|
|
|
|
/** Gets the attribute declared in this class */
|
|
Value declaredAttribute(string name) { Types::declaredAttribute(this, name, result, _) }
|
|
|
|
/**
|
|
* Holds if this class has the attribute `name`, including attributes
|
|
* declared by super classes.
|
|
*/
|
|
override predicate hasAttribute(string name) { this.getMro().declares(name) }
|
|
|
|
/**
|
|
* Holds if this class declares the attribute `name`,
|
|
* *not* including attributes declared by super classes.
|
|
*/
|
|
predicate declaresAttribute(string name) {
|
|
this.(ClassObjectInternal).getClassDeclaration().declaresAttribute(name)
|
|
}
|
|
|
|
/**
|
|
* Whether this class is a legal exception class.
|
|
* What constitutes a legal exception class differs between major versions
|
|
*/
|
|
predicate isLegalExceptionType() {
|
|
not this.isNewStyle()
|
|
or
|
|
this.getASuperType() = ClassValue::baseException()
|
|
or
|
|
major_version() = 2 and this = ClassValue::tuple()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A function in the Python program, both Python and built-in.
|
|
* Note that this does not include other callables such as bound-methods.
|
|
*/
|
|
abstract class FunctionValue extends CallableValue {
|
|
/**
|
|
* Gets the qualified name for this function.
|
|
* Should return the same name as the `__qualname__` attribute on functions in Python 3.
|
|
*/
|
|
abstract string getQualifiedName();
|
|
|
|
/** Gets a longer, more descriptive version of toString() */
|
|
abstract string descriptiveString();
|
|
|
|
/** Gets the minimum number of parameters that can be correctly passed to this function */
|
|
abstract int minParameters();
|
|
|
|
/** Gets the maximum number of parameters that can be correctly passed to this function */
|
|
abstract int maxParameters();
|
|
|
|
predicate isOverridingMethod() { this.overrides(_) }
|
|
|
|
predicate isOverriddenMethod() { exists(Value f | f.overrides(this)) }
|
|
|
|
/** Whether `name` is a legal argument name for this function */
|
|
bindingset[name]
|
|
predicate isLegalArgumentName(string name) {
|
|
this.getScope().getAnArg().asName().getId() = name
|
|
or
|
|
this.getScope().getAKeywordOnlyArg().getId() = name
|
|
or
|
|
this.getScope().hasKwArg()
|
|
}
|
|
|
|
/**
|
|
* Whether this is a "normal" method, that is, it is exists as a class attribute
|
|
* which is not a lambda and not the __new__ method.
|
|
*/
|
|
predicate isNormalMethod() {
|
|
exists(ClassValue cls, string name |
|
|
cls.declaredAttribute(name) = this and
|
|
name != "__new__" and
|
|
exists(Expr expr, AstNode origin | expr.pointsTo(this, origin) | not origin instanceof Lambda)
|
|
)
|
|
}
|
|
|
|
/** Gets a class that may be raised by this function */
|
|
abstract ClassValue getARaisedType();
|
|
|
|
/** Gets a call-site from where this function is called as a function */
|
|
CallNode getAFunctionCall() { result.getFunction().pointsTo() = this }
|
|
|
|
/** Gets a call-site from where this function is called as a method */
|
|
CallNode getAMethodCall() {
|
|
exists(BoundMethodObjectInternal bm |
|
|
result.getFunction().pointsTo() = bm and
|
|
bm.getFunction() = this
|
|
)
|
|
}
|
|
|
|
/** Gets a class that this function may return */
|
|
abstract ClassValue getAnInferredReturnType();
|
|
|
|
/** Holds if this function represents a lambda. */
|
|
predicate isLambda() { this.getOrigin().getNode() instanceof Lambda }
|
|
}
|
|
|
|
/** A Python function. */
|
|
class PythonFunctionValue extends FunctionValue {
|
|
PythonFunctionValue() { this instanceof PythonFunctionObjectInternal }
|
|
|
|
override string getQualifiedName() {
|
|
result = this.(PythonFunctionObjectInternal).getScope().getQualifiedName()
|
|
}
|
|
|
|
override string descriptiveString() {
|
|
if this.getScope().isMethod()
|
|
then
|
|
exists(Class cls | this.getScope().getScope() = cls |
|
|
result = "method " + this.getQualifiedName()
|
|
)
|
|
else result = "function " + this.getQualifiedName()
|
|
}
|
|
|
|
override int minParameters() {
|
|
exists(Function f |
|
|
f = this.getScope() and
|
|
result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault())
|
|
)
|
|
}
|
|
|
|
override int maxParameters() {
|
|
exists(Function f |
|
|
f = this.getScope() and
|
|
if exists(f.getVararg())
|
|
then result = 2147483647 // INT_MAX
|
|
else result = count(f.getAnArg())
|
|
)
|
|
}
|
|
|
|
/** Gets a control flow node corresponding to a return statement in this function */
|
|
ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() }
|
|
|
|
override ClassValue getARaisedType() { scope_raises(result, this.getScope()) }
|
|
|
|
override ClassValue getAnInferredReturnType() {
|
|
/*
|
|
* We have to do a special version of this because builtin functions have no
|
|
* explicit return nodes that we can query and get the class of.
|
|
*/
|
|
|
|
result = this.getAReturnedNode().pointsTo().getClass()
|
|
}
|
|
}
|
|
|
|
/** A builtin function, such as `len` or `print`. */
|
|
class BuiltinFunctionValue extends FunctionValue {
|
|
BuiltinFunctionValue() { this instanceof BuiltinFunctionObjectInternal }
|
|
|
|
override string getQualifiedName() { result = this.(BuiltinFunctionObjectInternal).getName() }
|
|
|
|
override string descriptiveString() { result = "builtin-function " + this.getName() }
|
|
|
|
override int minParameters() { none() }
|
|
|
|
override int maxParameters() { none() }
|
|
|
|
override ClassValue getARaisedType() {
|
|
/* Information is unavailable for C code in general */
|
|
none()
|
|
}
|
|
|
|
override ClassValue getAnInferredReturnType() {
|
|
/*
|
|
* We have to do a special version of this because builtin functions have no
|
|
* explicit return nodes that we can query and get the class of.
|
|
*/
|
|
|
|
result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType())
|
|
}
|
|
}
|
|
|
|
/** A builtin method, such as `list.append` or `set.add` */
|
|
class BuiltinMethodValue extends FunctionValue {
|
|
BuiltinMethodValue() { this instanceof BuiltinMethodObjectInternal }
|
|
|
|
override string getQualifiedName() {
|
|
exists(Builtin cls |
|
|
cls.isClass() and
|
|
cls.getMember(_) = this.(BuiltinMethodObjectInternal).getBuiltin() and
|
|
result = cls.getName() + "." + this.getName()
|
|
)
|
|
}
|
|
|
|
override string descriptiveString() { result = "builtin-method " + this.getQualifiedName() }
|
|
|
|
override int minParameters() { none() }
|
|
|
|
override int maxParameters() { none() }
|
|
|
|
override ClassValue getARaisedType() {
|
|
/* Information is unavailable for C code in general */
|
|
none()
|
|
}
|
|
|
|
override ClassValue getAnInferredReturnType() {
|
|
result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType())
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A class representing sequence objects with a length and tracked items.
|
|
*/
|
|
class SequenceValue extends Value instanceof SequenceObjectInternal {
|
|
Value getItem(int n) { result = super.getItem(n) }
|
|
|
|
int length() { result = super.length() }
|
|
}
|
|
|
|
/** A class representing tuple objects */
|
|
class TupleValue extends SequenceValue {
|
|
TupleValue() { this instanceof TupleObjectInternal }
|
|
}
|
|
|
|
/**
|
|
* A class representing strings, either present in the source as a literal, or
|
|
* in a builtin as a value.
|
|
*/
|
|
class StringValue extends Value {
|
|
StringValue() {
|
|
this instanceof BytesObjectInternal or
|
|
this instanceof UnicodeObjectInternal
|
|
}
|
|
|
|
string getText() {
|
|
result = this.(BytesObjectInternal).strValue()
|
|
or
|
|
result = this.(UnicodeObjectInternal).strValue()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A class representing numbers (ints and floats), either present in the source as a literal,
|
|
* or in a builtin as a value.
|
|
*/
|
|
class NumericValue extends Value {
|
|
NumericValue() {
|
|
this instanceof IntObjectInternal or
|
|
this instanceof FloatObjectInternal
|
|
}
|
|
|
|
/** Gets the integer-value if it is a constant integer, and it fits in a QL int */
|
|
int getIntValue() { result = this.(IntObjectInternal).intValue() }
|
|
|
|
/** Gets the float-value if it is a constant float */
|
|
int getFloatValue() { result = this.(FloatObjectInternal).floatValue() }
|
|
}
|
|
|
|
/**
|
|
* A Python property:
|
|
*
|
|
* @property def f():
|
|
* ....
|
|
*
|
|
* https://docs.python.org/3/howto/descriptor.html#properties
|
|
* https://docs.python.org/3/library/functions.html#property
|
|
*/
|
|
class PropertyValue extends Value instanceof PropertyInternal {
|
|
CallableValue getGetter() { result = super.getGetter() }
|
|
|
|
CallableValue getSetter() { result = super.getSetter() }
|
|
|
|
CallableValue getDeleter() { result = super.getDeleter() }
|
|
}
|
|
|
|
/** A method-resolution-order sequence of classes */
|
|
class MRO extends TClassList {
|
|
/** Gets a textual representation of this element. */
|
|
string toString() { result = this.(ClassList).toString() }
|
|
|
|
/** Gets the `n`th class in this MRO */
|
|
ClassValue getItem(int n) { result = this.(ClassList).getItem(n) }
|
|
|
|
/** Holds if any class in this MRO declares the attribute `name` */
|
|
predicate declares(string name) { this.(ClassList).declares(name) }
|
|
|
|
/** Gets the length of this MRO */
|
|
int length() { result = this.(ClassList).length() }
|
|
|
|
/** Holds if this MRO contains `cls` */
|
|
predicate contains(ClassValue cls) { this.(ClassList).contains(cls) }
|
|
|
|
/** Gets the value from scanning for the attribute `name` in this MRO. */
|
|
Value lookup(string name) { this.(ClassList).lookup(name, result, _) }
|
|
|
|
/**
|
|
* Gets the MRO formed by removing all classes before `cls`
|
|
* from this MRO.
|
|
*/
|
|
MRO startingAt(ClassValue cls) { result = this.(ClassList).startingAt(cls) }
|
|
}
|
|
|
|
module ClassValue {
|
|
/** Get the `ClassValue` for the `bool` class. */
|
|
ClassValue bool() { result = TBuiltinClassObject(Builtin::special("bool")) }
|
|
|
|
/** Get the `ClassValue` for the `tuple` class. */
|
|
ClassValue tuple() { result = TBuiltinClassObject(Builtin::special("tuple")) }
|
|
|
|
/** Get the `ClassValue` for the `list` class. */
|
|
ClassValue list() { result = TBuiltinClassObject(Builtin::special("list")) }
|
|
|
|
/** Get the `ClassValue` for `xrange` (Python 2), or `range` (only Python 3) */
|
|
ClassValue range() {
|
|
major_version() = 2 and result = TBuiltinClassObject(Builtin::special("xrange"))
|
|
or
|
|
major_version() = 3 and result = TBuiltinClassObject(Builtin::special("range"))
|
|
}
|
|
|
|
/** Get the `ClassValue` for the `dict` class. */
|
|
ClassValue dict() { result = TBuiltinClassObject(Builtin::special("dict")) }
|
|
|
|
/** Get the `ClassValue` for the `set` class. */
|
|
ClassValue set() { result = TBuiltinClassObject(Builtin::special("set")) }
|
|
|
|
/** Get the `ClassValue` for the `object` class. */
|
|
ClassValue object() { result = TBuiltinClassObject(Builtin::special("object")) }
|
|
|
|
/** Get the `ClassValue` for the `int` class. */
|
|
ClassValue int_() { result = TBuiltinClassObject(Builtin::special("int")) }
|
|
|
|
/** Get the `ClassValue` for the `long` class. */
|
|
ClassValue long() { result = TBuiltinClassObject(Builtin::special("long")) }
|
|
|
|
/** Get the `ClassValue` for the `float` class. */
|
|
ClassValue float_() { result = TBuiltinClassObject(Builtin::special("float")) }
|
|
|
|
/** Get the `ClassValue` for the `complex` class. */
|
|
ClassValue complex() { result = TBuiltinClassObject(Builtin::special("complex")) }
|
|
|
|
/** Get the `ClassValue` for the `bytes` class (also called `str` in Python 2). */
|
|
ClassValue bytes() { result = TBuiltinClassObject(Builtin::special("bytes")) }
|
|
|
|
/**
|
|
* Get the `ClassValue` for the class of unicode strings.
|
|
* `str` in Python 3 and `unicode` in Python 2.
|
|
*/
|
|
ClassValue unicode() { result = TBuiltinClassObject(Builtin::special("unicode")) }
|
|
|
|
/**
|
|
* Get the `ClassValue` for the `str` class. This is `bytes` in Python 2,
|
|
* and `str` in Python 3.
|
|
*/
|
|
ClassValue str() { if major_version() = 2 then result = bytes() else result = unicode() }
|
|
|
|
/** Get the `ClassValue` for the `property` class. */
|
|
ClassValue property() { result = TBuiltinClassObject(Builtin::special("property")) }
|
|
|
|
/** Get the `ClassValue` for the class of Python functions. */
|
|
ClassValue functionType() { result = TBuiltinClassObject(Builtin::special("FunctionType")) }
|
|
|
|
/** Get the `ClassValue` for the class of builtin functions. */
|
|
ClassValue builtinFunction() { result = Value::named("len").getClass() }
|
|
|
|
/** Get the `ClassValue` for the `generatorType` class. */
|
|
ClassValue generator() { result = TBuiltinClassObject(Builtin::special("generator")) }
|
|
|
|
/** Get the `ClassValue` for the `type` class. */
|
|
ClassValue type() { result = TType() }
|
|
|
|
/** Get the `ClassValue` for `ClassType`. */
|
|
ClassValue classType() { result = TBuiltinClassObject(Builtin::special("ClassType")) }
|
|
|
|
/** Get the `ClassValue` for `InstanceType`. */
|
|
ClassValue instanceType() { result = TBuiltinClassObject(Builtin::special("InstanceType")) }
|
|
|
|
/** Get the `ClassValue` for `super`. */
|
|
ClassValue super_() { result = TBuiltinClassObject(Builtin::special("super")) }
|
|
|
|
/** Get the `ClassValue` for the `classmethod` class. */
|
|
ClassValue classmethod() { result = TBuiltinClassObject(Builtin::special("ClassMethod")) }
|
|
|
|
/** Get the `ClassValue` for the `staticmethod` class. */
|
|
ClassValue staticmethod() { result = TBuiltinClassObject(Builtin::special("StaticMethod")) }
|
|
|
|
/** Get the `ClassValue` for the `MethodType` class. */
|
|
pragma[noinline]
|
|
ClassValue methodType() { result = TBuiltinClassObject(Builtin::special("MethodType")) }
|
|
|
|
/** Get the `ClassValue` for the `MethodDescriptorType` class. */
|
|
ClassValue methodDescriptorType() {
|
|
result = TBuiltinClassObject(Builtin::special("MethodDescriptorType"))
|
|
}
|
|
|
|
/** Get the `ClassValue` for the `GetSetDescriptorType` class. */
|
|
ClassValue getSetDescriptorType() {
|
|
result = TBuiltinClassObject(Builtin::special("GetSetDescriptorType"))
|
|
}
|
|
|
|
/** Get the `ClassValue` for the `StopIteration` class. */
|
|
ClassValue stopIteration() { result = TBuiltinClassObject(Builtin::builtin("StopIteration")) }
|
|
|
|
/** Get the `ClassValue` for the class of modules. */
|
|
ClassValue module_() { result = TBuiltinClassObject(Builtin::special("ModuleType")) }
|
|
|
|
/** Get the `ClassValue` for the `Exception` class. */
|
|
ClassValue exception() { result = TBuiltinClassObject(Builtin::special("Exception")) }
|
|
|
|
/** Get the `ClassValue` for the `BaseException` class. */
|
|
ClassValue baseException() { result = TBuiltinClassObject(Builtin::special("BaseException")) }
|
|
|
|
/** Get the `ClassValue` for the `NoneType` class. */
|
|
ClassValue nonetype() { result = TBuiltinClassObject(Builtin::special("NoneType")) }
|
|
|
|
/** Get the `ClassValue` for the `TypeError` class */
|
|
ClassValue typeError() { result = TBuiltinClassObject(Builtin::special("TypeError")) }
|
|
|
|
/** Get the `ClassValue` for the `NameError` class. */
|
|
ClassValue nameError() { result = TBuiltinClassObject(Builtin::builtin("NameError")) }
|
|
|
|
/** Get the `ClassValue` for the `AttributeError` class. */
|
|
ClassValue attributeError() { result = TBuiltinClassObject(Builtin::builtin("AttributeError")) }
|
|
|
|
/** Get the `ClassValue` for the `KeyError` class. */
|
|
ClassValue keyError() { result = TBuiltinClassObject(Builtin::builtin("KeyError")) }
|
|
|
|
/** Get the `ClassValue` for the `LookupError` class. */
|
|
ClassValue lookupError() { result = TBuiltinClassObject(Builtin::builtin("LookupError")) }
|
|
|
|
/** Get the `ClassValue` for the `IndexError` class. */
|
|
ClassValue indexError() { result = TBuiltinClassObject(Builtin::builtin("IndexError")) }
|
|
|
|
/** Get the `ClassValue` for the `IOError` class. */
|
|
ClassValue ioError() { result = TBuiltinClassObject(Builtin::builtin("IOError")) }
|
|
|
|
/** Get the `ClassValue` for the `NotImplementedError` class. */
|
|
ClassValue notImplementedError() {
|
|
result = TBuiltinClassObject(Builtin::builtin("NotImplementedError"))
|
|
}
|
|
|
|
/** Get the `ClassValue` for the `ImportError` class. */
|
|
ClassValue importError() { result = TBuiltinClassObject(Builtin::builtin("ImportError")) }
|
|
|
|
/** Get the `ClassValue` for the `UnicodeEncodeError` class. */
|
|
ClassValue unicodeEncodeError() {
|
|
result = TBuiltinClassObject(Builtin::builtin("UnicodeEncodeError"))
|
|
}
|
|
|
|
/** Get the `ClassValue` for the `UnicodeDecodeError` class. */
|
|
ClassValue unicodeDecodeError() {
|
|
result = TBuiltinClassObject(Builtin::builtin("UnicodeDecodeError"))
|
|
}
|
|
|
|
/** Get the `ClassValue` for the `SystemExit` class. */
|
|
ClassValue systemExit() { result = TBuiltinClassObject(Builtin::builtin("SystemExit")) }
|
|
}
|