@zzzen/pyright-internal 1.2.0-dev.20261018 → 1.2.0-dev.20261101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/analyzer/binder.js +20 -9
  2. package/dist/analyzer/binder.js.map +1 -1
  3. package/dist/analyzer/checker.js +4 -0
  4. package/dist/analyzer/checker.js.map +1 -1
  5. package/dist/analyzer/codeFlowEngine.js +3 -3
  6. package/dist/analyzer/codeFlowEngine.js.map +1 -1
  7. package/dist/analyzer/dataClasses.js +14 -5
  8. package/dist/analyzer/dataClasses.js.map +1 -1
  9. package/dist/analyzer/decorators.d.ts +1 -1
  10. package/dist/analyzer/decorators.js +22 -2
  11. package/dist/analyzer/decorators.js.map +1 -1
  12. package/dist/analyzer/namedTuples.d.ts +2 -1
  13. package/dist/analyzer/namedTuples.js +63 -35
  14. package/dist/analyzer/namedTuples.js.map +1 -1
  15. package/dist/analyzer/operations.js +45 -1
  16. package/dist/analyzer/operations.js.map +1 -1
  17. package/dist/analyzer/parseTreeUtils.d.ts +2 -0
  18. package/dist/analyzer/parseTreeUtils.js +43 -0
  19. package/dist/analyzer/parseTreeUtils.js.map +1 -1
  20. package/dist/analyzer/patternMatching.js +3 -1
  21. package/dist/analyzer/patternMatching.js.map +1 -1
  22. package/dist/analyzer/sentinel.js +7 -1
  23. package/dist/analyzer/sentinel.js.map +1 -1
  24. package/dist/analyzer/sourceFile.js +13 -6
  25. package/dist/analyzer/sourceFile.js.map +1 -1
  26. package/dist/analyzer/tupleComparison.d.ts +6 -0
  27. package/dist/analyzer/tupleComparison.js +731 -0
  28. package/dist/analyzer/tupleComparison.js.map +1 -0
  29. package/dist/analyzer/tuples.js +35 -4
  30. package/dist/analyzer/tuples.js.map +1 -1
  31. package/dist/analyzer/typeEvaluator.js +232 -20
  32. package/dist/analyzer/typeEvaluator.js.map +1 -1
  33. package/dist/analyzer/typeEvaluatorTypes.d.ts +1 -1
  34. package/dist/analyzer/typeEvaluatorTypes.js +2 -3
  35. package/dist/analyzer/typeEvaluatorTypes.js.map +1 -1
  36. package/dist/analyzer/typeGuards.d.ts +1 -0
  37. package/dist/analyzer/typeGuards.js +257 -41
  38. package/dist/analyzer/typeGuards.js.map +1 -1
  39. package/dist/analyzer/typeUtils.js +3 -0
  40. package/dist/analyzer/typeUtils.js.map +1 -1
  41. package/dist/analyzer/types.d.ts +14 -0
  42. package/dist/analyzer/types.js +215 -10
  43. package/dist/analyzer/types.js.map +1 -1
  44. package/dist/common/processUtils.d.ts +6 -0
  45. package/dist/common/processUtils.js +56 -1
  46. package/dist/common/processUtils.js.map +1 -1
  47. package/dist/common/textEditTracker.js +1 -1
  48. package/dist/common/textEditTracker.js.map +1 -1
  49. package/dist/common/windowsProcessTree.d.ts +2 -0
  50. package/dist/common/windowsProcessTree.js +23 -0
  51. package/dist/common/windowsProcessTree.js.map +1 -1
  52. package/dist/common/windowsProcessTree.json +23 -3
  53. package/dist/languageServerBase.d.ts +3 -0
  54. package/dist/languageServerBase.js +44 -12
  55. package/dist/languageServerBase.js.map +1 -1
  56. package/dist/languageService/callHierarchyProvider.js +6 -6
  57. package/dist/languageService/callHierarchyProvider.js.map +1 -1
  58. package/dist/languageService/referencesProvider.js +3 -3
  59. package/dist/languageService/referencesProvider.js.map +1 -1
  60. package/dist/languageService/renameProvider.js +1 -1
  61. package/dist/languageService/renameProvider.js.map +1 -1
  62. package/dist/languageService/symbolIndexer.js +2 -2
  63. package/dist/languageService/symbolIndexer.js.map +1 -1
  64. package/dist/localization/localize.d.ts +2 -0
  65. package/dist/localization/localize.js +2 -0
  66. package/dist/localization/localize.js.map +1 -1
  67. package/dist/localization/package.nls.en-us.json +8 -0
  68. package/dist/parser/parseTreeTokenReuse.d.ts +34 -0
  69. package/dist/parser/parseTreeTokenReuse.js +897 -0
  70. package/dist/parser/parseTreeTokenReuse.js.map +1 -0
  71. package/dist/parser/tokenizer.d.ts +10 -1
  72. package/dist/parser/tokenizer.js +184 -130
  73. package/dist/parser/tokenizer.js.map +1 -1
  74. package/dist/tests/checker.test.js +8 -0
  75. package/dist/tests/checker.test.js.map +1 -1
  76. package/dist/tests/fourSlashParser.test.js +35 -0
  77. package/dist/tests/fourSlashParser.test.js.map +1 -1
  78. package/dist/tests/harness/fourslash/fourSlashParser.js +14 -4
  79. package/dist/tests/harness/fourslash/fourSlashParser.js.map +1 -1
  80. package/dist/tests/languageServer.test.js +15 -0
  81. package/dist/tests/languageServer.test.js.map +1 -1
  82. package/dist/tests/lsp/languageServer.js +6 -6
  83. package/dist/tests/lsp/languageServer.js.map +1 -1
  84. package/dist/tests/mypyPrimerAnalysis.test.js +133 -0
  85. package/dist/tests/mypyPrimerAnalysis.test.js.map +1 -1
  86. package/dist/tests/typeEvaluator1.test.js +3 -1
  87. package/dist/tests/typeEvaluator1.test.js.map +1 -1
  88. package/dist/tests/typeEvaluator2.test.js +54 -1
  89. package/dist/tests/typeEvaluator2.test.js.map +1 -1
  90. package/dist/tests/typeEvaluator4.test.js +10 -3
  91. package/dist/tests/typeEvaluator4.test.js.map +1 -1
  92. package/dist/tests/typeEvaluator6.test.js +35 -0
  93. package/dist/tests/typeEvaluator6.test.js.map +1 -1
  94. package/dist/tests/typeEvaluator7.test.js +20 -0
  95. package/dist/tests/typeEvaluator7.test.js.map +1 -1
  96. package/dist/tests/typeEvaluator8.test.js +1291 -1
  97. package/dist/tests/typeEvaluator8.test.js.map +1 -1
  98. package/dist/tests/types.test.js +55 -0
  99. package/dist/tests/types.test.js.map +1 -1
  100. package/dist/typeServer/programWrapper.js +8 -2
  101. package/dist/typeServer/programWrapper.js.map +1 -1
  102. package/dist/typeServer/server.d.ts +12 -2
  103. package/dist/typeServer/server.js +97 -0
  104. package/dist/typeServer/server.js.map +1 -1
  105. package/package.json +1 -1
@@ -43,7 +43,10 @@ var __importStar = (this && this.__importStar) || (function () {
43
43
  })();
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  const assert = __importStar(require("assert"));
46
+ const constraintSolution_1 = require("../analyzer/constraintSolution");
47
+ const TupleComparison = __importStar(require("../analyzer/tupleComparison"));
46
48
  const types_1 = require("../analyzer/types");
49
+ const typeUtils_1 = require("../analyzer/typeUtils");
47
50
  const configOptions_1 = require("../common/configOptions");
48
51
  const diagnosticRules_1 = require("../common/diagnosticRules");
49
52
  const pythonVersion_1 = require("../common/pythonVersion");
@@ -445,7 +448,7 @@ test('Tuple10', () => {
445
448
  });
446
449
  test('Tuple11', () => {
447
450
  const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tuple11.py']);
448
- TestUtils.validateResults(analysisResults, 1);
451
+ TestUtils.validateResults(analysisResults, 3);
449
452
  });
450
453
  test('Tuple12', () => {
451
454
  const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tuple12.py']);
@@ -520,6 +523,61 @@ test('NamedTuple11', () => {
520
523
  const analysisResults = TestUtils.typeAnalyzeSampleFiles(['namedTuple11.py']);
521
524
  TestUtils.validateResults(analysisResults, 3);
522
525
  });
526
+ test('NamedTupleReplace1', () => {
527
+ const configOptions = new configOptions_1.ConfigOptions(uri_1.Uri.empty());
528
+ configOptions.defaultPythonVersion = pythonVersion_1.pythonVersion3_12;
529
+ const analysisResults1 = TestUtils.typeAnalyzeSampleFiles(['namedTupleReplace1.py'], configOptions);
530
+ TestUtils.validateResults(analysisResults1, 8);
531
+ configOptions.defaultPythonVersion = pythonVersion_1.pythonVersion3_13;
532
+ const analysisResults2 = TestUtils.typeAnalyzeSampleFiles(['namedTupleReplace1.py'], configOptions);
533
+ TestUtils.validateResults(analysisResults2, 8);
534
+ });
535
+ test('NamedTupleReplace2', () => {
536
+ const configOptions = new configOptions_1.ConfigOptions(uri_1.Uri.empty());
537
+ configOptions.defaultPythonVersion = pythonVersion_1.pythonVersion3_12;
538
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['namedTupleReplace2.py'], configOptions);
539
+ TestUtils.validateResults(analysisResults, 0);
540
+ });
541
+ test('NamedTupleReplace3', () => {
542
+ for (const pythonVersion of [pythonVersion_1.pythonVersion3_12, pythonVersion_1.pythonVersion3_13]) {
543
+ const configOptions = new configOptions_1.ConfigOptions(uri_1.Uri.empty());
544
+ configOptions.defaultPythonVersion = pythonVersion;
545
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['namedTupleReplace3.py'], configOptions);
546
+ TestUtils.validateResults(analysisResults, 2);
547
+ assert.deepStrictEqual(analysisResults[0].errors.map((diagnostic) => [diagnostic.range.start.line + 1, diagnostic.getRule()]), [
548
+ [15, diagnosticRules_1.DiagnosticRule.reportCallIssue],
549
+ [54, diagnosticRules_1.DiagnosticRule.reportCallIssue],
550
+ ]);
551
+ }
552
+ });
553
+ test('NamedTupleReplace4', () => {
554
+ for (const pythonVersion of [pythonVersion_1.pythonVersion3_12, pythonVersion_1.pythonVersion3_13]) {
555
+ const configOptions = new configOptions_1.ConfigOptions(uri_1.Uri.empty());
556
+ configOptions.defaultPythonVersion = pythonVersion;
557
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['namedTupleReplace4.py'], configOptions);
558
+ const expectedErrors = [
559
+ [25, diagnosticRules_1.DiagnosticRule.reportArgumentType],
560
+ [28, diagnosticRules_1.DiagnosticRule.reportArgumentType],
561
+ ];
562
+ if (pythonVersion === pythonVersion_1.pythonVersion3_13) {
563
+ expectedErrors.push([35, diagnosticRules_1.DiagnosticRule.reportArgumentType], [38, diagnosticRules_1.DiagnosticRule.reportArgumentType]);
564
+ }
565
+ TestUtils.validateResults(analysisResults, expectedErrors.length);
566
+ assert.deepStrictEqual(analysisResults[0].errors.map((diagnostic) => [diagnostic.range.start.line + 1, diagnostic.getRule()]), expectedErrors);
567
+ }
568
+ });
569
+ test('NamedTupleReplace5', () => {
570
+ for (const pythonVersion of [pythonVersion_1.pythonVersion3_12, pythonVersion_1.pythonVersion3_13]) {
571
+ const configOptions = new configOptions_1.ConfigOptions(uri_1.Uri.empty());
572
+ configOptions.defaultPythonVersion = pythonVersion;
573
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['namedTupleReplace5.py'], configOptions);
574
+ TestUtils.validateResults(analysisResults, 2);
575
+ assert.deepStrictEqual(analysisResults[0].errors.map((diagnostic) => [diagnostic.range.start.line + 1, diagnostic.getRule()]), [
576
+ [19, diagnosticRules_1.DiagnosticRule.reportReturnType],
577
+ [28, diagnosticRules_1.DiagnosticRule.reportReturnType],
578
+ ]);
579
+ }
580
+ });
523
581
  test('NamedTuple12', () => {
524
582
  const configOptions = new configOptions_1.ConfigOptions(uri_1.Uri.empty());
525
583
  configOptions.defaultPythonVersion = pythonVersion_1.pythonVersion3_12;
@@ -660,6 +718,73 @@ test('Descriptor7', () => {
660
718
  [134, diagnosticRules_1.DiagnosticRule.reportAttributeAccessIssue],
661
719
  ]);
662
720
  });
