Skip to content

Commit d775f0f

Browse files
committed
Improve diagnostic messages for imported helpers
1 parent 3a95f92 commit d775f0f

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();
@@ -11272,6 +11274,9 @@ namespace ts {
1127211274
member = prop;
1127311275
}
1127411276
else if (memberDecl.kind === SyntaxKind.SpreadAssignment) {
11277+
if (languageVersion < ScriptTarget.ESNext) {
11278+
checkExternalEmitHelpers(memberDecl, ExternalEmitHelpers.Assign);
11279+
}
1127511280
if (propertiesArray.length > 0) {
1127611281
spread = getSpreadType(spread, createObjectLiteralType(), /*isFromObjectLiteral*/ true);
1127711282
propertiesArray = [];
@@ -11459,6 +11464,9 @@ namespace ts {
1145911464
}
1146011465

1146111466
function checkJsxSpreadAttribute(node: JsxSpreadAttribute, elementAttributesType: Type, nameTable: Map<boolean>) {
11467+
if (compilerOptions.jsx === JsxEmit.React) {
11468+
checkExternalEmitHelpers(node, ExternalEmitHelpers.Assign);
11469+
}
1146211470
const type = checkExpression(node.expression);
1146311471
const props = getPropertiesOfType(type);
1146411472
for (const prop of props) {
@@ -14223,6 +14231,9 @@ namespace ts {
1422314231
}
1422414232
}
1422514233
else if (property.kind === SyntaxKind.SpreadAssignment) {
14234+
if (languageVersion < ScriptTarget.ESNext) {
14235+
checkExternalEmitHelpers(property, ExternalEmitHelpers.Rest);
14236+
}
1422614237
checkReferenceExpression(property.expression, Diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access);
1422714238
}
1422814239
else {
@@ -15107,6 +15118,13 @@ namespace ts {
1510715118
checkGrammarFunctionLikeDeclaration(<FunctionLikeDeclaration>node);
1510815119
}
1510915120

15121+
if (isAsyncFunctionLike(node) && languageVersion < ScriptTarget.ES2017) {
15122+
checkExternalEmitHelpers(node, ExternalEmitHelpers.Awaiter);
15123+
if (languageVersion < ScriptTarget.ES2015) {
15124+
checkExternalEmitHelpers(node, ExternalEmitHelpers.Generator);
15125+
}
15126+
}
15127+
1511015128
checkTypeParameters(node.typeParameters);
1511115129

1511215130
forEach(node.parameters, checkParameter);
@@ -16242,7 +16260,15 @@ namespace ts {
1624216260
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);
1624316261
}
1624416262

16263+
const firstDecorator = node.decorators[0];
16264+
checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Decorate);
16265+
if (node.kind === SyntaxKind.Parameter) {
16266+
checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Param);
16267+
}
16268+
1624516269
if (compilerOptions.emitDecoratorMetadata) {
16270+
checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Metadata);
16271+
1624616272
// we only need to perform these checks if we are emitting serialized type metadata for the target of a decorator.
1624716273
switch (node.kind) {
1624816274
case SyntaxKind.ClassDeclaration:
@@ -16629,7 +16655,7 @@ namespace ts {
1662916655
}
1663016656

1663116657
function checkCollisionWithGlobalPromiseInGeneratedCode(node: Node, name: Identifier): void {
16632-
if (!needCollisionCheckForIdentifier(node, name, "Promise")) {
16658+
if (languageVersion >= ScriptTarget.ES2017 || !needCollisionCheckForIdentifier(node, name, "Promise")) {
1663316659
return;
1663416660
}
1663516661

@@ -16806,6 +16832,9 @@ namespace ts {
1680616832
}
1680716833

1680816834
if (node.kind === SyntaxKind.BindingElement) {
16835+
if (node.parent.kind === SyntaxKind.ObjectBindingPattern && languageVersion < ScriptTarget.ESNext) {
16836+
checkExternalEmitHelpers(node, ExternalEmitHelpers.Rest);
16837+
}
1680916838
// check computed properties inside property names of binding elements
1681016839
if (node.propertyName && node.propertyName.kind === SyntaxKind.ComputedPropertyName) {
1681116840
checkComputedPropertyName(<ComputedPropertyName>node.propertyName);
@@ -17724,6 +17753,10 @@ namespace ts {
1772417753

1772517754
const baseTypeNode = getClassExtendsHeritageClauseElement(node);
1772617755
if (baseTypeNode) {
17756+
if (languageVersion < ScriptTarget.ES2015) {
17757+
checkExternalEmitHelpers(baseTypeNode.parent, ExternalEmitHelpers.Extends);
17758+
}
17759+
1772717760
const baseTypes = getBaseTypes(type);
1772817761
if (baseTypes.length && produceDiagnostics) {
1772917762
const baseType = baseTypes[0];
@@ -20164,8 +20197,6 @@ namespace ts {
2016420197

2016520198
// Initialize global symbol table
2016620199
let augmentations: LiteralExpression[][];
20167-
let requestedExternalEmitHelpers: NodeFlags = 0;
20168-
let firstFileRequestingExternalHelpers: SourceFile;
2016920200
for (const file of host.getSourceFiles()) {
2017020201
if (!isExternalOrCommonJsModule(file)) {
2017120202
mergeSymbolTable(globals, file.locals);
@@ -20185,15 +20216,6 @@ namespace ts {
2018520216
}
2018620217
}
2018720218
}
20188-
if ((compilerOptions.isolatedModules || isExternalModule(file)) && !file.isDeclarationFile) {
20189-
const fileRequestedExternalEmitHelpers = file.flags & NodeFlags.EmitHelperFlags;
20190-
if (fileRequestedExternalEmitHelpers) {
20191-
requestedExternalEmitHelpers |= fileRequestedExternalEmitHelpers;
20192-
if (firstFileRequestingExternalHelpers === undefined) {
20193-
firstFileRequestingExternalHelpers = file;
20194-
}
20195-
}
20196-
}
2019720219
}
2019820220

2019920221
if (augmentations) {
@@ -20259,57 +20281,51 @@ namespace ts {
2025920281
const symbol = getGlobalSymbol("ReadonlyArray", SymbolFlags.Type, /*diagnostic*/ undefined);
2026020282
globalReadonlyArrayType = symbol && <GenericType>getTypeOfGlobalSymbol(symbol, /*arity*/ 1);
2026120283
anyReadonlyArrayType = globalReadonlyArrayType ? createTypeFromGenericGlobalType(globalReadonlyArrayType, [anyType]) : anyArrayType;
20284+
}
2026220285

20263-
// If we have specified that we are importing helpers, we should report global
20264-
// errors if we cannot resolve the helpers external module, or if it does not have
20265-
// the necessary helpers exported.
20266-
if (compilerOptions.importHelpers && firstFileRequestingExternalHelpers) {
20267-
// Find the first reference to the helpers module.
20268-
const helpersModule = resolveExternalModule(
20269-
firstFileRequestingExternalHelpers,
20270-
externalHelpersModuleNameText,
20271-
Diagnostics.Cannot_find_module_0,
20272-
/*errorNode*/ undefined);
20273-
20274-
// If we found the module, report errors if it does not have the necessary exports.
20275-
if (helpersModule) {
20276-
const exports = helpersModule.exports;
20277-
if (requestedExternalEmitHelpers & NodeFlags.HasClassExtends && languageVersion < ScriptTarget.ES2015) {
20278-
verifyHelperSymbol(exports, "__extends", SymbolFlags.Value);
20279-
}
20280-
if (requestedExternalEmitHelpers & NodeFlags.HasSpreadAttribute &&
20281-
(languageVersion < ScriptTarget.ESNext || compilerOptions.jsx === JsxEmit.React)) {
20282-
verifyHelperSymbol(exports, "__assign", SymbolFlags.Value);
20283-
}
20284-
if (languageVersion < ScriptTarget.ESNext && requestedExternalEmitHelpers & NodeFlags.HasRestAttribute) {
20285-
verifyHelperSymbol(exports, "__rest", SymbolFlags.Value);
20286-
}
20287-
if (requestedExternalEmitHelpers & NodeFlags.HasDecorators) {
20288-
verifyHelperSymbol(exports, "__decorate", SymbolFlags.Value);
20289-
if (compilerOptions.emitDecoratorMetadata) {
20290-
verifyHelperSymbol(exports, "__metadata", SymbolFlags.Value);
20291-
}
20292-
}
20293-
if (requestedExternalEmitHelpers & NodeFlags.HasParamDecorators) {
20294-
verifyHelperSymbol(exports, "__param", SymbolFlags.Value);
20295-
}
20296-
if (requestedExternalEmitHelpers & NodeFlags.HasAsyncFunctions) {
20297-
verifyHelperSymbol(exports, "__awaiter", SymbolFlags.Value);
20298-
if (languageVersion < ScriptTarget.ES2015) {
20299-
verifyHelperSymbol(exports, "__generator", SymbolFlags.Value);
20286+
function checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) {
20287+
if ((requestedExternalEmitHelpers & helpers) !== helpers && compilerOptions.importHelpers) {
20288+
const sourceFile = getSourceFileOfNode(location);
20289+
if (isEffectiveExternalModule(sourceFile, compilerOptions)) {
20290+
const helpersModule = resolveHelpersModule(sourceFile, location);
20291+
if (helpersModule !== unknownSymbol) {
20292+
const uncheckedHelpers = helpers & ~requestedExternalEmitHelpers;
20293+
for (let helper = ExternalEmitHelpers.FirstEmitHelper; helper <= ExternalEmitHelpers.LastEmitHelper; helper <<= 1) {
20294+
if (uncheckedHelpers & helper) {
20295+
const name = getHelperName(helper);
20296+
const symbol = getSymbol(helpersModule.exports, escapeIdentifier(name), SymbolFlags.Value);
20297+
if (!symbol) {
20298+
error(location, Diagnostics.This_syntax_requires_an_imported_helper_named_1_but_module_0_has_no_exported_member_1, externalHelpersModuleNameText, name);
20299+
}
20300+
}
2030020301
}
2030120302
}
20303+
requestedExternalEmitHelpers |= helpers;
2030220304
}
2030320305
}
2030420306
}
2030520307

20306-
function verifyHelperSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags) {
20307-
const symbol = getSymbol(symbols, escapeIdentifier(name), meaning);
20308-
if (!symbol) {
20309-
error(/*location*/ undefined, Diagnostics.Module_0_has_no_exported_member_1, externalHelpersModuleNameText, name);
20308+
function getHelperName(helper: ExternalEmitHelpers) {
20309+
switch (helper) {
20310+
case ExternalEmitHelpers.Extends: return "__extends";
20311+
case ExternalEmitHelpers.Assign: return "__assign";
20312+
case ExternalEmitHelpers.Rest: return "__rest";
20313+
case ExternalEmitHelpers.Decorate: return "__decorate";
20314+
case ExternalEmitHelpers.Metadata: return "__metadata";
20315+
case ExternalEmitHelpers.Param: return "__param";
20316+
case ExternalEmitHelpers.Awaiter: return "__awaiter";
20317+
case ExternalEmitHelpers.Generator: return "__generator";
2031020318
}
2031120319
}
2031220320

20321+
function resolveHelpersModule(node: SourceFile, errorNode: Node) {
20322+
if (!externalHelpersModule) {
20323+
externalHelpersModule = resolveExternalModule(node, externalHelpersModuleNameText, Diagnostics.This_syntax_requires_an_imported_helper_but_module_0_cannot_be_found, errorNode) || unknownSymbol;
20324+
}
20325+
return externalHelpersModule;
20326+
}
20327+
20328+
2031320329
function createInstantiatedPromiseLikeType(): ObjectType {
2031420330
const promiseLikeType = getGlobalPromiseLikeType();
2031520331
if (promiseLikeType !== emptyGenericType) {

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,10 @@
10231023
"category": "Error",
10241024
"code": 2342
10251025
},
1026+
"This syntax requires an imported helper named '{1}', but module '{0}' has no exported member '{1}'.": {
1027+
"category": "Error",
1028+
"code": 2343
1029+
},
10261030
"Type '{0}' does not satisfy the constraint '{1}'.": {
10271031
"category": "Error",
10281032
"code": 2344
@@ -1063,6 +1067,10 @@
10631067
"category": "Error",
10641068
"code": 2353
10651069
},
1070+
"This syntax requires an imported helper but module '{0}' cannot be found.": {
1071+
"category": "Error",
1072+
"code": 2354
1073+
},
10661074
"A function whose declared type is neither 'void' nor 'any' must return a value.": {
10671075
"category": "Error",
10681076
"code": 2355

0 commit comments

Comments
 (0)