Merge branch 'move_declarations6'

This commit is contained in:
Eric Traut 2019-10-20 02:22:37 -07:00
commit 361dbbd072
23 changed files with 831 additions and 668 deletions

View File

@ -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",

View File

@ -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
View File

@ -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
}
}

View File

@ -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": {

View File

@ -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": {

View File

@ -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",

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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) {

View File

@ -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];
}
}

View File

@ -160,7 +160,7 @@ export class Symbol {
getTypedDeclarations() {
return this.getDeclarations().filter(
decl => hasTypeForDeclaration(decl, false));
decl => hasTypeForDeclaration(decl));
}
}

View File

@ -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) {

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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();
}
}

View File

@ -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);

View File

@ -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,