Skip to content

Commit fad37bd

Browse files
authored
Merge pull request #1487 from markshannon/python-tuple-assignment-points-to
Python ESSA dataflow: better handling of tuple unpacking.
2 parents 0d4ff2d + 208d313 commit fad37bd

32 files changed

+395
-66
lines changed

python/ql/src/semmle/python/Flow.qll

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,8 @@ class DefinitionNode extends ControlFlowNode {
745745
exists(Assign a | a.getATarget().(Tuple).getAnElt().getAFlowNode() = this)
746746
or
747747
exists(Assign a | a.getATarget().(List).getAnElt().getAFlowNode() = this)
748+
or
749+
exists(For for | for.getTarget().getAFlowNode() = this)
748750
}
749751

750752
/** flow node corresponding to the value assigned for the definition corresponding to this flow node */
@@ -864,7 +866,7 @@ class DictNode extends ControlFlowNode {
864866

865867
}
866868

867-
private Expr assigned_value(Expr lhs) {
869+
private AstNode assigned_value(Expr lhs) {
868870
/* lhs = result */
869871
exists(Assign a | a.getATarget() = lhs and result = a.getValue())
870872
or
@@ -881,6 +883,9 @@ private Expr assigned_value(Expr lhs) {
881883
lhs = target.getElt(index) and
882884
result = values.getElt(index)
883885
)
886+
or
887+
/* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */
888+
result.(For).getTarget() = lhs
884889
}
885890

886891
/** A flow node for a `for` statement. */

python/ql/src/semmle/python/dataflow/SsaDefinitions.qll

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,10 @@ abstract class PythonSsaSourceVariable extends SsaSourceVariable {
5858
or
5959
SsaSource::assignment_definition(this, def, _)
6060
or
61-
SsaSource::multi_assignment_definition(this, def)
61+
SsaSource::multi_assignment_definition(this, def, _, _)
6262
or
6363
SsaSource::deletion_definition(this, def)
6464
or
65-
SsaSource::iteration_defined_variable(this, def, _)
66-
or
6765
SsaSource::init_module_submodule_defn(this, def)
6866
or
6967
SsaSource::parameter_definition(this, def)
@@ -381,10 +379,11 @@ cached module SsaSource {
381379
}
382380

383381
/** Holds if `v` is defined by multiple assignment at `defn`. */
384-
cached predicate multi_assignment_definition(Variable v, ControlFlowNode defn) {
382+
cached predicate multi_assignment_definition(Variable v, ControlFlowNode defn, int n, SequenceNode lhs) {
385383
defn.(NameNode).defines(v) and
386384
not exists(defn.(DefinitionNode).getValue()) and
387-
exists(SequenceNode s | s.getAnElement() = defn)
385+
lhs.getElement(n) = defn and
386+
lhs.getBasicBlock().dominates(defn.getBasicBlock())
388387
}
389388

390389
/** Holds if `v` is defined by a `for` statement, the definition being `defn` */

python/ql/src/semmle/python/objects/Callables.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ abstract class CallableObjectInternal extends ObjectInternal {
4848

4949
override string strValue() { none() }
5050

51+
/* Callables aren't iterable */
52+
override ObjectInternal getIterNext() { none() }
53+
5154
}
5255

5356
/** Class representing Python functions */

python/ql/src/semmle/python/objects/Classes.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ abstract class ClassObjectInternal extends ObjectInternal {
8989
}
9090

9191
override predicate subscriptUnknown() { none() }
92+
93+
/* Classes aren't usually iterable, but can e.g. Enums */
94+
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
95+
9296
}
9397

9498
/** Class representing Python source classes */

python/ql/src/semmle/python/objects/Constants.qll

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ private abstract class BooleanObjectInternal extends ConstantObjectInternal {
8383
none()
8484
}
8585

86+
/* Booleans aren't iterable */
87+
override ObjectInternal getIterNext() { none() }
88+
8689
}
8790

8891
private class TrueObjectInternal extends BooleanObjectInternal, TTrue {
@@ -165,6 +168,9 @@ private class NoneObjectInternal extends ConstantObjectInternal, TNone {
165168

166169
override int length() { none() }
167170

171+
/* None isn't iterable */
172+
override ObjectInternal getIterNext() { none() }
173+
168174
}
169175

170176

@@ -203,6 +209,9 @@ private class IntObjectInternal extends ConstantObjectInternal, TInt {
203209

204210
override int length() { none() }
205211

212+
/* ints aren't iterable */
213+
override ObjectInternal getIterNext() { none() }
214+
206215
}
207216

208217
private class FloatObjectInternal extends ConstantObjectInternal, TFloat {
@@ -248,6 +257,9 @@ private class FloatObjectInternal extends ConstantObjectInternal, TFloat {
248257

249258
override int length() { none() }
250259

260+
/* floats aren't iterable */
261+
override ObjectInternal getIterNext() { none() }
262+
251263
}
252264

253265

@@ -290,6 +302,10 @@ private class UnicodeObjectInternal extends ConstantObjectInternal, TUnicode {
290302
result = this.strValue().length()
291303
}
292304

305+
override ObjectInternal getIterNext() {
306+
result = TUnknownInstance(this.getClass())
307+
}
308+
293309
}
294310

295311
private class BytesObjectInternal extends ConstantObjectInternal, TBytes {
@@ -331,6 +347,10 @@ private class BytesObjectInternal extends ConstantObjectInternal, TBytes {
331347
result = this.strValue().length()
332348
}
333349

350+
override ObjectInternal getIterNext() {
351+
result = TUnknownInstance(this.getClass())
352+
}
353+
334354
}
335355

336356

python/ql/src/semmle/python/objects/Descriptors.qll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class PropertyInternal extends ObjectInternal, TProperty {
9191
)
9292
}
9393

94+
/* Properties aren't iterable */
95+
override ObjectInternal getIterNext() { none() }
96+
9497
}
9598

