Skip to content

Commit 94544b3

Browse files
committed
Merge pull request #414 from Microsoft/typeOfFunctionAndStaticFunction
Emit Type of function and static function correctly in declaration file
2 parents 1347621 + b175b09 commit 94544b3

37 files changed

+2926
-134
lines changed

src/compiler/checker.ts

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,11 @@ module ts {
661661
return callback(globals);
662662
}
663663

664+
function getQualifiedLeftMeaning(rightMeaning: SymbolFlags) {
665+
// If we are looking in value space, the parent meaning is value, other wise it is namespace
666+
return rightMeaning === SymbolFlags.Value ? SymbolFlags.Value : SymbolFlags.Namespace;
667+
}
668+
664669
function getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): Symbol[] {
665670
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable): Symbol[] {
666671
function canQualifySymbol(symbolFromSymbolTable: Symbol, meaning: SymbolFlags) {
@@ -670,15 +675,17 @@ module ts {
670675
}
671676

672677
// If symbol needs qualification, make sure that parent is accessible, if it is then this symbol is accessible too
673-
var accessibleParent = getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, SymbolFlags.Namespace);
678+
var accessibleParent = getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, getQualifiedLeftMeaning(meaning));
674679
return !!accessibleParent;
675680
}
676681

677682
function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol) {
678683
if (symbol === (resolvedAliasSymbol || symbolFromSymbolTable)) {
679-
// if symbolfrom symbolTable or alias resolution matches the symbol,
684+
// if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table)
685+
// and if symbolfrom symbolTable or alias resolution matches the symbol,
680686
// check the symbol can be qualified, it is only then this symbol is accessible
681-
return canQualifySymbol(symbolFromSymbolTable, meaning);
687+
return !forEach(symbolFromSymbolTable.declarations, declaration => hasExternalModuleSymbol(declaration)) &&
688+
canQualifySymbol(symbolFromSymbolTable, meaning);
682689
}
683690
}
684691

@@ -698,7 +705,7 @@ module ts {
698705
// Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain
699706
// but only if the symbolFromSymbolTable can be qualified
700707
var accessibleSymbolsFromExports = resolvedImportedSymbol.exports ? getAccessibleSymbolChainFromSymbolTable(resolvedImportedSymbol.exports) : undefined;
701-
if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, SymbolFlags.Namespace)) {
708+
if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) {
702709
return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports);
703710
}
704711
}
@@ -758,8 +765,6 @@ module ts {
758765
return { accessibility: SymbolAccessibility.Accessible, aliasesToMakeVisible: hasAccessibleDeclarations.aliasesToMakeVisible };
759766
}
760767

761-
// TODO(shkamat): Handle static method of class
762-
763768
// If we havent got the accessible symbol doesnt mean the symbol is actually inaccessible.
764769
// It could be qualified symbol and hence verify the path
765770
// eg:
@@ -772,18 +777,46 @@ module ts {
772777
// we are going to see if c can be accessed in scope directly.
773778
// But it cant, hence the accessible is going to be undefined, but that doesnt mean m.c is accessible
774779
// It is accessible if the parent m is accessible because then m.c can be accessed through qualification
775-
meaningToLook = SymbolFlags.Namespace;
780+
meaningToLook = getQualifiedLeftMeaning(meaning);
776781
symbol = symbol.parent;
777782
}
778783

779-
// This is a local symbol that cannot be named
784+
// This could be a symbol that is not exported in the external module
785+
// or it could be a symbol from different external module that is not aliased and hence cannot be named
786+
var symbolExternalModule = forEach(initialSymbol.declarations, declaration => getExternalModuleContainer(declaration));
787+
if (symbolExternalModule) {
788+
var enclosingExternalModule = getExternalModuleContainer(enclosingDeclaration);
789+
if (symbolExternalModule !== enclosingExternalModule) {
790+
// name from different external module that is not visibile
791+
return {
792+
accessibility: SymbolAccessibility.CannotBeNamed,
793+
errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning),
794+
errorModuleName: symbolToString(symbolExternalModule)
795+
};
796+
}
797+
}
798+
799+
// Just a local name that is not accessible
780800
return {
781-
accessibility: SymbolAccessibility.CannotBeNamed,
801+
accessibility: SymbolAccessibility.NotAccessible,
782802
errorSymbolName: symbolToString(initialSymbol, enclosingDeclaration, meaning),
783803
};
784804
}
785805

786806
return { accessibility: SymbolAccessibility.Accessible };
807+
808+
function getExternalModuleContainer(declaration: Declaration) {
809+
for (; declaration; declaration = declaration.parent) {
810+
if (hasExternalModuleSymbol(declaration)) {
811+
return getSymbolOfNode(declaration);
812+
}
813+
}
814+
}
815+
}
816+
817+
function hasExternalModuleSymbol(declaration: Declaration) {
818+
return (declaration.kind === SyntaxKind.ModuleDeclaration && declaration.name.kind === SyntaxKind.StringLiteral) ||
819+
(declaration.kind === SyntaxKind.SourceFile && isExternalModule(<SourceFile>declaration));
787820
}
788821

