@zzzen/pyright-internal 1.2.0-dev.20230416 → 1.2.0-dev.20230430

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 (224) hide show
  1. package/dist/analyzer/analyzerFileInfo.d.ts +4 -1
  2. package/dist/analyzer/analyzerFileInfo.js.map +1 -1
  3. package/dist/analyzer/backgroundAnalysisProgram.d.ts +4 -4
  4. package/dist/analyzer/backgroundAnalysisProgram.js +10 -7
  5. package/dist/analyzer/backgroundAnalysisProgram.js.map +1 -1
  6. package/dist/analyzer/binder.js +13 -10
  7. package/dist/analyzer/binder.js.map +1 -1
  8. package/dist/analyzer/checker.js +6 -3
  9. package/dist/analyzer/checker.js.map +1 -1
  10. package/dist/analyzer/codeFlowEngine.js +17 -0
  11. package/dist/analyzer/codeFlowEngine.js.map +1 -1
  12. package/dist/analyzer/commentUtils.d.ts +1 -1
  13. package/dist/analyzer/commentUtils.js +18 -3
  14. package/dist/analyzer/commentUtils.js.map +1 -1
  15. package/dist/analyzer/dataClasses.js +5 -2
  16. package/dist/analyzer/dataClasses.js.map +1 -1
  17. package/dist/analyzer/declarationUtils.d.ts +6 -1
  18. package/dist/analyzer/declarationUtils.js +10 -8
  19. package/dist/analyzer/declarationUtils.js.map +1 -1
  20. package/dist/analyzer/docStringConversion.js +2 -3
  21. package/dist/analyzer/docStringConversion.js.map +1 -1
  22. package/dist/analyzer/importResolver.d.ts +15 -15
  23. package/dist/analyzer/importResolver.js +477 -477
  24. package/dist/analyzer/importResolver.js.map +1 -1
  25. package/dist/analyzer/namedTuples.d.ts +1 -1
  26. package/dist/analyzer/namedTuples.js +29 -41
  27. package/dist/analyzer/namedTuples.js.map +1 -1
  28. package/dist/analyzer/packageTypeReport.d.ts +2 -1
  29. package/dist/analyzer/packageTypeReport.js +2 -1
  30. package/dist/analyzer/packageTypeReport.js.map +1 -1
  31. package/dist/analyzer/packageTypeVerifier.d.ts +1 -1
  32. package/dist/analyzer/packageTypeVerifier.js +27 -13
  33. package/dist/analyzer/packageTypeVerifier.js.map +1 -1
  34. package/dist/analyzer/patternMatching.d.ts +6 -2
  35. package/dist/analyzer/patternMatching.js +108 -2
  36. package/dist/analyzer/patternMatching.js.map +1 -1
  37. package/dist/analyzer/program.d.ts +34 -44
  38. package/dist/analyzer/program.js +1382 -1529
  39. package/dist/analyzer/program.js.map +1 -1
  40. package/dist/analyzer/protocols.js +1 -1
  41. package/dist/analyzer/protocols.js.map +1 -1
  42. package/dist/analyzer/service.d.ts +14 -27
  43. package/dist/analyzer/service.js +81 -114
  44. package/dist/analyzer/service.js.map +1 -1
  45. package/dist/analyzer/sourceFile.d.ts +7 -21
  46. package/dist/analyzer/sourceFile.js +58 -130
  47. package/dist/analyzer/sourceFile.js.map +1 -1
  48. package/dist/analyzer/sourceFileInfoUtils.d.ts +7 -1
  49. package/dist/analyzer/sourceFileInfoUtils.js.map +1 -1
  50. package/dist/analyzer/typeEvaluator.js +398 -334
  51. package/dist/analyzer/typeEvaluator.js.map +1 -1
  52. package/dist/analyzer/typeEvaluatorTypes.d.ts +6 -3
  53. package/dist/analyzer/typeEvaluatorTypes.js +0 -2
  54. package/dist/analyzer/typeEvaluatorTypes.js.map +1 -1
  55. package/dist/analyzer/typeGuards.d.ts +2 -0
  56. package/dist/analyzer/typeGuards.js +175 -176
  57. package/dist/analyzer/typeGuards.js.map +1 -1
  58. package/dist/analyzer/typeStubWriter.js.map +1 -1
  59. package/dist/analyzer/typeUtils.d.ts +1 -0
  60. package/dist/analyzer/typeUtils.js +44 -17
  61. package/dist/analyzer/typeUtils.js.map +1 -1
  62. package/dist/analyzer/types.js +10 -2
  63. package/dist/analyzer/types.js.map +1 -1
  64. package/dist/backgroundAnalysisBase.d.ts +10 -11
  65. package/dist/backgroundAnalysisBase.js +87 -87
  66. package/dist/backgroundAnalysisBase.js.map +1 -1
  67. package/dist/commands/dumpFileDebugInfoCommand.js +8 -8
  68. package/dist/commands/dumpFileDebugInfoCommand.js.map +1 -1
  69. package/dist/commands/quickActionCommand.js +4 -1
  70. package/dist/commands/quickActionCommand.js.map +1 -1
  71. package/dist/common/cancellationUtils.d.ts +1 -1
  72. package/dist/common/cancellationUtils.js +9 -9
  73. package/dist/common/cancellationUtils.js.map +1 -1
  74. package/dist/common/commandLineOptions.d.ts +1 -2
  75. package/dist/common/commandLineOptions.js.map +1 -1
  76. package/dist/common/configOptions.d.ts +2 -2
  77. package/dist/common/configOptions.js.map +1 -1
  78. package/dist/common/console.d.ts +5 -9
  79. package/dist/common/console.js +46 -33
  80. package/dist/common/console.js.map +1 -1
  81. package/dist/common/deferred.js +10 -10
  82. package/dist/common/deferred.js.map +1 -1
  83. package/dist/common/extensibility.d.ts +33 -9
  84. package/dist/common/extensibility.js +1 -1
  85. package/dist/common/extensibility.js.map +1 -1
  86. package/dist/common/fileBasedCancellationUtils.js +5 -5
  87. package/dist/common/fileBasedCancellationUtils.js.map +1 -1
  88. package/dist/common/fileSystem.d.ts +12 -10
  89. package/dist/common/fileSystem.js.map +1 -1
  90. package/dist/common/fullAccessHost.d.ts +3 -3
  91. package/dist/common/fullAccessHost.js +6 -6
  92. package/dist/common/fullAccessHost.js.map +1 -1
  93. package/dist/common/pathUtils.d.ts +2 -2
  94. package/dist/common/pathUtils.js.map +1 -1
  95. package/dist/common/realFileSystem.js +12 -7
  96. package/dist/common/realFileSystem.js.map +1 -1
  97. package/dist/common/uriParser.d.ts +2 -2
  98. package/dist/common/uriParser.js +3 -3
  99. package/dist/common/uriParser.js.map +1 -1
  100. package/dist/common/workspaceEditUtils.js +7 -4
  101. package/dist/common/workspaceEditUtils.js.map +1 -1
  102. package/dist/languageServerBase.d.ts +32 -30
  103. package/dist/languageServerBase.js +276 -337
  104. package/dist/languageServerBase.js.map +1 -1
  105. package/dist/languageService/analyzerServiceExecutor.js +0 -1
  106. package/dist/languageService/analyzerServiceExecutor.js.map +1 -1
  107. package/dist/languageService/callHierarchyProvider.d.ts +16 -12
  108. package/dist/languageService/callHierarchyProvider.js +138 -41
  109. package/dist/languageService/callHierarchyProvider.js.map +1 -1
  110. package/dist/languageService/completionProvider.js +119 -10
  111. package/dist/languageService/completionProvider.js.map +1 -1
  112. package/dist/languageService/definitionProvider.d.ts +23 -9
  113. package/dist/languageService/definitionProvider.js +116 -91
  114. package/dist/languageService/definitionProvider.js.map +1 -1
  115. package/dist/languageService/documentHighlightProvider.d.ts +8 -3
  116. package/dist/languageService/documentHighlightProvider.js +17 -6
  117. package/dist/languageService/documentHighlightProvider.js.map +1 -1
  118. package/dist/languageService/documentSymbolCollector.d.ts +2 -2
  119. package/dist/languageService/documentSymbolCollector.js +4 -3
  120. package/dist/languageService/documentSymbolCollector.js.map +1 -1
  121. package/dist/languageService/documentSymbolProvider.js +5 -3
  122. package/dist/languageService/documentSymbolProvider.js.map +1 -1
  123. package/dist/languageService/hoverProvider.d.ts +28 -28
  124. package/dist/languageService/hoverProvider.js +142 -120
  125. package/dist/languageService/hoverProvider.js.map +1 -1
  126. package/dist/languageService/importAdder.d.ts +13 -2
  127. package/dist/languageService/importAdder.js +73 -26
  128. package/dist/languageService/importAdder.js.map +1 -1
  129. package/dist/languageService/indentationUtils.js +6 -1
  130. package/dist/languageService/indentationUtils.js.map +1 -1
  131. package/dist/languageService/insertionPointUtils.js +3 -3
  132. package/dist/languageService/insertionPointUtils.js.map +1 -1
  133. package/dist/languageService/navigationUtils.d.ts +6 -0
  134. package/dist/languageService/navigationUtils.js +28 -0
  135. package/dist/languageService/navigationUtils.js.map +1 -0
  136. package/dist/languageService/quickActions.d.ts +2 -2
  137. package/dist/languageService/quickActions.js +12 -1
  138. package/dist/languageService/quickActions.js.map +1 -1
  139. package/dist/languageService/referencesProvider.d.ts +8 -2
  140. package/dist/languageService/referencesProvider.js +81 -3
  141. package/dist/languageService/referencesProvider.js.map +1 -1
  142. package/dist/languageService/renameModuleProvider.d.ts +13 -13
  143. package/dist/languageService/renameModuleProvider.js +43 -43
  144. package/dist/languageService/renameModuleProvider.js.map +1 -1
  145. package/dist/languageService/signatureHelpProvider.d.ts +19 -23
  146. package/dist/languageService/signatureHelpProvider.js +111 -18
  147. package/dist/languageService/signatureHelpProvider.js.map +1 -1
  148. package/dist/localization/localize.d.ts +2 -0
  149. package/dist/localization/localize.js +2 -0
  150. package/dist/localization/localize.js.map +1 -1
  151. package/dist/localization/package.nls.en-us.json +2 -0
  152. package/dist/parser/characterStream.d.ts +3 -3
  153. package/dist/parser/characterStream.js +12 -12
  154. package/dist/parser/characterStream.js.map +1 -1
  155. package/dist/parser/parser.d.ts +1 -1
  156. package/dist/parser/parser.js.map +1 -1
  157. package/dist/pyright.js +57 -54
  158. package/dist/pyright.js.map +1 -1
  159. package/dist/pyrightFileSystem.d.ts +1 -1
  160. package/dist/pyrightFileSystem.js +21 -21
  161. package/dist/pyrightFileSystem.js.map +1 -1
  162. package/dist/readonlyAugmentedFileSystem.d.ts +6 -6
  163. package/dist/readonlyAugmentedFileSystem.js +28 -28
  164. package/dist/readonlyAugmentedFileSystem.js.map +1 -1
  165. package/dist/server.js +6 -6
  166. package/dist/server.js.map +1 -1
  167. package/dist/tests/chainedSourceFiles.test.js +1 -1
  168. package/dist/tests/chainedSourceFiles.test.js.map +1 -1
  169. package/dist/tests/checker.test.js +2 -2
  170. package/dist/tests/completions.test.js +173 -0
  171. package/dist/tests/completions.test.js.map +1 -1
  172. package/dist/tests/docStringConversion.test.js +11 -1
  173. package/dist/tests/docStringConversion.test.js.map +1 -1
  174. package/dist/tests/fourslash/completions.override2.fourslash.js +15 -0
  175. package/dist/tests/fourslash/completions.override2.fourslash.js.map +1 -1
  176. package/dist/tests/fourslash/fourslash.d.ts +11 -1
  177. package/dist/tests/fourslash/showcallhierarchy.incomingCalls.aliasedFunction.fourslash.js +5 -6
  178. package/dist/tests/fourslash/showcallhierarchy.incomingCalls.aliasedFunction.fourslash.js.map +1 -1
  179. package/dist/tests/fourslash/showcallhierarchy.incomingCalls.aliasedVariable.fourslash.js +1 -4
  180. package/dist/tests/fourslash/showcallhierarchy.incomingCalls.aliasedVariable.fourslash.js.map +1 -1
  181. package/dist/tests/fourslash/showcallhierarchy.incomingCalls.function.fourslash.js +10 -9
  182. package/dist/tests/fourslash/showcallhierarchy.incomingCalls.function.fourslash.js.map +1 -1
  183. package/dist/tests/fourslash/showcallhierarchy.outgoingCalls.aliasedFunction1.fourslash.d.ts +1 -0
  184. package/dist/tests/fourslash/showcallhierarchy.outgoingCalls.aliasedFunction1.fourslash.js +54 -0
  185. package/dist/tests/fourslash/showcallhierarchy.outgoingCalls.aliasedFunction1.fourslash.js.map +1 -0
  186. package/dist/tests/fourslash/showcallhierarchy.outgoingCalls.aliasedFunction2.fourslash.d.ts +1 -0
  187. package/dist/tests/fourslash/showcallhierarchy.outgoingCalls.aliasedFunction2.fourslash.js +46 -0
  188. package/dist/tests/fourslash/showcallhierarchy.outgoingCalls.aliasedFunction2.fourslash.js.map +1 -0
  189. package/dist/tests/fourslash/showcallhierarchy.outgoingCalls.function.fourslash.d.ts +1 -0
  190. package/dist/tests/fourslash/showcallhierarchy.outgoingCalls.function.fourslash.js +39 -0
  191. package/dist/tests/fourslash/showcallhierarchy.outgoingCalls.function.fourslash.js.map +1 -0
  192. package/dist/tests/harness/fourslash/testLanguageService.d.ts +3 -3
  193. package/dist/tests/harness/fourslash/testLanguageService.js.map +1 -1
  194. package/dist/tests/harness/fourslash/testState.d.ts +14 -9
  195. package/dist/tests/harness/fourslash/testState.js +144 -102
  196. package/dist/tests/harness/fourslash/testState.js.map +1 -1
  197. package/dist/tests/harness/vfs/filesystem.d.ts +8 -8
  198. package/dist/tests/harness/vfs/filesystem.js +68 -68
  199. package/dist/tests/harness/vfs/filesystem.js.map +1 -1
  200. package/dist/tests/importAdder.test.js +69 -1
  201. package/dist/tests/importAdder.test.js.map +1 -1
  202. package/dist/tests/importStatementUtils.test.js +34 -0
  203. package/dist/tests/importStatementUtils.test.js.map +1 -1
  204. package/dist/tests/indentationUtils.ptvs.test.js +2 -2
  205. package/dist/tests/indentationUtils.ptvs.test.js.map +1 -1
  206. package/dist/tests/indentationUtils.test.js +18 -0
  207. package/dist/tests/indentationUtils.test.js.map +1 -1
  208. package/dist/tests/moveSymbol.updateReference.test.js +14 -0
  209. package/dist/tests/moveSymbol.updateReference.test.js.map +1 -1
  210. package/dist/tests/signatureHelp.test.js +5 -1
  211. package/dist/tests/signatureHelp.test.js.map +1 -1
  212. package/dist/tests/sourceFile.test.js +1 -1
  213. package/dist/tests/sourceFile.test.js.map +1 -1
  214. package/dist/tests/testStateUtils.js +1 -1
  215. package/dist/tests/testStateUtils.js.map +1 -1
  216. package/dist/tests/typeEvaluator1.test.js +8 -0
  217. package/dist/tests/typeEvaluator1.test.js.map +1 -1
  218. package/dist/tests/typeEvaluator2.test.js +12 -0
  219. package/dist/tests/typeEvaluator2.test.js.map +1 -1
  220. package/dist/tests/typeEvaluator3.test.js +7 -1
  221. package/dist/tests/typeEvaluator3.test.js.map +1 -1
  222. package/dist/tests/typeEvaluator4.test.js +5 -1
  223. package/dist/tests/typeEvaluator4.test.js.map +1 -1
  224. package/package.json +1 -1
@@ -48,7 +48,6 @@ const textRangeCollection_1 = require("../common/textRangeCollection");
48
48
  const timing_1 = require("../common/timing");
49
49
  const workspaceEditUtils_1 = require("../common/workspaceEditUtils");
50
50
  const autoImporter_1 = require("../languageService/autoImporter");
51
- const callHierarchyProvider_1 = require("../languageService/callHierarchyProvider");
52
51
  const completionProvider_1 = require("../languageService/completionProvider");
53
52
  const documentSymbolCollector_1 = require("../languageService/documentSymbolCollector");
54
53
  const importAdder_1 = require("../languageService/importAdder");
