Skip to content

Commit 7a42000

Browse files
authored
Merge pull request #12305 from Microsoft/improveImportHelpersDiagnostics
Improve diagnostic messages for imported helpers
2 parents 030da0b + 5c294bf commit 7a42000

File tree

7 files changed

+133
-108
lines changed

7 files changed

+133
-108
lines changed

src/compiler/binder.ts

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,6 @@ namespace ts {
518518
hasExplicitReturn = false;
519519
bindChildren(node);
520520
// Reset all reachability check related flags on node (for incremental scenarios)
521-
// Reset all emit helper flags on node (for incremental scenarios)
522521
node.flags &= ~NodeFlags.ReachabilityAndEmitFlags;
523522
if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
524523
node.flags |= NodeFlags.HasImplicitReturn;
@@ -1950,9 +1949,6 @@ namespace ts {
19501949
return bindParameter(<ParameterDeclaration>node);
19511950
case SyntaxKind.VariableDeclaration:
19521951
case SyntaxKind.BindingElement:
1953-
if ((node as BindingElement).dotDotDotToken && node.parent.kind === SyntaxKind.ObjectBindingPattern) {
1954-
emitFlags |= NodeFlags.HasRestAttribute;
1955-
}
19561952
return bindVariableDeclarationOrBindingElement(<VariableDeclaration | BindingElement>node);
19571953
case SyntaxKind.PropertyDeclaration:
19581954
case SyntaxKind.PropertySignature:
@@ -1980,7 +1976,6 @@ namespace ts {
19801976
}
19811977
root = root.parent;
19821978
}
1983-
emitFlags |= hasRest ? NodeFlags.HasRestAttribute : NodeFlags.HasSpreadAttribute;
19841979
return;
19851980

19861981
case SyntaxKind.CallSignature:
@@ -2236,15 +2231,6 @@ namespace ts {
22362231
}
22372232

22382233
function bindClassLikeDeclaration(node: ClassLikeDeclaration) {
2239-
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
2240-
if (getClassExtendsHeritageClauseElement(node) !== undefined) {
2241-
emitFlags |= NodeFlags.HasClassExtends;
2242-
}
2243-
if (nodeIsDecorated(node)) {
2244-
emitFlags |= NodeFlags.HasDecorators;
2245-
}
2246-
}
2247-
22482234
if (node.kind === SyntaxKind.ClassDeclaration) {
22492235
bindBlockScopedDeclaration(node, SymbolFlags.Class, SymbolFlags.ClassExcludes);
22502236
}
@@ -2314,12 +2300,6 @@ namespace ts {
23142300
}
23152301

23162302
function bindParameter(node: ParameterDeclaration) {
2317-
if (!isDeclarationFile(file) &&
2318-
!isInAmbientContext(node) &&
2319-
nodeIsDecorated(node)) {
2320-
emitFlags |= (NodeFlags.HasDecorators | NodeFlags.HasParamDecorators);
2321-
}
2322-
23232303
if (inStrictMode) {
23242304
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
23252305
// strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
@@ -2377,9 +2357,6 @@ namespace ts {
23772357
if (isAsyncFunctionLike(node)) {
23782358
emitFlags |= NodeFlags.HasAsyncFunctions;
23792359
}
2380-
if (nodeIsDecorated(node)) {
2381-
emitFlags |= NodeFlags.HasDecorators;
2382-
}
23832360
}
23842361

23852362
if (currentFlow && isObjectLiteralOrClassExpressionMethod(node)) {

src/compiler/checker.ts

Lines changed: 69 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ namespace ts {
3838
// is because diagnostics can be quite expensive, and we want to allow hosts to bail out if
3939
// they no longer need the information (for example, if the user started editing again).
4040
let cancellationToken: CancellationToken;
41+
let requestedExternalEmitHelpers: ExternalEmitHelpers;
42+
let externalHelpersModule: Symbol;
4143

4244
const Symbol = objectAllocator.getSymbolConstructor();
4345
const Type = objectAllocator.getTypeConstructor();
@@ -11461,6 +11463,9 @@ namespace ts {
1146111463
member = prop;
1146211464
}
1146311465
else if (memberDecl.kind === SyntaxKind.SpreadAssignment) {
11466+
if (languageVersion < ScriptTarget.ESNext) {
11467+
checkExternalEmitHelpers(memberDecl, ExternalEmitHelpers.Assign);
11468+
}
1146411469
if (propertiesArray.length > 0) {
1146511470
spread = getSpreadType(spread, createObjectLiteralType(), /*isFromObjectLiteral*/ true);
1146611471
propertiesArray = [];
@@ -11654,6 +11659,9 @@ namespace ts {
1165411659
}
1165511660

1165611661
function checkJsxSpreadAttribute(node: JsxSpreadAttribute, elementAttributesType: Type, nameTable: Map<boolean>) {
11662+
if (compilerOptions.jsx === JsxEmit.React) {
11663+
checkExternalEmitHelpers(node, ExternalEmitHelpers.Assign);
11664+
}
1165711665
const type = checkExpression(node.expression);
1165811666
const props = getPropertiesOfType(type);
1165911667
for (const prop of props) {
@@ -14419,6 +14427,9 @@ namespace ts {
1441914427
}
1442014428
}
1442114429
else if (property.kind === SyntaxKind.SpreadAssignment) {
14430+
if (languageVersion < ScriptTarget.ESNext) {
14431+
checkExternalEmitHelpers(property, ExternalEmitHelpers.Rest);
14432+
}
1442214433
const nonRestNames: PropertyName[] = [];
1442314434
if (allProperties) {
1442414435
for (let i = 0; i < allProperties.length - 1; i++) {
@@ -15331,6 +15342,13 @@ namespace ts {
1533115342
checkGrammarFunctionLikeDeclaration(<FunctionLikeDeclaration>node);
1533215343
}
1533315344

15345+
if (isAsyncFunctionLike(node) && languageVersion < ScriptTarget.ES2017) {
15346+
checkExternalEmitHelpers(node, ExternalEmitHelpers.Awaiter);
15347+
if (languageVersion < ScriptTarget.ES2015) {
15348+
checkExternalEmitHelpers(node, ExternalEmitHelpers.Generator);
15349+
}
15350+
}
15351+
1533415352
checkTypeParameters(node.typeParameters);
1533515353

1533615354
forEach(node.parameters, checkParameter);
@@ -16466,7 +16484,15 @@ namespace ts {
1646616484
error(node, Diagnostics.Experimental_support_for_decorators_is_a_feature_that_is_subject_to_change_in_a_future_release_Set_the_experimentalDecorators_option_to_remove_this_warning);
1646716485
}
1646816486

16487+
const firstDecorator = node.decorators[0];
16488+
checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Decorate);
16489+
if (node.kind === SyntaxKind.Parameter) {
16490+
checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Param);
16491+
}
16492+
1646916493
if (compilerOptions.emitDecoratorMetadata) {
16494+
checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Metadata);
16495+
1647016496
// we only need to perform these checks if we are emitting serialized type metadata for the target of a decorator.
1647116497
switch (node.kind) {
1647216498
case SyntaxKind.ClassDeclaration:
@@ -16853,7 +16879,7 @@ namespace ts {
1685316879
}
1685416880

1685516881
function checkCollisionWithGlobalPromiseInGeneratedCode(node: Node, name: Identifier): void {
16856-
if (!needCollisionCheckForIdentifier(node, name, "Promise")) {
16882+
if (languageVersion >= ScriptTarget.ES2017 || !needCollisionCheckForIdentifier(node, name, "Promise")) {
1685716883
return;
1685816884
}
1685916885

@@ -17030,6 +17056,9 @@ namespace ts {
1703017056
}
1703117057

1703217058
if (node.kind === SyntaxKind.BindingElement) {
17059+
if (node.parent.kind === SyntaxKind.ObjectBindingPattern && languageVersion < ScriptTarget.ESNext) {
17060+
checkExternalEmitHelpers(node, ExternalEmitHelpers.Rest);
17061+
}
1703317062
// check computed properties inside property names of binding elements
1703417063
if (node.propertyName && node.propertyName.kind === SyntaxKind.ComputedPropertyName) {
1703517064
checkComputedPropertyName(<ComputedPropertyName>node.propertyName);
@@ -17947,6 +17976,10 @@ namespace ts {
1794717976

1794817977
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
1794917978
if (baseTypeNode) {
17979+
if (languageVersion < ScriptTarget.ES2015) {
17980+
checkExternalEmitHelpers(baseTypeNode.parent, ExternalEmitHelpers.Extends);
17981+
}
17982+
1795017983
const baseTypes = getBaseTypes(type);
1795117984
if (baseTypes.length && produceDiagnostics) {
1795217985
const baseType = baseTypes[0];
@@ -20387,8 +20420,6 @@ namespace ts {
2038720420

2038820421
// Initialize global symbol table
2038920422
let augmentations: LiteralExpression[][];
20390-
let requestedExternalEmitHelpers: NodeFlags = 0;
20391-
let firstFileRequestingExternalHelpers: SourceFile;
2039220423
for (const file of host.getSourceFiles()) {
2039320424
if (!isExternalOrCommonJsModule(file)) {
2039420425
mergeSymbolTable(globals, file.locals);
@@ -20408,15 +20439,6 @@ namespace ts {
2040820439
}
2040920440
}
2041020441
}
20411-
if ((compilerOptions.isolatedModules || isExternalModule(file)) && !file.isDeclarationFile) {
20412-
const fileRequestedExternalEmitHelpers = file.flags & NodeFlags.EmitHelperFlags;
20413-
if (fileRequestedExternalEmitHelpers) {
20414-
requestedExternalEmitHelpers |= fileRequestedExternalEmitHelpers;
20415-
if (firstFileRequestingExternalHelpers === undefined) {
20416-
firstFileRequestingExternalHelpers = file;
20417-
}
20418-
}
20419-
}
2042020442
}
2042120443

2042220444
if (augmentations) {
@@ -20482,57 +20504,51 @@ namespace ts {
2048220504
const symbol = getGlobalSymbol("ReadonlyArray", SymbolFlags.Type, /*diagnostic*/ undefined);
2048320505
globalReadonlyArrayType = symbol && <GenericType>getTypeOfGlobalSymbol(symbol, /*arity*/ 1);
2048420506
anyReadonlyArrayType = globalReadonlyArrayType ? createTypeFromGenericGlobalType(globalReadonlyArrayType, [anyType]) : anyArrayType;
20507+
}
2048520508

20486-
// If we have specified that we are importing helpers, we should report global
20487-
// errors if we cannot resolve the helpers external module, or if it does not have
20488-
// the necessary helpers exported.
20489-
if (compilerOptions.importHelpers && firstFileRequestingExternalHelpers) {
20490-
// Find the first reference to the helpers module.
20491-
const helpersModule = resolveExternalModule(
20492-
firstFileRequestingExternalHelpers,
20493-
externalHelpersModuleNameText,
20494-
Diagnostics.Cannot_find_module_0,
20495-
/*errorNode*/ undefined);
20496-
20497-
// If we found the module, report errors if it does not have the necessary exports.
20498-
if (helpersModule) {
20499-
const exports = helpersModule.exports;
20500-
if (requestedExternalEmitHelpers & NodeFlags.HasClassExtends && languageVersion < ScriptTarget.ES2015) {
20501-
verifyHelperSymbol(exports, "__extends", SymbolFlags.Value);
20502-
}
20503-
if (requestedExternalEmitHelpers & NodeFlags.HasSpreadAttribute &&
20504-
(languageVersion < ScriptTarget.ESNext || compilerOptions.jsx === JsxEmit.React)) {
20505-
verifyHelperSymbol(exports, "__assign", SymbolFlags.Value);
20506-
}
20507-
if (languageVersion < ScriptTarget.ESNext && requestedExternalEmitHelpers & NodeFlags.HasRestAttribute) {
20508-
verifyHelperSymbol(exports, "__rest", SymbolFlags.Value);
20509-
}
20510-
if (requestedExternalEmitHelpers & NodeFlags.HasDecorators) {
20511-
verifyHelperSymbol(exports, "__decorate", SymbolFlags.Value);
20512-
if (compilerOptions.emitDecoratorMetadata) {
20513-
verifyHelperSymbol(exports, "__metadata", SymbolFlags.Value);
20514-
}
20515-
}
20516-
if (requestedExternalEmitHelpers & NodeFlags.HasParamDecorators) {
20517-
verifyHelperSymbol(exports, "__param", SymbolFlags.Value);
20518-
}
20519-
if (requestedExternalEmitHelpers & NodeFlags.HasAsyncFunctions) {
20520-
verifyHelperSymbol(exports, "__awaiter", SymbolFlags.Value);
20521-
if (languageVersion < ScriptTarget.ES2015) {
20522-
verifyHelperSymbol(exports, "__generator", SymbolFlags.Value);
20509+
function checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) {
20510+
if ((requestedExternalEmitHelpers & helpers) !== helpers && compilerOptions.importHelpers) {
20511+
const sourceFile = getSourceFileOfNode(location);
20512+
if (isEffectiveExternalModule(sourceFile, compilerOptions)) {
20513+
const helpersModule = resolveHelpersModule(sourceFile, location);
20514+
if (helpersModule !== unknownSymbol) {
20515+
const uncheckedHelpers = helpers & ~requestedExternalEmitHelpers;
20516+
for (let helper = ExternalEmitHelpers.FirstEmitHelper; helper <= ExternalEmitHelpers.LastEmitHelper; helper <<= 1) {
20517+
if (uncheckedHelpers & helper) {
20518+
const name = getHelperName(helper);
20519+
const symbol = getSymbol(helpersModule.exports, escapeIdentifier(name), SymbolFlags.Value);
20520+
if (!symbol) {
20521+
error(location, Diagnostics.This_syntax_requires_an_imported_helper_named_1_but_module_0_has_no_exported_member_1, externalHelpersModuleNameText, name);
20522+
}
20523+
}
2052320524
}
2052420525
}
20526+
requestedExternalEmitHelpers |= helpers;
2052520527
}
2052620528
}
2052720529
}
2052820530

20529-
function verifyHelperSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags) {
20530-
const symbol = getSymbol(symbols, escapeIdentifier(name), meaning);
20531-
if (!symbol) {
20532-
error(/*location*/ undefined, Diagnostics.Module_0_has_no_exported_member_1, externalHelpersModuleNameText, name);
20531+
function getHelperName(helper: ExternalEmitHelpers) {
20532+
switch (helper) {
20533+
case ExternalEmitHelpers.Extends: return "__extends";
20534+
case ExternalEmitHelpers.Assign: return "__assign";
20535+
case ExternalEmitHelpers.Rest: return "__rest";
20536+
case ExternalEmitHelpers.Decorate: return "__decorate";
20537+
case ExternalEmitHelpers.Metadata: return "__metadata";
20538+
case ExternalEmitHelpers.Param: return "__param";
20539+
case ExternalEmitHelpers.Awaiter: return "__awaiter";
20540+
case ExternalEmitHelpers.Generator: return "__generator";
2053320541
}
2053420542
}
2053520543

20544+
function resolveHelpersModule(node: SourceFile, errorNode: Node) {
20545+
if (!externalHelpersModule) {
20546+
externalHelpersModule = resolveExternalModule(node, externalHelpersModuleNameText, Diagnostics.This_syntax_requires_an_imported_helper_but_module_0_cannot_be_found, errorNode) || unknownSymbol;
20547+
}
20548+
return externalHelpersModule;
20549+
}
20550+
20551+
2053620552
function createInstantiatedPromiseLikeType(): ObjectType {
2053720553
const promiseLikeType = getGlobalPromiseLikeType();
2053820554
if (promiseLikeType !== emptyGenericType) {

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,10 @@
10271027
"category": "Error",
10281028
"code": 2342
10291029
},
1030+
"This syntax requires an imported helper named '{1}', but module '{0}' has no exported member '{1}'.": {
1031+
"category": "Error",
1032+
"code": 2343
1033+
},
10301034
"Type '{0}' does not satisfy the constraint '{1}'.": {
10311035
"category": "Error",
10321036
"code": 2344
@@ -1067,6 +1071,10 @@
10671071
"category": "Error",
10681072
"code": 2353
10691073
},
1074+
"This syntax requires an imported helper but module '{0}' cannot be found.": {
1075+
"category": "Error",
1076+
"code": 2354
1077+
},
10701078
"A function whose declared type is neither 'void' nor 'any' must return a value.": {
10711079
"category": "Error",
10721080
"code": 2355

0 commit comments

Comments
 (0)