Add error message for keywords with escapes in them (#32718)
* Add error message for keywords with escapes in them * Move check into parser during advance to next token to utilize context for contextual keywords * git add . * Add tests for extended escapes * Better error courtesy of @DanielRossenwaser * Add test of browser-inconsistent case and alter condition to match spec * Merge adjacent conditions * Use seperate functions for checking keywords vs not * Use flags to track unicode escape presence * Adjust error text
This commit is contained in:
parent
51411c1f8b
commit
4ab85bbf35
|
@ -851,6 +851,10 @@
|
|||
"category": "Error",
|
||||
"code": 1259
|
||||
},
|
||||
"Keywords cannot contain escape characters.": {
|
||||
"category": "Error",
|
||||
"code": 1260
|
||||
},
|
||||
"'with' statements are not allowed in an async function block.": {
|
||||
"category": "Error",
|
||||
"code": 1300
|
||||
|
|
|
@ -1086,10 +1086,19 @@ namespace ts {
|
|||
return currentToken;
|
||||
}
|
||||
|
||||
function nextToken(): SyntaxKind {
|
||||
function nextTokenWithoutCheck() {
|
||||
return currentToken = scanner.scan();
|
||||
}
|
||||
|
||||
function nextToken(): SyntaxKind {
|
||||
// if the keyword had an escape
|
||||
if (isKeyword(currentToken) && (scanner.hasUnicodeEscape() || scanner.hasExtendedUnicodeEscape())) {
|
||||
// issue a parse error for the escape
|
||||
parseErrorAt(scanner.getTokenPos(), scanner.getTextPos(), Diagnostics.Keywords_cannot_contain_escape_characters);
|
||||
}
|
||||
return nextTokenWithoutCheck();
|
||||
}
|
||||
|
||||
function nextTokenJSDoc(): JSDocSyntaxKind {
|
||||
return currentToken = scanner.scanJsDocToken();
|
||||
}
|
||||
|
@ -1380,7 +1389,7 @@ namespace ts {
|
|||
node.originalKeywordKind = token();
|
||||
}
|
||||
node.escapedText = escapeLeadingUnderscores(internIdentifier(scanner.getTokenValue()));
|
||||
nextToken();
|
||||
nextTokenWithoutCheck();
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace ts {
|
|||
getTokenPos(): number;
|
||||
getTokenText(): string;
|
||||
getTokenValue(): string;
|
||||
hasUnicodeEscape(): boolean;
|
||||
hasExtendedUnicodeEscape(): boolean;
|
||||
hasPrecedingLineBreak(): boolean;
|
||||
isIdentifier(): boolean;
|
||||
|
@ -884,6 +885,7 @@ namespace ts {
|
|||
getTokenPos: () => tokenPos,
|
||||
getTokenText: () => text.substring(tokenPos, pos),
|
||||
getTokenValue: () => tokenValue,
|
||||
hasUnicodeEscape: () => (tokenFlags & TokenFlags.UnicodeEscape) !== 0,
|
||||
hasExtendedUnicodeEscape: () => (tokenFlags & TokenFlags.ExtendedUnicodeEscape) !== 0,
|
||||
hasPrecedingLineBreak: () => (tokenFlags & TokenFlags.PrecedingLineBreak) !== 0,
|
||||
isIdentifier: () => token === SyntaxKind.Identifier || token > SyntaxKind.LastReservedWord,
|
||||
|
@ -1245,6 +1247,7 @@ namespace ts {
|
|||
return scanExtendedUnicodeEscape();
|
||||
}
|
||||
|
||||
tokenFlags |= TokenFlags.UnicodeEscape;
|
||||
// '\uDDDD'
|
||||
return scanHexadecimalEscape(/*numDigits*/ 4);
|
||||
|
||||
|
@ -1376,6 +1379,7 @@ namespace ts {
|
|||
if (!(ch >= 0 && isIdentifierPart(ch, languageVersion))) {
|
||||
break;
|
||||
}
|
||||
tokenFlags |= TokenFlags.UnicodeEscape;
|
||||
result += text.substring(start, pos);
|
||||
result += utf16EncodeAsString(ch);
|
||||
// Valid Unicode escape is always six characters
|
||||
|
@ -1868,6 +1872,7 @@ namespace ts {
|
|||
const cookedChar = peekUnicodeEscape();
|
||||
if (cookedChar >= 0 && isIdentifierStart(cookedChar, languageVersion)) {
|
||||
pos += 6;
|
||||
tokenFlags |= TokenFlags.UnicodeEscape;
|
||||
tokenValue = String.fromCharCode(cookedChar) + scanIdentifierParts();
|
||||
return token = getIdentifierToken();
|
||||
}
|
||||
|
@ -2156,6 +2161,7 @@ namespace ts {
|
|||
const cookedChar = peekUnicodeEscape();
|
||||
if (cookedChar >= 0 && isIdentifierStart(cookedChar, languageVersion)) {
|
||||
pos += 6;
|
||||
tokenFlags |= TokenFlags.UnicodeEscape;
|
||||
tokenValue = String.fromCharCode(cookedChar) + scanIdentifierParts();
|
||||
return token = getIdentifierToken();
|
||||
}
|
||||
|
|
|
@ -1679,6 +1679,8 @@ namespace ts {
|
|||
/* @internal */
|
||||
ContainsSeparator = 1 << 9, // e.g. `0b1100_0101`
|
||||
/* @internal */
|
||||
UnicodeEscape = 1 << 10,
|
||||
/* @internal */
|
||||
BinaryOrOctalSpecifier = BinarySpecifier | OctalSpecifier,
|
||||
/* @internal */
|
||||
NumericLiteralFlags = Scientific | Octal | HexSpecifier | BinaryOrOctalSpecifier | ContainsSeparator
|
||||
|
|
|
@ -2682,6 +2682,10 @@ namespace ts {
|
|||
return isKeyword(token) && !isContextualKeyword(token);
|
||||
}
|
||||
|
||||
export function isFutureReservedKeyword(token: SyntaxKind): boolean {
|
||||
return SyntaxKind.FirstFutureReservedWord <= token && token <= SyntaxKind.LastFutureReservedWord;
|
||||
}
|
||||
|
||||
export function isStringANonContextualKeyword(name: string) {
|
||||
const token = stringToToken(name);
|
||||
return token !== undefined && isNonContextualKeyword(token);
|
||||
|
|
|
@ -3174,6 +3174,7 @@ declare namespace ts {
|
|||
getTokenPos(): number;
|
||||
getTokenText(): string;
|
||||
getTokenValue(): string;
|
||||
hasUnicodeEscape(): boolean;
|
||||
hasExtendedUnicodeEscape(): boolean;
|
||||
hasPrecedingLineBreak(): boolean;
|
||||
isIdentifier(): boolean;
|
||||
|
|
|
@ -3174,6 +3174,7 @@ declare namespace ts {
|
|||
getTokenPos(): number;
|
||||
getTokenText(): string;
|
||||
getTokenValue(): string;
|
||||
hasUnicodeEscape(): boolean;
|
||||
hasExtendedUnicodeEscape(): boolean;
|
||||
hasPrecedingLineBreak(): boolean;
|
||||
isIdentifier(): boolean;
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
tests/cases/conformance/scanner/ecmascript5/scannerUnicodeEscapeInKeyword1.ts(1,1): error TS1260: Keywords cannot contain escape characters.
|
||||
|
||||
|
||||
==== tests/cases/conformance/scanner/ecmascript5/scannerUnicodeEscapeInKeyword1.ts (1 errors) ====
|
||||
\u0076ar x = "hello";
|
||||
~~~~~~~~
|
||||
!!! error TS1260: Keywords cannot contain escape characters.
|
|
@ -0,0 +1,61 @@
|
|||
tests/cases/conformance/scanner/ecmascript5/file1.ts(3,5): error TS1260: Keywords cannot contain escape characters.
|
||||
tests/cases/conformance/scanner/ecmascript5/file1.ts(8,5): error TS1260: Keywords cannot contain escape characters.
|
||||
tests/cases/conformance/scanner/ecmascript5/file1.ts(13,1): error TS1260: Keywords cannot contain escape characters.
|
||||
tests/cases/conformance/scanner/ecmascript5/file2.ts(1,1): error TS1260: Keywords cannot contain escape characters.
|
||||
tests/cases/conformance/scanner/ecmascript5/file2.ts(5,5): error TS1260: Keywords cannot contain escape characters.
|
||||
tests/cases/conformance/scanner/ecmascript5/file2.ts(10,5): error TS1260: Keywords cannot contain escape characters.
|
||||
tests/cases/conformance/scanner/ecmascript5/file2.ts(15,1): error TS1260: Keywords cannot contain escape characters.
|
||||
|
||||
|
||||
==== tests/cases/conformance/scanner/ecmascript5/file1.ts (3 errors) ====
|
||||
var \u0061wait = 12; // ok
|
||||
async function main() {
|
||||
\u0061wait 12; // not ok
|
||||
~~~~~~~~~~
|
||||
!!! error TS1260: Keywords cannot contain escape characters.
|
||||
}
|
||||
|
||||
var \u0079ield = 12; // ok
|
||||
function *gen() {
|
||||
\u0079ield 12; //not ok
|
||||
~~~~~~~~~~
|
||||
!!! error TS1260: Keywords cannot contain escape characters.
|
||||
}
|
||||
|
||||
type typ\u0065 = 12; // ok
|
||||
|
||||
typ\u0065 notok = 0; // not ok
|
||||
~~~~~~~~~
|
||||
!!! error TS1260: Keywords cannot contain escape characters.
|
||||
|
||||
export {};
|
||||
==== tests/cases/conformance/scanner/ecmascript5/file2.ts (4 errors) ====
|
||||
\u{0076}ar x = "hello"; // not ok
|
||||
~~~~~~~~~~
|
||||
!!! error TS1260: Keywords cannot contain escape characters.
|
||||
|
||||
var \u{0061}wait = 12; // ok
|
||||
async function main() {
|
||||
\u{0061}wait 12; // not ok
|
||||
~~~~~~~~~~~~
|
||||
!!! error TS1260: Keywords cannot contain escape characters.
|
||||
}
|
||||
|
||||
var \u{0079}ield = 12; // ok
|
||||
function *gen() {
|
||||
\u{0079}ield 12; //not ok
|
||||
~~~~~~~~~~~~
|
||||
!!! error TS1260: Keywords cannot contain escape characters.
|
||||
}
|
||||
|
||||
type typ\u{0065} = 12; // ok
|
||||
|
||||
typ\u{0065} notok = 0; // not ok
|
||||
~~~~~~~~~~~
|
||||
!!! error TS1260: Keywords cannot contain escape characters.
|
||||
|
||||
export {};
|
||||
|
||||
const a = {def\u0061ult: 12}; // OK, `default` not in keyword position
|
||||
// chrome and jsc may still error on this, ref https://bugs.chromium.org/p/chromium/issues/detail?id=993000 and https://bugs.webkit.org/show_bug.cgi?id=200638
|
||||
|
62
tests/baselines/reference/scannerUnicodeEscapeInKeyword2.js
Normal file
62
tests/baselines/reference/scannerUnicodeEscapeInKeyword2.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
//// [tests/cases/conformance/scanner/ecmascript5/scannerUnicodeEscapeInKeyword2.ts] ////
|
||||
|
||||
//// [file1.ts]
|
||||
var \u0061wait = 12; // ok
|
||||
async function main() {
|
||||
\u0061wait 12; // not ok
|
||||
}
|
||||
|
||||
var \u0079ield = 12; // ok
|
||||
function *gen() {
|
||||
\u0079ield 12; //not ok
|
||||
}
|
||||
|
||||
type typ\u0065 = 12; // ok
|
||||
|
||||
typ\u0065 notok = 0; // not ok
|
||||
|
||||
export {};
|
||||
//// [file2.ts]
|
||||
\u{0076}ar x = "hello"; // not ok
|
||||
|
||||
var \u{0061}wait = 12; // ok
|
||||
async function main() {
|
||||
\u{0061}wait 12; // not ok
|
||||
}
|
||||
|
||||
var \u{0079}ield = 12; // ok
|
||||
function *gen() {
|
||||
\u{0079}ield 12; //not ok
|
||||
}
|
||||
|
||||
type typ\u{0065} = 12; // ok
|
||||
|
||||
typ\u{0065} notok = 0; // not ok
|
||||
|
||||
export {};
|
||||
|
||||
const a = {def\u0061ult: 12}; // OK, `default` not in keyword position
|
||||
// chrome and jsc may still error on this, ref https://bugs.chromium.org/p/chromium/issues/detail?id=993000 and https://bugs.webkit.org/show_bug.cgi?id=200638
|
||||
|
||||
|
||||
//// [file1.js]
|
||||
var \u0061wait = 12; // ok
|
||||
async function main() {
|
||||
await 12; // not ok
|
||||
}
|
||||
var \u0079ield = 12; // ok
|
||||
function* gen() {
|
||||
yield 12; //not ok
|
||||
}
|
||||
//// [file2.js]
|
||||
var x = "hello"; // not ok
|
||||
var \u{0061}wait = 12; // ok
|
||||
async function main() {
|
||||
await 12; // not ok
|
||||
}
|
||||
var \u{0079}ield = 12; // ok
|
||||
function* gen() {
|
||||
yield 12; //not ok
|
||||
}
|
||||
const a = { def\u0061ult: 12 }; // OK, `default` not in keyword position
|
||||
// chrome and jsc may still error on this, ref https://bugs.chromium.org/p/chromium/issues/detail?id=993000 and https://bugs.webkit.org/show_bug.cgi?id=200638
|
|
@ -0,0 +1,62 @@
|
|||
=== tests/cases/conformance/scanner/ecmascript5/file1.ts ===
|
||||
var \u0061wait = 12; // ok
|
||||
>\u0061wait : Symbol(\u0061wait, Decl(file1.ts, 0, 3))
|
||||
|
||||
async function main() {
|
||||
>main : Symbol(main, Decl(file1.ts, 0, 20))
|
||||
|
||||
\u0061wait 12; // not ok
|
||||
}
|
||||
|
||||
var \u0079ield = 12; // ok
|
||||
>\u0079ield : Symbol(\u0079ield, Decl(file1.ts, 5, 3))
|
||||
|
||||
function *gen() {
|
||||
>gen : Symbol(gen, Decl(file1.ts, 5, 20))
|
||||
|
||||
\u0079ield 12; //not ok
|
||||
}
|
||||
|
||||
type typ\u0065 = 12; // ok
|
||||
>typ\u0065 : Symbol(typ\u0065, Decl(file1.ts, 8, 1))
|
||||
|
||||
typ\u0065 notok = 0; // not ok
|
||||
>notok : Symbol(notok, Decl(file1.ts, 10, 20))
|
||||
|
||||
export {};
|
||||
=== tests/cases/conformance/scanner/ecmascript5/file2.ts ===
|
||||
\u{0076}ar x = "hello"; // not ok
|
||||
>x : Symbol(x, Decl(file2.ts, 0, 10))
|
||||
|
||||
var \u{0061}wait = 12; // ok
|
||||
>\u{0061}wait : Symbol(\u{0061}wait, Decl(file2.ts, 2, 3))
|
||||
|
||||
async function main() {
|
||||
>main : Symbol(main, Decl(file2.ts, 2, 22))
|
||||
|
||||
\u{0061}wait 12; // not ok
|
||||
}
|
||||
|
||||
var \u{0079}ield = 12; // ok
|
||||
>\u{0079}ield : Symbol(\u{0079}ield, Decl(file2.ts, 7, 3))
|
||||
|
||||
function *gen() {
|
||||
>gen : Symbol(gen, Decl(file2.ts, 7, 22))
|
||||
|
||||
\u{0079}ield 12; //not ok
|
||||
}
|
||||
|
||||
type typ\u{0065} = 12; // ok
|
||||
>typ\u{0065} : Symbol(typ\u{0065}, Decl(file2.ts, 10, 1))
|
||||
|
||||
typ\u{0065} notok = 0; // not ok
|
||||
>notok : Symbol(notok, Decl(file2.ts, 12, 22))
|
||||
|
||||
export {};
|
||||
|
||||
const a = {def\u0061ult: 12}; // OK, `default` not in keyword position
|
||||
>a : Symbol(a, Decl(file2.ts, 18, 5))
|
||||
>def\u0061ult : Symbol(def\u0061ult, Decl(file2.ts, 18, 11))
|
||||
|
||||
// chrome and jsc may still error on this, ref https://bugs.chromium.org/p/chromium/issues/detail?id=993000 and https://bugs.webkit.org/show_bug.cgi?id=200638
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
=== tests/cases/conformance/scanner/ecmascript5/file1.ts ===
|
||||
var \u0061wait = 12; // ok
|
||||
>\u0061wait : number
|
||||
>12 : 12
|
||||
|
||||
async function main() {
|
||||
>main : () => Promise<void>
|
||||
|
||||
\u0061wait 12; // not ok
|
||||
>\u0061wait 12 : 12
|
||||
>12 : 12
|
||||
}
|
||||
|
||||
var \u0079ield = 12; // ok
|
||||
>\u0079ield : number
|
||||
>12 : 12
|
||||
|
||||
function *gen() {
|
||||
>gen : () => Generator<number, void, unknown>
|
||||
|
||||
\u0079ield 12; //not ok
|
||||
>\u0079ield 12 : any
|
||||
>12 : 12
|
||||
}
|
||||
|
||||
type typ\u0065 = 12; // ok
|
||||
>typ\u0065 : 12
|
||||
|
||||
typ\u0065 notok = 0; // not ok
|
||||
>notok : 0
|
||||
|
||||
export {};
|
||||
=== tests/cases/conformance/scanner/ecmascript5/file2.ts ===
|
||||
\u{0076}ar x = "hello"; // not ok
|
||||
>x : string
|
||||
>"hello" : "hello"
|
||||
|
||||
var \u{0061}wait = 12; // ok
|
||||
>\u{0061}wait : number
|
||||
>12 : 12
|
||||
|
||||
async function main() {
|
||||
>main : () => Promise<void>
|
||||
|
||||
\u{0061}wait 12; // not ok
|
||||
>\u{0061}wait 12 : 12
|
||||
>12 : 12
|
||||
}
|
||||
|
||||
var \u{0079}ield = 12; // ok
|
||||
>\u{0079}ield : number
|
||||
>12 : 12
|
||||
|
||||
function *gen() {
|
||||
>gen : () => Generator<number, void, unknown>
|
||||
|
||||
\u{0079}ield 12; //not ok
|
||||
>\u{0079}ield 12 : any
|
||||
>12 : 12
|
||||
}
|
||||
|
||||
type typ\u{0065} = 12; // ok
|
||||
>typ\u{0065} : 12
|
||||
|
||||
typ\u{0065} notok = 0; // not ok
|
||||
>notok : 0
|
||||
|
||||
export {};
|
||||
|
||||
const a = {def\u0061ult: 12}; // OK, `default` not in keyword position
|
||||
>a : { def\u0061ult: number; }
|
||||
>{def\u0061ult: 12} : { def\u0061ult: number; }
|
||||
>def\u0061ult : number
|
||||
>12 : 12
|
||||
|
||||
// chrome and jsc may still error on this, ref https://bugs.chromium.org/p/chromium/issues/detail?id=993000 and https://bugs.webkit.org/show_bug.cgi?id=200638
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
tests/cases/compiler/switchStatementsWithMultipleDefaults.ts(8,5): error TS1113: A 'default' clause cannot appear more than once in a 'switch' statement.
|
||||
tests/cases/compiler/switchStatementsWithMultipleDefaults.ts(20,13): error TS1113: A 'default' clause cannot appear more than once in a 'switch' statement.
|
||||
tests/cases/compiler/switchStatementsWithMultipleDefaults.ts(27,22): error TS1108: A 'return' statement can only be used within a function body.
|
||||
tests/cases/compiler/switchStatementsWithMultipleDefaults.ts(25,13): error TS1260: Keywords cannot contain escape characters.
|
||||
|
||||
|
||||
==== tests/cases/compiler/switchStatementsWithMultipleDefaults.ts (3 errors) ====
|
||||
==== tests/cases/compiler/switchStatementsWithMultipleDefaults.ts (1 errors) ====
|
||||
var x = 10;
|
||||
|
||||
switch (x) {
|
||||
|
@ -12,8 +10,6 @@ tests/cases/compiler/switchStatementsWithMultipleDefaults.ts(27,22): error TS110
|
|||
default: // No issues.
|
||||
break;
|
||||
default: // Error; second 'default' clause.
|
||||
~~~~~~~~
|
||||
!!! error TS1113: A 'default' clause cannot appear more than once in a 'switch' statement.
|
||||
default: // Error; third 'default' clause.
|
||||
case 3:
|
||||
x *= x;
|
||||
|
@ -26,17 +22,15 @@ tests/cases/compiler/switchStatementsWithMultipleDefaults.ts(27,22): error TS110
|
|||
switch (x * x) {
|
||||
default: // No issues.
|
||||
default: // Error; second 'default' clause.
|
||||
~~~~~~~~
|
||||
!!! error TS1113: A 'default' clause cannot appear more than once in a 'switch' statement.
|
||||
break;
|
||||
case 10000:
|
||||
x /= x;
|
||||
default: // Error, third 'default' clause
|
||||
def\u0061ult: // Error, fourth 'default' clause.
|
||||
~~~~~~~~~~~~
|
||||
!!! error TS1260: Keywords cannot contain escape characters.
|
||||
// Errors on fifth-seventh
|
||||
default: return;
|
||||
~~~~~~
|
||||
!!! error TS1108: A 'return' statement can only be used within a function body.
|
||||
default: default:
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// @target: esnext
|
||||
// @filename: file1.ts
|
||||
var \u0061wait = 12; // ok
|
||||
async function main() {
|
||||
\u0061wait 12; // not ok
|
||||
}
|
||||
|
||||
var \u0079ield = 12; // ok
|
||||
function *gen() {
|
||||
\u0079ield 12; //not ok
|
||||
}
|
||||
|
||||
type typ\u0065 = 12; // ok
|
||||
|
||||
typ\u0065 notok = 0; // not ok
|
||||
|
||||
export {};
|
||||
// @filename: file2.ts
|
||||
|
||||
\u{0076}ar x = "hello"; // not ok
|
||||
|
||||
var \u{0061}wait = 12; // ok
|
||||
async function main() {
|
||||
\u{0061}wait 12; // not ok
|
||||
}
|
||||
|
||||
var \u{0079}ield = 12; // ok
|
||||
function *gen() {
|
||||
\u{0079}ield 12; //not ok
|
||||
}
|
||||
|
||||
type typ\u{0065} = 12; // ok
|
||||
|
||||
typ\u{0065} notok = 0; // not ok
|
||||
|
||||
export {};
|
||||
|
||||
const a = {def\u0061ult: 12}; // OK, `default` not in keyword position
|
||||
// chrome and jsc may still error on this, ref https://bugs.chromium.org/p/chromium/issues/detail?id=993000 and https://bugs.webkit.org/show_bug.cgi?id=200638
|
Loading…
Reference in a new issue