mirror of
https://github.com/microsoft/pyright.git
synced 2024-08-17 11:50:22 +03:00
Merge branch 'move_declarations6'
This commit is contained in:
commit
361dbbd072
24
client/package-lock.json
generated
24
client/package-lock.json
generated
@ -5,9 +5,9 @@
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "12.7.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.8.tgz",
|
||||
"integrity": "sha512-FMdVn84tJJdV+xe+53sYiZS4R5yn1mAIxfj+DVoNiQjTYz1+OYmjwEZr1ev9nU0axXwda0QDbYl06QHanRVH3A==",
|
||||
"version": "12.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz",
|
||||
"integrity": "sha512-TJtwsqZ39pqcljJpajeoofYRfeZ7/I/OMUQ5pR4q5wOKf2ocrUvBAZUMhWsOvKx3dVc/aaV5GluBivt0sWqA5A==",
|
||||
"dev": true
|
||||
},
|
||||
"agent-base": {
|
||||
@ -199,9 +199,9 @@
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz",
|
||||
"integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==",
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
@ -1016,9 +1016,9 @@
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
|
||||
"integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==",
|
||||
"version": "3.6.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
|
||||
"integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
|
||||
"dev": true
|
||||
},
|
||||
"uc.micro": {
|
||||
@ -1082,9 +1082,9 @@
|
||||
}
|
||||
},
|
||||
"vsce": {
|
||||
"version": "1.66.0",
|
||||
"resolved": "https://registry.npmjs.org/vsce/-/vsce-1.66.0.tgz",
|
||||
"integrity": "sha512-Zf4+WD4PhEcOr7jkU08SI9lwFqDhmhk73YOCGQ/tNLaBy+PnnX4eSdqj9LdzDLuI2dsyomJLXzDSNgxuaInxCQ==",
|
||||
"version": "1.68.0",
|
||||
"resolved": "https://registry.npmjs.org/vsce/-/vsce-1.68.0.tgz",
|
||||
"integrity": "sha512-yFbRYu4x4GbdQzZdEQQeRJBxgPdummgcUOFHUtnclW8XQl3MTuKgXL3TtI09gb5oq7jE6kdyvBmpBcmDGsmhcQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"azure-devops-node-api": "^7.2.0",
|
||||
|
@ -104,8 +104,8 @@
|
||||
"postinstall": "node ./node_modules/vscode/bin/install"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^3.6.3",
|
||||
"vsce": "^1.66.0",
|
||||
"typescript": "^3.6.4",
|
||||
"vsce": "^1.68.0",
|
||||
"vscode": "^1.1.36"
|
||||
},
|
||||
"dependencies": {
|
||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@ -11,15 +11,15 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "11.13.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.21.tgz",
|
||||
"integrity": "sha512-fLwcSjMmDnjfk4FP7/QDiNzXSCEOGNvEe9eA6vaITmC784+Gm70wF7woaFQxUb2CpMjgLBhSPyhH0oIe1JS2uw==",
|
||||
"version": "11.13.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.22.tgz",
|
||||
"integrity": "sha512-rOsaPRUGTOXbRBOKToy4cgZXY4Y+QSVhxcLwdEveozbk7yuudhWMpxxcaXqYizLMP3VY7OcWCFtx9lGFh5j5kg==",
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
|
||||
"integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==",
|
||||
"version": "3.6.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
|
||||
"integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "^11.13.21",
|
||||
"typescript": "^3.6.3"
|
||||
"@types/node": "^11.13.22",
|
||||
"typescript": "^3.6.4"
|
||||
},
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
|
42
server/package-lock.json
generated
42
server/package-lock.json
generated
@ -484,9 +484,9 @@
|
||||
}
|
||||
},
|
||||
"@types/jest": {
|
||||
"version": "24.0.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.18.tgz",
|
||||
"integrity": "sha512-jcDDXdjTcrQzdN06+TSVsPPqxvsZA/5QkYfIZlq1JMw7FdP5AZylbOc+6B/cuDurctRe+MziUMtQ3xQdrbjqyQ==",
|
||||
"version": "24.0.19",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.19.tgz",
|
||||
"integrity": "sha512-YYiqfSjocv7lk5H/T+v5MjATYjaTMsUkbDnjGqSMoO88jWdtJXJV4ST/7DKZcoMHMBvB2SeSfyOzZfkxXHR5xg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/jest-diff": "*"
|
||||
@ -499,9 +499,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "11.13.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.21.tgz",
|
||||
"integrity": "sha512-fLwcSjMmDnjfk4FP7/QDiNzXSCEOGNvEe9eA6vaITmC784+Gm70wF7woaFQxUb2CpMjgLBhSPyhH0oIe1JS2uw==",
|
||||
"version": "11.13.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.22.tgz",
|
||||
"integrity": "sha512-rOsaPRUGTOXbRBOKToy4cgZXY4Y+QSVhxcLwdEveozbk7yuudhWMpxxcaXqYizLMP3VY7OcWCFtx9lGFh5j5kg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/stack-utils": {
|
||||
@ -1070,9 +1070,9 @@
|
||||
"integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow=="
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.5.5",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
|
||||
"integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==",
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz",
|
||||
"integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==",
|
||||
"dev": true
|
||||
},
|
||||
"bn.js": {
|
||||
@ -6131,9 +6131,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"terser": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.3.4.tgz",
|
||||
"integrity": "sha512-Kcrn3RiW8NtHBP0ssOAzwa2MsIRQ8lJWiBG/K7JgqPlomA3mtb2DEmp4/hrUA+Jujx+WZ02zqd7GYD+QRBB/2Q==",
|
||||
"version": "4.3.9",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-4.3.9.tgz",
|
||||
"integrity": "sha512-NFGMpHjlzmyOtPL+fDw3G7+6Ueh/sz4mkaUYa4lJCxOPTNzd0Uj0aZJOmsDYoSQyfuVoWDMSWTPU3huyOm2zdA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "^2.20.0",
|
||||
@ -6415,9 +6415,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
|
||||
"integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==",
|
||||
"version": "3.6.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
|
||||
"integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
|
||||
"dev": true
|
||||
},
|
||||
"typescript-char": {
|
||||
@ -6780,9 +6780,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"webpack": {
|
||||
"version": "4.41.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.0.tgz",
|
||||
"integrity": "sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g==",
|
||||
"version": "4.41.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.2.tgz",
|
||||
"integrity": "sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@webassemblyjs/ast": "1.8.5",
|
||||
@ -6989,9 +6989,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.0.tgz",
|
||||
"integrity": "sha512-6gpP93MR+VOOehKbCPchro3wFZNSNmek8A2kbkOAZLIZAYx1KP/zAqwO0sOHi3xJEb+UBz8NaYt/17UNit1Q9w==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
|
@ -32,8 +32,8 @@
|
||||
"@types/chokidar": "^2.1.3",
|
||||
"@types/command-line-args": "^5.0.0",
|
||||
"@types/fs-extra": "^5.1.0",
|
||||
"@types/jest": "^24.0.18",
|
||||
"@types/node": "^11.13.21",
|
||||
"@types/jest": "^24.0.19",
|
||||
"@types/node": "^11.13.22",
|
||||
"fs-extra": "^7.0.1",
|
||||
"jest": "^24.9.0",
|
||||
"node-loader": "^0.6.0",
|
||||
@ -41,8 +41,8 @@
|
||||
"ts-loader": "^5.4.5",
|
||||
"tslint": "^5.20.0",
|
||||
"tslint-microsoft-contrib": "^6.2.0",
|
||||
"typescript": "^3.6.3",
|
||||
"webpack": "^4.41.0",
|
||||
"typescript": "^3.6.4",
|
||||
"webpack": "^4.41.2",
|
||||
"webpack-cli": "^3.3.9"
|
||||
},
|
||||
"types": "out/main.d.ts",
|
||||
|
@ -13,16 +13,22 @@ import StringMap from '../common/stringMap';
|
||||
import { TextRange } from '../common/textRange';
|
||||
import { TextRangeCollection } from '../common/textRangeCollection';
|
||||
import { Scope } from './scope';
|
||||
import { ModuleType } from './types';
|
||||
import { SymbolTable } from './symbol';
|
||||
|
||||
// Maps import paths to the parse tree for the imported module.
|
||||
export type ImportMap = Map<string, ModuleType>;
|
||||
// Maps import paths to the symbol table for the imported module.
|
||||
export type ImportLookup = (filePath: string) => ImportLookupResult | undefined;
|
||||
|
||||
export interface ImportLookupResult {
|
||||
symbolTable: SymbolTable;
|
||||
docString?: string;
|
||||
}
|
||||
|
||||
export interface AnalyzerFileInfo {
|
||||
importMap: ImportMap;
|
||||
importLookup: ImportLookup;
|
||||
futureImports: StringMap<boolean>;
|
||||
builtinsScope?: Scope;
|
||||
typingModulePath?: string;
|
||||
collectionsModulePath?: string;
|
||||
diagnosticSink: TextRangeDiagnosticSink;
|
||||
executionEnvironment: ExecutionEnvironment;
|
||||
diagnosticSettings: DiagnosticSettings;
|
||||
|
@ -27,18 +27,18 @@ import StringMap from '../common/stringMap';
|
||||
import { TextRange } from '../common/textRange';
|
||||
import { AssignmentExpressionNode, AssignmentNode, AugmentedAssignmentExpressionNode,
|
||||
AwaitExpressionNode, ClassNode, DelNode, ExceptNode, ExpressionNode, ForNode,
|
||||
FunctionNode, GlobalNode, IfNode, ImportAsNode, ImportFromAsNode, LambdaNode,
|
||||
ListComprehensionNode, ModuleNameNode, ModuleNode, NameNode, NonlocalNode, ParseNode,
|
||||
ParseNodeArray, ParseNodeType, RaiseNode, StatementNode, StringListNode, SuiteNode,
|
||||
TryNode, TypeAnnotationExpressionNode, WhileNode, WithNode, YieldExpressionNode,
|
||||
YieldFromExpressionNode } from '../parser/parseNodes';
|
||||
FunctionNode, GlobalNode, IfNode, ImportAsNode, ImportFromNode, LambdaNode,
|
||||
ListComprehensionNode, ModuleNameNode, ModuleNode, NameNode, NonlocalNode,
|
||||
ParseNode, ParseNodeArray, ParseNodeType, RaiseNode, StatementNode, StringListNode,
|
||||
SuiteNode, TryNode, TypeAnnotationExpressionNode, WhileNode, WithNode,
|
||||
YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes';
|
||||
import * as StringTokenUtils from '../parser/stringTokenUtils';
|
||||
import { StringTokenFlags } from '../parser/tokenizerTypes';
|
||||
import { AnalyzerFileInfo } from './analyzerFileInfo';
|
||||
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
||||
import { DeclarationType } from './declaration';
|
||||
import { AliasDeclaration, DeclarationType, ModuleLoaderActions } from './declaration';
|
||||
import * as DocStringUtils from './docStringUtils';
|
||||
import { ImportType } from './importResult';
|
||||
import { ImplicitImport, ImportResult, ImportType } from './importResult';
|
||||
import { defaultTypeSourceId, TypeSourceId } from './inferredType';
|
||||
import * as ParseTreeUtils from './parseTreeUtils';
|
||||
import { ParseTreeWalker } from './parseTreeWalker';
|
||||
@ -593,18 +593,171 @@ export abstract class Binder extends ParseTreeWalker {
|
||||
}
|
||||
|
||||
visitImportAs(node: ImportAsNode): boolean {
|
||||
if (node.alias) {
|
||||
this._bindNameToScope(this._currentScope, node.alias.nameToken.value);
|
||||
} else if (node.module.nameParts.length > 0) {
|
||||
this._bindNameToScope(this._currentScope, node.module.nameParts[0].nameToken.value);
|
||||
if (node.module.nameParts.length > 0) {
|
||||
const firstNamePartValue = node.module.nameParts[0].nameToken.value;
|
||||
|
||||
let symbolName: string | undefined;
|
||||
if (node.alias) {
|
||||
// The symbol name is defined by the alias.
|
||||
symbolName = node.alias.nameToken.value;
|
||||
} else {
|
||||
// There was no alias, so we need to use the first element of
|
||||
// the name parts as the symbol.
|
||||
symbolName = firstNamePartValue;
|
||||
}
|
||||
|
||||
const symbol = this._bindNameToScope(this._currentScope, symbolName);
|
||||
|
||||
const importInfo = AnalyzerNodeInfo.getImportInfo(node.module);
|
||||
assert(importInfo !== undefined);
|
||||
|
||||
if (importInfo && importInfo.isImportFound && importInfo.resolvedPaths.length > 0 && symbol) {
|
||||
// See if there's already a matching alias delaration for this import.
|
||||
// if so, we'll update it rather than creating a new one. This is required
|
||||
// to handle cases where multiple import statements target the same
|
||||
// starting symbol such as "import a.b.c" and "import a.d". In this case,
|
||||
// we'll build a single declaration that describes the combined actions
|
||||
// of both import statements, thus reflecting the behavior of the
|
||||
// python module loader.
|
||||
const existingDecl = symbol.getDeclarations().find(
|
||||
decl => decl.type === DeclarationType.Alias &&
|
||||
decl.firstNamePart === firstNamePartValue);
|
||||
|
||||
const newDecl: AliasDeclaration = existingDecl as AliasDeclaration || {
|
||||
type: DeclarationType.Alias,
|
||||
path: '',
|
||||
range: getEmptyRange(),
|
||||
firstNamePart: firstNamePartValue,
|
||||
implicitImports: new Map<string, ModuleLoaderActions>()
|
||||
};
|
||||
|
||||
// Add the implicit imports for this module if it's the last
|
||||
// name part we're resolving.
|
||||
if (node.alias || node.module.nameParts.length === 1) {
|
||||
newDecl.path = importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1];
|
||||
this._addImplicitImportsToLoaderActions(importInfo, newDecl);
|
||||
} else {
|
||||
// Fill in the remaining name parts.
|
||||
let curLoaderActions: ModuleLoaderActions = newDecl;
|
||||
|
||||
for (let i = 1; i < node.module.nameParts.length; i++) {
|
||||
if (i >= importInfo.resolvedPaths.length) {
|
||||
break;
|
||||
}
|
||||
|
||||
const namePartValue = node.module.nameParts[i].nameToken.value;
|
||||
|
||||
// Is there an existing loader action for this name?
|
||||
let loaderActions = curLoaderActions.implicitImports.get(namePartValue);
|
||||
if (!loaderActions) {
|
||||
// Allocate a new loader action.
|
||||
loaderActions = {
|
||||
path: '',
|
||||
implicitImports: new Map<string, ModuleLoaderActions>()
|
||||
};
|
||||
curLoaderActions.implicitImports.set(namePartValue, loaderActions);
|
||||
}
|
||||
|
||||
// If this is the last name part we're resolving, add in the
|
||||
// implicit imports as well.
|
||||
if (i === node.module.nameParts.length - 1) {
|
||||
loaderActions.path = importInfo.resolvedPaths[i];
|
||||
this._addImplicitImportsToLoaderActions(importInfo, loaderActions);
|
||||
}
|
||||
|
||||
curLoaderActions = loaderActions;
|
||||
}
|
||||
}
|
||||
|
||||
if (!existingDecl) {
|
||||
symbol.addDeclaration(newDecl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
visitImportFromAs(node: ImportFromAsNode): boolean {
|
||||
const nameNode = node.alias || node.name;
|
||||
this._bindNameToScope(this._currentScope, nameNode.nameToken.value);
|
||||
visitImportFrom(node: ImportFromNode): boolean {
|
||||
const importInfo = AnalyzerNodeInfo.getImportInfo(node.module);
|
||||
|
||||
let resolvedPath = '';
|
||||
if (importInfo && importInfo.isImportFound) {
|
||||
resolvedPath = importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1];
|
||||
}
|
||||
|
||||
if (node.isWildcardImport) {
|
||||
if (importInfo && importInfo.implicitImports) {
|
||||
const lookupInfo = this._fileInfo.importLookup(resolvedPath);
|
||||
if (lookupInfo) {
|
||||
lookupInfo.symbolTable.forEach((_, name) => {
|
||||
const symbol = this._bindNameToScope(this._currentScope, name);
|
||||
if (symbol) {
|
||||
const aliasDecl: AliasDeclaration = {
|
||||
type: DeclarationType.Alias,
|
||||
path: resolvedPath,
|
||||
range: getEmptyRange(),
|
||||
symbolName: name,
|
||||
implicitImports: new Map<string, ModuleLoaderActions>()
|
||||
};
|
||||
symbol.addDeclaration(aliasDecl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Also add all of the implicitly-imported modules for
|
||||
// the import module.
|
||||
importInfo.implicitImports.forEach(implicitImport => {
|
||||
const symbol = this._bindNameToScope(this._currentScope, implicitImport.name);
|
||||
if (symbol) {
|
||||
const aliasDecl: AliasDeclaration = {
|
||||
type: DeclarationType.Alias,
|
||||
path: implicitImport.path,
|
||||
range: getEmptyRange(),
|
||||
implicitImports: new Map<string, ModuleLoaderActions>()
|
||||
};
|
||||
symbol.addDeclaration(aliasDecl);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
node.imports.forEach(importSymbolNode => {
|
||||
const importedName = importSymbolNode.name.nameToken.value;
|
||||
const nameNode = importSymbolNode.alias || importSymbolNode.name;
|
||||
const symbol = this._bindNameToScope(this._currentScope, nameNode.nameToken.value);
|
||||
|
||||
if (symbol) {
|
||||
let aliasDecl: AliasDeclaration | undefined;
|
||||
|
||||
// Is the import referring to an implicitly-imported module?
|
||||
let implicitImport: ImplicitImport | undefined;
|
||||
if (importInfo && importInfo.implicitImports) {
|
||||
implicitImport = importInfo.implicitImports.find(imp => imp.name === importedName);
|
||||
}
|
||||
|
||||
if (implicitImport) {
|
||||
aliasDecl = {
|
||||
type: DeclarationType.Alias,
|
||||
path: implicitImport.path,
|
||||
range: getEmptyRange(),
|
||||
implicitImports: new Map<string, ModuleLoaderActions>()
|
||||
};
|
||||
} else if (resolvedPath) {
|
||||
aliasDecl = {
|
||||
type: DeclarationType.Alias,
|
||||
path: resolvedPath,
|
||||
range: getEmptyRange(),
|
||||
symbolName: importedName,
|
||||
implicitImports: new Map<string, ModuleLoaderActions>()
|
||||
};
|
||||
}
|
||||
|
||||
if (aliasDecl) {
|
||||
symbol.addDeclaration(aliasDecl);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -775,6 +928,20 @@ export abstract class Binder extends ParseTreeWalker {
|
||||
});
|
||||
}
|
||||
|
||||
private _addImplicitImportsToLoaderActions(importResult: ImportResult, loaderActions: ModuleLoaderActions) {
|
||||
importResult.implicitImports.forEach(implicitImport => {
|
||||
const existingLoaderAction = loaderActions.implicitImports.get(implicitImport.name);
|
||||
if (existingLoaderAction) {
|
||||
existingLoaderAction.path = implicitImport.path;
|
||||
} else {
|
||||
loaderActions.implicitImports.set(implicitImport.name, {
|
||||
path: implicitImport.path,
|
||||
implicitImports: new Map<string, ModuleLoaderActions>()
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handles some special-case assignment statements that are found
|
||||
// within the typings.pyi file.
|
||||
private _handleTypingStubAssignment(node: AssignmentNode) {
|
||||
@ -943,6 +1110,8 @@ export abstract class Binder extends ParseTreeWalker {
|
||||
}
|
||||
|
||||
export class ModuleScopeBinder extends Binder {
|
||||
private _moduleDocString?: string;
|
||||
|
||||
constructor(node: ModuleNode, fileInfo: AnalyzerFileInfo) {
|
||||
super(node, fileInfo.builtinsScope ? ScopeType.Module : ScopeType.Builtin,
|
||||
fileInfo.builtinsScope, fileInfo);
|
||||
@ -963,15 +1132,16 @@ export class ModuleScopeBinder extends Binder {
|
||||
this._addParentLinks(moduleNode, moduleNode.statements);
|
||||
this.walkMultiple(moduleNode.statements);
|
||||
|
||||
// Associate the module's scope with the module type.
|
||||
const moduleType = ModuleType.create(this._currentScope.getSymbolTable(),
|
||||
this._getDocString((this._scopedNode as ModuleNode).statements));
|
||||
AnalyzerNodeInfo.setExpressionType(this._scopedNode, moduleType);
|
||||
this._moduleDocString = this._getDocString((this._scopedNode as ModuleNode).statements);
|
||||
}
|
||||
|
||||
bind() {
|
||||
this.bindDeferred();
|
||||
}
|
||||
|
||||
getModuleDocString() {
|
||||
return this._moduleDocString;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClassScopeBinder extends Binder {
|
||||
|
@ -12,7 +12,7 @@
|
||||
import { DiagnosticTextRange } from '../common/diagnostic';
|
||||
import { ClassNode, ExpressionNode, FunctionNode, NameNode,
|
||||
ParameterNode, ParseNode, StringListNode } from '../parser/parseNodes';
|
||||
import { ModuleType, Type } from './types';
|
||||
import { Type } from './types';
|
||||
|
||||
export const enum DeclarationType {
|
||||
BuiltIn,
|
||||
@ -21,7 +21,6 @@ export const enum DeclarationType {
|
||||
Function,
|
||||
Method,
|
||||
Class,
|
||||
Module,
|
||||
Alias
|
||||
}
|
||||
|
||||
@ -70,26 +69,42 @@ export interface VariableDeclaration extends DeclarationBase {
|
||||
isConstant?: boolean;
|
||||
}
|
||||
|
||||
export interface ModuleDeclaration extends DeclarationBase {
|
||||
type: DeclarationType.Module;
|
||||
moduleType: ModuleType;
|
||||
}
|
||||
|
||||
// Alias declarations are used for imports. They are resolved
|
||||
// after the binding phase.
|
||||
export interface AliasDeclaration extends DeclarationBase {
|
||||
type: DeclarationType.Alias;
|
||||
|
||||
// If a symbol is present, the alias refers to the symbol
|
||||
// within a module (whose path is defined in the 'path'
|
||||
// field). If symbolName is missing, the alias refers to
|
||||
// the module itself.
|
||||
// If a symbolName is defined, the alias refers to a symbol
|
||||
// within a resolved module (whose path is defined in the
|
||||
// 'path' field). If symbolName is missing, the alias refers
|
||||
// to the module itself.
|
||||
symbolName?: string;
|
||||
|
||||
// The resolved declaration.
|
||||
resolvedDeclarations?: Declaration[];
|
||||
// The first part of the multi-part name used in the import
|
||||
// statement (e.g. for "import a.b.c", firstNamePart would
|
||||
// be "a").
|
||||
firstNamePart?: string;
|
||||
|
||||
// If the alias is targeting a module, multiple other modules
|
||||
// may also need to be resolved and inserted implicitly into
|
||||
// the module's namespace to emulate the behavior of the python
|
||||
// module loader. This can be recursive (e.g. in the case of
|
||||
// an "import a.b.c.d" statement).
|
||||
implicitImports: Map<string, ModuleLoaderActions>;
|
||||
}
|
||||
|
||||
// This interface represents a set of actions that the python loader
|
||||
// performs when a module import is encountered.
|
||||
export interface ModuleLoaderActions {
|
||||
// The resolved path of the implicit import. This can be empty
|
||||
// if the resolved path doesn't reference a module (e.g. it's
|
||||
// a directory).
|
||||
path: string;
|
||||
|
||||
// See comment for "implicitImports" field in AliasDeclaration.
|
||||
implicitImports: Map<string, ModuleLoaderActions>;
|
||||
}
|
||||
|
||||
export type Declaration = BuiltInDeclaration | ClassDeclaration |
|
||||
FunctionDeclaration | ParameterDeclaration | VariableDeclaration |
|
||||
ModuleDeclaration | AliasDeclaration;
|
||||
AliasDeclaration;
|
||||
|
@ -9,12 +9,14 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
|
||||
import { getEmptyRange } from '../common/diagnostic';
|
||||
import { NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
|
||||
import { ImportLookup } from './analyzerFileInfo';
|
||||
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
||||
import { AliasDeclaration, Declaration, DeclarationType } from './declaration';
|
||||
import { AliasDeclaration, Declaration, DeclarationType, ModuleLoaderActions } from './declaration';
|
||||
import * as ParseTreeUtils from './parseTreeUtils';
|
||||
import { Symbol } from './symbol';
|
||||
import { ClassType, ModuleType, ObjectType, Type, TypeCategory, UnknownType } from './types';
|
||||
import { ClassType, ModuleType, ObjectType, Type, TypeCategory } from './types';
|
||||
import * as TypeUtils from './typeUtils';
|
||||
|
||||
export function getDeclarationsForNameNode(node: NameNode): Declaration[] | undefined {
|
||||
@ -51,6 +53,22 @@ export function getDeclarationsForNameNode(node: NameNode): Declaration[] | unde
|
||||
return subtype;
|
||||
});
|
||||
}
|
||||
} else if (node.parent && node.parent.nodeType === ParseNodeType.ModuleName) {
|
||||
const namePartIndex = node.parent.nameParts.findIndex(part => part === node);
|
||||
const importInfo = AnalyzerNodeInfo.getImportInfo(node.parent);
|
||||
if (namePartIndex >= 0 && importInfo && namePartIndex < importInfo.resolvedPaths.length) {
|
||||
if (importInfo.resolvedPaths[namePartIndex]) {
|
||||
// Synthesize an alias declaration for this name part. The only
|
||||
// time this case is used is for the hover provider.
|
||||
const aliasDeclaration: AliasDeclaration = {
|
||||
type: DeclarationType.Alias,
|
||||
path: importInfo.resolvedPaths[namePartIndex],
|
||||
range: getEmptyRange(),
|
||||
implicitImports: new Map<string, ModuleLoaderActions>()
|
||||
};
|
||||
declarations = [aliasDeclaration];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const scopeNode = ParseTreeUtils.getScopeNodeForNode(node);
|
||||
if (scopeNode) {
|
||||
@ -73,44 +91,65 @@ export function isFunctionOrMethodDeclaration(declaration: Declaration) {
|
||||
return declaration.type === DeclarationType.Method || declaration.type === DeclarationType.Function;
|
||||
}
|
||||
|
||||
export function resolveDeclarationAliases(declaration: Declaration) {
|
||||
let resolvedDeclaration: Declaration | undefined = declaration;
|
||||
const resolvedDeclarations: AliasDeclaration[] = [];
|
||||
// If the specified declaration is an alias declaration that points
|
||||
// to a symbol, it resolves the alias and looks up the symbol, then
|
||||
// returns the first declaration associated with that symbol. It does
|
||||
// this recursively if necessary. If a symbol lookup fails, undefined
|
||||
// is returned.
|
||||
export function resolveAliasDeclaration(declaration: Declaration, importLookup: ImportLookup):
|
||||
Declaration | undefined {
|
||||
|
||||
while (resolvedDeclaration && resolvedDeclaration.type === DeclarationType.Alias) {
|
||||
// Detect circular dependencies.
|
||||
if (resolvedDeclarations.find(decl => decl === resolvedDeclaration)) {
|
||||
let curDeclaration: Declaration | undefined = declaration;
|
||||
const alreadyVisited: Declaration[] = [];
|
||||
|
||||
while (true) {
|
||||
if (curDeclaration.type !== DeclarationType.Alias) {
|
||||
return curDeclaration;
|
||||
}
|
||||
|
||||
if (!curDeclaration.symbolName) {
|
||||
return curDeclaration;
|
||||
}
|
||||
|
||||
const lookupResult = importLookup(declaration.path);
|
||||
if (!lookupResult) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
resolvedDeclarations.push(resolvedDeclaration);
|
||||
resolvedDeclaration = resolvedDeclaration.resolvedDeclarations ?
|
||||
resolvedDeclaration.resolvedDeclarations[0] : undefined;
|
||||
}
|
||||
const symbol = lookupResult.symbolTable.get(curDeclaration.symbolName);
|
||||
if (!symbol) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return resolvedDeclaration;
|
||||
const declarations = symbol.getDeclarations();
|
||||
if (declarations.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
curDeclaration = declarations[0];
|
||||
|
||||
// Make sure we don't follow a circular list indefinitely.
|
||||
if (alreadyVisited.find(decl => decl === curDeclaration)) {
|
||||
return declaration;
|
||||
}
|
||||
alreadyVisited.push(curDeclaration);
|
||||
}
|
||||
}
|
||||
|
||||
export function getTypeForDeclaration(declaration: Declaration): Type | undefined {
|
||||
const resolvedDeclaration = resolveDeclarationAliases(declaration);
|
||||
|
||||
if (!resolvedDeclaration) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
switch (resolvedDeclaration.type) {
|
||||
switch (declaration.type) {
|
||||
case DeclarationType.BuiltIn:
|
||||
return resolvedDeclaration.declaredType;
|
||||
return declaration.declaredType;
|
||||
|
||||
case DeclarationType.Class:
|
||||
return AnalyzerNodeInfo.getExpressionType(resolvedDeclaration.node.name);
|
||||
return AnalyzerNodeInfo.getExpressionType(declaration.node.name);
|
||||
|
||||
case DeclarationType.Function:
|
||||
case DeclarationType.Method:
|
||||
return AnalyzerNodeInfo.getExpressionType(resolvedDeclaration.node.name);
|
||||
return AnalyzerNodeInfo.getExpressionType(declaration.node.name);
|
||||
|
||||
case DeclarationType.Parameter: {
|
||||
let typeAnnotationNode = resolvedDeclaration.node.typeAnnotation;
|
||||
let typeAnnotationNode = declaration.node.typeAnnotation;
|
||||
if (typeAnnotationNode && typeAnnotationNode.nodeType === ParseNodeType.StringList) {
|
||||
typeAnnotationNode = typeAnnotationNode.typeAnnotation;
|
||||
}
|
||||
@ -125,7 +164,7 @@ export function getTypeForDeclaration(declaration: Declaration): Type | undefine
|
||||
}
|
||||
|
||||
case DeclarationType.Variable: {
|
||||
let typeAnnotationNode = resolvedDeclaration.typeAnnotationNode;
|
||||
let typeAnnotationNode = declaration.typeAnnotationNode;
|
||||
if (typeAnnotationNode && typeAnnotationNode.nodeType === ParseNodeType.StringList) {
|
||||
typeAnnotationNode = typeAnnotationNode.typeAnnotation;
|
||||
}
|
||||
@ -140,20 +179,14 @@ export function getTypeForDeclaration(declaration: Declaration): Type | undefine
|
||||
return undefined;
|
||||
}
|
||||
|
||||
case DeclarationType.Module:
|
||||
return resolvedDeclaration.moduleType;
|
||||
case DeclarationType.Alias: {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function hasTypeForDeclaration(declaration: Declaration, resolveAliases = true): boolean {
|
||||
const resolvedDeclaration = resolveAliases ?
|
||||
resolveDeclarationAliases(declaration) : declaration;
|
||||
|
||||
if (!resolvedDeclaration) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (resolvedDeclaration.type) {
|
||||
export function hasTypeForDeclaration(declaration: Declaration): boolean {
|
||||
switch (declaration.type) {
|
||||
case DeclarationType.BuiltIn:
|
||||
case DeclarationType.Class:
|
||||
case DeclarationType.Function:
|
||||
@ -161,13 +194,10 @@ export function hasTypeForDeclaration(declaration: Declaration, resolveAliases =
|
||||
return true;
|
||||
|
||||
case DeclarationType.Parameter:
|
||||
return !!resolvedDeclaration.node.typeAnnotation;
|
||||
return !!declaration.node.typeAnnotation;
|
||||
|
||||
case DeclarationType.Variable:
|
||||
return !!resolvedDeclaration.typeAnnotationNode;
|
||||
|
||||
case DeclarationType.Module:
|
||||
return true;
|
||||
return !!declaration.typeAnnotationNode;
|
||||
|
||||
case DeclarationType.Alias:
|
||||
return false;
|
||||
|
@ -608,16 +608,12 @@ export class ExpressionEvaluator {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const moduleType = this._fileInfo.importMap.get(typingImportPath);
|
||||
if (!moduleType) {
|
||||
const lookupResult = this._fileInfo.importLookup(typingImportPath);
|
||||
if (!lookupResult) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (moduleType.category !== TypeCategory.Module) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const symbol = ModuleType.getField(moduleType, symbolName);
|
||||
const symbol = lookupResult.symbolTable.get(symbolName);
|
||||
if (!symbol) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -23,14 +23,13 @@ import { Duration, timingStats } from '../common/timing';
|
||||
import { ModuleSymbolMap } from '../languageService/completionProvider';
|
||||
import { HoverResults } from '../languageService/hoverProvider';
|
||||
import { SignatureHelpResults } from '../languageService/signatureHelpProvider';
|
||||
import { ImportMap } from './analyzerFileInfo';
|
||||
import { ImportLookupResult } from './analyzerFileInfo';
|
||||
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
||||
import { CircularDependency } from './circularDependency';
|
||||
import { ImportResolver } from './importResolver';
|
||||
import { ImportResult, ImportType } from './importResult';
|
||||
import { Scope } from './scope';
|
||||
import { SourceFile } from './sourceFile';
|
||||
import { ModuleType } from './types';
|
||||
import { TypeStubWriter } from './typeStubWriter';
|
||||
|
||||
const _maxImportDepth = 256;
|
||||
@ -308,7 +307,9 @@ export class Program {
|
||||
|
||||
// Now do binding of the open files.
|
||||
for (const sourceFileInfo of openFiles) {
|
||||
this._bindFile(sourceFileInfo, options, importResolver);
|
||||
if (this._bindFile(sourceFileInfo, options, importResolver, isTimeElapsedOpenFiles)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isTimeElapsedOpenFiles()) {
|
||||
return true;
|
||||
@ -505,11 +506,15 @@ export class Program {
|
||||
}
|
||||
}
|
||||
|
||||
// Binds the specified file and all of its dependencies, recursively. If
|
||||
// it runs out of time, it returns true. If it completes, it returns false.
|
||||
private _bindFile(fileToAnalyze: SourceFileInfo,
|
||||
options: ConfigOptions, importResolver: ImportResolver) {
|
||||
options: ConfigOptions, importResolver: ImportResolver,
|
||||
timeElapsedCallback: () => boolean,
|
||||
recursionMap: Map<string, true> = new Map<string, true>()): boolean {
|
||||
|
||||
if (!this._isFileNeeded(fileToAnalyze) || !fileToAnalyze.sourceFile.isBindingRequired()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
this._parseFile(fileToAnalyze, options, importResolver);
|
||||
@ -517,7 +522,11 @@ export class Program {
|
||||
// We need to parse and bind the builtins import first.
|
||||
let builtinsScope: Scope | undefined;
|
||||
if (fileToAnalyze.builtinsImport) {
|
||||
this._bindFile(fileToAnalyze.builtinsImport, options, importResolver);
|
||||
if (this._bindFile(fileToAnalyze.builtinsImport, options,
|
||||
importResolver, timeElapsedCallback)) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the builtins scope to pass to the binding pass.
|
||||
const parseResults = fileToAnalyze.builtinsImport.sourceFile.getParseResults();
|
||||
@ -525,22 +534,49 @@ export class Program {
|
||||
builtinsScope = AnalyzerNodeInfo.getScope(parseResults.parseTree);
|
||||
assert(builtinsScope !== undefined);
|
||||
}
|
||||
}
|
||||
|
||||
fileToAnalyze.sourceFile.bind(options, builtinsScope);
|
||||
}
|
||||
const filePath = fileToAnalyze.sourceFile.getFilePath();
|
||||
if (recursionMap.has(filePath)) {
|
||||
// Avoid infinite recursion for cyclical dependencies.
|
||||
return false;
|
||||
}
|
||||
|
||||
private _buildImportMap(sourceFileInfo: SourceFileInfo): ImportMap {
|
||||
const importMap: ImportMap = new Map<string, ModuleType>();
|
||||
// Bind any other files that this file depends upon.
|
||||
recursionMap.set(filePath, true);
|
||||
for (const importedFile of fileToAnalyze.imports) {
|
||||
if (this._bindFile(importedFile, options, importResolver,
|
||||
timeElapsedCallback, recursionMap)) {
|
||||
|
||||
for (const importedFileInfo of sourceFileInfo.imports) {
|
||||
const moduleType = importedFileInfo.sourceFile.getModuleType();
|
||||
if (moduleType) {
|
||||
importMap.set(importedFileInfo.sourceFile.getFilePath(), moduleType);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return importMap;
|
||||
if (timeElapsedCallback()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
fileToAnalyze.sourceFile.bind(options, this._lookUpImport, builtinsScope);
|
||||
return false;
|
||||
}
|
||||
|
||||
private _lookUpImport = (filePath: string): ImportLookupResult | undefined => {
|
||||
const sourceFileInfo = this._sourceFileMap[filePath];
|
||||
if (!sourceFileInfo) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const symbolTable = sourceFileInfo.sourceFile.getModuleSymbolTable();
|
||||
if (!symbolTable) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const docString = sourceFileInfo.sourceFile.getModuleDocString();
|
||||
|
||||
return {
|
||||
symbolTable,
|
||||
docString
|
||||
};
|
||||
}
|
||||
|
||||
// Build a map of all modules within this program and the module-
|
||||
@ -550,9 +586,9 @@ export class Program {
|
||||
|
||||
this._sourceFileList.forEach(fileInfo => {
|
||||
if (fileInfo !== sourceFileToExclude) {
|
||||
const moduleType = fileInfo.sourceFile.getModuleType();
|
||||
if (moduleType) {
|
||||
moduleSymbolMap[fileInfo.sourceFile.getFilePath()] = moduleType.fields;
|
||||
const symbolTable = fileInfo.sourceFile.getModuleSymbolTable();
|
||||
if (symbolTable) {
|
||||
moduleSymbolMap[fileInfo.sourceFile.getFilePath()] = symbolTable;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -590,15 +626,12 @@ export class Program {
|
||||
closureMap.set(fileToAnalyze.sourceFile.getFilePath(), false);
|
||||
|
||||
if (fileToAnalyze.sourceFile.isTypeAnalysisRequired()) {
|
||||
// Build the import map for the file.
|
||||
const importMap = this._buildImportMap(fileToAnalyze);
|
||||
|
||||
// Do a type analysis pass and determine if any internal changes occurred
|
||||
// during the pass. If so, continue to analyze until it stops changing and
|
||||
// mark all of its dependencies as needing to be reanalyzed.
|
||||
let didAnalysisChange = false;
|
||||
while (true) {
|
||||
fileToAnalyze.sourceFile.doTypeAnalysis(options, importMap);
|
||||
fileToAnalyze.sourceFile.doTypeAnalysis(options, this._lookUpImport);
|
||||
|
||||
if (!fileToAnalyze.sourceFile.isTypeAnalysisRequired()) {
|
||||
break;
|
||||
@ -690,7 +723,10 @@ export class Program {
|
||||
}
|
||||
|
||||
// Make sure the file is parsed and bound.
|
||||
this._bindFile(fileToAnalyze, options, importResolver);
|
||||
if (this._bindFile(fileToAnalyze, options, importResolver, timeElapsedCallback)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (timeElapsedCallback()) {
|
||||
return true;
|
||||
}
|
||||
@ -840,7 +876,7 @@ export class Program {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return sourceFile.getDefinitionsForPosition(position);
|
||||
return sourceFile.getDefinitionsForPosition(position, this._lookUpImport);
|
||||
}
|
||||
|
||||
getReferencesForPosition(filePath: string, position: DiagnosticTextPosition,
|
||||
@ -860,7 +896,7 @@ export class Program {
|
||||
}
|
||||
|
||||
const referencesResult = sourceFileInfo.sourceFile.getReferencesForPosition(
|
||||
position, includeDeclaration);
|
||||
position, includeDeclaration, this._lookUpImport);
|
||||
|
||||
if (!referencesResult) {
|
||||
return undefined;
|
||||
@ -878,7 +914,7 @@ export class Program {
|
||||
}
|
||||
|
||||
curSourceFileInfo.sourceFile.addReferences(referencesResult,
|
||||
includeDeclaration);
|
||||
includeDeclaration, this._lookUpImport);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -889,7 +925,8 @@ export class Program {
|
||||
addSymbolsForDocument(filePath: string, symbolList: SymbolInformation[]) {
|
||||
const sourceFileInfo = this._sourceFileMap[filePath];
|
||||
if (sourceFileInfo) {
|
||||
sourceFileInfo.sourceFile.addSymbolsForDocument(symbolList);
|
||||
sourceFileInfo.sourceFile.addSymbolsForDocument(
|
||||
symbolList, this._lookUpImport);
|
||||
}
|
||||
}
|
||||
|
||||
@ -901,7 +938,8 @@ export class Program {
|
||||
}
|
||||
|
||||
for (const sourceFileInfo of this._sourceFileList) {
|
||||
sourceFileInfo.sourceFile.addSymbolsForDocument(symbolList, query);
|
||||
sourceFileInfo.sourceFile.addSymbolsForDocument(
|
||||
symbolList, this._lookUpImport, query);
|
||||
}
|
||||
}
|
||||
|
||||
@ -914,7 +952,7 @@ export class Program {
|
||||
}
|
||||
|
||||
return sourceFileInfo.sourceFile.getHoverForPosition(position,
|
||||
this._buildImportMap(sourceFileInfo));
|
||||
this._lookUpImport);
|
||||
}
|
||||
|
||||
getSignatureHelpForPosition(filePath: string, position: DiagnosticTextPosition,
|
||||
@ -955,7 +993,7 @@ export class Program {
|
||||
|
||||
return sourceFileInfo.sourceFile.getCompletionsForPosition(
|
||||
position, options, importResolver,
|
||||
() => this._buildImportMap(sourceFileInfo),
|
||||
this._lookUpImport,
|
||||
() => this._buildModuleSymbolsMap(sourceFileInfo));
|
||||
}
|
||||
|
||||
@ -989,7 +1027,7 @@ export class Program {
|
||||
}
|
||||
|
||||
const referencesResult = sourceFileInfo.sourceFile.getReferencesForPosition(
|
||||
position, true);
|
||||
position, true, this._lookUpImport);
|
||||
|
||||
if (!referencesResult) {
|
||||
return undefined;
|
||||
@ -1006,7 +1044,8 @@ export class Program {
|
||||
});
|
||||
}
|
||||
|
||||
curSourceFileInfo.sourceFile.addReferences(referencesResult, true);
|
||||
curSourceFileInfo.sourceFile.addReferences(referencesResult,
|
||||
true, this._lookUpImport);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1178,30 +1217,26 @@ export class Program {
|
||||
const newImportPathMap = new Map<string, UpdateImportInfo>();
|
||||
imports.forEach(importResult => {
|
||||
if (importResult.isImportFound) {
|
||||
if (!this._isImportAllowed(sourceFileInfo, importResult, importResult.isStubFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (importResult.resolvedPaths.length > 0) {
|
||||
const filePath = importResult.resolvedPaths[
|
||||
importResult.resolvedPaths.length - 1];
|
||||
if (filePath) {
|
||||
newImportPathMap.set(filePath, {
|
||||
isTypeshedFile: !!importResult.isTypeshedFile,
|
||||
isThirdPartyImport: importResult.importType === ImportType.ThirdParty
|
||||
});
|
||||
if (this._isImportAllowed(sourceFileInfo, importResult, importResult.isStubFile)) {
|
||||
if (importResult.resolvedPaths.length > 0) {
|
||||
const filePath = importResult.resolvedPaths[
|
||||
importResult.resolvedPaths.length - 1];
|
||||
if (filePath) {
|
||||
newImportPathMap.set(filePath, {
|
||||
isTypeshedFile: !!importResult.isTypeshedFile,
|
||||
isThirdPartyImport: importResult.importType === ImportType.ThirdParty
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
importResult.implicitImports.forEach(implicitImport => {
|
||||
if (!this._isImportAllowed(sourceFileInfo, importResult, implicitImport.isStubFile)) {
|
||||
return;
|
||||
if (this._isImportAllowed(sourceFileInfo, importResult, implicitImport.isStubFile)) {
|
||||
newImportPathMap.set(implicitImport.path, {
|
||||
isTypeshedFile: !!importResult.isTypeshedFile,
|
||||
isThirdPartyImport: importResult.importType === ImportType.ThirdParty
|
||||
});
|
||||
}
|
||||
|
||||
newImportPathMap.set(implicitImport.path, {
|
||||
isTypeshedFile: !!importResult.isTypeshedFile,
|
||||
isThirdPartyImport: importResult.importType === ImportType.ThirdParty
|
||||
});
|
||||
});
|
||||
} else if (options.verboseOutput) {
|
||||
if (!sourceFileInfo.isTypeshedFile || options.diagnosticSettings.reportTypeshedErrors) {
|
||||
|
@ -34,7 +34,7 @@ import { SignatureHelpProvider, SignatureHelpResults } from '../languageService/
|
||||
import { ModuleNode } from '../parser/parseNodes';
|
||||
import { ModuleImport, ParseOptions, Parser, ParseResults } from '../parser/parser';
|
||||
import { Token } from '../parser/tokenizerTypes';
|
||||
import { AnalyzerFileInfo, ImportMap } from './analyzerFileInfo';
|
||||
import { AnalyzerFileInfo, ImportLookup } from './analyzerFileInfo';
|
||||
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
||||
import { ModuleScopeBinder } from './binder';
|
||||
import { CircularDependency } from './circularDependency';
|
||||
@ -43,9 +43,9 @@ import { ImportResolver } from './importResolver';
|
||||
import { ImportResult } from './importResult';
|
||||
import { ParseTreeCleanerWalker } from './parseTreeCleaner';
|
||||
import { Scope } from './scope';
|
||||
import { SymbolTable } from './symbol';
|
||||
import { TestWalker } from './testWalker';
|
||||
import { TypeAnalyzer } from './typeAnalyzer';
|
||||
import { ModuleType, TypeCategory } from './types';
|
||||
|
||||
const _maxImportCyclesPerFile = 4;
|
||||
|
||||
@ -107,7 +107,8 @@ export class SourceFile {
|
||||
private _parseTreeNeedsCleaning = false;
|
||||
|
||||
private _parseResults?: ParseResults;
|
||||
private _moduleType?: ModuleType;
|
||||
private _moduleSymbolTable?: SymbolTable;
|
||||
private _moduleDocString?: string;
|
||||
|
||||
// Diagnostics generated during different phases of analysis.
|
||||
private _parseDiagnostics: Diagnostic[] = [];
|
||||
@ -144,6 +145,7 @@ export class SourceFile {
|
||||
private _imports?: ImportResult[];
|
||||
private _builtinsImport?: ImportResult;
|
||||
private _typingModulePath?: string;
|
||||
private _collectionsModulePath?: string;
|
||||
|
||||
constructor(filePath: string, isTypeshedStubFile: boolean, isThirdPartyImport: boolean,
|
||||
console?: ConsoleInterface) {
|
||||
@ -280,8 +282,12 @@ export class SourceFile {
|
||||
return this._builtinsImport;
|
||||
}
|
||||
|
||||
getModuleType(): ModuleType | undefined {
|
||||
return this._moduleType;
|
||||
getModuleSymbolTable(): SymbolTable | undefined {
|
||||
return this._moduleSymbolTable;
|
||||
}
|
||||
|
||||
getModuleDocString(): string | undefined {
|
||||
return this._moduleDocString;
|
||||
}
|
||||
|
||||
// Indicates whether the contents of the file have changed since
|
||||
@ -318,7 +324,8 @@ export class SourceFile {
|
||||
this._fileContentsVersion++;
|
||||
this._isTypeAnalysisFinalized = false;
|
||||
this._isTypeAnalysisPassNeeded = true;
|
||||
this._moduleType = undefined;
|
||||
this._moduleSymbolTable = undefined;
|
||||
this._moduleDocString = undefined;
|
||||
}
|
||||
|
||||
markReanalysisRequired(): void {
|
||||
@ -327,6 +334,14 @@ export class SourceFile {
|
||||
this._typeAnalysisPassNumber = 1;
|
||||
this._isTypeAnalysisFinalized = false;
|
||||
this._isTypeAnalysisPassNeeded = true;
|
||||
|
||||
// If the file continas a wildcard import, we need to rebind
|
||||
// also because the dependent import may have changed.
|
||||
if (this._parseResults && this._parseResults.containsWildcardImport) {
|
||||
this._isBindingNeeded = true;
|
||||
this._moduleSymbolTable = undefined;
|
||||
this._moduleDocString = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
setClientVersion(version: number | null, contents: string): void {
|
||||
@ -450,7 +465,7 @@ export class SourceFile {
|
||||
|
||||
// Resolve imports.
|
||||
timingStats.resolveImportsTime.timeOperation(() => {
|
||||
[this._imports, this._builtinsImport, this._typingModulePath] =
|
||||
[this._imports, this._builtinsImport, this._typingModulePath, this._collectionsModulePath] =
|
||||
this._resolveImports(importResolver, parseResults.importedModules, execEnvironment);
|
||||
this._parseDiagnostics = diagSink.diagnostics;
|
||||
});
|
||||
@ -481,7 +496,8 @@ export class SourceFile {
|
||||
typeIgnoreLines: {},
|
||||
predominantEndOfLineSequence: '\n',
|
||||
predominantTabSequence: ' '
|
||||
}
|
||||
},
|
||||
containsWildcardImport: false
|
||||
};
|
||||
this._imports = undefined;
|
||||
this._builtinsImport = undefined;
|
||||
@ -502,18 +518,20 @@ export class SourceFile {
|
||||
return true;
|
||||
}
|
||||
|
||||
getDefinitionsForPosition(position: DiagnosticTextPosition): DocumentTextRange[] | undefined {
|
||||
getDefinitionsForPosition(position: DiagnosticTextPosition, importLookup: ImportLookup):
|
||||
DocumentTextRange[] | undefined {
|
||||
|
||||
// If we have no completed analysis job, there's nothing to do.
|
||||
if (!this._parseResults) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return DefinitionProvider.getDefinitionsForPosition(
|
||||
this._parseResults, position);
|
||||
this._parseResults, position, importLookup);
|
||||
}
|
||||
|
||||
getReferencesForPosition(position: DiagnosticTextPosition, includeDeclaration: boolean):
|
||||
ReferencesResult | undefined {
|
||||
getReferencesForPosition(position: DiagnosticTextPosition, includeDeclaration: boolean,
|
||||
importLookup: ImportLookup): ReferencesResult | undefined {
|
||||
|
||||
// If we have no completed analysis job, there's nothing to do.
|
||||
if (!this._parseResults) {
|
||||
@ -521,37 +539,43 @@ export class SourceFile {
|
||||
}
|
||||
|
||||
return ReferencesProvider.getReferencesForPosition(
|
||||
this._parseResults, this._filePath, position, includeDeclaration);
|
||||
this._parseResults, this._filePath, position, includeDeclaration, importLookup);
|
||||
}
|
||||
|
||||
addReferences(referencesResult: ReferencesResult, includeDeclaration: boolean): void {
|
||||
addReferences(referencesResult: ReferencesResult, includeDeclaration: boolean,
|
||||
importLookup: ImportLookup): void {
|
||||
|
||||
// If we have no completed analysis job, there's nothing to do.
|
||||
if (!this._parseResults) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReferencesProvider.addReferences(
|
||||
this._parseResults, this._filePath, referencesResult, includeDeclaration);
|
||||
this._parseResults, this._filePath, referencesResult, includeDeclaration, importLookup);
|
||||
}
|
||||
|
||||
addSymbolsForDocument(symbolList: SymbolInformation[], query?: string) {
|
||||
addSymbolsForDocument(symbolList: SymbolInformation[], importLookup: ImportLookup,
|
||||
query?: string) {
|
||||
|
||||
// If we have no completed analysis job, there's nothing to do.
|
||||
if (!this._parseResults) {
|
||||
return;
|
||||
}
|
||||
|
||||
DocumentSymbolProvider.addSymbolsForDocument(symbolList, query,
|
||||
this._filePath, this._parseResults);
|
||||
this._filePath, this._parseResults, importLookup);
|
||||
}
|
||||
|
||||
getHoverForPosition(position: DiagnosticTextPosition, importMap: ImportMap): HoverResults | undefined {
|
||||
getHoverForPosition(position: DiagnosticTextPosition,
|
||||
importLookup: ImportLookup): HoverResults | undefined {
|
||||
|
||||
// If we have no completed analysis job, there's nothing to do.
|
||||
if (!this._parseResults) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return HoverProvider.getHoverForPosition(
|
||||
this._parseResults, position, importMap);
|
||||
this._parseResults, position, importLookup);
|
||||
}
|
||||
|
||||
getSignatureHelpForPosition(position: DiagnosticTextPosition): SignatureHelpResults | undefined {
|
||||
@ -572,7 +596,7 @@ export class SourceFile {
|
||||
|
||||
getCompletionsForPosition(position: DiagnosticTextPosition,
|
||||
configOptions: ConfigOptions, importResolver: ImportResolver,
|
||||
importMapCallback: () => ImportMap,
|
||||
importLookup: ImportLookup,
|
||||
moduleSymbolsCallback: () => ModuleSymbolMap): CompletionList | undefined {
|
||||
|
||||
// If we have no completed analysis job, there's nothing to do.
|
||||
@ -589,7 +613,7 @@ export class SourceFile {
|
||||
const completionProvider = new CompletionProvider(
|
||||
this._parseResults, this._fileContents,
|
||||
importResolver, position,
|
||||
this._filePath, configOptions, importMapCallback,
|
||||
this._filePath, configOptions, importLookup,
|
||||
moduleSymbolsCallback);
|
||||
|
||||
return completionProvider.getCompletionsForPosition();
|
||||
@ -623,7 +647,7 @@ export class SourceFile {
|
||||
this._isTypeAnalysisFinalized = false;
|
||||
}
|
||||
|
||||
bind(configOptions: ConfigOptions, builtinsScope?: Scope) {
|
||||
bind(configOptions: ConfigOptions, importLookup: ImportLookup, builtinsScope?: Scope) {
|
||||
assert(!this.isParseRequired());
|
||||
assert(this.isBindingRequired());
|
||||
assert(this._parseResults);
|
||||
@ -631,7 +655,7 @@ export class SourceFile {
|
||||
try {
|
||||
// Perform name binding.
|
||||
timingStats.bindTime.timeOperation(() => {
|
||||
const fileInfo = this._buildFileInfo(configOptions, undefined, builtinsScope);
|
||||
const fileInfo = this._buildFileInfo(configOptions, importLookup, builtinsScope);
|
||||
this._cleanParseTreeIfRequired();
|
||||
|
||||
const binder = new ModuleScopeBinder(
|
||||
@ -648,10 +672,8 @@ export class SourceFile {
|
||||
this._bindDiagnostics = fileInfo.diagnosticSink.diagnostics;
|
||||
const moduleScope = AnalyzerNodeInfo.getScope(this._parseResults!.parseTree);
|
||||
assert(moduleScope !== undefined);
|
||||
const moduleType = AnalyzerNodeInfo.getExpressionType(
|
||||
this._parseResults!.parseTree);
|
||||
assert(moduleType && moduleType.category === TypeCategory.Module);
|
||||
this._moduleType = moduleType as ModuleType;
|
||||
this._moduleSymbolTable = moduleScope!.getSymbolTable();
|
||||
this._moduleDocString = binder.getModuleDocString();
|
||||
});
|
||||
} catch (e) {
|
||||
const message: string = (e.stack ? e.stack.toString() : undefined) ||
|
||||
@ -675,7 +697,7 @@ export class SourceFile {
|
||||
this._isBindingNeeded = false;
|
||||
}
|
||||
|
||||
doTypeAnalysis(configOptions: ConfigOptions, importMap: ImportMap) {
|
||||
doTypeAnalysis(configOptions: ConfigOptions, importLookup: ImportLookup) {
|
||||
assert(!this.isParseRequired());
|
||||
assert(!this.isBindingRequired());
|
||||
assert(this.isTypeAnalysisRequired());
|
||||
@ -683,7 +705,7 @@ export class SourceFile {
|
||||
|
||||
try {
|
||||
timingStats.typeAnalyzerTime.timeOperation(() => {
|
||||
const fileInfo = this._buildFileInfo(configOptions, importMap, undefined);
|
||||
const fileInfo = this._buildFileInfo(configOptions, importLookup, undefined);
|
||||
|
||||
// Perform static type analysis.
|
||||
const typeAnalyzer = new TypeAnalyzer(this._parseResults!.parseTree,
|
||||
@ -741,15 +763,18 @@ export class SourceFile {
|
||||
this._diagnosticVersion++;
|
||||
}
|
||||
|
||||
private _buildFileInfo(configOptions: ConfigOptions, importMap?: ImportMap, builtinsScope?: Scope) {
|
||||
private _buildFileInfo(configOptions: ConfigOptions, importLookup: ImportLookup,
|
||||
builtinsScope?: Scope) {
|
||||
|
||||
assert(this._parseResults !== undefined);
|
||||
const analysisDiagnostics = new TextRangeDiagnosticSink(this._parseResults!.tokenizerOutput.lines);
|
||||
|
||||
const fileInfo: AnalyzerFileInfo = {
|
||||
importMap: importMap || new Map<string, ModuleType>(),
|
||||
importLookup,
|
||||
futureImports: this._parseResults!.futureImports,
|
||||
builtinsScope,
|
||||
typingModulePath: this._typingModulePath,
|
||||
collectionsModulePath: this._collectionsModulePath,
|
||||
diagnosticSink: analysisDiagnostics,
|
||||
executionEnvironment: configOptions.findExecEnvironment(this._filePath),
|
||||
diagnosticSettings: this._diagnosticSettings,
|
||||
@ -775,7 +800,7 @@ export class SourceFile {
|
||||
private _resolveImports(importResolver: ImportResolver,
|
||||
moduleImports: ModuleImport[],
|
||||
execEnv: ExecutionEnvironment):
|
||||
[ImportResult[], ImportResult?, string?] {
|
||||
[ImportResult[], ImportResult?, string?, string?] {
|
||||
|
||||
const imports: ImportResult[] = [];
|
||||
|
||||
@ -817,6 +842,8 @@ export class SourceFile {
|
||||
typingModulePath = typingImportResult.resolvedPaths[0];
|
||||
}
|
||||
|
||||
let collectionsModulePath: string | undefined;
|
||||
|
||||
for (const moduleImport of moduleImports) {
|
||||
const importResult = importResolver.resolveImport(
|
||||
this._filePath,
|
||||
@ -827,6 +854,16 @@ export class SourceFile {
|
||||
importedSymbols: moduleImport.importedSymbols
|
||||
}
|
||||
);
|
||||
|
||||
// If the file imports the stdlib 'collections' module, stash
|
||||
// away its file path. The type analyzer may need this to
|
||||
// access types defined in the collections module.
|
||||
if (importResult.isImportFound && importResult.isTypeshedFile) {
|
||||
if (moduleImport.nameParts.length >= 1 && moduleImport.nameParts[0] === 'collections') {
|
||||
collectionsModulePath = importResult.resolvedPaths[importResult.resolvedPaths.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
imports.push(importResult);
|
||||
|
||||
// Associate the import results with the module import
|
||||
@ -835,6 +872,6 @@ export class SourceFile {
|
||||
AnalyzerNodeInfo.setImportInfo(moduleImport.nameNode, importResult);
|
||||
}
|
||||
|
||||
return [imports, builtinsImportResult, typingModulePath];
|
||||
return [imports, builtinsImportResult, typingModulePath, collectionsModulePath];
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ export class Symbol {
|
||||
|
||||
getTypedDeclarations() {
|
||||
return this.getDeclarations().filter(
|
||||
decl => hasTypeForDeclaration(decl, false));
|
||||
decl => hasTypeForDeclaration(decl));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,7 @@
|
||||
import * as assert from 'assert';
|
||||
|
||||
import { DiagnosticLevel } from '../common/configOptions';
|
||||
import { AddMissingOptionalToParamAction, Diagnostic,
|
||||
DiagnosticAddendum, getEmptyRange } from '../common/diagnostic';
|
||||
import { AddMissingOptionalToParamAction, Diagnostic, DiagnosticAddendum } from '../common/diagnostic';
|
||||
import { DiagnosticRule } from '../common/diagnosticRules';
|
||||
import { convertOffsetsToRange } from '../common/positionUtils';
|
||||
import { PythonVersion } from '../common/pythonVersion';
|
||||
@ -22,15 +21,15 @@ import { AssertNode, AssignmentExpressionNode, AssignmentNode, AugmentedAssignme
|
||||
BinaryExpressionNode, BreakNode, CallExpressionNode, ClassNode, ContinueNode, DecoratorNode,
|
||||
DelNode, ErrorExpressionNode, ExceptNode, ExpressionNode, FormatStringNode, ForNode,
|
||||
FunctionNode, IfNode, ImportAsNode, ImportFromNode, IndexExpressionNode, LambdaNode,
|
||||
ListComprehensionNode, MemberAccessExpressionNode, ModuleNode, NameNode, ParameterCategory, ParameterNode,
|
||||
ParseNode, ParseNodeType, RaiseNode, ReturnNode, SliceExpressionNode, StringListNode,
|
||||
SuiteNode, TernaryExpressionNode, TryNode, TupleExpressionNode,
|
||||
ListComprehensionNode, MemberAccessExpressionNode, ModuleNode, NameNode, ParameterCategory,
|
||||
ParameterNode, ParseNode, ParseNodeType, RaiseNode, ReturnNode, SliceExpressionNode,
|
||||
StringListNode, SuiteNode, TernaryExpressionNode, TryNode, TupleExpressionNode,
|
||||
TypeAnnotationExpressionNode, UnaryExpressionNode, UnpackExpressionNode, WhileNode, WithNode,
|
||||
YieldExpressionNode, YieldFromExpressionNode } from '../parser/parseNodes';
|
||||
import { KeywordType } from '../parser/tokenizerTypes';
|
||||
import { AnalyzerFileInfo } from './analyzerFileInfo';
|
||||
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
||||
import { AliasDeclaration, Declaration, DeclarationType, ModuleDeclaration,
|
||||
import { AliasDeclaration, Declaration, DeclarationType, ModuleLoaderActions,
|
||||
VariableDeclaration } from './declaration';
|
||||
import * as DeclarationUtils from './declarationUtils';
|
||||
import { EvaluatorFlags, ExpressionEvaluator } from './expressionEvaluator';
|
||||
@ -1297,95 +1296,62 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
}
|
||||
|
||||
visitImportAs(node: ImportAsNode): boolean {
|
||||
const importInfo = AnalyzerNodeInfo.getImportInfo(node.module);
|
||||
assert(importInfo !== undefined);
|
||||
if (node.module.nameParts.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (importInfo && importInfo.isImportFound && importInfo.resolvedPaths.length > 0) {
|
||||
const resolvedPath = importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1];
|
||||
let symbolNameNode: NameNode;
|
||||
if (node.alias) {
|
||||
// The symbol name is defined by the alias.
|
||||
symbolNameNode = node.alias;
|
||||
} else {
|
||||
// There was no alias, so we need to use the first element of
|
||||
// the name parts as the symbol.
|
||||
symbolNameNode = node.module.nameParts[0];
|
||||
}
|
||||
|
||||
let moduleType = this._getModuleTypeForImportPath(importInfo, resolvedPath);
|
||||
// Look up the symbol to find the alias declaration.
|
||||
let symbolType: Type | undefined;
|
||||
let symbol: Symbol | undefined;
|
||||
[symbol, symbolType] = this._getAliasedSymbolTypeForName(symbolNameNode.nameToken.value);
|
||||
if (!symbolType) {
|
||||
symbolType = UnknownType.create();
|
||||
}
|
||||
|
||||
// Is there a cached module type associated with this node?
|
||||
const cachedModuleType = AnalyzerNodeInfo.getExpressionType(node) as ModuleType;
|
||||
if (cachedModuleType && cachedModuleType.category === TypeCategory.Module && moduleType) {
|
||||
// If the fields match, use the cached version instead of the
|
||||
// newly-created version so we preserve the work done to
|
||||
// populate the loader fields.
|
||||
if (moduleType.fields === cachedModuleType.fields) {
|
||||
moduleType = cachedModuleType;
|
||||
}
|
||||
// Is there a cached module type associated with this node? If so, use
|
||||
// it instead of the type we just created. This will preserve the
|
||||
// symbol accessed flags.
|
||||
const cachedModuleType = AnalyzerNodeInfo.getExpressionType(node) as ModuleType;
|
||||
if (cachedModuleType && cachedModuleType.category === TypeCategory.Module && symbolType) {
|
||||
if (isTypeSame(symbolType, cachedModuleType)) {
|
||||
symbolType = cachedModuleType;
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleType) {
|
||||
// Import the implicit imports in the module's namespace.
|
||||
importInfo.implicitImports.forEach(implicitImport => {
|
||||
const implicitModuleType = this._getModuleTypeForImportPath(
|
||||
importInfo, implicitImport.path);
|
||||
if (implicitModuleType) {
|
||||
if (!ModuleType.getField(moduleType!, implicitImport.name)) {
|
||||
const moduleDeclaration: ModuleDeclaration = {
|
||||
type: DeclarationType.Module,
|
||||
moduleType: implicitModuleType,
|
||||
path: implicitImport.path,
|
||||
range: getEmptyRange()
|
||||
};
|
||||
const aliasDeclaration: AliasDeclaration = {
|
||||
type: DeclarationType.Alias,
|
||||
resolvedDeclarations: [moduleDeclaration],
|
||||
path: '',
|
||||
range: getEmptyRange()
|
||||
};
|
||||
// Cache the module type for subsequent passes.
|
||||
AnalyzerNodeInfo.setExpressionType(node, symbolType);
|
||||
|
||||
const newSymbol = Symbol.createWithType(
|
||||
SymbolFlags.ClassMember, implicitModuleType, defaultTypeSourceId);
|
||||
newSymbol.addDeclaration(aliasDeclaration);
|
||||
setSymbolPreservingAccess(moduleType!.loaderFields!, implicitImport.name,
|
||||
newSymbol);
|
||||
}
|
||||
}
|
||||
});
|
||||
this._assignTypeToNameNode(symbolNameNode, symbolType);
|
||||
this._updateExpressionTypeForNode(symbolNameNode, symbolType);
|
||||
|
||||
const moduleDeclaration: ModuleDeclaration = {
|
||||
type: DeclarationType.Module,
|
||||
moduleType,
|
||||
path: resolvedPath,
|
||||
range: getEmptyRange()
|
||||
};
|
||||
if (node.alias) {
|
||||
this._conditionallyReportUnusedName(symbolNameNode, false,
|
||||
this._fileInfo.diagnosticSettings.reportUnusedImport,
|
||||
DiagnosticRule.reportUnusedImport,
|
||||
`Import '${ node.alias.nameToken.value }' is not accessed`);
|
||||
} else {
|
||||
if (symbol && !symbol.isAccessed()) {
|
||||
const nameParts = node.module.nameParts;
|
||||
if (nameParts.length > 0) {
|
||||
const multipartName = nameParts.map(np => np.nameToken.value).join('.');
|
||||
const textRange: TextRange = { start: nameParts[0].start, length: nameParts[0].length };
|
||||
TextRange.extend(textRange, nameParts[nameParts.length - 1]);
|
||||
this._fileInfo.diagnosticSink.addUnusedCodeWithTextRange(
|
||||
`'${ multipartName }' is not accessed`, textRange);
|
||||
|
||||
if (node.alias) {
|
||||
this._assignTypeToNameNode(node.alias, moduleType, moduleDeclaration);
|
||||
this._updateExpressionTypeForNode(node.alias, moduleType);
|
||||
|
||||
this._conditionallyReportUnusedName(node.alias, false,
|
||||
this._fileInfo.diagnosticSettings.reportUnusedImport,
|
||||
this._addDiagnostic(this._fileInfo.diagnosticSettings.reportUnusedImport,
|
||||
DiagnosticRule.reportUnusedImport,
|
||||
`Import '${ node.alias.nameToken.value }' is not accessed`);
|
||||
} else {
|
||||
this._bindMultiPartModuleNameToType(node.module.nameParts,
|
||||
moduleType, moduleDeclaration);
|
||||
}
|
||||
|
||||
// Cache the module type for subsequent passes.
|
||||
AnalyzerNodeInfo.setExpressionType(node, moduleType);
|
||||
} else {
|
||||
// We were unable to resolve the import. Bind the names (or alias)
|
||||
// to an unknown type.
|
||||
const symbolType = UnknownType.create();
|
||||
const nameNode = node.module.nameParts.length > 0 ? node.module.nameParts[0] : undefined;
|
||||
const aliasNode = node.alias || nameNode;
|
||||
|
||||
if (node.alias && nameNode) {
|
||||
this._updateExpressionTypeForNode(nameNode, symbolType);
|
||||
}
|
||||
|
||||
if (aliasNode) {
|
||||
this._assignTypeToNameNode(aliasNode, symbolType);
|
||||
this._updateExpressionTypeForNode(aliasNode, symbolType);
|
||||
|
||||
this._conditionallyReportUnusedName(aliasNode, false,
|
||||
this._fileInfo.diagnosticSettings.reportUnusedImport,
|
||||
DiagnosticRule.reportUnusedImport,
|
||||
`Import '${ aliasNode.nameToken.value }' is not accessed`);
|
||||
`Import '${ multipartName }' is not accessed`, textRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1395,72 +1361,44 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
|
||||
visitImportFrom(node: ImportFromNode): boolean {
|
||||
const importInfo = AnalyzerNodeInfo.getImportInfo(node.module);
|
||||
let symbol: Symbol | undefined;
|
||||
let symbolType: Type | undefined;
|
||||
|
||||
if (importInfo && importInfo.isImportFound) {
|
||||
const resolvedPath = importInfo.resolvedPaths.length > 0 ?
|
||||
importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1] : '';
|
||||
|
||||
// Empty list implies "import *"
|
||||
if (node.isWildcardImport) {
|
||||
const moduleType = this._getModuleTypeForImportPath(importInfo, resolvedPath);
|
||||
if (moduleType) {
|
||||
if (resolvedPath) {
|
||||
// Import the fields in the current permanent scope.
|
||||
const moduleFields = moduleType.fields;
|
||||
moduleFields.forEach((boundValue, fieldName) => {
|
||||
this._addSymbolToPermanentScope(fieldName);
|
||||
this._addTypeSourceToName(fieldName, TypeUtils.getEffectiveTypeOfSymbol(boundValue),
|
||||
node.id, boundValue.hasDeclarations() ? boundValue.getDeclarations()[0] : undefined);
|
||||
});
|
||||
const lookupInfo = this._fileInfo.importLookup(resolvedPath);
|
||||
if (lookupInfo) {
|
||||
lookupInfo.symbolTable.forEach((_, name) => {
|
||||
[symbol, symbolType] = this._getAliasedSymbolTypeForName(name);
|
||||
if (symbol) {
|
||||
this._addTypeSourceToName(name, symbolType || UnknownType.create(),
|
||||
node.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Import the fields in the current permanent scope.
|
||||
importInfo.implicitImports.forEach(implicitImport => {
|
||||
const moduleType = this._getModuleTypeForImportPath(importInfo, resolvedPath);
|
||||
if (moduleType) {
|
||||
this._addSymbolToPermanentScope(implicitImport.name);
|
||||
this._addTypeSourceToName(implicitImport.name, moduleType, node.id);
|
||||
[symbol, symbolType] = this._getAliasedSymbolTypeForName(implicitImport.name);
|
||||
if (symbol) {
|
||||
this._addTypeSourceToName(implicitImport.name,
|
||||
symbolType || UnknownType.create(), node.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
node.imports.forEach(importAs => {
|
||||
const name = importAs.name.nameToken.value;
|
||||
const aliasNode = importAs.alias || importAs.name;
|
||||
let symbolType: Type | undefined;
|
||||
let declarations: Declaration[] | undefined;
|
||||
|
||||
// Is the name referring to an implicit import?
|
||||
const implicitImport = importInfo.implicitImports.find(impImport => impImport.name === name);
|
||||
if (implicitImport) {
|
||||
const moduleType = this._getModuleTypeForImportPath(importInfo, implicitImport.path);
|
||||
if (moduleType && this._fileInfo.importMap.has(implicitImport.path)) {
|
||||
symbolType = moduleType;
|
||||
declarations = [{
|
||||
type: DeclarationType.Module,
|
||||
moduleType,
|
||||
path: implicitImport.path,
|
||||
range: getEmptyRange()
|
||||
}];
|
||||
}
|
||||
} else {
|
||||
const moduleType = this._getModuleTypeForImportPath(importInfo, resolvedPath);
|
||||
if (moduleType) {
|
||||
const symbol = ModuleType.getField(moduleType, name);
|
||||
|
||||
// For imports of the form "from . import X", the symbol
|
||||
// will have no declarations.
|
||||
if (symbol && symbol.hasDeclarations()) {
|
||||
symbolType = TypeUtils.getEffectiveTypeOfSymbol(symbol);
|
||||
declarations = symbol.getDeclarations();
|
||||
} else {
|
||||
this._addError(
|
||||
`'${ importAs.name.nameToken.value }' is unknown import symbol`,
|
||||
importAs.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[symbol, symbolType] = this._getAliasedSymbolTypeForName(aliasNode.nameToken.value);
|
||||
if (!symbolType) {
|
||||
this._addError(
|
||||
`'${ importAs.name.nameToken.value }' is unknown import symbol`,
|
||||
importAs.name
|
||||
);
|
||||
symbolType = UnknownType.create();
|
||||
}
|
||||
|
||||
@ -1468,53 +1406,27 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
if (importAs.alias) {
|
||||
this._updateExpressionTypeForNode(importAs.alias, symbolType);
|
||||
}
|
||||
|
||||
let aliasDeclaration: AliasDeclaration | undefined;
|
||||
if (declarations) {
|
||||
aliasDeclaration = {
|
||||
type: DeclarationType.Alias,
|
||||
resolvedDeclarations: declarations,
|
||||
symbolName: name,
|
||||
path: '',
|
||||
range: getEmptyRange()
|
||||
};
|
||||
}
|
||||
|
||||
this._assignTypeToNameNode(aliasNode, symbolType, aliasDeclaration);
|
||||
|
||||
// Python files generated by protoc ("_pb2.py" files) contain
|
||||
// unused imports. Don't report these because they're in generated
|
||||
// files that shouldn't be edited.
|
||||
if (importInfo.importName !== '__future__' &&
|
||||
!this._fileInfo.filePath.endsWith('_pb2.py')) {
|
||||
|
||||
this._conditionallyReportUnusedName(aliasNode, false,
|
||||
this._fileInfo.diagnosticSettings.reportUnusedImport,
|
||||
DiagnosticRule.reportUnusedImport,
|
||||
`Import '${ aliasNode.nameToken.value }' is not accessed`);
|
||||
}
|
||||
this._addTypeSourceToName(aliasNode.nameToken.value, symbolType, node.id);
|
||||
this._assignTypeToNameNode(aliasNode, symbolType);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// We were unable to resolve the import. Bind the names (or aliases)
|
||||
// to an unknown type.
|
||||
if (!node.isWildcardImport) {
|
||||
node.imports.forEach(importAs => {
|
||||
const aliasNode = importAs.alias || importAs.name;
|
||||
const symbolType = UnknownType.create();
|
||||
}
|
||||
|
||||
this._updateExpressionTypeForNode(importAs.name, symbolType);
|
||||
if (importAs.alias) {
|
||||
this._updateExpressionTypeForNode(importAs.name, symbolType);
|
||||
}
|
||||
if (!node.isWildcardImport) {
|
||||
node.imports.forEach(importAs => {
|
||||
const aliasNode = importAs.alias || importAs.name;
|
||||
// Python files generated by protoc ("_pb2.py" files) contain
|
||||
// unused imports. Don't report these because they're in generated
|
||||
// files that shouldn't be edited.
|
||||
if ((!importInfo || importInfo.importName !== '__future__') &&
|
||||
!this._fileInfo.filePath.endsWith('_pb2.py')) {
|
||||
|
||||
this._assignTypeToNameNode(aliasNode, symbolType);
|
||||
this._conditionallyReportUnusedName(aliasNode, false,
|
||||
this._fileInfo.diagnosticSettings.reportUnusedImport,
|
||||
DiagnosticRule.reportUnusedImport,
|
||||
`Import '${ aliasNode.nameToken.value }' is not accessed`);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -1587,6 +1499,58 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
return false;
|
||||
}
|
||||
|
||||
private _getAliasedSymbolTypeForName(name: string): [Symbol | undefined, Type | undefined] {
|
||||
const symbolWithScope = this._currentScope.lookUpSymbolRecursive(name);
|
||||
if (!symbolWithScope) {
|
||||
return [undefined, undefined];
|
||||
}
|
||||
|
||||
const aliasDecl = symbolWithScope.symbol.getDeclarations().find(
|
||||
decl => decl.type === DeclarationType.Alias);
|
||||
|
||||
let symbolType: Type | undefined;
|
||||
if (aliasDecl && aliasDecl.type === DeclarationType.Alias) {
|
||||
if (aliasDecl.symbolName) {
|
||||
assert(aliasDecl.path);
|
||||
const lookupResults = this._fileInfo.importLookup(aliasDecl.path);
|
||||
if (lookupResults) {
|
||||
const symbol = lookupResults.symbolTable.get(aliasDecl.symbolName);
|
||||
if (symbol) {
|
||||
symbolType = TypeUtils.getEffectiveTypeOfSymbol(symbol);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Build a module type that corresponds to the declaration and
|
||||
// its associated loader actions.
|
||||
const moduleType = ModuleType.create();
|
||||
this._applyLoaderActionsToModuleType(moduleType, aliasDecl);
|
||||
symbolType = moduleType;
|
||||
}
|
||||
}
|
||||
|
||||
return [symbolWithScope ? symbolWithScope.symbol : undefined, symbolType];
|
||||
}
|
||||
|
||||
private _applyLoaderActionsToModuleType(moduleType: ModuleType, loaderActions: ModuleLoaderActions) {
|
||||
if (loaderActions.path) {
|
||||
const lookupResults = this._fileInfo.importLookup(loaderActions.path);
|
||||
if (lookupResults) {
|
||||
moduleType.fields = lookupResults.symbolTable;
|
||||
moduleType.docString = lookupResults.docString;
|
||||
}
|
||||
}
|
||||
|
||||
loaderActions.implicitImports.forEach((implicitImport, name) => {
|
||||
const importedModuleType = ModuleType.create();
|
||||
const importedModuleSymbol = Symbol.createWithType(
|
||||
SymbolFlags.None, importedModuleType, defaultTypeSourceId);
|
||||
moduleType.loaderFields.set(name, importedModuleSymbol);
|
||||
|
||||
// Recursively apply loader actions.
|
||||
this._applyLoaderActionsToModuleType(importedModuleType, implicitImport);
|
||||
});
|
||||
}
|
||||
|
||||
// Validates that a call to isinstance is necessary. This is a
|
||||
// common source of programming errors.
|
||||
private _validateIsInstanceCallNecessary(node: CallExpressionNode) {
|
||||
@ -2082,7 +2046,8 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
return;
|
||||
}
|
||||
|
||||
primaryDeclaration = DeclarationUtils.resolveDeclarationAliases(primaryDeclaration);
|
||||
primaryDeclaration = DeclarationUtils.resolveAliasDeclaration(primaryDeclaration,
|
||||
this._fileInfo.importLookup);
|
||||
if (!primaryDeclaration || primaryDeclaration.node === node) {
|
||||
return;
|
||||
}
|
||||
@ -2683,18 +2648,13 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
}
|
||||
|
||||
private _findCollectionsImportSymbolTable(): SymbolTable | undefined {
|
||||
let moduleType: ModuleType | undefined;
|
||||
for (const key of this._fileInfo.importMap.keys()) {
|
||||
if (key.endsWith('collections/__init__.pyi')) {
|
||||
moduleType = this._fileInfo.importMap.get(key);
|
||||
break;
|
||||
if (this._fileInfo.collectionsModulePath) {
|
||||
const lookupResult = this._fileInfo.importLookup(this._fileInfo.collectionsModulePath);
|
||||
if (lookupResult) {
|
||||
return lookupResult.symbolTable;
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleType) {
|
||||
return moduleType.fields;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -3055,23 +3015,24 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
}
|
||||
}
|
||||
|
||||
const moduleType = this._fileInfo.importMap.get(path);
|
||||
if (moduleType) {
|
||||
return ModuleType.cloneForLoadedModule(moduleType);
|
||||
const lookupResults = this._fileInfo.importLookup(path);
|
||||
if (lookupResults) {
|
||||
const moduleType = ModuleType.create(lookupResults.symbolTable);
|
||||
moduleType.docString = lookupResults.docString;
|
||||
return moduleType;
|
||||
} else if (importResult) {
|
||||
// There was no module even though the import was resolved. This
|
||||
// happens in the case of namespace packages, where an __init__.py
|
||||
// is not necessarily present. We'll synthesize a module type in
|
||||
// this case.
|
||||
const moduleType = ModuleType.cloneForLoadedModule(
|
||||
ModuleType.create(new SymbolTable()));
|
||||
const moduleType = ModuleType.create();
|
||||
|
||||
// Add the implicit imports.
|
||||
importResult.implicitImports.forEach(implicitImport => {
|
||||
const implicitModuleType = this._getModuleTypeForImportPath(
|
||||
undefined, implicitImport.path);
|
||||
if (implicitModuleType) {
|
||||
setSymbolPreservingAccess(moduleType.loaderFields!, implicitImport.name,
|
||||
setSymbolPreservingAccess(moduleType.loaderFields, implicitImport.name,
|
||||
Symbol.createWithType(
|
||||
SymbolFlags.ClassMember, implicitModuleType, defaultTypeSourceId));
|
||||
}
|
||||
@ -3364,83 +3325,6 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
this._evaluateExpressionForAssignment(target, srcType, srcExpr);
|
||||
}
|
||||
|
||||
private _bindMultiPartModuleNameToType(nameParts: NameNode[], type: ModuleType,
|
||||
declaration?: Declaration): void {
|
||||
|
||||
// The target symbol table will change as we progress through
|
||||
// the multi-part name. Start with the current scope's symbol
|
||||
// table, which should include the first part of the name.
|
||||
const permanentScope = ScopeUtils.getPermanentScope(this._currentScope);
|
||||
let targetSymbolTable = permanentScope.getSymbolTable();
|
||||
|
||||
for (let i = 0; i < nameParts.length; i++) {
|
||||
const name = nameParts[i].nameToken.value;
|
||||
|
||||
// Does this symbol already exist within this scope?
|
||||
let targetSymbol = targetSymbolTable.get(name);
|
||||
let symbolType = targetSymbol ?
|
||||
TypeUtils.getEffectiveTypeOfSymbol(targetSymbol) : undefined;
|
||||
|
||||
if (!symbolType || symbolType.category !== TypeCategory.Module) {
|
||||
symbolType = ModuleType.create(new SymbolTable());
|
||||
symbolType = ModuleType.cloneForLoadedModule(symbolType);
|
||||
}
|
||||
|
||||
// If the symbol didn't exist, create a new one.
|
||||
if (!targetSymbol) {
|
||||
targetSymbol = Symbol.createWithType(SymbolFlags.ClassMember,
|
||||
symbolType, defaultTypeSourceId);
|
||||
}
|
||||
|
||||
if (i === 0) {
|
||||
// Assign the first part of the multi-part name to the current scope.
|
||||
this._assignTypeToNameNode(nameParts[0], symbolType);
|
||||
}
|
||||
|
||||
if (i === nameParts.length - 1) {
|
||||
const moduleType = symbolType;
|
||||
moduleType.fields = type.fields;
|
||||
moduleType.docString = type.docString;
|
||||
|
||||
if (type.loaderFields) {
|
||||
assert(moduleType.loaderFields !== undefined);
|
||||
|
||||
// Copy the loader fields, which may include implicit
|
||||
// imports for the module.
|
||||
type.loaderFields.forEach((symbol, name) => {
|
||||
setSymbolPreservingAccess(moduleType.loaderFields!, name, symbol);
|
||||
});
|
||||
}
|
||||
|
||||
if (declaration) {
|
||||
targetSymbol.addDeclaration(declaration);
|
||||
}
|
||||
}
|
||||
|
||||
setSymbolPreservingAccess(targetSymbolTable, name, targetSymbol);
|
||||
assert(symbolType.loaderFields !== undefined);
|
||||
targetSymbolTable = symbolType.loaderFields!;
|
||||
|
||||
// If this is the last element, determine if it's accessed.
|
||||
if (i === nameParts.length - 1) {
|
||||
// Is this module ever accessed?
|
||||
if (targetSymbol && !targetSymbol.isAccessed()) {
|
||||
const multipartName = nameParts.map(np => np.nameToken.value).join('.');
|
||||
const textRange = { start: nameParts[0].start, length: nameParts[0].length };
|
||||
if (nameParts.length > 1) {
|
||||
TextRange.extend(textRange, nameParts[nameParts.length - 1]);
|
||||
}
|
||||
this._fileInfo.diagnosticSink.addUnusedCodeWithTextRange(
|
||||
`'${ multipartName }' is not accessed`, textRange);
|
||||
|
||||
this._addDiagnostic(this._fileInfo.diagnosticSettings.reportUnusedImport,
|
||||
DiagnosticRule.reportUnusedImport,
|
||||
`Import '${ multipartName }' is not accessed`, textRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _assignTypeToNameNode(nameNode: NameNode, srcType: Type, declaration?: Declaration,
|
||||
srcExpressionNode?: ParseNode) {
|
||||
|
||||
@ -3508,28 +3392,6 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
this._addAssignmentTypeConstraint(node, type);
|
||||
}
|
||||
|
||||
// Finds the nearest permanent scope (as opposed to temporary scope) and
|
||||
// adds a new symbol with the specified name if it doesn't already exist.
|
||||
private _addSymbolToPermanentScope(name: string) {
|
||||
const permanentScope = ScopeUtils.getPermanentScope(this._currentScope);
|
||||
assert(permanentScope.getType() !== ScopeType.Temporary);
|
||||
|
||||
let symbol = permanentScope.lookUpSymbol(name);
|
||||
if (!symbol) {
|
||||
symbol = permanentScope.addSymbol(name, SymbolFlags.ClassMember);
|
||||
}
|
||||
|
||||
// Variables that are defined within a module or a class
|
||||
// are considered public by default. Don't flag them
|
||||
// "not access" unless the name indicates that it's private.
|
||||
const scopeType = permanentScope.getType();
|
||||
if (scopeType === ScopeType.Class || scopeType === ScopeType.Module) {
|
||||
if (!this._isSymbolPrivate(name, scopeType)) {
|
||||
this._setSymbolAccessed(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _addTypeSourceToName(name: string, type: Type, typeSourceId: TypeSourceId,
|
||||
declaration?: Declaration) {
|
||||
|
||||
|
@ -115,24 +115,14 @@ export interface ModuleType extends TypeBase {
|
||||
// A "loader" module includes symbols that were injected by
|
||||
// the module loader. We keep these separate so we don't
|
||||
// pollute the symbols exported by the module itself.
|
||||
loaderFields?: SymbolTable;
|
||||
loaderFields: SymbolTable;
|
||||
}
|
||||
|
||||
export namespace ModuleType {
|
||||
export function create(fields: SymbolTable, docString?: string) {
|
||||
export function create(symbolTable?: SymbolTable) {
|
||||
const newModuleType: ModuleType = {
|
||||
category: TypeCategory.Module,
|
||||
fields,
|
||||
docString
|
||||
};
|
||||
return newModuleType;
|
||||
}
|
||||
|
||||
export function cloneForLoadedModule(moduleType: ModuleType) {
|
||||
const newModuleType: ModuleType = {
|
||||
category: TypeCategory.Module,
|
||||
fields: moduleType.fields,
|
||||
docString: moduleType.docString,
|
||||
fields: symbolTable || new SymbolTable(),
|
||||
loaderFields: new SymbolTable()
|
||||
};
|
||||
return newModuleType;
|
||||
@ -1217,6 +1207,24 @@ export function isTypeSame(type1: Type, type2: Type, recursionCount = 0): boolea
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case TypeCategory.Module: {
|
||||
const type2Module = type2 as ModuleType;
|
||||
|
||||
// Module types are the same if they share the same
|
||||
// module symbol table.
|
||||
if (type1.fields === type2Module.fields) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If both symbol tables are empty, we can also assume
|
||||
// they're equal.
|
||||
if (type1.fields.isEmpty() && type2Module.fields.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -11,10 +11,10 @@
|
||||
import { CompletionItem, CompletionItemKind, CompletionList,
|
||||
MarkupKind, TextEdit } from 'vscode-languageserver';
|
||||
|
||||
import { ImportMap } from '../analyzer/analyzerFileInfo';
|
||||
import { ImportLookup } from '../analyzer/analyzerFileInfo';
|
||||
import * as AnalyzerNodeInfo from '../analyzer/analyzerNodeInfo';
|
||||
import { Declaration, DeclarationType } from '../analyzer/declaration';
|
||||
import { getTypeForDeclaration } from '../analyzer/declarationUtils';
|
||||
import { getTypeForDeclaration, resolveAliasDeclaration } from '../analyzer/declarationUtils';
|
||||
import { ImportedModuleDescriptor, ImportResolver, ModuleNameAndType } from '../analyzer/importResolver';
|
||||
import { ImportType } from '../analyzer/importResult';
|
||||
import * as ImportStatementUtils from '../analyzer/importStatementUtils';
|
||||
@ -142,7 +142,7 @@ export class CompletionProvider {
|
||||
private _position: DiagnosticTextPosition,
|
||||
private _filePath: string,
|
||||
private _configOptions: ConfigOptions,
|
||||
private _importMapCallback: () => ImportMap,
|
||||
private _importLookup: ImportLookup,
|
||||
private _moduleSymbolsCallback: () => ModuleSymbolMap) {
|
||||
}
|
||||
|
||||
@ -566,13 +566,9 @@ export class CompletionProvider {
|
||||
const resolvedPath = importInfo.resolvedPaths.length > 0 ?
|
||||
importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1] : '';
|
||||
|
||||
const importMap = this._importMapCallback();
|
||||
|
||||
const moduleType = importMap.get(resolvedPath);
|
||||
if (moduleType) {
|
||||
const symbolTable = new SymbolTable();
|
||||
TypeUtils.getMembersForModule(moduleType, symbolTable);
|
||||
this._addSymbolsForSymbolTable(symbolTable,
|
||||
const lookupResults = this._importLookup(resolvedPath);
|
||||
if (lookupResults) {
|
||||
this._addSymbolsForSymbolTable(lookupResults.symbolTable,
|
||||
name => {
|
||||
// Don't suggest symbols that have already been imported.
|
||||
return !importFromNode.imports.find(
|
||||
@ -646,46 +642,60 @@ export class CompletionProvider {
|
||||
let typeDetail: string | undefined;
|
||||
let documentation: string | undefined;
|
||||
|
||||
const declaration = declarations[0];
|
||||
const type = getTypeForDeclaration(declaration);
|
||||
itemKind = this._convertDeclarationTypeToItemKind(declaration, type);
|
||||
const declaration = resolveAliasDeclaration(declarations[0], this._importLookup);
|
||||
if (declaration) {
|
||||
const type = getTypeForDeclaration(declaration);
|
||||
itemKind = this._convertDeclarationTypeToItemKind(declaration, type);
|
||||
|
||||
if (type) {
|
||||
switch (declaration.type) {
|
||||
case DeclarationType.BuiltIn:
|
||||
case DeclarationType.Variable:
|
||||
case DeclarationType.Parameter:
|
||||
typeDetail = name + ': ' + printType(type);
|
||||
break;
|
||||
if (type) {
|
||||
switch (declaration.type) {
|
||||
case DeclarationType.BuiltIn:
|
||||
case DeclarationType.Variable:
|
||||
case DeclarationType.Parameter:
|
||||
typeDetail = name + ': ' + printType(type);
|
||||
break;
|
||||
|
||||
case DeclarationType.Function:
|
||||
case DeclarationType.Method:
|
||||
if (type.category === TypeCategory.OverloadedFunction) {
|
||||
typeDetail = type.overloads.map(overload =>
|
||||
name + printType(overload.type)).join('\n');
|
||||
} else {
|
||||
typeDetail = name + printType(type);
|
||||
case DeclarationType.Function:
|
||||
case DeclarationType.Method:
|
||||
if (type.category === TypeCategory.OverloadedFunction) {
|
||||
typeDetail = type.overloads.map(overload =>
|
||||
name + printType(overload.type)).join('\n');
|
||||
} else {
|
||||
typeDetail = name + printType(type);
|
||||
}
|
||||
break;
|
||||
|
||||
case DeclarationType.Class: {
|
||||
typeDetail = 'class ' + name + '()';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case DeclarationType.Class:
|
||||
typeDetail = 'class ' + name + '()';
|
||||
break;
|
||||
case DeclarationType.Alias: {
|
||||
typeDetail = name;
|
||||
if (declaration.path) {
|
||||
const lookupResults = this._importLookup(declaration.path);
|
||||
if (lookupResults) {
|
||||
documentation = lookupResults.docString;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case DeclarationType.Module:
|
||||
default:
|
||||
typeDetail = name;
|
||||
break;
|
||||
default: {
|
||||
typeDetail = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type) {
|
||||
if (type.category === TypeCategory.Module) {
|
||||
documentation = type.docString;
|
||||
} else if (type.category === TypeCategory.Class) {
|
||||
documentation = ClassType.getDocString(type);
|
||||
} else if (type.category === TypeCategory.Function) {
|
||||
documentation = FunctionType.getDocString(type);
|
||||
if (type) {
|
||||
if (type.category === TypeCategory.Module) {
|
||||
documentation = type.docString;
|
||||
} else if (type.category === TypeCategory.Class) {
|
||||
documentation = ClassType.getDocString(type);
|
||||
} else if (type.category === TypeCategory.Function) {
|
||||
documentation = FunctionType.getDocString(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -834,7 +844,12 @@ export class CompletionProvider {
|
||||
private _convertDeclarationTypeToItemKind(declaration: Declaration,
|
||||
type?: Type): CompletionItemKind {
|
||||
|
||||
switch (declaration.type) {
|
||||
const resolvedDeclaration = resolveAliasDeclaration(declaration, this._importLookup);
|
||||
if (!resolvedDeclaration) {
|
||||
return CompletionItemKind.Variable;
|
||||
}
|
||||
|
||||
switch (resolvedDeclaration.type) {
|
||||
case DeclarationType.BuiltIn:
|
||||
if (type) {
|
||||
if (type.category === TypeCategory.Class) {
|
||||
@ -849,7 +864,7 @@ export class CompletionProvider {
|
||||
return CompletionItemKind.Variable;
|
||||
|
||||
case DeclarationType.Variable:
|
||||
return declaration.isConstant ?
|
||||
return resolvedDeclaration.isConstant ?
|
||||
CompletionItemKind.Constant :
|
||||
CompletionItemKind.Variable;
|
||||
|
||||
@ -865,15 +880,8 @@ export class CompletionProvider {
|
||||
case DeclarationType.Class:
|
||||
return CompletionItemKind.Class;
|
||||
|
||||
case DeclarationType.Module:
|
||||
return CompletionItemKind.Module;
|
||||
|
||||
case DeclarationType.Alias:
|
||||
if (declaration.resolvedDeclarations) {
|
||||
return this._convertDeclarationTypeToItemKind(
|
||||
declaration.resolvedDeclarations[0], type);
|
||||
}
|
||||
return CompletionItemKind.Variable;
|
||||
return CompletionItemKind.Module;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,10 @@
|
||||
* definition is the top of the resolved import file.
|
||||
*/
|
||||
|
||||
import { ImportLookup } from '../analyzer/analyzerFileInfo';
|
||||
import * as AnalyzerNodeInfo from '../analyzer/analyzerNodeInfo';
|
||||
import { Declaration } from '../analyzer/declaration';
|
||||
import { resolveDeclarationAliases } from '../analyzer/declarationUtils';
|
||||
import { resolveAliasDeclaration } from '../analyzer/declarationUtils';
|
||||
import * as ParseTreeUtils from '../analyzer/parseTreeUtils';
|
||||
import { Symbol } from '../analyzer/symbol';
|
||||
import { ModuleType, TypeCategory } from '../analyzer/types';
|
||||
@ -29,7 +30,8 @@ const _startOfFileRange: DiagnosticTextRange = { start: _startOfFilePosition, en
|
||||
|
||||
export class DefinitionProvider {
|
||||
static getDefinitionsForPosition(parseResults: ParseResults,
|
||||
position: DiagnosticTextPosition): DocumentTextRange[] | undefined {
|
||||
position: DiagnosticTextPosition, importLookup: ImportLookup):
|
||||
DocumentTextRange[] | undefined {
|
||||
|
||||
const offset = convertPositionToOffset(position, parseResults.tokenizerOutput.lines);
|
||||
if (offset === undefined) {
|
||||
@ -43,17 +45,17 @@ export class DefinitionProvider {
|
||||
|
||||
const definitions: DocumentTextRange[] = [];
|
||||
|
||||
if (node.nodeType === ParseNodeType.ModuleName) {
|
||||
this._addDefinitionsForModuleNameNode(definitions, node, offset);
|
||||
} else if (node.nodeType === ParseNodeType.Name) {
|
||||
if (node.nodeType === ParseNodeType.Name) {
|
||||
// Is the user hovering over a member name? If so, we need to search
|
||||
// in the scope of that type rather than the current node's scope.
|
||||
if (node.parent && node.parent.nodeType === ParseNodeType.MemberAccess &&
|
||||
node === node.parent.memberName) {
|
||||
|
||||
this._addDefinitionsForMemberAccessNode(definitions, node.parent);
|
||||
this._addDefinitionsForMemberAccessNode(definitions, node.parent, importLookup);
|
||||
} else if (node.parent && node.parent.nodeType === ParseNodeType.ModuleName) {
|
||||
this._addDefinitionsForModuleNameNode(definitions, node.parent, offset);
|
||||
} else {
|
||||
this._addDefinitionsForNameNode(definitions, node);
|
||||
this._addDefinitionsForNameNode(definitions, node, importLookup);
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,7 +63,7 @@ export class DefinitionProvider {
|
||||
}
|
||||
|
||||
private static _addDefinitionsForMemberAccessNode(definitions: DocumentTextRange[],
|
||||
node: MemberAccessExpressionNode) {
|
||||
node: MemberAccessExpressionNode, importLookup: ImportLookup) {
|
||||
|
||||
const baseType = AnalyzerNodeInfo.getExpressionType(node.leftExpression);
|
||||
if (!baseType) {
|
||||
@ -88,14 +90,16 @@ export class DefinitionProvider {
|
||||
|
||||
if (symbol) {
|
||||
const declarations = symbol.getDeclarations();
|
||||
this._addResultsForDeclarations(definitions, declarations);
|
||||
this._addResultsForDeclarations(definitions, declarations, importLookup);
|
||||
}
|
||||
|
||||
return subtype;
|
||||
});
|
||||
}
|
||||
|
||||
private static _addDefinitionsForNameNode(definitions: DocumentTextRange[], node: NameNode) {
|
||||
private static _addDefinitionsForNameNode(definitions: DocumentTextRange[],
|
||||
node: NameNode, importLookup: ImportLookup) {
|
||||
|
||||
const scopeNode = ParseTreeUtils.getScopeNodeForNode(node);
|
||||
if (!scopeNode) {
|
||||
return;
|
||||
@ -113,15 +117,15 @@ export class DefinitionProvider {
|
||||
|
||||
const declarations = symbolWithScope.symbol.getDeclarations();
|
||||
if (declarations) {
|
||||
this._addResultsForDeclarations(definitions, declarations);
|
||||
this._addResultsForDeclarations(definitions, declarations, importLookup);
|
||||
}
|
||||
}
|
||||
|
||||
private static _addResultsForDeclarations(definitions: DocumentTextRange[],
|
||||
declarations: Declaration[]) {
|
||||
declarations: Declaration[], importLookup: ImportLookup) {
|
||||
|
||||
declarations.forEach(decl => {
|
||||
const resolvedDecl = resolveDeclarationAliases(decl);
|
||||
const resolvedDecl = resolveAliasDeclaration(decl, importLookup);
|
||||
if (resolvedDecl && resolvedDecl.path) {
|
||||
definitions.push({
|
||||
path: resolvedDecl.path,
|
||||
|
@ -11,9 +11,10 @@
|
||||
import { Location, Position, Range, SymbolInformation, SymbolKind } from 'vscode-languageserver';
|
||||
import VSCodeUri from 'vscode-uri';
|
||||
|
||||
import { ImportLookup } from '../analyzer/analyzerFileInfo';
|
||||
import * as AnalyzerNodeInfo from '../analyzer/analyzerNodeInfo';
|
||||
import { Declaration, DeclarationType } from '../analyzer/declaration';
|
||||
import { getTypeForDeclaration } from '../analyzer/declarationUtils';
|
||||
import { getTypeForDeclaration, resolveAliasDeclaration } from '../analyzer/declarationUtils';
|
||||
import * as ParseTreeUtils from '../analyzer/parseTreeUtils';
|
||||
import { ParseTreeWalker } from '../analyzer/parseTreeWalker';
|
||||
import { TypeCategory } from '../analyzer/types';
|
||||
@ -31,15 +32,18 @@ class FindSymbolTreeWalker extends ParseTreeWalker {
|
||||
private _parseResults: ParseResults;
|
||||
private _symbolResults: SymbolInformation[];
|
||||
private _query: string | undefined;
|
||||
private _importLookup: ImportLookup;
|
||||
|
||||
constructor(filePath: string, parseResults: ParseResults,
|
||||
results: SymbolInformation[], query: string | undefined) {
|
||||
results: SymbolInformation[], query: string | undefined,
|
||||
importLookup: ImportLookup) {
|
||||
|
||||
super();
|
||||
this._filePath = filePath;
|
||||
this._parseResults = parseResults;
|
||||
this._symbolResults = results;
|
||||
this._query = query;
|
||||
this._importLookup = importLookup;
|
||||
}
|
||||
|
||||
findSymbols() {
|
||||
@ -99,6 +103,11 @@ class FindSymbolTreeWalker extends ParseTreeWalker {
|
||||
}
|
||||
}
|
||||
|
||||
const resolvedSymbol = resolveAliasDeclaration(declaration, this._importLookup);
|
||||
if (!resolvedSymbol) {
|
||||
return;
|
||||
}
|
||||
|
||||
let symbolKind: SymbolKind;
|
||||
switch (declaration.type) {
|
||||
case DeclarationType.Class:
|
||||
@ -118,7 +127,7 @@ class FindSymbolTreeWalker extends ParseTreeWalker {
|
||||
}
|
||||
break;
|
||||
|
||||
case DeclarationType.Module:
|
||||
case DeclarationType.Alias:
|
||||
symbolKind = SymbolKind.Module;
|
||||
break;
|
||||
|
||||
@ -173,10 +182,10 @@ class FindSymbolTreeWalker extends ParseTreeWalker {
|
||||
|
||||
export class DocumentSymbolProvider {
|
||||
static addSymbolsForDocument(symbolList: SymbolInformation[], query: string | undefined,
|
||||
filePath: string, parseResults: ParseResults) {
|
||||
filePath: string, parseResults: ParseResults, importLookup: ImportLookup) {
|
||||
|
||||
const symbolTreeWalker = new FindSymbolTreeWalker(filePath, parseResults,
|
||||
symbolList, query);
|
||||
symbolList, query, importLookup);
|
||||
symbolTreeWalker.findSymbols();
|
||||
}
|
||||
}
|
||||
|
@ -9,17 +9,16 @@
|
||||
* position within a smart editor.
|
||||
*/
|
||||
|
||||
import { ImportMap } from '../analyzer/analyzerFileInfo';
|
||||
import { ImportLookup } from '../analyzer/analyzerFileInfo';
|
||||
import * as AnalyzerNodeInfo from '../analyzer/analyzerNodeInfo';
|
||||
import { Declaration, DeclarationType } from '../analyzer/declaration';
|
||||
import * as DeclarationUtils from '../analyzer/declarationUtils';
|
||||
import { ImportType } from '../analyzer/importResult';
|
||||
import * as ParseTreeUtils from '../analyzer/parseTreeUtils';
|
||||
import { ClassType, FunctionType, printType, Type, TypeCategory, UnknownType } from '../analyzer/types';
|
||||
import { DiagnosticTextPosition, DiagnosticTextRange } from '../common/diagnostic';
|
||||
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils';
|
||||
import { TextRange } from '../common/textRange';
|
||||
import { ModuleNameNode, ParseNode, ParseNodeType } from '../parser/parseNodes';
|
||||
import { ParseNode, ParseNodeType } from '../parser/parseNodes';
|
||||
import { ParseResults } from '../parser/parser';
|
||||
|
||||
export interface HoverTextPart {
|
||||
@ -34,7 +33,7 @@ export interface HoverResults {
|
||||
|
||||
export class HoverProvider {
|
||||
static getHoverForPosition(parseResults: ParseResults, position: DiagnosticTextPosition,
|
||||
importMap: ImportMap): HoverResults | undefined {
|
||||
importLookup: ImportLookup): HoverResults | undefined {
|
||||
|
||||
const offset = convertPositionToOffset(position, parseResults.tokenizerOutput.lines);
|
||||
if (offset === undefined) {
|
||||
@ -54,18 +53,19 @@ export class HoverProvider {
|
||||
}
|
||||
};
|
||||
|
||||
if (node.nodeType === ParseNodeType.ModuleName) {
|
||||
this._addResultsForModuleNameNode(results.parts, node, offset, importMap);
|
||||
} else if (node.nodeType === ParseNodeType.Name) {
|
||||
if (node.nodeType === ParseNodeType.Name) {
|
||||
const declarations = DeclarationUtils.getDeclarationsForNameNode(node);
|
||||
if (declarations && declarations.length > 0) {
|
||||
this._addResultsForDeclaration(results.parts, declarations[0], node);
|
||||
}
|
||||
|
||||
// If we had no declaration, see if we can provide a minimal tooltip.
|
||||
if (results.parts.length === 0) {
|
||||
this._addResultsPart(results.parts, node.nameToken.value + this._getTypeText(node), true);
|
||||
this._addDocumentationPart(results.parts, node);
|
||||
this._addResultsForDeclaration(results.parts, declarations[0], node, importLookup);
|
||||
} else if (!node.parent || node.parent.nodeType !== ParseNodeType.ModuleName) {
|
||||
// If we had no declaration, see if we can provide a minimal tooltip. We'll skip
|
||||
// this if it's part of a module name, since a module name part with no declaration
|
||||
// is a directory (a namespace package), and we don't want to provide any hover
|
||||
// information in that case.
|
||||
if (results.parts.length === 0) {
|
||||
this._addResultsPart(results.parts, node.nameToken.value + this._getTypeText(node), true);
|
||||
this._addDocumentationPart(results.parts, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,16 +73,11 @@ export class HoverProvider {
|
||||
}
|
||||
|
||||
private static _addResultsForDeclaration(parts: HoverTextPart[],
|
||||
declaration: Declaration, node: ParseNode): void {
|
||||
|
||||
let resolvedDecl: Declaration | undefined = declaration;
|
||||
while (resolvedDecl && resolvedDecl.type === DeclarationType.Alias) {
|
||||
resolvedDecl = resolvedDecl.resolvedDeclarations ?
|
||||
resolvedDecl.resolvedDeclarations[0] : undefined;
|
||||
}
|
||||
declaration: Declaration, node: ParseNode, importLookup: ImportLookup): void {
|
||||
|
||||
const resolvedDecl = DeclarationUtils.resolveAliasDeclaration(declaration, importLookup);
|
||||
if (!resolvedDecl) {
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (resolvedDecl.type) {
|
||||
@ -109,7 +104,7 @@ export class HoverProvider {
|
||||
|
||||
case DeclarationType.Class: {
|
||||
if (node.nodeType === ParseNodeType.Name) {
|
||||
this._addResultsPart(parts, '(class) ' + this._getTypeText(node), true);
|
||||
this._addResultsPart(parts, '(class) ' + node.nameToken.value, true);
|
||||
this._addDocumentationPart(parts, node);
|
||||
return;
|
||||
}
|
||||
@ -139,7 +134,7 @@ export class HoverProvider {
|
||||
break;
|
||||
}
|
||||
|
||||
case DeclarationType.Module: {
|
||||
case DeclarationType.Alias: {
|
||||
if (node.nodeType === ParseNodeType.Name) {
|
||||
this._addResultsPart(parts, '(module) ' + node.nameToken.value, true);
|
||||
this._addDocumentationPart(parts, node);
|
||||
@ -150,46 +145,6 @@ export class HoverProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private static _addResultsForModuleNameNode(parts: HoverTextPart[], node: ModuleNameNode,
|
||||
offset: number, importMap: ImportMap) {
|
||||
|
||||
// If this is an imported module name, try to map the position
|
||||
// to the resolved import path.
|
||||
const importInfo = AnalyzerNodeInfo.getImportInfo(node);
|
||||
if (!importInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
let pathOffset = node.nameParts.findIndex(range => {
|
||||
return offset >= range.start && offset < TextRange.getEnd(range);
|
||||
});
|
||||
|
||||
if (pathOffset < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathOffset >= importInfo.resolvedPaths.length) {
|
||||
pathOffset = importInfo.resolvedPaths.length - 1;
|
||||
}
|
||||
|
||||
if (importInfo.resolvedPaths[pathOffset]) {
|
||||
const resolvedPath = importInfo.resolvedPaths[pathOffset];
|
||||
this._addResultsPart(parts, '(module) "' + resolvedPath + '"', true);
|
||||
|
||||
if (importInfo.importType === ImportType.ThirdParty && !importInfo.isStubFile) {
|
||||
this._addResultsPart(parts,
|
||||
'No type stub found for this module. Imported symbol types are unknown.');
|
||||
}
|
||||
|
||||
// If the module has been resolved and already analyzed,
|
||||
// we can add the docString for it as well.
|
||||
const moduleType = importMap.get(resolvedPath);
|
||||
if (moduleType) {
|
||||
this._addDocumentationPartForType(parts, moduleType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static _getTypeFromNode(node: ParseNode): Type | undefined {
|
||||
return AnalyzerNodeInfo.getExpressionType(node);
|
||||
}
|
||||
|
@ -8,11 +8,11 @@
|
||||
* by a location within a file.
|
||||
*/
|
||||
|
||||
import { ImportLookup } from '../analyzer/analyzerFileInfo';
|
||||
import { Declaration, DeclarationType } from '../analyzer/declaration';
|
||||
import * as DeclarationUtils from '../analyzer/declarationUtils';
|
||||
import * as ParseTreeUtils from '../analyzer/parseTreeUtils';
|
||||
import { ParseTreeWalker } from '../analyzer/parseTreeWalker';
|
||||
import { Symbol } from '../analyzer/symbol';
|
||||
import { DiagnosticTextPosition, DocumentTextRange } from '../common/diagnostic';
|
||||
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils';
|
||||
import { TextRange } from '../common/textRange';
|
||||
@ -31,15 +31,18 @@ class FindReferencesTreeWalker extends ParseTreeWalker {
|
||||
private _filePath: string;
|
||||
private _referencesResult: ReferencesResult;
|
||||
private _includeDeclaration: boolean;
|
||||
private _importLookup: ImportLookup;
|
||||
|
||||
constructor(parseResults: ParseResults, filePath: string,
|
||||
referencesResult: ReferencesResult, includeDeclaration: boolean) {
|
||||
referencesResult: ReferencesResult, includeDeclaration: boolean,
|
||||
importLookup: ImportLookup) {
|
||||
|
||||
super();
|
||||
this._parseResults = parseResults;
|
||||
this._filePath = filePath;
|
||||
this._referencesResult = referencesResult;
|
||||
this._includeDeclaration = includeDeclaration;
|
||||
this._importLookup = importLookup;
|
||||
}
|
||||
|
||||
findReferences() {
|
||||
@ -69,14 +72,22 @@ class FindReferencesTreeWalker extends ParseTreeWalker {
|
||||
}
|
||||
|
||||
private _resultsContainsDeclaration(declaration: Declaration) {
|
||||
const resolvedDecl = DeclarationUtils.resolveAliasDeclaration(declaration, this._importLookup);
|
||||
if (!resolvedDecl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The reference results declarations are already resolved, so we don't
|
||||
// need to call resolveAliasDeclaration on them.
|
||||
return this._referencesResult.declarations.some(decl =>
|
||||
DeclarationUtils.areDeclarationsSame(decl, declaration));
|
||||
DeclarationUtils.areDeclarationsSame(decl, resolvedDecl));
|
||||
}
|
||||
}
|
||||
|
||||
export class ReferencesProvider {
|
||||
static getReferencesForPosition(parseResults: ParseResults, filePath: string,
|
||||
position: DiagnosticTextPosition, includeDeclaration: boolean):
|
||||
position: DiagnosticTextPosition, includeDeclaration: boolean,
|
||||
importLookup: ImportLookup):
|
||||
ReferencesResult | undefined {
|
||||
|
||||
const offset = convertPositionToOffset(position, parseResults.tokenizerOutput.lines);
|
||||
@ -94,12 +105,24 @@ export class ReferencesProvider {
|
||||
}
|
||||
|
||||
const declarations = DeclarationUtils.getDeclarationsForNameNode(node);
|
||||
if (!declarations || declarations.length === 0) {
|
||||
if (!declarations) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const resolvedDeclarations: Declaration[] = [];
|
||||
declarations.forEach(decl => {
|
||||
const resovledDecl = DeclarationUtils.resolveAliasDeclaration(decl, importLookup);
|
||||
if (resovledDecl) {
|
||||
resolvedDeclarations.push(resovledDecl);
|
||||
}
|
||||
});
|
||||
|
||||
if (resolvedDeclarations.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Is this a type that potentially requires a global search?
|
||||
const symbolDeclType = declarations[0].type;
|
||||
const symbolDeclType = resolvedDeclarations[0].type;
|
||||
|
||||
// Parameters are local to a scope, so they don't require a global search.
|
||||
const requiresGlobalSearch = symbolDeclType !== DeclarationType.Parameter;
|
||||
@ -107,22 +130,23 @@ export class ReferencesProvider {
|
||||
const results: ReferencesResult = {
|
||||
requiresGlobalSearch,
|
||||
nodeAtOffset: node,
|
||||
declarations,
|
||||
declarations: resolvedDeclarations,
|
||||
locations: []
|
||||
};
|
||||
|
||||
const refTreeWalker = new FindReferencesTreeWalker(parseResults,
|
||||
filePath, results, includeDeclaration);
|
||||
filePath, results, includeDeclaration, importLookup);
|
||||
refTreeWalker.findReferences();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
static addReferences(parseResults: ParseResults, filePath: string,
|
||||
referencesResult: ReferencesResult, includeDeclaration: boolean): void {
|
||||
referencesResult: ReferencesResult, includeDeclaration: boolean,
|
||||
importLookup: ImportLookup): void {
|
||||
|
||||
const refTreeWalker = new FindReferencesTreeWalker(parseResults,
|
||||
filePath, referencesResult, includeDeclaration);
|
||||
filePath, referencesResult, includeDeclaration, importLookup);
|
||||
refTreeWalker.findReferences();
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ export interface ParseResults {
|
||||
importedModules: ModuleImport[];
|
||||
futureImports: StringMap<boolean>;
|
||||
tokenizerOutput: TokenizerOutput;
|
||||
containsWildcardImport: boolean;
|
||||
}
|
||||
|
||||
export interface ParseExpressionTextResults {
|
||||
@ -92,6 +93,7 @@ export class Parser {
|
||||
private _isParsingTypeAnnotation = false;
|
||||
private _futureImportMap = new StringMap<boolean>();
|
||||
private _importedModules: ModuleImport[] = [];
|
||||
private _containsWildcardImport = false;
|
||||
|
||||
parseSourceFile(fileContents: string, parseOptions: ParseOptions,
|
||||
diagSink: DiagnosticSink, cancelToken?: CancelToken): ParseResults {
|
||||
@ -132,7 +134,8 @@ export class Parser {
|
||||
parseTree: moduleNode,
|
||||
importedModules: this._importedModules,
|
||||
futureImports: this._futureImportMap,
|
||||
tokenizerOutput: this._tokenizerOutput!
|
||||
tokenizerOutput: this._tokenizerOutput!,
|
||||
containsWildcardImport: this._containsWildcardImport
|
||||
};
|
||||
}
|
||||
|
||||
@ -979,6 +982,7 @@ export class Parser {
|
||||
if (this._consumeTokenIfOperator(OperatorType.Multiply)) {
|
||||
extendRange(importFromNode, possibleStarToken);
|
||||
importFromNode.isWildcardImport = true;
|
||||
this._containsWildcardImport = true;
|
||||
} else {
|
||||
const inParen = this._consumeTokenIfType(TokenType.OpenParenthesis);
|
||||
|
||||
|
@ -15,8 +15,8 @@ import { AnalyzerFileInfo } from '../analyzer/analyzerFileInfo';
|
||||
import { ModuleScopeBinder } from '../analyzer/binder';
|
||||
import { ImportResolver } from '../analyzer/importResolver';
|
||||
import { Program } from '../analyzer/program';
|
||||
import { SymbolTable } from '../analyzer/symbol';
|
||||
import { TestWalker } from '../analyzer/testWalker';
|
||||
import { ModuleType } from '../analyzer/types';
|
||||
import { cloneDiagnosticSettings, ConfigOptions, ExecutionEnvironment } from '../common/configOptions';
|
||||
import { Diagnostic, DiagnosticCategory } from '../common/diagnostic';
|
||||
import { DiagnosticSink, TextRangeDiagnosticSink } from '../common/diagnosticSink';
|
||||
@ -77,7 +77,7 @@ export function buildAnalyzerFileInfo(filePath: string, parseResults: ParseResul
|
||||
const analysisDiagnostics = new TextRangeDiagnosticSink(parseResults.tokenizerOutput.lines);
|
||||
|
||||
const fileInfo: AnalyzerFileInfo = {
|
||||
importMap: new Map<string, ModuleType>(),
|
||||
importLookup: _ => undefined,
|
||||
futureImports: new StringMap<boolean>(),
|
||||
builtinsScope: undefined,
|
||||
diagnosticSink: analysisDiagnostics,
|
||||
|
Loading…
Reference in New Issue
Block a user