Skip to content

Commit df3567c

Browse files
committed
Merge pull request #197 from Microsoft/locals_exports
Alternative proposal for grouping exported and local declarations
2 parents fc00047 + 42df260 commit df3567c

12 files changed

+197
-56
lines changed

src/compiler/binder.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,30 +136,27 @@ module ts {
136136
// but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way
137137
// when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope.
138138
var exportKind = 0;
139-
var exportExcludes = 0;
140139
if (symbolKind & SymbolFlags.Value) {
141140
exportKind |= SymbolFlags.ExportValue;
142-
exportExcludes |= SymbolFlags.Value;
143141
}
144142
if (symbolKind & SymbolFlags.Type) {
145143
exportKind |= SymbolFlags.ExportType;
146-
exportExcludes |= SymbolFlags.Type;
147144
}
148145
if (symbolKind & SymbolFlags.Namespace) {
149146
exportKind |= SymbolFlags.ExportNamespace;
150-
exportExcludes |= SymbolFlags.Namespace;
151147
}
152148
if (node.flags & NodeFlags.Export || (node.kind !== SyntaxKind.ImportDeclaration && isAmbientContext(container))) {
153149
if (exportKind) {
154-
var local = declareSymbol(container.locals, undefined, node, exportKind, exportExcludes);
150+
var local = declareSymbol(container.locals, undefined, node, exportKind, symbolExcludes);
155151
local.exportSymbol = declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes);
152+
node.localSymbol = local;
156153
}
157154
else {
158155
declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes);
159156
}
160157
}
161158
else {
162-
declareSymbol(container.locals, undefined, node, symbolKind, symbolExcludes | exportKind);
159+
declareSymbol(container.locals, undefined, node, symbolKind, symbolExcludes);
163160
}
164161
}
165162

src/compiler/checker.ts

Lines changed: 109 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4819,24 +4819,21 @@ module ts {
48194819
error(signatureDeclarationNode, Diagnostics.Specialized_overload_signature_is_not_assignable_to_any_non_specialized_signature);
48204820
}
48214821

