Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0a97f5f

Browse filesBrowse files
Merge pull request #960 from Microsoft/templates
Support for ES6 Templates
2 parents 33cee0c + 3e8978f commit 0a97f5f
Copy full SHA for 0a97f5f

File tree

Expand file treeCollapse file tree

304 files changed

+3226
-206
lines changed
Filter options

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner
Expand file treeCollapse file tree

304 files changed

+3226
-206
lines changed

‎src/compiler/checker.ts

Copy file name to clipboardExpand all lines: src/compiler/checker.ts
+37-9Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ module ts {
427427
if (result.flags & SymbolFlags.BlockScopedVariable) {
428428
// Block-scoped variables cannot be used before their definition
429429
var declaration = forEach(result.declarations, d => d.flags & NodeFlags.BlockScoped ? d : undefined);
430-
Debug.assert(declaration, "Block-scoped variable declaration is undefined");
430+
Debug.assert(declaration !== undefined, "Block-scoped variable declaration is undefined");
431431
var declarationSourceFile = getSourceFileOfNode(declaration);
432432
var referenceSourceFile = getSourceFileOfNode(errorLocation);
433433
if (declarationSourceFile === referenceSourceFile) {
@@ -472,7 +472,7 @@ module ts {
472472
function getSymbolOfPartOfRightHandSideOfImport(entityName: EntityName, importDeclaration?: ImportDeclaration): Symbol {
473473
if (!importDeclaration) {
474474
importDeclaration = getAncestor(entityName, SyntaxKind.ImportDeclaration);
475-
Debug.assert(importDeclaration);
475+
Debug.assert(importDeclaration !== undefined);
476476
}
477477
// There are three things we might try to look for. In the following examples,
478478
// the search term is enclosed in |...|:
@@ -3334,7 +3334,6 @@ module ts {
33343334
}
33353335
if (reportErrors) {
33363336
headMessage = headMessage || Diagnostics.Type_0_is_not_assignable_to_type_1;
3337-
Debug.assert(headMessage);
33383337
reportError(headMessage, typeToString(source), typeToString(target));
33393338
}
33403339
return Ternary.False;
@@ -4912,7 +4911,7 @@ module ts {
49124911
}
49134912
return createArrayType(getUnionType(elementTypes));
49144913
}
4915-
4914+
49164915
function isNumericName(name: string) {
49174916
// The intent of numeric names is that
49184917
// - they are names with text in a numeric form, and that
@@ -4937,7 +4936,7 @@ module ts {
49374936
// with the strings '"Infinity"', '"-Infinity"', and '"NaN"' respectively.
49384937
return (+name).toString() === name;
49394938
}
4940-
4939+
49414940
function checkObjectLiteral(node: ObjectLiteral, contextualMapper?: TypeMapper): Type {
49424941
var members = node.symbol.members;
49434942
var properties: SymbolTable = {};
@@ -5660,6 +5659,13 @@ module ts {
56605659
return getReturnTypeOfSignature(signature);
56615660
}
56625661

5662+
function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type {
5663+
// TODO (drosen): Make sure substitutions are assignable to the tag's arguments.
5664+
checkExpression(node.tag);
5665+
checkExpression(node.template);
5666+
return anyType;
5667+
}
5668+
56635669
function checkTypeAssertion(node: TypeAssertion): Type {
56645670
var exprType = checkExpression(node.operand);
56655671
var targetType = getTypeFromTypeNode(node.type);
@@ -6170,6 +6176,19 @@ module ts {
61706176
return getUnionType([type1, type2]);
61716177
}
61726178

6179+
function checkTemplateExpression(node: TemplateExpression): Type {
6180+
// We just want to check each expressions, but we are unconcerned with
6181+
// the type of each expression, as any value may be coerced into a string.
6182+
// It is worth asking whether this is what we really want though.
6183+
// A place where we actually *are* concerned with the expressions' types are
6184+
// in tagged templates.
6185+
forEach((<TemplateExpression>node).templateSpans, templateSpan => {
6186+
checkExpression(templateSpan.expression);
6187+
});
6188+
6189+
return stringType;
6190+
}
6191+
61736192
function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper?: TypeMapper): Type {
61746193
var saveContextualType = node.contextualType;
61756194
node.contextualType = contextualType;
@@ -6223,7 +6242,10 @@ module ts {
62236242
return booleanType;
62246243
case SyntaxKind.NumericLiteral:
62256244
return numberType;
6245+
case SyntaxKind.TemplateExpression:
6246+
return checkTemplateExpression(<TemplateExpression>node);
62266247
case SyntaxKind.StringLiteral:
6248+
case SyntaxKind.NoSubstitutionTemplateLiteral:
62276249
return stringType;
62286250
case SyntaxKind.RegularExpressionLiteral:
62296251
return globalRegExpType;
@@ -6240,6 +6262,8 @@ module ts {
62406262
case SyntaxKind.CallExpression:
62416263
case SyntaxKind.NewExpression:
62426264
return checkCallExpression(<CallExpression>node);
6265+
case SyntaxKind.TaggedTemplateExpression:
6266+
return checkTaggedTemplateExpression(<TaggedTemplateExpression>node);
62436267
case SyntaxKind.TypeAssertion:
62446268
return checkTypeAssertion(<TypeAssertion>node);
62456269
case SyntaxKind.ParenExpression:
@@ -7549,17 +7573,17 @@ module ts {
75497573
errorMessage = Diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor;
75507574
}
75517575
else {
7552-
Debug.assert(derived.flags & SymbolFlags.Property);
7576+
Debug.assert((derived.flags & SymbolFlags.Property) !== 0);
75537577
errorMessage = Diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_property;
75547578
}
75557579
}
75567580
else if (base.flags & SymbolFlags.Property) {
7557-
Debug.assert(derived.flags & SymbolFlags.Method);
7581+
Debug.assert((derived.flags & SymbolFlags.Method) !== 0);
75587582
errorMessage = Diagnostics.Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function;
75597583
}
75607584
else {
7561-
Debug.assert(base.flags & SymbolFlags.Accessor);
7562-
Debug.assert(derived.flags & SymbolFlags.Method);
7585+
Debug.assert((base.flags & SymbolFlags.Accessor) !== 0);
7586+
Debug.assert((derived.flags & SymbolFlags.Method) !== 0);
75637587
errorMessage = Diagnostics.Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_function;
75647588
}
75657589

@@ -8016,6 +8040,7 @@ module ts {
80168040
case SyntaxKind.IndexedAccess:
80178041
case SyntaxKind.CallExpression:
80188042
case SyntaxKind.NewExpression:
8043+
case SyntaxKind.TaggedTemplateExpression:
80198044
case SyntaxKind.TypeAssertion:
80208045
case SyntaxKind.ParenExpression:
80218046
case SyntaxKind.PrefixOperator:
@@ -8293,6 +8318,9 @@ module ts {
82938318
case SyntaxKind.CallExpression:
82948319
case SyntaxKind.NewExpression:
82958320
return (<CallExpression>parent).typeArguments && (<CallExpression>parent).typeArguments.indexOf(node) >= 0;
8321+
case SyntaxKind.TaggedTemplateExpression:
8322+
// TODO (drosen): TaggedTemplateExpressions may eventually support type arguments.
8323+
return false;
82968324
}
82978325
}
82988326

‎src/compiler/core.ts

Copy file name to clipboardExpand all lines: src/compiler/core.ts
+14-7Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ module ts {
1919
[index: string]: T;
2020
}
2121

22+
export enum Comparison {
23+
LessThan = -1,
24+
EqualTo = 0,
25+
GreaterThan = 1
26+
}
27+
2228
export interface StringSet extends Map<any> { }
2329

2430
export function forEach<T, U>(array: T[], callback: (element: T) => U): U {
@@ -93,6 +99,7 @@ module ts {
9399
export function concatenate<T>(array1: T[], array2: T[]): T[] {
94100
if (!array2 || !array2.length) return array1;
95101
if (!array1 || !array1.length) return array2;
102+
96103
return array1.concat(array2);
97104
}
98105

@@ -326,11 +333,11 @@ module ts {
326333
};
327334
}
328335

329-
export function compareValues<T>(a: T, b: T): number {
330-
if (a === b) return 0;
331-
if (a === undefined) return -1;
332-
if (b === undefined) return 1;
333-
return a < b ? -1 : 1;
336+
export function compareValues<T>(a: T, b: T): Comparison {
337+
if (a === b) return Comparison.EqualTo;
338+
if (a === undefined) return Comparison.LessThan;
339+
if (b === undefined) return Comparison.GreaterThan;
340+
return a < b ? Comparison.LessThan : Comparison.GreaterThan;
334341
}
335342

336343
function getDiagnosticFilename(diagnostic: Diagnostic): string {
@@ -355,7 +362,7 @@ module ts {
355362
var previousDiagnostic = diagnostics[0];
356363
for (var i = 1; i < diagnostics.length; i++) {
357364
var currentDiagnostic = diagnostics[i];
358-
var isDupe = compareDiagnostics(currentDiagnostic, previousDiagnostic) === 0;
365+
var isDupe = compareDiagnostics(currentDiagnostic, previousDiagnostic) === Comparison.EqualTo;
359366
if (!isDupe) {
360367
newDiagnostics.push(currentDiagnostic);
361368
previousDiagnostic = currentDiagnostic;
@@ -644,7 +651,7 @@ module ts {
644651
return currentAssertionLevel >= level;
645652
}
646653

647-
export function assert(expression: any, message?: string, verboseDebugInfo?: () => string): void {
654+
export function assert(expression: boolean, message?: string, verboseDebugInfo?: () => string): void {
648655
if (!expression) {
649656
var verboseDebugString = "";
650657
if (verboseDebugInfo) {

‎src/compiler/diagnosticInformationMap.generated.ts

Copy file name to clipboardExpand all lines: src/compiler/diagnosticInformationMap.generated.ts
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ module ts {
121121
const_declarations_can_only_be_declared_inside_a_block: { code: 1156, category: DiagnosticCategory.Error, key: "'const' declarations can only be declared inside a block." },
122122
let_declarations_can_only_be_declared_inside_a_block: { code: 1157, category: DiagnosticCategory.Error, key: "'let' declarations can only be declared inside a block." },
123123
Aliased_type_cannot_be_an_object_type_literal_Use_an_interface_declaration_instead: { code: 1158, category: DiagnosticCategory.Error, key: "Aliased type cannot be an object type literal. Use an interface declaration instead." },
124+
Invalid_template_literal_expected: { code: 1159, category: DiagnosticCategory.Error, key: "Invalid template literal; expected '}'" },
125+
Tagged_templates_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1160, category: DiagnosticCategory.Error, key: "Tagged templates are only available when targeting ECMAScript 6 and higher." },
124126
Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
125127
Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." },
126128
Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." },

‎src/compiler/diagnosticMessages.json

Copy file name to clipboardExpand all lines: src/compiler/diagnosticMessages.json
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,14 @@
475475
"category": "Error",
476476
"code": 1158
477477
},
478+
"Invalid template literal; expected '}'": {
479+
"category": "Error",
480+
"code": 1159
481+
},
482+
"Tagged templates are only available when targeting ECMAScript 6 and higher.": {
483+
"category": "Error",
484+
"code": 1160
485+
},
478486

479487
"Duplicate identifier '{0}'.": {
480488
"category": "Error",

‎src/compiler/emitter.ts

Copy file name to clipboardExpand all lines: src/compiler/emitter.ts
+129-3Lines changed: 129 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -786,14 +786,123 @@ module ts {
786786
}
787787
}
788788

789-
function emitLiteral(node: LiteralExpression) {
790-
var text = getSourceTextOfLocalNode(node);
791-
if (node.kind === SyntaxKind.StringLiteral && compilerOptions.sourceMap) {
789+
function emitLiteral(node: LiteralExpression): void {
790+
var text = getLiteralText();
791+
792+
if (compilerOptions.sourceMap && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
792793
writer.writeLiteral(text);
793794
}
794795
else {
795796
write(text);
796797
}
798+
799+
function getLiteralText() {
800+
if (compilerOptions.target < ScriptTarget.ES6 && isTemplateLiteralKind(node.kind)) {
801+
return getTemplateLiteralAsStringLiteral(node)
802+
}
803+
804+
return getSourceTextOfLocalNode(node);
805+
}
806+
}
807+
808+
function getTemplateLiteralAsStringLiteral(node: LiteralExpression): string {
809+
return '"' + escapeString(node.text) + '"';
810+
}
811+
812+
function emitTemplateExpression(node: TemplateExpression): void {
813+
// In ES6 mode and above, we can simply emit each portion of a template in order, but in
814+
// ES3 & ES5 we must convert the template expression into a series of string concatenations.
815+
if (compilerOptions.target >= ScriptTarget.ES6) {
816+
forEachChild(node, emit);
817+
return;
818+
}
819+
820+
Debug.assert(node.parent.kind !== SyntaxKind.TaggedTemplateExpression);
821+
822+
var templateNeedsParens = isExpression(node.parent)
823+
&& node.parent.kind !== SyntaxKind.ParenExpression
824+
&& comparePrecedenceToBinaryPlus(node.parent) !== Comparison.LessThan;
825+
826+
if (templateNeedsParens) {
827+
write("(");
828+
}
829+
830+
emitLiteral(node.head);
831+
832+
forEach(node.templateSpans, templateSpan => {
833+
// Check if the expression has operands and binds its operands less closely than binary '+'.
834+
// If it does, we need to wrap the expression in parentheses. Otherwise, something like
835+
// `abc${ 1 << 2}`
836+
// becomes
837+
// "abc" + 1 << 2 + ""
838+
// which is really
839+
// ("abc" + 1) << (2 + "")
840+
// rather than
841+
// "abc" + (1 << 2) + ""
842+
var needsParens = templateSpan.expression.kind !== SyntaxKind.ParenExpression
843+
&& comparePrecedenceToBinaryPlus(templateSpan.expression) !== Comparison.GreaterThan;
844+
845+
write(" + ");
846+
847+
if (needsParens) {
848+
write("(");
849+
}
850+
emit(templateSpan.expression);
851+
if (needsParens) {
852+
write(")");
853+
}
854+
855+
// Only emit if the literal is non-empty.
856+
// The binary '+' operator is left-associative, so the first string concatenation will force
857+
// the result up to this point to be a string. Emitting a '+ ""' has no semantic effect.
858+
if (templateSpan.literal.text.length !== 0) {
859+
write(" + ")
860+
emitLiteral(templateSpan.literal);
861+
}
862+
});
863+
864+
if (templateNeedsParens) {
865+
write(")");
866+
}
867+
868+
/**
869+
* Returns whether the expression has lesser, greater,
870+
* or equal precedence to the binary '+' operator
871+
*/
872+
function comparePrecedenceToBinaryPlus(expression: Expression): Comparison {
873+
// All binary expressions have lower precedence than '+' apart from '*', '/', and '%'.
874+
// All unary operators have a higher precedence apart from yield.
875+
// Arrow functions and conditionals have a lower precedence,
876+
// although we convert the former into regular function expressions in ES5 mode,
877+
// and in ES6 mode this function won't get called anyway.
878+
//
879+
// TODO (drosen): Note that we need to account for the upcoming 'yield' and
880+
// spread ('...') unary operators that are anticipated for ES6.
881+
Debug.assert(compilerOptions.target <= ScriptTarget.ES5);
882+
switch (expression.kind) {
883+
case SyntaxKind.BinaryExpression:
884+
switch ((<BinaryExpression>expression).operator) {
885+
case SyntaxKind.AsteriskToken:
886+
case SyntaxKind.SlashToken:
887+
case SyntaxKind.PercentToken:
888+
return Comparison.GreaterThan;
889+
case SyntaxKind.PlusToken:
890+
return Comparison.EqualTo;
891+
default:
892+
return Comparison.LessThan;
893+
}
894+
case SyntaxKind.ConditionalExpression:
895+
return Comparison.LessThan;
896+
default:
897+
return Comparison.GreaterThan;
898+
}
899+
}
900+
901+
}
902+
903+
function emitTemplateSpan(span: TemplateSpan) {
904+
emit(span.expression);
905+
emit(span.literal);
797906
}
798907

799908
// This function specifically handles numeric/string literals for enum and accessor 'identifiers'.
@@ -977,6 +1086,13 @@ module ts {
9771086
}
9781087
}
9791088

1089+
function emitTaggedTemplateExpression(node: TaggedTemplateExpression): void {
1090+
Debug.assert(compilerOptions.target >= ScriptTarget.ES6, "Trying to emit a tagged template in pre-ES6 mode.");
1091+
emit(node.tag);
1092+
write(" ");
1093+
emit(node.template);
1094+
}
1095+
9801096
function emitParenExpression(node: ParenExpression) {
9811097
if (node.expression.kind === SyntaxKind.TypeAssertion) {
9821098
var operand = (<TypeAssertion>node.expression).operand;
@@ -2085,7 +2201,15 @@ module ts {
20852201
case SyntaxKind.NumericLiteral:
20862202
case SyntaxKind.StringLiteral:
20872203
case SyntaxKind.RegularExpressionLiteral:
2204+
case SyntaxKind.NoSubstitutionTemplateLiteral:
2205+
case SyntaxKind.TemplateHead:
2206+
case SyntaxKind.TemplateMiddle:
2207+
case SyntaxKind.TemplateTail:
20882208
return emitLiteral(<LiteralExpression>node);
2209+
case SyntaxKind.TemplateExpression:
2210+
return emitTemplateExpression(<TemplateExpression>node);
2211+
case SyntaxKind.TemplateSpan:
2212+
return emitTemplateSpan(<TemplateSpan>node);
20892213
case SyntaxKind.QualifiedName:
20902214
return emitPropertyAccess(<QualifiedName>node);
20912215
case SyntaxKind.ArrayLiteral:
@@ -2102,6 +2226,8 @@ module ts {
21022226
return emitCallExpression(<CallExpression>node);
21032227
case SyntaxKind.NewExpression:
21042228
return emitNewExpression(<NewExpression>node);
2229+
case SyntaxKind.TaggedTemplateExpression:
2230+
return emitTaggedTemplateExpression(<TaggedTemplateExpression>node);
21052231
case SyntaxKind.TypeAssertion:
21062232
return emit((<TypeAssertion>node).operand);
21072233
case SyntaxKind.ParenExpression:

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.