Skip to content

Commit 815dc90

Browse files
authored
Issue an error on cross-file merges we cant emit (#38148)
1 parent d7e437a commit 815dc90

File tree

9 files changed

+149
-2
lines changed

9 files changed

+149
-2
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6029,14 +6029,19 @@ namespace ts {
60296029
serializeAsNamespaceDeclaration(realMembers, localName, modifierFlags, !!(symbol.flags & (SymbolFlags.Function | SymbolFlags.Assignment)));
60306030
}
60316031
if (length(mergedMembers)) {
6032+
const containingFile = getSourceFileOfNode(context.enclosingDeclaration);
60326033
const localName = getInternalSymbolName(symbol, symbolName);
60336034
const nsBody = createModuleBlock([createExportDeclaration(
60346035
/*decorators*/ undefined,
60356036
/*modifiers*/ undefined,
6036-
createNamedExports(map(filter(mergedMembers, n => n.escapedName !== InternalSymbolName.ExportEquals), s => {
6037+
createNamedExports(mapDefined(filter(mergedMembers, n => n.escapedName !== InternalSymbolName.ExportEquals), s => {
60376038
const name = unescapeLeadingUnderscores(s.escapedName);
60386039
const localName = getInternalSymbolName(s, name);
60396040
const aliasDecl = s.declarations && getDeclarationOfAliasSymbol(s);
6041+
if (containingFile && (aliasDecl ? containingFile !== getSourceFileOfNode(aliasDecl) : !some(s.declarations, d => getSourceFileOfNode(d) === containingFile))) {
6042+
context.tracker?.reportNonlocalAugmentation?.(containingFile, symbol, s);
6043+
return undefined;
6044+
}
60406045
const target = aliasDecl && getTargetOfAliasDeclaration(aliasDecl, /*dontRecursivelyResolve*/ true);
60416046
includePrivateSymbol(target || s);
60426047
const targetName = target ? getInternalSymbolName(target, unescapeLeadingUnderscores(target.escapedName)) : localName;

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4388,6 +4388,14 @@
43884388
"category": "Error",
43894389
"code": 6231
43904390
},
4391+
"Declaration augments declaration in another file. This cannot be serialized.": {
4392+
"category": "Error",
4393+
"code": 6232
4394+
},
4395+
"This is the declaration being augmented. Consider moving the augmenting declaration into the same file.": {
4396+
"category": "Error",
4397+
"code": 6233
4398+
},
43914399

43924400
"Projects to reference": {
43934401
"category": "Message",

src/compiler/transformers/declarations.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ namespace ts {
7676
reportLikelyUnsafeImportRequiredError,
7777
moduleResolverHost: host,
7878
trackReferencedAmbientModule,
79-
trackExternalModuleSymbolOfImportTypeNode
79+
trackExternalModuleSymbolOfImportTypeNode,
80+
reportNonlocalAugmentation
8081
};
8182
let errorNameNode: DeclarationName | undefined;
8283

@@ -190,6 +191,17 @@ namespace ts {
190191
}
191192
}
192193

194+
function reportNonlocalAugmentation(containingFile: SourceFile, parentSymbol: Symbol, symbol: Symbol) {
195+
const primaryDeclaration = find(parentSymbol.declarations, d => getSourceFileOfNode(d) === containingFile)!;
196+
const augmentingDeclarations = filter(symbol.declarations, d => getSourceFileOfNode(d) !== containingFile);
197+
for (const augmentations of augmentingDeclarations) {
198+
context.addDiagnostic(addRelatedInfo(
199+
createDiagnosticForNode(augmentations, Diagnostics.Declaration_augments_declaration_in_another_file_This_cannot_be_serialized),
200+
createDiagnosticForNode(primaryDeclaration, Diagnostics.This_is_the_declaration_being_augmented_Consider_moving_the_augmenting_declaration_into_the_same_file)
201+
));
202+
}
203+
}
204+
193205
function transformDeclarationsForJS(sourceFile: SourceFile, bundled?: boolean) {
194206
const oldDiag = getSymbolAccessibilityDiagnostic;
195207
getSymbolAccessibilityDiagnostic = (s) => ({

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6468,6 +6468,7 @@ namespace ts {
64686468
moduleResolverHost?: ModuleSpecifierResolutionHost & { getCommonSourceDirectory(): string };
64696469
trackReferencedAmbientModule?(decl: ModuleDeclaration, symbol: Symbol): void;
64706470
trackExternalModuleSymbolOfImportTypeNode?(symbol: Symbol): void;
6471+
reportNonlocalAugmentation?(containingFile: SourceFile, parentSymbol: Symbol, augmentingSymbol: Symbol): void;
64716472
}
64726473

64736474
export interface TextSpan {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
tests/cases/conformance/jsdoc/declarations/index.js(4,1): error TS6232: Declaration augments declaration in another file. This cannot be serialized.
2+
3+
4+
==== tests/cases/conformance/jsdoc/declarations/index.js (1 errors) ====
5+
const m = require("./exporter");
6+
7+
module.exports = m.default;
8+
module.exports.memberName = "thing";
9+
~~~~~~~~~~~~~~~~~~~~~~~~~
10+
!!! error TS6232: Declaration augments declaration in another file. This cannot be serialized.
11+
!!! related TS6233 /.src/tests/cases/conformance/jsdoc/declarations/exporter.js:1:10: This is the declaration being augmented. Consider moving the augmenting declaration into the same file.
12+
13+
==== tests/cases/conformance/jsdoc/declarations/exporter.js (0 errors) ====
14+
function validate() {}
15+
16+
export default validate;
17+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//// [tests/cases/conformance/jsdoc/declarations/jsDeclarationsCrossfileMerge.ts] ////
2+
3+
//// [index.js]
4+
const m = require("./exporter");
5+
6+
module.exports = m.default;
7+
module.exports.memberName = "thing";
8+
9+
//// [exporter.js]
10+
function validate() {}
11+
12+
export default validate;
13+
14+
15+
//// [exporter.js]
16+
"use strict";
17+
Object.defineProperty(exports, "__esModule", { value: true });
18+
function validate() { }
19+
exports.default = validate;
20+
//// [index.js]
21+
var m = require("./exporter");
22+
module.exports = m.default;
23+
module.exports.memberName = "thing";
24+
25+
26+
//// [index.d.ts]
27+
declare const _exports: typeof import("./exporter").default;
28+
export = _exports;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=== tests/cases/conformance/jsdoc/declarations/index.js ===
2+
const m = require("./exporter");
3+
>m : Symbol(m, Decl(index.js, 0, 5))
4+
>require : Symbol(require)
5+
>"./exporter" : Symbol("tests/cases/conformance/jsdoc/declarations/exporter", Decl(exporter.js, 0, 0))
6+
7+
module.exports = m.default;
8+
>module.exports : Symbol("tests/cases/conformance/jsdoc/declarations/index", Decl(index.js, 0, 0))
9+
>module : Symbol(export=, Decl(index.js, 0, 32))
10+
>exports : Symbol(export=, Decl(index.js, 0, 32))
11+
>m.default : Symbol(default, Decl(exporter.js, 0, 22))
12+
>m : Symbol(m, Decl(index.js, 0, 5))
13+
>default : Symbol(default, Decl(exporter.js, 0, 22))
14+
15+
module.exports.memberName = "thing";
16+
>module.exports.memberName : Symbol(memberName)
17+
>module.exports : Symbol(memberName, Decl(index.js, 2, 27))
18+
>module : Symbol(module, Decl(index.js, 0, 32))
19+
>exports : Symbol("tests/cases/conformance/jsdoc/declarations/index", Decl(index.js, 0, 0))
20+
>memberName : Symbol(memberName, Decl(index.js, 2, 27))
21+
22+
=== tests/cases/conformance/jsdoc/declarations/exporter.js ===
23+
function validate() {}
24+
>validate : Symbol(validate, Decl(exporter.js, 0, 0))
25+
26+
export default validate;
27+
>validate : Symbol(validate, Decl(exporter.js, 0, 0))
28+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
=== tests/cases/conformance/jsdoc/declarations/index.js ===
2+
const m = require("./exporter");
3+
>m : typeof import("tests/cases/conformance/jsdoc/declarations/exporter")
4+
>require("./exporter") : typeof import("tests/cases/conformance/jsdoc/declarations/exporter")
5+
>require : any
6+
>"./exporter" : "./exporter"
7+
8+
module.exports = m.default;
9+
>module.exports = m.default : typeof import("tests/cases/conformance/jsdoc/declarations/exporter").default
10+
>module.exports : typeof import("tests/cases/conformance/jsdoc/declarations/exporter").default
11+
>module : { "\"tests/cases/conformance/jsdoc/declarations/index\"": typeof import("tests/cases/conformance/jsdoc/declarations/exporter").default; }
12+
>exports : typeof import("tests/cases/conformance/jsdoc/declarations/exporter").default
13+
>m.default : { (): void; memberName: string; }
14+
>m : typeof import("tests/cases/conformance/jsdoc/declarations/exporter")
15+
>default : { (): void; memberName: string; }
16+
17+
module.exports.memberName = "thing";
18+
>module.exports.memberName = "thing" : "thing"
19+
>module.exports.memberName : string
20+
>module.exports : typeof import("tests/cases/conformance/jsdoc/declarations/exporter").default
21+
>module : { "\"tests/cases/conformance/jsdoc/declarations/index\"": typeof import("tests/cases/conformance/jsdoc/declarations/exporter").default; }
22+
>exports : typeof import("tests/cases/conformance/jsdoc/declarations/exporter").default
23+
>memberName : string
24+
>"thing" : "thing"
25+
26+
=== tests/cases/conformance/jsdoc/declarations/exporter.js ===
27+
function validate() {}
28+
>validate : typeof validate
29+
30+
export default validate;
31+
>validate : typeof validate
32+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @target: es5
4+
// @lib: es6
5+
// @outDir: ./out
6+
// @declaration: true
7+
// @filename: index.js
8+
const m = require("./exporter");
9+
10+
module.exports = m.default;
11+
module.exports.memberName = "thing";
12+
13+
// @filename: exporter.js
14+
function validate() {}
15+
16+
export default validate;

0 commit comments

Comments
 (0)