From 76b130a5cf33916c1898d6624ed7571ccc0eb92e Mon Sep 17 00:00:00 2001 From: bgenia Date: Wed, 27 Nov 2024 23:34:51 +0300 Subject: [PATCH 1/3] Disallow confusing combinations of !in and !instanceof --- src/compiler/diagnosticMessages.json | 8 ++ src/compiler/scanner.ts | 11 ++ ...xclamationBeforeInAndInstanceOf.errors.txt | 24 ++++ ...nfusingExclamationBeforeInAndInstanceOf.js | 30 +++++ ...ngExclamationBeforeInAndInstanceOf.symbols | 49 ++++++++ ...singExclamationBeforeInAndInstanceOf.types | 111 ++++++++++++++++++ ...nfusingExclamationBeforeInAndInstanceOf.ts | 14 +++ 7 files changed, 247 insertions(+) create mode 100644 tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.errors.txt create mode 100644 tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.js create mode 100644 tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.symbols create mode 100644 tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.types create mode 100644 tests/cases/compiler/confusingExclamationBeforeInAndInstanceOf.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 7f686076f1824..47223a350ef36 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1834,6 +1834,14 @@ "category": "Error", "code": 1544 }, + "Confusing combination of '!' token and 'instanceof' keyword like 'a !instanceof b' is disallowed, as it might be misinterpreted as '!(a instanceof b)'": { + "category": "Error", + "code": 1545 + }, + "Confusing combination of '!' token and 'in' keyword like 'a !in b' is disallowed, as it might be misinterpreted as '!(a in b)'": { + "category": "Error", + "code": 1546 + }, "The types of '{0}' are incompatible between these types.": { "category": "Error", diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 50e827c17bd24..8af878a158906 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -1967,7 +1967,18 @@ export function createScanner( } return pos += 2, token = SyntaxKind.ExclamationEqualsToken; } + const hasLeadingWhitespace = charCodeChecked(pos - 1) === CharacterCodes.space; pos++; + const nextIdentifierToken = lookAhead(() => { + tokenValue = scanIdentifierParts(); + return getIdentifierToken(); + }); + if (hasLeadingWhitespace && nextIdentifierToken === SyntaxKind.InKeyword) { + error(Diagnostics.Confusing_combination_of_token_and_in_keyword_like_a_in_b_is_disallowed_as_it_might_be_misinterpreted_as_a_in_b); + } + if (hasLeadingWhitespace && nextIdentifierToken === SyntaxKind.InstanceOfKeyword) { + error(Diagnostics.Confusing_combination_of_token_and_instanceof_keyword_like_a_instanceof_b_is_disallowed_as_it_might_be_misinterpreted_as_a_instanceof_b); + } return token = SyntaxKind.ExclamationToken; case CharacterCodes.doubleQuote: case CharacterCodes.singleQuote: diff --git a/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.errors.txt b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.errors.txt new file mode 100644 index 0000000000000..448c6adef20b6 --- /dev/null +++ b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.errors.txt @@ -0,0 +1,24 @@ +confusingExclamationBeforeInAndInstanceOf.ts(8,4): error TS1545: Confusing combination of '!' token and 'instanceof' keyword like 'a !instanceof b' is disallowed, as it might be misinterpreted as '!(a instanceof b)' +confusingExclamationBeforeInAndInstanceOf.ts(14,4): error TS1546: Confusing combination of '!' token and 'in' keyword like 'a !in b' is disallowed, as it might be misinterpreted as '!(a in b)' + + +==== confusingExclamationBeforeInAndInstanceOf.ts (2 errors) ==== + declare let a: any; + declare let b: any; + + a! instanceof b; // should work + a!instanceof b; // should work + a/**/!instanceof b; // should work + a!/**/instanceof b; // should work + a !instanceof b; // should error + +!!! error TS1545: Confusing combination of '!' token and 'instanceof' keyword like 'a !instanceof b' is disallowed, as it might be misinterpreted as '!(a instanceof b)' + + a! in b; // should work + a!in b; // should work + a/**/!in b; // should work + a!/**/in b; // should work + a !in b; // should error + +!!! error TS1546: Confusing combination of '!' token and 'in' keyword like 'a !in b' is disallowed, as it might be misinterpreted as '!(a in b)' + \ No newline at end of file diff --git a/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.js b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.js new file mode 100644 index 0000000000000..8ed30742d4cf6 --- /dev/null +++ b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.js @@ -0,0 +1,30 @@ +//// [tests/cases/compiler/confusingExclamationBeforeInAndInstanceOf.ts] //// + +//// [confusingExclamationBeforeInAndInstanceOf.ts] +declare let a: any; +declare let b: any; + +a! instanceof b; // should work +a!instanceof b; // should work +a/**/!instanceof b; // should work +a!/**/instanceof b; // should work +a !instanceof b; // should error + +a! in b; // should work +a!in b; // should work +a/**/!in b; // should work +a!/**/in b; // should work +a !in b; // should error + + +//// [confusingExclamationBeforeInAndInstanceOf.js] +a instanceof b; // should work +a instanceof b; // should work +a /**/ instanceof b; // should work +a /**/ instanceof b; // should work +a instanceof b; // should error +a in b; // should work +a in b; // should work +a /**/ in b; // should work +a /**/ in b; // should work +a in b; // should error diff --git a/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.symbols b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.symbols new file mode 100644 index 0000000000000..36e56c2041c9c --- /dev/null +++ b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.symbols @@ -0,0 +1,49 @@ +//// [tests/cases/compiler/confusingExclamationBeforeInAndInstanceOf.ts] //// + +=== confusingExclamationBeforeInAndInstanceOf.ts === +declare let a: any; +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) + +declare let b: any; +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + +a! instanceof b; // should work +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + +a!instanceof b; // should work +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + +a/**/!instanceof b; // should work +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + +a!/**/instanceof b; // should work +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + +a !instanceof b; // should error +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + +a! in b; // should work +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + +a!in b; // should work +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + +a/**/!in b; // should work +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + +a!/**/in b; // should work +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + +a !in b; // should error +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + diff --git a/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.types b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.types new file mode 100644 index 0000000000000..f9d86b00c3ae3 --- /dev/null +++ b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.types @@ -0,0 +1,111 @@ +//// [tests/cases/compiler/confusingExclamationBeforeInAndInstanceOf.ts] //// + +=== confusingExclamationBeforeInAndInstanceOf.ts === +declare let a: any; +>a : any +> : ^^^ + +declare let b: any; +>b : any +> : ^^^ + +a! instanceof b; // should work +>a! instanceof b : boolean +> : ^^^^^^^ +>a! : any +> : ^^^ +>a : any +> : ^^^ +>b : any +> : ^^^ + +a!instanceof b; // should work +>a!instanceof b : boolean +> : ^^^^^^^ +>a! : any +> : ^^^ +>a : any +> : ^^^ +>b : any +> : ^^^ + +a/**/!instanceof b; // should work +>a/**/!instanceof b : boolean +> : ^^^^^^^ +>a/**/! : any +> : ^^^ +>a : any +> : ^^^ +>b : any +> : ^^^ + +a!/**/instanceof b; // should work +>a!/**/instanceof b : boolean +> : ^^^^^^^ +>a! : any +> : ^^^ +>a : any +> : ^^^ +>b : any +> : ^^^ + +a !instanceof b; // should error +>a !instanceof b : boolean +> : ^^^^^^^ +>a ! : any +> : ^^^ +>a : any +> : ^^^ +>b : any +> : ^^^ + +a! in b; // should work +>a! in b : boolean +> : ^^^^^^^ +>a! : any +> : ^^^ +>a : any +> : ^^^ +>b : any +> : ^^^ + +a!in b; // should work +>a!in b : boolean +> : ^^^^^^^ +>a! : any +> : ^^^ +>a : any +> : ^^^ +>b : any +> : ^^^ + +a/**/!in b; // should work +>a/**/!in b : boolean +> : ^^^^^^^ +>a/**/! : any +> : ^^^ +>a : any +> : ^^^ +>b : any +> : ^^^ + +a!/**/in b; // should work +>a!/**/in b : boolean +> : ^^^^^^^ +>a! : any +> : ^^^ +>a : any +> : ^^^ +>b : any +> : ^^^ + +a !in b; // should error +>a !in b : boolean +> : ^^^^^^^ +>a ! : any +> : ^^^ +>a : any +> : ^^^ +>b : any +> : ^^^ + diff --git a/tests/cases/compiler/confusingExclamationBeforeInAndInstanceOf.ts b/tests/cases/compiler/confusingExclamationBeforeInAndInstanceOf.ts new file mode 100644 index 0000000000000..99f222a9e46cf --- /dev/null +++ b/tests/cases/compiler/confusingExclamationBeforeInAndInstanceOf.ts @@ -0,0 +1,14 @@ +declare let a: any; +declare let b: any; + +a! instanceof b; // should work +a!instanceof b; // should work +a/**/!instanceof b; // should work +a!/**/instanceof b; // should work +a !instanceof b; // should error + +a! in b; // should work +a!in b; // should work +a/**/!in b; // should work +a!/**/in b; // should work +a !in b; // should error From 5947f8fc12811677b6639b36b59813427d465611 Mon Sep 17 00:00:00 2001 From: bgenia Date: Wed, 27 Nov 2024 23:50:22 +0300 Subject: [PATCH 2/3] Use isWhiteSpaceLike to check for leading whitespace before exclamation --- src/compiler/scanner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 8af878a158906..3098d84e22fd4 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -1967,7 +1967,7 @@ export function createScanner( } return pos += 2, token = SyntaxKind.ExclamationEqualsToken; } - const hasLeadingWhitespace = charCodeChecked(pos - 1) === CharacterCodes.space; + const hasLeadingWhitespace = isWhiteSpaceLike(charCodeChecked(pos - 1)); pos++; const nextIdentifierToken = lookAhead(() => { tokenValue = scanIdentifierParts(); From a2429cb1ce79bc907290dae380d6a0072e0c7b54 Mon Sep 17 00:00:00 2001 From: bgenia Date: Thu, 28 Nov 2024 00:03:09 +0300 Subject: [PATCH 3/3] Add tests with leading tab characters before exclamation --- ...xclamationBeforeInAndInstanceOf.errors.txt | 12 +++++++++-- ...nfusingExclamationBeforeInAndInstanceOf.js | 4 ++++ ...ngExclamationBeforeInAndInstanceOf.symbols | 8 ++++++++ ...singExclamationBeforeInAndInstanceOf.types | 20 +++++++++++++++++++ ...nfusingExclamationBeforeInAndInstanceOf.ts | 2 ++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.errors.txt b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.errors.txt index 448c6adef20b6..0cbd233deb38a 100644 --- a/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.errors.txt +++ b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.errors.txt @@ -1,8 +1,10 @@ confusingExclamationBeforeInAndInstanceOf.ts(8,4): error TS1545: Confusing combination of '!' token and 'instanceof' keyword like 'a !instanceof b' is disallowed, as it might be misinterpreted as '!(a instanceof b)' -confusingExclamationBeforeInAndInstanceOf.ts(14,4): error TS1546: Confusing combination of '!' token and 'in' keyword like 'a !in b' is disallowed, as it might be misinterpreted as '!(a in b)' +confusingExclamationBeforeInAndInstanceOf.ts(9,4): error TS1545: Confusing combination of '!' token and 'instanceof' keyword like 'a !instanceof b' is disallowed, as it might be misinterpreted as '!(a instanceof b)' +confusingExclamationBeforeInAndInstanceOf.ts(15,4): error TS1546: Confusing combination of '!' token and 'in' keyword like 'a !in b' is disallowed, as it might be misinterpreted as '!(a in b)' +confusingExclamationBeforeInAndInstanceOf.ts(16,4): error TS1546: Confusing combination of '!' token and 'in' keyword like 'a !in b' is disallowed, as it might be misinterpreted as '!(a in b)' -==== confusingExclamationBeforeInAndInstanceOf.ts (2 errors) ==== +==== confusingExclamationBeforeInAndInstanceOf.ts (4 errors) ==== declare let a: any; declare let b: any; @@ -12,6 +14,9 @@ confusingExclamationBeforeInAndInstanceOf.ts(14,4): error TS1546: Confusing comb a!/**/instanceof b; // should work a !instanceof b; // should error +!!! error TS1545: Confusing combination of '!' token and 'instanceof' keyword like 'a !instanceof b' is disallowed, as it might be misinterpreted as '!(a instanceof b)' + a !instanceof b; // should error + !!! error TS1545: Confusing combination of '!' token and 'instanceof' keyword like 'a !instanceof b' is disallowed, as it might be misinterpreted as '!(a instanceof b)' a! in b; // should work @@ -20,5 +25,8 @@ confusingExclamationBeforeInAndInstanceOf.ts(14,4): error TS1546: Confusing comb a!/**/in b; // should work a !in b; // should error +!!! error TS1546: Confusing combination of '!' token and 'in' keyword like 'a !in b' is disallowed, as it might be misinterpreted as '!(a in b)' + a !in b; // should error + !!! error TS1546: Confusing combination of '!' token and 'in' keyword like 'a !in b' is disallowed, as it might be misinterpreted as '!(a in b)' \ No newline at end of file diff --git a/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.js b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.js index 8ed30742d4cf6..c49a88e2e3493 100644 --- a/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.js +++ b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.js @@ -9,12 +9,14 @@ a!instanceof b; // should work a/**/!instanceof b; // should work a!/**/instanceof b; // should work a !instanceof b; // should error +a !instanceof b; // should error a! in b; // should work a!in b; // should work a/**/!in b; // should work a!/**/in b; // should work a !in b; // should error +a !in b; // should error //// [confusingExclamationBeforeInAndInstanceOf.js] @@ -23,8 +25,10 @@ a instanceof b; // should work a /**/ instanceof b; // should work a /**/ instanceof b; // should work a instanceof b; // should error +a instanceof b; // should error a in b; // should work a in b; // should work a /**/ in b; // should work a /**/ in b; // should work a in b; // should error +a in b; // should error diff --git a/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.symbols b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.symbols index 36e56c2041c9c..4646982fb3f7a 100644 --- a/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.symbols +++ b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.symbols @@ -27,6 +27,10 @@ a !instanceof b; // should error >a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) >b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) +a !instanceof b; // should error +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + a! in b; // should work >a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) >b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) @@ -47,3 +51,7 @@ a !in b; // should error >a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) >b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) +a !in b; // should error +>a : Symbol(a, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 0, 11)) +>b : Symbol(b, Decl(confusingExclamationBeforeInAndInstanceOf.ts, 1, 11)) + diff --git a/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.types b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.types index f9d86b00c3ae3..6165c34ddf9d8 100644 --- a/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.types +++ b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.types @@ -59,6 +59,16 @@ a !instanceof b; // should error >b : any > : ^^^ +a !instanceof b; // should error +>a !instanceof b : boolean +> : ^^^^^^^ +>a ! : any +> : ^^^ +>a : any +> : ^^^ +>b : any +> : ^^^ + a! in b; // should work >a! in b : boolean > : ^^^^^^^ @@ -109,3 +119,13 @@ a !in b; // should error >b : any > : ^^^ +a !in b; // should error +>a !in b : boolean +> : ^^^^^^^ +>a ! : any +> : ^^^ +>a : any +> : ^^^ +>b : any +> : ^^^ + diff --git a/tests/cases/compiler/confusingExclamationBeforeInAndInstanceOf.ts b/tests/cases/compiler/confusingExclamationBeforeInAndInstanceOf.ts index 99f222a9e46cf..66227cab625cd 100644 --- a/tests/cases/compiler/confusingExclamationBeforeInAndInstanceOf.ts +++ b/tests/cases/compiler/confusingExclamationBeforeInAndInstanceOf.ts @@ -6,9 +6,11 @@ a!instanceof b; // should work a/**/!instanceof b; // should work a!/**/instanceof b; // should work a !instanceof b; // should error +a !instanceof b; // should error a! in b; // should work a!in b; // should work a/**/!in b; // should work a!/**/in b; // should work a !in b; // should error +a !in b; // should error