@@ -83,7 +82,7 @@ class Program {
83
82
  this._sourceFileList = [];
84
83
  this._sourceFileMap = new Map();
85
84
  this._parsedFileCount = 0;
86
- this._lookUpImport = (filePathOrModule) => {
85
+ this._lookUpImport = (filePathOrModule, options) => {
87
86
  var _a;
88
87
  let sourceFileInfo;
89
88
  if (typeof filePathOrModule === 'string') {
@@ -102,7 +101,7 @@ class Program {
102
101
  // See if the source file already exists in the program.
103
102
  sourceFileInfo = this.getSourceFileInfo(resolvedPath);
104
103
  if (!sourceFileInfo) {
105
- resolvedPath = (0, pathUtils_1.normalizePathCase)(this._fs, resolvedPath);
104
+ resolvedPath = (0, pathUtils_1.normalizePathCase)(this.fileSystem, resolvedPath);
106
105
  // Start tracking the source file.
107
106
  this.addTrackedFile(resolvedPath);
108
107
  sourceFileInfo = this.getSourceFileInfo(resolvedPath);
@@ -117,7 +116,7 @@ class Program {
117
116
  // Bind the file if it's not already bound. Don't count this time
118
117
  // against the type checker.
119
118
  timing_1.timingStats.typeCheckerTime.subtractFromTime(() => {
120
- this._bindFile(sourceFileInfo);
119
+ this._bindFile(sourceFileInfo, /* content */ undefined, options === null || options === void 0 ? void 0 : options.skipFileNeededCheck);
121
120
  });
122
121
  }
123
122
  const symbolTable = sourceFileInfo.sourceFile.getModuleSymbolTable();
@@ -148,17 +147,29 @@ class Program {
148
147
  this._id = Program._nextId;
149
148
  Program._nextId += 1;
150
149
  }
151
- dispose() {
152
- this._cacheManager.unregisterCacheOwner(this);
150
+ get id() {
151
+ return this._id;
152
+ }
153
+ get console() {
154
+ return this._console;
155
+ }
156
+ get rootPath() {
157
+ return this._configOptions.projectRoot;
153
158
  }
154
159
  get evaluator() {
155
160
  return this._evaluator;
156
161
  }
157
- get console() {
158
- return this._console;
162
+ get configOptions() {
163
+ return this._configOptions;
159
164
  }
160
- get id() {
161
- return this._id;
165
+ get importResolver() {
166
+ return this._importResolver;
167
+ }
168
+ get fileSystem() {
169
+ return this._importResolver.fileSystem;
170
+ }
171
+ dispose() {
172
+ this._cacheManager.unregisterCacheOwner(this);
162
173
  }
163
174
  setConfigOptions(configOptions) {
164
175
  this._configOptions = configOptions;
@@ -166,12 +177,6 @@ class Program {
166
177
  // Create a new evaluator with the updated config options.
167
178
  this._createNewEvaluator();
168
179
  }
169
- get rootPath() {
170
- return this._configOptions.projectRoot;
171
- }
172
- getConfigOptions() {
173
- return this._configOptions;
174
- }
175
180
  setImportResolver(importResolver) {
176
181
  this._importResolver = importResolver;
177
182
  // Create a new evaluator with the updated import resolver.
@@ -179,21 +184,18 @@ class Program {
179
184
  // older import resolver when resolving imports after parsing.
180
185
  this._createNewEvaluator();
181
186
  }
182
- getImportResolver() {
183
- return this._importResolver;
184
- }
185
187
  // Sets the list of tracked files that make up the program.
186
188
  setTrackedFiles(filePaths) {
187
189
  if (this._sourceFileList.length > 0) {
188
190
  // We need to determine which files to remove from the existing file list.
189
191
  const newFileMap = new Map();
190
192
  filePaths.forEach((path) => {
191
- newFileMap.set((0, pathUtils_1.normalizePathCase)(this._fs, path), path);
193
+ newFileMap.set((0, pathUtils_1.normalizePathCase)(this.fileSystem, path), path);
192
194
  });
193
195
  // Files that are not in the tracked file list are
194
196
  // marked as no longer tracked.
195
197
  this._sourceFileList.forEach((oldFile) => {
196
- const filePath = (0, pathUtils_1.normalizePathCase)(this._fs, oldFile.sourceFile.getFilePath());
198
+ const filePath = (0, pathUtils_1.normalizePathCase)(this.fileSystem, oldFile.sourceFile.getFilePath());
197
199
  if (!newFileMap.has(filePath)) {
198
200
  oldFile.isTracked = false;
199
201
  }
@@ -240,7 +242,7 @@ class Program {
240
242
  sourceFileInfo.isTracked = true;
241
243
  return sourceFileInfo.sourceFile;
242
244
  }
243
- const sourceFile = new sourceFile_1.SourceFile(this._fs, filePath, importName, isThirdPartyImport, isInPyTypedPackage, this._console, this._logTracker);
245
+ const sourceFile = new sourceFile_1.SourceFile(this.fileSystem, filePath, importName, isThirdPartyImport, isInPyTypedPackage, this._console, this._logTracker);
244
246
  sourceFileInfo = {
245
247
  sourceFile,
246
248
  isTracked: true,
@@ -262,7 +264,7 @@ class Program {
262
264
  let sourceFileInfo = this.getSourceFileInfo(filePath);
263
265
  if (!sourceFileInfo) {
264
266
  const importName = this._getImportNameForFile(filePath);
265
- const sourceFile = new sourceFile_1.SourceFile(this._fs, filePath, importName,
267
+ const sourceFile = new sourceFile_1.SourceFile(this.fileSystem, filePath, importName,
266
268
  /* isThirdPartyImport */ false,
267
269
  /* isInPyTypedPackage */ false, this._console, this._logTracker, options === null || options === void 0 ? void 0 : options.realFilePath, (_a = options === null || options === void 0 ? void 0 : options.ipythonMode) !== null && _a !== void 0 ? _a : sourceFile_1.IPythonMode.None);
268
270
  const chainedFilePath = options === null || options === void 0 ? void 0 : options.chainedFilePath;
@@ -310,7 +312,7 @@ class Program {
310
312
  if (sourceFileInfo) {
311
313
  sourceFileInfo.isOpenByClient = false;
312
314
  sourceFileInfo.isTracked = isTracked !== null && isTracked !== void 0 ? isTracked : sourceFileInfo.isTracked;
313
- sourceFileInfo.sourceFile.setClientVersion(null, []);
315
+ sourceFileInfo.sourceFile.setClientVersion(null, '');
314
316
  // There is no guarantee that content is saved before the file is closed.
315
317
  // We need to mark the file dirty so we can re-analyze next time.
316
318
  // This won't matter much for OpenFileOnly users, but it will matter for
@@ -405,7 +407,7 @@ class Program {
405
407
  return this._configOptions.functionSignatureDisplay;
406
408
  }
407
409
  containsSourceFileIn(folder) {
408
- const normalized = (0, pathUtils_1.normalizePathCase)(this._fs, folder);
410
+ const normalized = (0, pathUtils_1.normalizePathCase)(this.fileSystem, folder);
409
411
  return this._sourceFileList.some((i) => i.sourceFile.getFilePath().startsWith(normalized));
410
412
  }
411
413
  owns(filePath) {
@@ -428,8 +430,11 @@ class Program {
428
430
  var _a;
429
431
  return (_a = this.getBoundSourceFileInfo(filePath)) === null || _a === void 0 ? void 0 : _a.sourceFile;
430
432
  }
433
+ getSourceFileInfoList() {
434
+ return this._sourceFileList;
435
+ }
431
436
  getSourceFileInfo(filePath) {
432
- return this._sourceFileMap.get((0, pathUtils_1.normalizePathCase)(this._fs, filePath));
437
+ return this._sourceFileMap.get((0, pathUtils_1.normalizePathCase)(this.fileSystem, filePath));
433
438
  }
434
439
  getBoundSourceFileInfo(filePath, content, force) {
435
440
  const sourceFileInfo = this.getSourceFileInfo(filePath);
@@ -504,13 +509,20 @@ class Program {
504
509
  // _handleMemoryHighUsage will make sure we don't OOM and
505
510
  // at the end of this method, we will drop all trees and symbol tables
506
511
  // created due to indexing.
512
+ let count = 0;
507
513
  const initiallyParsedSet = new Set();
508
514
  for (const sourceFileInfo of this._sourceFileList) {
509
515
  if (!sourceFileInfo.sourceFile.isParseRequired()) {
510
516
  initiallyParsedSet.add(sourceFileInfo);
511
517
  }
518
+ if ((0, sourceFileInfoUtils_1.isUserCode)(sourceFileInfo) && !sourceFileInfo.sourceFile.isIndexingRequired()) {
519
+ count++;
520
+ }
521
+ }
522
+ if (count >= exports.MaxWorkspaceIndexFileCount) {
523
+ // Already processed max files.
524
+ return 0;
512
525
  }
513
- let count = 0;
514
526
  for (const sourceFileInfo of this._sourceFileList) {
515
527
  if (!(0, sourceFileInfoUtils_1.isUserCode)(sourceFileInfo) || !sourceFileInfo.sourceFile.isIndexingRequired()) {
516
528
  continue;
@@ -540,6 +552,25 @@ class Program {
540
552
  }
541
553
  }
542
554
  }
555
+ // This will allow the callback to execute a type evaluator with an associated
556
+ // cancellation token and provide a read-only program.
557
+ run(callback, token) {
558
+ var _a;
559
+ const evaluator = (_a = this._evaluator) !== null && _a !== void 0 ? _a : this._createNewEvaluator();
560
+ return evaluator.runWithCancellationToken(token, () => callback(this));
561
+ }
562
+ getSourceMapper(filePath, token, mapCompiled, preferStubs) {
563
+ const sourceFileInfo = this.getSourceFileInfo(filePath);
564
+ const execEnv = this._configOptions.findExecEnvironment(filePath);
565
+ return this._createSourceMapper(execEnv, token, sourceFileInfo, mapCompiled, preferStubs);
566
+ }
567
+ getParseResults(filePath) {
568
+ var _a;
569
+ return (_a = this.getBoundSourceFile(filePath)) === null || _a === void 0 ? void 0 : _a.getParseResults();
570
+ }
571
+ handleMemoryHighUsage() {
572
+ this._handleMemoryHighUsage();
573
+ }
543
574
  // Prints a detailed list of files that have been checked and the times associated
544
575
  // with each of them, sorted greatest to least.
545
576
  printDetailedAnalysisTimes() {
@@ -620,7 +651,7 @@ class Program {
620
651
  }
621
652
  const typeStubDir = (0, pathUtils_1.getDirectoryPath)(typeStubPath);
622
653
  try {
623
- (0, pathUtils_1.makeDirectories)(this._fs, typeStubDir, stubPath);
654
+ (0, pathUtils_1.makeDirectories)(this.fileSystem, typeStubDir, stubPath);
624
655
  }
625
656
  catch (e) {
626
657
  const errMsg = `Could not create directory for '${typeStubDir}'`;
@@ -647,833 +678,269 @@ class Program {
647
678
  const evaluator = this._evaluator || this._createNewEvaluator();
648
679
  return evaluator.printType(type, options);
649
680
  }
650
- static _getPrintTypeFlags(configOptions) {
651
- let flags = 0 /* None */;
652
- if (configOptions.diagnosticRuleSet.printUnknownAsAny) {
653
- flags |= 1 /* PrintUnknownWithAny */;
654
- }
655
- if (configOptions.diagnosticRuleSet.omitConditionalConstraint) {
656
- flags |= 64 /* OmitConditionalConstraint */;
657
- }
658
- if (configOptions.diagnosticRuleSet.omitTypeArgsIfUnknown) {
659
- flags |= 2 /* OmitTypeArgumentsIfUnknown */;
660
- }
661
- if (configOptions.diagnosticRuleSet.omitUnannotatedParamType) {
662
- flags |= 4 /* OmitUnannotatedParamType */;
681
+ getTextOnRange(filePath, range, token) {
682
+ const sourceFileInfo = this.getSourceFileInfo(filePath);
683
+ if (!sourceFileInfo) {
684
+ return undefined;
663
685
  }
664
- if (configOptions.diagnosticRuleSet.pep604Printing) {
665
- flags |= 8 /* PEP604 */;
686
+ const sourceFile = sourceFileInfo.sourceFile;
687
+ const fileContents = sourceFile.getOpenFileContents();
688
+ if (fileContents === undefined) {
689
+ // this only works with opened file
690
+ return undefined;
666
691
  }
667
- return flags;
668
- }
669
- get _fs() {
670
- return this._importResolver.fileSystem;
671
- }
672
- _getImportNameForFile(filePath) {
673
- // We allow illegal module names (e.g. names that include "-" in them)
674
- // because we want a unique name for each module even if it cannot be
675
- // imported through an "import" statement. It's important to have a
676
- // unique name in case two modules declare types with the same local
677
- // name. The type checker uses the fully-qualified (unique) module name
678
- // to differentiate between such types.
679
- const moduleNameAndType = this._importResolver.getModuleNameForImport(filePath, this._configOptions.getDefaultExecEnvironment(),
680
- /* allowIllegalModuleName */ true);
681
- return moduleNameAndType.moduleName;
692
+ return this._runEvaluatorWithCancellationToken(token, () => {
693
+ this._parseFile(sourceFileInfo);
694
+ const parseTree = sourceFile.getParseResults();
695
+ const textRange = (0, positionUtils_1.convertRangeToTextRange)(range, parseTree.tokenizerOutput.lines);
696
+ if (!textRange) {
697
+ return undefined;
698
+ }
699
+ return fileContents.substr(textRange.start, textRange.length);
700
+ });
682
701
  }
683
- // A "shadowed" file is a python source file that has been added to the program because
684
- // it "shadows" a type stub file for purposes of finding doc strings and definitions.
685
- // We need to track the relationship so if the original type stub is removed from the
686
- // program, we can remove the corresponding shadowed file and any files it imports.
687
- _addShadowedFile(stubFile, shadowImplPath) {
688
- let shadowFileInfo = this.getSourceFileInfo(shadowImplPath);
689
- if (!shadowFileInfo) {
690
- shadowFileInfo = this.addInterimFile(shadowImplPath);
691
- }
692
- if (!shadowFileInfo.shadows.includes(stubFile)) {
693
- shadowFileInfo.shadows.push(stubFile);
702
+ getAutoImports(filePath, range, similarityLimit, nameMap, options, token) {
703
+ const sourceFileInfo = this.getSourceFileInfo(filePath);
704
+ if (!sourceFileInfo) {
705
+ return [];
694
706
  }
695
- if (!stubFile.shadowedBy.includes(shadowFileInfo)) {
696
- stubFile.shadowedBy.push(shadowFileInfo);
707
+ const sourceFile = sourceFileInfo.sourceFile;
708
+ const fileContents = sourceFile.getOpenFileContents();
709
+ if (fileContents === undefined) {
710
+ // this only works with opened file
711
+ return [];
697
712
  }
698
- return shadowFileInfo.sourceFile;
699
- }
700
- _createInterimFileInfo(filePath) {
701
- const importName = this._getImportNameForFile(filePath);
702
- const sourceFile = new sourceFile_1.SourceFile(this._fs, filePath, importName,
703
- /* isThirdPartyImport */ false,
704
- /* isInPyTypedPackage */ false, this._console, this._logTracker);
705
- const sourceFileInfo = {
706
- sourceFile,
707
- isTracked: false,
708
- isOpenByClient: false,
709
- isTypeshedFile: false,
710
- isThirdPartyImport: false,
711
- isThirdPartyPyTypedPresent: false,
712
- diagnosticsVersion: undefined,
713
- imports: [],
714
- importedBy: [],
715
- shadows: [],
716
- shadowedBy: [],
717
- };
718
- return sourceFileInfo;
713
+ return this._runEvaluatorWithCancellationToken(token, () => {
714
+ var _a;
715
+ this._bindFile(sourceFileInfo);
716
+ const parseTree = sourceFile.getParseResults();
717
+ const textRange = (0, positionUtils_1.convertRangeToTextRange)(range, parseTree.tokenizerOutput.lines);
718
+ if (!textRange) {
719
+ return [];
720
+ }
721
+ const currentNode = (0, parseTreeUtils_1.findNodeByOffset)(parseTree.parseTree, textRange.start);
722
+ if (!currentNode) {
723
+ return [];
724
+ }
725
+ const writtenWord = fileContents.substr(textRange.start, textRange.length);
726
+ const map = this._buildModuleSymbolsMap(sourceFileInfo, options.libraryMap,
727
+ /* includeSymbolsFromIndices */ true, token);
728
+ options.patternMatcher =
729
+ (_a = options.patternMatcher) !== null && _a !== void 0 ? _a : ((p, t) => (0, stringUtils_1.computeCompletionSimilarity)(p, t) > similarityLimit);
730
+ const autoImporter = new autoImporter_1.AutoImporter(this._configOptions.findExecEnvironment(filePath), this._importResolver, parseTree, range.start, new completionProvider_1.CompletionMap(), map, options);
731
+ // Filter out any name that is already defined in the current scope.
732
+ const results = [];
733
+ const currentScope = (0, scopeUtils_1.getScopeForNode)(currentNode);
734
+ if (currentScope) {
735
+ const info = nameMap === null || nameMap === void 0 ? void 0 : nameMap.get(writtenWord);
736
+ if (info) {
737
+ // No scope filter is needed since we only do exact match.
738
+ (0, collectionUtils_1.appendArray)(results, autoImporter.getAutoImportCandidatesForAbbr(writtenWord, info, token));
739
+ }
740
+ results.push(...autoImporter
741
+ .getAutoImportCandidates(writtenWord, similarityLimit, /* abbrFromUsers */ undefined, token)
742
+ .filter((r) => !currentScope.lookUpSymbolRecursive(r.name)));
743
+ }
744
+ return results;
745
+ });
719
746
  }
720
- _createNewEvaluator() {
721
- if (this._evaluator) {
722
- // We shouldn't need to call this, but there appears to be a bug
723
- // in the v8 garbage collector where it's unable to resolve orphaned
724
- // objects without us giving it some assistance.
725
- this._evaluator.disposeEvaluator();
726
- }
727
- this._evaluator = (0, typeEvaluatorWithTracker_1.createTypeEvaluatorWithTracker)(this._lookUpImport, {
728
- printTypeFlags: Program._getPrintTypeFlags(this._configOptions),
729
- logCalls: this._configOptions.logTypeEvaluationTime,
730
- minimumLoggingThreshold: this._configOptions.typeEvaluationTimeThreshold,
731
- evaluateUnknownImportsAsAny: !!this._configOptions.evaluateUnknownImportsAsAny,
732
- verifyTypeCacheEvaluatorFlags: !!this._configOptions.internalTestMode,
733
- }, this._logTracker, this._configOptions.logTypeEvaluationTime
734
- ? (0, tracePrinter_1.createTracePrinter)(this._importResolver.getImportRoots(this._configOptions.findExecEnvironment(this._configOptions.projectRoot)))
735
- : undefined);
736
- return this._evaluator;
747
+ getDiagnostics(options) {
748
+ const fileDiagnostics = this._removeUnneededFiles();
749
+ this._sourceFileList.forEach((sourceFileInfo) => {
750
+ if (this._shouldCheckFile(sourceFileInfo)) {
751
+ const diagnostics = sourceFileInfo.sourceFile.getDiagnostics(options, sourceFileInfo.diagnosticsVersion);
752
+ if (diagnostics !== undefined) {
753
+ fileDiagnostics.push({
754
+ filePath: sourceFileInfo.sourceFile.getFilePath(),
755
+ version: sourceFileInfo.sourceFile.getClientVersion(),
756
+ diagnostics,
757
+ });
758
+ // Update the cached diagnosticsVersion so we can determine
759
+ // whether there are any updates next time we call getDiagnostics.
760
+ sourceFileInfo.diagnosticsVersion = sourceFileInfo.sourceFile.getDiagnosticVersion();
761
+ }
762
+ }
763
+ else if (!sourceFileInfo.isOpenByClient &&
764
+ options.checkOnlyOpenFiles &&
765
+ sourceFileInfo.diagnosticsVersion !== undefined) {
766
+ // This condition occurs when the user switches from workspace to
767
+ // "open files only" mode. Clear all diagnostics for this file.
768
+ fileDiagnostics.push({
769
+ filePath: sourceFileInfo.sourceFile.getFilePath(),
770
+ version: sourceFileInfo.sourceFile.getClientVersion(),
771
+ diagnostics: [],
772
+ });
773
+ sourceFileInfo.diagnosticsVersion = undefined;
774
+ }
775
+ });
776
+ return fileDiagnostics;
737
777
  }
738
- _parseFile(fileToParse, content, force) {
739
- if (!force && (!this._isFileNeeded(fileToParse) || !fileToParse.sourceFile.isParseRequired())) {
740
- return;
741
- }
742
- // SourceFile.parse should only be called here in the program, as calling it
743
- // elsewhere could break the entire dependency graph maintained by the program.
744
- // Other parts of the program should use _parseFile to create ParseResults from
745
- // the sourceFile. For standalone parseResults, use parseFile or the Parser directly.
746
- if (fileToParse.sourceFile.parse(this._configOptions, this._importResolver, content)) {
747
- this._parsedFileCount++;
748
- this._updateSourceFileImports(fileToParse, this._configOptions);
778
+ getDiagnosticsForRange(filePath, range) {
779
+ const sourceFile = this.getSourceFile(filePath);
780
+ if (!sourceFile) {
781
+ return [];
749
782
  }
750
- if (fileToParse.sourceFile.isFileDeleted()) {
751
- fileToParse.isTracked = false;
752
- // Mark any files that depend on this file as dirty
753
- // also. This will retrigger analysis of these other files.
754
- const markDirtySet = new Set();
755
- this._markFileDirtyRecursive(fileToParse, markDirtySet);
756
- // Invalidate the import resolver's cache as well.
757
- this._importResolver.invalidateCache();
783
+ const unfilteredDiagnostics = sourceFile.getDiagnostics(this._configOptions);
784
+ if (!unfilteredDiagnostics) {
785
+ return [];
758
786
  }
787
+ return unfilteredDiagnostics.filter((diag) => {
788
+ return (0, textRange_1.doRangesIntersect)(diag.range, range);
789
+ });
759
790
  }
760
- _getImplicitImports(file) {
761
- var _a, _b;
762
- // If file is not parsed, then chainedSourceFile, ipythonDisplayImport,
763
- // builtinsImport might not exist or incorrect.
764
- // They will be added when _parseFile is called and _updateSourceFileImports ran.
765
- if (file.builtinsImport === file) {
766
- return undefined;
767
- }
768
- const tryReturn = (input) => {
769
- if (!input || input.sourceFile.isFileDeleted()) {
791
+ getFileIndex(filePath, options, token) {
792
+ if (options.indexingForAutoImportMode) {
793
+ // Memory optimization. We only want to hold onto symbols
794
+ // usable outside when importSymbolsOnly is on.
795
+ const name = (0, pathUtils_1.stripFileExtension)((0, pathUtils_1.getFileName)(filePath));
796
+ if ((0, symbolNameUtils_1.isPrivateOrProtectedName)(name)) {
770
797
  return undefined;
771
798
  }
772
- return input;
773
- };
774
- return (_b = (_a = tryReturn(file.chainedSourceFile)) !== null && _a !== void 0 ? _a : tryReturn(file.ipythonDisplayImport)) !== null && _b !== void 0 ? _b : file.builtinsImport;
775
- }
776
- _bindImplicitImports(fileToAnalyze) {
777
- // Get all of the potential imports for this file.
778
- const implicitImports = [];
779
- const implicitSet = new Set();
780
- let nextImplicitImport = this._getImplicitImports(fileToAnalyze);
781
- while (nextImplicitImport) {
782
- const implicitPath = nextImplicitImport.sourceFile.getFilePath();
783
- if (implicitSet.has(implicitPath)) {
784
- // We've found a cycle. Break out of the loop.
785
- debug.fail(`Found a cycle in implicit imports files for ${implicitPath}`);
786
- }
787
- implicitSet.add(implicitPath);
788
- implicitImports.push(nextImplicitImport);
789
- this._parseFile(nextImplicitImport);
790
- nextImplicitImport = this._getImplicitImports(nextImplicitImport);
791
- }
792
- if (implicitImports.length === 0) {
793
- return;
794
- }
795
- // Go in reverse order (so top of chain is first).
796
- let implicitImport = implicitImports.pop();
797
- while (implicitImport) {
798
- // Bind this file, but don't recurse into its imports.
799
- this._bindFile(implicitImport, undefined, undefined, /* isImplicitImport */ true);
800
- implicitImport = implicitImports.pop();
801
- }
802
- }
803
- // Binds the specified file and all of its dependencies, recursively. If
804
- // it runs out of time, it returns true. If it completes, it returns false.
805
- _bindFile(fileToAnalyze, content, force, isImplicitImport) {
806
- var _a, _b;
807
- if (!force && (!this._isFileNeeded(fileToAnalyze) || !fileToAnalyze.sourceFile.isBindingRequired())) {
808
- return;
809
799
  }
810
- this._parseFile(fileToAnalyze, content, force);
811
- // Create a function to get the scope info.
812
- const getScopeIfAvailable = (fileInfo) => {
813
- if (!fileInfo || fileInfo === fileToAnalyze) {
800
+ this._handleMemoryHighUsage();
801
+ return this._runEvaluatorWithCancellationToken(token, () => {
802
+ var _a;
803
+ const sourceFileInfo = this.getSourceFileInfo(filePath);
804
+ if (!sourceFileInfo) {
814
805
  return undefined;
815
806
  }
816
- // If the file was deleted, there's no scope to return.
817
- if (fileInfo.sourceFile.isFileDeleted()) {
818
- return undefined;
807
+ const content = (_a = sourceFileInfo.sourceFile.getFileContent()) !== null && _a !== void 0 ? _a : '';
808
+ if (options.indexingForAutoImportMode &&
809
+ !options.includeAllSymbols &&
810
+ !sourceFileInfo.sourceFile.isStubFile() &&
811
+ !sourceFileInfo.sourceFile.isThirdPartyPyTypedPresent()) {
812
+ // Perf optimization. if py file doesn't contain __all__
813
+ // No need to parse and bind.
814
+ if (content.indexOf('__all__') < 0) {
815
+ return undefined;
816
+ }
819
817
  }
820
- const parseResults = fileInfo.sourceFile.getParseResults();
821
- if (!parseResults) {
822
- return undefined;
818
+ this._bindFile(sourceFileInfo, content);
819
+ return sourceFileInfo.sourceFile.index(options, token);
820
+ });
821
+ }
822
+ addSymbolsForDocument(filePath, symbolList, token) {
823
+ return this._runEvaluatorWithCancellationToken(token, () => {
824
+ const sourceFileInfo = this.getSourceFileInfo(filePath);
825
+ if (sourceFileInfo) {
826
+ if (!sourceFileInfo.sourceFile.getCachedIndexResults()) {
827
+ // If we already have cached index for this file, no need to bind this file.
828
+ this._bindFile(sourceFileInfo);
829
+ }
830
+ sourceFileInfo.sourceFile.addHierarchicalSymbolsForDocument(symbolList, token);
823
831
  }
824
- // File should already be bound because of the chained file binding above.
825
- const scope = AnalyzerNodeInfo.getScope(parseResults.parseTree);
826
- return scope;
827
- };
828
- let builtinsScope;
829
- if (fileToAnalyze.builtinsImport && fileToAnalyze.builtinsImport !== fileToAnalyze) {
830
- // Bind all of the implicit imports first. So we don't recurse into them.
831
- if (!isImplicitImport) {
832
- this._bindImplicitImports(fileToAnalyze);
832
+ });
833
+ }
834
+ reportSymbolsForWorkspace(query, reporter, token) {
835
+ this._runEvaluatorWithCancellationToken(token, () => {
836
+ // Don't do a search if the query is empty. We'll return
837
+ // too many results in this case.
838
+ if (!query) {
839
+ return;
833
840
  }
834
- // If it is not builtin module itself, we need to parse and bind
835
- // the ipython display import if required. Otherwise, get builtin module.
836
- builtinsScope =
837
- (_b = (_a = getScopeIfAvailable(fileToAnalyze.chainedSourceFile)) !== null && _a !== void 0 ? _a : getScopeIfAvailable(fileToAnalyze.ipythonDisplayImport)) !== null && _b !== void 0 ? _b : getScopeIfAvailable(fileToAnalyze.builtinsImport);
841
+ // "Workspace symbols" searches symbols only from user code.
842
+ for (const sourceFileInfo of this._sourceFileList) {
843
+ if (!(0, sourceFileInfoUtils_1.isUserCode)(sourceFileInfo)) {
844
+ continue;
845
+ }
846
+ if (!sourceFileInfo.sourceFile.getCachedIndexResults()) {
847
+ // If we already have cached index for this file, no need to bind this file.
848
+ this._bindFile(sourceFileInfo);
849
+ }
850
+ const symbolList = sourceFileInfo.sourceFile.getSymbolsForDocument(query, token);
851
+ if (symbolList.length > 0) {
852
+ reporter(symbolList);
853
+ }
854
+ // This operation can consume significant memory, so check
855
+ // for situations where we need to discard the type cache.
856
+ this._handleMemoryHighUsage();
857
+ }
858
+ });
859
+ }
860
+ async getCompletionsForPosition(filePath, position, workspacePath, options, nameMap, libraryMap, token) {
861
+ const sourceFileInfo = this.getSourceFileInfo(filePath);
862
+ if (!sourceFileInfo) {
863
+ return undefined;
838
864
  }
839
- let futureImports = fileToAnalyze.sourceFile.getParseResults().futureImports;
840
- if (fileToAnalyze.chainedSourceFile) {
841
- futureImports = this._getEffectiveFutureImports(futureImports, fileToAnalyze.chainedSourceFile);
865
+ let sourceMapper;
866
+ const completionResult = this._logTracker.log(`completion at ${filePath}:${position.line}:${position.character}`, (ls) => {
867
+ var _a;
868
+ const result = this._runEvaluatorWithCancellationToken(token, () => {
869
+ this._bindFile(sourceFileInfo);
870
+ const execEnv = this._configOptions.findExecEnvironment(filePath);
871
+ sourceMapper = this._createSourceMapper(execEnv, token, sourceFileInfo, /* mapCompiled */ true);
872
+ return sourceFileInfo.sourceFile.getCompletionsForPosition(position, workspacePath, this._configOptions, this._importResolver, this._lookUpImport, this._evaluator, options, sourceMapper, nameMap, libraryMap, () => this._buildModuleSymbolsMap(sourceFileInfo, libraryMap, options.includeUserSymbolsInAutoImport, token), token);
873
+ });
874
+ ls.add(`found ${(_a = result === null || result === void 0 ? void 0 : result.completionMap.size) !== null && _a !== void 0 ? _a : 'null'} items`);
875
+ return result;
876
+ });
877
+ const completionResultsList = {
878
+ completionList: vscode_languageserver_types_1.CompletionList.create(completionResult === null || completionResult === void 0 ? void 0 : completionResult.completionMap.toArray()),
879
+ memberAccessInfo: completionResult === null || completionResult === void 0 ? void 0 : completionResult.memberAccessInfo,
880
+ autoImportInfo: completionResult === null || completionResult === void 0 ? void 0 : completionResult.autoImportInfo,
881
+ extensionInfo: completionResult === null || completionResult === void 0 ? void 0 : completionResult.extensionInfo,
882
+ };
883
+ const parseResults = sourceFileInfo.sourceFile.getParseResults();
884
+ if ((parseResults === null || parseResults === void 0 ? void 0 : parseResults.parseTree) && (parseResults === null || parseResults === void 0 ? void 0 : parseResults.text)) {
885
+ const offset = (0, positionUtils_1.convertPositionToOffset)(position, parseResults.tokenizerOutput.lines);
886
+ if (offset !== undefined && sourceMapper) {
887
+ await Promise.all(extensibility_1.Extensions.getProgramExtensions(parseResults.parseTree).map((e) => {
888
+ var _a;
889
+ return (_a = e.completionListExtension) === null || _a === void 0 ? void 0 : _a.updateCompletionResults(this.evaluator, sourceMapper, options, completionResultsList, parseResults, offset, this._configOptions.functionSignatureDisplay, token);
890
+ }));
891
+ }
842
892
  }
843
- fileToAnalyze.effectiveFutureImports = futureImports.size > 0 ? futureImports : undefined;
844
- fileToAnalyze.sourceFile.bind(this._configOptions, this._lookUpImport, builtinsScope, futureImports);
893
+ return completionResultsList;
845
894
  }
846
- _getEffectiveFutureImports(futureImports, chainedSourceFile) {
847
- var _a;
848
- const effectiveFutureImports = new Set(futureImports);
849
- (_a = chainedSourceFile.effectiveFutureImports) === null || _a === void 0 ? void 0 : _a.forEach((value) => {
850
- effectiveFutureImports.add(value);
895
+ resolveCompletionItem(filePath, completionItem, options, nameMap, libraryMap, token) {
896
+ return this._runEvaluatorWithCancellationToken(token, () => {
897
+ const sourceFileInfo = this.getSourceFileInfo(filePath);
898
+ if (!sourceFileInfo) {
899
+ return;
900
+ }
901
+ this._bindFile(sourceFileInfo);
902
+ const execEnv = this._configOptions.findExecEnvironment(filePath);
903
+ sourceFileInfo.sourceFile.resolveCompletionItem(this._configOptions, this._importResolver, this._lookUpImport, this._evaluator, options, this._createSourceMapper(execEnv, token, sourceFileInfo, /* mapCompiled */ true), nameMap, libraryMap, () => this._buildModuleSymbolsMap(sourceFileInfo, libraryMap, options.includeUserSymbolsInAutoImport, token), completionItem, token);
851
904
  });
852
- return effectiveFutureImports;
853
905
  }
854
- // Build a map of all modules within this program and the module-
855
- // level scope that contains the symbol table for the module.
856
- _buildModuleSymbolsMap(sourceFileToExclude, libraryMap, includeSymbolsFromIndices, token) {
857
- // If we have library map, always use the map for library symbols.
858
- const predicate = (s) => {
859
- if (!libraryMap) {
860
- // We don't have any prebuilt indices, so we need to include
861
- // all files.
862
- return true;
906
+ renameModule(path, newPath, token) {
907
+ return this._runEvaluatorWithCancellationToken(token, () => {
908
+ if ((0, pathUtils_1.isFile)(this.fileSystem, path)) {
909
+ const fileInfo = this.getSourceFileInfo(path);
910
+ if (!fileInfo) {
911
+ return undefined;
912
+ }
863
913
  }
864
- if (!this._configOptions.indexing) {
865
- // We have some prebuilt indices such as stdlib, but indexing is disabled.
866
- // Include files we don't have prebuilt indices.
867
- return libraryMap.get(s.sourceFile.getFilePath()) === undefined;
914
+ const renameModuleProvider = renameModuleProvider_1.RenameModuleProvider.createForModule(this._importResolver, this._configOptions, this._evaluator, path, newPath, token);
915
+ if (!renameModuleProvider) {
916
+ return undefined;
868
917
  }
869
- // We have prebuilt indices for third party libraries. Include only
870
- // user files.
871
- return (0, sourceFileInfoUtils_1.isUserCode)(s);
872
- };
873
- // Only include import alias from user files if indexing is off for now.
874
- // Currently, when indexing is off, we don't do import alias deduplication, so
875
- // adding import alias is cheap. But when indexing is on, we do deduplication, which
876
- // require resolveAliasDeclaration that can cause more files to be parsed and bound.
877
- return (0, autoImporter_1.buildModuleSymbolsMap)(this._sourceFileList.filter((s) => s !== sourceFileToExclude && predicate(s)), includeSymbolsFromIndices, token);
878
- }
879
- _shouldCheckFile(fileInfo) {
880
- // Always do a full checking for a file that's open in the editor.
881
- if (fileInfo.isOpenByClient) {
882
- return true;
883
- }
884
- // If the file isn't currently open, only perform full checking for
885
- // files that are tracked, and only if the checkOnlyOpenFiles is disabled.
886
- if (!this._configOptions.checkOnlyOpenFiles && fileInfo.isTracked) {
887
- return true;
888
- }
889
- return false;
918
+ this._processModuleReferences(renameModuleProvider, renameModuleProvider.lastModuleName, path);
919
+ return { edits: renameModuleProvider.getEdits(), fileOperations: [] };
920
+ });
890
921
  }
891
- _checkTypes(fileToCheck, token) {
892
- return this._logTracker.log(`analyzing: ${fileToCheck.sourceFile.getFilePath()}`, (logState) => {
893
- // If the file isn't needed because it was eliminated from the
894
- // transitive closure or deleted, skip the file rather than wasting
895
- // time on it.
896
- if (!this._isFileNeeded(fileToCheck)) {
897
- logState.suppress();
898
- return false;
922
+ moveSymbolAtPosition(filePath, newFilePath, position, options, token) {
923
+ return this._runEvaluatorWithCancellationToken(token, () => {
924
+ var _a, _b;
925
+ const sourceFileExt = (0, pathUtils_1.getFileExtension)(filePath);
926
+ const destFileExt = (0, pathUtils_1.getFileExtension)(newFilePath);
927
+ if (sourceFileExt.toLowerCase() !== destFileExt.toLowerCase()) {
928
+ // Don't allow moving a symbol from py to pyi or vice versa.
929
+ return undefined;
899
930
  }
900
- if (!fileToCheck.sourceFile.isCheckingRequired()) {
901
- logState.suppress();
902
- return false;
931
+ const fileInfo = this.getSourceFileInfo(filePath);
932
+ if (!fileInfo) {
933
+ return undefined;
903
934
  }
904
- if (!this._shouldCheckFile(fileToCheck)) {
905
- logState.suppress();
906
- return false;
935
+ const newFileInfo = this.getBoundSourceFileInfo(newFilePath);
936
+ if (fileInfo === newFileInfo) {
937
+ // Can't move symbol to the same file.
938
+ return undefined;
907
939
  }
908
- this._bindFile(fileToCheck);
909
- if (this._preCheckCallback) {
910
- const parseResults = fileToCheck.sourceFile.getParseResults();
911
- if (parseResults) {
912
- this._preCheckCallback(parseResults, this._evaluator);
913
- }
914
- }
915
- if (!this._disableChecker) {
916
- // For ipython, make sure we check all its dependent files first since
917
- // their results can affect this file's result.
918
- let dependentFiles = undefined;
919
- if (fileToCheck.sourceFile.getIPythonMode() === sourceFile_1.IPythonMode.CellDocs) {
920
- dependentFiles = [];
921
- const importedByFiles = (0, sourceFileInfoUtils_1.collectImportedByFiles)(fileToCheck);
922
- for (const file of importedByFiles) {
923
- if (!(0, sourceFileInfoUtils_1.isUserCode)(file)) {
924
- continue;
925
- }
926
- // If the file is already analyzed, it will be no op.
927
- // And make sure we don't dump parse tree and etc while
928
- // recursively calling checker. Otherwise, inner check
929
- // can dump parse tree required by outer check.
930
- const handle = this._cacheManager.pauseTracking();
931
- try {
932
- this._checkTypes(file, token);
933
- }
934
- finally {
935
- handle.dispose();
936
- }
937
- const parseResults = file.sourceFile.getParseResults();
938
- if (parseResults) {
939
- dependentFiles.push(parseResults);
940
- }
941
- }
942
- }
943
- const execEnv = this._configOptions.findExecEnvironment(fileToCheck.sourceFile.getFilePath());
944
- fileToCheck.sourceFile.check(this._importResolver, this._evaluator, this._createSourceMapper(execEnv, token, fileToCheck), dependentFiles);
945
- }
946
- // For very large programs, we may need to discard the evaluator and
947
- // its cached types to avoid running out of heap space.
948
- this._handleMemoryHighUsage();
949
- // Detect import cycles that involve the file.
950
- if (this._configOptions.diagnosticRuleSet.reportImportCycles !== 'none') {
951
- // Don't detect import cycles when doing type stub generation. Some
952
- // third-party modules are pretty convoluted.
953
- if (!this._allowedThirdPartyImports) {
954
- // We need to force all of the files to be parsed and build
955
- // a closure map for the files.
956
- const closureMap = new Map();
957
- this._getImportsRecursive(fileToCheck, closureMap, 0);
958
- closureMap.forEach((file) => {
959
- timing_1.timingStats.cycleDetectionTime.timeOperation(() => {
960
- const filesVisitedMap = new Map();
961
- if (!this._detectAndReportImportCycles(file, filesVisitedMap)) {
962
- // If no cycles were found in any of the files we visited,
963
- // set a flag to indicates that we don't need to visit them again
964
- // on subsequent cycle checks.
965
- filesVisitedMap.forEach((sourceFileInfo) => {
966
- sourceFileInfo.sourceFile.setNoCircularDependencyConfirmed();
967
- });
968
- }
969
- });
970
- });
971
- }
972
- }
973
- return true;
974
- });
975
- }
976
- // Builds a map of files that includes the specified file and all of the files
977
- // it imports (recursively) and ensures that all such files. If any of these files
978
- // have already been checked (they and their recursive imports have completed the
979
- // check phase), they are not included in the results.
980
- _getImportsRecursive(file, closureMap, recursionCount) {
981
- // If the file is already in the closure map, we found a cyclical
982
- // dependency. Don't recur further.
983
- const filePath = (0, pathUtils_1.normalizePathCase)(this._fs, file.sourceFile.getFilePath());
984
- if (closureMap.has(filePath)) {
985
- return;
986
- }
987
- // If the import chain is too long, emit an error. Otherwise we
988
- // risk blowing the stack.
989
- if (recursionCount > _maxImportDepth) {
990
- file.sourceFile.setHitMaxImportDepth(_maxImportDepth);
991
- return;
992
- }
993
- // Add the file to the closure map.
994
- closureMap.set(filePath, file);
995
- // If this file hasn't already been parsed, parse it now. This will
996
- // discover any files it imports. Skip this if the file is part
997
- // of a library. We'll assume that no cycles will be generated from
998
- // library code or typeshed stubs.
999
- if ((0, sourceFileInfoUtils_1.isUserCode)(file)) {
1000
- this._parseFile(file);
1001
- }
1002
- // Recursively add the file's imports.
1003
- for (const importedFileInfo of file.imports) {
1004
- this._getImportsRecursive(importedFileInfo, closureMap, recursionCount + 1);
1005
- }
1006
- }
1007
- _detectAndReportImportCycles(sourceFileInfo, filesVisited, dependencyChain = [], dependencyMap = new Map()) {
1008
- // Don't bother checking for typestub files or third-party files.
1009
- if (sourceFileInfo.sourceFile.isStubFile() || sourceFileInfo.isThirdPartyImport) {
1010
- return false;
1011
- }
1012
- // If we've already confirmed that this source file isn't part of a
1013
- // cycle, we can skip it entirely.
1014
- if (sourceFileInfo.sourceFile.isNoCircularDependencyConfirmed()) {
1015
- return false;
1016
- }
1017
- const filePath = (0, pathUtils_1.normalizePathCase)(this._fs, sourceFileInfo.sourceFile.getFilePath());
1018
- filesVisited.set(filePath, sourceFileInfo);
1019
- let detectedCycle = false;
1020
- if (dependencyMap.has(filePath)) {
1021
- // We detect a cycle (partial or full). A full cycle is one that is
1022
- // rooted in the file at the start of our dependency chain. A partial
1023
- // cycle loops back on some other file in the dependency chain. We
1024
- // will report only full cycles here and leave the reporting of
1025
- // partial cycles to other passes.
1026
- detectedCycle = true;
1027
- // Look for chains at least two in length. A file that contains
1028
- // an "import . from X" will technically create a cycle with
1029
- // itself, but those are not interesting to report.
1030
- if (dependencyChain.length > 1 && sourceFileInfo === dependencyChain[0]) {
1031
- this._logImportCycle(dependencyChain);
1032
- }
1033
- }
1034
- else {
1035
- // If we've already checked this dependency along
1036
- // some other path, we can skip it.
1037
- if (dependencyMap.has(filePath)) {
1038
- return false;
1039
- }
1040
- // We use both a map (for fast lookups) and a list
1041
- // (for ordering information). Set the dependency map
1042
- // entry to true to indicate that we're actively exploring
1043
- // that dependency.
1044
- dependencyMap.set(filePath, true);
1045
- dependencyChain.push(sourceFileInfo);
1046
- for (const imp of sourceFileInfo.imports) {
1047
- if (this._detectAndReportImportCycles(imp, filesVisited, dependencyChain, dependencyMap)) {
1048
- detectedCycle = true;
1049
- }
1050
- }
1051
- // Set the dependencyMap entry to false to indicate that we have
1052
- // already explored this file and don't need to explore it again.
1053
- dependencyMap.set(filePath, false);
1054
- dependencyChain.pop();
1055
- }
1056
- return detectedCycle;
1057
- }
1058
- _logImportCycle(dependencyChain) {
1059
- const circDep = new circularDependency_1.CircularDependency();
1060
- dependencyChain.forEach((sourceFileInfo) => {
1061
- circDep.appendPath(sourceFileInfo.sourceFile.getFilePath());
1062
- });
1063
- circDep.normalizeOrder();
1064
- const firstFilePath = circDep.getPaths()[0];
1065
- const firstSourceFile = this.getSourceFileInfo(firstFilePath);
1066
- (0, debug_1.assert)(firstSourceFile !== undefined);
1067
- firstSourceFile.sourceFile.addCircularDependency(circDep);
1068
- }
1069
- _markFileDirtyRecursive(sourceFileInfo, markSet, forceRebinding = false) {
1070
- const filePath = (0, pathUtils_1.normalizePathCase)(this._fs, sourceFileInfo.sourceFile.getFilePath());
1071
- // Don't mark it again if it's already been visited.
1072
- if (markSet.has(filePath)) {
1073
- return;
1074
- }
1075
- sourceFileInfo.sourceFile.markReanalysisRequired(forceRebinding);
1076
- markSet.add(filePath);
1077
- sourceFileInfo.importedBy.forEach((dep) => {
1078
- // Changes on chained source file can change symbols in the symbol table and
1079
- // dependencies on the dependent file. Force rebinding.
1080
- const forceRebinding = dep.chainedSourceFile === sourceFileInfo;
1081
- this._markFileDirtyRecursive(dep, markSet, forceRebinding);
1082
- });
1083
- // Change in the current file could impact checker result of chainedSourceFile such as unused symbols.
1084
- let chainedSourceFile = sourceFileInfo.chainedSourceFile;
1085
- while (chainedSourceFile) {
1086
- if (chainedSourceFile.sourceFile.isCheckingRequired()) {
1087
- // If the file is marked for checking, its chained one should be marked
1088
- // as well. Stop here.
1089
- return;
1090
- }
1091
- chainedSourceFile.sourceFile.markReanalysisRequired(/* forceRebinding */ false);
1092
- chainedSourceFile = chainedSourceFile.chainedSourceFile;
1093
- }
1094
- }
1095
- getTextOnRange(filePath, range, token) {
1096
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1097
- if (!sourceFileInfo) {
1098
- return undefined;
1099
- }
1100
- const sourceFile = sourceFileInfo.sourceFile;
1101
- const fileContents = sourceFile.getOpenFileContents();
1102
- if (fileContents === undefined) {
1103
- // this only works with opened file
1104
- return undefined;
1105
- }
1106
- return this._runEvaluatorWithCancellationToken(token, () => {
1107
- this._parseFile(sourceFileInfo);
1108
- const parseTree = sourceFile.getParseResults();
1109
- const textRange = (0, positionUtils_1.convertRangeToTextRange)(range, parseTree.tokenizerOutput.lines);
1110
- if (!textRange) {
1111
- return undefined;
1112
- }
1113
- return fileContents.substr(textRange.start, textRange.length);
1114
- });
1115
- }
1116
- getAutoImports(filePath, range, similarityLimit, nameMap, options, token) {
1117
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1118
- if (!sourceFileInfo) {
1119
- return [];
1120
- }
1121
- const sourceFile = sourceFileInfo.sourceFile;
1122
- const fileContents = sourceFile.getOpenFileContents();
1123
- if (fileContents === undefined) {
1124
- // this only works with opened file
1125
- return [];
1126
- }
1127
- return this._runEvaluatorWithCancellationToken(token, () => {
1128
- var _a;
1129
- this._bindFile(sourceFileInfo);
1130
- const parseTree = sourceFile.getParseResults();
1131
- const textRange = (0, positionUtils_1.convertRangeToTextRange)(range, parseTree.tokenizerOutput.lines);
1132
- if (!textRange) {
1133
- return [];
1134
- }
1135
- const currentNode = (0, parseTreeUtils_1.findNodeByOffset)(parseTree.parseTree, textRange.start);
1136
- if (!currentNode) {
1137
- return [];
1138
- }
1139
- const writtenWord = fileContents.substr(textRange.start, textRange.length);
1140
- const map = this._buildModuleSymbolsMap(sourceFileInfo, options.libraryMap,
1141
- /* includeSymbolsFromIndices */ true, token);
1142
- options.patternMatcher =
1143
- (_a = options.patternMatcher) !== null && _a !== void 0 ? _a : ((p, t) => (0, stringUtils_1.computeCompletionSimilarity)(p, t) > similarityLimit);
1144
- const autoImporter = new autoImporter_1.AutoImporter(this._configOptions.findExecEnvironment(filePath), this._importResolver, parseTree, range.start, new completionProvider_1.CompletionMap(), map, options);
1145
- // Filter out any name that is already defined in the current scope.
1146
- const results = [];
1147
- const currentScope = (0, scopeUtils_1.getScopeForNode)(currentNode);
1148
- if (currentScope) {
1149
- const info = nameMap === null || nameMap === void 0 ? void 0 : nameMap.get(writtenWord);
1150
- if (info) {
1151
- // No scope filter is needed since we only do exact match.
1152
- (0, collectionUtils_1.appendArray)(results, autoImporter.getAutoImportCandidatesForAbbr(writtenWord, info, token));
1153
- }
1154
- results.push(...autoImporter
1155
- .getAutoImportCandidates(writtenWord, similarityLimit, /* abbrFromUsers */ undefined, token)
1156
- .filter((r) => !currentScope.lookUpSymbolRecursive(r.name)));
1157
- }
1158
- return results;
1159
- });
1160
- }
1161
- getDiagnostics(options) {
1162
- const fileDiagnostics = this._removeUnneededFiles();
1163
- this._sourceFileList.forEach((sourceFileInfo) => {
1164
- if (this._shouldCheckFile(sourceFileInfo)) {
1165
- const diagnostics = sourceFileInfo.sourceFile.getDiagnostics(options, sourceFileInfo.diagnosticsVersion);
1166
- if (diagnostics !== undefined) {
1167
- fileDiagnostics.push({
1168
- filePath: sourceFileInfo.sourceFile.getFilePath(),
1169
- version: sourceFileInfo.sourceFile.getClientVersion(),
1170
- diagnostics,
1171
- });
1172
- // Update the cached diagnosticsVersion so we can determine
1173
- // whether there are any updates next time we call getDiagnostics.
1174
- sourceFileInfo.diagnosticsVersion = sourceFileInfo.sourceFile.getDiagnosticVersion();
1175
- }
1176
- }
1177
- else if (!sourceFileInfo.isOpenByClient &&
1178
- options.checkOnlyOpenFiles &&
1179
- sourceFileInfo.diagnosticsVersion !== undefined) {
1180
- // This condition occurs when the user switches from workspace to
1181
- // "open files only" mode. Clear all diagnostics for this file.
1182
- fileDiagnostics.push({
1183
- filePath: sourceFileInfo.sourceFile.getFilePath(),
1184
- version: sourceFileInfo.sourceFile.getClientVersion(),
1185
- diagnostics: [],
1186
- });
1187
- sourceFileInfo.diagnosticsVersion = undefined;
1188
- }
1189
- });
1190
- return fileDiagnostics;
1191
- }
1192
- getDiagnosticsForRange(filePath, range) {
1193
- const sourceFile = this.getSourceFile(filePath);
1194
- if (!sourceFile) {
1195
- return [];
1196
- }
1197
- const unfilteredDiagnostics = sourceFile.getDiagnostics(this._configOptions);
1198
- if (!unfilteredDiagnostics) {
1199
- return [];
1200
- }
1201
- return unfilteredDiagnostics.filter((diag) => {
1202
- return (0, textRange_1.doRangesIntersect)(diag.range, range);
1203
- });
1204
- }
1205
- getDefinitionsForPosition(filePath, position, filter, token) {
1206
- return this._runEvaluatorWithCancellationToken(token, () => {
1207
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1208
- if (!sourceFileInfo) {
1209
- return undefined;
1210
- }
1211
- this._bindFile(sourceFileInfo);
1212
- const execEnv = this._configOptions.findExecEnvironment(filePath);
1213
- return sourceFileInfo.sourceFile.getDefinitionsForPosition(this._createSourceMapper(execEnv, token, sourceFileInfo), position, filter, this._evaluator, token);
1214
- });
1215
- }
1216
- getTypeDefinitionsForPosition(filePath, position, token) {
1217
- return this._runEvaluatorWithCancellationToken(token, () => {
1218
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1219
- if (!sourceFileInfo) {
1220
- return undefined;
1221
- }
1222
- this._bindFile(sourceFileInfo);
1223
- const execEnv = this._configOptions.findExecEnvironment(filePath);
1224
- return sourceFileInfo.sourceFile.getTypeDefinitionsForPosition(this._createSourceMapper(execEnv, token, sourceFileInfo,
1225
- /* mapCompiled */ false,
1226
- /* preferStubs */ true), position, this._evaluator, filePath, token);
1227
- });
1228
- }
1229
- reportReferencesForPosition(filePath, position, includeDeclaration, reporter, token) {
1230
- this._runEvaluatorWithCancellationToken(token, () => {
1231
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1232
- if (!sourceFileInfo) {
1233
- return;
1234
- }
1235
- const invokedFromUserFile = (0, sourceFileInfoUtils_1.isUserCode)(sourceFileInfo);
1236
- this._bindFile(sourceFileInfo);
1237
- const execEnv = this._configOptions.findExecEnvironment(filePath);
1238
- const referencesResult = this._getDeclarationForPosition(sourceFileInfo, position, documentSymbolCollector_1.DocumentSymbolCollectorUseCase.Reference, this._createSourceMapper(execEnv, token, sourceFileInfo), token, reporter);
1239
- if (!referencesResult) {
1240
- return;
1241
- }
1242
- // Do we need to do a global search as well?
1243
- if (referencesResult.requiresGlobalSearch) {
1244
- for (const curSourceFileInfo of this._sourceFileList) {
1245
- (0, cancellationUtils_1.throwIfCancellationRequested)(token);
1246
- // "Find all references" will only include references from user code
1247
- // unless the file is explicitly opened in the editor or it is invoked from non user files.
1248
- if (curSourceFileInfo.isOpenByClient || !invokedFromUserFile || (0, sourceFileInfoUtils_1.isUserCode)(curSourceFileInfo)) {
1249
- // See if the reference symbol's string is located somewhere within the file.
1250
- // If not, we can skip additional processing for the file.
1251
- const fileContents = curSourceFileInfo.sourceFile.getFileContent();
1252
- if (!fileContents || referencesResult.symbolNames.some((s) => fileContents.search(s) >= 0)) {
1253
- this._bindFile(curSourceFileInfo);
1254
- curSourceFileInfo.sourceFile.addReferences(referencesResult, includeDeclaration, this._evaluator, token);
1255
- }
1256
- // This operation can consume significant memory, so check
1257
- // for situations where we need to discard the type cache.
1258
- this._handleMemoryHighUsage();
1259
- }
1260
- }
1261
- // Make sure to include declarations regardless where they are defined
1262
- // if includeDeclaration is set.
1263
- if (includeDeclaration) {
1264
- for (const decl of referencesResult.declarations) {
1265
- (0, cancellationUtils_1.throwIfCancellationRequested)(token);
1266
- if (referencesResult.locations.some((l) => l.path === decl.path)) {
1267
- // Already included.
1268
- continue;
1269
- }
1270
- const declFileInfo = this.getSourceFileInfo(decl.path);
1271
- if (!declFileInfo) {
1272
- // The file the declaration belongs to doesn't belong to the program.
1273
- continue;
1274
- }
1275
- const tempResult = new referencesProvider_1.ReferencesResult(referencesResult.requiresGlobalSearch, referencesResult.nodeAtOffset, referencesResult.symbolNames, referencesResult.declarations, referencesResult.useCase);
1276
- declFileInfo.sourceFile.addReferences(tempResult, includeDeclaration, this._evaluator, token);
1277
- for (const loc of tempResult.locations) {
1278
- // Include declarations only. And throw away any references
1279
- if (loc.path === decl.path && (0, textRange_1.doesRangeContain)(decl.range, loc.range)) {
1280
- referencesResult.addLocations(loc);
1281
- }
1282
- }
1283
- }
1284
- }
1285
- }
1286
- else {
1287
- sourceFileInfo.sourceFile.addReferences(referencesResult, includeDeclaration, this._evaluator, token);
1288
- }
1289
- });
1290
- }
1291
- getFileIndex(filePath, options, token) {
1292
- if (options.indexingForAutoImportMode) {
1293
- // Memory optimization. We only want to hold onto symbols
1294
- // usable outside when importSymbolsOnly is on.
1295
- const name = (0, pathUtils_1.stripFileExtension)((0, pathUtils_1.getFileName)(filePath));
1296
- if ((0, symbolNameUtils_1.isPrivateOrProtectedName)(name)) {
1297
- return undefined;
1298
- }
1299
- }
1300
- this._handleMemoryHighUsage();
1301
- return this._runEvaluatorWithCancellationToken(token, () => {
1302
- var _a;
1303
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1304
- if (!sourceFileInfo) {
1305
- return undefined;
1306
- }
1307
- const content = (_a = sourceFileInfo.sourceFile.getFileContent()) !== null && _a !== void 0 ? _a : '';
1308
- if (options.indexingForAutoImportMode &&
1309
- !options.includeAllSymbols &&
1310
- !sourceFileInfo.sourceFile.isStubFile() &&
1311
- !sourceFileInfo.sourceFile.isThirdPartyPyTypedPresent()) {
1312
- // Perf optimization. if py file doesn't contain __all__
1313
- // No need to parse and bind.
1314
- if (content.indexOf('__all__') < 0) {
1315
- return undefined;
1316
- }
1317
- }
1318
- this._bindFile(sourceFileInfo, content);
1319
- return sourceFileInfo.sourceFile.index(options, token);
1320
- });
1321
- }
1322
- addSymbolsForDocument(filePath, symbolList, token) {
1323
- return this._runEvaluatorWithCancellationToken(token, () => {
1324
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1325
- if (sourceFileInfo) {
1326
- if (!sourceFileInfo.sourceFile.getCachedIndexResults()) {
1327
- // If we already have cached index for this file, no need to bind this file.
1328
- this._bindFile(sourceFileInfo);
1329
- }
1330
- sourceFileInfo.sourceFile.addHierarchicalSymbolsForDocument(symbolList, token);
1331
- }
1332
- });
1333
- }
1334
- reportSymbolsForWorkspace(query, reporter, token) {
1335
- this._runEvaluatorWithCancellationToken(token, () => {
1336
- // Don't do a search if the query is empty. We'll return
1337
- // too many results in this case.
1338
- if (!query) {
1339
- return;
1340
- }
1341
- // "Workspace symbols" searches symbols only from user code.
1342
- for (const sourceFileInfo of this._sourceFileList) {
1343
- if (!(0, sourceFileInfoUtils_1.isUserCode)(sourceFileInfo)) {
1344
- continue;
1345
- }
1346
- if (!sourceFileInfo.sourceFile.getCachedIndexResults()) {
1347
- // If we already have cached index for this file, no need to bind this file.
1348
- this._bindFile(sourceFileInfo);
1349
- }
1350
- const symbolList = sourceFileInfo.sourceFile.getSymbolsForDocument(query, token);
1351
- if (symbolList.length > 0) {
1352
- reporter(symbolList);
1353
- }
1354
- // This operation can consume significant memory, so check
1355
- // for situations where we need to discard the type cache.
1356
- this._handleMemoryHighUsage();
1357
- }
1358
- });
1359
- }
1360
- getHoverForPosition(filePath, position, format, token) {
1361
- return this._runEvaluatorWithCancellationToken(token, () => {
1362
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1363
- if (!sourceFileInfo) {
1364
- return undefined;
1365
- }
1366
- this._bindFile(sourceFileInfo);
1367
- const execEnv = this._configOptions.findExecEnvironment(filePath);
1368
- return sourceFileInfo.sourceFile.getHoverForPosition(this._createSourceMapper(execEnv, token, sourceFileInfo, /* mapCompiled */ true), position, format, this._evaluator, this.functionSignatureDisplay(), token);
1369
- });
1370
- }
1371
- getDocumentHighlight(filePath, position, token) {
1372
- return this._runEvaluatorWithCancellationToken(token, () => {
1373
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1374
- if (!sourceFileInfo) {
1375
- return undefined;
1376
- }
1377
- this._bindFile(sourceFileInfo);
1378
- const execEnv = this._configOptions.findExecEnvironment(filePath);
1379
- return sourceFileInfo.sourceFile.getDocumentHighlight(this._createSourceMapper(execEnv, token, sourceFileInfo), position, this._evaluator, token);
1380
- });
1381
- }
1382
- getSignatureHelpForPosition(filePath, position, format, token) {
1383
- return this._runEvaluatorWithCancellationToken(token, () => {
1384
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1385
- if (!sourceFileInfo) {
1386
- return undefined;
1387
- }
1388
- this._bindFile(sourceFileInfo);
1389
- const execEnv = this._configOptions.findExecEnvironment(filePath);
1390
- return sourceFileInfo.sourceFile.getSignatureHelpForPosition(position, this._createSourceMapper(execEnv, token, sourceFileInfo, /* mapCompiled */ true), this._evaluator, format, token);
1391
- });
1392
- }
1393
- async getCompletionsForPosition(filePath, position, workspacePath, options, nameMap, libraryMap, token) {
1394
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1395
- if (!sourceFileInfo) {
1396
- return undefined;
1397
- }
1398
- let sourceMapper;
1399
- const completionResult = this._logTracker.log(`completion at ${filePath}:${position.line}:${position.character}`, (ls) => {
1400
- var _a;
1401
- const result = this._runEvaluatorWithCancellationToken(token, () => {
1402
- this._bindFile(sourceFileInfo);
1403
- const execEnv = this._configOptions.findExecEnvironment(filePath);
1404
- sourceMapper = this._createSourceMapper(execEnv, token, sourceFileInfo, /* mapCompiled */ true);
1405
- return sourceFileInfo.sourceFile.getCompletionsForPosition(position, workspacePath, this._configOptions, this._importResolver, this._lookUpImport, this._evaluator, options, sourceMapper, nameMap, libraryMap, () => this._buildModuleSymbolsMap(sourceFileInfo, libraryMap, options.includeUserSymbolsInAutoImport, token), token);
1406
- });
1407
- ls.add(`found ${(_a = result === null || result === void 0 ? void 0 : result.completionMap.size) !== null && _a !== void 0 ? _a : 'null'} items`);
1408
- return result;
1409
- });
1410
- const completionResultsList = {
1411
- completionList: vscode_languageserver_types_1.CompletionList.create(completionResult === null || completionResult === void 0 ? void 0 : completionResult.completionMap.toArray()),
1412
- memberAccessInfo: completionResult === null || completionResult === void 0 ? void 0 : completionResult.memberAccessInfo,
1413
- autoImportInfo: completionResult === null || completionResult === void 0 ? void 0 : completionResult.autoImportInfo,
1414
- extensionInfo: completionResult === null || completionResult === void 0 ? void 0 : completionResult.extensionInfo,
1415
- };
1416
- const parseResults = sourceFileInfo.sourceFile.getParseResults();
1417
- if ((parseResults === null || parseResults === void 0 ? void 0 : parseResults.parseTree) && (parseResults === null || parseResults === void 0 ? void 0 : parseResults.text)) {
1418
- const offset = (0, positionUtils_1.convertPositionToOffset)(position, parseResults.tokenizerOutput.lines);
1419
- if (offset !== undefined && sourceMapper) {
1420
- await Promise.all(extensibility_1.Extensions.getProgramExtensions(parseResults.parseTree).map((e) => {
1421
- var _a;
1422
- return (_a = e.completionListExtension) === null || _a === void 0 ? void 0 : _a.updateCompletionResults(this.evaluator, sourceMapper, options, completionResultsList, parseResults, offset, this._configOptions.functionSignatureDisplay, token);
1423
- }));
1424
- }
1425
- }
1426
- return completionResultsList;
1427
- }
1428
- resolveCompletionItem(filePath, completionItem, options, nameMap, libraryMap, token) {
1429
- return this._runEvaluatorWithCancellationToken(token, () => {
1430
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1431
- if (!sourceFileInfo) {
1432
- return;
1433
- }
1434
- this._bindFile(sourceFileInfo);
1435
- const execEnv = this._configOptions.findExecEnvironment(filePath);
1436
- sourceFileInfo.sourceFile.resolveCompletionItem(this._configOptions, this._importResolver, this._lookUpImport, this._evaluator, options, this._createSourceMapper(execEnv, token, sourceFileInfo, /* mapCompiled */ true), nameMap, libraryMap, () => this._buildModuleSymbolsMap(sourceFileInfo, libraryMap, options.includeUserSymbolsInAutoImport, token), completionItem, token);
1437
- });
1438
- }
1439
- renameModule(path, newPath, token) {
1440
- return this._runEvaluatorWithCancellationToken(token, () => {
1441
- if ((0, pathUtils_1.isFile)(this._fs, path)) {
1442
- const fileInfo = this.getSourceFileInfo(path);
1443
- if (!fileInfo) {
1444
- return undefined;
1445
- }
1446
- }
1447
- const renameModuleProvider = renameModuleProvider_1.RenameModuleProvider.createForModule(this._importResolver, this._configOptions, this._evaluator, path, newPath, token);
1448
- if (!renameModuleProvider) {
1449
- return undefined;
1450
- }
1451
- this._processModuleReferences(renameModuleProvider, renameModuleProvider.lastModuleName, path);
1452
- return { edits: renameModuleProvider.getEdits(), fileOperations: [] };
1453
- });
1454
- }
1455
- moveSymbolAtPosition(filePath, newFilePath, position, options, token) {
1456
- return this._runEvaluatorWithCancellationToken(token, () => {
1457
- var _a, _b;
1458
- const sourceFileExt = (0, pathUtils_1.getFileExtension)(filePath);
1459
- const destFileExt = (0, pathUtils_1.getFileExtension)(newFilePath);
1460
- if (sourceFileExt.toLowerCase() !== destFileExt.toLowerCase()) {
1461
- // Don't allow moving a symbol from py to pyi or vice versa.
1462
- return undefined;
1463
- }
1464
- const fileInfo = this.getSourceFileInfo(filePath);
1465
- if (!fileInfo) {
1466
- return undefined;
1467
- }
1468
- const newFileInfo = this.getBoundSourceFileInfo(newFilePath);
1469
- if (fileInfo === newFileInfo) {
1470
- // Can't move symbol to the same file.
1471
- return undefined;
1472
- }
1473
- this._bindFile(fileInfo);
1474
- const parseResults = fileInfo.sourceFile.getParseResults();
1475
- if (!parseResults) {
1476
- return undefined;
940
+ this._bindFile(fileInfo);
941
+ const parseResults = fileInfo.sourceFile.getParseResults();
942
+ if (!parseResults) {
943
+ return undefined;
1477
944
  }
1478
945
  const offset = (0, positionUtils_1.convertPositionToOffset)(position, parseResults.tokenizerOutput.lines);
1479
946
  if (offset === undefined) {
@@ -1617,804 +1084,1190 @@ class Program {
1617
1084
  if ((nameNode === null || nameNode === void 0 ? void 0 : nameNode.nodeType) !== 38 /* Name */) {
1618
1085
  continue;
1619
1086
  }
1620
- // decl is synthesized. there is no node associated with the decl.
1621
- // ex) import a or import a.b
1622
- const dottedName1 = ((_a = nameNode.parent) === null || _a === void 0 ? void 0 : _a.nodeType) === 37 /* ModuleName */ ? nameNode.parent.nameParts : [nameNode];
1623
- for (const [decl, names] of importData.declarations) {
1624
- if (decl.node) {
1625
- if (textRange_1.TextRange.containsRange(decl.node, nameNode)) {
1626
- tracker.removeNodes({ node: nameNode, parseResults: parseResults });
1627
- break;
1087
+ // decl is synthesized. there is no node associated with the decl.
1088
+ // ex) import a or import a.b
1089
+ const dottedName1 = ((_a = nameNode.parent) === null || _a === void 0 ? void 0 : _a.nodeType) === 37 /* ModuleName */ ? nameNode.parent.nameParts : [nameNode];
1090
+ for (const [decl, names] of importData.declarations) {
1091
+ if (decl.node) {
1092
+ if (textRange_1.TextRange.containsRange(decl.node, nameNode)) {
1093
+ tracker.removeNodes({ node: nameNode, parseResults: parseResults });
1094
+ break;
1095
+ }
1096
+ }
1097
+ const dottedName2 = (0, parseTreeUtils_1.getDottedName)((0, parseTreeUtils_1.getDottedNameWithGivenNodeAsLastName)(names[0]));
1098
+ if (dottedName2 && (0, collectionUtils_1.arrayEquals)(dottedName1, dottedName2, (e1, e2) => e1.value === e2.value)) {
1099
+ tracker.removeNodes({ node: nameNode, parseResults: parseResults });
1100
+ break;
1101
+ }
1102
+ }
1103
+ }
1104
+ const oldText = sourceFile.getFileContent();
1105
+ const newText = (0, workspaceEditUtils_1.applyTextEditsToString)(tracker.getEdits(token).filter((v) => v.filePath === filePath), parseResults.tokenizerOutput.lines, oldText);
1106
+ // We will attempt to remove unused imports multiple times since removing 1 unused import
1107
+ // could make another import unused. This is due to how we calculate which import is not used.
1108
+ // ex) import os, os.path, os.path.xxx
1109
+ // `os.path` and `os.path.xxx` will be marked as used due to `import os`.
1110
+ // once `os` is removed `os.path` will be marked as unused and so on.
1111
+ // We will attempt to remove those chained unused imports up to 10 chain.
1112
+ if (attempt > 10 || oldText === newText) {
1113
+ return newText;
1114
+ }
1115
+ _updateFileContent(cloned, filePath, newText);
1116
+ return _tryGetTextAfterUnusedImportsRemoved(cloned, filePath, importData, attempt + 1, token);
1117
+ }
1118
+ });
1119
+ function _updateFileContent(cloned, filePath, text) {
1120
+ var _a, _b;
1121
+ const info = cloned.getSourceFileInfo(filePath);
1122
+ const version = info ? ((_a = info.sourceFile.getClientVersion()) !== null && _a !== void 0 ? _a : 0) + 1 : 0;
1123
+ const chainedFilePath = info ? (_b = info.chainedSourceFile) === null || _b === void 0 ? void 0 : _b.sourceFile.getFilePath() : undefined;
1124
+ const ipythonMode = info ? info.sourceFile.getIPythonMode() : sourceFile_1.IPythonMode.None;
1125
+ const isTracked = info ? info.isTracked : true;
1126
+ const realFilePath = info ? info.sourceFile.getRealFilePath() : filePath;
1127
+ cloned.setFileOpened(filePath, version, text, {
1128
+ chainedFilePath,
1129
+ ipythonMode,
1130
+ isTracked,
1131
+ realFilePath,
1132
+ });
1133
+ }
1134
+ function _getNumberOfBlankLinesToInsert(parseResults, decl, position) {
1135
+ if (position.line === 0 && position.character === 0) {
1136
+ return 0;
1137
+ }
1138
+ let previousStatement;
1139
+ const offset = (0, positionUtils_1.convertPositionToOffset)(position, parseResults.tokenizerOutput.lines);
1140
+ if (offset && parseResults.parseTree.statements.length > 0) {
1141
+ previousStatement = parseResults.parseTree.statements.reduce((prev, curr) => offset < curr.start ? prev : curr);
1142
+ }
1143
+ // This basically try to add some blank lines after the last line with text.
1144
+ let linesToAdd = 0;
1145
+ if (previousStatement) {
1146
+ if ((0, declaration_1.isVariableDeclaration)(decl)) {
1147
+ switch (previousStatement.nodeType) {
1148
+ case 47 /* StatementList */:
1149
+ // Small statement such as call, assignment, etc.
1150
+ linesToAdd = 0;
1151
+ break;
1152
+ case 10 /* Class */:
1153
+ case 28 /* Function */:
1154
+ linesToAdd = 2;
1155
+ break;
1156
+ default:
1157
+ // any other statement such as if, while, etc. we will add 1 blank line.
1158
+ linesToAdd = 1;
1159
+ }
1160
+ }
1161
+ else {
1162
+ linesToAdd = 2;
1163
+ }
1164
+ }
1165
+ // If the position is not at the beginning of the line, we need to add 1 more '\n'
1166
+ // to start from blank line.
1167
+ linesToAdd += position.character !== 0 ? 1 : 0;
1168
+ // If there are already blank lines, we only add the difference.
1169
+ const desiredBlankLines = linesToAdd;
1170
+ const startingLine = position.character !== 0 ? position.line : position.line - 1;
1171
+ for (let i = 0; i < desiredBlankLines; i++) {
1172
+ const currentLine = startingLine - i;
1173
+ if (currentLine < 0 || !(0, parseTreeUtils_1.isBlankLine)(parseResults, currentLine)) {
1174
+ break;
1175
+ }
1176
+ linesToAdd--;
1177
+ }
1178
+ return linesToAdd;
1179
+ }
1180
+ }
1181
+ clone() {
1182
+ var _a, _b;
1183
+ const program = new Program(this._importResolver, this._configOptions, this._console, new logTracker_1.LogTracker(this._console, 'Cloned'));
1184
+ // Cloned program will use whatever user files the program currently has.
1185
+ const userFiles = this.getUserFiles();
1186
+ program.setTrackedFiles(userFiles.map((i) => i.sourceFile.getFilePath()));
1187
+ program.markAllFilesDirty(true);
1188
+ // Make sure we keep editor content (open file) which could be different than one in the file system.
1189
+ for (const fileInfo of this.getOpened()) {
1190
+ const version = fileInfo.sourceFile.getClientVersion();
1191
+ if (version === undefined) {
1192
+ continue;
1193
+ }
1194
+ program.setFileOpened(fileInfo.sourceFile.getFilePath(), version, (_a = fileInfo.sourceFile.getOpenFileContents()) !== null && _a !== void 0 ? _a : '', {
1195
+ chainedFilePath: (_b = fileInfo.chainedSourceFile) === null || _b === void 0 ? void 0 : _b.sourceFile.getFilePath(),
1196
+ ipythonMode: fileInfo.sourceFile.getIPythonMode(),
1197
+ isTracked: fileInfo.isTracked,
1198
+ realFilePath: fileInfo.sourceFile.getRealFilePath(),
1199
+ });
1200
+ }
1201
+ return program;
1202
+ }
1203
+ canRenameSymbolAtPosition(filePath, position, isDefaultWorkspace, allowModuleRename, token) {
1204
+ return this._runEvaluatorWithCancellationToken(token, () => {
1205
+ const sourceFileInfo = this.getSourceFileInfo(filePath);
1206
+ if (!sourceFileInfo) {
1207
+ return undefined;
1208
+ }
1209
+ this._bindFile(sourceFileInfo);
1210
+ const referencesResult = this._getReferenceResult(sourceFileInfo, filePath, position, allowModuleRename, token);
1211
+ if (!referencesResult) {
1212
+ return undefined;
1213
+ }
1214
+ if (referencesResult.containsOnlyImportDecls &&
1215
+ !this._supportRenameModule(referencesResult.declarations, isDefaultWorkspace)) {
1216
+ return undefined;
1217
+ }
1218
+ const renameMode = this._getRenameSymbolMode(sourceFileInfo, referencesResult, isDefaultWorkspace);
1219
+ if (renameMode === 'none') {
1220
+ return undefined;
1221
+ }
1222
+ // Return the range of the symbol.
1223
+ const parseResult = sourceFileInfo.sourceFile.getParseResults();
1224
+ return {
1225
+ range: (0, positionUtils_1.convertTextRangeToRange)(referencesResult.nodeAtOffset, parseResult.tokenizerOutput.lines),
1226
+ declarations: referencesResult.declarations,
1227
+ };
1228
+ });
1229
+ }
1230
+ renameSymbolAtPosition(filePath, position, newName, isDefaultWorkspace, allowModuleRename, token) {
1231
+ return this._runEvaluatorWithCancellationToken(token, () => {
1232
+ var _a;
1233
+ const sourceFileInfo = this.getSourceFileInfo(filePath);
1234
+ if (!sourceFileInfo) {
1235
+ return undefined;
1236
+ }
1237
+ this._bindFile(sourceFileInfo);
1238
+ const referencesResult = this._getReferenceResult(sourceFileInfo, filePath, position, allowModuleRename, token);
1239
+ if (!referencesResult) {
1240
+ return undefined;
1241
+ }
1242
+ if (referencesResult.containsOnlyImportDecls) {
1243
+ // All decls must be on a user file.
1244
+ if (!this._supportRenameModule(referencesResult.declarations, isDefaultWorkspace)) {
1245
+ return undefined;
1246
+ }
1247
+ const moduleInfo = renameModuleProvider_1.RenameModuleProvider.getRenameModulePathInfo(renameModuleProvider_1.RenameModuleProvider.getRenameModulePath(referencesResult.declarations), newName);
1248
+ if (!moduleInfo) {
1249
+ // Can't figure out module to rename.
1250
+ return undefined;
1251
+ }
1252
+ const editActions = this.renameModule(moduleInfo.filePath, moduleInfo.newFilePath, token);
1253
+ // Add file system rename.
1254
+ editActions === null || editActions === void 0 ? void 0 : editActions.fileOperations.push({
1255
+ kind: 'rename',
1256
+ oldFilePath: moduleInfo.filePath,
1257
+ newFilePath: moduleInfo.newFilePath,
1258
+ });
1259
+ if ((0, sourceMapper_1.isStubFile)(moduleInfo.filePath)) {
1260
+ const matchingFiles = this._importResolver.getSourceFilesFromStub(moduleInfo.filePath, this._configOptions.findExecEnvironment(filePath),
1261
+ /* mapCompiled */ false);
1262
+ for (const matchingFile of matchingFiles) {
1263
+ const matchingFileInfo = renameModuleProvider_1.RenameModuleProvider.getRenameModulePathInfo(matchingFile, newName);
1264
+ if (matchingFileInfo) {
1265
+ editActions === null || editActions === void 0 ? void 0 : editActions.fileOperations.push({
1266
+ kind: 'rename',
1267
+ oldFilePath: matchingFileInfo.filePath,
1268
+ newFilePath: matchingFileInfo.newFilePath,
1269
+ });
1270
+ }
1271
+ }
1272
+ }
1273
+ return editActions;
1274
+ }
1275
+ const referenceProvider = new referencesProvider_1.ReferencesProvider(this, token);
1276
+ const renameMode = this._getRenameSymbolMode(sourceFileInfo, referencesResult, isDefaultWorkspace);
1277
+ switch (renameMode) {
1278
+ case 'singleFileMode':
1279
+ referenceProvider.addReferencesToResult(sourceFileInfo.sourceFile.getFilePath(),
1280
+ /* includeDeclaration */ true, referencesResult);
1281
+ break;
1282
+ case 'multiFileMode': {
1283
+ for (const curSourceFileInfo of this._sourceFileList) {
1284
+ // Make sure we only add user code to the references to prevent us
1285
+ // from accidentally changing third party library or type stub.
1286
+ if ((0, sourceFileInfoUtils_1.isUserCode)(curSourceFileInfo)) {
1287
+ // Make sure searching symbol name exists in the file.
1288
+ const content = (_a = curSourceFileInfo.sourceFile.getFileContent()) !== null && _a !== void 0 ? _a : '';
1289
+ if (!referencesResult.symbolNames.some((s) => content.search(s) >= 0)) {
1290
+ continue;
1628
1291
  }
1292
+ this._bindFile(curSourceFileInfo, content);
1293
+ referenceProvider.addReferencesToResult(curSourceFileInfo.sourceFile.getFilePath(),
1294
+ /* includeDeclaration */ true, referencesResult);
1629
1295
  }
1630
- const dottedName2 = (0, parseTreeUtils_1.getDottedName)((0, parseTreeUtils_1.getDottedNameWithGivenNodeAsLastName)(names[0]));
1631
- if (dottedName2 && (0, collectionUtils_1.arrayEquals)(dottedName1, dottedName2, (e1, e2) => e1.value === e2.value)) {
1632
- tracker.removeNodes({ node: nameNode, parseResults: parseResults });
1633
- break;
1634
- }
1296
+ // This operation can consume significant memory, so check
1297
+ // for situations where we need to discard the type cache.
1298
+ this._handleMemoryHighUsage();
1635
1299
  }
1300
+ break;
1636
1301
  }
1637
- const oldText = sourceFile.getFileContent();
1638
- const newText = (0, workspaceEditUtils_1.applyTextEditsToString)(tracker.getEdits(token).filter((v) => v.filePath === filePath), parseResults.tokenizerOutput.lines, oldText);
1639
- // We will attempt to remove unused imports multiple times since removing 1 unused import
1640
- // could make another import unused. This is due to how we calculate which import is not used.
1641
- // ex) import os, os.path, os.path.xxx
1642
- // `os.path` and `os.path.xxx` will be marked as used due to `import os`.
1643
- // once `os` is removed `os.path` will be marked as unused and so on.
1644
- // We will attempt to remove those chained unused imports up to 10 chain.
1645
- if (attempt > 10 || oldText === newText) {
1646
- return newText;
1647
- }
1648
- _updateFileContent(cloned, filePath, newText);
1649
- return _tryGetTextAfterUnusedImportsRemoved(cloned, filePath, importData, attempt + 1, token);
1302
+ case 'none':
1303
+ // Rename is not allowed.
1304
+ // ex) rename symbols from libraries.
1305
+ return undefined;
1306
+ default:
1307
+ (0, debug_1.assertNever)(renameMode);
1650
1308
  }
1651
- });
1652
- function _updateFileContent(cloned, filePath, text) {
1653
- var _a, _b;
1654
- const info = cloned.getSourceFileInfo(filePath);
1655
- const version = info ? ((_a = info.sourceFile.getClientVersion()) !== null && _a !== void 0 ? _a : 0) + 1 : 0;
1656
- const chainedFilePath = info ? (_b = info.chainedSourceFile) === null || _b === void 0 ? void 0 : _b.sourceFile.getFilePath() : undefined;
1657
- const ipythonMode = info ? info.sourceFile.getIPythonMode() : sourceFile_1.IPythonMode.None;
1658
- const isTracked = info ? info.isTracked : true;
1659
- const realFilePath = info ? info.sourceFile.getRealFilePath() : filePath;
1660
- cloned.setFileOpened(filePath, version, [{ text }], {
1661
- chainedFilePath,
1662
- ipythonMode,
1663
- isTracked,
1664
- realFilePath,
1309
+ const edits = [];
1310
+ referencesResult.locations.forEach((loc) => {
1311
+ edits.push({
1312
+ filePath: loc.path,
1313
+ range: loc.range,
1314
+ replacementText: newName,
1315
+ });
1665
1316
  });
1317
+ return { edits, fileOperations: [] };
1318
+ });
1319
+ }
1320
+ // Returns a value from 0 to 1 (or more) indicating how "full" the cache is
1321
+ // relative to some predetermined high-water mark. We'll compute this value
1322
+ // based on two easy-to-compute metrics: the number of entries in the type
1323
+ // cache and the number of parsed files.
1324
+ getCacheUsage() {
1325
+ const typeCacheEntryCount = this._evaluator.getTypeCacheEntryCount();
1326
+ const entryCountRatio = typeCacheEntryCount / 750000;
1327
+ const fileCountRatio = this._parsedFileCount / 1000;
1328
+ return Math.max(entryCountRatio, fileCountRatio);
1329
+ }
1330
+ // Discards any cached information associated with this program.
1331
+ emptyCache() {
1332
+ this._createNewEvaluator();
1333
+ this._discardCachedParseResults();
1334
+ this._parsedFileCount = 0;
1335
+ extensibility_1.Extensions.getProgramExtensions(this.rootPath).forEach((e) => (e.clearCache ? e.clearCache() : null));
1336
+ }
1337
+ test_createSourceMapper(execEnv, from) {
1338
+ return this._createSourceMapper(execEnv, vscode_languageserver_1.CancellationToken.None, /* from */ from, /* mapCompiled */ false);
1339
+ }
1340
+ _getRenameSymbolMode(sourceFileInfo, referencesResult, isDefaultWorkspace) {
1341
+ // We have 2 different cases
1342
+ // Single file mode.
1343
+ // 1. rename on default workspace (ex, standalone file mode).
1344
+ // 2. rename local symbols.
1345
+ // 3. rename symbols defined in the non user open file.
1346
+ //
1347
+ // and Multi file mode.
1348
+ // 1. rename public symbols defined in user files on regular workspace (ex, open folder mode).
1349
+ const userFile = (0, sourceFileInfoUtils_1.isUserCode)(sourceFileInfo);
1350
+ if (isDefaultWorkspace ||
1351
+ (userFile && !referencesResult.requiresGlobalSearch) ||
1352
+ (!userFile &&
1353
+ sourceFileInfo.isOpenByClient &&
1354
+ referencesResult.declarations.every((d) => this.getSourceFileInfo(d.path) === sourceFileInfo))) {
1355
+ return 'singleFileMode';
1666
1356
  }
1667
- function _getNumberOfBlankLinesToInsert(parseResults, decl, position) {
1668
- if (position.line === 0 && position.character === 0) {
1669
- return 0;
1357
+ if (referencesResult.declarations.every((d) => (0, sourceFileInfoUtils_1.isUserCode)(this.getSourceFileInfo(d.path)))) {
1358
+ return 'multiFileMode';
1359
+ }
1360
+ // Rename is not allowed.
1361
+ // ex) rename symbols from libraries.
1362
+ return 'none';
1363
+ }
1364
+ _supportRenameModule(declarations, isDefaultWorkspace) {
1365
+ // Rename module is not supported for standalone file and all decls must be on a user file.
1366
+ return !isDefaultWorkspace && declarations.every((d) => (0, sourceFileInfoUtils_1.isUserCode)(this.getSourceFileInfo(d.path)));
1367
+ }
1368
+ _getReferenceResult(sourceFileInfo, filePath, position, allowModuleRename, token) {
1369
+ const execEnv = this._configOptions.findExecEnvironment(filePath);
1370
+ const referencesResult = this._getDeclarationForPosition(sourceFileInfo, position, documentSymbolCollector_1.DocumentSymbolCollectorUseCase.Rename, this._createSourceMapper(execEnv, token), token);
1371
+ if (!referencesResult) {
1372
+ return undefined;
1373
+ }
1374
+ if (allowModuleRename && referencesResult.containsOnlyImportDecls) {
1375
+ return referencesResult;
1376
+ }
1377
+ if (referencesResult.nonImportDeclarations.length === 0) {
1378
+ // There is no symbol we can rename.
1379
+ return undefined;
1380
+ }
1381
+ // Use declarations that doesn't contain import decls.
1382
+ return new referencesProvider_1.ReferencesResult(referencesResult.requiresGlobalSearch, referencesResult.nodeAtOffset, referencesResult.symbolNames, referencesResult.nonImportDeclarations, referencesResult.useCase);
1383
+ }
1384
+ _getDeclarationForPosition(sourceFileInfo, position, useCase, sourceMapper, token, reporter) {
1385
+ // If we have no completed analysis job, there's nothing to do.
1386
+ const parseResults = sourceFileInfo.sourceFile.getParseResults();
1387
+ if (!parseResults) {
1388
+ return undefined;
1389
+ }
1390
+ return referencesProvider_1.ReferencesProvider.getDeclarationForPosition(sourceMapper, parseResults, sourceFileInfo.sourceFile.getFilePath(), position, this._evaluator, reporter, useCase, token, Array.from((0, sourceFileInfoUtils_1.collectImportedByFiles)(sourceFileInfo)).map((fileInfo) => fileInfo.sourceFile));
1391
+ }
1392
+ _processModuleReferences(renameModuleProvider, filteringText, currentFilePath) {
1393
+ var _a;
1394
+ // _sourceFileList contains every user files that match "include" pattern including
1395
+ // py file even if corresponding pyi exists.
1396
+ for (const currentFileInfo of this._sourceFileList) {
1397
+ // Make sure we only touch user code to prevent us
1398
+ // from accidentally changing third party library or type stub.
1399
+ if (!(0, sourceFileInfoUtils_1.isUserCode)(currentFileInfo)) {
1400
+ continue;
1670
1401
  }
1671
- let previousStatement;
1672
- const offset = (0, positionUtils_1.convertPositionToOffset)(position, parseResults.tokenizerOutput.lines);
1673
- if (offset && parseResults.parseTree.statements.length > 0) {
1674
- previousStatement = parseResults.parseTree.statements.reduce((prev, curr) => offset < curr.start ? prev : curr);
1402
+ // If module name isn't mentioned in the current file, skip the file
1403
+ // except the file that got actually renamed/moved.
1404
+ // The file that got moved might have relative import paths we need to update.
1405
+ const filePath = currentFileInfo.sourceFile.getFilePath();
1406
+ const content = (_a = currentFileInfo.sourceFile.getFileContent()) !== null && _a !== void 0 ? _a : '';
1407
+ if (filePath !== currentFilePath && content.indexOf(filteringText) < 0) {
1408
+ continue;
1675
1409
  }
1676
- // This basically try to add some blank lines after the last line with text.
1677
- let linesToAdd = 0;
1678
- if (previousStatement) {
1679
- if ((0, declaration_1.isVariableDeclaration)(decl)) {
1680
- switch (previousStatement.nodeType) {
1681
- case 47 /* StatementList */:
1682
- // Small statement such as call, assignment, etc.
1683
- linesToAdd = 0;
1684
- break;
1685
- case 10 /* Class */:
1686
- case 28 /* Function */:
1687
- linesToAdd = 2;
1688
- break;
1689
- default:
1690
- // any other statement such as if, while, etc. we will add 1 blank line.
1691
- linesToAdd = 1;
1410
+ this._bindFile(currentFileInfo, content);
1411
+ const parseResult = currentFileInfo.sourceFile.getParseResults();
1412
+ if (!parseResult) {
1413
+ continue;
1414
+ }
1415
+ renameModuleProvider.renameReferences(parseResult);
1416
+ // This operation can consume significant memory, so check
1417
+ // for situations where we need to discard the type cache.
1418
+ this._handleMemoryHighUsage();
1419
+ }
1420
+ }
1421
+ _handleMemoryHighUsage() {
1422
+ const cacheUsage = this._cacheManager.getCacheUsage();
1423
+ // If the total cache has exceeded 75%, determine whether we should empty
1424
+ // the cache.
1425
+ if (cacheUsage > 0.75) {
1426
+ const usedHeapRatio = this._cacheManager.getUsedHeapRatio(this._configOptions.verboseOutput ? this._console : undefined);
1427
+ // The type cache uses a Map, which has an absolute limit of 2^24 entries
1428
+ // before it will fail. If we cross the 95% mark, we'll empty the cache.
1429
+ const absoluteMaxCacheEntryCount = (1 << 24) * 0.9;
1430
+ const typeCacheEntryCount = this._evaluator.getTypeCacheEntryCount();
1431
+ // If we use more than 90% of the heap size limit, avoid a crash
1432
+ // by emptying the type cache.
1433
+ if (typeCacheEntryCount > absoluteMaxCacheEntryCount || usedHeapRatio > 0.9) {
1434
+ this._cacheManager.emptyCache(this._console);
1435
+ }
1436
+ }
1437
+ }
1438
+ // Discards all cached parse results and file contents to free up memory.
1439
+ // It does not discard cached index results or diagnostics for files.
1440
+ _discardCachedParseResults() {
1441
+ for (const sourceFileInfo of this._sourceFileList) {
1442
+ sourceFileInfo.sourceFile.dropParseAndBindInfo();
1443
+ }
1444
+ }
1445
+ // Wrapper function that should be used when invoking this._evaluator
1446
+ // with a cancellation token. It handles cancellation exceptions and
1447
+ // any other unexpected exceptions.
1448
+ _runEvaluatorWithCancellationToken(token, callback) {
1449
+ try {
1450
+ if (token) {
1451
+ return this._evaluator.runWithCancellationToken(token, callback);
1452
+ }
1453
+ else {
1454
+ return callback();
1455
+ }
1456
+ }
1457
+ catch (e) {
1458
+ // An unexpected exception occurred, potentially leaving the current evaluator
1459
+ // in an inconsistent state. Discard it and replace it with a fresh one. It is
1460
+ // Cancellation exceptions are known to handle this correctly.
1461
+ if (!cancellationUtils_1.OperationCanceledException.is(e)) {
1462
+ this._createNewEvaluator();
1463
+ }
1464
+ throw e;
1465
+ }
1466
+ }
1467
+ // Returns a list of empty file diagnostic entries for the files
1468
+ // that have been removed. This is needed to clear out the
1469
+ // errors for files that have been deleted or closed.
1470
+ _removeUnneededFiles() {
1471
+ const fileDiagnostics = [];
1472
+ // If a file is no longer tracked, opened or shadowed, it can
1473
+ // be removed from the program.
1474
+ for (let i = 0; i < this._sourceFileList.length;) {
1475
+ const fileInfo = this._sourceFileList[i];
1476
+ if (!this._isFileNeeded(fileInfo)) {
1477
+ fileDiagnostics.push({
1478
+ filePath: fileInfo.sourceFile.getFilePath(),
1479
+ version: fileInfo.sourceFile.getClientVersion(),
1480
+ diagnostics: [],
1481
+ });
1482
+ fileInfo.sourceFile.prepareForClose();
1483
+ this._removeSourceFileFromListAndMap(fileInfo.sourceFile.getFilePath(), i);
1484
+ // Unlink any imports and remove them from the list if
1485
+ // they are no longer referenced.
1486
+ fileInfo.imports.forEach((importedFile) => {
1487
+ const indexToRemove = importedFile.importedBy.findIndex((fi) => fi === fileInfo);
1488
+ if (indexToRemove < 0) {
1489
+ return;
1692
1490
  }
1693
- }
1694
- else {
1695
- linesToAdd = 2;
1696
- }
1491
+ importedFile.importedBy.splice(indexToRemove, 1);
1492
+ // See if we need to remove the imported file because it
1493
+ // is no longer needed. If its index is >= i, it will be
1494
+ // removed when we get to it.
1495
+ if (!this._isFileNeeded(importedFile)) {
1496
+ const indexToRemove = this._sourceFileList.findIndex((fi) => fi === importedFile);
1497
+ if (indexToRemove >= 0 && indexToRemove < i) {
1498
+ fileDiagnostics.push({
1499
+ filePath: importedFile.sourceFile.getFilePath(),
1500
+ version: importedFile.sourceFile.getClientVersion(),
1501
+ diagnostics: [],
1502
+ });
1503
+ importedFile.sourceFile.prepareForClose();
1504
+ this._removeSourceFileFromListAndMap(importedFile.sourceFile.getFilePath(), indexToRemove);
1505
+ i--;
1506
+ }
1507
+ }
1508
+ });
1509
+ // Remove any shadowed files corresponding to this file.
1510
+ fileInfo.shadowedBy.forEach((shadowedFile) => {
1511
+ shadowedFile.shadows = shadowedFile.shadows.filter((f) => f !== fileInfo);
1512
+ });
1513
+ fileInfo.shadowedBy = [];
1697
1514
  }
1698
- // If the position is not at the beginning of the line, we need to add 1 more '\n'
1699
- // to start from blank line.
1700
- linesToAdd += position.character !== 0 ? 1 : 0;
1701
- // If there are already blank lines, we only add the difference.
1702
- const desiredBlankLines = linesToAdd;
1703
- const startingLine = position.character !== 0 ? position.line : position.line - 1;
1704
- for (let i = 0; i < desiredBlankLines; i++) {
1705
- const currentLine = startingLine - i;
1706
- if (currentLine < 0 || !(0, parseTreeUtils_1.isBlankLine)(parseResults, currentLine)) {
1707
- break;
1515
+ else {
1516
+ // If we're showing the user errors only for open files, clear
1517
+ // out the errors for the now-closed file.
1518
+ if (!this._shouldCheckFile(fileInfo) && fileInfo.diagnosticsVersion !== undefined) {
1519
+ fileDiagnostics.push({
1520
+ filePath: fileInfo.sourceFile.getFilePath(),
1521
+ version: fileInfo.sourceFile.getClientVersion(),
1522
+ diagnostics: [],
1523
+ });
1524
+ fileInfo.diagnosticsVersion = undefined;
1708
1525
  }
1709
- linesToAdd--;
1526
+ i++;
1710
1527
  }
1711
- return linesToAdd;
1712
1528
  }
1529
+ return fileDiagnostics;
1713
1530
  }
1714
- clone() {
1715
- var _a;
1716
- const program = new Program(this._importResolver, this._configOptions, this._console, new logTracker_1.LogTracker(this._console, 'Cloned'));
1717
- // Cloned program will use whatever user files the program currently has.
1718
- const userFiles = this.getUserFiles();
1719
- program.setTrackedFiles(userFiles.map((i) => i.sourceFile.getFilePath()));
1720
- program.markAllFilesDirty(true);
1721
- // Make sure we keep editor content (open file) which could be different than one in the file system.
1722
- for (const fileInfo of this.getOpened()) {
1723
- const version = fileInfo.sourceFile.getClientVersion();
1724
- if (version === undefined) {
1725
- continue;
1531
+ _isFileNeeded(fileInfo, skipFileNeededCheck) {
1532
+ if (fileInfo.sourceFile.isFileDeleted()) {
1533
+ return false;
1534
+ }
1535
+ if (!!skipFileNeededCheck || fileInfo.isTracked || fileInfo.isOpenByClient) {
1536
+ return true;
1537
+ }
1538
+ if (fileInfo.shadows.length > 0) {
1539
+ return true;
1540
+ }
1541
+ if (fileInfo.importedBy.length === 0) {
1542
+ return false;
1543
+ }
1544
+ // It's possible for a cycle of files to be imported
1545
+ // by a tracked file but then abandoned. The import cycle
1546
+ // will keep the entire group "alive" if we don't detect
1547
+ // the condition and garbage collect them.
1548
+ return this._isImportNeededRecursive(fileInfo, new Set());
1549
+ }
1550
+ _isImportNeededRecursive(fileInfo, recursionSet) {
1551
+ if (fileInfo.isTracked || fileInfo.isOpenByClient || fileInfo.shadows.length > 0) {
1552
+ return true;
1553
+ }
1554
+ const filePath = (0, pathUtils_1.normalizePathCase)(this.fileSystem, fileInfo.sourceFile.getFilePath());
1555
+ // Avoid infinite recursion.
1556
+ if (recursionSet.has(filePath)) {
1557
+ return false;
1558
+ }
1559
+ recursionSet.add(filePath);
1560
+ for (const importerInfo of fileInfo.importedBy) {
1561
+ if (this._isImportNeededRecursive(importerInfo, recursionSet)) {
1562
+ return true;
1726
1563
  }
1727
- program.setFileOpened(fileInfo.sourceFile.getFilePath(), version, [{ text: fileInfo.sourceFile.getOpenFileContents() }], {
1728
- chainedFilePath: (_a = fileInfo.chainedSourceFile) === null || _a === void 0 ? void 0 : _a.sourceFile.getFilePath(),
1729
- ipythonMode: fileInfo.sourceFile.getIPythonMode(),
1730
- isTracked: fileInfo.isTracked,
1731
- realFilePath: fileInfo.sourceFile.getRealFilePath(),
1732
- });
1733
1564
  }
1734
- return program;
1565
+ return false;
1735
1566
  }
1736
- canRenameSymbolAtPosition(filePath, position, isDefaultWorkspace, allowModuleRename, token) {
1737
- return this._runEvaluatorWithCancellationToken(token, () => {
1738
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1739
- if (!sourceFileInfo) {
1740
- return undefined;
1567
+ _createSourceMapper(execEnv, token, from, mapCompiled, preferStubs) {
1568
+ const sourceMapper = new sourceMapper_1.SourceMapper(this._importResolver, execEnv, this._evaluator, (stubFilePath, implFilePath) => {
1569
+ let stubFileInfo = this.getSourceFileInfo(stubFilePath);
1570
+ if (!stubFileInfo) {
1571
+ // Special case for import statement like "import X.Y". The SourceFile
1572
+ // for X might not be in memory since import `X.Y` only brings in Y.
1573
+ stubFileInfo = this.addInterimFile(stubFilePath);
1741
1574
  }
1742
- this._bindFile(sourceFileInfo);
1743
- const referencesResult = this._getReferenceResult(sourceFileInfo, filePath, position, allowModuleRename, token);
1744
- if (!referencesResult) {
1745
- return undefined;
1575
+ this._addShadowedFile(stubFileInfo, implFilePath);
1576
+ return this.getBoundSourceFile(implFilePath);
1577
+ }, (f) => {
1578
+ let fileInfo = this.getBoundSourceFileInfo(f);
1579
+ if (!fileInfo) {
1580
+ // Special case for import statement like "import X.Y". The SourceFile
1581
+ // for X might not be in memory since import `X.Y` only brings in Y.
1582
+ fileInfo = this.addInterimFile(f);
1583
+ // Even though this file is not referenced by anything, make sure
1584
+ // we have a parse tree for the doc string.
1585
+ this._parseFile(fileInfo, /* content */ undefined, /* force */ true);
1746
1586
  }
1747
- if (referencesResult.containsOnlyImportDecls &&
1748
- !this._supportRenameModule(referencesResult.declarations, isDefaultWorkspace)) {
1749
- return undefined;
1587
+ return fileInfo;
1588
+ }, mapCompiled !== null && mapCompiled !== void 0 ? mapCompiled : false, preferStubs !== null && preferStubs !== void 0 ? preferStubs : false, from, token);
1589
+ return sourceMapper;
1590
+ }
1591
+ _isImportAllowed(importer, importResult, isImportStubFile) {
1592
+ // Don't import native libs. We don't want to track these files,
1593
+ // and we definitely don't want to attempt to parse them.
1594
+ if (importResult.isNativeLib) {
1595
+ return false;
1596
+ }
1597
+ let thirdPartyImportAllowed = this._configOptions.useLibraryCodeForTypes ||
1598
+ (importResult.importType === 1 /* ThirdParty */ && !!importResult.pyTypedInfo) ||
1599
+ (importResult.importType === 2 /* Local */ && importer.isThirdPartyPyTypedPresent);
1600
+ if (importResult.importType === 1 /* ThirdParty */ ||
1601
+ (importer.isThirdPartyImport && importResult.importType === 2 /* Local */)) {
1602
+ if (this._allowedThirdPartyImports) {
1603
+ if (importResult.isRelative) {
1604
+ // If it's a relative import, we'll allow it because the
1605
+ // importer was already deemed to be allowed.
1606
+ thirdPartyImportAllowed = true;
1607
+ }
1608
+ else if (this._allowedThirdPartyImports.some((importName) => {
1609
+ // If this import name is the one that was explicitly
1610
+ // allowed or is a child of that import name,
1611
+ // it's considered allowed.
1612
+ if (importResult.importName === importName) {
1613
+ return true;
1614
+ }
1615
+ if (importResult.importName.startsWith(importName + '.')) {
1616
+ return true;
1617
+ }
1618
+ return false;
1619
+ })) {
1620
+ thirdPartyImportAllowed = true;
1621
+ }
1750
1622
  }
1751
- const renameMode = this._getRenameSymbolMode(sourceFileInfo, referencesResult, isDefaultWorkspace);
1752
- if (renameMode === 'none') {
1753
- return undefined;
1623
+ else if (importer.isThirdPartyImport && this._configOptions.useLibraryCodeForTypes) {
1624
+ // If the importing file is a third-party import, allow importing of
1625
+ // additional third-party imports. This supports the case where the importer
1626
+ // is in a py.typed library but is importing from another non-py.typed
1627
+ // library. It also supports the case where someone explicitly opens a
1628
+ // library source file in their editor.
1629
+ thirdPartyImportAllowed = true;
1754
1630
  }
1755
- // Return the range of the symbol.
1756
- const parseResult = sourceFileInfo.sourceFile.getParseResults();
1757
- return {
1758
- range: (0, positionUtils_1.convertTextRangeToRange)(referencesResult.nodeAtOffset, parseResult.tokenizerOutput.lines),
1759
- declarations: referencesResult.declarations,
1760
- };
1761
- });
1762
- }
1763
- renameSymbolAtPosition(filePath, position, newName, isDefaultWorkspace, allowModuleRename, token) {
1764
- return this._runEvaluatorWithCancellationToken(token, () => {
1765
- var _a;
1766
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1767
- if (!sourceFileInfo) {
1768
- return undefined;
1631
+ else if (importResult.isNamespacePackage &&
1632
+ importResult.filteredImplicitImports.some((implicitImport) => !!implicitImport.pyTypedInfo)) {
1633
+ // Handle the case where the import targets a namespace package, and a
1634
+ // submodule contained within it has a py.typed marker.
1635
+ thirdPartyImportAllowed = true;
1769
1636
  }
1770
- this._bindFile(sourceFileInfo);
1771
- const referencesResult = this._getReferenceResult(sourceFileInfo, filePath, position, allowModuleRename, token);
1772
- if (!referencesResult) {
1773
- return undefined;
1637
+ // Some libraries ship with stub files that import from non-stubs. Don't
1638
+ // explore those.
1639
+ // Don't explore any third-party files unless they're type stub files
1640
+ // or we've been told explicitly that third-party imports are OK.
1641
+ if (!isImportStubFile) {
1642
+ return thirdPartyImportAllowed;
1774
1643
  }
1775
- if (referencesResult.containsOnlyImportDecls) {
1776
- // All decls must be on a user file.
1777
- if (!this._supportRenameModule(referencesResult.declarations, isDefaultWorkspace)) {
1778
- return undefined;
1779
- }
1780
- const moduleInfo = renameModuleProvider_1.RenameModuleProvider.getRenameModulePathInfo(renameModuleProvider_1.RenameModuleProvider.getRenameModulePath(referencesResult.declarations), newName);
1781
- if (!moduleInfo) {
1782
- // Can't figure out module to rename.
1783
- return undefined;
1644
+ }
1645
+ return true;
1646
+ }
1647
+ _updateSourceFileImports(sourceFileInfo, options) {
1648
+ const filesAdded = [];
1649
+ // Get the new list of imports and see if it changed from the last
1650
+ // list of imports for this file.
1651
+ const imports = sourceFileInfo.sourceFile.getImports();
1652
+ // Create a local function that determines whether the import should
1653
+ // be considered a "third-party import" and whether it is coming from
1654
+ // a third-party package that claims to be typed. An import is
1655
+ // considered third-party if it is external to the importer
1656
+ // or is internal but the importer is itself a third-party package.
1657
+ const getThirdPartyImportInfo = (importResult) => {
1658
+ let isThirdPartyImport = false;
1659
+ let isPyTypedPresent = false;
1660
+ if (importResult.importType === 1 /* ThirdParty */) {
1661
+ isThirdPartyImport = true;
1662
+ if (importResult.pyTypedInfo) {
1663
+ isPyTypedPresent = true;
1784
1664
  }
1785
- const editActions = this.renameModule(moduleInfo.filePath, moduleInfo.newFilePath, token);
1786
- // Add file system rename.
1787
- editActions === null || editActions === void 0 ? void 0 : editActions.fileOperations.push({
1788
- kind: 'rename',
1789
- oldFilePath: moduleInfo.filePath,
1790
- newFilePath: moduleInfo.newFilePath,
1665
+ }
1666
+ else if (sourceFileInfo.isThirdPartyImport && importResult.importType === 2 /* Local */) {
1667
+ isThirdPartyImport = true;
1668
+ if (sourceFileInfo.isThirdPartyPyTypedPresent) {
1669
+ isPyTypedPresent = true;
1670
+ }
1671
+ }
1672
+ return {
1673
+ isThirdPartyImport,
1674
+ isPyTypedPresent,
1675
+ };
1676
+ };
1677
+ // Create a map of unique imports, since imports can appear more than once.
1678
+ const newImportPathMap = new Map();
1679
+ // Add chained source file as import if it exists.
1680
+ if (sourceFileInfo.chainedSourceFile) {
1681
+ if (sourceFileInfo.chainedSourceFile.sourceFile.isFileDeleted()) {
1682
+ sourceFileInfo.chainedSourceFile = undefined;
1683
+ }
1684
+ else {
1685
+ const filePath = sourceFileInfo.chainedSourceFile.sourceFile.getFilePath();
1686
+ newImportPathMap.set((0, pathUtils_1.normalizePathCase)(this.fileSystem, filePath), {
1687
+ path: filePath,
1688
+ isTypeshedFile: false,
1689
+ isThirdPartyImport: false,
1690
+ isPyTypedPresent: false,
1791
1691
  });
1792
- if ((0, sourceMapper_1.isStubFile)(moduleInfo.filePath)) {
1793
- const matchingFiles = this._importResolver.getSourceFilesFromStub(moduleInfo.filePath, this._configOptions.findExecEnvironment(filePath),
1794
- /* mapCompiled */ false);
1795
- for (const matchingFile of matchingFiles) {
1796
- const matchingFileInfo = renameModuleProvider_1.RenameModuleProvider.getRenameModulePathInfo(matchingFile, newName);
1797
- if (matchingFileInfo) {
1798
- editActions === null || editActions === void 0 ? void 0 : editActions.fileOperations.push({
1799
- kind: 'rename',
1800
- oldFilePath: matchingFileInfo.filePath,
1801
- newFilePath: matchingFileInfo.newFilePath,
1692
+ }
1693
+ }
1694
+ imports.forEach((importResult) => {
1695
+ if (importResult.isImportFound) {
1696
+ if (this._isImportAllowed(sourceFileInfo, importResult, importResult.isStubFile)) {
1697
+ if (importResult.resolvedPaths.length > 0) {
1698
+ const filePath = importResult.resolvedPaths[importResult.resolvedPaths.length - 1];
1699
+ if (filePath) {
1700
+ const thirdPartyTypeInfo = getThirdPartyImportInfo(importResult);
1701
+ newImportPathMap.set((0, pathUtils_1.normalizePathCase)(this.fileSystem, filePath), {
1702
+ path: filePath,
1703
+ isTypeshedFile: !!importResult.isStdlibTypeshedFile || !!importResult.isThirdPartyTypeshedFile,
1704
+ isThirdPartyImport: thirdPartyTypeInfo.isThirdPartyImport,
1705
+ isPyTypedPresent: thirdPartyTypeInfo.isPyTypedPresent,
1802
1706
  });
1803
1707
  }
1804
1708
  }
1805
1709
  }
1806
- return editActions;
1807
- }
1808
- const renameMode = this._getRenameSymbolMode(sourceFileInfo, referencesResult, isDefaultWorkspace);
1809
- switch (renameMode) {
1810
- case 'singleFileMode':
1811
- sourceFileInfo.sourceFile.addReferences(referencesResult, true, this._evaluator, token);
1812
- break;
1813
- case 'multiFileMode': {
1814
- for (const curSourceFileInfo of this._sourceFileList) {
1815
- // Make sure we only add user code to the references to prevent us
1816
- // from accidentally changing third party library or type stub.
1817
- if ((0, sourceFileInfoUtils_1.isUserCode)(curSourceFileInfo)) {
1818
- // Make sure searching symbol name exists in the file.
1819
- const content = (_a = curSourceFileInfo.sourceFile.getFileContent()) !== null && _a !== void 0 ? _a : '';
1820
- if (!referencesResult.symbolNames.some((s) => content.search(s) >= 0)) {
1821
- continue;
1710
+ importResult.filteredImplicitImports.forEach((implicitImport) => {
1711
+ if (this._isImportAllowed(sourceFileInfo, importResult, implicitImport.isStubFile)) {
1712
+ if (!implicitImport.isNativeLib) {
1713
+ const thirdPartyTypeInfo = getThirdPartyImportInfo(importResult);
1714
+ newImportPathMap.set((0, pathUtils_1.normalizePathCase)(this.fileSystem, implicitImport.path), {
1715
+ path: implicitImport.path,
1716
+ isTypeshedFile: !!importResult.isStdlibTypeshedFile || !!importResult.isThirdPartyTypeshedFile,
1717
+ isThirdPartyImport: thirdPartyTypeInfo.isThirdPartyImport,
1718
+ isPyTypedPresent: thirdPartyTypeInfo.isPyTypedPresent,
1719
+ });
1720
+ }
1721
+ }
1722
+ });
1723
+ // If the stub was found but the non-stub (source) file was not, dump
1724
+ // the failure to the log for diagnostic purposes.
1725
+ if (importResult.nonStubImportResult && !importResult.nonStubImportResult.isImportFound) {
1726
+ // We'll skip this for imports from within stub files and imports that target
1727
+ // stdlib typeshed stubs because many of these are known to not have
1728
+ // associated source files, and we don't want to fill the logs with noise.
1729
+ if (!sourceFileInfo.sourceFile.isStubFile() && !importResult.isStdlibTypeshedFile) {
1730
+ if (options.verboseOutput) {
1731
+ this._console.info(`Could not resolve source for '${importResult.importName}' ` +
1732
+ `in file '${sourceFileInfo.sourceFile.getFilePath()}'`);
1733
+ if (importResult.nonStubImportResult.importFailureInfo) {
1734
+ importResult.nonStubImportResult.importFailureInfo.forEach((diag) => {
1735
+ this._console.info(` ${diag}`);
1736
+ });
1822
1737
  }
1823
- this._bindFile(curSourceFileInfo, content);
1824
- curSourceFileInfo.sourceFile.addReferences(referencesResult, true, this._evaluator, token);
1825
1738
  }
1826
- // This operation can consume significant memory, so check
1827
- // for situations where we need to discard the type cache.
1828
- this._handleMemoryHighUsage();
1829
1739
  }
1830
- break;
1831
1740
  }
1832
- case 'none':
1833
- // Rename is not allowed.
1834
- // ex) rename symbols from libraries.
1835
- return undefined;
1836
- default:
1837
- (0, debug_1.assertNever)(renameMode);
1838
1741
  }
1839
- const edits = [];
1840
- referencesResult.locations.forEach((loc) => {
1841
- edits.push({
1842
- filePath: loc.path,
1843
- range: loc.range,
1844
- replacementText: newName,
1845
- });
1846
- });
1847
- return { edits, fileOperations: [] };
1742
+ else if (options.verboseOutput) {
1743
+ this._console.info(`Could not import '${importResult.importName}' ` +
1744
+ `in file '${sourceFileInfo.sourceFile.getFilePath()}'`);
1745
+ if (importResult.importFailureInfo) {
1746
+ importResult.importFailureInfo.forEach((diag) => {
1747
+ this._console.info(` ${diag}`);
1748
+ });
1749
+ }
1750
+ }
1848
1751
  });
1849
- }
1850
- getCallForPosition(filePath, position, token) {
1851
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1852
- if (!sourceFileInfo) {
1853
- return undefined;
1854
- }
1855
- this._bindFile(sourceFileInfo);
1856
- const execEnv = this._configOptions.findExecEnvironment(filePath);
1857
- const referencesResult = sourceFileInfo.sourceFile.getDeclarationForPosition(this._createSourceMapper(execEnv, token, sourceFileInfo), position, this._evaluator, undefined, documentSymbolCollector_1.DocumentSymbolCollectorUseCase.Reference, token);
1858
- if (!referencesResult || referencesResult.declarations.length === 0) {
1859
- return undefined;
1860
- }
1861
- const { targetDecl, callItemUri, symbolName } = callHierarchyProvider_1.CallHierarchyProvider.getTargetDeclaration(referencesResult, filePath);
1862
- return callHierarchyProvider_1.CallHierarchyProvider.getCallForDeclaration(symbolName, targetDecl, this._evaluator, token, callItemUri);
1863
- }
1864
- getIncomingCallsForPosition(filePath, position, token) {
1865
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1866
- if (!sourceFileInfo) {
1867
- return undefined;
1868
- }
1869
- this._bindFile(sourceFileInfo);
1870
- const execEnv = this._configOptions.findExecEnvironment(filePath);
1871
- const referencesResult = sourceFileInfo.sourceFile.getDeclarationForPosition(this._createSourceMapper(execEnv, token, sourceFileInfo), position, this._evaluator, undefined, documentSymbolCollector_1.DocumentSymbolCollectorUseCase.Reference, token);
1872
- if (!referencesResult || referencesResult.declarations.length === 0) {
1873
- return undefined;
1874
- }
1875
- const { targetDecl, symbolName } = callHierarchyProvider_1.CallHierarchyProvider.getTargetDeclaration(referencesResult, filePath);
1876
- let items = [];
1877
- const sourceFiles = targetDecl.type === 8 /* Alias */ ? [sourceFileInfo] : this._sourceFileList;
1878
- for (const curSourceFileInfo of sourceFiles) {
1879
- if ((0, sourceFileInfoUtils_1.isUserCode)(curSourceFileInfo) || curSourceFileInfo.isOpenByClient) {
1880
- this._bindFile(curSourceFileInfo);
1881
- const itemsToAdd = callHierarchyProvider_1.CallHierarchyProvider.getIncomingCallsForDeclaration(curSourceFileInfo.sourceFile.getFilePath(), symbolName, targetDecl, curSourceFileInfo.sourceFile.getParseResults(), this._evaluator, token);
1882
- if (itemsToAdd) {
1883
- items = items.concat(...itemsToAdd);
1752
+ const updatedImportMap = new Map();
1753
+ sourceFileInfo.imports.forEach((importInfo) => {
1754
+ const oldFilePath = (0, pathUtils_1.normalizePathCase)(this.fileSystem, importInfo.sourceFile.getFilePath());
1755
+ // A previous import was removed.
1756
+ if (!newImportPathMap.has(oldFilePath)) {
1757
+ importInfo.importedBy = importInfo.importedBy.filter((fi) => (0, pathUtils_1.normalizePathCase)(this.fileSystem, fi.sourceFile.getFilePath()) !==
1758
+ (0, pathUtils_1.normalizePathCase)(this.fileSystem, sourceFileInfo.sourceFile.getFilePath()));
1759
+ }
1760
+ else {
1761
+ updatedImportMap.set(oldFilePath, importInfo);
1762
+ }
1763
+ });
1764
+ // See if there are any new imports to be added.
1765
+ newImportPathMap.forEach((importInfo, normalizedImportPath) => {
1766
+ if (!updatedImportMap.has(normalizedImportPath)) {
1767
+ // We found a new import to add. See if it's already part
1768
+ // of the program.
1769
+ let importedFileInfo = this.getSourceFileInfo(importInfo.path);
1770
+ if (!importedFileInfo) {
1771
+ const importName = this._getImportNameForFile(importInfo.path);
1772
+ const sourceFile = new sourceFile_1.SourceFile(this.fileSystem, importInfo.path, importName, importInfo.isThirdPartyImport, importInfo.isPyTypedPresent, this._console, this._logTracker);
1773
+ importedFileInfo = {
1774
+ sourceFile,
1775
+ isTracked: false,
1776
+ isOpenByClient: false,
1777
+ isTypeshedFile: importInfo.isTypeshedFile,
1778
+ isThirdPartyImport: importInfo.isThirdPartyImport,
1779
+ isThirdPartyPyTypedPresent: importInfo.isPyTypedPresent,
1780
+ diagnosticsVersion: undefined,
1781
+ imports: [],
1782
+ importedBy: [],
1783
+ shadows: [],
1784
+ shadowedBy: [],
1785
+ };
1786
+ this._addToSourceFileListAndMap(importedFileInfo);
1787
+ filesAdded.push(importedFileInfo);
1884
1788
  }
1885
- // This operation can consume significant memory, so check
1886
- // for situations where we need to discard the type cache.
1887
- this._handleMemoryHighUsage();
1789
+ importedFileInfo.importedBy.push(sourceFileInfo);
1790
+ updatedImportMap.set(normalizedImportPath, importedFileInfo);
1888
1791
  }
1792
+ });
1793
+ // Update the imports list. It should now map the set of imports
1794
+ // specified by the source file.
1795
+ sourceFileInfo.imports = [];
1796
+ newImportPathMap.forEach((_, path) => {
1797
+ if (this.getSourceFileInfo(path)) {
1798
+ sourceFileInfo.imports.push(this.getSourceFileInfo(path));
1799
+ }
1800
+ });
1801
+ // Resolve the builtins import for the file. This needs to be
1802
+ // analyzed before the file can be analyzed.
1803
+ sourceFileInfo.builtinsImport = undefined;
1804
+ const builtinsImport = sourceFileInfo.sourceFile.getBuiltinsImport();
1805
+ if (builtinsImport && builtinsImport.isImportFound) {
1806
+ const resolvedBuiltinsPath = builtinsImport.resolvedPaths[builtinsImport.resolvedPaths.length - 1];
1807
+ sourceFileInfo.builtinsImport = this.getSourceFileInfo(resolvedBuiltinsPath);
1889
1808
  }
1890
- return items;
1891
- }
1892
- getOutgoingCallsForPosition(filePath, position, token) {
1893
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1894
- if (!sourceFileInfo) {
1895
- return undefined;
1896
- }
1897
- this._bindFile(sourceFileInfo);
1898
- const execEnv = this._configOptions.findExecEnvironment(filePath);
1899
- const referencesResult = sourceFileInfo.sourceFile.getDeclarationForPosition(this._createSourceMapper(execEnv, token, sourceFileInfo), position, this._evaluator, undefined, documentSymbolCollector_1.DocumentSymbolCollectorUseCase.Reference, token);
1900
- if (!referencesResult || referencesResult.declarations.length === 0) {
1901
- return undefined;
1902
- }
1903
- const { targetDecl } = callHierarchyProvider_1.CallHierarchyProvider.getTargetDeclaration(referencesResult, filePath);
1904
- return callHierarchyProvider_1.CallHierarchyProvider.getOutgoingCallsForDeclaration(targetDecl, sourceFileInfo.sourceFile.getParseResults(), this._evaluator, token);
1905
- }
1906
- performQuickAction(filePath, command, args, token) {
1907
- const sourceFileInfo = this.getSourceFileInfo(filePath);
1908
- if (!sourceFileInfo) {
1909
- return undefined;
1809
+ // Resolve the ipython display import for the file. This needs to be
1810
+ // analyzed before the file can be analyzed.
1811
+ sourceFileInfo.ipythonDisplayImport = undefined;
1812
+ const ipythonDisplayImport = sourceFileInfo.sourceFile.getIPythonDisplayImport();
1813
+ if (ipythonDisplayImport && ipythonDisplayImport.isImportFound) {
1814
+ const resolvedIPythonDisplayPath = ipythonDisplayImport.resolvedPaths[ipythonDisplayImport.resolvedPaths.length - 1];
1815
+ sourceFileInfo.ipythonDisplayImport = this.getSourceFileInfo(resolvedIPythonDisplayPath);
1910
1816
  }
1911
- this._bindFile(sourceFileInfo);
1912
- return sourceFileInfo.sourceFile.performQuickAction(command, args, token);
1913
- }
1914
- // Returns a value from 0 to 1 (or more) indicating how "full" the cache is
1915
- // relative to some predetermined high-water mark. We'll compute this value
1916
- // based on two easy-to-compute metrics: the number of entries in the type
1917
- // cache and the number of parsed files.
1918
- getCacheUsage() {
1919
- const typeCacheEntryCount = this._evaluator.getTypeCacheEntryCount();
1920
- const entryCountRatio = typeCacheEntryCount / 750000;
1921
- const fileCountRatio = this._parsedFileCount / 1000;
1922
- return Math.max(entryCountRatio, fileCountRatio);
1817
+ return filesAdded;
1923
1818
  }
1924
- // Discards any cached information associated with this program.
1925
- emptyCache() {
1926
- this._createNewEvaluator();
1927
- this._discardCachedParseResults();
1928
- this._parsedFileCount = 0;
1929
- extensibility_1.Extensions.getProgramExtensions(this.rootPath).forEach((e) => (e.clearCache ? e.clearCache() : null));
1819
+ _removeSourceFileFromListAndMap(filePath, indexToRemove) {
1820
+ this._sourceFileMap.delete((0, pathUtils_1.normalizePathCase)(this.fileSystem, filePath));
1821
+ this._sourceFileList.splice(indexToRemove, 1);
1930
1822
  }
1931
- test_createSourceMapper(execEnv, from) {
1932
- return this._createSourceMapper(execEnv, vscode_languageserver_1.CancellationToken.None, /* from */ from, /* mapCompiled */ false);
1823
+ _addToSourceFileListAndMap(fileInfo) {
1824
+ const filePath = (0, pathUtils_1.normalizePathCase)(this.fileSystem, fileInfo.sourceFile.getFilePath());
1825
+ // We should never add a file with the same path twice.
1826
+ (0, debug_1.assert)(!this._sourceFileMap.has(filePath));
1827
+ this._sourceFileList.push(fileInfo);
1828
+ this._sourceFileMap.set(filePath, fileInfo);
1933
1829
  }
1934
- _getRenameSymbolMode(sourceFileInfo, referencesResult, isDefaultWorkspace) {
1935
- // We have 2 different cases
1936
- // Single file mode.
1937
- // 1. rename on default workspace (ex, standalone file mode).
1938
- // 2. rename local symbols.
1939
- // 3. rename symbols defined in the non user open file.
1940
- //
1941
- // and Multi file mode.
1942
- // 1. rename public symbols defined in user files on regular workspace (ex, open folder mode).
1943
- const userFile = (0, sourceFileInfoUtils_1.isUserCode)(sourceFileInfo);
1944
- if (isDefaultWorkspace ||
1945
- (userFile && !referencesResult.requiresGlobalSearch) ||
1946
- (!userFile &&
1947
- sourceFileInfo.isOpenByClient &&
1948
- referencesResult.declarations.every((d) => this.getSourceFileInfo(d.path) === sourceFileInfo))) {
1949
- return 'singleFileMode';
1830
+ static _getPrintTypeFlags(configOptions) {
1831
+ let flags = 0 /* None */;
1832
+ if (configOptions.diagnosticRuleSet.printUnknownAsAny) {
1833
+ flags |= 1 /* PrintUnknownWithAny */;
1834
+ }
1835
+ if (configOptions.diagnosticRuleSet.omitConditionalConstraint) {
1836
+ flags |= 64 /* OmitConditionalConstraint */;
1837
+ }
1838
+ if (configOptions.diagnosticRuleSet.omitTypeArgsIfUnknown) {
1839
+ flags |= 2 /* OmitTypeArgumentsIfUnknown */;
1840
+ }
1841
+ if (configOptions.diagnosticRuleSet.omitUnannotatedParamType) {
1842
+ flags |= 4 /* OmitUnannotatedParamType */;
1950
1843
  }
1951
- if (referencesResult.declarations.every((d) => (0, sourceFileInfoUtils_1.isUserCode)(this.getSourceFileInfo(d.path)))) {
1952
- return 'multiFileMode';
1844
+ if (configOptions.diagnosticRuleSet.pep604Printing) {
1845
+ flags |= 8 /* PEP604 */;
1953
1846
  }
1954
- // Rename is not allowed.
1955
- // ex) rename symbols from libraries.
1956
- return 'none';
1847
+ return flags;
1957
1848
  }
1958
- _supportRenameModule(declarations, isDefaultWorkspace) {
1959
- // Rename module is not supported for standalone file and all decls must be on a user file.
1960
- return !isDefaultWorkspace && declarations.every((d) => (0, sourceFileInfoUtils_1.isUserCode)(this.getSourceFileInfo(d.path)));
1849
+ _getImportNameForFile(filePath) {
1850
+ // We allow illegal module names (e.g. names that include "-" in them)
1851
+ // because we want a unique name for each module even if it cannot be
1852
+ // imported through an "import" statement. It's important to have a
1853
+ // unique name in case two modules declare types with the same local
1854
+ // name. The type checker uses the fully-qualified (unique) module name
1855
+ // to differentiate between such types.
1856
+ const moduleNameAndType = this._importResolver.getModuleNameForImport(filePath, this._configOptions.getDefaultExecEnvironment(),
1857
+ /* allowIllegalModuleName */ true);
1858
+ return moduleNameAndType.moduleName;
1961
1859
  }
1962
- _getReferenceResult(sourceFileInfo, filePath, position, allowModuleRename, token) {
1963
- const execEnv = this._configOptions.findExecEnvironment(filePath);
1964
- const referencesResult = this._getDeclarationForPosition(sourceFileInfo, position, documentSymbolCollector_1.DocumentSymbolCollectorUseCase.Rename, this._createSourceMapper(execEnv, token), token);
1965
- if (!referencesResult) {
1966
- return undefined;
1860
+ // A "shadowed" file is a python source file that has been added to the program because
1861
+ // it "shadows" a type stub file for purposes of finding doc strings and definitions.
1862
+ // We need to track the relationship so if the original type stub is removed from the
1863
+ // program, we can remove the corresponding shadowed file and any files it imports.
1864
+ _addShadowedFile(stubFile, shadowImplPath) {
1865
+ let shadowFileInfo = this.getSourceFileInfo(shadowImplPath);
1866
+ if (!shadowFileInfo) {
1867
+ shadowFileInfo = this.addInterimFile(shadowImplPath);
1967
1868
  }
1968
- if (allowModuleRename && referencesResult.containsOnlyImportDecls) {
1969
- return referencesResult;
1869
+ if (!shadowFileInfo.shadows.includes(stubFile)) {
1870
+ shadowFileInfo.shadows.push(stubFile);
1970
1871
  }
1971
- if (referencesResult.nonImportDeclarations.length === 0) {
1972
- // There is no symbol we can rename.
1973
- return undefined;
1872
+ if (!stubFile.shadowedBy.includes(shadowFileInfo)) {
1873
+ stubFile.shadowedBy.push(shadowFileInfo);
1974
1874
  }
1975
- // Use declarations that doesn't contain import decls.
1976
- return new referencesProvider_1.ReferencesResult(referencesResult.requiresGlobalSearch, referencesResult.nodeAtOffset, referencesResult.symbolNames, referencesResult.nonImportDeclarations, referencesResult.useCase);
1977
- }
1978
- _getDeclarationForPosition(sourceFileInfo, position, useCase, sourceMapper, token, reporter) {
1979
- return sourceFileInfo.sourceFile.getDeclarationForPosition(sourceMapper, position, this._evaluator, reporter, useCase, token, Array.from((0, sourceFileInfoUtils_1.collectImportedByFiles)(sourceFileInfo)).map((fileInfo) => fileInfo.sourceFile));
1875
+ return shadowFileInfo.sourceFile;
1980
1876
  }
1981
- _processModuleReferences(renameModuleProvider, filteringText, currentFilePath) {
1982
- var _a;
1983
- // _sourceFileList contains every user files that match "include" pattern including
1984
- // py file even if corresponding pyi exists.
1985
- for (const currentFileInfo of this._sourceFileList) {
1986
- // Make sure we only touch user code to prevent us
1987
- // from accidentally changing third party library or type stub.
1988
- if (!(0, sourceFileInfoUtils_1.isUserCode)(currentFileInfo)) {
1989
- continue;
1990
- }
1991
- // If module name isn't mentioned in the current file, skip the file
1992
- // except the file that got actually renamed/moved.
1993
- // The file that got moved might have relative import paths we need to update.
1994
- const filePath = currentFileInfo.sourceFile.getFilePath();
1995
- const content = (_a = currentFileInfo.sourceFile.getFileContent()) !== null && _a !== void 0 ? _a : '';
1996
- if (filePath !== currentFilePath && content.indexOf(filteringText) < 0) {
1997
- continue;
1998
- }
1999
- this._bindFile(currentFileInfo, content);
2000
- const parseResult = currentFileInfo.sourceFile.getParseResults();
2001
- if (!parseResult) {
2002
- continue;
2003
- }
2004
- renameModuleProvider.renameReferences(parseResult);
2005
- // This operation can consume significant memory, so check
2006
- // for situations where we need to discard the type cache.
2007
- this._handleMemoryHighUsage();
2008
- }
1877
+ _createInterimFileInfo(filePath) {
1878
+ const importName = this._getImportNameForFile(filePath);
1879
+ const sourceFile = new sourceFile_1.SourceFile(this.fileSystem, filePath, importName,
1880
+ /* isThirdPartyImport */ false,
1881
+ /* isInPyTypedPackage */ false, this._console, this._logTracker);
1882
+ const sourceFileInfo = {
1883
+ sourceFile,
1884
+ isTracked: false,
1885
+ isOpenByClient: false,
1886
+ isTypeshedFile: false,
1887
+ isThirdPartyImport: false,
1888
+ isThirdPartyPyTypedPresent: false,
1889
+ diagnosticsVersion: undefined,
1890
+ imports: [],
1891
+ importedBy: [],
1892
+ shadows: [],
1893
+ shadowedBy: [],
1894
+ };
1895
+ return sourceFileInfo;
2009
1896
  }
2010
- _handleMemoryHighUsage() {
2011
- const cacheUsage = this._cacheManager.getCacheUsage();
2012
- // If the total cache has exceeded 75%, determine whether we should empty
2013
- // the cache.
2014
- if (cacheUsage > 0.75) {
2015
- const usedHeapRatio = this._cacheManager.getUsedHeapRatio(this._configOptions.verboseOutput ? this._console : undefined);
2016
- // The type cache uses a Map, which has an absolute limit of 2^24 entries
2017
- // before it will fail. If we cross the 95% mark, we'll empty the cache.
2018
- const absoluteMaxCacheEntryCount = (1 << 24) * 0.9;
2019
- const typeCacheEntryCount = this._evaluator.getTypeCacheEntryCount();
2020
- // If we use more than 90% of the heap size limit, avoid a crash
2021
- // by emptying the type cache.
2022
- if (typeCacheEntryCount > absoluteMaxCacheEntryCount || usedHeapRatio > 0.9) {
2023
- this._cacheManager.emptyCache(this._console);
2024
- }
1897
+ _createNewEvaluator() {
1898
+ if (this._evaluator) {
1899
+ // We shouldn't need to call this, but there appears to be a bug
1900
+ // in the v8 garbage collector where it's unable to resolve orphaned
1901
+ // objects without us giving it some assistance.
1902
+ this._evaluator.disposeEvaluator();
2025
1903
  }
1904
+ this._evaluator = (0, typeEvaluatorWithTracker_1.createTypeEvaluatorWithTracker)(this._lookUpImport, {
1905
+ printTypeFlags: Program._getPrintTypeFlags(this._configOptions),
1906
+ logCalls: this._configOptions.logTypeEvaluationTime,
1907
+ minimumLoggingThreshold: this._configOptions.typeEvaluationTimeThreshold,
1908
+ evaluateUnknownImportsAsAny: !!this._configOptions.evaluateUnknownImportsAsAny,
1909
+ verifyTypeCacheEvaluatorFlags: !!this._configOptions.internalTestMode,
1910
+ }, this._logTracker, this._configOptions.logTypeEvaluationTime
1911
+ ? (0, tracePrinter_1.createTracePrinter)(this._importResolver.getImportRoots(this._configOptions.findExecEnvironment(this._configOptions.projectRoot)))
1912
+ : undefined);
1913
+ return this._evaluator;
2026
1914
  }
2027
- // Discards all cached parse results and file contents to free up memory.
2028
- // It does not discard cached index results or diagnostics for files.
2029
- _discardCachedParseResults() {
2030
- for (const sourceFileInfo of this._sourceFileList) {
2031
- sourceFileInfo.sourceFile.dropParseAndBindInfo();
1915
+ _parseFile(fileToParse, content, skipFileNeededCheck) {
1916
+ if (!this._isFileNeeded(fileToParse, skipFileNeededCheck) || !fileToParse.sourceFile.isParseRequired()) {
1917
+ return;
2032
1918
  }
2033
- }
2034
- // Wrapper function that should be used when invoking this._evaluator
2035
- // with a cancellation token. It handles cancellation exceptions and
2036
- // any other unexpected exceptions.
2037
- _runEvaluatorWithCancellationToken(token, callback) {
2038
- try {
2039
- if (token) {
2040
- return this._evaluator.runWithCancellationToken(token, callback);
2041
- }
2042
- else {
2043
- return callback();
2044
- }
1919
+ // SourceFile.parse should only be called here in the program, as calling it
1920
+ // elsewhere could break the entire dependency graph maintained by the program.
1921
+ // Other parts of the program should use _parseFile to create ParseResults from
1922
+ // the sourceFile. For standalone parseResults, use parseFile or the Parser directly.
1923
+ if (fileToParse.sourceFile.parse(this._configOptions, this._importResolver, content)) {
1924
+ this._parsedFileCount++;
1925
+ this._updateSourceFileImports(fileToParse, this._configOptions);
2045
1926
  }
2046
- catch (e) {
2047
- // An unexpected exception occurred, potentially leaving the current evaluator
2048
- // in an inconsistent state. Discard it and replace it with a fresh one. It is
2049
- // Cancellation exceptions are known to handle this correctly.
2050
- if (!cancellationUtils_1.OperationCanceledException.is(e)) {
2051
- this._createNewEvaluator();
2052
- }
2053
- throw e;
1927
+ if (fileToParse.sourceFile.isFileDeleted()) {
1928
+ fileToParse.isTracked = false;
1929
+ // Mark any files that depend on this file as dirty
1930
+ // also. This will retrigger analysis of these other files.
1931
+ const markDirtySet = new Set();
1932
+ this._markFileDirtyRecursive(fileToParse, markDirtySet);
1933
+ // Invalidate the import resolver's cache as well.
1934
+ this._importResolver.invalidateCache();
2054
1935
  }
2055
1936
  }
2056
- // Returns a list of empty file diagnostic entries for the files
2057
- // that have been removed. This is needed to clear out the
2058
- // errors for files that have been deleted or closed.
2059
- _removeUnneededFiles() {
2060
- const fileDiagnostics = [];
2061
- // If a file is no longer tracked, opened or shadowed, it can
2062
- // be removed from the program.
2063
- for (let i = 0; i < this._sourceFileList.length;) {
2064
- const fileInfo = this._sourceFileList[i];
2065
- if (!this._isFileNeeded(fileInfo)) {
2066
- fileDiagnostics.push({
2067
- filePath: fileInfo.sourceFile.getFilePath(),
2068
- version: fileInfo.sourceFile.getClientVersion(),
2069
- diagnostics: [],
2070
- });
2071
- fileInfo.sourceFile.prepareForClose();
2072
- this._removeSourceFileFromListAndMap(fileInfo.sourceFile.getFilePath(), i);
2073
- // Unlink any imports and remove them from the list if
2074
- // they are no longer referenced.
2075
- fileInfo.imports.forEach((importedFile) => {
2076
- const indexToRemove = importedFile.importedBy.findIndex((fi) => fi === fileInfo);
2077
- if (indexToRemove < 0) {
2078
- return;
2079
- }
2080
- importedFile.importedBy.splice(indexToRemove, 1);
2081
- // See if we need to remove the imported file because it
2082
- // is no longer needed. If its index is >= i, it will be
2083
- // removed when we get to it.
2084
- if (!this._isFileNeeded(importedFile)) {
2085
- const indexToRemove = this._sourceFileList.findIndex((fi) => fi === importedFile);
2086
- if (indexToRemove >= 0 && indexToRemove < i) {
2087
- fileDiagnostics.push({
2088
- filePath: importedFile.sourceFile.getFilePath(),
2089
- version: importedFile.sourceFile.getClientVersion(),
2090
- diagnostics: [],
2091
- });
2092
- importedFile.sourceFile.prepareForClose();
2093
- this._removeSourceFileFromListAndMap(importedFile.sourceFile.getFilePath(), indexToRemove);
2094
- i--;
2095
- }
2096
- }
2097
- });
2098
- // Remove any shadowed files corresponding to this file.
2099
- fileInfo.shadowedBy.forEach((shadowedFile) => {
2100
- shadowedFile.shadows = shadowedFile.shadows.filter((f) => f !== fileInfo);
2101
- });
2102
- fileInfo.shadowedBy = [];
2103
- }
2104
- else {
2105
- // If we're showing the user errors only for open files, clear
2106
- // out the errors for the now-closed file.
2107
- if (!this._shouldCheckFile(fileInfo) && fileInfo.diagnosticsVersion !== undefined) {
2108
- fileDiagnostics.push({
2109
- filePath: fileInfo.sourceFile.getFilePath(),
2110
- version: fileInfo.sourceFile.getClientVersion(),
2111
- diagnostics: [],
2112
- });
2113
- fileInfo.diagnosticsVersion = undefined;
2114
- }
2115
- i++;
2116
- }
1937
+ _getImplicitImports(file) {
1938
+ var _a, _b;
1939
+ // If file is not parsed, then chainedSourceFile, ipythonDisplayImport,
1940
+ // builtinsImport might not exist or incorrect.
1941
+ // They will be added when _parseFile is called and _updateSourceFileImports ran.
1942
+ if (file.builtinsImport === file) {
1943
+ return undefined;
2117
1944
  }
2118
- return fileDiagnostics;
1945
+ const tryReturn = (input) => {
1946
+ if (!input || input.sourceFile.isFileDeleted()) {
1947
+ return undefined;
1948
+ }
1949
+ return input;
1950
+ };
1951
+ return (_b = (_a = tryReturn(file.chainedSourceFile)) !== null && _a !== void 0 ? _a : tryReturn(file.ipythonDisplayImport)) !== null && _b !== void 0 ? _b : file.builtinsImport;
2119
1952
  }
2120
- _isFileNeeded(fileInfo) {
2121
- if (fileInfo.sourceFile.isFileDeleted()) {
2122
- return false;
2123
- }
2124
- if (fileInfo.isTracked || fileInfo.isOpenByClient) {
2125
- return true;
1953
+ _bindImplicitImports(fileToAnalyze, skipFileNeededCheck) {
1954
+ // Get all of the potential imports for this file.
1955
+ const implicitImports = [];
1956
+ const implicitSet = new Set();
1957
+ let nextImplicitImport = this._getImplicitImports(fileToAnalyze);
1958
+ while (nextImplicitImport) {
1959
+ const implicitPath = nextImplicitImport.sourceFile.getFilePath();
1960
+ if (implicitSet.has(implicitPath)) {
1961
+ // We've found a cycle. Break out of the loop.
1962
+ debug.fail(`Found a cycle in implicit imports files for ${implicitPath}`);
1963
+ }
1964
+ implicitSet.add(implicitPath);
1965
+ implicitImports.push(nextImplicitImport);
1966
+ this._parseFile(nextImplicitImport, /* content */ undefined, skipFileNeededCheck);
1967
+ nextImplicitImport = this._getImplicitImports(nextImplicitImport);
2126
1968
  }
2127
- if (fileInfo.shadows.length > 0) {
2128
- return true;
1969
+ if (implicitImports.length === 0) {
1970
+ return;
2129
1971
  }
2130
- if (fileInfo.importedBy.length === 0) {
2131
- return false;
1972
+ // Go in reverse order (so top of chain is first).
1973
+ let implicitImport = implicitImports.pop();
1974
+ while (implicitImport) {
1975
+ // Bind this file, but don't recurse into its imports.
1976
+ this._bindFile(implicitImport, undefined, skipFileNeededCheck, /* isImplicitImport */ true);
1977
+ implicitImport = implicitImports.pop();
2132
1978
  }
2133
- // It's possible for a cycle of files to be imported
2134
- // by a tracked file but then abandoned. The import cycle
2135
- // will keep the entire group "alive" if we don't detect
2136
- // the condition and garbage collect them.
2137
- return this._isImportNeededRecursive(fileInfo, new Set());
2138
1979
  }
2139
- _isImportNeededRecursive(fileInfo, recursionSet) {
2140
- if (fileInfo.isTracked || fileInfo.isOpenByClient || fileInfo.shadows.length > 0) {
2141
- return true;
2142
- }
2143
- const filePath = (0, pathUtils_1.normalizePathCase)(this._fs, fileInfo.sourceFile.getFilePath());
2144
- // Avoid infinite recursion.
2145
- if (recursionSet.has(filePath)) {
2146
- return false;
1980
+ // Binds the specified file and all of its dependencies, recursively. If
1981
+ // it runs out of time, it returns true. If it completes, it returns false.
1982
+ _bindFile(fileToAnalyze, content, skipFileNeededCheck, isImplicitImport) {
1983
+ var _a, _b;
1984
+ if (!this._isFileNeeded(fileToAnalyze, skipFileNeededCheck) || !fileToAnalyze.sourceFile.isBindingRequired()) {
1985
+ return;
2147
1986
  }
2148
- recursionSet.add(filePath);
2149
- for (const importerInfo of fileInfo.importedBy) {
2150
- if (this._isImportNeededRecursive(importerInfo, recursionSet)) {
2151
- return true;
1987
+ this._parseFile(fileToAnalyze, content, skipFileNeededCheck);
1988
+ // Create a function to get the scope info.
1989
+ const getScopeIfAvailable = (fileInfo) => {
1990
+ if (!fileInfo || fileInfo === fileToAnalyze) {
1991
+ return undefined;
1992
+ }
1993
+ // If the file was deleted, there's no scope to return.
1994
+ if (fileInfo.sourceFile.isFileDeleted()) {
1995
+ return undefined;
1996
+ }
1997
+ const parseResults = fileInfo.sourceFile.getParseResults();
1998
+ if (!parseResults) {
1999
+ return undefined;
2000
+ }
2001
+ // File should already be bound because of the chained file binding above.
2002
+ const scope = AnalyzerNodeInfo.getScope(parseResults.parseTree);
2003
+ return scope;
2004
+ };
2005
+ let builtinsScope;
2006
+ if (fileToAnalyze.builtinsImport && fileToAnalyze.builtinsImport !== fileToAnalyze) {
2007
+ // Bind all of the implicit imports first. So we don't recurse into them.
2008
+ if (!isImplicitImport) {
2009
+ this._bindImplicitImports(fileToAnalyze);
2152
2010
  }
2011
+ // If it is not builtin module itself, we need to parse and bind
2012
+ // the ipython display import if required. Otherwise, get builtin module.
2013
+ builtinsScope =
2014
+ (_b = (_a = getScopeIfAvailable(fileToAnalyze.chainedSourceFile)) !== null && _a !== void 0 ? _a : getScopeIfAvailable(fileToAnalyze.ipythonDisplayImport)) !== null && _b !== void 0 ? _b : getScopeIfAvailable(fileToAnalyze.builtinsImport);
2153
2015
  }
2154
- return false;
2016
+ let futureImports = fileToAnalyze.sourceFile.getParseResults().futureImports;
2017
+ if (fileToAnalyze.chainedSourceFile) {
2018
+ futureImports = this._getEffectiveFutureImports(futureImports, fileToAnalyze.chainedSourceFile);
2019
+ }
2020
+ fileToAnalyze.effectiveFutureImports = futureImports.size > 0 ? futureImports : undefined;
2021
+ fileToAnalyze.sourceFile.bind(this._configOptions, this._lookUpImport, builtinsScope, futureImports);
2155
2022
  }
2156
- _createSourceMapper(execEnv, token, from, mapCompiled, preferStubs) {
2157
- const sourceMapper = new sourceMapper_1.SourceMapper(this._importResolver, execEnv, this._evaluator, (stubFilePath, implFilePath) => {
2158
- let stubFileInfo = this.getSourceFileInfo(stubFilePath);
2159
- if (!stubFileInfo) {
2160
- // Special case for import statement like "import X.Y". The SourceFile
2161
- // for X might not be in memory since import `X.Y` only brings in Y.
2162
- stubFileInfo = this.addInterimFile(stubFilePath);
2023
+ _getEffectiveFutureImports(futureImports, chainedSourceFile) {
2024
+ var _a;
2025
+ const effectiveFutureImports = new Set(futureImports);
2026
+ (_a = chainedSourceFile.effectiveFutureImports) === null || _a === void 0 ? void 0 : _a.forEach((value) => {
2027
+ effectiveFutureImports.add(value);
2028
+ });
2029
+ return effectiveFutureImports;
2030
+ }
2031
+ // Build a map of all modules within this program and the module-
2032
+ // level scope that contains the symbol table for the module.
2033
+ _buildModuleSymbolsMap(sourceFileToExclude, libraryMap, includeSymbolsFromIndices, token) {
2034
+ // If we have library map, always use the map for library symbols.
2035
+ const predicate = (s) => {
2036
+ if (!libraryMap) {
2037
+ // We don't have any prebuilt indices, so we need to include
2038
+ // all files.
2039
+ return true;
2163
2040
  }
2164
- this._addShadowedFile(stubFileInfo, implFilePath);
2165
- return this.getBoundSourceFile(implFilePath);
2166
- }, (f) => {
2167
- let fileInfo = this.getBoundSourceFileInfo(f);
2168
- if (!fileInfo) {
2169
- // Special case for import statement like "import X.Y". The SourceFile
2170
- // for X might not be in memory since import `X.Y` only brings in Y.
2171
- fileInfo = this.addInterimFile(f);
2172
- // Even though this file is not referenced by anything, make sure
2173
- // we have a parse tree for the doc string.
2174
- this._parseFile(fileInfo, /* content */ undefined, /* force */ true);
2041
+ if (!this._configOptions.indexing) {
2042
+ // We have some prebuilt indices such as stdlib, but indexing is disabled.
2043
+ // Include files we don't have prebuilt indices.
2044
+ return libraryMap.get(s.sourceFile.getFilePath()) === undefined;
2175
2045
  }
2176
- return fileInfo;
2177
- }, mapCompiled !== null && mapCompiled !== void 0 ? mapCompiled : false, preferStubs !== null && preferStubs !== void 0 ? preferStubs : false, from, token);
2178
- return sourceMapper;
2046
+ // We have prebuilt indices for third party libraries. Include only
2047
+ // user files.
2048
+ return (0, sourceFileInfoUtils_1.isUserCode)(s);
2049
+ };
2050
+ // Only include import alias from user files if indexing is off for now.
2051
+ // Currently, when indexing is off, we don't do import alias deduplication, so
2052
+ // adding import alias is cheap. But when indexing is on, we do deduplication, which
2053
+ // require resolveAliasDeclaration that can cause more files to be parsed and bound.
2054
+ return (0, autoImporter_1.buildModuleSymbolsMap)(this._sourceFileList.filter((s) => s !== sourceFileToExclude && predicate(s)), includeSymbolsFromIndices, token);
2179
2055
  }
2180
- _isImportAllowed(importer, importResult, isImportStubFile) {
2181
- // Don't import native libs. We don't want to track these files,
2182
- // and we definitely don't want to attempt to parse them.
2183
- if (importResult.isNativeLib) {
2184
- return false;
2056
+ _shouldCheckFile(fileInfo) {
2057
+ // Always do a full checking for a file that's open in the editor.
2058
+ if (fileInfo.isOpenByClient) {
2059
+ return true;
2185
2060
  }
2186
- let thirdPartyImportAllowed = this._configOptions.useLibraryCodeForTypes ||
2187
- (importResult.importType === 1 /* ThirdParty */ && !!importResult.pyTypedInfo) ||
2188
- (importResult.importType === 2 /* Local */ && importer.isThirdPartyPyTypedPresent);
2189
- if (importResult.importType === 1 /* ThirdParty */ ||
2190
- (importer.isThirdPartyImport && importResult.importType === 2 /* Local */)) {
2191
- if (this._allowedThirdPartyImports) {
2192
- if (importResult.isRelative) {
2193
- // If it's a relative import, we'll allow it because the
2194
- // importer was already deemed to be allowed.
2195
- thirdPartyImportAllowed = true;
2196
- }
2197
- else if (this._allowedThirdPartyImports.some((importName) => {
2198
- // If this import name is the one that was explicitly
2199
- // allowed or is a child of that import name,
2200
- // it's considered allowed.
2201
- if (importResult.importName === importName) {
2202
- return true;
2203
- }
2204
- if (importResult.importName.startsWith(importName + '.')) {
2205
- return true;
2206
- }
2207
- return false;
2208
- })) {
2209
- thirdPartyImportAllowed = true;
2210
- }
2211
- }
2212
- else if (importer.isThirdPartyImport && this._configOptions.useLibraryCodeForTypes) {
2213
- // If the importing file is a third-party import, allow importing of
2214
- // additional third-party imports. This supports the case where the importer
2215
- // is in a py.typed library but is importing from another non-py.typed
2216
- // library. It also supports the case where someone explicitly opens a
2217
- // library source file in their editor.
2218
- thirdPartyImportAllowed = true;
2219
- }
2220
- else if (importResult.isNamespacePackage &&
2221
- importResult.filteredImplicitImports.some((implicitImport) => !!implicitImport.pyTypedInfo)) {
2222
- // Handle the case where the import targets a namespace package, and a
2223
- // submodule contained within it has a py.typed marker.
2224
- thirdPartyImportAllowed = true;
2225
- }
2226
- // Some libraries ship with stub files that import from non-stubs. Don't
2227
- // explore those.
2228
- // Don't explore any third-party files unless they're type stub files
2229
- // or we've been told explicitly that third-party imports are OK.
2230
- if (!isImportStubFile) {
2231
- return thirdPartyImportAllowed;
2232
- }
2061
+ // If the file isn't currently open, only perform full checking for
2062
+ // files that are tracked, and only if the checkOnlyOpenFiles is disabled.
2063
+ if (!this._configOptions.checkOnlyOpenFiles && fileInfo.isTracked) {
2064
+ return true;
2233
2065
  }
2234
- return true;
2066
+ return false;
2235
2067
  }
2236
- _updateSourceFileImports(sourceFileInfo, options) {
2237
- const filesAdded = [];
2238
- // Get the new list of imports and see if it changed from the last
2239
- // list of imports for this file.
2240
- const imports = sourceFileInfo.sourceFile.getImports();
2241
- // Create a local function that determines whether the import should
2242
- // be considered a "third-party import" and whether it is coming from
2243
- // a third-party package that claims to be typed. An import is
2244
- // considered third-party if it is external to the importer
2245
- // or is internal but the importer is itself a third-party package.
2246
- const getThirdPartyImportInfo = (importResult) => {
2247
- let isThirdPartyImport = false;
2248
- let isPyTypedPresent = false;
2249
- if (importResult.importType === 1 /* ThirdParty */) {
2250
- isThirdPartyImport = true;
2251
- if (importResult.pyTypedInfo) {
2252
- isPyTypedPresent = true;
2253
- }
2068
+ _checkTypes(fileToCheck, token) {
2069
+ return this._logTracker.log(`analyzing: ${fileToCheck.sourceFile.getFilePath()}`, (logState) => {
2070
+ // If the file isn't needed because it was eliminated from the
2071
+ // transitive closure or deleted, skip the file rather than wasting
2072
+ // time on it.
2073
+ if (!this._isFileNeeded(fileToCheck)) {
2074
+ logState.suppress();
2075
+ return false;
2254
2076
  }
2255
- else if (sourceFileInfo.isThirdPartyImport && importResult.importType === 2 /* Local */) {
2256
- isThirdPartyImport = true;
2257
- if (sourceFileInfo.isThirdPartyPyTypedPresent) {
2258
- isPyTypedPresent = true;
2259
- }
2077
+ if (!fileToCheck.sourceFile.isCheckingRequired()) {
2078
+ logState.suppress();
2079
+ return false;
2260
2080
  }
2261
- return {
2262
- isThirdPartyImport,
2263
- isPyTypedPresent,
2264
- };
2265
- };
2266
- // Create a map of unique imports, since imports can appear more than once.
2267
- const newImportPathMap = new Map();
2268
- // Add chained source file as import if it exists.
2269
- if (sourceFileInfo.chainedSourceFile) {
2270
- if (sourceFileInfo.chainedSourceFile.sourceFile.isFileDeleted()) {
2271
- sourceFileInfo.chainedSourceFile = undefined;
2081
+ if (!this._shouldCheckFile(fileToCheck)) {
2082
+ logState.suppress();
2083
+ return false;
2272
2084
  }
2273
- else {
2274
- const filePath = sourceFileInfo.chainedSourceFile.sourceFile.getFilePath();
2275
- newImportPathMap.set((0, pathUtils_1.normalizePathCase)(this._fs, filePath), {
2276
- path: filePath,
2277
- isTypeshedFile: false,
2278
- isThirdPartyImport: false,
2279
- isPyTypedPresent: false,
2280
- });
2085
+ this._bindFile(fileToCheck);
2086
+ if (this._preCheckCallback) {
2087
+ const parseResults = fileToCheck.sourceFile.getParseResults();
2088
+ if (parseResults) {
2089
+ this._preCheckCallback(parseResults, this._evaluator);
2090
+ }
2281
2091
  }
2282
- }
2283
- imports.forEach((importResult) => {
2284
- if (importResult.isImportFound) {
2285
- if (this._isImportAllowed(sourceFileInfo, importResult, importResult.isStubFile)) {
2286
- if (importResult.resolvedPaths.length > 0) {
2287
- const filePath = importResult.resolvedPaths[importResult.resolvedPaths.length - 1];
2288
- if (filePath) {
2289
- const thirdPartyTypeInfo = getThirdPartyImportInfo(importResult);
2290
- newImportPathMap.set((0, pathUtils_1.normalizePathCase)(this._fs, filePath), {
2291
- path: filePath,
2292
- isTypeshedFile: !!importResult.isStdlibTypeshedFile || !!importResult.isThirdPartyTypeshedFile,
2293
- isThirdPartyImport: thirdPartyTypeInfo.isThirdPartyImport,
2294
- isPyTypedPresent: thirdPartyTypeInfo.isPyTypedPresent,
2295
- });
2092
+ if (!this._disableChecker) {
2093
+ // For ipython, make sure we check all its dependent files first since
2094
+ // their results can affect this file's result.
2095
+ let dependentFiles = undefined;
2096
+ if (fileToCheck.sourceFile.getIPythonMode() === sourceFile_1.IPythonMode.CellDocs) {
2097
+ dependentFiles = [];
2098
+ const importedByFiles = (0, sourceFileInfoUtils_1.collectImportedByFiles)(fileToCheck);
2099
+ for (const file of importedByFiles) {
2100
+ if (!(0, sourceFileInfoUtils_1.isUserCode)(file)) {
2101
+ continue;
2296
2102
  }
2297
- }
2298
- }
2299
- importResult.filteredImplicitImports.forEach((implicitImport) => {
2300
- if (this._isImportAllowed(sourceFileInfo, importResult, implicitImport.isStubFile)) {
2301
- if (!implicitImport.isNativeLib) {
2302
- const thirdPartyTypeInfo = getThirdPartyImportInfo(importResult);
2303
- newImportPathMap.set((0, pathUtils_1.normalizePathCase)(this._fs, implicitImport.path), {
2304
- path: implicitImport.path,
2305
- isTypeshedFile: !!importResult.isStdlibTypeshedFile || !!importResult.isThirdPartyTypeshedFile,
2306
- isThirdPartyImport: thirdPartyTypeInfo.isThirdPartyImport,
2307
- isPyTypedPresent: thirdPartyTypeInfo.isPyTypedPresent,
2308
- });
2103
+ // If the file is already analyzed, it will be no op.
2104
+ // And make sure we don't dump parse tree and etc while
2105
+ // recursively calling checker. Otherwise, inner check
2106
+ // can dump parse tree required by outer check.
2107
+ const handle = this._cacheManager.pauseTracking();
2108
+ try {
2109
+ this._checkTypes(file, token);
2309
2110
  }
2310
- }
2311
- });
2312
- // If the stub was found but the non-stub (source) file was not, dump
2313
- // the failure to the log for diagnostic purposes.
2314
- if (importResult.nonStubImportResult && !importResult.nonStubImportResult.isImportFound) {
2315
- // We'll skip this for imports from within stub files and imports that target
2316
- // stdlib typeshed stubs because many of these are known to not have
2317
- // associated source files, and we don't want to fill the logs with noise.
2318
- if (!sourceFileInfo.sourceFile.isStubFile() && !importResult.isStdlibTypeshedFile) {
2319
- if (options.verboseOutput) {
2320
- this._console.info(`Could not resolve source for '${importResult.importName}' ` +
2321
- `in file '${sourceFileInfo.sourceFile.getFilePath()}'`);
2322
- if (importResult.nonStubImportResult.importFailureInfo) {
2323
- importResult.nonStubImportResult.importFailureInfo.forEach((diag) => {
2324
- this._console.info(` ${diag}`);
2325
- });
2326
- }
2111
+ finally {
2112
+ handle.dispose();
2113
+ }
2114
+ const parseResults = file.sourceFile.getParseResults();
2115
+ if (parseResults) {
2116
+ dependentFiles.push(parseResults);
2327
2117
  }
2328
2118
  }
2329
2119
  }
2120
+ const execEnv = this._configOptions.findExecEnvironment(fileToCheck.sourceFile.getFilePath());
2121
+ fileToCheck.sourceFile.check(this._importResolver, this._evaluator, this._createSourceMapper(execEnv, token, fileToCheck), dependentFiles);
2330
2122
  }
2331
- else if (options.verboseOutput) {
2332
- this._console.info(`Could not import '${importResult.importName}' ` +
2333
- `in file '${sourceFileInfo.sourceFile.getFilePath()}'`);
2334
- if (importResult.importFailureInfo) {
2335
- importResult.importFailureInfo.forEach((diag) => {
2336
- this._console.info(` ${diag}`);
2123
+ // For very large programs, we may need to discard the evaluator and
2124
+ // its cached types to avoid running out of heap space.
2125
+ this._handleMemoryHighUsage();
2126
+ // Detect import cycles that involve the file.
2127
+ if (this._configOptions.diagnosticRuleSet.reportImportCycles !== 'none') {
2128
+ // Don't detect import cycles when doing type stub generation. Some
2129
+ // third-party modules are pretty convoluted.
2130
+ if (!this._allowedThirdPartyImports) {
2131
+ // We need to force all of the files to be parsed and build
2132
+ // a closure map for the files.
2133
+ const closureMap = new Map();
2134
+ this._getImportsRecursive(fileToCheck, closureMap, 0);
2135
+ closureMap.forEach((file) => {
2136
+ timing_1.timingStats.cycleDetectionTime.timeOperation(() => {
2137
+ const filesVisitedMap = new Map();
2138
+ if (!this._detectAndReportImportCycles(file, filesVisitedMap)) {
2139
+ // If no cycles were found in any of the files we visited,
2140
+ // set a flag to indicates that we don't need to visit them again
2141
+ // on subsequent cycle checks.
2142
+ filesVisitedMap.forEach((sourceFileInfo) => {
2143
+ sourceFileInfo.sourceFile.setNoCircularDependencyConfirmed();
2144
+ });
2145
+ }
2146
+ });
2337
2147
  });
2338
2148
  }
2339
2149
  }
2150
+ return true;
2340
2151
  });
2341
- const updatedImportMap = new Map();
2342
- sourceFileInfo.imports.forEach((importInfo) => {
2343
- const oldFilePath = (0, pathUtils_1.normalizePathCase)(this._fs, importInfo.sourceFile.getFilePath());
2344
- // A previous import was removed.
2345
- if (!newImportPathMap.has(oldFilePath)) {
2346
- importInfo.importedBy = importInfo.importedBy.filter((fi) => (0, pathUtils_1.normalizePathCase)(this._fs, fi.sourceFile.getFilePath()) !==
2347
- (0, pathUtils_1.normalizePathCase)(this._fs, sourceFileInfo.sourceFile.getFilePath()));
2152
+ }
2153
+ // Builds a map of files that includes the specified file and all of the files
2154
+ // it imports (recursively) and ensures that all such files. If any of these files
2155
+ // have already been checked (they and their recursive imports have completed the
2156
+ // check phase), they are not included in the results.
2157
+ _getImportsRecursive(file, closureMap, recursionCount) {
2158
+ // If the file is already in the closure map, we found a cyclical
2159
+ // dependency. Don't recur further.
2160
+ const filePath = (0, pathUtils_1.normalizePathCase)(this.fileSystem, file.sourceFile.getFilePath());
2161
+ if (closureMap.has(filePath)) {
2162
+ return;
2163
+ }
2164
+ // If the import chain is too long, emit an error. Otherwise we
2165
+ // risk blowing the stack.
2166
+ if (recursionCount > _maxImportDepth) {
2167
+ file.sourceFile.setHitMaxImportDepth(_maxImportDepth);
2168
+ return;
2169
+ }
2170
+ // Add the file to the closure map.
2171
+ closureMap.set(filePath, file);
2172
+ // If this file hasn't already been parsed, parse it now. This will
2173
+ // discover any files it imports. Skip this if the file is part
2174
+ // of a library. We'll assume that no cycles will be generated from
2175
+ // library code or typeshed stubs.
2176
+ if ((0, sourceFileInfoUtils_1.isUserCode)(file)) {
2177
+ this._parseFile(file);
2178
+ }
2179
+ // Recursively add the file's imports.
2180
+ for (const importedFileInfo of file.imports) {
2181
+ this._getImportsRecursive(importedFileInfo, closureMap, recursionCount + 1);
2182
+ }
2183
+ }
2184
+ _detectAndReportImportCycles(sourceFileInfo, filesVisited, dependencyChain = [], dependencyMap = new Map()) {
2185
+ // Don't bother checking for typestub files or third-party files.
2186
+ if (sourceFileInfo.sourceFile.isStubFile() || sourceFileInfo.isThirdPartyImport) {
2187
+ return false;
2188
+ }
2189
+ // If we've already confirmed that this source file isn't part of a
2190
+ // cycle, we can skip it entirely.
2191
+ if (sourceFileInfo.sourceFile.isNoCircularDependencyConfirmed()) {
2192
+ return false;
2193
+ }
2194
+ const filePath = (0, pathUtils_1.normalizePathCase)(this.fileSystem, sourceFileInfo.sourceFile.getFilePath());
2195
+ filesVisited.set(filePath, sourceFileInfo);
2196
+ let detectedCycle = false;
2197
+ if (dependencyMap.has(filePath)) {
2198
+ // We detect a cycle (partial or full). A full cycle is one that is
2199
+ // rooted in the file at the start of our dependency chain. A partial
2200
+ // cycle loops back on some other file in the dependency chain. We
2201
+ // will report only full cycles here and leave the reporting of
2202
+ // partial cycles to other passes.
2203
+ detectedCycle = true;
2204
+ // Look for chains at least two in length. A file that contains
2205
+ // an "import . from X" will technically create a cycle with
2206
+ // itself, but those are not interesting to report.
2207
+ if (dependencyChain.length > 1 && sourceFileInfo === dependencyChain[0]) {
2208
+ this._logImportCycle(dependencyChain);
2348
2209
  }
2349
- else {
2350
- updatedImportMap.set(oldFilePath, importInfo);
2210
+ }
2211
+ else {
2212
+ // If we've already checked this dependency along
2213
+ // some other path, we can skip it.
2214
+ if (dependencyMap.has(filePath)) {
2215
+ return false;
2351
2216
  }
2352
- });
2353
- // See if there are any new imports to be added.
2354
- newImportPathMap.forEach((importInfo, normalizedImportPath) => {
2355
- if (!updatedImportMap.has(normalizedImportPath)) {
2356
- // We found a new import to add. See if it's already part
2357
- // of the program.
2358
- let importedFileInfo = this.getSourceFileInfo(importInfo.path);
2359
- if (!importedFileInfo) {
2360
- const importName = this._getImportNameForFile(importInfo.path);
2361
- const sourceFile = new sourceFile_1.SourceFile(this._fs, importInfo.path, importName, importInfo.isThirdPartyImport, importInfo.isPyTypedPresent, this._console, this._logTracker);
2362
- importedFileInfo = {
2363
- sourceFile,
2364
- isTracked: false,
2365
- isOpenByClient: false,
2366
- isTypeshedFile: importInfo.isTypeshedFile,
2367
- isThirdPartyImport: importInfo.isThirdPartyImport,
2368
- isThirdPartyPyTypedPresent: importInfo.isPyTypedPresent,
2369
- diagnosticsVersion: undefined,
2370
- imports: [],
2371
- importedBy: [],
2372
- shadows: [],
2373
- shadowedBy: [],
2374
- };
2375
- this._addToSourceFileListAndMap(importedFileInfo);
2376
- filesAdded.push(importedFileInfo);
2217
+ // We use both a map (for fast lookups) and a list
2218
+ // (for ordering information). Set the dependency map
2219
+ // entry to true to indicate that we're actively exploring
2220
+ // that dependency.
2221
+ dependencyMap.set(filePath, true);
2222
+ dependencyChain.push(sourceFileInfo);
2223
+ for (const imp of sourceFileInfo.imports) {
2224
+ if (this._detectAndReportImportCycles(imp, filesVisited, dependencyChain, dependencyMap)) {
2225
+ detectedCycle = true;
2377
2226
  }
2378
- importedFileInfo.importedBy.push(sourceFileInfo);
2379
- updatedImportMap.set(normalizedImportPath, importedFileInfo);
2380
- }
2381
- });
2382
- // Update the imports list. It should now map the set of imports
2383
- // specified by the source file.
2384
- sourceFileInfo.imports = [];
2385
- newImportPathMap.forEach((_, path) => {
2386
- if (this.getSourceFileInfo(path)) {
2387
- sourceFileInfo.imports.push(this.getSourceFileInfo(path));
2388
2227
  }
2389
- });
2390
- // Resolve the builtins import for the file. This needs to be
2391
- // analyzed before the file can be analyzed.
2392
- sourceFileInfo.builtinsImport = undefined;
2393
- const builtinsImport = sourceFileInfo.sourceFile.getBuiltinsImport();
2394
- if (builtinsImport && builtinsImport.isImportFound) {
2395
- const resolvedBuiltinsPath = builtinsImport.resolvedPaths[builtinsImport.resolvedPaths.length - 1];
2396
- sourceFileInfo.builtinsImport = this.getSourceFileInfo(resolvedBuiltinsPath);
2397
- }
2398
- // Resolve the ipython display import for the file. This needs to be
2399
- // analyzed before the file can be analyzed.
2400
- sourceFileInfo.ipythonDisplayImport = undefined;
2401
- const ipythonDisplayImport = sourceFileInfo.sourceFile.getIPythonDisplayImport();
2402
- if (ipythonDisplayImport && ipythonDisplayImport.isImportFound) {
2403
- const resolvedIPythonDisplayPath = ipythonDisplayImport.resolvedPaths[ipythonDisplayImport.resolvedPaths.length - 1];
2404
- sourceFileInfo.ipythonDisplayImport = this.getSourceFileInfo(resolvedIPythonDisplayPath);
2228
+ // Set the dependencyMap entry to false to indicate that we have
2229
+ // already explored this file and don't need to explore it again.
2230
+ dependencyMap.set(filePath, false);
2231
+ dependencyChain.pop();
2405
2232
  }
2406
- return filesAdded;
2233
+ return detectedCycle;
2407
2234
  }
2408
- _removeSourceFileFromListAndMap(filePath, indexToRemove) {
2409
- this._sourceFileMap.delete((0, pathUtils_1.normalizePathCase)(this._fs, filePath));
2410
- this._sourceFileList.splice(indexToRemove, 1);
2235
+ _logImportCycle(dependencyChain) {
2236
+ const circDep = new circularDependency_1.CircularDependency();
2237
+ dependencyChain.forEach((sourceFileInfo) => {
2238
+ circDep.appendPath(sourceFileInfo.sourceFile.getFilePath());
2239
+ });
2240
+ circDep.normalizeOrder();
2241
+ const firstFilePath = circDep.getPaths()[0];
2242
+ const firstSourceFile = this.getSourceFileInfo(firstFilePath);
2243
+ (0, debug_1.assert)(firstSourceFile !== undefined);
2244
+ firstSourceFile.sourceFile.addCircularDependency(circDep);
2411
2245
  }
2412
- _addToSourceFileListAndMap(fileInfo) {
2413
- const filePath = (0, pathUtils_1.normalizePathCase)(this._fs, fileInfo.sourceFile.getFilePath());
2414
- // We should never add a file with the same path twice.
2415
- (0, debug_1.assert)(!this._sourceFileMap.has(filePath));
2416
- this._sourceFileList.push(fileInfo);
2417
- this._sourceFileMap.set(filePath, fileInfo);
2246
+ _markFileDirtyRecursive(sourceFileInfo, markSet, forceRebinding = false) {
2247
+ const filePath = (0, pathUtils_1.normalizePathCase)(this.fileSystem, sourceFileInfo.sourceFile.getFilePath());
2248
+ // Don't mark it again if it's already been visited.
2249
+ if (markSet.has(filePath)) {
2250
+ return;
2251
+ }
2252
+ sourceFileInfo.sourceFile.markReanalysisRequired(forceRebinding);
2253
+ markSet.add(filePath);
2254
+ sourceFileInfo.importedBy.forEach((dep) => {
2255
+ // Changes on chained source file can change symbols in the symbol table and
2256
+ // dependencies on the dependent file. Force rebinding.
2257
+ const forceRebinding = dep.chainedSourceFile === sourceFileInfo;
2258
+ this._markFileDirtyRecursive(dep, markSet, forceRebinding);
2259
+ });
2260
+ // Change in the current file could impact checker result of chainedSourceFile such as unused symbols.
2261
+ let chainedSourceFile = sourceFileInfo.chainedSourceFile;
2262
+ while (chainedSourceFile) {
2263
+ if (chainedSourceFile.sourceFile.isCheckingRequired()) {
2264
+ // If the file is marked for checking, its chained one should be marked
2265
+ // as well. Stop here.
2266
+ return;
2267
+ }
2268
+ chainedSourceFile.sourceFile.markReanalysisRequired(/* forceRebinding */ false);
2269
+ chainedSourceFile = chainedSourceFile.chainedSourceFile;
2270
+ }
2418
2271
  }
2419
2272
  }
2420
2273
  exports.Program = Program;