9699
/** A class representing classmethods in Python */
@@ -176,6 +179,9 @@ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
176179
result = this.getFunction().getName()
177180
}
178181

182+
/* Classmethods aren't iterable */
183+
override ObjectInternal getIterNext() { none() }
184+
179185
}
180186

181187
class StaticMethodObjectInternal extends ObjectInternal, TStaticMethod {
@@ -247,4 +253,7 @@ class StaticMethodObjectInternal extends ObjectInternal, TStaticMethod {
247253
result = this.getFunction().getName()
248254
}
249255

256+
/* Staticmethods aren't iterable */
257+
override ObjectInternal getIterNext() { none() }
258+
250259
}

python/ql/src/semmle/python/objects/Instances.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ abstract class InstanceObject extends ObjectInternal {
5151

5252
override string getName() { none() }
5353

54+
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
55+
5456
}
5557

5658
private predicate self_variable_reaching_init_exit(EssaVariable self) {
@@ -366,6 +368,8 @@ class UnknownInstanceInternal extends TUnknownInstance, ObjectInternal {
366368

367369
override string getName() { none() }
368370

371+
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
372+
369373
}
370374

371375
private int lengthFromClass(ClassObjectInternal cls) {
@@ -472,5 +476,7 @@ class SuperInstance extends TSuperInstance, ObjectInternal {
472476

473477
override string getName() { none() }
474478

479+
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
480+
475481
}
476482

python/ql/src/semmle/python/objects/Modules.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ abstract class ModuleObjectInternal extends ObjectInternal {
5252
any(PackageObjectInternal package).getInitModule() = this
5353
}
5454

55+
/* Modules aren't iterable */
56+
override ObjectInternal getIterNext() { none() }
57+
5558
}
5659

5760
/** A class representing built-in modules */
@@ -408,5 +411,8 @@ class AbsentModuleAttributeObjectInternal extends ObjectInternal, TAbsentModuleA
408411
/* We know what this is called, but not its innate name */
409412
override string getName() { none() }
410413

414+
/* Modules aren't iterable */
415+
override ObjectInternal getIterNext() { none() }
416+
411417
}
412418

python/ql/src/semmle/python/objects/ObjectInternal.qll

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ class ObjectInternal extends TObject {
167167
*/
168168
abstract string getName();
169169

170+
/** Gets the 'object' resulting from iterating over this object.
171+
* Used in the context `for i in this:`. The result is the 'object'
172+
* assigned to `i`.
173+
*/
174+
abstract ObjectInternal getIterNext();
175+
170176
}
171177

172178

@@ -249,6 +255,9 @@ class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject {
249255
override string getName() {
250256
result = this.getBuiltin().getName()
251257
}
258+
259+
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
260+
252261
}
253262

254263

@@ -326,6 +335,8 @@ class UnknownInternal extends ObjectInternal, TUnknown {
326335

327336
override string getName() { none() }
328337

338+
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
339+
329340
}
330341

331342
class UndefinedInternal extends ObjectInternal, TUndefined {
@@ -404,6 +415,8 @@ class UndefinedInternal extends ObjectInternal, TUndefined {
404415

405416
override string getName() { none() }
406417

418+
override ObjectInternal getIterNext() { none() }
419+
407420
}
408421

409422
module ObjectInternal {

python/ql/src/semmle/python/objects/Sequences.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ abstract class SequenceObjectInternal extends ObjectInternal {
3232

3333
override string getName() { none() }
3434

35+
override ObjectInternal getIterNext() { result = this.getItem(_) }
36+
3537
}
3638

3739
abstract class TupleObjectInternal extends SequenceObjectInternal {

0 commit comments

Comments
 (0)