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

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 (238) hide show
  1. package/dist/analyzer/binder.js +29 -12
  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/constructorTransform.js +12 -0
  8. package/dist/analyzer/constructorTransform.js.map +1 -1
  9. package/dist/analyzer/constructors.d.ts +1 -1
  10. package/dist/analyzer/constructors.js +1 -1
  11. package/dist/analyzer/constructors.js.map +1 -1
  12. package/dist/analyzer/dataClasses.js +14 -5
  13. package/dist/analyzer/dataClasses.js.map +1 -1
  14. package/dist/analyzer/decorators.d.ts +2 -2
  15. package/dist/analyzer/decorators.js +22 -2
  16. package/dist/analyzer/decorators.js.map +1 -1
  17. package/dist/analyzer/functionTransform.d.ts +5 -0
  18. package/dist/analyzer/functionTransform.js +6 -3
  19. package/dist/analyzer/functionTransform.js.map +1 -1
  20. package/dist/analyzer/missingBindingMetadata.d.ts +6 -0
  21. package/dist/analyzer/missingBindingMetadata.js +22 -0
  22. package/dist/analyzer/missingBindingMetadata.js.map +1 -0
  23. package/dist/analyzer/namedTuples.d.ts +5 -1
  24. package/dist/analyzer/namedTuples.js +64 -35
  25. package/dist/analyzer/namedTuples.js.map +1 -1
  26. package/dist/analyzer/operations.js +45 -1
  27. package/dist/analyzer/operations.js.map +1 -1
  28. package/dist/analyzer/parseTreeUtils.d.ts +2 -0
  29. package/dist/analyzer/parseTreeUtils.js +43 -0
  30. package/dist/analyzer/parseTreeUtils.js.map +1 -1
  31. package/dist/analyzer/patternMatching.d.ts +5 -1
  32. package/dist/analyzer/patternMatching.js +6 -1
  33. package/dist/analyzer/patternMatching.js.map +1 -1
  34. package/dist/analyzer/program.d.ts +3 -0
  35. package/dist/analyzer/program.js +71 -1
  36. package/dist/analyzer/program.js.map +1 -1
  37. package/dist/analyzer/sentinel.js +7 -1
  38. package/dist/analyzer/sentinel.js.map +1 -1
  39. package/dist/analyzer/sourceFile.js +13 -6
  40. package/dist/analyzer/sourceFile.js.map +1 -1
  41. package/dist/analyzer/tupleComparison.d.ts +6 -0
  42. package/dist/analyzer/tupleComparison.js +731 -0
  43. package/dist/analyzer/tupleComparison.js.map +1 -0
  44. package/dist/analyzer/tuples.d.ts +1 -1
  45. package/dist/analyzer/tuples.js +35 -4
  46. package/dist/analyzer/tuples.js.map +1 -1
  47. package/dist/analyzer/typeCacheUtils.d.ts +1 -0
  48. package/dist/analyzer/typeCacheUtils.js +10 -0
  49. package/dist/analyzer/typeCacheUtils.js.map +1 -1
  50. package/dist/analyzer/typeEvaluator/boundMethodSpecialization.d.ts +15 -0
  51. package/dist/analyzer/typeEvaluator/boundMethodSpecialization.js +98 -0
  52. package/dist/analyzer/typeEvaluator/boundMethodSpecialization.js.map +1 -0
  53. package/dist/analyzer/typeEvaluator/classConstruction.d.ts +25 -0
  54. package/dist/analyzer/typeEvaluator/classConstruction.js +251 -0
  55. package/dist/analyzer/typeEvaluator/classConstruction.js.map +1 -0
  56. package/dist/analyzer/typeEvaluator/collectionInference.d.ts +41 -0
  57. package/dist/analyzer/typeEvaluator/collectionInference.js +695 -0
  58. package/dist/analyzer/typeEvaluator/collectionInference.js.map +1 -0
  59. package/dist/analyzer/typeEvaluator/diagnostics.d.ts +33 -0
  60. package/dist/analyzer/typeEvaluator/diagnostics.js +198 -0
  61. package/dist/analyzer/typeEvaluator/diagnostics.js.map +1 -0
  62. package/dist/analyzer/typeEvaluator/evaluatorCore.d.ts +80 -0
  63. package/dist/analyzer/typeEvaluator/evaluatorCore.js +1126 -0
  64. package/dist/analyzer/typeEvaluator/evaluatorCore.js.map +1 -0
  65. package/dist/analyzer/typeEvaluator/evaluatorNarrowing.d.ts +17 -0
  66. package/dist/analyzer/typeEvaluator/evaluatorNarrowing.js +370 -0
  67. package/dist/analyzer/typeEvaluator/evaluatorNarrowing.js.map +1 -0
  68. package/dist/analyzer/typeEvaluator/expressionEvaluation.d.ts +81 -0
  69. package/dist/analyzer/typeEvaluator/expressionEvaluation.js +1802 -0
  70. package/dist/analyzer/typeEvaluator/expressionEvaluation.js.map +1 -0
  71. package/dist/analyzer/typeEvaluator/flowAnalysis.d.ts +33 -0
  72. package/dist/analyzer/typeEvaluator/flowAnalysis.js +217 -0
  73. package/dist/analyzer/typeEvaluator/flowAnalysis.js.map +1 -0
  74. package/dist/analyzer/typeEvaluator/internals.d.ts +137 -0
  75. package/dist/analyzer/typeEvaluator/internals.js +10 -0
  76. package/dist/analyzer/typeEvaluator/internals.js.map +1 -0
  77. package/dist/analyzer/typeEvaluator/literalTypes.d.ts +3 -0
  78. package/dist/analyzer/typeEvaluator/literalTypes.js +52 -0
  79. package/dist/analyzer/typeEvaluator/literalTypes.js.map +1 -0
  80. package/dist/analyzer/typeEvaluator/memberResolution.d.ts +79 -0
  81. package/dist/analyzer/typeEvaluator/memberResolution.js +1007 -0
  82. package/dist/analyzer/typeEvaluator/memberResolution.js.map +1 -0
  83. package/dist/analyzer/typeEvaluator/overrideValidation.d.ts +15 -0
  84. package/dist/analyzer/typeEvaluator/overrideValidation.js +422 -0
  85. package/dist/analyzer/typeEvaluator/overrideValidation.js.map +1 -0
  86. package/dist/analyzer/typeEvaluator/returnTypeInference.d.ts +33 -0
  87. package/dist/analyzer/typeEvaluator/returnTypeInference.js +226 -0
  88. package/dist/analyzer/typeEvaluator/returnTypeInference.js.map +1 -0
  89. package/dist/analyzer/typeEvaluator/specialFormCreation.d.ts +39 -0
  90. package/dist/analyzer/typeEvaluator/specialFormCreation.js +1728 -0
  91. package/dist/analyzer/typeEvaluator/specialFormCreation.js.map +1 -0
  92. package/dist/analyzer/typeEvaluator/tupleUnpacking.d.ts +2 -0
  93. package/dist/analyzer/typeEvaluator/tupleUnpacking.js +36 -0
  94. package/dist/analyzer/typeEvaluator/tupleUnpacking.js.map +1 -0
  95. package/dist/analyzer/typeEvaluator/typeCompatibility.d.ts +36 -0
  96. package/dist/analyzer/typeEvaluator/typeCompatibility.js +1320 -0
  97. package/dist/analyzer/typeEvaluator/typeCompatibility.js.map +1 -0
  98. package/dist/analyzer/typeEvaluator/typeGuardNarrowing.d.ts +186 -0
  99. package/dist/analyzer/typeEvaluator/typeGuardNarrowing.js +1683 -0
  100. package/dist/analyzer/typeEvaluator/typeGuardNarrowing.js.map +1 -0
  101. package/dist/analyzer/typeEvaluator/typeVarHandling.d.ts +48 -0
  102. package/dist/analyzer/typeEvaluator/typeVarHandling.js +756 -0
  103. package/dist/analyzer/typeEvaluator/typeVarHandling.js.map +1 -0
  104. package/dist/analyzer/typeEvaluator.d.ts +3 -31
  105. package/dist/analyzer/typeEvaluator.js +5140 -13656
  106. package/dist/analyzer/typeEvaluator.js.map +1 -1
  107. package/dist/analyzer/typeEvaluatorTypes.d.ts +1 -1
  108. package/dist/analyzer/typeEvaluatorTypes.js +2 -3
  109. package/dist/analyzer/typeEvaluatorTypes.js.map +1 -1
  110. package/dist/analyzer/typeGuards.d.ts +3 -2
  111. package/dist/analyzer/typeGuards.js +236 -1375
  112. package/dist/analyzer/typeGuards.js.map +1 -1
  113. package/dist/analyzer/typeUtils.js +9 -2
  114. package/dist/analyzer/typeUtils.js.map +1 -1
  115. package/dist/analyzer/typedDicts.js +111 -0
  116. package/dist/analyzer/typedDicts.js.map +1 -1
  117. package/dist/analyzer/types.d.ts +14 -0
  118. package/dist/analyzer/types.js +215 -10
  119. package/dist/analyzer/types.js.map +1 -1
  120. package/dist/browser/browserConnection.d.ts +12 -0
  121. package/dist/browser/browserConnection.js +23 -0
  122. package/dist/browser/browserConnection.js.map +1 -0
  123. package/dist/browser/browserFileSystem.d.ts +73 -0
  124. package/dist/browser/browserFileSystem.js +578 -0
  125. package/dist/browser/browserFileSystem.js.map +1 -0
  126. package/dist/browser/browserHost.d.ts +4 -0
  127. package/dist/browser/browserHost.js +10 -0
  128. package/dist/browser/browserHost.js.map +1 -0
  129. package/dist/browser/browserProcess.d.ts +1 -0
  130. package/dist/browser/browserProcess.js +17 -0
  131. package/dist/browser/browserProcess.js.map +1 -0
  132. package/dist/browser/browserServer.d.ts +8 -0
  133. package/dist/browser/browserServer.js +32 -0
  134. package/dist/browser/browserServer.js.map +1 -0
  135. package/dist/browser/cancellation/common.d.ts +10 -0
  136. package/dist/browser/cancellation/common.js +114 -0
  137. package/dist/browser/cancellation/common.js.map +1 -0
  138. package/dist/browser/cancellation/objectURLReceiverStrategy.d.ts +5 -0
  139. package/dist/browser/cancellation/objectURLReceiverStrategy.js +37 -0
  140. package/dist/browser/cancellation/objectURLReceiverStrategy.js.map +1 -0
  141. package/dist/browser/cancellation/objectURLSenderStrategy.d.ts +7 -0
  142. package/dist/browser/cancellation/objectURLSenderStrategy.js +32 -0
  143. package/dist/browser/cancellation/objectURLSenderStrategy.js.map +1 -0
  144. package/dist/browser/worker.d.ts +1 -0
  145. package/dist/browser/worker.js +33 -0
  146. package/dist/browser/worker.js.map +1 -0
  147. package/dist/common/processUtils.d.ts +6 -0
  148. package/dist/common/processUtils.js +56 -1
  149. package/dist/common/processUtils.js.map +1 -1
  150. package/dist/common/textEditTracker.js +1 -1
  151. package/dist/common/textEditTracker.js.map +1 -1
  152. package/dist/common/windowsProcessTree.d.ts +2 -0
  153. package/dist/common/windowsProcessTree.js +23 -0
  154. package/dist/common/windowsProcessTree.js.map +1 -1
  155. package/dist/common/windowsProcessTree.json +23 -3
  156. package/dist/languageServerBase.d.ts +3 -0
  157. package/dist/languageServerBase.js +44 -12
  158. package/dist/languageServerBase.js.map +1 -1
  159. package/dist/languageService/callHierarchyProvider.js +6 -6
  160. package/dist/languageService/callHierarchyProvider.js.map +1 -1
  161. package/dist/languageService/referencesProvider.js +3 -3
  162. package/dist/languageService/referencesProvider.js.map +1 -1
  163. package/dist/languageService/renameProvider.js +1 -1
  164. package/dist/languageService/renameProvider.js.map +1 -1
  165. package/dist/languageService/symbolIndexer.js +2 -2
  166. package/dist/languageService/symbolIndexer.js.map +1 -1
  167. package/dist/localization/localize.d.ts +2 -0
  168. package/dist/localization/localize.js +2 -0
  169. package/dist/localization/localize.js.map +1 -1
  170. package/dist/localization/package.nls.en-us.json +8 -0
  171. package/dist/parser/parseTreeTokenReuse.d.ts +34 -0
  172. package/dist/parser/parseTreeTokenReuse.js +897 -0
  173. package/dist/parser/parseTreeTokenReuse.js.map +1 -0
  174. package/dist/parser/tokenizer.d.ts +10 -1
  175. package/dist/parser/tokenizer.js +184 -130
  176. package/dist/parser/tokenizer.js.map +1 -1
  177. package/dist/pyrightServer.d.ts +34 -0
  178. package/dist/pyrightServer.js +239 -0
  179. package/dist/pyrightServer.js.map +1 -0
  180. package/dist/server.d.ts +3 -23
  181. package/dist/server.js +12 -232
  182. package/dist/server.js.map +1 -1
  183. package/dist/tests/benchmarks/typeInferenceComparison.d.ts +1 -0
  184. package/dist/tests/benchmarks/typeInferenceComparison.js +1235 -0
  185. package/dist/tests/benchmarks/typeInferenceComparison.js.map +1 -0
  186. package/dist/tests/benchmarks/typeInferencePyrightEntry.d.ts +1 -0
  187. package/dist/tests/benchmarks/typeInferencePyrightEntry.js +53 -0
  188. package/dist/tests/benchmarks/typeInferencePyrightEntry.js.map +1 -0
  189. package/dist/tests/browserCancellation.test.d.ts +1 -0
  190. package/dist/tests/browserCancellation.test.js +63 -0
  191. package/dist/tests/browserCancellation.test.js.map +1 -0
  192. package/dist/tests/browserServer.test.d.ts +1 -0
  193. package/dist/tests/browserServer.test.js +177 -0
  194. package/dist/tests/browserServer.test.js.map +1 -0
  195. package/dist/tests/checker.test.js +8 -0
  196. package/dist/tests/checker.test.js.map +1 -1
  197. package/dist/tests/constraintSolver.test.js +45 -0
  198. package/dist/tests/constraintSolver.test.js.map +1 -1
  199. package/dist/tests/fourSlashParser.test.js +35 -0
  200. package/dist/tests/fourSlashParser.test.js.map +1 -1
  201. package/dist/tests/harness/fourslash/fourSlashParser.js +14 -4
  202. package/dist/tests/harness/fourslash/fourSlashParser.js.map +1 -1
  203. package/dist/tests/languageServer.test.js +15 -0
  204. package/dist/tests/languageServer.test.js.map +1 -1
  205. package/dist/tests/lsp/languageServer.js +6 -6
  206. package/dist/tests/lsp/languageServer.js.map +1 -1
  207. package/dist/tests/mypyPrimerAnalysis.test.js +148 -1
  208. package/dist/tests/mypyPrimerAnalysis.test.js.map +1 -1
  209. package/dist/tests/typeCacheUtils.test.js +46 -0
  210. package/dist/tests/typeCacheUtils.test.js.map +1 -1
  211. package/dist/tests/typeEvaluator1.test.js +78 -1
  212. package/dist/tests/typeEvaluator1.test.js.map +1 -1
  213. package/dist/tests/typeEvaluator2.test.js +54 -1
  214. package/dist/tests/typeEvaluator2.test.js.map +1 -1
  215. package/dist/tests/typeEvaluator3.test.js +4 -0
  216. package/dist/tests/typeEvaluator3.test.js.map +1 -1
  217. package/dist/tests/typeEvaluator4.test.js +42 -3
  218. package/dist/tests/typeEvaluator4.test.js.map +1 -1
  219. package/dist/tests/typeEvaluator6.test.js +35 -0
  220. package/dist/tests/typeEvaluator6.test.js.map +1 -1
  221. package/dist/tests/typeEvaluator7.test.js +414 -0
  222. package/dist/tests/typeEvaluator7.test.js.map +1 -1
  223. package/dist/tests/typeEvaluator8.test.js +1291 -1
  224. package/dist/tests/typeEvaluator8.test.js.map +1 -1
  225. package/dist/tests/typeUtils.test.js +28 -0
  226. package/dist/tests/typeUtils.test.js.map +1 -1
  227. package/dist/tests/types.test.js +55 -0
  228. package/dist/tests/types.test.js.map +1 -1
  229. package/dist/typeServer/programWrapper.js +8 -2
  230. package/dist/typeServer/programWrapper.js.map +1 -1
  231. package/dist/typeServer/server.d.ts +12 -2
  232. package/dist/typeServer/server.js +97 -0
  233. package/dist/typeServer/server.js.map +1 -1
  234. package/dist/typeServer/stubGenerator.js +136 -3
  235. package/dist/typeServer/stubGenerator.js.map +1 -1
  236. package/dist/typeServer/typeServerConversionUtils.js +13 -0
  237. package/dist/typeServer/typeServerConversionUtils.js.map +1 -1
  238. package/package.json +3 -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