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