Skip to content

Add error message for keywords with escapes in them #32718

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Aug 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 11 additions & 2 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down Expand Up @@ -1380,7 +1389,7 @@ namespace ts {
node.originalKeywordKind = token();
}
node.escapedText = escapeLeadingUnderscores(internIdentifier(scanner.getTokenValue()));
nextToken();
nextTokenWithoutCheck();
return finishNode(node);
}

Expand Down
6 changes: 6 additions & 0 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ namespace ts {
getTokenPos(): number;
getTokenText(): string;
getTokenValue(): string;
hasUnicodeEscape(): boolean;
hasExtendedUnicodeEscape(): boolean;
hasPrecedingLineBreak(): boolean;
isIdentifier(): boolean;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -1245,6 +1247,7 @@ namespace ts {
return scanExtendedUnicodeEscape();
}

tokenFlags |= TokenFlags.UnicodeEscape;
// '\uDDDD'
return scanHexadecimalEscape(/*numDigits*/ 4);

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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();
}
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2639,6 +2639,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);
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3174,6 +3174,7 @@ declare namespace ts {
getTokenPos(): number;
getTokenText(): string;
getTokenValue(): string;
hasUnicodeEscape(): boolean;
hasExtendedUnicodeEscape(): boolean;
hasPrecedingLineBreak(): boolean;
isIdentifier(): boolean;
Expand Down
1 change: 1 addition & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3174,6 +3174,7 @@ declare namespace ts {
getTokenPos(): number;
getTokenText(): string;
getTokenValue(): string;
hasUnicodeEscape(): boolean;
hasExtendedUnicodeEscape(): boolean;
hasPrecedingLineBreak(): boolean;
isIdentifier(): boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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 changes: 62 additions & 0 deletions tests/baselines/reference/scannerUnicodeEscapeInKeyword2.js
Original file line number Diff line number Diff line change
@@ -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
62 changes: 62 additions & 0 deletions tests/baselines/reference/scannerUnicodeEscapeInKeyword2.symbols
Original file line number Diff line number Diff line change
@@ -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

Loading