Files
codeql/python/ql/lib/semmle/python/objects/ObjectAPI.qll
Taus 1c68c987b0 Python: Change all remaining occurrences of StrConst
Done using
```
git grep StrConst | xargs sed -i 's/StrConst/StringLiteral/g'
```
2024-04-22 12:00:09 +00:00

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")) }
}