mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge pull request #4077 from yoff/MagicMethods
Python: Add support for magic methods
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
private import python
|
||||
private import DataFlowPublic
|
||||
import semmle.python.SpecialMethods
|
||||
|
||||
//--------
|
||||
// Data flow graph
|
||||
@@ -164,17 +165,67 @@ class DataFlowClassValue extends DataFlowCallable, TClassValue {
|
||||
override string getName() { result = c.getName() }
|
||||
}
|
||||
|
||||
/** Represents a call to a callable */
|
||||
class DataFlowCall extends CallNode {
|
||||
DataFlowCallable callable;
|
||||
newtype TDataFlowCall =
|
||||
TCallNode(CallNode call) or
|
||||
TSpecialCall(SpecialMethodCallNode special)
|
||||
|
||||
DataFlowCall() { this = callable.getACall() }
|
||||
abstract class DataFlowCall extends TDataFlowCall {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Get the callable to which this call goes. */
|
||||
DataFlowCallable getCallable() { result = callable }
|
||||
abstract DataFlowCallable getCallable();
|
||||
|
||||
/** Get the specified argument to this call. */
|
||||
abstract ControlFlowNode getArg(int n);
|
||||
|
||||
/** Get the control flow node representing this call. */
|
||||
abstract ControlFlowNode getNode();
|
||||
|
||||
/** Gets the enclosing callable of this call. */
|
||||
DataFlowCallable getEnclosingCallable() { result.getScope() = this.getNode().getScope() }
|
||||
abstract DataFlowCallable getEnclosingCallable();
|
||||
}
|
||||
|
||||
/** Represents a call to a callable. */
|
||||
class CallNodeCall extends DataFlowCall, TCallNode {
|
||||
CallNode call;
|
||||
DataFlowCallable callable;
|
||||
|
||||
CallNodeCall() {
|
||||
this = TCallNode(call) and
|
||||
call = callable.getACall()
|
||||
}
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override ControlFlowNode getArg(int n) { result = call.getArg(n) }
|
||||
|
||||
override ControlFlowNode getNode() { result = call }
|
||||
|
||||
override DataFlowCallable getCallable() { result = callable }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() }
|
||||
}
|
||||
|
||||
/** Represents a call to a special method. */
|
||||
class SpecialCall extends DataFlowCall, TSpecialCall {
|
||||
SpecialMethodCallNode special;
|
||||
|
||||
SpecialCall() { this = TSpecialCall(special) }
|
||||
|
||||
override string toString() { result = special.toString() }
|
||||
|
||||
override ControlFlowNode getArg(int n) { result = special.(SpecialMethod::Potential).getArg(n) }
|
||||
|
||||
override ControlFlowNode getNode() { result = special }
|
||||
|
||||
override DataFlowCallable getCallable() {
|
||||
result = TCallableValue(special.getResolvedSpecialMethod())
|
||||
}
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() {
|
||||
result.getScope() = special.getNode().getScope()
|
||||
}
|
||||
}
|
||||
|
||||
/** A data flow node that represents a call argument. */
|
||||
@@ -227,7 +278,7 @@ class OutNode extends CfgNode {
|
||||
* `kind`.
|
||||
*/
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
call = result.getNode() and
|
||||
call.getNode() = result.getNode() and
|
||||
kind = TNormalReturnKind()
|
||||
}
|
||||
|
||||
|
||||
117
python/ql/src/semmle/python/SpecialMethods.qll
Normal file
117
python/ql/src/semmle/python/SpecialMethods.qll
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Provides support for special methods.
|
||||
* This is done in two steps:
|
||||
* - A subset of `ControlFlowNode`s are labelled as potentially corresponding to
|
||||
* a special method call (by being an instance of `SpecialMethod::Potential`).
|
||||
* - A subset of the potential special method calls are labelled as being actual
|
||||
* special method calls (`SpecialMethodCallNode`) if the appropriate method is defined.
|
||||
* Extend `SpecialMethod::Potential` to capture more cases.
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
/** A control flow node which might correspond to a special method call. */
|
||||
class PotentialSpecialMethodCallNode extends ControlFlowNode {
|
||||
PotentialSpecialMethodCallNode() { this instanceof SpecialMethod::Potential }
|
||||
}
|
||||
|
||||
/**
|
||||
* Machinery for detecting special method calls.
|
||||
* Extend `SpecialMethod::Potential` to capture more cases.
|
||||
*/
|
||||
module SpecialMethod {
|
||||
/** A control flow node which might correspond to a special method call. */
|
||||
abstract class Potential extends ControlFlowNode {
|
||||
/** Gets the name of the method that would be called. */
|
||||
abstract string getSpecialMethodName();
|
||||
|
||||
/** Gets the control flow node that would be passed as the specified argument. */
|
||||
abstract ControlFlowNode getArg(int n);
|
||||
|
||||
/**
|
||||
* Gets the control flow node corresponding to the instance
|
||||
* that would define the special method.
|
||||
*/
|
||||
ControlFlowNode getSelf() { result = this.getArg(0) }
|
||||
}
|
||||
|
||||
/** A binary expression node that might correspond to a special method call. */
|
||||
class SpecialBinOp extends Potential, BinaryExprNode {
|
||||
Operator operator;
|
||||
|
||||
SpecialBinOp() { this.getOp() = operator }
|
||||
|
||||
override string getSpecialMethodName() { result = operator.getSpecialMethodName() }
|
||||
|
||||
override ControlFlowNode getArg(int n) {
|
||||
n = 0 and result = this.getLeft()
|
||||
or
|
||||
n = 1 and result = this.getRight()
|
||||
}
|
||||
}
|
||||
|
||||
/** A subscript expression node that might correspond to a special method call. */
|
||||
abstract class SpecialSubscript extends Potential, SubscriptNode {
|
||||
override ControlFlowNode getArg(int n) {
|
||||
n = 0 and result = this.getObject()
|
||||
or
|
||||
n = 1 and result = this.getIndex()
|
||||
}
|
||||
}
|
||||
|
||||
/** A subscript expression node that might correspond to a call to __getitem__. */
|
||||
class SpecialGetItem extends SpecialSubscript {
|
||||
SpecialGetItem() { this.isLoad() }
|
||||
|
||||
override string getSpecialMethodName() { result = "__getitem__" }
|
||||
}
|
||||
|
||||
/** A subscript expression node that might correspond to a call to __setitem__. */
|
||||
class SpecialSetItem extends SpecialSubscript {
|
||||
SpecialSetItem() { this.isStore() }
|
||||
|
||||
override string getSpecialMethodName() { result = "__setitem__" }
|
||||
|
||||
override ControlFlowNode getArg(int n) {
|
||||
n = 0 and result = this.getObject()
|
||||
or
|
||||
n = 1 and result = this.getIndex()
|
||||
or
|
||||
n = 2 and result = this.getValueNode()
|
||||
}
|
||||
|
||||
private ControlFlowNode getValueNode() {
|
||||
exists(AssignStmt a |
|
||||
a.getATarget() = this.getNode() and
|
||||
result.getNode() = a.getValue()
|
||||
)
|
||||
or
|
||||
exists(AugAssign a |
|
||||
a.getTarget() = this.getNode() and
|
||||
result.getNode() = a.getValue()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A subscript expression node that might correspond to a call to __delitem__. */
|
||||
class SpecialDelItem extends SpecialSubscript {
|
||||
SpecialDelItem() { this.isDelete() }
|
||||
|
||||
override string getSpecialMethodName() { result = "__delitem__" }
|
||||
}
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a special method call. */
|
||||
class SpecialMethodCallNode extends PotentialSpecialMethodCallNode {
|
||||
Value resolvedSpecialMethod;
|
||||
|
||||
SpecialMethodCallNode() {
|
||||
exists(SpecialMethod::Potential pot |
|
||||
this.(SpecialMethod::Potential) = pot and
|
||||
pot.getSelf().pointsTo().getClass().lookup(pot.getSpecialMethodName()) = resolvedSpecialMethod
|
||||
)
|
||||
}
|
||||
|
||||
/** The method that is called. */
|
||||
Value getResolvedSpecialMethod() { result = resolvedSpecialMethod }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
| classes.py:620:5:620:16 | SSA variable with_getitem | classes.py:614:15:614:18 | ControlFlowNode for self |
|
||||
| classes.py:637:5:637:16 | SSA variable with_setitem | classes.py:632:15:632:18 | ControlFlowNode for self |
|
||||
| classes.py:654:5:654:16 | SSA variable with_delitem | classes.py:649:15:649:18 | ControlFlowNode for self |
|
||||
| classes.py:735:5:735:12 | SSA variable with_add | classes.py:729:15:729:18 | ControlFlowNode for self |
|
||||
| classes.py:752:5:752:12 | SSA variable with_sub | classes.py:746:15:746:18 | ControlFlowNode for self |
|
||||
| classes.py:769:5:769:12 | SSA variable with_mul | classes.py:763:15:763:18 | ControlFlowNode for self |
|
||||
| classes.py:786:5:786:15 | SSA variable with_matmul | classes.py:780:15:780:18 | ControlFlowNode for self |
|
||||
| classes.py:803:5:803:16 | SSA variable with_truediv | classes.py:797:15:797:18 | ControlFlowNode for self |
|
||||
| classes.py:820:5:820:17 | SSA variable with_floordiv | classes.py:814:15:814:18 | ControlFlowNode for self |
|
||||
| classes.py:837:5:837:12 | SSA variable with_mod | classes.py:831:15:831:18 | ControlFlowNode for self |
|
||||
| classes.py:877:5:877:12 | SSA variable with_pow | classes.py:865:15:865:18 | ControlFlowNode for self |
|
||||
| classes.py:894:5:894:15 | SSA variable with_lshift | classes.py:888:15:888:18 | ControlFlowNode for self |
|
||||
| classes.py:911:5:911:15 | SSA variable with_rshift | classes.py:905:15:905:18 | ControlFlowNode for self |
|
||||
| classes.py:928:5:928:12 | SSA variable with_and | classes.py:922:15:922:18 | ControlFlowNode for self |
|
||||
| classes.py:945:5:945:12 | SSA variable with_xor | classes.py:939:15:939:18 | ControlFlowNode for self |
|
||||
| classes.py:962:5:962:11 | SSA variable with_or | classes.py:956:15:956:18 | ControlFlowNode for self |
|
||||
|
||||
@@ -7,9 +7,10 @@ class ArgumentRoutingConfig extends DataFlow::Configuration {
|
||||
ArgumentRoutingConfig() { this = "ArgumentRoutingConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) {
|
||||
exists(AssignmentDefinition def |
|
||||
exists(AssignmentDefinition def, DataFlow::DataFlowCall call |
|
||||
def.getVariable() = node.(DataFlow::EssaNode).getVar() and
|
||||
def.getValue().(DataFlow::DataFlowCall).getCallable().getName().matches("With\\_%")
|
||||
def.getValue() = call.getNode() and
|
||||
call.getCallable().getName().matches("With\\_%")
|
||||
) and
|
||||
node.(DataFlow::EssaNode).getVar().getName().matches("with\\_%")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
| classes.py:622:18:622:21 | ControlFlowNode for arg2 | classes.py:613:15:613:17 | ControlFlowNode for key |
|
||||
| classes.py:640:18:640:21 | ControlFlowNode for arg2 | classes.py:631:15:631:17 | ControlFlowNode for key |
|
||||
| classes.py:656:22:656:25 | ControlFlowNode for arg2 | classes.py:648:15:648:17 | ControlFlowNode for key |
|
||||
| classes.py:737:16:737:19 | ControlFlowNode for arg2 | classes.py:728:15:728:19 | ControlFlowNode for other |
|
||||
| classes.py:754:16:754:19 | ControlFlowNode for arg2 | classes.py:745:15:745:19 | ControlFlowNode for other |
|
||||
| classes.py:771:16:771:19 | ControlFlowNode for arg2 | classes.py:762:15:762:19 | ControlFlowNode for other |
|
||||
| classes.py:788:19:788:22 | ControlFlowNode for arg2 | classes.py:779:15:779:19 | ControlFlowNode for other |
|
||||
| classes.py:805:20:805:23 | ControlFlowNode for arg2 | classes.py:796:15:796:19 | ControlFlowNode for other |
|
||||
| classes.py:822:22:822:25 | ControlFlowNode for arg2 | classes.py:813:15:813:19 | ControlFlowNode for other |
|
||||
| classes.py:839:16:839:19 | ControlFlowNode for arg2 | classes.py:830:15:830:19 | ControlFlowNode for other |
|
||||
| classes.py:879:17:879:20 | ControlFlowNode for arg2 | classes.py:864:15:864:19 | ControlFlowNode for other |
|
||||
| classes.py:896:20:896:23 | ControlFlowNode for arg2 | classes.py:887:15:887:19 | ControlFlowNode for other |
|
||||
| classes.py:913:20:913:23 | ControlFlowNode for arg2 | classes.py:904:15:904:19 | ControlFlowNode for other |
|
||||
| classes.py:930:16:930:19 | ControlFlowNode for arg2 | classes.py:921:15:921:19 | ControlFlowNode for other |
|
||||
| classes.py:947:16:947:19 | ControlFlowNode for arg2 | classes.py:938:15:938:19 | ControlFlowNode for other |
|
||||
| classes.py:964:15:964:18 | ControlFlowNode for arg2 | classes.py:955:15:955:19 | ControlFlowNode for other |
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| classes.py:640:26:640:29 | ControlFlowNode for arg3 | classes.py:630:15:630:19 | ControlFlowNode for value |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,39 @@
|
||||
| classes.py:32:12:32:31 | ControlFlowNode for Attribute() | classes.py:32:12:32:31 | ControlFlowNode for Attribute() |
|
||||
| classes.py:212:7:212:22 | ControlFlowNode for set() | classes.py:212:7:212:22 | ControlFlowNode for set() |
|
||||
| classes.py:216:7:216:28 | ControlFlowNode for frozenset() | classes.py:216:7:216:28 | ControlFlowNode for frozenset() |
|
||||
| classes.py:220:7:220:26 | ControlFlowNode for dict() | classes.py:220:7:220:26 | ControlFlowNode for dict() |
|
||||
| classes.py:369:27:369:50 | ControlFlowNode for dict() | classes.py:369:27:369:50 | ControlFlowNode for dict() |
|
||||
| classes.py:563:12:563:24 | ControlFlowNode for Attribute() | classes.py:563:12:563:24 | ControlFlowNode for Attribute() |
|
||||
| classes.py:41:16:41:35 | ControlFlowNode for Attribute() | classes.py:41:16:41:35 | ControlFlowNode for Attribute() |
|
||||
| classes.py:264:9:264:24 | ControlFlowNode for set() | classes.py:264:9:264:24 | ControlFlowNode for set() |
|
||||
| classes.py:269:9:269:30 | ControlFlowNode for frozenset() | classes.py:269:9:269:30 | ControlFlowNode for frozenset() |
|
||||
| classes.py:274:9:274:28 | ControlFlowNode for dict() | classes.py:274:9:274:28 | ControlFlowNode for dict() |
|
||||
| classes.py:454:29:454:52 | ControlFlowNode for dict() | classes.py:454:29:454:52 | ControlFlowNode for dict() |
|
||||
| classes.py:622:5:622:16 | ControlFlowNode for with_getitem | classes.py:612:21:612:24 | SSA variable self |
|
||||
| classes.py:622:18:622:21 | ControlFlowNode for arg2 | classes.py:612:27:612:29 | SSA variable key |
|
||||
| classes.py:640:5:640:16 | ControlFlowNode for with_setitem | classes.py:629:21:629:24 | SSA variable self |
|
||||
| classes.py:640:18:640:21 | ControlFlowNode for arg2 | classes.py:629:27:629:29 | SSA variable key |
|
||||
| classes.py:640:26:640:29 | ControlFlowNode for arg3 | classes.py:629:32:629:36 | SSA variable value |
|
||||
| classes.py:656:9:656:20 | ControlFlowNode for with_delitem | classes.py:647:21:647:24 | SSA variable self |
|
||||
| classes.py:656:22:656:25 | ControlFlowNode for arg2 | classes.py:647:27:647:29 | SSA variable key |
|
||||
| classes.py:683:16:683:28 | ControlFlowNode for Attribute() | classes.py:683:16:683:28 | ControlFlowNode for Attribute() |
|
||||
| classes.py:737:5:737:12 | ControlFlowNode for with_add | classes.py:727:17:727:20 | SSA variable self |
|
||||
| classes.py:737:16:737:19 | ControlFlowNode for arg2 | classes.py:727:23:727:27 | SSA variable other |
|
||||
| classes.py:754:5:754:12 | ControlFlowNode for with_sub | classes.py:744:17:744:20 | SSA variable self |
|
||||
| classes.py:754:16:754:19 | ControlFlowNode for arg2 | classes.py:744:23:744:27 | SSA variable other |
|
||||
| classes.py:771:5:771:12 | ControlFlowNode for with_mul | classes.py:761:17:761:20 | SSA variable self |
|
||||
| classes.py:771:16:771:19 | ControlFlowNode for arg2 | classes.py:761:23:761:27 | SSA variable other |
|
||||
| classes.py:788:5:788:15 | ControlFlowNode for with_matmul | classes.py:778:20:778:23 | SSA variable self |
|
||||
| classes.py:788:19:788:22 | ControlFlowNode for arg2 | classes.py:778:26:778:30 | SSA variable other |
|
||||
| classes.py:805:5:805:16 | ControlFlowNode for with_truediv | classes.py:795:21:795:24 | SSA variable self |
|
||||
| classes.py:805:20:805:23 | ControlFlowNode for arg2 | classes.py:795:27:795:31 | SSA variable other |
|
||||
| classes.py:822:5:822:17 | ControlFlowNode for with_floordiv | classes.py:812:22:812:25 | SSA variable self |
|
||||
| classes.py:822:22:822:25 | ControlFlowNode for arg2 | classes.py:812:28:812:32 | SSA variable other |
|
||||
| classes.py:839:5:839:12 | ControlFlowNode for with_mod | classes.py:829:17:829:20 | SSA variable self |
|
||||
| classes.py:839:16:839:19 | ControlFlowNode for arg2 | classes.py:829:23:829:27 | SSA variable other |
|
||||
| classes.py:879:5:879:12 | ControlFlowNode for with_pow | classes.py:863:17:863:20 | SSA variable self |
|
||||
| classes.py:879:17:879:20 | ControlFlowNode for arg2 | classes.py:863:23:863:27 | SSA variable other |
|
||||
| classes.py:896:5:896:15 | ControlFlowNode for with_lshift | classes.py:886:20:886:23 | SSA variable self |
|
||||
| classes.py:896:20:896:23 | ControlFlowNode for arg2 | classes.py:886:26:886:30 | SSA variable other |
|
||||
| classes.py:913:5:913:15 | ControlFlowNode for with_rshift | classes.py:903:20:903:23 | SSA variable self |
|
||||
| classes.py:913:20:913:23 | ControlFlowNode for arg2 | classes.py:903:26:903:30 | SSA variable other |
|
||||
| classes.py:930:5:930:12 | ControlFlowNode for with_and | classes.py:920:17:920:20 | SSA variable self |
|
||||
| classes.py:930:16:930:19 | ControlFlowNode for arg2 | classes.py:920:23:920:27 | SSA variable other |
|
||||
| classes.py:947:5:947:12 | ControlFlowNode for with_xor | classes.py:937:17:937:20 | SSA variable self |
|
||||
| classes.py:947:16:947:19 | ControlFlowNode for arg2 | classes.py:937:23:937:27 | SSA variable other |
|
||||
| classes.py:964:5:964:11 | ControlFlowNode for with_or | classes.py:954:16:954:19 | SSA variable self |
|
||||
| classes.py:964:15:964:18 | ControlFlowNode for arg2 | classes.py:954:22:954:26 | SSA variable other |
|
||||
|
||||
Reference in New Issue
Block a user