Python points-to: Handle subclassing of ABCs.

This commit is contained in:
Mark Shannon
2019-04-17 12:32:40 +01:00
parent 2d4f64f2e5
commit ddc4ada130
6 changed files with 68 additions and 27 deletions

View File

@@ -62,6 +62,19 @@ abstract class ClassObjectInternal extends ObjectInternal {
override int length() { none() }
boolean isIterableSubclass() {
this = ObjectInternal::builtin("list") and result = true
or
this = ObjectInternal::builtin("set") and result = true
or
this = ObjectInternal::builtin("dict") and result = true
or
this != ObjectInternal::builtin("list") and
this != ObjectInternal::builtin("set") and
this != ObjectInternal::builtin("dict") and
result = false
}
}
class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject {

View File

@@ -31,7 +31,7 @@ class SpecificInstanceInternal extends TSpecificInstance, ObjectInternal {
override boolean isClass() { result = false }
override boolean isComparable() { result = false }
override boolean isComparable() { result = true }
override ObjectInternal getClass() {
this = TSpecificInstance(_, result, _)

View File

@@ -159,6 +159,8 @@ predicate literal_instantiation(ControlFlowNode n, ClassObjectInternal cls, Poin
or
n instanceof DictNode and cls = ObjectInternal::builtin("dict")
or
n instanceof SetNode and cls = ObjectInternal::builtin("set")
or
n.getNode() instanceof ImaginaryLiteral and cls = ObjectInternal::builtin("complex")
)
}

View File

@@ -1746,10 +1746,23 @@ cached module Types {
or
mro.getItem(n) = sup and result = true
or
mro.getItem(n) != sup and result = mroContains(mro, sup, n+1)
mro.getItem(n) = abc_to_concrete(sup) and result = true
or
mro.getItem(n) != sup and sup != AbstractBaseClass::named("Iterable") and
mro.getItem(n) != abc_to_concrete(sup) and result = mroContains(mro, sup, n+1)
or
sup = AbstractBaseClass::named("Iterable") and result = mro.getItem(n).isIterableSubclass()
)
}
private ClassObjectInternal abc_to_concrete(ClassObjectInternal c) {
c = AbstractBaseClass::named("Sequence") and result = ObjectInternal::builtin("list")
or
c = AbstractBaseClass::named("Set") and result = ObjectInternal::builtin("set")
or
c = AbstractBaseClass::named("Mapping") and result = ObjectInternal::builtin("dict")
}
cached boolean hasAttr(ObjectInternal cls, string name) {
result = mroHasAttr(Types::getMro(cls), name, 0)
}
@@ -1776,6 +1789,19 @@ cached module Types {
}
module AbstractBaseClass {
ClassObjectInternal named(string name) {
exists(ModuleObjectInternal m |
m.getName() = "_abcoll"
or
m.getName() = "_collections_abc"
|
m.attribute(name, result, _)
)
}
}
module AttributePointsTo {
predicate pointsTo(AttrNode f, Context context, ObjectInternal value, ControlFlowNode origin) {

View File

@@ -1,30 +1,30 @@
| import | test.py:17:1:17:3 | ControlFlowNode for bar | Function bar | test.py:2:1:2:19 | ControlFlowNode for FunctionExpr |
| import | test.py:18:1:18:7 | ControlFlowNode for bar() | 21 | test.py:18:5:18:6 | ControlFlowNode for IntegerLiteral |
| import | test.py:19:1:19:13 | ControlFlowNode for bar() | 22 | test.py:19:5:19:6 | ControlFlowNode for IntegerLiteral |
| import | test.py:20:1:20:13 | ControlFlowNode for bar() | True | test.py:20:9:20:12 | ControlFlowNode for True |
| import | test.py:21:1:21:11 | ControlFlowNode for bar() | 24 | test.py:21:5:21:6 | ControlFlowNode for IntegerLiteral |
| import | test.py:22:1:22:13 | ControlFlowNode for bar() | 7 | test.py:22:7:22:7 | ControlFlowNode for IntegerLiteral |
| import | test.py:18:1:18:7 | ControlFlowNode for bar() | int 21 | test.py:18:5:18:6 | ControlFlowNode for IntegerLiteral |
| import | test.py:19:1:19:13 | ControlFlowNode for bar() | int 22 | test.py:19:5:19:6 | ControlFlowNode for IntegerLiteral |
| import | test.py:20:1:20:13 | ControlFlowNode for bar() | bool True | test.py:20:9:20:12 | ControlFlowNode for True |
| import | test.py:21:1:21:11 | ControlFlowNode for bar() | int 24 | test.py:21:5:21:6 | ControlFlowNode for IntegerLiteral |
| import | test.py:22:1:22:13 | ControlFlowNode for bar() | int 7 | test.py:22:7:22:7 | ControlFlowNode for IntegerLiteral |
| runtime | test.py:4:5:4:5 | ControlFlowNode for b | None | test.py:2:14:2:17 | ControlFlowNode for None |
| runtime | test.py:11:5:11:7 | ControlFlowNode for bar | Function bar | test.py:2:1:2:19 | ControlFlowNode for FunctionExpr |
| runtime | test.py:12:5:12:11 | ControlFlowNode for bar() | 11 | test.py:12:9:12:10 | ControlFlowNode for IntegerLiteral |
| runtime | test.py:13:5:13:17 | ControlFlowNode for bar() | 12 | test.py:13:9:13:10 | ControlFlowNode for IntegerLiteral |
| runtime | test.py:14:5:14:17 | ControlFlowNode for bar() | True | test.py:14:13:14:16 | ControlFlowNode for True |
| runtime | test.py:15:5:15:15 | ControlFlowNode for bar() | 14 | test.py:15:9:15:10 | ControlFlowNode for IntegerLiteral |
| test.py:12 from runtime | test.py:3:5:3:5 | ControlFlowNode for a | 11 | test.py:12:9:12:10 | ControlFlowNode for IntegerLiteral |
| runtime | test.py:12:5:12:11 | ControlFlowNode for bar() | int 11 | test.py:12:9:12:10 | ControlFlowNode for IntegerLiteral |
| runtime | test.py:13:5:13:17 | ControlFlowNode for bar() | int 12 | test.py:13:9:13:10 | ControlFlowNode for IntegerLiteral |
| runtime | test.py:14:5:14:17 | ControlFlowNode for bar() | bool True | test.py:14:13:14:16 | ControlFlowNode for True |
| runtime | test.py:15:5:15:15 | ControlFlowNode for bar() | int 14 | test.py:15:9:15:10 | ControlFlowNode for IntegerLiteral |
| test.py:12 from runtime | test.py:3:5:3:5 | ControlFlowNode for a | int 11 | test.py:12:9:12:10 | ControlFlowNode for IntegerLiteral |
| test.py:12 from runtime | test.py:4:5:4:5 | ControlFlowNode for b | None | test.py:2:14:2:17 | ControlFlowNode for None |
| test.py:13 from runtime | test.py:3:5:3:5 | ControlFlowNode for a | 12 | test.py:13:9:13:10 | ControlFlowNode for IntegerLiteral |
| test.py:13 from runtime | test.py:3:5:3:5 | ControlFlowNode for a | int 12 | test.py:13:9:13:10 | ControlFlowNode for IntegerLiteral |
| test.py:13 from runtime | test.py:4:5:4:5 | ControlFlowNode for b | None | test.py:13:13:13:16 | ControlFlowNode for None |
| test.py:14 from runtime | test.py:3:5:3:5 | ControlFlowNode for a | 13 | test.py:14:9:14:10 | ControlFlowNode for IntegerLiteral |
| test.py:14 from runtime | test.py:4:5:4:5 | ControlFlowNode for b | True | test.py:14:13:14:16 | ControlFlowNode for True |
| test.py:15 from runtime | test.py:3:5:3:5 | ControlFlowNode for a | 14 | test.py:15:9:15:10 | ControlFlowNode for IntegerLiteral |
| test.py:14 from runtime | test.py:3:5:3:5 | ControlFlowNode for a | int 13 | test.py:14:9:14:10 | ControlFlowNode for IntegerLiteral |
| test.py:14 from runtime | test.py:4:5:4:5 | ControlFlowNode for b | bool True | test.py:14:13:14:16 | ControlFlowNode for True |
| test.py:15 from runtime | test.py:3:5:3:5 | ControlFlowNode for a | int 14 | test.py:15:9:15:10 | ControlFlowNode for IntegerLiteral |
| test.py:15 from runtime | test.py:4:5:4:5 | ControlFlowNode for b | '' | test.py:15:13:15:14 | ControlFlowNode for Str |
| test.py:18 from import | test.py:3:5:3:5 | ControlFlowNode for a | 21 | test.py:18:5:18:6 | ControlFlowNode for IntegerLiteral |
| test.py:18 from import | test.py:3:5:3:5 | ControlFlowNode for a | int 21 | test.py:18:5:18:6 | ControlFlowNode for IntegerLiteral |
| test.py:18 from import | test.py:4:5:4:5 | ControlFlowNode for b | None | test.py:2:14:2:17 | ControlFlowNode for None |
| test.py:19 from import | test.py:3:5:3:5 | ControlFlowNode for a | 22 | test.py:19:5:19:6 | ControlFlowNode for IntegerLiteral |
| test.py:19 from import | test.py:3:5:3:5 | ControlFlowNode for a | int 22 | test.py:19:5:19:6 | ControlFlowNode for IntegerLiteral |
| test.py:19 from import | test.py:4:5:4:5 | ControlFlowNode for b | None | test.py:19:9:19:12 | ControlFlowNode for None |
| test.py:20 from import | test.py:3:5:3:5 | ControlFlowNode for a | 23 | test.py:20:5:20:6 | ControlFlowNode for IntegerLiteral |
| test.py:20 from import | test.py:4:5:4:5 | ControlFlowNode for b | True | test.py:20:9:20:12 | ControlFlowNode for True |
| test.py:21 from import | test.py:3:5:3:5 | ControlFlowNode for a | 24 | test.py:21:5:21:6 | ControlFlowNode for IntegerLiteral |
| test.py:20 from import | test.py:3:5:3:5 | ControlFlowNode for a | int 23 | test.py:20:5:20:6 | ControlFlowNode for IntegerLiteral |
| test.py:20 from import | test.py:4:5:4:5 | ControlFlowNode for b | bool True | test.py:20:9:20:12 | ControlFlowNode for True |
| test.py:21 from import | test.py:3:5:3:5 | ControlFlowNode for a | int 24 | test.py:21:5:21:6 | ControlFlowNode for IntegerLiteral |
| test.py:21 from import | test.py:4:5:4:5 | ControlFlowNode for b | '' | test.py:21:9:21:10 | ControlFlowNode for Str |
| test.py:22 from import | test.py:3:5:3:5 | ControlFlowNode for a | 3 | test.py:22:12:22:12 | ControlFlowNode for IntegerLiteral |
| test.py:22 from import | test.py:4:5:4:5 | ControlFlowNode for b | 7 | test.py:22:7:22:7 | ControlFlowNode for IntegerLiteral |
| test.py:22 from import | test.py:3:5:3:5 | ControlFlowNode for a | int 3 | test.py:22:12:22:12 | ControlFlowNode for IntegerLiteral |
| test.py:22 from import | test.py:4:5:4:5 | ControlFlowNode for b | int 7 | test.py:22:7:22:7 | ControlFlowNode for IntegerLiteral |

View File

@@ -3,12 +3,12 @@ import python
import python
import semmle.python.pointsto.PointsTo2
import semmle.python.pointsto.PointsToContext2
import semmle.python.pointsto.PointsTo
import semmle.python.pointsto.PointsToContext
import semmle.python.objects.ObjectInternal
from ControlFlowNode f, PointsToContext2 ctx, ObjectInternal obj, ControlFlowNode orig
from ControlFlowNode f, PointsToContext ctx, Value obj, ControlFlowNode orig
where exists(ExprStmt s | s.getValue().getAFlowNode() = f) and
PointsTo2::points_to(f, ctx, obj, orig)
PointsTo::pointsTo(f, ctx, obj, orig)
select ctx, f, obj.toString(), orig