721
+ test('MethodDescriptor1', () => {
722
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['methodDescriptor1.py']);
723
+ TestUtils.validateResults(analysisResults, 0);
724
+ });
725
+ test('MethodDescriptor2', () => {
726
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['methodDescriptor2.py']);
727
+ TestUtils.validateResults(analysisResults, 0);
728
+ });
729
+ test('MethodDescriptor3', () => {
730
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['methodDescriptor3.py']);
731
+ TestUtils.validateResults(analysisResults, 4);
732
+ const errors = [...analysisResults[0].errors].sort((a, b) => a.range.start.line - b.range.start.line);
733
+ assert.deepStrictEqual(errors.map((diagnostic) => [diagnostic.range.start.line + 1, diagnostic.getRule()]), [
734
+ [14, diagnosticRules_1.DiagnosticRule.reportCallIssue],
735
+ [17, diagnosticRules_1.DiagnosticRule.reportArgumentType],
736
+ [20, diagnosticRules_1.DiagnosticRule.reportAttributeAccessIssue],
737
+ [24, diagnosticRules_1.DiagnosticRule.reportFunctionMemberAccess],
738
+ ]);
739
+ assert.match(errors[2].message, /Cannot assign to attribute "__func__"/);
740
+ });
741
+ test('MethodDescriptor4', () => {
742
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['methodDescriptor4.py']);
743
+ TestUtils.validateResults(analysisResults, 2);
744
+ analysisResults[0].errors.forEach((diagnostic) => assert.strictEqual(diagnostic.getRule(), diagnosticRules_1.DiagnosticRule.reportAssignmentType));
745
+ });
746
+ test('MethodDescriptor5', () => {
747
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['methodDescriptor5.py']);
748
+ TestUtils.validateResults(analysisResults, 15);
749
+ const errors = [...analysisResults[0].errors].sort((a, b) => a.range.start.line - b.range.start.line);
750
+ assert.deepStrictEqual(errors.map((diagnostic) => [diagnostic.range.start.line + 1, diagnostic.getRule()]).sort(), [
751
+ [31, diagnosticRules_1.DiagnosticRule.reportArgumentType],
752
+ [40, diagnosticRules_1.DiagnosticRule.reportCallIssue],
753
+ [49, diagnosticRules_1.DiagnosticRule.reportArgumentType],
754
+ [50, diagnosticRules_1.DiagnosticRule.reportCallIssue],
755
+ [61, diagnosticRules_1.DiagnosticRule.reportArgumentType],
756
+ [73, diagnosticRules_1.DiagnosticRule.reportGeneralTypeIssues],
757
+ [73, diagnosticRules_1.DiagnosticRule.reportGeneralTypeIssues],
758
+ [74, diagnosticRules_1.DiagnosticRule.reportOptionalMemberAccess],
759
+ [74, diagnosticRules_1.DiagnosticRule.reportAttributeAccessIssue],
760
+ [74, diagnosticRules_1.DiagnosticRule.reportOptionalMemberAccess],
761
+ [75, diagnosticRules_1.DiagnosticRule.reportOperatorIssue],
762
+ [75, diagnosticRules_1.DiagnosticRule.reportOptionalMemberAccess],
763
+ [83, diagnosticRules_1.DiagnosticRule.reportArgumentType],
764
+ [94, diagnosticRules_1.DiagnosticRule.reportArgumentType],
765
+ [104, diagnosticRules_1.DiagnosticRule.reportArgumentType],
766
+ ].sort());
767
+ });
768
+ test('MethodDescriptor6', () => {
769
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['methodDescriptor6.py']);
770
+ TestUtils.validateResults(analysisResults, 1);
771
+ assert.strictEqual(analysisResults[0].errors[0].getRule(), diagnosticRules_1.DiagnosticRule.reportOperatorIssue);
772
+ });
773
+ test('MethodDescriptor7', () => {
774
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['methodDescriptor7.py']);
775
+ TestUtils.validateResults(analysisResults, 9);
776
+ assert.deepStrictEqual(analysisResults[0].errors.map((diagnostic) => [diagnostic.range.start.line + 1, diagnostic.getRule()]).sort(), [
777
+ [36, diagnosticRules_1.DiagnosticRule.reportArgumentType],
778
+ [62, diagnosticRules_1.DiagnosticRule.reportArgumentType],
779
+ [84, diagnosticRules_1.DiagnosticRule.reportCallIssue],
780
+ [84, diagnosticRules_1.DiagnosticRule.reportArgumentType],
781
+ [85, diagnosticRules_1.DiagnosticRule.reportArgumentType],
782
+ [86, diagnosticRules_1.DiagnosticRule.reportCallIssue],
783
+ [103, diagnosticRules_1.DiagnosticRule.reportCallIssue],
784
+ [103, diagnosticRules_1.DiagnosticRule.reportArgumentType],
785
+ [114, diagnosticRules_1.DiagnosticRule.reportInconsistentOverload],
786
+ ].sort());
787
+ });
663
788
  test('Partial1', () => {
664
789
  const analysisResults = TestUtils.typeAnalyzeSampleFiles(['partial1.py']);
665
790
  TestUtils.validateResults(analysisResults, 18);
@@ -1169,4 +1294,1169 @@ test('TypeForm9', () => {
1169
1294
  const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typeForm9.py']);
1170
1295
  TestUtils.validateResults(analysisResults, 10);
1171
1296
  });
1297
+ test('TupleComparisonResults1', () => {
1298
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults1.py']);
1299
+ TestUtils.validateResults(analysisResults, 18);
1300
+ });
1301
+ test('TupleLiteralInference1', () => {
1302
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleLiteralInference1.py']);
1303
+ TestUtils.validateResults(analysisResults, 2);
1304
+ });
1305
+ test('TupleLiteralInference2', () => {
1306
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleLiteralInference2.py']);
1307
+ TestUtils.validateResults(analysisResults, 2);
1308
+ analysisResults[0].errors.forEach((diag) => assert.strictEqual(diag.getRule(), diagnosticRules_1.DiagnosticRule.reportArgumentType));
1309
+ });
1310
+ test('TupleLiteralInference3', () => {
1311
+ const comparisonSpy = jest.spyOn(TupleComparison, 'getTupleComparisonResult');
1312
+ try {
1313
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleLiteralInference3.py']);
1314
+ TestUtils.validateResults(analysisResults, 0);
1315
+ assert.ok(comparisonSpy.mock.calls.length > 0);
1316
+ const callsByNode = new Map();
1317
+ comparisonSpy.mock.calls.forEach(([, , , , node]) => {
1318
+ const calls = (callsByNode.get(node.id) ?? 0) + 1;
1319
+ callsByNode.set(node.id, calls);
1320
+ assert.ok(calls <= types_1.maxSubtypesForInferredType, 'Optional tuple precision exceeded the comparison budget');
1321
+ });
1322
+ }
1323
+ finally {
1324
+ comparisonSpy.mockRestore();
1325
+ }
1326
+ });
1327
+ test('TupleLiteralInference4', () => {
1328
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleLiteralInference4.py']);
1329
+ TestUtils.validateResults(analysisResults, 2);
1330
+ analysisResults[0].errors.forEach((diag) => assert.strictEqual(diag.getRule(), diagnosticRules_1.DiagnosticRule.reportArgumentType));
1331
+ assert.deepStrictEqual(analysisResults[0].errors.map((diag) => diag.range.start.line + 1).sort((left, right) => left - right), [114, 123]);
1332
+ });
1333
+ test('TupleLiteralInference5', () => {
1334
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleLiteralInference5.py']);
1335
+ TestUtils.validateResults(analysisResults, 2);
1336
+ analysisResults[0].errors.forEach((diag) => assert.strictEqual(diag.getRule(), diagnosticRules_1.DiagnosticRule.reportArgumentType));
1337
+ assert.deepStrictEqual(analysisResults[0].errors.map((diag) => diag.range.start.line + 1).sort((left, right) => left - right), [80, 89]);
1338
+ });
1339
+ test('TupleComparisonResults2', () => {
1340
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults2.py']);
1341
+ TestUtils.validateResults(analysisResults, 0);
1342
+ });
1343
+ test('TupleComparisonResults3', () => {
1344
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults3.py']);
1345
+ TestUtils.validateResults(analysisResults, 0);
1346
+ });
1347
+ test('TupleComparisonResults4', () => {
1348
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults4.py']);
1349
+ TestUtils.validateResults(analysisResults, 16);
1350
+ });
1351
+ test('TupleComparisonResults5', () => {
1352
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults5.py']);
1353
+ TestUtils.validateResults(analysisResults, 0);
1354
+ });
1355
+ test('TupleComparisonResults6', () => {
1356
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles([
1357
+ 'tupleComparisonResource_hierarchy_64.py',
1358
+ 'tupleComparisonResource_hierarchy_128.py',
1359
+ 'tupleComparisonResource_hierarchy_256.py',
1360
+ 'tupleComparisonResource_literal_union.py',
1361
+ 'tupleComparisonResource_bool_union.py',
1362
+ 'tupleComparisonResource_mixed_union.py',
1363
+ 'tupleComparisonResource_mixed_union_consumer.py',
1364
+ 'tupleComparisonResource_unsupported_union.py',
1365
+ 'tupleComparisonResource_narrow_argument_union.py',
1366
+ 'tupleComparisonResource_nested_tuple.py',
1367
+ 'tupleComparisonResource_nested_list.py',
1368
+ 'tupleComparisonResource_repeated_nested_types.py',
1369
+ 'tupleComparisonResource_recursive_container.py',
1370
+ 'tupleComparisonResource_recursive_nonbool.py',
1371
+ 'tupleComparisonResource_recursive_bad_consumer.py',
1372
+ 'tupleComparisonResource_long_alias_chain.py',
1373
+ 'tupleComparisonResource_cyclic_source.py',
1374
+ 'tupleComparisonResource_growing_alias_lt.py',
1375
+ 'tupleComparisonResource_growing_alias_le.py',
1376
+ 'tupleComparisonResource_growing_alias_gt.py',
1377
+ 'tupleComparisonResource_growing_alias_ge.py',
1378
+ 'tupleComparisonResource_growing_custom_bool.py',
1379
+ 'tupleComparisonResource_growing_custom_str.py',
1380
+ 'tupleComparisonResource_growing_two_parameters.py',
1381
+ 'tupleComparisonResource_literal_boolean_result.py',
1382
+ 'tupleComparisonResource_large_variadic_suffix.py',
1383
+ 'tupleComparisonResource_large_variadic_custom.py',
1384
+ 'tupleComparisonResource_large_variadic_nonbool.py',
1385
+ 'tupleComparisonResource_one_sided_variadic.py',
1386
+ 'tupleComparisonResource_deep_nonbool.py',
1387
+ ]);
1388
+ const expectedErrors = [0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1389
+ analysisResults.forEach((result, index) => TestUtils.validateResults([result], expectedErrors[index]));
1390
+ });
1391
+ test('TupleComparisonResults7', () => {
1392
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults7.py']);
1393
+ TestUtils.validateResults(analysisResults, 28);
1394
+ });
1395
+ test('TupleComparisonResults8', () => {
1396
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults8.py']);
1397
+ TestUtils.validateResults(analysisResults, 1);
1398
+ });
1399
+ test('TupleComparisonResults9', () => {
1400
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults9.py']);
1401
+ TestUtils.validateResults(analysisResults, 12);
1402
+ });
1403
+ test('TupleComparisonResults10', () => {
1404
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults10.py']);
1405
+ TestUtils.validateResults(analysisResults, 7);
1406
+ });
1407
+ test('TupleComparisonResults11', () => {
1408
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults11.py']);
1409
+ TestUtils.validateResults(analysisResults, 7);
1410
+ assert.strictEqual(analysisResults[0].errors.filter((diag) => diag.getRule() === diagnosticRules_1.DiagnosticRule.reportReturnType).length, 4);
1411
+ assert.strictEqual(analysisResults[0].errors.filter((diag) => diag.getRule() === diagnosticRules_1.DiagnosticRule.reportOperatorIssue).length, 3);
1412
+ });
1413
+ test('TupleComparisonResults12', () => {
1414
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults12.py']);
1415
+ TestUtils.validateResults(analysisResults, 1);
1416
+ assert.strictEqual(analysisResults[0].errors[0].getRule(), diagnosticRules_1.DiagnosticRule.reportOperatorIssue);
1417
+ assert.strictEqual(analysisResults[0].errors[0].range.start.line + 1, 84);
1418
+ });
1419
+ test('TupleComparisonResults13', () => {
1420
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults13.py']);
1421
+ TestUtils.validateResults(analysisResults, 10);
1422
+ analysisResults[0].errors.forEach((diag) => assert.strictEqual(diag.getRule(), diagnosticRules_1.DiagnosticRule.reportReturnType));
1423
+ assert.deepStrictEqual(analysisResults[0].errors.map((diag) => diag.range.start.line + 1).sort((left, right) => left - right), [49, 53, 57, 61, 65, 69, 73, 77, 81, 85]);
1424
+ });
1425
+ test('TupleLiteralInference6', () => {
1426
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleLiteralInference6.py']);
1427
+ TestUtils.validateResults(analysisResults, 2);
1428
+ analysisResults[0].errors.forEach((diag) => assert.strictEqual(diag.getRule(), diagnosticRules_1.DiagnosticRule.reportArgumentType));
1429
+ assert.deepStrictEqual(analysisResults[0].errors.map((diag) => diag.range.start.line + 1).sort((left, right) => left - right), [35, 44]);
1430
+ });
1431
+ test('TupleLiteralInferenceRetentionBudget', () => {
1432
+ const state = (0, testState_1.parseAndGetTestState)('// @filename: test.py\n//// /*marker*/0').state;
1433
+ const evaluator = state.program.evaluator;
1434
+ const node = (0, testState_1.getNodeAtMarker)(state, 'marker');
1435
+ const intType = evaluator.getBuiltInObject(node, 'int');
1436
+ const tupleClass = evaluator.getTupleClassType();
1437
+ assert.ok(tupleClass && (0, types_1.isInstantiableClass)(tupleClass));
1438
+ const buildGraph = () => {
1439
+ const types = [];
1440
+ let type = intType;
1441
+ for (let depth = 0; depth < 16; depth++) {
1442
+ const tuple = types_1.ClassType.cloneAsInstance(types_1.ClassType.specialize(tupleClass, [type], true, false, [
1443
+ { type, isUnbounded: false },
1444
+ { type, isUnbounded: false },
1445
+ ]));
1446
+ types.push(tuple);
1447
+ type = tuple;
1448
+ }
1449
+ return types;
1450
+ };
1451
+ const types = buildGraph();
1452
+ const baseTypes = buildGraph();
1453
+ const type = types[types.length - 1];
1454
+ const baseType = baseTypes[baseTypes.length - 1];
1455
+ let reads = 0;
1456
+ baseTypes.forEach((type) => {
1457
+ const args = type.priv.tupleTypeArgs;
1458
+ Object.defineProperty(type.priv, 'tupleTypeArgs', {
1459
+ get: () => {
1460
+ reads++;
1461
+ return args;
1462
+ },
1463
+ });
1464
+ });
1465
+ assert.strictEqual((0, types_1.retainInferredTupleLiterals)(type, baseType), baseType);
1466
+ assert.strictEqual(reads, 0, 'Over-budget retention should fall back before comparing the base graph');
1467
+ });
1468
+ test('TupleComparisonResults14', () => {
1469
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults14.py']);
1470
+ TestUtils.validateResults(analysisResults, 2);
1471
+ analysisResults[0].errors.forEach((diag) => assert.strictEqual(diag.getRule(), diagnosticRules_1.DiagnosticRule.reportArgumentType));
1472
+ assert.deepStrictEqual(analysisResults[0].errors.map((diag) => diag.range.start.line + 1).sort((left, right) => left - right), [118, 119]);
1473
+ });
1474
+ test('TupleComparisonResults15', () => {
1475
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults15.py']);
1476
+ TestUtils.validateResults(analysisResults, 2);
1477
+ assert.strictEqual(analysisResults[0].errors.filter((diag) => diag.getRule() === diagnosticRules_1.DiagnosticRule.reportReturnType).length, 1);
1478
+ assert.strictEqual(analysisResults[0].errors.filter((diag) => diag.getRule() === diagnosticRules_1.DiagnosticRule.reportOperatorIssue).length, 1);
1479
+ assert.deepStrictEqual(analysisResults[0].errors
1480
+ .slice()
1481
+ .sort((left, right) => left.range.start.line - right.range.start.line)
1482
+ .map((diag) => [diag.range.start.line + 1, diag.getRule()]), [
1483
+ [41, diagnosticRules_1.DiagnosticRule.reportReturnType],
1484
+ [45, diagnosticRules_1.DiagnosticRule.reportOperatorIssue],
1485
+ ]);
1486
+ });
1487
+ test('TupleComparisonEqualityMemoization', () => {
1488
+ const code = `// @filename: test.py
1489
+ //// /*marker*/0
1490
+ `;
1491
+ const state = (0, testState_1.parseAndGetTestState)(code).state;
1492
+ const evaluator = state.program.evaluator;
1493
+ const node = (0, testState_1.getNodeAtMarker)(state, 'marker');
1494
+ assert.ok(node.nodeType === 40 /* ParseNodeType.Number */);
1495
+ const intType = evaluator.getBuiltInObject(node, 'int');
1496
+ const boolType = evaluator.getBuiltInObject(node, 'bool');
1497
+ const tupleClass = evaluator.getTupleClassType();
1498
+ assert.ok(tupleClass && (0, types_1.isInstantiableClass)(tupleClass));
1499
+ let type = types_1.ClassType.cloneAsInstance(types_1.ClassType.specialize(tupleClass, [intType], true, false, [{ type: intType, isUnbounded: false }]));
1500
+ const types = [type];
1501
+ for (let depth = 1; depth <= 16; depth++) {
1502
+ type = types_1.ClassType.cloneAsInstance(types_1.ClassType.specialize(tupleClass, [type], true, false, [
1503
+ { type, isUnbounded: false },
1504
+ { type, isUnbounded: false },
1505
+ ]));
1506
+ types.push(type);
1507
+ }
1508
+ let reads = 0;
1509
+ types.forEach((type) => {
1510
+ const args = type.priv.tupleTypeArgs;
1511
+ Object.defineProperty(type.priv, 'tupleTypeArgs', {
1512
+ get: () => {
1513
+ reads++;
1514
+ return args;
1515
+ },
1516
+ });
1517
+ });
1518
+ const result = TupleComparison.getTupleComparisonResult(evaluator, type, type, 20 /* OperatorType.LessThan */, node);
1519
+ assert.ok(result && (0, types_1.isTypeSame)(result.type, boolType));
1520
+ assert.ok(reads < 1000, `Shared tuple graph was repeatedly expanded (${reads} tuple-argument reads)`);
1521
+ });
1522
+ test('TupleComparisonResults16', () => {
1523
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults16.py']);
1524
+ TestUtils.validateResults(analysisResults, 6);
1525
+ analysisResults[0].errors.forEach((diag) => assert.strictEqual(diag.getRule(), diagnosticRules_1.DiagnosticRule.reportArgumentType));
1526
+ assert.deepStrictEqual(analysisResults[0].errors.map((diag) => diag.range.start.line + 1).sort((left, right) => left - right), [112, 113, 114, 115, 116, 117]);
1527
+ });
1528
+ test('TupleComparisonResults17', () => {
1529
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults17.py']);
1530
+ TestUtils.validateResults(analysisResults, 8);
1531
+ analysisResults[0].errors.forEach((diag) => assert.strictEqual(diag.getRule(), diagnosticRules_1.DiagnosticRule.reportArgumentType));
1532
+ assert.deepStrictEqual(analysisResults[0].errors.map((diag) => diag.range.start.line + 1).sort((left, right) => left - right), [82, 83, 84, 85, 96, 97, 98, 99]);
1533
+ });
1534
+ test('TupleComparisonDistinctEqualityMemoization', () => {
1535
+ const lines = ['// @filename: test.py', '////type L0 = tuple[int]', '////type R0 = tuple[int]'];
1536
+ for (let depth = 1; depth <= 16; depth++) {
1537
+ lines.push(`////type L${depth} = tuple[L${depth - 1}, L${depth - 1}]`);
1538
+ lines.push(`////type R${depth} = tuple[R${depth - 1}, R${depth - 1}]`);
1539
+ }
1540
+ lines.push('////def compare(left: L16, right: R16) -> bool:');
1541
+ lines.push('//// return /*left*/left < /*right*/right');
1542
+ const state = (0, testState_1.parseAndGetTestState)(lines.join('\n')).state;
1543
+ const evaluator = state.program.evaluator;
1544
+ const leftNode = (0, testState_1.getNodeAtMarker)(state, 'left');
1545
+ const rightNode = (0, testState_1.getNodeAtMarker)(state, 'right');
1546
+ assert.ok(leftNode.nodeType === 38 /* ParseNodeType.Name */ && rightNode.nodeType === 38 /* ParseNodeType.Name */);
1547
+ const left = evaluator.getTypeOfExpression(leftNode).type;
1548
+ const right = evaluator.getTypeOfExpression(rightNode).type;
1549
+ assert.ok((0, types_1.isClassInstance)(left) && (0, types_1.isClassInstance)(right));
1550
+ assert.notStrictEqual(left, right);
1551
+ assert.notStrictEqual(left.priv.tupleTypeArgs?.[0].type, right.priv.tupleTypeArgs?.[0].type);
1552
+ const types = new Set();
1553
+ const visit = (type) => {
1554
+ if (!(0, types_1.isClassInstance)(type) || !type.priv.tupleTypeArgs || types.has(type.priv))
1555
+ return;
1556
+ types.add(type.priv);
1557
+ type.priv.tupleTypeArgs.forEach((arg) => visit(arg.type));
1558
+ };
1559
+ visit(left);
1560
+ visit(right);
1561
+ let reads = 0;
1562
+ types.forEach((priv) => {
1563
+ const args = priv.tupleTypeArgs;
1564
+ Object.defineProperty(priv, 'tupleTypeArgs', {
1565
+ get: () => {
1566
+ reads++;
1567
+ return args;
1568
+ },
1569
+ });
1570
+ });
1571
+ const node = leftNode.parent;
1572
+ assert.ok(node?.nodeType === 7 /* ParseNodeType.BinaryOperation */);
1573
+ const result = evaluator.getTypeOfExpression(node);
1574
+ assert.ok((0, types_1.isTypeSame)(result.type, evaluator.getBuiltInObject(node, 'bool')));
1575
+ assert.ok(reads > 0 && reads < 1000, `Distinct tuple graphs were repeatedly expanded (${reads} tuple-argument reads)`);
1576
+ });
1577
+ test('TupleComparisonResults18', () => {
1578
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults18.py']);
1579
+ TestUtils.validateResults(analysisResults, 6);
1580
+ analysisResults[0].errors.forEach((diag) => assert.strictEqual(diag.getRule(), diagnosticRules_1.DiagnosticRule.reportArgumentType));
1581
+ assert.deepStrictEqual(analysisResults[0].errors.map((diag) => diag.range.start.line + 1).sort((left, right) => left - right), [115, 116, 117, 118, 119, 120]);
1582
+ });
1583
+ test('TupleComparisonResults19', () => {
1584
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults19.py']);
1585
+ TestUtils.validateResults(analysisResults, 4);
1586
+ analysisResults[0].errors.forEach((diag) => assert.strictEqual(diag.getRule(), diagnosticRules_1.DiagnosticRule.reportArgumentType));
1587
+ assert.deepStrictEqual(analysisResults[0].errors.map((diag) => diag.range.start.line + 1).sort((left, right) => left - right), [46, 47, 48, 49]);
1588
+ });
1589
+ test('TupleComparisonResults20', () => {
1590
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults20.py']);
1591
+ TestUtils.validateResults(analysisResults, 4);
1592
+ analysisResults[0].errors.forEach((diag) => assert.strictEqual(diag.getRule(), diagnosticRules_1.DiagnosticRule.reportArgumentType));
1593
+ assert.deepStrictEqual(analysisResults[0].errors.map((diag) => diag.range.start.line + 1).sort((left, right) => left - right), [49, 50, 51, 52]);
1594
+ });
1595
+ test('TupleComparisonResults24', () => {
1596
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults24.py']);
1597
+ TestUtils.validateResults(analysisResults, 14);
1598
+ assert.deepStrictEqual(analysisResults[0].errors
1599
+ .slice()
1600
+ .sort((left, right) => left.range.start.line - right.range.start.line)
1601
+ .map((diag) => [diag.range.start.line + 1, diag.getRule()]), [
1602
+ [34, diagnosticRules_1.DiagnosticRule.reportArgumentType],
1603
+ [35, diagnosticRules_1.DiagnosticRule.reportArgumentType],
1604
+ [36, diagnosticRules_1.DiagnosticRule.reportArgumentType],
1605
+ [37, diagnosticRules_1.DiagnosticRule.reportArgumentType],
1606
+ [42, diagnosticRules_1.DiagnosticRule.reportReturnType],
1607
+ [47, diagnosticRules_1.DiagnosticRule.reportReturnType],
1608
+ [52, diagnosticRules_1.DiagnosticRule.reportReturnType],
1609
+ [57, diagnosticRules_1.DiagnosticRule.reportReturnType],
1610
+ [87, diagnosticRules_1.DiagnosticRule.reportArgumentType],
1611
+ [88, diagnosticRules_1.DiagnosticRule.reportArgumentType],
1612
+ [89, diagnosticRules_1.DiagnosticRule.reportArgumentType],
1613
+ [90, diagnosticRules_1.DiagnosticRule.reportArgumentType],
1614
+ [111, diagnosticRules_1.DiagnosticRule.reportReturnType],
1615
+ [117, diagnosticRules_1.DiagnosticRule.reportReturnType],
1616
+ ]);
1617
+ });
1618
+ for (const context of ['chained', 'parenthesized']) {
1619
+ for (const form of ['tuple', 'name']) {
1620
+ for (const operator of ['<', '<=', '>', '>=']) {
1621
+ test(`TupleComparisonChainedIdentity ${context} ${form} ${operator}`, () => {
1622
+ const left = form === 'tuple' ? '(/*middle*/value, 0)' : '/*middle*/pair';
1623
+ const right = form === 'tuple' ? '(value, 1)' : 'pair';
1624
+ const comparison = `${left} ${operator} ${right}`;
1625
+ const expression = context === 'chained' ? `(Rebind(),) < ${comparison}` : `0 < (${comparison})`;
1626
+ const lines = [
1627
+ '// @filename: test.py',
1628
+ '////class Value:',
1629
+ ...['__lt__', '__le__', '__gt__', '__ge__'].flatMap((method) => [
1630
+ `//// def ${method}(self, other: object) -> str:`,
1631
+ '//// return "ordered"',
1632
+ ]),
1633
+ '////value = Value()',
1634
+ '////pair: tuple[Value, int] = (value, 0)',
1635
+ '////class Rebind:',
1636
+ '//// def __lt__(self, other: object) -> bool:',
1637
+ '//// global value, pair',
1638
+ '//// value = Value()',
1639
+ '//// pair = (value, 1)',
1640
+ '//// return True',
1641
+ '////def compare() -> None:',
1642
+ `//// _ = ${expression}`,
1643
+ ];
1644
+ const state = (0, testState_1.parseAndGetTestState)(lines.join('\n')).state;
1645
+ const evaluator = state.program.evaluator;
1646
+ const marker = (0, testState_1.getNodeAtMarker)(state, 'middle');
1647
+ assert.ok(marker.nodeType === 38 /* ParseNodeType.Name */);
1648
+ const node = form === 'tuple' ? marker.parent?.parent : marker.parent;
1649
+ assert.ok(node?.nodeType === 7 /* ParseNodeType.BinaryOperation */);
1650
+ // Whole-chain propagation can hide the child result; inspect the actual comparison node.
1651
+ const result = evaluator.getTypeOfExpression(node);
1652
+ const boolType = evaluator.getBuiltInObject(node, 'bool');
1653
+ const expected = context === 'chained'
1654
+ ? (0, types_1.combineTypes)([evaluator.getBuiltInObject(node, 'str'), boolType])
1655
+ : boolType;
1656
+ assert.ok((0, types_1.isTypeSame)(result.type, expected), 'The complete middle-comparison type must be preserved');
1657
+ assert.strictEqual(evaluator.assignType(boolType, result.type), context !== 'chained');
1658
+ });
1659
+ }
1660
+ }
1661
+ }
1662
+ test('TupleComparisonClosedResultWorkBound', () => {
1663
+ for (const shape of ['below', 'limit', 'over', 'open', 'depth', 'deep', 'never']) {
1664
+ const code = `// @filename: test.py
1665
+ ////class /*value*/Value[T]:
1666
+ //// def __lt__(self, other: object) -> str:
1667
+ //// return "ordered"
1668
+ ////type Tree[T] = tuple[Value[T], tuple[Tree[list[T]], ...]]
1669
+ ////def compare(left: Tree[int], right: Tree[int]) -> None:
1670
+ //// /*compare*/left < right
1671
+ `;
1672
+ const state = (0, testState_1.parseAndGetTestState)(code).state;
1673
+ const evaluator = state.program.evaluator;
1674
+ const node = (0, testState_1.getNodeAtMarker)(state, 'compare').parent;
1675
+ assert.ok(node?.nodeType === 7 /* ParseNodeType.BinaryOperation */);
1676
+ const strType = evaluator.getBuiltInObject(node, 'str');
1677
+ const boolType = evaluator.getBuiltInObject(node, 'bool');
1678
+ const listType = evaluator.getBuiltInObject(node, 'list');
1679
+ const tupleClass = evaluator.getTupleClassType();
1680
+ assert.ok((0, types_1.isClassInstance)(listType) && tupleClass && (0, types_1.isInstantiableClass)(tupleClass));
1681
+ let payload = strType;
1682
+ let reads = 0;
1683
+ if (shape === 'never') {
1684
+ payload = types_1.ClassType.specialize(listType, [types_1.NeverType.createNever()], true);
1685
+ }
1686
+ else if (shape === 'open') {
1687
+ const valueNode = (0, testState_1.getNodeAtMarker)(state, 'value');
1688
+ assert.ok(valueNode.nodeType === 38 /* ParseNodeType.Name */);
1689
+ const valueClass = evaluator.getTypeOfExpression(valueNode).type;
1690
+ assert.ok((0, types_1.isInstantiableClass)(valueClass) && valueClass.shared.typeParams.length === 1);
1691
+ payload = types_1.ClassType.specialize(listType, [valueClass.shared.typeParams[0]], true);
1692
+ }
1693
+ else if (shape === 'depth' || shape === 'deep') {
1694
+ const depth = types_1.maxTypeRecursionCount + (shape === 'deep' ? 1 : 0);
1695
+ for (let index = 0; index < depth; index++) {
1696
+ payload = types_1.ClassType.specialize(listType, [payload], true);
1697
+ }
1698
+ }
1699
+ else {
1700
+ const width = types_1.maxSubtypesForInferredType - (shape === 'below' ? 2 : shape === 'limit' ? 1 : 0);
1701
+ const args = Array.from({ length: width }, () => {
1702
+ const arg = { type: strType, isUnbounded: false };
1703
+ Object.defineProperty(arg, 'type', {
1704
+ get: () => {
1705
+ reads++;
1706
+ return strType;
1707
+ },
1708
+ });
1709
+ return arg;
1710
+ });
1711
+ payload = types_1.ClassType.cloneAsInstance(types_1.ClassType.specialize(tupleClass, [strType], true, false, args));
1712
+ }
1713
+ const originalCall = evaluator.getTypeOfMagicMethodCall;
1714
+ const originalExpand = evaluator.mapSubtypesExpandTypeVars;
1715
+ let expansions = 0;
1716
+ evaluator.getTypeOfMagicMethodCall = (...args) => (0, types_1.isClassInstance)(args[0]) && args[0].shared.name === 'Value' ? { type: payload } : originalCall(...args);
1717
+ evaluator.mapSubtypesExpandTypeVars = (...args) => {
1718
+ assert.ok(++expansions <= 2 * (types_1.maxSubtypesForInferredType + 1), `Unbounded expansion (${shape})`);
1719
+ return originalExpand(...args);
1720
+ };
1721
+ try {
1722
+ reads = 0;
1723
+ const result = evaluator.getTypeOfExpression(node);
1724
+ assert.ok(expansions > 0);
1725
+ assert.ok(reads <= types_1.maxSubtypesForInferredType, `Unbounded result traversal (${shape}: ${reads})`);
1726
+ if (shape === 'over' || shape === 'open' || shape === 'deep') {
1727
+ assert.ok((0, types_1.isTypeSame)(result.type, boolType), `Complex result should use the existing fallback (${shape})`);
1728
+ }
1729
+ else {
1730
+ if (shape !== 'depth' && shape !== 'never')
1731
+ assert.ok(reads > 0);
1732
+ assert.ok((0, types_1.isTypeSame)(result.type, (0, types_1.combineTypes)([payload, boolType])), `Closed result should preserve exactly payload | bool (${shape})`);
1733
+ }
1734
+ }
1735
+ finally {
1736
+ evaluator.getTypeOfMagicMethodCall = originalCall;
1737
+ evaluator.mapSubtypesExpandTypeVars = originalExpand;
1738
+ }
1739
+ }
1740
+ });
1741
+ test('TupleComparisonGrowingWorkBound', () => {
1742
+ for (const parameters of ['T', 'T, T']) {
1743
+ const lines = [
1744
+ '// @filename: test.py',
1745
+ `////class Box[${parameters === 'T' ? 'T' : 'T, U'}]:`,
1746
+ ...['__lt__', '__le__', '__gt__', '__ge__'].flatMap((method) => [
1747
+ `//// def ${method}(self, other: object) -> str:`,
1748
+ '//// return "ordered"',
1749
+ ]),
1750
+ `////type Tree[T] = tuple[T, tuple[Tree[Box[${parameters}]], ...]]`,
1751
+ '////def compare(left: Tree[int], right: Tree[int]) -> None:',
1752
+ ...['<', '<=', '>', '>='].map((operator, index) => `//// /*op${index}*/left ${operator} right`),
1753
+ ];
1754
+ const state = (0, testState_1.parseAndGetTestState)(lines.join('\n')).state;
1755
+ const evaluator = state.program.evaluator;
1756
+ const original = evaluator.mapSubtypesExpandTypeVars;
1757
+ let visits = 0;
1758
+ evaluator.mapSubtypesExpandTypeVars = (...args) => {
1759
+ assert.ok(++visits <= 64, `Recursive tuple ordering exceeded 64 type-expansion visits (${parameters})`);
1760
+ return original(...args);
1761
+ };
1762
+ try {
1763
+ for (let index = 0; index < 4; index++) {
1764
+ visits = 0;
1765
+ const node = (0, testState_1.getNodeAtMarker)(state, `op${index}`).parent;
1766
+ assert.ok(node?.nodeType === 7 /* ParseNodeType.BinaryOperation */);
1767
+ const result = evaluator.getTypeOfExpression(node);
1768
+ assert.ok(visits > 0);
1769
+ assert.ok((0, types_1.isTypeSame)(result.type, (0, types_1.combineTypes)([
1770
+ evaluator.getBuiltInObject(node, 'str'),
1771
+ evaluator.getBuiltInObject(node, 'bool'),
1772
+ ])));
1773
+ }
1774
+ }
1775
+ finally {
1776
+ evaluator.mapSubtypesExpandTypeVars = original;
1777
+ }
1778
+ }
1779
+ });
1780
+ test('TupleComparisonAliasSpecializationBound', () => {
1781
+ const lines = [
1782
+ '// @filename: test.py',
1783
+ '////type Tree[T] = tuple[int, tuple[Tree[list[T]], ...]]',
1784
+ '////def compare(left: Tree[int], right: Tree[int]) -> None:',
1785
+ ...['<', '<=', '>', '>='].map((operator, index) => `//// /*op${index}*/left ${operator} right`),
1786
+ ];
1787
+ const state = (0, testState_1.parseAndGetTestState)(lines.join('\n')).state;
1788
+ const evaluator = state.program.evaluator;
1789
+ const original = evaluator.mapSubtypesExpandTypeVars;
1790
+ let visits = 0;
1791
+ evaluator.mapSubtypesExpandTypeVars = (...args) => {
1792
+ if ((0, types_1.getTypeAliasInfo)(args[0])?.shared.name === 'Tree') {
1793
+ assert.ok(++visits <= 2 * (types_1.maxSubtypesForInferredType + 1), `Recursive aliases exceeded 64 specializations (${visits} operand expansions)`);
1794
+ }
1795
+ return original(...args);
1796
+ };
1797
+ try {
1798
+ for (let index = 0; index < 4; index++) {
1799
+ visits = 0;
1800
+ const node = (0, testState_1.getNodeAtMarker)(state, `op${index}`).parent;
1801
+ assert.ok(node?.nodeType === 7 /* ParseNodeType.BinaryOperation */);
1802
+ const result = evaluator.getTypeOfExpression(node);
1803
+ assert.ok(visits > 0);
1804
+ assert.ok((0, types_1.isTypeSame)(result.type, evaluator.getBuiltInObject(node, 'bool')));
1805
+ }
1806
+ }
1807
+ finally {
1808
+ evaluator.mapSubtypesExpandTypeVars = original;
1809
+ }
1810
+ });
1811
+ test('TupleComparisonHomogeneousVariadicWorkBound', () => {
1812
+ const methods = ['__lt__', '__le__', '__gt__', '__ge__'];
1813
+ const lines = [
1814
+ '// @filename: test.py',
1815
+ '////class Value:',
1816
+ ...methods.flatMap((method) => [
1817
+ `//// def ${method}(self, other: object) -> str:`,
1818
+ '//// return "ordered"',
1819
+ ]),
1820
+ `////type Values = tuple[*tuple[Value, ...], ${Array(32).fill('Value').join(', ')}]`,
1821
+ '////def compare(left: Values, right: Values) -> None:',
1822
+ ...['<', '<=', '>', '>='].map((operator, index) => `//// /*op${index}*/left ${operator} right`),
1823
+ ];
1824
+ const state = (0, testState_1.parseAndGetTestState)(lines.join('\n')).state;
1825
+ const evaluator = state.program.evaluator;
1826
+ const original = evaluator.getTypeOfMagicMethodCall;
1827
+ let calls = 0;
1828
+ evaluator.getTypeOfMagicMethodCall = (...args) => {
1829
+ calls++;
1830
+ return original(...args);
1831
+ };
1832
+ try {
1833
+ for (let index = 0; index < 4; index++) {
1834
+ calls = 0;
1835
+ const node = (0, testState_1.getNodeAtMarker)(state, `op${index}`).parent;
1836
+ assert.ok(node?.nodeType === 7 /* ParseNodeType.BinaryOperation */);
1837
+ const result = evaluator.getTypeOfExpression(node);
1838
+ assert.ok(calls > 0 && calls <= types_1.maxSubtypesForInferredType, `Homogeneous pairs required ${calls} calls`);
1839
+ assert.ok((0, types_1.isTypeSame)(result.type, (0, types_1.combineTypes)([evaluator.getBuiltInObject(node, 'str'), evaluator.getBuiltInObject(node, 'bool')])));
1840
+ }
1841
+ }
1842
+ finally {
1843
+ evaluator.getTypeOfMagicMethodCall = original;
1844
+ }
1845
+ });
1846
+ test('TupleLiteralInferenceScalarOrderingWorkBound', () => {
1847
+ const lines = [
1848
+ '// @filename: test.py',
1849
+ '////def compare(index: int) -> None:',
1850
+ '//// choices = ((0, 1, 2, 3, 4, 5, 6, 7),)[0]',
1851
+ '//// left = choices[index]',
1852
+ '//// right = choices[index]',
1853
+ ...['<', '<=', '>', '>='].map((operator, index) => `//// /*op${index}*/left ${operator} right`),
1854
+ ];
1855
+ const state = (0, testState_1.parseAndGetTestState)(lines.join('\n')).state;
1856
+ const evaluator = state.program.evaluator;
1857
+ const original = evaluator.getTypeOfMagicMethodCall;
1858
+ for (let index = 0; index < 4; index++) {
1859
+ const node = (0, testState_1.getNodeAtMarker)(state, `op${index}`).parent;
1860
+ assert.ok(node?.nodeType === 7 /* ParseNodeType.BinaryOperation */);
1861
+ const left = evaluator.getTypeOfExpression(node.d.leftExpr).type;
1862
+ evaluator.getTypeOfExpression(node.d.rightExpr);
1863
+ assert.strictEqual(left.props?.inferredTupleLiteral?.alternatives, 8);
1864
+ let calls = 0;
1865
+ evaluator.getTypeOfMagicMethodCall = (...args) => {
1866
+ calls++;
1867
+ return original(...args);
1868
+ };
1869
+ try {
1870
+ const result = evaluator.getTypeOfExpression(node);
1871
+ assert.ok(calls > 0 && calls <= 2, `Builtin scalar ordering expanded ${calls} magic calls`);
1872
+ assert.ok((0, types_1.isTypeSame)(result.type, evaluator.getBuiltInObject(node, 'bool')));
1873
+ }
1874
+ finally {
1875
+ evaluator.getTypeOfMagicMethodCall = original;
1876
+ }
1877
+ }
1878
+ });
1879
+ test('TupleLiteralInferenceConstantIndexWorkBound', () => {
1880
+ const values = Array.from({ length: 32 }, (_, index) => index % 7).join(', ');
1881
+ const lines = [
1882
+ '// @filename: test.py',
1883
+ '////def index_row() -> None:',
1884
+ `//// nested = ((${values},),)`,
1885
+ '//// row = nested[0]',
1886
+ '//// value = /*row*/row[0]',
1887
+ ];
1888
+ const state = (0, testState_1.parseAndGetTestState)(lines.join('\n')).state;
1889
+ const evaluator = state.program.evaluator;
1890
+ const rowNode = (0, testState_1.getNodeAtMarker)(state, 'row');
1891
+ assert.ok(rowNode.nodeType === 38 /* ParseNodeType.Name */);
1892
+ const node = rowNode.parent;
1893
+ assert.ok(node?.nodeType === 27 /* ParseNodeType.Index */);
1894
+ const row = evaluator.getTypeOfExpression(rowNode).type;
1895
+ assert.ok((0, types_1.isClassInstance)(row) && row.priv.tupleTypeArgs?.length === 32);
1896
+ const intType = evaluator.getBuiltInObject(node, 'int');
1897
+ assert.ok((0, types_1.isClassInstance)(intType));
1898
+ const expectedType = types_1.ClassType.cloneWithLiteral(intType, 0);
1899
+ const literals = new Set();
1900
+ const seen = new Set();
1901
+ const visit = (type) => {
1902
+ if (seen.has(type))
1903
+ return;
1904
+ seen.add(type);
1905
+ if ((0, types_1.isUnion)(type)) {
1906
+ type.priv.subtypes.forEach(visit);
1907
+ }
1908
+ else if ((0, types_1.isClassInstance)(type)) {
1909
+ if (type.priv.literalValue !== undefined)
1910
+ literals.add(type.priv);
1911
+ type.priv.typeArgs?.forEach(visit);
1912
+ type.priv.tupleTypeArgs?.forEach((arg) => visit(arg.type));
1913
+ }
1914
+ };
1915
+ visit(row);
1916
+ assert.ok(literals.size >= 7);
1917
+ let reads = 0;
1918
+ literals.forEach((priv) => {
1919
+ const value = priv.literalValue;
1920
+ Object.defineProperty(priv, 'literalValue', {
1921
+ get: () => {
1922
+ reads++;
1923
+ return value;
1924
+ },
1925
+ });
1926
+ });
1927
+ const result = evaluator.getTypeOfExpression(node);
1928
+ const indexReads = reads;
1929
+ assert.ok((0, types_1.isTypeSame)(result.type, expectedType));
1930
+ assert.ok(indexReads < 8, `Constant tuple indexing reprocessed ${indexReads} literal payloads`);
1931
+ });
1932
+ test('TupleLiteralInferenceConstantIndexMethodContract', () => {
1933
+ const code = `// @filename: test.py
1934
+ ////class /*noncallable*/NonCallable:
1935
+ //// __getitem__: None
1936
+ ////class /*constrained*/ConstrainedSelf:
1937
+ //// def __getitem__(self: str, index: int) -> str:
1938
+ //// return ""
1939
+ ////def index_row(value: tuple[int, str]) -> None:
1940
+ //// result = /*row*/value[0]
1941
+ `;
1942
+ for (const mode of ['missing', 'inherited', 'noncallable', 'constrained']) {
1943
+ const state = (0, testState_1.parseAndGetTestState)(code).state;
1944
+ const evaluator = state.program.evaluator;
1945
+ const rowNode = (0, testState_1.getNodeAtMarker)(state, 'row');
1946
+ assert.ok(rowNode.nodeType === 38 /* ParseNodeType.Name */);
1947
+ const node = rowNode.parent;
1948
+ assert.ok(node?.nodeType === 27 /* ParseNodeType.Index */);
1949
+ const row = evaluator.getTypeOfExpression(rowNode).type;
1950
+ assert.ok((0, types_1.isClassInstance)(row));
1951
+ const originalMembers = row.shared.mro.filter(types_1.isInstantiableClass).map((type) => ({
1952
+ fields: type.shared.fields,
1953
+ symbol: type.shared.fields.get('__getitem__'),
1954
+ }));
1955
+ assert.ok(row.shared.fields.has('__getitem__'));
1956
+ try {
1957
+ if (mode === 'missing') {
1958
+ originalMembers.forEach(({ fields }) => fields.delete('__getitem__'));
1959
+ }
1960
+ else if (mode === 'inherited') {
1961
+ row.shared.fields.delete('__getitem__');
1962
+ }
1963
+ else {
1964
+ const classNode = (0, testState_1.getNodeAtMarker)(state, mode);
1965
+ assert.ok(classNode.nodeType === 38 /* ParseNodeType.Name */);
1966
+ const replacement = evaluator.getTypeOfExpression(classNode).type;
1967
+ assert.ok((0, types_1.isInstantiableClass)(replacement));
1968
+ const symbol = replacement.shared.fields.get('__getitem__');
1969
+ assert.ok(symbol);
1970
+ row.shared.fields.set('__getitem__', symbol);
1971
+ }
1972
+ const method = evaluator.getBoundMagicMethod(row, '__getitem__');
1973
+ const result = evaluator.getTypeOfExpression(node).type;
1974
+ if (mode === 'inherited') {
1975
+ assert.ok(method);
1976
+ assert.ok((0, types_1.isTypeSame)(result, evaluator.getBuiltInObject(node, 'int')));
1977
+ }
1978
+ else {
1979
+ assert.ok(!method, `Native tuple method should not bind (${mode})`);
1980
+ assert.ok((0, types_1.isUnknown)(result));
1981
+ }
1982
+ }
1983
+ finally {
1984
+ originalMembers.forEach(({ fields, symbol }) => {
1985
+ if (symbol) {
1986
+ fields.set('__getitem__', symbol);
1987
+ }
1988
+ else {
1989
+ fields.delete('__getitem__');
1990
+ }
1991
+ });
1992
+ }
1993
+ }
1994
+ });
1995
+ test('TupleComparisonResults21', () => {
1996
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults21.py']);
1997
+ TestUtils.validateResults(analysisResults, 13);
1998
+ assert.strictEqual(analysisResults[0].errors.filter((diag) => diag.getRule() === diagnosticRules_1.DiagnosticRule.reportArgumentType).length, 11);
1999
+ assert.strictEqual(analysisResults[0].errors.filter((diag) => diag.getRule() === diagnosticRules_1.DiagnosticRule.reportIndexIssue).length, 2);
2000
+ });
2001
+ test('TupleComparisonResults22', () => {
2002
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults22.py']);
2003
+ TestUtils.validateResults(analysisResults, 8);
2004
+ assert.deepStrictEqual(analysisResults[0].errors.map((diag) => [diag.getRule(), diag.range.start.line + 1]), [
2005
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 29],
2006
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 41],
2007
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 56],
2008
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 63],
2009
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 71],
2010
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 80],
2011
+ [diagnosticRules_1.DiagnosticRule.reportReturnType, 142],
2012
+ [diagnosticRules_1.DiagnosticRule.reportReturnType, 146],
2013
+ ]);
2014
+ });
2015
+ test('TupleComparisonFreshEmptyListWorkBound', () => {
2016
+ for (const [width, exact] of [
2017
+ [types_1.maxSubtypesForInferredType, true],
2018
+ [types_1.maxSubtypesForInferredType + 1, true],
2019
+ [types_1.maxSubtypesForInferredType, false],
2020
+ [types_1.maxSubtypesForInferredType + 1, false],
2021
+ ]) {
2022
+ const left = Array.from({ length: width }, () => '[]').join(', ');
2023
+ const right = Array.from({ length: width }, () => (exact ? '[ReturnsText()]' : 'other')).join(', ');
2024
+ const state = (0, testState_1.parseAndGetTestState)(`// @filename: test.py
2025
+ ////class ReturnsText:
2026
+ //// def __lt__(self, other: object) -> str: ...
2027
+ ////def compare(other: list[ReturnsText]) -> None:
2028
+ //// (/*empty*/${left}) < (${right})
2029
+ `).state;
2030
+ const evaluator = state.program.evaluator;
2031
+ const emptyNode = (0, testState_1.getNodeAtMarker)(state, 'empty');
2032
+ assert.ok(emptyNode.nodeType === 34 /* ParseNodeType.List */);
2033
+ const leftNode = emptyNode.parent;
2034
+ assert.ok(leftNode?.nodeType === 52 /* ParseNodeType.Tuple */);
2035
+ const comparison = leftNode.parent;
2036
+ assert.ok(comparison?.nodeType === 7 /* ParseNodeType.BinaryOperation */);
2037
+ const rightNode = comparison.d.rightExpr;
2038
+ assert.ok(rightNode.nodeType === 52 /* ParseNodeType.Tuple */);
2039
+ const leftType = evaluator.getTypeOfExpression(leftNode).type;
2040
+ const rightType = evaluator.getTypeOfExpression(rightNode).type;
2041
+ assert.ok((0, types_1.isClassInstance)(leftType) && (0, types_1.isClassInstance)(rightType));
2042
+ const nodes = [...leftNode.d.items, ...rightNode.d.items];
2043
+ const descriptors = nodes.map((node) => Object.getOwnPropertyDescriptor(node, 'nodeType'));
2044
+ let reads = 0;
2045
+ nodes.forEach((node, index) => {
2046
+ Object.defineProperty(node, 'nodeType', {
2047
+ get: () => {
2048
+ reads++;
2049
+ return descriptors[index].value;
2050
+ },
2051
+ configurable: true,
2052
+ });
2053
+ });
2054
+ let result;
2055
+ try {
2056
+ result = TupleComparison.getTupleComparisonResult(evaluator, leftType, rightType, 20 /* OperatorType.LessThan */, comparison);
2057
+ }
2058
+ finally {
2059
+ nodes.forEach((node, index) => Object.defineProperty(node, 'nodeType', descriptors[index]));
2060
+ }
2061
+ assert.ok(result);
2062
+ assert.strictEqual(evaluator.printType(result.type), exact && width === types_1.maxSubtypesForInferredType ? 'bool' : 'Unknown | bool');
2063
+ assert.ok(reads <= 4 * width, `Empty-list proof repeated source scans (${reads} node-kind reads)`);
2064
+ }
2065
+ });
2066
+ test('TupleComparisonResults23', () => {
2067
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleComparisonResults23.py']);
2068
+ TestUtils.validateResults(analysisResults, 9);
2069
+ assert.deepStrictEqual(analysisResults[0].errors.map((diag) => [diag.getRule(), diag.range.start.line + 1]), [
2070
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 35],
2071
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 36],
2072
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 37],
2073
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 38],
2074
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 50],
2075
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 51],
2076
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 52],
2077
+ [diagnosticRules_1.DiagnosticRule.reportAssertTypeFailure, 53],
2078
+ [diagnosticRules_1.DiagnosticRule.reportReturnType, 83],
2079
+ ]);
2080
+ });
2081
+ test('TupleComparisonFreshEmptyListChainedComparison', () => {
2082
+ for (const [operator, token] of [
2083
+ [20 /* OperatorType.LessThan */, '<'],
2084
+ [21 /* OperatorType.LessThanOrEqual */, '<='],
2085
+ [15 /* OperatorType.GreaterThan */, '>'],
2086
+ [16 /* OperatorType.GreaterThanOrEqual */, '>='],
2087
+ ]) {
2088
+ for (const parenthesized of [false, true]) {
2089
+ const state = (0, testState_1.parseAndGetTestState)(`// @filename: test.py
2090
+ ////class ReturnsText:
2091
+ //// def __lt__(self, other: object) -> str: ...
2092
+ ////class MutatesTuple:
2093
+ //// def __lt__(self, other: tuple[list[ReturnsText]] | bool) -> bool:
2094
+ //// if isinstance(other, tuple):
2095
+ //// other[0].append(ReturnsText())
2096
+ //// return True
2097
+ ////def compare(prefix: MutatesTuple) -> None:
2098
+ //// prefix < ${parenthesized ? '(' : ''}(/*empty*/[],) ${token} ([ReturnsText()],)${parenthesized ? ')' : ''}
2099
+ `).state;
2100
+ const evaluator = state.program.evaluator;
2101
+ const emptyNode = (0, testState_1.getNodeAtMarker)(state, 'empty');
2102
+ assert.ok(emptyNode.nodeType === 34 /* ParseNodeType.List */);
2103
+ const leftNode = emptyNode.parent;
2104
+ assert.ok(leftNode?.nodeType === 52 /* ParseNodeType.Tuple */);
2105
+ const comparison = leftNode.parent;
2106
+ assert.ok(comparison?.nodeType === 7 /* ParseNodeType.BinaryOperation */);
2107
+ assert.strictEqual(!!comparison.d.hasParens, parenthesized);
2108
+ const rightNode = comparison.d.rightExpr;
2109
+ assert.ok(rightNode.nodeType === 52 /* ParseNodeType.Tuple */);
2110
+ const leftType = evaluator.getTypeOfExpression(leftNode).type;
2111
+ const rightType = evaluator.getTypeOfExpression(rightNode).type;
2112
+ assert.ok((0, types_1.isClassInstance)(leftType) && (0, types_1.isClassInstance)(rightType));
2113
+ const result = TupleComparison.getTupleComparisonResult(evaluator, leftType, rightType, operator, comparison);
2114
+ assert.ok(result);
2115
+ assert.strictEqual(evaluator.printType(result.type), parenthesized ? 'bool' : 'Unknown | bool');
2116
+ }
2117
+ }
2118
+ });
2119
+ test('TupleLiteralInferenceUnionRetentionWorkBound', () => {
2120
+ for (const width of [8, 9]) {
2121
+ const alternatives = Array.from({ length: width }, (_, index) => `tuple[Literal[${index}]]`).join(' | ');
2122
+ const state = (0, testState_1.parseAndGetTestState)(`// @filename: test.py
2123
+ ////from typing import Literal
2124
+ ////def retain(value: ${alternatives}, fallback: tuple[int]) -> None:
2125
+ //// /*value*/value
2126
+ //// /*fallback*/fallback
2127
+ `).state;
2128
+ const evaluator = state.program.evaluator;
2129
+ const nameNode = (0, testState_1.getNodeAtMarker)(state, 'value');
2130
+ const fallbackNode = (0, testState_1.getNodeAtMarker)(state, 'fallback');
2131
+ assert.ok(nameNode.nodeType === 38 /* ParseNodeType.Name */ && fallbackNode.nodeType === 38 /* ParseNodeType.Name */);
2132
+ const type = (0, types_1.retainInferredTupleLiterals)(evaluator.getTypeOfExpression(nameNode).type, evaluator.getTypeOfExpression(fallbackNode).type);
2133
+ assert.ok((0, types_1.isUnion)(type) && type.priv.subtypes.length === width);
2134
+ const info = type.props?.inferredTupleLiteral;
2135
+ assert.ok(info && info.alternatives === width);
2136
+ const tuples = type.priv.subtypes.filter(types_1.isClassInstance);
2137
+ assert.strictEqual(tuples.length, width);
2138
+ const values = tuples.map((tuple) => {
2139
+ const args = tuple.priv.tupleTypeArgs;
2140
+ assert.ok(args?.length === 1);
2141
+ const value = args[0].type;
2142
+ assert.ok((0, types_1.isClassInstance)(value) && typeof value.priv.literalValue === 'number');
2143
+ return value.priv.literalValue;
2144
+ });
2145
+ assert.deepStrictEqual(values.sort((left, right) => left - right), Array.from({ length: width }, (_, index) => index));
2146
+ let reads = 0;
2147
+ tuples.forEach((tuple) => {
2148
+ const args = tuple.priv.tupleTypeArgs;
2149
+ Object.defineProperty(tuple.priv, 'tupleTypeArgs', {
2150
+ get: () => {
2151
+ reads++;
2152
+ return args;
2153
+ },
2154
+ });
2155
+ });
2156
+ const retained = (0, types_1.retainInferredTupleLiterals)(type, info.baseType);
2157
+ const retentionReads = reads;
2158
+ assert.ok(retentionReads > 0 && retentionReads <= types_1.maxSubtypesForInferredType, `Unchanged ${width}-way tuple union required ${retentionReads} tuple-argument reads`);
2159
+ assert.ok((0, types_1.isUnion)(retained) && retained.priv.subtypes === type.priv.subtypes);
2160
+ assert.ok(retained.props?.inferredTupleLiteral?.baseType === info.baseType);
2161
+ assert.strictEqual(retained.props?.inferredTupleLiteral?.alternatives, width);
2162
+ assert.strictEqual(evaluator.printType(retained), evaluator.printType(type));
2163
+ }
2164
+ });
2165
+ test('TupleLiteralInferenceRetentionSubtypeNormalization', () => {
2166
+ const state = (0, testState_1.parseAndGetTestState)(`// @filename: test.py
2167
+ ////from typing import Literal
2168
+ ////def retain(value: Literal[0] | tuple[Literal[1]], fallback: int | tuple[int]) -> None:
2169
+ //// /*value*/value
2170
+ //// /*fallback*/fallback
2171
+ `).state;
2172
+ const evaluator = state.program.evaluator;
2173
+ const valueNode = (0, testState_1.getNodeAtMarker)(state, 'value');
2174
+ const fallbackNode = (0, testState_1.getNodeAtMarker)(state, 'fallback');
2175
+ assert.ok(valueNode.nodeType === 38 /* ParseNodeType.Name */ && fallbackNode.nodeType === 38 /* ParseNodeType.Name */);
2176
+ const type = evaluator.getTypeOfExpression(valueNode).type;
2177
+ const baseType = evaluator.getTypeOfExpression(fallbackNode).type;
2178
+ assert.ok((0, types_1.isUnion)(type) && type.priv.subtypes.length === 2);
2179
+ const literal = type.priv.subtypes.find((subtype) => (0, types_1.isClassInstance)(subtype) && subtype.priv.literalValue === 0);
2180
+ const tuple = type.priv.subtypes.find((subtype) => (0, types_1.isClassInstance)(subtype) && !!subtype.priv.tupleTypeArgs);
2181
+ assert.ok(literal && tuple && !literal.props?.inferredTupleLiteral);
2182
+ const retained = (0, types_1.retainInferredTupleLiterals)(type, baseType);
2183
+ assert.ok((0, types_1.isUnion)(retained) && retained.priv.subtypes.length === 2);
2184
+ const retainedLiteral = retained.priv.subtypes.find((subtype) => (0, types_1.isClassInstance)(subtype) && subtype.priv.literalValue === 0);
2185
+ assert.ok(retainedLiteral && retainedLiteral !== literal);
2186
+ assert.ok(retainedLiteral.props?.inferredTupleLiteral);
2187
+ assert.ok((0, types_1.isClassInstance)(retainedLiteral.props.inferredTupleLiteral.baseType));
2188
+ assert.strictEqual(retainedLiteral.props.inferredTupleLiteral.baseType.priv.literalValue, undefined);
2189
+ assert.ok(retained.priv.subtypes.includes(tuple));
2190
+ assert.ok(retained.props?.inferredTupleLiteral?.baseType === baseType);
2191
+ assert.strictEqual(literal.props?.inferredTupleLiteral, undefined);
2192
+ assert.strictEqual(evaluator.printType(retained), evaluator.printType(type));
2193
+ });
2194
+ test('TupleLiteralInferenceUnchangedSpecializationWorkBound', () => {
2195
+ for (const width of [8, 9]) {
2196
+ const choices = Array.from({ length: width }, (_, index) => `(${index},)`).join(', ');
2197
+ const state = (0, testState_1.parseAndGetTestState)(`// @filename: test.py
2198
+ ////def select() -> None:
2199
+ //// choices = (${choices})
2200
+ //// /*choices*/choices
2201
+ `).state;
2202
+ const evaluator = state.program.evaluator;
2203
+ const node = (0, testState_1.getNodeAtMarker)(state, 'choices');
2204
+ assert.ok(node.nodeType === 38 /* ParseNodeType.Name */);
2205
+ const type = evaluator.getTypeOfExpression(node).type;
2206
+ assert.ok((0, types_1.isClassInstance)(type) && types_1.ClassType.isTupleClass(type));
2207
+ const originalArgs = type.priv.tupleTypeArgs;
2208
+ assert.ok(originalArgs && originalArgs.length === width);
2209
+ const info = type.props?.inferredTupleLiteral;
2210
+ assert.ok(info && info.alternatives === 1);
2211
+ let reads = 0;
2212
+ originalArgs.forEach((arg, index) => {
2213
+ const tuple = arg.type;
2214
+ assert.ok((0, types_1.isClassInstance)(tuple));
2215
+ const args = tuple.priv.tupleTypeArgs;
2216
+ assert.ok(args?.length === 1);
2217
+ const value = args[0].type;
2218
+ assert.ok((0, types_1.isClassInstance)(value) && value.priv.literalValue === index);
2219
+ Object.defineProperty(tuple.priv, 'tupleTypeArgs', {
2220
+ get: () => {
2221
+ reads++;
2222
+ return args;
2223
+ },
2224
+ });
2225
+ });
2226
+ const transformedArgs = [];
2227
+ let readsAtLastTransform = 0;
2228
+ class IdentityTransformer extends typeUtils_1.TypeVarTransformer {
2229
+ apply(arg) {
2230
+ transformedArgs.push(arg);
2231
+ readsAtLastTransform = reads;
2232
+ return arg;
2233
+ }
2234
+ }
2235
+ const transformed = new IdentityTransformer().transformTypeVarsInClassType(type, 0);
2236
+ const recombinationReads = reads - readsAtLastTransform;
2237
+ assert.strictEqual(transformedArgs.length, width);
2238
+ originalArgs.forEach((arg, index) => assert.strictEqual(transformedArgs[index], arg.type));
2239
+ assert.strictEqual(recombinationReads, 0, `Unchanged ${width}-element tuple required ${recombinationReads} tuple-argument reads after transformation`);
2240
+ assert.strictEqual(transformed, type);
2241
+ assert.strictEqual(type.priv.tupleTypeArgs, originalArgs);
2242
+ assert.strictEqual(type.props?.inferredTupleLiteral, info);
2243
+ }
2244
+ });
2245
+ test('TupleLiteralInferenceUnmarkedSpecializationWorkBound', () => {
2246
+ for (const width of [types_1.maxSubtypesForInferredType, 8, 16, 32]) {
2247
+ const entries = Array.from({ length: width }, () => 'int').join(', ');
2248
+ const state = (0, testState_1.parseAndGetTestState)(`// @filename: test.py
2249
+ ////def specialize(value: tuple[${entries}]) -> None:
2250
+ //// /*value*/value
2251
+ `).state;
2252
+ const evaluator = state.program.evaluator;
2253
+ const node = (0, testState_1.getNodeAtMarker)(state, 'value');
2254
+ assert.ok(node.nodeType === 38 /* ParseNodeType.Name */);
2255
+ const type = evaluator.getTypeOfExpression(node).type;
2256
+ assert.ok((0, types_1.isClassInstance)(type) && types_1.ClassType.isTupleClass(type));
2257
+ assert.strictEqual(type.props?.inferredTupleLiteral, undefined);
2258
+ const args = type.priv.tupleTypeArgs;
2259
+ assert.ok(args?.length === width);
2260
+ const expectedType = evaluator.printType(type);
2261
+ const descriptors = args.map((arg) => Object.getOwnPropertyDescriptor(arg, 'type'));
2262
+ let reads = 0;
2263
+ args.forEach((arg, index) => {
2264
+ const value = arg.type;
2265
+ assert.strictEqual(value.props?.inferredTupleLiteral, undefined);
2266
+ Object.defineProperty(arg, 'type', {
2267
+ configurable: true,
2268
+ get: () => {
2269
+ reads++;
2270
+ return value;
2271
+ },
2272
+ });
2273
+ assert.ok(descriptors[index]);
2274
+ });
2275
+ let specialized;
2276
+ try {
2277
+ specialized = types_1.ClassType.specialize(type, type.priv.typeArgs, type.priv.isTypeArgExplicit, !!type.priv.includeSubclasses, args, type.priv.isEmptyContainer);
2278
+ }
2279
+ finally {
2280
+ args.forEach((arg, index) => Object.defineProperty(arg, 'type', descriptors[index]));
2281
+ }
2282
+ assert.ok(reads <= width, `Unmarked ${width}-element specialization required ${reads} tuple-entry reads`);
2283
+ assert.notStrictEqual(specialized, type);
2284
+ assert.notStrictEqual(specialized.priv.tupleTypeArgs, args);
2285
+ assert.deepStrictEqual(specialized.priv.tupleTypeArgs, args);
2286
+ assert.strictEqual(specialized.priv.typeArgs, type.priv.typeArgs);
2287
+ assert.strictEqual(specialized.props?.inferredTupleLiteral, undefined);
2288
+ assert.strictEqual(evaluator.printType(specialized), expectedType);
2289
+ assert.strictEqual(type.priv.tupleTypeArgs, args);
2290
+ }
2291
+ });
2292
+ test('TupleLiteralInferenceSingletonUnionWorkBound', () => {
2293
+ const state = (0, testState_1.parseAndGetTestState)('// @filename: test.py\n//// /*value*/0').state;
2294
+ const evaluator = state.program.evaluator;
2295
+ const node = (0, testState_1.getNodeAtMarker)(state, 'value');
2296
+ const intType = evaluator.getBuiltInObject(node, 'int');
2297
+ const strType = evaluator.getBuiltInObject(node, 'str');
2298
+ assert.ok((0, types_1.isClassInstance)(intType));
2299
+ const retained = (0, types_1.retainInferredTupleLiterals)(types_1.ClassType.cloneWithLiteral(intType, 1), intType);
2300
+ assert.ok(retained.props?.inferredTupleLiteral);
2301
+ for (const original of [
2302
+ intType,
2303
+ (0, types_1.combineTypes)([intType, strType]),
2304
+ retained,
2305
+ types_1.NeverType.createNever(),
2306
+ types_1.UnknownType.create(),
2307
+ ]) {
2308
+ const type = types_1.TypeBase.cloneType(original);
2309
+ const props = type.props;
2310
+ const descriptor = Object.getOwnPropertyDescriptor(type, 'props');
2311
+ let reads = 0;
2312
+ Object.defineProperty(type, 'props', {
2313
+ configurable: true,
2314
+ get: () => {
2315
+ reads++;
2316
+ return props;
2317
+ },
2318
+ });
2319
+ try {
2320
+ assert.strictEqual((0, types_1.combineTypes)([type]), type);
2321
+ assert.strictEqual((0, types_1.combineTypes)([type], { skipElideRedundantLiterals: true }), type);
2322
+ assert.strictEqual((0, types_1.combineTypes)([type], { maxSubtypeCount: 0 }), type);
2323
+ }
2324
+ finally {
2325
+ if (descriptor) {
2326
+ Object.defineProperty(type, 'props', descriptor);
2327
+ }
2328
+ else {
2329
+ delete type.props;
2330
+ }
2331
+ }
2332
+ assert.strictEqual(reads, 0, 'A singleton union should not inspect optional inference metadata');
2333
+ assert.strictEqual(type.props, props);
2334
+ }
2335
+ });
2336
+ test('TupleSpecializationTypeVarSubstitution', () => {
2337
+ const state = (0, testState_1.parseAndGetTestState)(`// @filename: test.py
2338
+ ////from typing import TypeVar, TypeVarTuple, Unpack
2339
+ ////T = TypeVar("T")
2340
+ ////Ts = TypeVarTuple("Ts")
2341
+ ////def /*function*/specialize(
2342
+ //// bounded: tuple[T, int],
2343
+ //// unbounded: tuple[T, ...],
2344
+ //// unpacked: tuple[Unpack[Ts]],
2345
+ //// scalar: str,
2346
+ //// pair: tuple[str, int],
2347
+ ////) -> None:
2348
+ //// /*boundBody*/bounded
2349
+ `).state;
2350
+ const evaluator = state.program.evaluator;
2351
+ const functionNode = (0, testState_1.getNodeAtMarker)(state, 'function').parent;
2352
+ assert.ok(functionNode?.nodeType === 31 /* ParseNodeType.Function */);
2353
+ const functionResult = evaluator.getTypeOfFunction(functionNode);
2354
+ assert.ok(functionResult);
2355
+ const typeAt = (index) => {
2356
+ const type = types_1.FunctionType.getParamType(functionResult.functionType, index);
2357
+ assert.ok((0, types_1.isClassInstance)(type) && types_1.ClassType.isTupleClass(type));
2358
+ return type;
2359
+ };
2360
+ const scalar = types_1.FunctionType.getParamType(functionResult.functionType, 3);
2361
+ assert.ok((0, types_1.isClassInstance)(scalar) && types_1.ClassType.isBuiltIn(scalar, 'str'));
2362
+ for (const index of [0, 1]) {
2363
+ const type = typeAt(index);
2364
+ const args = type.priv.tupleTypeArgs;
2365
+ assert.ok(args && (0, types_1.isTypeVar)(args[0].type) && !types_1.TypeVarType.isBound(args[0].type));
2366
+ const originalTypeVar = args[0].type;
2367
+ const solution = new constraintSolution_1.ConstraintSolution();
2368
+ solution.setType(originalTypeVar, scalar);
2369
+ const specialized = (0, typeUtils_1.applySolvedTypeVars)(type, solution);
2370
+ assert.ok((0, types_1.isClassInstance)(specialized) && specialized !== type);
2371
+ assert.strictEqual(evaluator.printType(specialized), index === 0 ? 'tuple[str, int]' : 'tuple[str, ...]');
2372
+ const effectiveArgs = specialized.priv.typeArgs;
2373
+ assert.ok(effectiveArgs?.length === 1);
2374
+ assert.strictEqual(evaluator.printType(effectiveArgs[0]), index === 0 ? 'str | int' : 'str');
2375
+ assert.strictEqual(type.priv.tupleTypeArgs, args);
2376
+ assert.strictEqual(args[0].type, originalTypeVar);
2377
+ if (index === 0) {
2378
+ const boundNode = (0, testState_1.getNodeAtMarker)(state, 'boundBody');
2379
+ assert.ok(boundNode.nodeType === 38 /* ParseNodeType.Name */);
2380
+ const boundType = evaluator.getTypeOfExpression(boundNode).type;
2381
+ assert.ok((0, types_1.isClassInstance)(boundType) && boundType.priv.tupleTypeArgs);
2382
+ const boundTypeVar = boundType.priv.tupleTypeArgs[0].type;
2383
+ assert.ok((0, types_1.isTypeVar)(boundTypeVar) && types_1.TypeVarType.isBound(boundTypeVar));
2384
+ assert.strictEqual((0, typeUtils_1.applySolvedTypeVars)(boundType, solution), boundType);
2385
+ }
2386
+ else {
2387
+ solution.setType(originalTypeVar, types_1.NeverType.createNever());
2388
+ const empty = (0, typeUtils_1.applySolvedTypeVars)(type, solution);
2389
+ assert.ok((0, types_1.isClassInstance)(empty) && empty.priv.tupleTypeArgs?.length === 0);
2390
+ assert.strictEqual(evaluator.printType(empty), 'tuple[()]');
2391
+ assert.ok(empty.priv.typeArgs?.length === 1 && (0, types_1.isNever)(empty.priv.typeArgs[0]));
2392
+ }
2393
+ }
2394
+ const unpacked = typeAt(2);
2395
+ const args = unpacked.priv.tupleTypeArgs;
2396
+ assert.ok(args?.length === 1 && (0, types_1.isTypeVarTuple)(args[0].type));
2397
+ const solution = new constraintSolution_1.ConstraintSolution();
2398
+ solution.setType(args[0].type, types_1.ClassType.cloneForUnpacked(typeAt(4)));
2399
+ const specialized = (0, typeUtils_1.applySolvedTypeVars)(unpacked, solution);
2400
+ assert.ok((0, types_1.isClassInstance)(specialized) && specialized !== unpacked);
2401
+ assert.strictEqual(evaluator.printType(specialized), 'tuple[str, int]');
2402
+ const effectiveArgs = specialized.priv.typeArgs;
2403
+ assert.ok(effectiveArgs?.length === 1);
2404
+ assert.strictEqual(evaluator.printType(effectiveArgs[0]), 'str | int');
2405
+ assert.strictEqual(unpacked.priv.tupleTypeArgs, args);
2406
+ assert.ok((0, types_1.isTypeVarTuple)(args[0].type));
2407
+ });
2408
+ test('TupleLiteralInference7', () => {
2409
+ const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tupleLiteralInference7.py']);
2410
+ TestUtils.validateResults(analysisResults, 7);
2411
+ assert.deepStrictEqual(analysisResults[0].errors
2412
+ .filter((diag) => diag.getRule() === diagnosticRules_1.DiagnosticRule.reportArgumentType)
2413
+ .map((diag) => diag.range.start.line + 1), [24, 29, 51, 61, 75, 90]);
2414
+ assert.deepStrictEqual(analysisResults[0].errors
2415
+ .filter((diag) => diag.getRule() === diagnosticRules_1.DiagnosticRule.reportCallIssue)
2416
+ .map((diag) => diag.range.start.line + 1), [90]);
2417
+ });
2418
+ test('TupleLiteralInferenceDynamicIndexWorkBound', () => {
2419
+ for (const width of [8, 9, 2]) {
2420
+ const choices = Array.from({ length: width }, (_, index) => `(${index},)`).join(', ');
2421
+ const state = (0, testState_1.parseAndGetTestState)(`// @filename: test.py
2422
+ ////def select(index: int) -> None:
2423
+ //// choices = (${choices})
2424
+ //// selected = /*selected*/choices[index]
2425
+ `).state;
2426
+ const evaluator = state.program.evaluator;
2427
+ const nameNode = (0, testState_1.getNodeAtMarker)(state, 'selected');
2428
+ assert.ok(nameNode.nodeType === 38 /* ParseNodeType.Name */);
2429
+ const node = nameNode.parent;
2430
+ assert.ok(node?.nodeType === 27 /* ParseNodeType.Index */);
2431
+ const type = evaluator.getTypeOfExpression(nameNode).type;
2432
+ assert.ok((0, types_1.isClassInstance)(type) && type.priv.tupleTypeArgs?.length === width);
2433
+ const originalArgs = type.priv.tupleTypeArgs;
2434
+ const info = type.props?.inferredTupleLiteral;
2435
+ assert.ok(info && (0, types_1.isClassInstance)(info.baseType));
2436
+ const intType = evaluator.getBuiltInObject(node, 'int');
2437
+ let reads = 0;
2438
+ originalArgs.forEach((arg, index) => {
2439
+ const row = arg.type;
2440
+ assert.ok((0, types_1.isClassInstance)(row));
2441
+ const args = row.priv.tupleTypeArgs;
2442
+ assert.ok(args?.length === 1);
2443
+ const value = args[0].type;
2444
+ assert.ok((0, types_1.isClassInstance)(value) && value.priv.literalValue === index);
2445
+ Object.defineProperty(row.priv, 'tupleTypeArgs', {
2446
+ get: () => {
2447
+ reads++;
2448
+ return args;
2449
+ },
2450
+ });
2451
+ });
2452
+ const result = evaluator.getTypeOfExpression(node);
2453
+ const indexReads = reads;
2454
+ assert.ok(indexReads <= types_1.maxSubtypesForInferredType, `Dynamic ${width}-row indexing reprocessed ${indexReads} retained tuple arguments`);
2455
+ assert.ok((0, types_1.isClassInstance)(result.type) && result.type.priv.tupleTypeArgs?.length === 1);
2456
+ assert.ok((0, types_1.isTypeSame)(result.type.priv.tupleTypeArgs[0].type, intType));
2457
+ assert.strictEqual(result.type.props?.inferredTupleLiteral, undefined);
2458
+ assert.strictEqual(type.priv.tupleTypeArgs, originalArgs);
2459
+ assert.strictEqual(type.props?.inferredTupleLiteral, info);
2460
+ }
2461
+ });
1172
2462
  //# sourceMappingURL=typeEvaluator8.test.js.map