Skip to content

Commit 3ce250b

Browse files
committed
Adds some debugging changes.
1 parent 178acc8 commit 3ce250b

File tree

2 files changed

+64
-3
lines changed

2 files changed

+64
-3
lines changed

python/ql/src/Expressions/ExpectedMappingForFormatString.ql

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,37 @@
1313
import python
1414
import semmle.python.strings
1515

16+
/* modernized query
1617
from Expr e, ClassValue t
1718
where exists(BinaryExpr b | b.getOp() instanceof Mod and format_string(b.getLeft()) and e = b.getRight() and
1819
mapping_format(b.getLeft()) and e.pointsTo(_, t, _) and not t.isMapping())
19-
select e, "Right hand side of a % operator must be a mapping, not class $@.", t, t.getName()
20+
select e, "Right hand side of a % operator must be a mapping, not class $@.", t, t.getName()
21+
//*/
22+
23+
/* original query
24+
from Expr e, ClassObject t
25+
where exists(BinaryExpr b | b.getOp() instanceof Mod and format_string(b.getLeft()) and e = b.getRight() and
26+
mapping_format(b.getLeft()) and e.refersTo(_, t, _) and not t.isMapping())
27+
select e, "Right hand side of a % operator must be a mapping, not class $@.", t, t.getName()
28+
//*/
29+
30+
//* debug query
31+
from Expr e, ClassValue t, string s
32+
where any()
33+
and exists(BinaryExpr b | any()
34+
and b.getOp() instanceof Mod
35+
and format_string(b.getLeft())
36+
and e = b.getRight()
37+
and mapping_format(b.getLeft())
38+
and e.pointsTo().getClass() = t
39+
//and not t.isMapping()
40+
)
41+
and if t.hasAttribute("__getitem__") then s = "yes" else s = "no"
42+
select
43+
e
44+
, "Right hand side of a % operator must be a mapping, not class $@."
45+
, t
46+
, t.getName()
47+
, s
48+
, s
49+
//*/

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

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,14 +403,45 @@ class ClassValue extends Value {
403403
this.hasAttribute("__getitem__")
404404
}
405405

406+
/** Holds if this class is a container(). That is, does it have a __getitem__ method.*/
407+
predicate isContainer() {
408+
exists(this.lookup("__getitem__"))
409+
}
410+
411+
/** Holds if this class is probably a sequence. */
412+
predicate isSequence() {
413+
/* To determine whether something is a sequence or a mapping is not entirely clear,
414+
* so we need to guess a bit.
415+
*/
416+
this.getASuperType() = ClassValue::tupleType()
417+
or
418+
this.getASuperType() = ClassValue::list()
419+
or
420+
this.getASuperType() = ClassValue::rangeType()
421+
or
422+
this.getASuperType() = ClassValue::bytes()
423+
or
424+
this.getASuperType() = ClassValue::unicode()
425+
or
426+
/* Does this inherit from abc.Sequence? */
427+
this.getASuperType().getName() = "Sequence"
428+
or
429+
/* Does it have an index or __reversed__ method? */
430+
this.isContainer() and
431+
(
432+
this.hasAttribute("index") or
433+
this.hasAttribute("__reversed__")
434+
)
435+
}
436+
406437
/** Holds if this class is a mapping.
407438
*
408439
* This is an attempt to translate ClassObject::isMapping()
409440
*/
410441
predicate isMapping() {
411-
exists(this.attr("__getitem__"))
442+
this.hasAttribute("__getitem__")
412443
and
413-
not this instanceof SequenceValue
444+
not this.isSequence()
414445
}
415446

416447
/** Holds if this class is a descriptor. */

0 commit comments

Comments
 (0)