diff --git a/python/ql/src/Statements/DocStrings.ql b/python/ql/src/Statements/DocStrings.ql
index 7c55c65e204..d6a1d812300 100644
--- a/python/ql/src/Statements/DocStrings.ql
+++ b/python/ql/src/Statements/DocStrings.ql
@@ -27,8 +27,8 @@ predicate needs_docstring(Scope s) {
}
predicate function_needs_docstring(Function f) {
- not exists(FunctionObject fo, FunctionObject base | fo.overrides(base) and fo.getFunction() = f |
- not function_needs_docstring(base.getFunction())
+ not exists(FunctionValue fo, FunctionValue base | fo.overrides(base) and fo.getScope() = f |
+ not function_needs_docstring(base.getScope())
) and
f.getName() != "lambda" and
(f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2 and
diff --git a/python/ql/src/Statements/IterableStringOrSequence.ql b/python/ql/src/Statements/IterableStringOrSequence.ql
index 96872a9a556..fb7b2198bda 100644
--- a/python/ql/src/Statements/IterableStringOrSequence.ql
+++ b/python/ql/src/Statements/IterableStringOrSequence.ql
@@ -13,21 +13,15 @@
import python
-predicate is_a_string_type(ClassObject seqtype) {
- seqtype = theBytesType() and major_version() = 2
- or
- seqtype = theUnicodeType()
-}
from
- For loop, ControlFlowNode iter, Object str, Object seq, ControlFlowNode seq_origin,
- ClassObject strtype, ClassObject seqtype, ControlFlowNode str_origin
+ For loop, ControlFlowNode iter, Value str, Value seq, ControlFlowNode seq_origin, ControlFlowNode str_origin
where
loop.getIter().getAFlowNode() = iter and
- iter.refersTo(str, strtype, str_origin) and
- iter.refersTo(seq, seqtype, seq_origin) and
- is_a_string_type(strtype) and
- seqtype.isIterable() and
- not is_a_string_type(seqtype)
-select loop, "Iteration over $@, of class " + seqtype.getName() + ", may also iterate over $@.",
+ iter.pointsTo(str, str_origin) and
+ iter.pointsTo(seq, seq_origin) and
+ str.getClass() = ClassValue::str() and
+ seq.getClass().isIterable() and
+ not seq.getClass() = ClassValue::str()
+select loop, "Iteration over $@, of class " + seq.getClass().getName() + ", may also iterate over $@.",
seq_origin, "sequence", str_origin, "string"
diff --git a/python/ql/src/Statements/MismatchInMultipleAssignment.ql b/python/ql/src/Statements/MismatchInMultipleAssignment.ql
index 2c619c78857..157ddf1270b 100644
--- a/python/ql/src/Statements/MismatchInMultipleAssignment.ql
+++ b/python/ql/src/Statements/MismatchInMultipleAssignment.ql
@@ -36,15 +36,15 @@ predicate mismatched(Assign a, int lcount, int rcount, Location loc, string sequ
}
predicate mismatched_tuple_rhs(Assign a, int lcount, int rcount, Location loc) {
- exists(ExprList l, TupleObject r, AstNode origin |
+ exists(ExprList l, TupleValue r, AstNode origin |
(
a.getATarget().(Tuple).getElts() = l or
a.getATarget().(List).getElts() = l
) and
- a.getValue().refersTo(r, origin) and
+ a.getValue().pointsTo(r, origin) and
loc = origin.getLocation() and
lcount = len(l) and
- rcount = r.getLength() and
+ rcount = r.length() and
lcount != rcount and
not exists(Starred s | l.getAnItem() = s)
)
diff --git a/python/ql/src/Statements/ModificationOfLocals.ql b/python/ql/src/Statements/ModificationOfLocals.ql
index 38e5ccabcf5..2117748f3e2 100644
--- a/python/ql/src/Statements/ModificationOfLocals.ql
+++ b/python/ql/src/Statements/ModificationOfLocals.ql
@@ -12,23 +12,17 @@
import python
-Object aFunctionLocalsObject() {
- exists(Call c, Name n, GlobalVariable v |
- c = result.getOrigin() and
- n = c.getFunc() and
- n.getVariable() = v and
- v.getId() = "locals" and
- c.getScope() instanceof FastLocalsFunction
- )
+predicate originIsLocals(ControlFlowNode n) {
+ n.pointsTo(_, _, Value::named("locals").getACall())
}
predicate modification_of_locals(ControlFlowNode f) {
- f.(SubscriptNode).getObject().refersTo(aFunctionLocalsObject()) and
+ originIsLocals(f.(SubscriptNode).getObject()) and
(f.isStore() or f.isDelete())
or
exists(string mname, AttrNode attr |
attr = f.(CallNode).getFunction() and
- attr.getObject(mname).refersTo(aFunctionLocalsObject(), _)
+ originIsLocals(attr.getObject(mname))
|
mname = "pop" or
mname = "popitem" or
diff --git a/python/ql/src/Statements/NonIteratorInForLoop.qhelp b/python/ql/src/Statements/NonIteratorInForLoop.qhelp
index 0165db9fea9..5be3ff5d21f 100644
--- a/python/ql/src/Statements/NonIteratorInForLoop.qhelp
+++ b/python/ql/src/Statements/NonIteratorInForLoop.qhelp
@@ -17,7 +17,7 @@ for addressing the defect.
-In this example, the loop may attempt to iterate over None, which is not an iterator.
+In this example, the loop may attempt to iterate over None, which is not an iterable.
It is likely that the programmer forgot to test for None before the loop.
diff --git a/python/ql/src/Statements/NonIteratorInForLoop.ql b/python/ql/src/Statements/NonIteratorInForLoop.ql
index 26c3005e5fc..85982ccc030 100644
--- a/python/ql/src/Statements/NonIteratorInForLoop.ql
+++ b/python/ql/src/Statements/NonIteratorInForLoop.ql
@@ -22,4 +22,4 @@ where
not t.failedInference(_) and
not v = Value::named("None") and
not t.isDescriptorType()
-select loop, "$@ of class '$@' may be used in for-loop.", origin, "Non-iterator", t, t.getName()
+select loop, "$@ of class '$@' may be used in for-loop.", origin, "Non-iterable", t, t.getName()
diff --git a/python/ql/src/Statements/RedundantAssignment.ql b/python/ql/src/Statements/RedundantAssignment.ql
index 9f9f41fa52b..6688d248dcc 100644
--- a/python/ql/src/Statements/RedundantAssignment.ql
+++ b/python/ql/src/Statements/RedundantAssignment.ql
@@ -37,21 +37,30 @@ predicate maybe_defined_in_outer_scope(Name n) {
exists(SsaVariable v | v.getAUse().getNode() = n | v.maybeUndefined())
}
-Variable relevant_var(Name n) {
- n.getVariable() = result and
- (corresponding(n, _) or corresponding(_, n))
+/* Protection against FPs in projects that offer compatibility between Python 2 and 3,
+ * since many of them make assignments such as
+ *
+ * if PY2:
+ * bytes = str
+ * else:
+ * bytes = bytes
+ *
+ */
+predicate isBuiltin(string name) {
+ exists(Value v | v = Value::named(name) and v.isBuiltin())
}
predicate same_name(Name n1, Name n2) {
corresponding(n1, n2) and
- relevant_var(n1) = relevant_var(n2) and
- not exists(Object::builtin(n1.getId())) and
+ n1.getVariable() = n2.getVariable() and
+ not isBuiltin(n1.getId()) and
not maybe_defined_in_outer_scope(n2)
}
ClassObject value_type(Attribute a) { a.getObject().refersTo(_, result, _) }
predicate is_property_access(Attribute a) {
+ // TODO: We need something to model PropertyObject in the Value API
value_type(a).lookupAttribute(a.getName()) instanceof PropertyObject
}
@@ -77,9 +86,9 @@ predicate pyflakes_commented(AssignStmt assignment) {
}
predicate side_effecting_lhs(Attribute lhs) {
- exists(ClassObject cls, ClassObject decl |
- lhs.getObject().refersTo(_, cls, _) and
- decl = cls.getAnImproperSuperType() and
+ exists(ClassValue cls, ClassValue decl |
+ lhs.getObject().pointsTo().getClass() = cls and
+ decl = cls.getASuperType() and
not decl.isBuiltin()
|
decl.declaresAttribute("__setattr__")
@@ -90,6 +99,7 @@ from AssignStmt a, Expr left, Expr right
where
assignment(a, left, right) and
same_value(left, right) and
+ // some people use self-assignment to shut Pyflakes up, such as `ok = ok # Pyflakes`
not pyflakes_commented(a) and
not side_effecting_lhs(left)
select a, "This assignment assigns a variable to itself."
diff --git a/python/ql/src/Statements/ShouldUseWithStatement.ql b/python/ql/src/Statements/ShouldUseWithStatement.ql
index f86ba21b4b9..9be28d5ceec 100644
--- a/python/ql/src/Statements/ShouldUseWithStatement.ql
+++ b/python/ql/src/Statements/ShouldUseWithStatement.ql
@@ -22,12 +22,12 @@ predicate only_stmt_in_finally(Try t, Call c) {
)
}
-predicate points_to_context_manager(ControlFlowNode f, ClassObject cls) {
- cls.isContextManager() and
- forex(Object obj | f.refersTo(obj) | f.refersTo(obj, cls, _))
+predicate points_to_context_manager(ControlFlowNode f, ClassValue cls) {
+ forex(Value v | f.pointsTo(v) | v.getClass() = cls) and
+ cls.isContextManager()
}
-from Call close, Try t, ClassObject cls
+from Call close, Try t, ClassValue cls
where
only_stmt_in_finally(t, close) and
calls_close(close) and
diff --git a/python/ql/src/Statements/StatementNoEffect.ql b/python/ql/src/Statements/StatementNoEffect.ql
index a2bfdd5666f..0a80f8f9aa4 100644
--- a/python/ql/src/Statements/StatementNoEffect.ql
+++ b/python/ql/src/Statements/StatementNoEffect.ql
@@ -13,28 +13,28 @@
import python
-predicate understood_attribute(Attribute attr, ClassObject cls, ClassObject attr_cls) {
+predicate understood_attribute(Attribute attr, ClassValue cls, ClassValue attr_cls) {
exists(string name | attr.getName() = name |
- attr.getObject().refersTo(_, cls, _) and
- cls.attributeRefersTo(name, _, attr_cls, _)
+ attr.getObject().pointsTo().getClass() = cls and
+ cls.attr(name).getClass() = attr_cls
)
}
/* Conservative estimate of whether attribute lookup has a side effect */
predicate side_effecting_attribute(Attribute attr) {
- exists(ClassObject cls, ClassObject attr_cls |
+ exists(ClassValue cls, ClassValue attr_cls |
understood_attribute(attr, cls, attr_cls) and
side_effecting_descriptor_type(attr_cls)
)
}
predicate maybe_side_effecting_attribute(Attribute attr) {
- not understood_attribute(attr, _, _) and not attr.refersTo(_)
+ not understood_attribute(attr, _, _) and not attr.pointsTo(_)
or
side_effecting_attribute(attr)
}
-predicate side_effecting_descriptor_type(ClassObject descriptor) {
+predicate side_effecting_descriptor_type(ClassValue descriptor) {
descriptor.isDescriptorType() and
/*
* Technically all descriptor gets have side effects,
@@ -42,9 +42,9 @@ predicate side_effecting_descriptor_type(ClassObject descriptor) {
* we want to treat them as having no effect.
*/
- not descriptor = thePyFunctionType() and
- not descriptor = theStaticMethodType() and
- not descriptor = theClassMethodType()
+ not descriptor = ClassValue::function() and
+ not descriptor = ClassValue::staticmethod() and
+ not descriptor = ClassValue::classmethod()
}
/**
@@ -52,39 +52,39 @@ predicate side_effecting_descriptor_type(ClassObject descriptor) {
* side-effecting unless we know otherwise.
*/
predicate side_effecting_binary(Expr b) {
- exists(Expr sub, ClassObject cls, string method_name |
+ exists(Expr sub, ClassValue cls, string method_name |
binary_operator_special_method(b, sub, cls, method_name)
or
comparison_special_method(b, sub, cls, method_name)
|
method_name = special_method() and
cls.hasAttribute(method_name) and
- not exists(ClassObject declaring |
+ not exists(ClassValue declaring |
declaring.declaresAttribute(method_name) and
- declaring = cls.getAnImproperSuperType() and
+ declaring = cls.getASuperType() and
declaring.isBuiltin() and
- not declaring = theObjectType()
+ not declaring = ClassValue::object()
)
)
}
pragma[nomagic]
private predicate binary_operator_special_method(
- BinaryExpr b, Expr sub, ClassObject cls, string method_name
+ BinaryExpr b, Expr sub, ClassValue cls, string method_name
) {
method_name = special_method() and
sub = b.getLeft() and
method_name = b.getOp().getSpecialMethodName() and
- sub.refersTo(_, cls, _)
+ sub.pointsTo().getClass() = cls
}
pragma[nomagic]
-private predicate comparison_special_method(Compare b, Expr sub, ClassObject cls, string method_name) {
+private predicate comparison_special_method(Compare b, Expr sub, ClassValue cls, string method_name) {
exists(Cmpop op |
b.compares(sub, op, _) and
method_name = op.getSpecialMethodName()
) and
- sub.refersTo(_, cls, _)
+ sub.pointsTo().getClass() = cls
}
private string special_method() {
@@ -102,9 +102,8 @@ predicate is_notebook(File f) {
/** Expression (statement) in a jupyter/ipython notebook */
predicate in_notebook(Expr e) { is_notebook(e.getScope().(Module).getFile()) }
-FunctionObject assertRaises() {
- result =
- ModuleObject::named("unittest").attr("TestCase").(ClassObject).lookupAttribute("assertRaises")
+FunctionValue assertRaises() {
+ result = Value::named("unittest.TestCase").(ClassValue).lookup("assertRaises")
}
/** Holds if expression `e` is in a `with` block that tests for exceptions being raised. */
@@ -124,6 +123,7 @@ predicate python2_print(Expr e) {
}
predicate no_effect(Expr e) {
+ // strings can be used as comments
not e instanceof StrConst and
not e.hasSideEffects() and
forall(Expr sub | sub = e.getASubExpression*() |
diff --git a/python/ql/src/Statements/StringConcatenationInLoop.qhelp b/python/ql/src/Statements/StringConcatenationInLoop.qhelp
index 8d8f494ddd0..b1350e70478 100644
--- a/python/ql/src/Statements/StringConcatenationInLoop.qhelp
+++ b/python/ql/src/Statements/StringConcatenationInLoop.qhelp
@@ -3,13 +3,13 @@
"qhelp.dtd">
-If you concatenate strings in a loop then the time taken by the loop is quadratic in the number
+
If you concatenate strings in a loop then the time taken by the loop is quadratic in the number
of iterations.
-Initialize an empty list before the start of the list.
+
Initialize an empty list before the start of the loop.
During the loop append the substrings to the list.
At the end of the loop, convert the list to a string by using ''.join(list).
diff --git a/python/ql/src/Statements/StringConcatenationInLoop.ql b/python/ql/src/Statements/StringConcatenationInLoop.ql
index 3e0f72f3356..f225e27cdcd 100644
--- a/python/ql/src/Statements/StringConcatenationInLoop.ql
+++ b/python/ql/src/Statements/StringConcatenationInLoop.ql
@@ -14,13 +14,12 @@ import python
predicate string_concat_in_loop(BinaryExpr b) {
b.getOp() instanceof Add and
- exists(SsaVariable d, SsaVariable u, BinaryExprNode add, ClassObject str_type |
+ exists(SsaVariable d, SsaVariable u, BinaryExprNode add |
add.getNode() = b and d = u.getAnUltimateDefinition()
|
d.getDefinition().(DefinitionNode).getValue() = add and
u.getAUse() = add.getAnOperand() and
- add.getAnOperand().refersTo(_, str_type, _) and
- (str_type = theBytesType() or str_type = theUnicodeType())
+ add.getAnOperand().pointsTo().getClass() = ClassValue::str()
)
}
diff --git a/python/ql/src/Statements/TopLevelPrint.ql b/python/ql/src/Statements/TopLevelPrint.ql
index f295e1b0132..104af619049 100644
--- a/python/ql/src/Statements/TopLevelPrint.ql
+++ b/python/ql/src/Statements/TopLevelPrint.ql
@@ -34,6 +34,7 @@ predicate is_print_stmt(Stmt s) {
from Stmt p
where
is_print_stmt(p) and
+ // TODO: Need to discuss how we would like to handle ModuleObject.getKind in the glorious future
exists(ModuleObject m | m.getModule() = p.getScope() and m.getKind() = "module") and
not exists(If i | main_eq_name(i) and i.getASubStatement().getASubStatement*() = p)
select p, "Print statement may execute during import."
diff --git a/python/ql/src/Statements/UnnecessaryDelete.ql b/python/ql/src/Statements/UnnecessaryDelete.ql
index ac11f782b32..48f167ca706 100644
--- a/python/ql/src/Statements/UnnecessaryDelete.ql
+++ b/python/ql/src/Statements/UnnecessaryDelete.ql
@@ -27,8 +27,8 @@ where
* reference cycle, and an explicit call to `del` helps break this cycle.
*/
- not exists(FunctionObject ex |
- ex.hasLongName("sys.exc_info") and
+ not exists(FunctionValue ex |
+ ex = Value::named("sys.exc_info") and
ex.getACall().getScope() = f
)
select del, "Unnecessary deletion of local variable $@ in function $@.", e.getLocation(),
diff --git a/python/ql/src/Statements/UnusedExceptionObject.ql b/python/ql/src/Statements/UnusedExceptionObject.ql
index b1d20753b4a..32b59113c5b 100644
--- a/python/ql/src/Statements/UnusedExceptionObject.ql
+++ b/python/ql/src/Statements/UnusedExceptionObject.ql
@@ -12,9 +12,9 @@
import python
-from Call call, ClassObject ex
+from Call call, ClassValue ex
where
- call.getFunc().refersTo(ex) and
- ex.getAnImproperSuperType() = theExceptionType() and
+ call.getFunc().pointsTo(ex) and
+ ex.getASuperType() = ClassValue::exception() and
exists(ExprStmt s | s.getValue() = call)
select call, "Instantiating an exception, but not raising it, has no effect"
diff --git a/python/ql/src/Statements/UseOfExit.ql b/python/ql/src/Statements/UseOfExit.ql
index 495a5d738da..31e0e51ab39 100644
--- a/python/ql/src/Statements/UseOfExit.ql
+++ b/python/ql/src/Statements/UseOfExit.ql
@@ -12,7 +12,7 @@
import python
from CallNode call, string name
-where call.getFunction().refersTo(Object::quitter(name))
+where call.getFunction().pointsTo(Value::siteQuitter(name))
select call,
"The '" + name +
"' site.Quitter object may not exist if the 'site' module is not loaded or is modified."
diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll
index 1f1f7001da1..42e6bc95e7a 100644
--- a/python/ql/src/semmle/python/objects/ObjectAPI.qll
+++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll
@@ -301,6 +301,19 @@ module Value {
result = ObjectInternal::none_()
}
+ /**
+ * Shorcuts added by the `site` module to exit your interactive session.
+ *
+ * see https://docs.python.org/3/library/constants.html#constants-added-by-the-site-module
+ */
+ Value siteQuitter(string name) {
+ (
+ name = "exit"
+ or
+ name = "quit"
+ ) and
+ result = Value::named(name)
+ }
}
/** Class representing callables in the Python program
@@ -418,6 +431,12 @@ class ClassValue extends Value {
this.hasAttribute("__get__")
}
+ /** Holds if this class is a context manager. */
+ predicate isContextManager() {
+ this.hasAttribute("__enter__") and
+ this.hasAttribute("__exit__")
+ }
+
/** Gets the qualified name for this class.
* Should return the same name as the `__qualname__` attribute on classes in Python 3.
*/
@@ -698,34 +717,34 @@ module ClassValue {
ClassValue bool() {
result = TBuiltinClassObject(Builtin::special("bool"))
}
-
+
/** Get the `ClassValue` for the `tuple` class. */
ClassValue tuple() {
result = TBuiltinClassObject(Builtin::special("tuple"))
}
-
+
/** Get the `ClassValue` for the `list` class. */
ClassValue list() {
result = TBuiltinClassObject(Builtin::special("list"))
}
-
+
/** Get the `ClassValue` for `xrange` (Python 2), or `range` (only Python 3) */
ClassValue range() {
major_version() = 2 and result = TBuiltinClassObject(Builtin::special("xrange"))
or
major_version() = 3 and result = TBuiltinClassObject(Builtin::special("range"))
}
-
+
/** Get the `ClassValue` for the `dict` class. */
ClassValue dict() {
result = TBuiltinClassObject(Builtin::special("dict"))
}
-
+
/** Get the `ClassValue` for the `set` class. */
ClassValue set() {
result = TBuiltinClassObject(Builtin::special("set"))
}
-
+
/** Get the `ClassValue` for the `object` class. */
ClassValue object() {
result = TBuiltinClassObject(Builtin::special("object"))
@@ -735,7 +754,7 @@ module ClassValue {
ClassValue int_() {
result = TBuiltinClassObject(Builtin::special("int"))
}
-
+
/** Get the `ClassValue` for the `long` class. */
ClassValue long() {
result = TBuiltinClassObject(Builtin::special("long"))
@@ -745,7 +764,7 @@ module ClassValue {
ClassValue float_() {
result = TBuiltinClassObject(Builtin::special("float"))
}
-
+
/** Get the `ClassValue` for the `complex` class. */
ClassValue complex() {
result = TBuiltinClassObject(Builtin::special("complex"))
@@ -770,12 +789,12 @@ module ClassValue {
else
result = unicode()
}
-
+
/** Get the `ClassValue` for the `property` class. */
ClassValue property() {
result = TBuiltinClassObject(Builtin::special("property"))
}
-
+
/** Get the `ClassValue` for the class of Python functions. */
ClassValue functionType() {
result = TBuiltinClassObject(Builtin::special("FunctionType"))
@@ -785,32 +804,32 @@ module ClassValue {
ClassValue builtinFunction() {
result = Value::named("len").getClass()
}
-
+
/** Get the `ClassValue` for the `generatorType` class. */
ClassValue generator() {
result = TBuiltinClassObject(Builtin::special("generator"))
}
-
+
/** Get the `ClassValue` for the `type` class. */
ClassValue type() {
result = TType()
}
-
+
/** Get the `ClassValue` for `ClassType`. */
ClassValue classType() {
result = TBuiltinClassObject(Builtin::special("ClassType"))
}
-
+
/** Get the `ClassValue` for `InstanceType`. */
ClassValue instanceType() {
result = TBuiltinClassObject(Builtin::special("InstanceType"))
}
-
+
/** Get the `ClassValue` for `super`. */
ClassValue super_() {
result = TBuiltinClassObject(Builtin::special("super"))
}
-
+
/** Get the `ClassValue` for the `classmethod` class. */
ClassValue classmethod() {
result = TBuiltinClassObject(Builtin::special("ClassMethod"))
@@ -820,23 +839,23 @@ module ClassValue {
ClassValue staticmethod() {
result = TBuiltinClassObject(Builtin::special("StaticMethod"))
}
-
+
/** Get the `ClassValue` for the `MethodType` class. */
pragma [noinline]
ClassValue methodType() {
result = TBuiltinClassObject(Builtin::special("MethodType"))
}
-
+
/** Get the `ClassValue` for the `MethodDescriptorType` class. */
ClassValue methodDescriptorType() {
result = TBuiltinClassObject(Builtin::special("MethodDescriptorType"))
}
-
+
/** Get the `ClassValue` for the `GetSetDescriptorType` class. */
ClassValue getSetDescriptorType() {
result = TBuiltinClassObject(Builtin::special("GetSetDescriptorType"))
}
-
+
/** Get the `ClassValue` for the `StopIteration` class. */
ClassValue stopIteration() {
result = TBuiltinClassObject(Builtin::special("StopIteration"))
@@ -851,17 +870,17 @@ module ClassValue {
ClassValue exception() {
result = TBuiltinClassObject(Builtin::special("Exception"))
}
-
+
/** Get the `ClassValue` for the `BaseException` class. */
ClassValue baseException() {
result = TBuiltinClassObject(Builtin::special("BaseException"))
}
-
+
/** Get the `ClassValue` for the `NoneType` class. */
ClassValue nonetype() {
result = TBuiltinClassObject(Builtin::special("NoneType"))
}
-
+
/** Get the `ClassValue` for the `TypeError` class */
ClassValue typeError() {
result = TBuiltinClassObject(Builtin::special("TypeError"))
@@ -871,22 +890,22 @@ module ClassValue {
ClassValue nameError() {
result = TBuiltinClassObject(Builtin::builtin("NameError"))
}
-
+
/** Get the `ClassValue` for the `AttributeError` class. */
ClassValue attributeError() {
result = TBuiltinClassObject(Builtin::builtin("AttributeError"))
}
-
+
/** Get the `ClassValue` for the `KeyError` class. */
ClassValue keyError() {
result = TBuiltinClassObject(Builtin::builtin("KeyError"))
}
-
+
/** Get the `ClassValue` for the `IOError` class. */
ClassValue ioError() {
result = TBuiltinClassObject(Builtin::builtin("IOError"))
}
-
+
/** Get the `ClassValue` for the `NotImplementedError` class. */
ClassValue notImplementedError() {
result = TBuiltinClassObject(Builtin::builtin("NotImplementedError"))
diff --git a/python/ql/test/query-tests/Statements/general/MismatchInMultipleAssignment.expected b/python/ql/test/query-tests/Statements/general/MismatchInMultipleAssignment.expected
index 4228f4970ea..b4755862d85 100644
--- a/python/ql/test/query-tests/Statements/general/MismatchInMultipleAssignment.expected
+++ b/python/ql/test/query-tests/Statements/general/MismatchInMultipleAssignment.expected
@@ -1,4 +1,4 @@
| statements_test.py:19:5:19:18 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 2. | statements_test.py:19:15:19:18 | statements_test.py:19 | tuple |
-| statements_test.py:167:5:167:23 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 5. | statements_test.py:167:13:167:23 | statements_test.py:167 | list |
-| statements_test.py:176:5:176:48 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 5. | statements_test.py:171:16:171:24 | statements_test.py:171 | tuple |
-| statements_test.py:176:5:176:48 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 6. | statements_test.py:173:16:173:26 | statements_test.py:173 | tuple |
+| statements_test.py:169:5:169:23 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 5. | statements_test.py:169:13:169:23 | statements_test.py:169 | list |
+| statements_test.py:178:5:178:48 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 5. | statements_test.py:173:16:173:24 | statements_test.py:173 | tuple |
+| statements_test.py:178:5:178:48 | AssignStmt | Left hand side of assignment contains 3 variables, but right hand side is a $@ of length 6. | statements_test.py:175:16:175:26 | statements_test.py:175 | tuple |
diff --git a/python/ql/test/query-tests/Statements/general/NonIteratorInForLoop.expected b/python/ql/test/query-tests/Statements/general/NonIteratorInForLoop.expected
index 308f0aa027e..38d063a8c0e 100644
--- a/python/ql/test/query-tests/Statements/general/NonIteratorInForLoop.expected
+++ b/python/ql/test/query-tests/Statements/general/NonIteratorInForLoop.expected
@@ -1 +1 @@
-| test.py:50:1:50:23 | For | $@ of class '$@' may be used in for-loop. | test.py:50:10:50:22 | ControlFlowNode for NonIterator() | Non-iterator | test.py:45:1:45:26 | class NonIterator | NonIterator |
+| test.py:50:1:50:23 | For | $@ of class '$@' may be used in for-loop. | test.py:50:10:50:22 | ControlFlowNode for NonIterator() | Non-iterable | test.py:45:1:45:26 | class NonIterator | NonIterator |
diff --git a/python/ql/test/query-tests/Statements/general/RedundantAssignment.expected b/python/ql/test/query-tests/Statements/general/RedundantAssignment.expected
index f997ac54932..72e7116247a 100644
--- a/python/ql/test/query-tests/Statements/general/RedundantAssignment.expected
+++ b/python/ql/test/query-tests/Statements/general/RedundantAssignment.expected
@@ -1,3 +1,3 @@
| statements_test.py:54:5:54:9 | AssignStmt | This assignment assigns a variable to itself. |
| statements_test.py:57:9:57:19 | AssignStmt | This assignment assigns a variable to itself. |
-| statements_test.py:117:9:117:23 | AssignStmt | This assignment assigns a variable to itself. |
+| statements_test.py:119:9:119:23 | AssignStmt | This assignment assigns a variable to itself. |
diff --git a/python/ql/test/query-tests/Statements/general/UnnecessaryDelete.expected b/python/ql/test/query-tests/Statements/general/UnnecessaryDelete.expected
index 6f3d9c11e2a..d7dda673775 100644
--- a/python/ql/test/query-tests/Statements/general/UnnecessaryDelete.expected
+++ b/python/ql/test/query-tests/Statements/general/UnnecessaryDelete.expected
@@ -1 +1 @@
-| statements_test.py:185:5:185:9 | Delete | Unnecessary deletion of local variable $@ in function $@. | statements_test.py:185:9:185:9 | statements_test.py:185 | x | statements_test.py:183:1:183:31 | statements_test.py:183 | error_unnecessary_delete |
+| statements_test.py:187:5:187:9 | Delete | Unnecessary deletion of local variable $@ in function $@. | statements_test.py:187:9:187:9 | statements_test.py:187 | x | statements_test.py:185:1:185:31 | statements_test.py:185 | error_unnecessary_delete |
diff --git a/python/ql/test/query-tests/Statements/general/UnnecessaryElseClause.expected b/python/ql/test/query-tests/Statements/general/UnnecessaryElseClause.expected
index 6f333fc7bbf..830c2cd10ca 100644
--- a/python/ql/test/query-tests/Statements/general/UnnecessaryElseClause.expected
+++ b/python/ql/test/query-tests/Statements/general/UnnecessaryElseClause.expected
@@ -1,2 +1,2 @@
-| statements_test.py:63:1:63:19 | For | This 'for' statement has a redundant 'else' as no 'break' is present in the body. |
-| statements_test.py:68:1:68:13 | While | This 'while' statement has a redundant 'else' as no 'break' is present in the body. |
+| statements_test.py:65:1:65:19 | For | This 'for' statement has a redundant 'else' as no 'break' is present in the body. |
+| statements_test.py:70:1:70:13 | While | This 'while' statement has a redundant 'else' as no 'break' is present in the body. |
diff --git a/python/ql/test/query-tests/Statements/general/statements_test.py b/python/ql/test/query-tests/Statements/general/statements_test.py
index 897f95e0d44..0a74bb31c10 100644
--- a/python/ql/test/query-tests/Statements/general/statements_test.py
+++ b/python/ql/test/query-tests/Statements/general/statements_test.py
@@ -56,8 +56,10 @@ class Redundant(object):
def __init__(self, args):
args = args # violation
-#Non redundant assignment
-len = len
+if sys.version_info < (3,):
+ bytes = str
+else:
+ bytes = bytes # Should not be flagged
#Pointless else clauses
for x in range(10):