Skip to content

Commit 4ce3d5b

Browse files
authored
Merge pull request github#3040 from BekaValentine/python-objectapi-to-valueapi-iterreturnsnonself
Python: ObjectAPI to ValueAPI: IterReturnsNonSelf
2 parents 316d932 + be86c9c commit 4ce3d5b

File tree

2 files changed

+44
-9
lines changed

2 files changed

+44
-9
lines changed

python/ql/src/Functions/IterReturnsNonSelf.ql

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212

1313
import python
1414

15-
Function iter_method(ClassObject t) {
16-
result = t.lookupAttribute("__iter__").(FunctionObject).getFunction()
17-
}
15+
Function iter_method(ClassValue t) { result = t.lookup("__iter__").(FunctionValue).getScope() }
1816

1917
predicate is_self(Name value, Function f) { value.getVariable() = f.getArg(0).(Name).getVariable() }
2018

@@ -26,7 +24,7 @@ predicate returns_non_self(Function f) {
2624
exists(Return r | r.getScope() = f and not exists(r.getValue()))
2725
}
2826

29-
from ClassObject t, Function iter
27+
from ClassValue t, Function iter
3028
where t.isIterator() and iter = iter_method(t) and returns_non_self(iter)
3129
select t, "Class " + t.getName() + " is an iterator but its $@ method does not return 'self'.",
3230
iter, iter.getName()

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

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,29 @@ class ClassValue extends Value {
430430
this.hasAttribute("__getitem__")
431431
}
432432

433+
/** Holds if this class is an iterator. */
434+
predicate isIterator() {
435+
this.hasAttribute("__iter__") and
436+
(
437+
major_version() = 3 and this.hasAttribute("__next__")
438+
or
439+
/*
440+
* Because 'next' is a common method name we need to check that an __iter__
441+
* method actually returns this class. This is not needed for Py3 as the
442+
* '__next__' method exists to define a class as an iterator.
443+
*/
444+
445+
major_version() = 2 and
446+
this.hasAttribute("next") and
447+
exists(ClassValue other, FunctionValue iter | other.declaredAttribute("__iter__") = iter |
448+
iter.getAnInferredReturnType() = this
449+
)
450+
)
451+
or
452+
/* This will be redundant when we have C class information */
453+
this = ClassValue::generator()
454+
}
455+
433456
/** Holds if this class is a container(). That is, does it have a __getitem__ method. */
434457
predicate isContainer() { exists(this.lookup("__getitem__")) }
435458

@@ -583,11 +606,7 @@ abstract class FunctionValue extends CallableValue {
583606
}
584607

585608
/** Gets a class that this function may return */
586-
ClassValue getAnInferredReturnType() {
587-
result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType())
588-
or
589-
result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType())
590-
}
609+
abstract ClassValue getAnInferredReturnType();
591610
}
592611

593612
/** Class representing Python functions */
@@ -616,6 +635,13 @@ class PythonFunctionValue extends FunctionValue {
616635

617636
/** Gets a control flow node corresponding to a return statement in this function */
618637
ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() }
638+
639+
override ClassValue getAnInferredReturnType() {
640+
/* We have to do a special version of this because builtin functions have no
641+
* explicit return nodes that we can query and get the class of.
642+
*/
643+
result = this.getAReturnedNode().pointsTo().getClass()
644+
}
619645
}
620646

621647
/** Class representing builtin functions, such as `len` or `print` */
@@ -627,6 +653,13 @@ class BuiltinFunctionValue extends FunctionValue {
627653
override int minParameters() { none() }
628654

629655
override int maxParameters() { none() }
656+
657+
override ClassValue getAnInferredReturnType() {
658+
/* We have to do a special version of this because builtin functions have no
659+
* explicit return nodes that we can query and get the class of.
660+
*/
661+
result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType())
662+
}
630663
}
631664

632665
/** Class representing builtin methods, such as `list.append` or `set.add` */
@@ -644,6 +677,10 @@ class BuiltinMethodValue extends FunctionValue {
644677
override int minParameters() { none() }
645678

646679
override int maxParameters() { none() }
680+
681+
override ClassValue getAnInferredReturnType() {
682+
result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType())
683+
}
647684
}
648685

649686
/**

0 commit comments

Comments
 (0)