mirror of
https://github.com/github/codeql.git
synced 2025-12-18 01:33:15 +01:00
Python: Assorted improvements to API.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import python
|
||||
private import semmle.python.pointsto.PointsTo
|
||||
private import semmle.python.objects.ObjectInternal
|
||||
|
||||
/** An expression */
|
||||
class Expr extends Expr_, AstNode {
|
||||
@@ -71,7 +72,8 @@ class Expr extends Expr_, AstNode {
|
||||
result = this.getASubExpression()
|
||||
}
|
||||
|
||||
/** Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to
|
||||
/** NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead.
|
||||
* Gets what this expression might "refer-to". Performs a combination of localized (intra-procedural) points-to
|
||||
* analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly
|
||||
* precise, but may not provide information for a significant number of flow-nodes.
|
||||
* If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead.
|
||||
@@ -82,13 +84,15 @@ class Expr extends Expr_, AstNode {
|
||||
this.refersTo(_, obj, cls, origin)
|
||||
}
|
||||
|
||||
/** Gets what this expression might "refer-to" in the given `context`.
|
||||
/** NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead.
|
||||
* Gets what this expression might "refer-to" in the given `context`.
|
||||
*/
|
||||
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
|
||||
this.getAFlowNode().refersTo(context, obj, cls, origin.getAFlowNode())
|
||||
}
|
||||
|
||||
/** Whether this expression might "refer-to" to `value` which is from `origin`
|
||||
/** NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead.
|
||||
* Holds if this expression might "refer-to" to `value` which is from `origin`
|
||||
* Unlike `this.refersTo(value, _, origin)`, this predicate includes results
|
||||
* where the class cannot be inferred.
|
||||
*/
|
||||
@@ -97,11 +101,31 @@ class Expr extends Expr_, AstNode {
|
||||
this.getAFlowNode().refersTo(obj, origin.getAFlowNode())
|
||||
}
|
||||
|
||||
/** Equivalent to `this.refersTo(value, _)` */
|
||||
/** NOTE: `refersTo` will be deprecated in 2019. Use `pointsTo` instead.
|
||||
* Equivalent to `this.refersTo(value, _)` */
|
||||
predicate refersTo(Object obj) {
|
||||
this.refersTo(obj, _)
|
||||
}
|
||||
|
||||
/** Holds if this expression might "point-to" to `value` which is from `origin`
|
||||
* in the given `context`.
|
||||
*/
|
||||
predicate pointsTo(Context context, Value value, AstNode origin) {
|
||||
this.getAFlowNode().pointsTo(context, value, origin.getAFlowNode())
|
||||
}
|
||||
|
||||
/** Holds if this expression might "point-to" to `value` which is from `origin`.
|
||||
*/
|
||||
predicate pointsTo(Value value, AstNode origin) {
|
||||
this.getAFlowNode().pointsTo(value, origin.getAFlowNode())
|
||||
}
|
||||
|
||||
/** Holds if this expression might "point-to" to `value`.
|
||||
*/
|
||||
predicate pointsTo(Value value) {
|
||||
this.pointsTo(value, _)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** An attribute expression, such as `value.attr` */
|
||||
@@ -346,6 +370,10 @@ abstract class ImmutableLiteral extends Expr {
|
||||
abstract Object getLiteralObject();
|
||||
|
||||
abstract boolean booleanValue();
|
||||
final Value getLiteralValue() {
|
||||
result.(ConstantObjectInternal).getLiteral() = this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A numerical constant expression, such as `7` or `4.2` */
|
||||
@@ -472,8 +500,10 @@ class NegativeIntegerLiteral extends ImmutableLiteral, UnaryExpr {
|
||||
py_cobjectnames(result, "-" + this.getOperand().(IntegerLiteral).getN())
|
||||
}
|
||||
|
||||
/** Gets the (integer) value of this constant. Will not return a result if the value does not fit into
|
||||
a 32 bit signed value */
|
||||
int getValue() {
|
||||
result = -this.getOperand().(IntegerLiteral).getValue()
|
||||
result = -(this.getOperand().(IntegerLiteral).getValue())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -222,6 +222,11 @@ class ControlFlowNode extends @py_flow_node {
|
||||
this.pointsTo(_, value, _)
|
||||
}
|
||||
|
||||
/** Gets the value that this ControlFlowNode points-to. */
|
||||
Value pointsTo() {
|
||||
this.pointsTo(_, result, _)
|
||||
}
|
||||
|
||||
/** Gets a value that this ControlFlowNode may points-to. */
|
||||
Value inferredValue() {
|
||||
this.pointsTo(_, result, _)
|
||||
|
||||
@@ -73,12 +73,15 @@ abstract class ConstantObjectInternal extends ObjectInternal {
|
||||
|
||||
override predicate useOriginAsLegacyObject() { none() }
|
||||
|
||||
/** Gets an AST literal with the same value as this object */
|
||||
abstract ImmutableLiteral getLiteral();
|
||||
|
||||
}
|
||||
|
||||
private abstract class BooleanObjectInternal extends ConstantObjectInternal {
|
||||
|
||||
override ObjectInternal getClass() {
|
||||
result = TBuiltinClassObject(Builtin::special("bool"))
|
||||
result = ClassValue::bool()
|
||||
}
|
||||
|
||||
override int length() { none() }
|
||||
@@ -90,6 +93,10 @@ private abstract class BooleanObjectInternal extends ConstantObjectInternal {
|
||||
/* Booleans aren't iterable */
|
||||
override ObjectInternal getIterNext() { none() }
|
||||
|
||||
override ImmutableLiteral getLiteral() {
|
||||
result.(BooleanLiteral).booleanValue() = this.booleanValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class TrueObjectInternal extends BooleanObjectInternal, TTrue {
|
||||
@@ -175,6 +182,10 @@ private class NoneObjectInternal extends ConstantObjectInternal, TNone {
|
||||
/* None isn't iterable */
|
||||
override ObjectInternal getIterNext() { none() }
|
||||
|
||||
override ImmutableLiteral getLiteral() {
|
||||
result instanceof None
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -216,6 +227,12 @@ private class IntObjectInternal extends ConstantObjectInternal, TInt {
|
||||
/* ints aren't iterable */
|
||||
override ObjectInternal getIterNext() { none() }
|
||||
|
||||
override ImmutableLiteral getLiteral() {
|
||||
result.(IntegerLiteral).getValue() = this.intValue()
|
||||
or
|
||||
result.(NegativeIntegerLiteral).getOperand().(IntegerLiteral).getValue() = -this.intValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FloatObjectInternal extends ConstantObjectInternal, TFloat {
|
||||
@@ -264,6 +281,10 @@ private class FloatObjectInternal extends ConstantObjectInternal, TFloat {
|
||||
/* floats aren't iterable */
|
||||
override ObjectInternal getIterNext() { none() }
|
||||
|
||||
override ImmutableLiteral getLiteral() {
|
||||
result.(FloatLiteral).getValue() = this.floatValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -310,6 +331,10 @@ private class UnicodeObjectInternal extends ConstantObjectInternal, TUnicode {
|
||||
result = TUnknownInstance(this.getClass())
|
||||
}
|
||||
|
||||
override ImmutableLiteral getLiteral() {
|
||||
result.(Unicode).getText() = this.strValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class BytesObjectInternal extends ConstantObjectInternal, TBytes {
|
||||
@@ -355,6 +380,10 @@ private class BytesObjectInternal extends ConstantObjectInternal, TBytes {
|
||||
result = TUnknownInstance(this.getClass())
|
||||
}
|
||||
|
||||
override ImmutableLiteral getLiteral() {
|
||||
result.(Bytes).getText() = this.strValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ 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.
|
||||
@@ -85,7 +86,7 @@ class Value extends TObject {
|
||||
filepath = "" and bl = 0 and bc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
|
||||
/** Gets the name of this value, if it has one.
|
||||
/** 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.
|
||||
*/
|
||||
@@ -228,7 +229,7 @@ class CallableValue extends Value {
|
||||
cached ControlFlowNode getArgumentForCall(CallNode call, int n) {
|
||||
exists(ObjectInternal called, int offset |
|
||||
PointsToInternal::pointsTo(call.getFunction(), _, called, _) and
|
||||
called.functionAndOffset(this, offset)
|
||||
called.functionAndOffset(this, offset)
|
||||
|
|
||||
call.getArg(n-offset) = result
|
||||
or
|
||||
@@ -316,13 +317,13 @@ class ClassValue extends Value {
|
||||
result = Types::getBase(this, n)
|
||||
}
|
||||
|
||||
/** Holds if this class is a new style class.
|
||||
/** 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.
|
||||
/** 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)
|
||||
@@ -333,6 +334,20 @@ class ClassValue extends Value {
|
||||
result = this.(PythonClassObjectInternal).getScope()
|
||||
}
|
||||
|
||||
/** Holds if this class has the attribute `name`, including
|
||||
* attributes declared by super classes.
|
||||
*/
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A method-resolution-order sequence of classes */
|
||||
@@ -347,5 +362,87 @@ class MRO extends TClassList {
|
||||
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 class of Python functions. */
|
||||
ClassValue function() {
|
||||
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 `int` class. */
|
||||
ClassValue int_() {
|
||||
result = TBuiltinClassObject(Builtin::special("int"))
|
||||
}
|
||||
|
||||
/** Get the `ClassValue` for the `float` class. */
|
||||
ClassValue float_() {
|
||||
result = TBuiltinClassObject(Builtin::builtin("float"))
|
||||
}
|
||||
|
||||
/** 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 `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 class of modules. */
|
||||
ClassValue module_() {
|
||||
result = TBuiltinClassObject(Builtin::special("ModuleType"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user