Skip to content

Commit 9eebe00

Browse files
authored
Merge pull request #1869 from taus-semmle/python-fix-typehint-divergence
Python: Prevent divergence in type-hint analysis. (ODASA-8075)
2 parents 4952ad5 + 8882f14 commit 9eebe00

File tree

11 files changed

+72
-0
lines changed

11 files changed

+72
-0
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
163163

164164
override predicate useOriginAsLegacyObject() { none() }
165165

166+
override predicate isNotSubscriptedType() { any() }
167+
166168
}
167169

168170

@@ -288,6 +290,8 @@ class BuiltinFunctionObjectInternal extends CallableObjectInternal, TBuiltinFunc
288290

289291
override predicate useOriginAsLegacyObject() { none() }
290292

293+
override predicate isNotSubscriptedType() { any() }
294+
291295
}
292296

293297
/** Class representing methods of built-in classes (otherwise known as method-descriptors) such as `list.append`.
@@ -382,6 +386,8 @@ class BuiltinMethodObjectInternal extends CallableObjectInternal, TBuiltinMethod
382386

383387
override predicate useOriginAsLegacyObject() { none() }
384388

389+
override predicate isNotSubscriptedType() { any() }
390+
385391
}
386392

387393
/** Class representing bound-methods.
@@ -473,4 +479,6 @@ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod {
473479
this.getFunction().contextSensitiveCallee()
474480
}
475481

482+
override predicate isNotSubscriptedType() { any() }
483+
476484
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ abstract class ClassObjectInternal extends ObjectInternal {
103103
Types::getBase(this, _).hasAttribute(name)
104104
}
105105

106+
override predicate isNotSubscriptedType() { any() }
107+
106108
}
107109

108110
/** Class representing Python source classes */
@@ -445,6 +447,8 @@ class SubscriptedTypeInternal extends ObjectInternal, TSubscriptedType {
445447
/* Classes aren't usually iterable, but can e.g. Enums */
446448
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
447449

450+
override predicate isNotSubscriptedType() { none() }
451+
448452
}
449453

450454

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ abstract class ConstantObjectInternal extends ObjectInternal {
7676
/** Gets an AST literal with the same value as this object */
7777
abstract ImmutableLiteral getLiteral();
7878

79+
override predicate isNotSubscriptedType() { any() }
80+
7981
}
8082

8183
pragma[nomagic]

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ class PropertyInternal extends ObjectInternal, TProperty {
101101
/* Properties aren't iterable */
102102
override ObjectInternal getIterNext() { none() }
103103

104+
override predicate isNotSubscriptedType() { any() }
105+
104106
}
105107

106108
private class PropertySetterOrDeleter extends ObjectInternal, TPropertySetterOrDeleter {
@@ -174,6 +176,8 @@ private class PropertySetterOrDeleter extends ObjectInternal, TPropertySetterOrD
174176

175177
override predicate useOriginAsLegacyObject() { none() }
176178

179+
override predicate isNotSubscriptedType() { any() }
180+
177181
}
178182

179183

@@ -267,6 +271,8 @@ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
267271
/* Classmethods aren't iterable */
268272
override ObjectInternal getIterNext() { none() }
269273

274+
override predicate isNotSubscriptedType() { any() }
275+
270276
}
271277

272278
class StaticMethodObjectInternal extends ObjectInternal, TStaticMethod {
@@ -345,4 +351,6 @@ class StaticMethodObjectInternal extends ObjectInternal, TStaticMethod {
345351
/* Staticmethods aren't iterable */
346352
override ObjectInternal getIterNext() { none() }
347353

354+
override predicate isNotSubscriptedType() { any() }
355+
348356
}

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

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

5656
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
5757

58+
override predicate isNotSubscriptedType() { any() }
59+
5860
}
5961

6062
private predicate self_variable_reaching_init_exit(EssaVariable self) {
@@ -387,6 +389,8 @@ class UnknownInstanceInternal extends TUnknownInstance, ObjectInternal {
387389

388390
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
389391

392+
override predicate isNotSubscriptedType() { any() }
393+
390394
}
391395

392396
private int lengthFromClass(ClassObjectInternal cls) {
@@ -499,5 +503,7 @@ class SuperInstance extends TSuperInstance, ObjectInternal {
499503

500504
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
501505

506+
override predicate isNotSubscriptedType() { any() }
507+
502508
}
503509

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ abstract class ModuleObjectInternal extends ObjectInternal {
7171
py_exports(this.getSourceModule(), name)
7272
}
7373

74+
override predicate isNotSubscriptedType() { any() }
75+
7476
}
7577

7678
/** A class representing built-in modules */
@@ -448,5 +450,7 @@ class AbsentModuleAttributeObjectInternal extends ObjectInternal, TAbsentModuleA
448450
/* Modules aren't iterable */
449451
override ObjectInternal getIterNext() { none() }
450452

453+
override predicate isNotSubscriptedType() { any() }
454+
451455
}
452456

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ class ObjectInternal extends TObject {
187187
this.(ObjectInternal).attribute(name, _, _)
188188
}
189189