4822-
function checkFunctionOrConstructorSymbol(symbol: Symbol) {
4823-
function getEffectiveFlagsForFunctionCheck(n: Node) {
4824-
var flags = n.flags;
4825-
// We want to determine if an overload is effectively ambient, which can happen if it
4826-
// is nested in an ambient context. However, do not treat members of interfaces differently
4827-
// based on whether the interface itself is in an ambient context. Interfaces should never
4828-
// be considered ambient for purposes of comparing overload attributes.
4829-
if (n.parent.kind !== SyntaxKind.InterfaceDeclaration && isInAmbientContext(n)) {
4830-
if (!(flags & NodeFlags.Ambient)) {
4831-
// It is nested in an ambient context, which means it is automatically exported
4832-
flags |= NodeFlags.Export;
4833-
}
4834-
flags |= NodeFlags.Ambient;
4822+
function getEffectiveDeclarationFlags(n: Node, flagsToCheck: NodeFlags) {
4823+
var flags = n.flags;
4824+
if (n.parent.kind !== SyntaxKind.InterfaceDeclaration && isInAmbientContext(n)) {
4825+
if (!(flags & NodeFlags.Ambient)) {
4826+
// It is nested in an ambient context, which means it is automatically exported
4827+
flags |= NodeFlags.Export;
48354828
}
4836-
4837-
return flags & flagsToCheck;
4829+
flags |= NodeFlags.Ambient;
48384830
}
48394831

4832+
return flags & flagsToCheck;
4833+
}
4834+
4835+
function checkFunctionOrConstructorSymbol(symbol: Symbol) {
4836+
48404837
function checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionDeclaration, flagsToCheck: NodeFlags, someOverloadFlags: NodeFlags, allOverloadFlags: NodeFlags): void {
48414838
// Error if some overloads have a flag that is not shared by all overloads. To find the
48424839
// deviations, we XOR someOverloadFlags with allOverloadFlags
@@ -4849,10 +4846,10 @@ module ts {
48494846
// the canonical signature only if it is in the same container as the first overload
48504847
var implementationSharesContainerWithFirstOverload = implementation !== undefined && implementation.parent === overloads[0].parent;
48514848
var canonicalFlags = implementationSharesContainerWithFirstOverload
4852-
? getEffectiveFlagsForFunctionCheck(implementation)
4853-
: getEffectiveFlagsForFunctionCheck(overloads[0]);
4849+
? getEffectiveDeclarationFlags(implementation, flagsToCheck)
4850+
: getEffectiveDeclarationFlags(overloads[0], flagsToCheck);
48544851
forEach(overloads, o => {
4855-
var deviation = getEffectiveFlagsForFunctionCheck(o) ^ canonicalFlags;
4852+
var deviation = getEffectiveDeclarationFlags(o, flagsToCheck) ^ canonicalFlags;
48564853
if (deviation & NodeFlags.Export) {
48574854
error(o.name, Diagnostics.Overload_signatures_must_all_be_exported_or_not_exported);
48584855
}
@@ -4880,7 +4877,7 @@ module ts {
48804877
for (var i = 0; i < declarations.length; i++) {
48814878
var node = <FunctionDeclaration>declarations[i];
48824879
if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.Method || node.kind === SyntaxKind.Constructor) {
4883-
var currentNodeFlags = getEffectiveFlagsForFunctionCheck(node);
4880+
var currentNodeFlags = getEffectiveDeclarationFlags(node, flagsToCheck);
48844881
someNodeFlags |= currentNodeFlags;
48854882
allNodeFlags &= currentNodeFlags;
48864883

@@ -4951,14 +4948,97 @@ module ts {
49514948
}
49524949
}
49534950

4951+
function checkExportsOnMergedDeclarations(node: Node) {
4952+
var symbol: Symbol;
4953+
4954+
// Exports should be checked only if enclosing module contains both exported and non exported declarations.
4955+
// In case if all declarations are non-exported check is unnecesary.
4956+
4957+
// if localSymbol is defined on node then node itself is exported - check is required
4958+
var symbol = node.localSymbol;
4959+
if (!symbol) {
4960+
// local symbol is undefined => this declaration is non-exported.
4961+
// however symbol might contain other declarations that are exported
4962+
symbol = getSymbolOfNode(node);
4963+
if (!(symbol.flags & SymbolFlags.Export)) {
4964+
// this is a pure local symbol (all declarations are non-exported) - no need to check anything
4965+
return;
4966+
}
4967+
}
4968+
4969+
// run the check only for the first declaration in the list
4970+
if (getDeclarationOfKind(symbol, node.kind) !== node) {
4971+
return;
4972+
}
4973+
4974+
// we use SymbolFlags.ExportValue, SymbolFlags.ExportType and SymbolFlags.ExportNamespace
4975+
// to denote disjoint declarationSpaces (without making new enum type).
4976+
var exportedDeclarationSpaces: SymbolFlags = 0;
4977+
var nonExportedDeclarationSpaces: SymbolFlags = 0;
4978+
forEach(symbol.declarations, d => {
4979+
var declarationSpaces = getDeclarationSpaces(d);
4980+
if (getEffectiveDeclarationFlags(d, NodeFlags.Export)) {
4981+
exportedDeclarationSpaces |= declarationSpaces;
4982+
}
4983+
else {
4984+
nonExportedDeclarationSpaces |= declarationSpaces;
4985+
}
4986+
});
4987+
4988+
var commonDeclarationSpace = exportedDeclarationSpaces & nonExportedDeclarationSpaces
4989+
4990+
if (commonDeclarationSpace) {
4991+
// declaration spaces for exported and non-exported declarations intersect
4992+
forEach(symbol.declarations, d => {
4993+
if (getDeclarationSpaces(d) & commonDeclarationSpace) {
4994+
error(d.name, Diagnostics.Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local, identifierToString(d.name));
4995+
}
4996+
});
4997+
}
4998+
4999+
function getDeclarationSpaces(d: Declaration): SymbolFlags {
5000+
switch (d.kind) {
5001+
case SyntaxKind.InterfaceDeclaration:
5002+
return SymbolFlags.ExportType;
5003+
case SyntaxKind.ModuleDeclaration:
5004+
return (<ModuleDeclaration>d).name.kind === SyntaxKind.StringLiteral || isInstantiated(d)
5005+
? SymbolFlags.ExportNamespace | SymbolFlags.ExportValue
5006+
: SymbolFlags.ExportNamespace;
5007+
case SyntaxKind.ClassDeclaration:
5008+
case SyntaxKind.EnumDeclaration:
5009+
return SymbolFlags.ExportType | SymbolFlags.ExportValue;
5010+
case SyntaxKind.ImportDeclaration:
5011+
var result: SymbolFlags = 0;
5012+
var target = resolveImport(getSymbolOfNode(d));
5013+
forEach(target.declarations, d => { result |= getDeclarationSpaces(d); } )
5014+
return result;
5015+
default:
5016+
return SymbolFlags.ExportValue;
5017+
}
5018+
}
5019+
}
5020+
49545021
function checkFunctionDeclaration(node: FunctionDeclaration) {
49555022
checkSignatureDeclaration(node);
49565023

4957-
var symbol = getSymbolOfNode(node);
4958-
var firstDeclaration = getDeclarationOfKind(symbol, node.kind);
5024+
var symbol = getSymbolOfNode(node)
5025+
// first we want to check the local symbol that contain this declaration
5026+
// - if node.localSymbol !== undefined - this is current declaration is exported and localSymbol points to the local symbol
5027+
// - if node.localSymbol === undefined - this node is non-exported so we can just pick the result of getSymbolOfNode
5028+
var localSymbol = node.localSymbol || symbol;
5029+
5030+
var firstDeclaration = getDeclarationOfKind(localSymbol, node.kind);
49595031
// Only type check the symbol once
49605032
if (node === firstDeclaration) {
4961-
checkFunctionOrConstructorSymbol(symbol);
5033+
checkFunctionOrConstructorSymbol(localSymbol);
5034+
}
5035+
5036+
if (symbol.parent) {
5037+
// run check once for the first declaration
5038+
if (getDeclarationOfKind(symbol, node.kind) === node) {
5039+
// run check on export symbol to check that modifiers agree across all exported declarations
5040+
checkFunctionOrConstructorSymbol(symbol);
5041+
}
49625042
}
49635043

49645044
checkSourceElement(node.body);
@@ -5155,8 +5235,10 @@ module ts {
51555235
function checkVariableDeclaration(node: VariableDeclaration) {
51565236
checkSourceElement(node.type);
51575237

5158-
var symbol = getSymbolOfNode(node);
5238+
checkExportsOnMergedDeclarations(node);
51595239

5240+
var symbol = getSymbolOfNode(node);
5241+
51605242
var typeOfValueDeclaration = getTypeOfVariableOrParameterOrProperty(symbol);
51615243
var type: Type;
51625244
var useTypeFromValueDeclaration = node === symbol.valueDeclaration;
@@ -5441,6 +5523,7 @@ module ts {
54415523
checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0);
54425524
checkTypeParameters(node.typeParameters);
54435525
checkCollisionWithCapturedThisVariable(node, node.name);
5526+
checkExportsOnMergedDeclarations(node);
54445527
var symbol = getSymbolOfNode(node);
54455528
var type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
54465529
var staticType = <ObjectType>getTypeOfSymbol(symbol);
@@ -5595,6 +5678,7 @@ module ts {
55955678
function checkInterfaceDeclaration(node: InterfaceDeclaration) {
55965679
checkTypeNameIsReserved(node.name, Diagnostics.Interface_name_cannot_be_0);
55975680
checkTypeParameters(node.typeParameters);
5681+
checkExportsOnMergedDeclarations(node);
55985682
var symbol = getSymbolOfNode(node);
55995683
var firstInterfaceDecl = <InterfaceDeclaration>getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration);
56005684
if (symbol.declarations.length > 1) {
@@ -5640,6 +5724,7 @@ module ts {
56405724
function checkEnumDeclaration(node: EnumDeclaration) {
56415725
checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0);
56425726
checkCollisionWithCapturedThisVariable(node, node.name);
5727+
checkExportsOnMergedDeclarations(node);
56435728
var enumSymbol = getSymbolOfNode(node);
56445729
var enumType = getDeclaredTypeOfSymbol(enumSymbol);
56455730
var autoValue = 0;
@@ -5711,6 +5796,7 @@ module ts {
57115796

57125797
function checkModuleDeclaration(node: ModuleDeclaration) {
57135798
checkCollisionWithCapturedThisVariable(node, node.name);
5799+
checkExportsOnMergedDeclarations(node);
57145800
var symbol = getSymbolOfNode(node);
57155801
if (symbol.flags & SymbolFlags.ValueModule && symbol.declarations.length > 1 && !isInAmbientContext(node)) {
57165802
var classOrFunc = getFirstNonAmbientClassOrFunctionDeclaration(symbol);

src/compiler/diagnosticInformationMap.generated.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ module ts {
140140
A_signature_with_an_implementation_cannot_use_a_string_literal_type: { code: 2163, category: DiagnosticCategory.Error, key: "A signature with an implementation cannot use a string literal type." },
141141
Interface_0_cannot_simultaneously_extend_types_1_and_2_Colon: { code: 2189, category: DiagnosticCategory.Error, key: "Interface '{0}' cannot simultaneously extend types '{1}' and '{2}':" },
142142
Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it: { code: 2190, category: DiagnosticCategory.Error, key: "Initializer of parameter '{0}' cannot reference identifier '{1}' declared after it." },
143+
Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local: { code: 2192, category: DiagnosticCategory.Error, key: "Individual declarations in merged declaration {0} must be all exported or all local." },
143144
super_cannot_be_referenced_in_constructor_arguments: { code: 2193, category: DiagnosticCategory.Error, key: "'super' cannot be referenced in constructor arguments." },
144145
Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class: { code: 2194, category: DiagnosticCategory.Error, key: "Return type of constructor signature must be assignable to the instance type of the class" },
145146
Ambient_external_module_declaration_cannot_specify_relative_module_name: { code: 2196, category: DiagnosticCategory.Error, key: "Ambient external module declaration cannot specify relative module name." },

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,10 @@
552552
"category": "Error",
553553
"code": 2190
554554
},
555+
"Individual declarations in merged declaration {0} must be all exported or all local.": {
556+
"category": "Error",
557+
"code": 2192
558+
},
555559
"'super' cannot be referenced in constructor arguments.":{
556560
"category": "Error",
557561
"code": 2193

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ module ts {
243243
symbol?: Symbol; // Symbol declared by node (initialized by binding)
244244
locals?: SymbolTable; // Locals associated with node (initialized by binding)
245245
nextContainer?: Node; // Next container in declaration order (initialized by binding)
246+
localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes)
246247
}
247248

248249
export interface NodeArray<T> extends Array<T>, TextRange { }
@@ -699,6 +700,7 @@ module ts {
699700

700701
IsContainer = HasLocals | HasExports | HasMembers,
701702
PropertyOrAccessor = Property | Accessor,
703+
Export = ExportNamespace | ExportType | ExportValue,
702704
}
703705

704706
export interface Symbol {

tests/baselines/reference/anonymousModules.errors.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
==== tests/cases/compiler/anonymousModules.ts (12 errors) ====
1+
==== tests/cases/compiler/anonymousModules.ts (13 errors) ====
22
module {
33
~
44
!!! ';' expected.
@@ -18,13 +18,15 @@
1818
export var bar = 1;
1919
~~~~~~
2020
!!! Statement expected.
21+
~~~
22+
!!! Individual declarations in merged declaration bar must be all exported or all local.
2123
}
2224
~
2325
!!! Declaration or statement expected.
2426

2527
var bar = 2;
2628
~~~
27-
!!! Duplicate identifier 'bar'.
29+
!!! Individual declarations in merged declaration bar must be all exported or all local.
2830

2931
module {
3032
~

0 commit comments

Comments
 (0)