mirror of
https://github.com/microsoft/pyright.git
synced 2024-10-27 11:18:42 +03:00
Continued moving alias declaration creation from type analyzer to binder.
This commit is contained in:
parent
0c0dd705d0
commit
baa844938c
@ -19,7 +19,7 @@
|
|||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
|
|
||||||
import { DiagnosticLevel } from '../common/configOptions';
|
import { DiagnosticLevel } from '../common/configOptions';
|
||||||
import { CreateTypeStubFileAction, getEmptyPosition, getEmptyRange } from '../common/diagnostic';
|
import { CreateTypeStubFileAction, getEmptyRange } from '../common/diagnostic';
|
||||||
import { DiagnosticRule } from '../common/diagnosticRules';
|
import { DiagnosticRule } from '../common/diagnosticRules';
|
||||||
import { convertOffsetsToRange } from '../common/positionUtils';
|
import { convertOffsetsToRange } from '../common/positionUtils';
|
||||||
import { PythonVersion } from '../common/pythonVersion';
|
import { PythonVersion } from '../common/pythonVersion';
|
||||||
@ -594,15 +594,16 @@ export abstract class Binder extends ParseTreeWalker {
|
|||||||
|
|
||||||
visitImportAs(node: ImportAsNode): boolean {
|
visitImportAs(node: ImportAsNode): boolean {
|
||||||
if (node.module.nameParts.length > 0) {
|
if (node.module.nameParts.length > 0) {
|
||||||
let symbolName: string | undefined;
|
const firstNamePartValue = node.module.nameParts[0].nameToken.value;
|
||||||
|
|
||||||
|
let symbolName: string | undefined;
|
||||||
if (node.alias) {
|
if (node.alias) {
|
||||||
// The symbol name is defined by the alias.
|
// The symbol name is defined by the alias.
|
||||||
symbolName = node.alias.nameToken.value;
|
symbolName = node.alias.nameToken.value;
|
||||||
} else {
|
} else {
|
||||||
// There was no alias, so we need to use the first element of
|
// There was no alias, so we need to use the first element of
|
||||||
// the name parts as the symbol.
|
// the name parts as the symbol.
|
||||||
symbolName = node.module.nameParts[0].nameToken.value;
|
symbolName = firstNamePartValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (symbolName) {
|
if (symbolName) {
|
||||||
@ -612,21 +613,29 @@ export abstract class Binder extends ParseTreeWalker {
|
|||||||
assert(importInfo !== undefined);
|
assert(importInfo !== undefined);
|
||||||
|
|
||||||
if (importInfo && importInfo.isImportFound && importInfo.resolvedPaths.length > 0 && symbol) {
|
if (importInfo && importInfo.isImportFound && importInfo.resolvedPaths.length > 0 && symbol) {
|
||||||
const resolvedPath = importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1];
|
// 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(
|
const existingDecl = symbol.getDeclarations().find(
|
||||||
decl => decl.type === DeclarationType.Alias && decl.path === resolvedPath);
|
decl => decl.type === DeclarationType.Alias &&
|
||||||
|
decl.firstNamePart === firstNamePartValue);
|
||||||
|
|
||||||
const newDecl: AliasDeclaration = existingDecl as AliasDeclaration || {
|
const newDecl: AliasDeclaration = existingDecl as AliasDeclaration || {
|
||||||
type: DeclarationType.Alias,
|
type: DeclarationType.Alias,
|
||||||
path: '',
|
path: '',
|
||||||
range: getEmptyRange(),
|
range: getEmptyRange(),
|
||||||
|
firstNamePart: firstNamePartValue,
|
||||||
implicitImports: new Map<string, ModuleLoaderActions>()
|
implicitImports: new Map<string, ModuleLoaderActions>()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the implicit imports for this module if it's the last
|
// Add the implicit imports for this module if it's the last
|
||||||
// name part we're resolving.
|
// name part we're resolving.
|
||||||
if (node.alias || node.module.nameParts.length === 0) {
|
if (node.alias || node.module.nameParts.length === 1) {
|
||||||
newDecl.path = importInfo.resolvedPaths[0];
|
newDecl.path = importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1];
|
||||||
this._addImplicitImportsToLoaderActions(importInfo, newDecl);
|
this._addImplicitImportsToLoaderActions(importInfo, newDecl);
|
||||||
} else {
|
} else {
|
||||||
// Fill in the remaining name parts.
|
// Fill in the remaining name parts.
|
||||||
@ -1047,8 +1056,8 @@ export class ModuleScopeBinder extends Binder {
|
|||||||
this.walkMultiple(moduleNode.statements);
|
this.walkMultiple(moduleNode.statements);
|
||||||
|
|
||||||
// Associate the module's scope with the module type.
|
// Associate the module's scope with the module type.
|
||||||
const moduleType = ModuleType.create(this._currentScope.getSymbolTable(),
|
const moduleType = ModuleType.create(this._currentScope.getSymbolTable());
|
||||||
this._getDocString((this._scopedNode as ModuleNode).statements));
|
moduleType.docString = this._getDocString((this._scopedNode as ModuleNode).statements);
|
||||||
AnalyzerNodeInfo.setExpressionType(this._scopedNode, moduleType);
|
AnalyzerNodeInfo.setExpressionType(this._scopedNode, moduleType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ export interface VariableDeclaration extends DeclarationBase {
|
|||||||
|
|
||||||
// Alias declarations are used for imports. They are resolved
|
// Alias declarations are used for imports. They are resolved
|
||||||
// after the binding phase.
|
// after the binding phase.
|
||||||
export interface AliasDeclaration extends DeclarationBase, ModuleLoaderActions {
|
export interface AliasDeclaration extends DeclarationBase {
|
||||||
type: DeclarationType.Alias;
|
type: DeclarationType.Alias;
|
||||||
|
|
||||||
// If a symbolName is defined, the alias refers to a symbol
|
// If a symbolName is defined, the alias refers to a symbol
|
||||||
@ -79,6 +79,18 @@ export interface AliasDeclaration extends DeclarationBase, ModuleLoaderActions {
|
|||||||
// 'path' field). If symbolName is missing, the alias refers
|
// 'path' field). If symbolName is missing, the alias refers
|
||||||
// to the module itself.
|
// to the module itself.
|
||||||
symbolName?: string;
|
symbolName?: string;
|
||||||
|
|
||||||
|
// 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
|
// This interface represents a set of actions that the python loader
|
||||||
@ -89,11 +101,7 @@ export interface ModuleLoaderActions {
|
|||||||
// a directory).
|
// a directory).
|
||||||
path: string;
|
path: string;
|
||||||
|
|
||||||
// If the alias is targeting a module, multiple other modules
|
// See comment for "implicitImports" field in AliasDeclaration.
|
||||||
// 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>;
|
implicitImports: Map<string, ModuleLoaderActions>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1296,75 +1296,61 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitImportAs(node: ImportAsNode): boolean {
|
visitImportAs(node: ImportAsNode): boolean {
|
||||||
|
if (node.module.nameParts.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const importInfo = AnalyzerNodeInfo.getImportInfo(node.module);
|
const importInfo = AnalyzerNodeInfo.getImportInfo(node.module);
|
||||||
assert(importInfo !== undefined);
|
assert(importInfo !== undefined);
|
||||||
|
|
||||||
if (importInfo && importInfo.isImportFound && importInfo.resolvedPaths.length > 0) {
|
let symbolNameNode: NameNode;
|
||||||
const resolvedPath = importInfo.resolvedPaths[importInfo.resolvedPaths.length - 1];
|
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);
|
const symbolWithScope = this._currentScope.lookUpSymbolRecursive(
|
||||||
|
symbolNameNode.nameToken.value);
|
||||||
|
assert(symbolWithScope !== undefined);
|
||||||
|
const aliasDecl = symbolWithScope!.symbol.getDeclarations().find(
|
||||||
|
decl => decl.type === DeclarationType.Alias);
|
||||||
|
|
||||||
// Is there a cached module type associated with this node?
|
let symbolType: Type | undefined;
|
||||||
|
if (aliasDecl && aliasDecl.type === DeclarationType.Alias) {
|
||||||
|
symbolType = this._getSymbolTypeForAliasDeclaration(aliasDecl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!symbolType) {
|
||||||
|
symbolType = UnknownType.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
const cachedModuleType = AnalyzerNodeInfo.getExpressionType(node) as ModuleType;
|
||||||
if (cachedModuleType && cachedModuleType.category === TypeCategory.Module && moduleType) {
|
if (cachedModuleType && cachedModuleType.category === TypeCategory.Module && symbolType) {
|
||||||
// If the fields match, use the cached version instead of the
|
if (isTypeSame(symbolType, cachedModuleType)) {
|
||||||
// newly-created version so we preserve the work done to
|
symbolType = cachedModuleType;
|
||||||
// populate the loader fields.
|
|
||||||
if (moduleType.fields === cachedModuleType.fields) {
|
|
||||||
moduleType = cachedModuleType;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moduleType) {
|
// Cache the module type for subsequent passes.
|
||||||
// Import the implicit imports in the module's namespace.
|
AnalyzerNodeInfo.setExpressionType(node, symbolType);
|
||||||
importInfo.implicitImports.forEach(implicitImport => {
|
|
||||||
const implicitModuleType = this._getModuleTypeForImportPath(
|
this._assignTypeToNameNode(symbolNameNode, symbolType);
|
||||||
importInfo, implicitImport.path);
|
this._updateExpressionTypeForNode(symbolNameNode, symbolType);
|
||||||
if (implicitModuleType) {
|
|
||||||
if (!ModuleType.getField(moduleType!, implicitImport.name)) {
|
|
||||||
const newSymbol = Symbol.createWithType(
|
|
||||||
SymbolFlags.ClassMember, implicitModuleType, defaultTypeSourceId);
|
|
||||||
setSymbolPreservingAccess(moduleType!.loaderFields!, implicitImport.name,
|
|
||||||
newSymbol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (node.alias) {
|
if (node.alias) {
|
||||||
this._assignTypeToNameNode(node.alias, moduleType);
|
this._conditionallyReportUnusedName(symbolNameNode, false,
|
||||||
this._updateExpressionTypeForNode(node.alias, moduleType);
|
|
||||||
|
|
||||||
this._conditionallyReportUnusedName(node.alias, false,
|
|
||||||
this._fileInfo.diagnosticSettings.reportUnusedImport,
|
this._fileInfo.diagnosticSettings.reportUnusedImport,
|
||||||
DiagnosticRule.reportUnusedImport,
|
DiagnosticRule.reportUnusedImport,
|
||||||
`Import '${ node.alias.nameToken.value }' is not accessed`);
|
`Import '${ node.alias.nameToken.value }' is not accessed`);
|
||||||
} else {
|
} else {
|
||||||
this._bindMultiPartModuleNameToType(node.module.nameParts, moduleType);
|
// TODO - need to add unused import logic
|
||||||
}
|
|
||||||
|
|
||||||
// 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`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -1559,6 +1545,36 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getSymbolTypeForAliasDeclaration(declaration: AliasDeclaration): ModuleType | undefined {
|
||||||
|
// This call doesn't apply to alias declarations with a symbol name.
|
||||||
|
assert(declaration.symbolName === undefined);
|
||||||
|
|
||||||
|
// Build a module type that corresponds to the declaration and
|
||||||
|
// its associated loader actions.
|
||||||
|
const moduleType = ModuleType.create();
|
||||||
|
this._applyLoaderActionsToModuleType(moduleType, declaration);
|
||||||
|
return moduleType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _applyLoaderActionsToModuleType(moduleType: ModuleType, loaderActions: ModuleLoaderActions) {
|
||||||
|
if (loaderActions.path) {
|
||||||
|
const moduleSymbolTable = this._fileInfo.importLookup(loaderActions.path);
|
||||||
|
if (moduleSymbolTable) {
|
||||||
|
moduleType.fields = moduleSymbolTable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// Validates that a call to isinstance is necessary. This is a
|
||||||
// common source of programming errors.
|
// common source of programming errors.
|
||||||
private _validateIsInstanceCallNecessary(node: CallExpressionNode) {
|
private _validateIsInstanceCallNecessary(node: CallExpressionNode) {
|
||||||
@ -3022,21 +3038,20 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
|||||||
|
|
||||||
const symbolTable = this._fileInfo.importLookup(path);
|
const symbolTable = this._fileInfo.importLookup(path);
|
||||||
if (symbolTable) {
|
if (symbolTable) {
|
||||||
return ModuleType.cloneForLoadedModule(ModuleType.create(symbolTable));
|
return ModuleType.create(symbolTable);
|
||||||
} else if (importResult) {
|
} else if (importResult) {
|
||||||
// There was no module even though the import was resolved. This
|
// There was no module even though the import was resolved. This
|
||||||
// happens in the case of namespace packages, where an __init__.py
|
// happens in the case of namespace packages, where an __init__.py
|
||||||
// is not necessarily present. We'll synthesize a module type in
|
// is not necessarily present. We'll synthesize a module type in
|
||||||
// this case.
|
// this case.
|
||||||
const moduleType = ModuleType.cloneForLoadedModule(
|
const moduleType = ModuleType.create();
|
||||||
ModuleType.create(new SymbolTable()));
|
|
||||||
|
|
||||||
// Add the implicit imports.
|
// Add the implicit imports.
|
||||||
importResult.implicitImports.forEach(implicitImport => {
|
importResult.implicitImports.forEach(implicitImport => {
|
||||||
const implicitModuleType = this._getModuleTypeForImportPath(
|
const implicitModuleType = this._getModuleTypeForImportPath(
|
||||||
undefined, implicitImport.path);
|
undefined, implicitImport.path);
|
||||||
if (implicitModuleType) {
|
if (implicitModuleType) {
|
||||||
setSymbolPreservingAccess(moduleType.loaderFields!, implicitImport.name,
|
setSymbolPreservingAccess(moduleType.loaderFields, implicitImport.name,
|
||||||
Symbol.createWithType(
|
Symbol.createWithType(
|
||||||
SymbolFlags.ClassMember, implicitModuleType, defaultTypeSourceId));
|
SymbolFlags.ClassMember, implicitModuleType, defaultTypeSourceId));
|
||||||
}
|
}
|
||||||
@ -3329,83 +3344,6 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
|||||||
this._evaluateExpressionForAssignment(target, srcType, srcExpr);
|
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,
|
private _assignTypeToNameNode(nameNode: NameNode, srcType: Type, declaration?: Declaration,
|
||||||
srcExpressionNode?: ParseNode) {
|
srcExpressionNode?: ParseNode) {
|
||||||
|
|
||||||
|
@ -115,24 +115,14 @@ export interface ModuleType extends TypeBase {
|
|||||||
// A "loader" module includes symbols that were injected by
|
// A "loader" module includes symbols that were injected by
|
||||||
// the module loader. We keep these separate so we don't
|
// the module loader. We keep these separate so we don't
|
||||||
// pollute the symbols exported by the module itself.
|
// pollute the symbols exported by the module itself.
|
||||||
loaderFields?: SymbolTable;
|
loaderFields: SymbolTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace ModuleType {
|
export namespace ModuleType {
|
||||||
export function create(fields: SymbolTable, docString?: string) {
|
export function create(symbolTable?: SymbolTable) {
|
||||||
const newModuleType: ModuleType = {
|
const newModuleType: ModuleType = {
|
||||||
category: TypeCategory.Module,
|
category: TypeCategory.Module,
|
||||||
fields,
|
fields: symbolTable || new SymbolTable(),
|
||||||
docString
|
|
||||||
};
|
|
||||||
return newModuleType;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function cloneForLoadedModule(moduleType: ModuleType) {
|
|
||||||
const newModuleType: ModuleType = {
|
|
||||||
category: TypeCategory.Module,
|
|
||||||
fields: moduleType.fields,
|
|
||||||
docString: moduleType.docString,
|
|
||||||
loaderFields: new SymbolTable()
|
loaderFields: new SymbolTable()
|
||||||
};
|
};
|
||||||
return newModuleType;
|
return newModuleType;
|
||||||
@ -1223,7 +1213,17 @@ export function isTypeSame(type1: Type, type2: Type, recursionCount = 0): boolea
|
|||||||
|
|
||||||
// Module types are the same if they share the same
|
// Module types are the same if they share the same
|
||||||
// module symbol table.
|
// module symbol table.
|
||||||
return (type1.fields === type2Module.fields);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user