Use the isDeclarationVisible in checker to determine if the declaration needs to be emitted

This would help in unifying logic of when to check if the type is visible
This commit is contained in:
Sheetal Nandi 2014-07-16 11:37:49 -07:00
parent 5a23dd0684
commit 148abea09d
5 changed files with 151 additions and 64 deletions

View File

@ -917,6 +917,125 @@ module ts {
}
}
function isDeclarationVisible(node: Declaration): boolean {
function isGlobalSourceFile(node: Node) {
return node.kind === SyntaxKind.SourceFile && !(node.flags & NodeFlags.ExternalModule);
}
function getExternalModule(node: Node) {
for (; node; node = node.parent) {
if (node.kind === SyntaxKind.ModuleDeclaration) {
if ((<ModuleDeclaration>node).name.kind === SyntaxKind.StringLiteral) {
return node;
}
}
else if (node.kind === SyntaxKind.SourceFile) {
return (node.flags & NodeFlags.ExternalModule) ? node : null;
}
}
}
function isUsedInExportAssignment(node: Node) {
// Get source File and see if it is external module and has export assigned symbol
var externalModule = getExternalModule(node);
if (externalModule) {
// This is export assigned symbol node
var externalModuleSymbol = getSymbolOfNode(externalModule);
var exportAssignmentSymbol = getExportAssignmentSymbol(externalModuleSymbol);
var symbolOfNode = getSymbolOfNode(node);
if (exportAssignmentSymbol === symbolOfNode) {
return true;
}
if (exportAssignmentSymbol && !!(exportAssignmentSymbol.flags & SymbolFlags.Import)) {
// if export assigned symbol is import declaration, resolve the import
var resolvedExportSymbol = resolveImport(exportAssignmentSymbol);
if (resolvedExportSymbol === symbolOfNode) {
return true;
}
// TODO(shkamat): Chained import assignment
// eg. a should be visible too.
//module m {
// export module c {
// export class c {
// }
// }
//}
//import a = m.c;
//import b = a;
//export = b;
// Container of resolvedExportSymbol is visible
return forEach(resolvedExportSymbol.declarations, declaration => {
while (declaration) {
if (declaration === node) {
return true;
}
declaration = declaration.parent;
}
});
}
}
}
function determineIfDeclarationIsVisible() {
switch (node.kind) {
case SyntaxKind.VariableDeclaration:
if (!(node.flags & NodeFlags.Export)) {
// node.parent is variable statement so look at the variable statement's parent
return isGlobalSourceFile(node.parent.parent) || isUsedInExportAssignment(node);
}
// Exported members are visible if parent is visible
return isDeclarationVisible(node.parent.parent);
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.ImportDeclaration:
if (!(node.flags & NodeFlags.Export)) {
// TODO(shkamat): non exported aliases can be visible if they are referenced else where for value/type/namespace
return isGlobalSourceFile(node.parent) || isUsedInExportAssignment(node);
}
// Exported members are visible if parent is visible
return isDeclarationVisible(node.parent);
case SyntaxKind.Property:
case SyntaxKind.Method:
if (node.flags & NodeFlags.Private) {
// Private properties/methods are not visible
return false;
}
// Public properties/methods are visible if its parents are visible, so let it fall into next case statement
case SyntaxKind.Constructor:
case SyntaxKind.ConstructSignature:
case SyntaxKind.CallSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.Parameter:
case SyntaxKind.ModuleBlock:
return isDeclarationVisible(node.parent);
// Source file is always visible
case SyntaxKind.SourceFile:
return true;
default:
Debug.fail("isDeclarationVisible unknown: SyntaxKind: " + SyntaxKind[node.kind]);
}
}
if (node) {
var links = getNodeLinks(node);
if (links.isVisible === undefined) {
links.isVisible = determineIfDeclarationIsVisible();
}
return links.isVisible;
}
}
function getApparentType(type: Type): ApparentType {
if (type.flags & TypeFlags.TypeParameter) {
do {
@ -4892,7 +5011,7 @@ module ts {
checkTypeAssignableTo(checkAndMarkExpression(node.initializer, type), type, node, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
}
}
checkCollisionWithCapturedSuperVariable(node, node.name);
if (!useTypeFromValueDeclaration) {
// TypeScript 1.0 spec (April 2014): 5.1
@ -5870,22 +5989,6 @@ module ts {
return false;
}
function isReferencedInExportAssignment(node: Declaration): boolean {
var exportAssignedSymbol = getExportAssignmentSymbol(getSymbolOfNode(getContainerOfModuleElementDeclaration(node)));
if (exportAssignedSymbol) {
var symbol = getSymbolOfNode(node);
if (exportAssignedSymbol === symbol) {
// This symbol was export assigned symbol
return true;
}
// TODO(shkamat): if export assignment is alias, the alias target would make the node as referenced in export assignment
}
return false;
}
function isImplementationOfOverload(node: FunctionDeclaration) {
if (node.body) {
var symbol = getSymbolOfNode(node);
@ -5927,7 +6030,7 @@ module ts {
getEnumMemberValue: getEnumMemberValue,
isTopLevelValueImportedViaEntityName: isTopLevelValueImportedViaEntityName,
shouldEmitDeclarations: shouldEmitDeclarations,
isReferencedInExportAssignment: isReferencedInExportAssignment,
isDeclarationVisible: isDeclarationVisible,
isImplementationOfOverload: isImplementationOfOverload,
writeTypeAtLocation: writeTypeAtLocation,
writeReturnTypeOfSignatureDeclaration: writeReturnTypeOfSignatureDeclaration

View File

@ -1881,34 +1881,6 @@ module ts {
writeLine();
}
function isModuleElementExternallyVisible(node: Declaration) {
if (node.flags & NodeFlags.Export) {
// Exported member - emit this declaration
return true;
}
// If this node is in external module, check if this is export assigned
var moduleDeclaration = getContainerOfModuleElementDeclaration(node);
if ((moduleDeclaration.flags & NodeFlags.ExternalModule) || // Source file with external module flag
// Ambient external module declaration
(moduleDeclaration.kind === SyntaxKind.ModuleDeclaration && (<ModuleDeclaration>moduleDeclaration).name.kind === SyntaxKind.StringLiteral)) {
return resolver.isReferencedInExportAssignment(node);
}
return false;
}
function canEmitModuleElementDeclaration(node: Declaration) {
if (isModuleElementExternallyVisible(node)) {
// Either exported module element or is referenced in export assignment
return true;
}
// emit the declaration if this is in global scope source file
var moduleDeclaration = getContainerOfModuleElementDeclaration(node);
return moduleDeclaration.kind === SyntaxKind.SourceFile && !(moduleDeclaration.flags & NodeFlags.ExternalModule);
}
function emitDeclarationFlags(node: Declaration) {
if (node.flags & NodeFlags.Static) {
if (node.flags & NodeFlags.Private) {
@ -1935,8 +1907,7 @@ module ts {
}
function emitImportDeclaration(node: ImportDeclaration) {
// TODO(shkamat): Emit if import decl is used to declare type in this context
if (isModuleElementExternallyVisible(node)) {
if (resolver.isDeclarationVisible(node)) {
if (node.flags & NodeFlags.Export) {
write("export ");
}
@ -1957,7 +1928,7 @@ module ts {
}
function emitModuleDeclaration(node: ModuleDeclaration) {
if (canEmitModuleElementDeclaration(node)) {
if (resolver.isDeclarationVisible(node)) {
emitDeclarationFlags(node);
write("module ");
emitSourceTextOfNode(node.name);
@ -1980,7 +1951,7 @@ module ts {
}
function emitEnumDeclaration(node: EnumDeclaration) {
if (canEmitModuleElementDeclaration(node)) {
if (resolver.isDeclarationVisible(node)) {
emitDeclarationFlags(node);
write("enum ");
emitSourceTextOfNode(node.name);
@ -2043,7 +2014,7 @@ module ts {
}
}
if (canEmitModuleElementDeclaration(node)) {
if (resolver.isDeclarationVisible(node)) {
emitDeclarationFlags(node);
write("class ");
emitSourceTextOfNode(node.name);
@ -2067,7 +2038,7 @@ module ts {
}
function emitInterfaceDeclaration(node: InterfaceDeclaration) {
if (canEmitModuleElementDeclaration(node)) {
if (resolver.isDeclarationVisible(node)) {
emitDeclarationFlags(node);
write("interface ");
emitSourceTextOfNode(node.name);
@ -2094,8 +2065,9 @@ module ts {
}
function emitVariableDeclaration(node: VariableDeclaration) {
// If we are emitting property it isnt moduleElement and doesnt need canEmitModuleElement check
if (node.kind !== SyntaxKind.VariableDeclaration || canEmitModuleElementDeclaration(node)) {
// If we are emitting property it isnt moduleElement and hence we already know it needs to be emitted
// so there is no check needed to see if declaration is visible
if (node.kind !== SyntaxKind.VariableDeclaration || resolver.isDeclarationVisible(node)) {
emitSourceTextOfNode(node.name);
// If optional property emit ?
if (node.kind === SyntaxKind.Property && (node.flags & NodeFlags.QuestionMark)) {
@ -2109,7 +2081,7 @@ module ts {
}
function emitVariableStatement(node: VariableStatement) {
var hasDeclarationWithEmit = forEach(node.declarations, varDeclaration => canEmitModuleElementDeclaration(varDeclaration));
var hasDeclarationWithEmit = forEach(node.declarations, varDeclaration => resolver.isDeclarationVisible(varDeclaration));
if (hasDeclarationWithEmit) {
emitDeclarationFlags(node);
write("var ");
@ -2134,8 +2106,9 @@ module ts {
}
function emitFunctionDeclaration(node: FunctionDeclaration) {
// If we are emitting Method/Constructor it isnt moduleElement and doesnt need canEmitModuleElement check
if ((node.kind !== SyntaxKind.FunctionDeclaration || canEmitModuleElementDeclaration(node)) &&
// If we are emitting Method/Constructor it isnt moduleElement and hence already determined to be emitting
// so no need to verify if the declaration is visible
if ((node.kind !== SyntaxKind.FunctionDeclaration || resolver.isDeclarationVisible(node)) &&
!resolver.isImplementationOfOverload(node)) {
emitDeclarationFlags(node);
if (node.kind === SyntaxKind.FunctionDeclaration) {

View File

@ -273,12 +273,6 @@ module ts {
return s.parameters.length > 0 && (s.parameters[s.parameters.length - 1].flags & NodeFlags.Rest) !== 0;
}
export function getContainerOfModuleElementDeclaration(node: Declaration) {
// If the declaration is var declaration, then the parent is variable statement but we actually want the module
var container = node.kind === SyntaxKind.VariableDeclaration ? node.parent.parent : node.parent;
return container.kind == SyntaxKind.ModuleBlock ? container.parent : container;
}
enum ParsingContext {
SourceElements, // Elements in source file
ModuleElements, // Elements in module declaration

View File

@ -616,7 +616,7 @@ module ts {
getNodeCheckFlags(node: Node): NodeCheckFlags;
getEnumMemberValue(node: EnumMember): number;
shouldEmitDeclarations(): boolean;
isReferencedInExportAssignment(node: Declaration): boolean;
isDeclarationVisible(node: Declaration): boolean;
isImplementationOfOverload(node: FunctionDeclaration): boolean;
writeTypeAtLocation(location: Node, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter): void;
writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter): void;
@ -736,6 +736,7 @@ module ts {
flags?: NodeCheckFlags; // Set of flags specific to Node
enumMemberValue?: number; // Constant value of enum member
isIllegalTypeReferenceInConstraint?: boolean; // Is type reference in constraint refers to the type parameter from the same list
isVisible?: boolean; // Is this node visible
}
export enum TypeFlags {

View File

@ -31,5 +31,21 @@ module.exports = m;
//// [declFileExportAssignmentImportInternalModule.d.ts]
declare module m3 {
module m2 {
interface connectModule {
(res: any, req: any, next: any): void;
}
interface connectExport {
use: (mod: connectModule) => connectExport;
listen: (port: number) => void;
}
}
var server: {
(): m2.connectExport;
test1: m2.connectModule;
test2(): m2.connectModule;
};
}
import m = m3;
export = m;