789822
function hasVisibleDeclarations(symbol: Symbol): { aliasesToMakeVisible?: ImportDeclaration[]; } {
@@ -851,14 +884,25 @@ module ts {
851884
var symbolName: string;
852885
while (symbol) {
853886
var isFirstName = !symbolName;
854-
var meaningToLook = isFirstName ? meaning : SymbolFlags.Namespace;
855-
var accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaningToLook);
856-
var currentSymbolName = accessibleSymbolChain ? ts.map(accessibleSymbolChain, accessibleSymbol => getSymbolName(accessibleSymbol)).join(".") : getSymbolName(symbol);
887+
var accessibleSymbolChain = getAccessibleSymbolChain(symbol, enclosingDeclaration, meaning);
888+
889+
var currentSymbolName: string;
890+
if (accessibleSymbolChain) {
891+
currentSymbolName = ts.map(accessibleSymbolChain, accessibleSymbol => getSymbolName(accessibleSymbol)).join(".");
892+
}
893+
else {
894+
// If we didnt find accessible symbol chain for this symbol, break if this is external module
895+
if (!isFirstName && ts.forEach(symbol.declarations, declaration => hasExternalModuleSymbol(declaration))) {
896+
break;
897+
}
898+
currentSymbolName = getSymbolName(symbol);
899+
}
857900
symbolName = currentSymbolName + (isFirstName ? "" : ("." + symbolName));
858-
if (accessibleSymbolChain && !needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaningToLook : SymbolFlags.Namespace)) {
901+
if (accessibleSymbolChain && !needsQualification(accessibleSymbolChain[0], enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) {
859902
break;
860903
}
861904
symbol = accessibleSymbolChain ? accessibleSymbolChain[0].parent : symbol.parent;
905+
meaning = getQualifiedLeftMeaning(meaning);
862906
}
863907

864908
return symbolName;
@@ -942,10 +986,13 @@ module ts {
942986
writeTypeofSymbol(type);
943987
}
944988
// Use 'typeof T' for types of functions and methods that circularly reference themselves
945-
// TODO(shkamat): correct the usuage of typeof function - always on functions that are visible
946-
else if (type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && typeStack && contains(typeStack, type)) {
989+
else if (shouldWriteTypeOfFunctionSymbol()) {
947990
writeTypeofSymbol(type);
948991
}
992+
else if (typeStack && contains(typeStack, type)) {
993+
// Recursive usage, use any
994+
writer.write("any");
995+
}
949996
else {
950997
if (!typeStack) {
951998
typeStack = [];
@@ -954,6 +1001,23 @@ module ts {
9541001
writeLiteralType(type, allowFunctionOrConstructorTypeLiteral);
9551002
typeStack.pop();
9561003
}
1004+
1005+
function shouldWriteTypeOfFunctionSymbol() {
1006+
if (type.symbol) {
1007+
var isStaticMethodSymbol = !!(type.symbol.flags & SymbolFlags.Method && // typeof static method
1008+
ts.forEach(type.symbol.declarations, declaration => declaration.flags & NodeFlags.Static));
1009+
var isNonLocalFunctionSymbol = !!(type.symbol.flags & SymbolFlags.Function) &&
1010+
(type.symbol.parent || // is exported function symbol
1011+
ts.forEach(type.symbol.declarations, declaration =>
1012+
declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock));
1013+
1014+
if (isStaticMethodSymbol || isNonLocalFunctionSymbol) {
1015+
// typeof is allowed only for static/non local functions
1016+
return !!(flags & TypeFormatFlags.UseTypeOfFunction) || // use typeof if format flags specify it
1017+
(typeStack && contains(typeStack, type)); // it is type of the symbol uses itself recursively
1018+
}
1019+
}
1020+
}
9571021
}
9581022

9591023
function writeTypeofSymbol(type: ObjectType) {

src/compiler/diagnosticInformationMap.generated.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,18 @@ module ts {
231231
A_parameter_property_is_only_allowed_in_a_constructor_implementation: { code: 2246, category: DiagnosticCategory.Error, key: "A parameter property is only allowed in a constructor implementation." },
232232
Function_overload_must_be_static: { code: 2247, category: DiagnosticCategory.Error, key: "Function overload must be static." },
233233
Function_overload_must_not_be_static: { code: 2248, category: DiagnosticCategory.Error, key: "Function overload must not be static." },
234+
Public_static_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named: { code: 2249, category: DiagnosticCategory.Error, key: "Public static property '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named." },
235+
Public_property_0_of_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named: { code: 2250, category: DiagnosticCategory.Error, key: "Public property '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named." },
236+
Exported_variable_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named: { code: 2251, category: DiagnosticCategory.Error, key: "Exported variable '{0}' has or is using name '{1}' from external module {2} but cannot be named." },
237+
Parameter_0_of_constructor_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named: { code: 2252, category: DiagnosticCategory.Error, key: "Parameter '{0}' of constructor from exported class has or is using name '{1}' from external module {2} but cannot be named." },
238+
Parameter_0_of_public_static_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named: { code: 2253, category: DiagnosticCategory.Error, key: "Parameter '{0}' of public static method from exported class has or is using name '{1}' from external module {2} but cannot be named." },
239+
Parameter_0_of_public_method_from_exported_class_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named: { code: 2254, category: DiagnosticCategory.Error, key: "Parameter '{0}' of public method from exported class has or is using name '{1}' from external module {2} but cannot be named." },
240+
Parameter_0_of_exported_function_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named: { code: 2255, category: DiagnosticCategory.Error, key: "Parameter '{0}' of exported function has or is using name '{1}' from external module {2} but cannot be named." },
241+
Return_type_of_public_static_property_getter_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named: { code: 2256, category: DiagnosticCategory.Error, key: "Return type of public static property getter from exported class has or is using name '{0}' from external module {1} but cannot be named." },
242+
Return_type_of_public_property_getter_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named: { code: 2257, category: DiagnosticCategory.Error, key: "Return type of public property getter from exported class has or is using name '{0}' from external module {1} but cannot be named." },
243+
Return_type_of_public_static_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named: { code: 2258, category: DiagnosticCategory.Error, key: "Return type of public static method from exported class has or is using name '{0}' from external module {1} but cannot be named." },
244+
Return_type_of_public_method_from_exported_class_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named: { code: 2259, category: DiagnosticCategory.Error, key: "Return type of public method from exported class has or is using name '{0}' from external module {1} but cannot be named." },
245+
Return_type_of_exported_function_has_or_is_using_name_0_from_external_module_1_but_cannot_be_named: { code: 2260, category: DiagnosticCategory.Error, key: "Return type of exported function has or is using name '{0}' from external module {1} but cannot be named." },
234246
Circular_definition_of_import_alias_0: { code: 3000, category: DiagnosticCategory.Error, key: "Circular definition of import alias '{0}'." },
235247
Cannot_find_name_0: { code: 3001, category: DiagnosticCategory.Error, key: "Cannot find name '{0}'." },
236248
Module_0_has_no_exported_member_1: { code: 3002, category: DiagnosticCategory.Error, key: "Module '{0}' has no exported member '{1}'." },

src/compiler/diagnosticMessages.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,54 @@
916916
"category": "Error",
917917
"code": 2248
918918
},
919+
"Public static property '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named.": {
920+
"category": "Error",
921+
"code": 2249
922+
},
923+
"Public property '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named.": {
924+
"category": "Error",
925+
"code": 2250
926+
},
927+
"Exported variable '{0}' has or is using name '{1}' from external module {2} but cannot be named.": {
928+
"category": "Error",
929+
"code": 2251
930+
},
931+
"Parameter '{0}' of constructor from exported class has or is using name '{1}' from external module {2} but cannot be named.": {
932+
"category": "Error",
933+
"code": 2252
934+
},
935+
"Parameter '{0}' of public static method from exported class has or is using name '{1}' from external module {2} but cannot be named.": {
936+
"category": "Error",
937+
"code": 2253
938+
},
939+
"Parameter '{0}' of public method from exported class has or is using name '{1}' from external module {2} but cannot be named.": {
940+
"category": "Error",
941+
"code": 2254
942+
},
943+
"Parameter '{0}' of exported function has or is using name '{1}' from external module {2} but cannot be named.": {
944+
"category": "Error",
945+
"code": 2255
946+
},
947+
"Return type of public static property getter from exported class has or is using name '{0}' from external module {1} but cannot be named.": {
948+
"category": "Error",
949+
"code": 2256
950+
},
951+
"Return type of public property getter from exported class has or is using name '{0}' from external module {1} but cannot be named.": {
952+
"category": "Error",
953+
"code": 2257
954+
},
955+
"Return type of public static method from exported class has or is using name '{0}' from external module {1} but cannot be named.": {
956+
"category": "Error",
957+
"code": 2258
958+
},
959+
"Return type of public method from exported class has or is using name '{0}' from external module {1} but cannot be named.": {
960+
"category": "Error",
961+
"code": 2259
962+
},
963+
"Return type of exported function has or is using name '{0}' from external module {1} but cannot be named.": {
964+
"category": "Error",
965+
"code": 2260
966+
},
919967
"Circular definition of import alias '{0}'.": {
920968
"category": "Error",
921969
"code": 3000

0 commit comments

Comments
 (0)