diff --git a/tslang/lib/TypeScript/MLIRGenExpressions.cpp b/tslang/lib/TypeScript/MLIRGenExpressions.cpp index c491ea9b3..3a2ce3843 100644 --- a/tslang/lib/TypeScript/MLIRGenExpressions.cpp +++ b/tslang/lib/TypeScript/MLIRGenExpressions.cpp @@ -1463,7 +1463,9 @@ namespace mlirgen auto stringType = getStringType(); SmallVector strs; - auto text = convertWideToUTF8(templateExpressionAST->head->rawText); + // the text with its escapes processed (`text`), not as written (`rawText`): `${n}\n${n}` held + // a backslash and an n + auto text = convertWideToUTF8(templateExpressionAST->head->text); auto head = builder.create(location, stringType, getStringAttr(text)); // first string @@ -1483,7 +1485,7 @@ namespace mlirgen // expr value strs.push_back(exprValue); - auto spanText = convertWideToUTF8(span->literal->rawText); + auto spanText = convertWideToUTF8(span->literal->text); auto spanValue = builder.create(location, stringType, getStringAttr(spanText)); // text @@ -1510,10 +1512,11 @@ namespace mlirgen SmallVector strs; SmallVector vals; + // the strings a tag receives have their escapes processed, as in JavaScript std::string text = convertWideToUTF8( - templateExpressionAST->head - ? templateExpressionAST->head->rawText - : templateExpressionAST->rawText); + templateExpressionAST->head + ? templateExpressionAST->head->text + : templateExpressionAST->text); // first string strs.push_back(getStringAttr(text)); @@ -1527,7 +1530,7 @@ namespace mlirgen vals.push_back(exprValue); - auto spanText = convertWideToUTF8(span->literal->rawText); + auto spanText = convertWideToUTF8(span->literal->text); // text strs.push_back(getStringAttr(spanText)); } diff --git a/tslang/lib/TypeScript/MLIRGenTypes.cpp b/tslang/lib/TypeScript/MLIRGenTypes.cpp index e178604f3..c062f40d0 100644 --- a/tslang/lib/TypeScript/MLIRGenTypes.cpp +++ b/tslang/lib/TypeScript/MLIRGenTypes.cpp @@ -2072,7 +2072,7 @@ namespace mlirgen auto location = loc(templateLiteralTypeNode); // first string - auto text = convertWideToUTF8(templateLiteralTypeNode->head->rawText); + auto text = convertWideToUTF8(templateLiteralTypeNode->head->text); SmallVector types; getTemplateLiteralSpan(types, text, templateLiteralTypeNode->templateSpans, 0, genContext); @@ -2129,7 +2129,7 @@ namespace mlirgen auto typeText = mlir::cast(literalType.getValue()).getValue(); ss << typeText.str(); - auto spanText = convertWideToUTF8(span->literal->rawText); + auto spanText = convertWideToUTF8(span->literal->text); ss << spanText; getTemplateLiteralSpan(types, ss.str(), spans, spanIndex + 1, genContext); diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 7a9a4d06e..1dacbc915 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -644,6 +644,7 @@ tslang_add_test(NAME test-compile-00-types-indexed-access-type COMMAND test-runn tslang_add_test(NAME test-compile-00-types-mapped-type COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00types_mappedtype.ts") tslang_add_test(NAME test-compile-01-types-mapped-type COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/01types_mappedtype.ts") tslang_add_test(NAME test-compile-00-types-template-literal-type COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00types_templateliteraltype.ts") +tslang_add_test(NAME test-compile-00-template-literal-escapes COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00template_literal_escapes.ts") tslang_add_test(NAME test-compile-01-types-template-literal-type COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/01types_templateliteraltype.ts") tslang_add_test(NAME test-compile-00-types-utility COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00types_utility.ts") tslang_add_test(NAME test-compile-01-types-utility COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/01types_utility.ts") @@ -1167,6 +1168,7 @@ tslang_add_test(NAME test-jit-00-types-indexed-access-type COMMAND test-runner - tslang_add_test(NAME test-jit-00-types-mapped-type COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00types_mappedtype.ts") tslang_add_test(NAME test-jit-01-types-mapped-type COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/01types_mappedtype.ts") tslang_add_test(NAME test-jit-00-types-template-literal-type COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00types_templateliteraltype.ts") +tslang_add_test(NAME test-jit-00-template-literal-escapes COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00template_literal_escapes.ts") tslang_add_test(NAME test-jit-01-types-template-literal-type COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/01types_templateliteraltype.ts") tslang_add_test(NAME test-jit-00-types-utility COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00types_utility.ts") tslang_add_test(NAME test-jit-01-types-utility COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/01types_utility.ts") @@ -2024,6 +2026,7 @@ set(TSLANG_CORPUS 00types_keyof_enum.ts 00types_mappedtype.ts 00types_templateliteraltype.ts + 00template_literal_escapes.ts 00types_unknown1.ts 00types_utility.ts 00types.ts diff --git a/tslang/test/tester/tests/00template_literal_escapes.ts b/tslang/test/tester/tests/00template_literal_escapes.ts new file mode 100644 index 000000000..910d1ac29 --- /dev/null +++ b/tslang/test/tester/tests/00template_literal_escapes.ts @@ -0,0 +1,44 @@ +// Escape sequences around a substitution in a template literal stayed as written: `${n}\n${n}` +// held the four characters 1, \, n, 1. A template without substitutions and a string literal +// were fine. + +type Name = "you"; +// a template literal type: "hi\tyou" did not match it, as the type kept a backslash and a t +type Greeting = `hi\t${Name}`; + +function tag(strings: string[], a: number, b: number): string { + return strings[0] + "|" + strings[1] + "|" + strings[2]; +} + +function main() { + const n = 1; + + const s = `${n}\n${n}`; + assert(s.length == 3, "middle escape"); + assert(s == "1\n1", "middle text"); + + const head = `\t${n}`; + assert(head.length == 2, "head escape"); + assert(head == "\t1", "head text"); + + const tail = `${n}\\`; + assert(tail.length == 2, "tail escape"); + assert(tail == "1\\", "tail text"); + + const several = `a\x41${n}Bb`; + assert(several == "aA1Bb", "hex and unicode escapes"); + + const quote = `\`${n}\``; + assert(quote == "`1`", "escaped backticks"); + + const plain = `a\nb`; + assert(plain.length == 3, "no substitution"); + + const tagged = tag`x\ty${n}\n${2}z`; + assert(tagged == "x\ty|\n|z", "tagged template strings are cooked"); + + const greeting: Greeting = "hi\tyou"; + assert(greeting.length == 6, "template literal type"); + + print("done."); +}