190+
abstract predicate isNotSubscriptedType();
191+
190192
}
191193

192194

@@ -276,6 +278,8 @@ class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject {
276278

277279
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
278280

281+
override predicate isNotSubscriptedType() { any() }
282+
279283
}
280284

281285

@@ -359,6 +363,8 @@ class UnknownInternal extends ObjectInternal, TUnknown {
359363

360364
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
361365

366+
override predicate isNotSubscriptedType() { any() }
367+
362368
}
363369

364370
class UndefinedInternal extends ObjectInternal, TUndefined {
@@ -445,6 +451,8 @@ class UndefinedInternal extends ObjectInternal, TUndefined {
445451

446452
override ObjectInternal getIterNext() { none() }
447453

454+
override predicate isNotSubscriptedType() { any() }
455+
448456
}
449457

450458
module ObjectInternal {
@@ -630,6 +638,8 @@ class DecoratedFunction extends ObjectInternal, TDecoratedFunction {
630638

631639
override predicate useOriginAsLegacyObject() { none() }
632640

641+
override predicate isNotSubscriptedType() { any() }
642+
633643
}
634644

635645
/** Helper for boolean predicates returning both `true` and `false` */

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ class BuiltinTupleObjectInternal extends TBuiltinTuple, TupleObjectInternal {
131131

132132
override predicate useOriginAsLegacyObject() { none() }
133133

134+
override predicate isNotSubscriptedType() { any() }
135+
134136
}
135137

136138
/** A tuple declared by a tuple expression in the Python source code */
@@ -164,6 +166,8 @@ class PythonTupleObjectInternal extends TPythonTuple, TupleObjectInternal {
164166

165167
override predicate useOriginAsLegacyObject() { none() }
166168

169+
override predicate isNotSubscriptedType() { any() }
170+
167171
}
168172

169173
/** A tuple created by a `*` parameter */
@@ -195,6 +199,8 @@ class VarargsTupleObjectInternal extends TVarargsTuple, TupleObjectInternal {
195199

196200
override predicate useOriginAsLegacyObject() { any() }
197201

202+
override predicate isNotSubscriptedType() { any() }
203+
198204
}
199205

200206

@@ -277,4 +283,6 @@ class SysVersionInfoObjectInternal extends TSysVersionInfo, SequenceObjectIntern
277283

278284
override predicate useOriginAsLegacyObject() { any() }
279285

286+
override predicate isNotSubscriptedType() { any() }
287+
280288
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,8 @@ cached newtype TObject =
240240
/* Represents a subscript operation applied to a type. For type-hint analysis */
241241
TSubscriptedType(ObjectInternal generic, ObjectInternal index) {
242242
isType(generic) and
243+
generic.isNotSubscriptedType() and
244+
index.isNotSubscriptedType() and
243245
Expressions::subscriptPartsPointsTo(_, _, generic, index)
244246
}
245247

python/ql/test/3/library-tests/PointsTo/typehints/Values.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,12 @@
1010
| test.py:6:1:6:20 | test.py:6 | ControlFlowNode for FunctionExpr | import | test.py:6:1:6:20 | Function bar |
1111
| test.py:6:11:6:13 | test.py:6 | ControlFlowNode for set | import | file://:0:0:0:0 | builtin-class set |
1212
| test.py:6:17:6:19 | test.py:6 | ControlFlowNode for Set | import | ../../lib/typing.py:23:1:23:23 | class Set |
13+
| test.py:9:6:9:13 | test.py:9 | ControlFlowNode for Optional | import | ../../lib/typing.py:18:12:18:32 | _Optional() |
14+
| test.py:9:6:9:28 | test.py:9 | ControlFlowNode for Subscript | import | file://:0:0:0:0 | _Optional()[Unknown value] |
15+
| test.py:9:15:9:22 | test.py:9 | ControlFlowNode for Optional | import | ../../lib/typing.py:18:12:18:32 | _Optional() |
16+
| test.py:9:15:9:27 | test.py:9 | ControlFlowNode for Subscript | import | file://:0:0:0:0 | _Optional()[builtin-class int] |
17+
| test.py:9:24:9:26 | test.py:9 | ControlFlowNode for int | import | file://:0:0:0:0 | builtin-class int |
18+
| test.py:10:6:10:13 | test.py:10 | ControlFlowNode for Optional | import | ../../lib/typing.py:18:12:18:32 | _Optional() |
19+
| test.py:10:6:10:18 | test.py:10 | ControlFlowNode for Subscript | import | file://:0:0:0:0 | _Optional()[builtin-class int] |
20+
| test.py:10:15:10:17 | test.py:10 | ControlFlowNode for int | import | file://:0:0:0:0 | builtin-class int |
21+
| test.py:10:20:10:22 | test.py:10 | ControlFlowNode for int | import | file://:0:0:0:0 | builtin-class int |

0 commit comments

Comments
 (0)