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..3098d84e22fd4 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 = isWhiteSpaceLike(charCodeChecked(pos - 1)); 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..0cbd233deb38a --- /dev/null +++ b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.errors.txt @@ -0,0 +1,32 @@ +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(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 (4 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 !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)' + 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..c49a88e2e3493 --- /dev/null +++ b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.js @@ -0,0 +1,34 @@ +//// [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 !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] +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 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 new file mode 100644 index 0000000000000..4646982fb3f7a --- /dev/null +++ b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.symbols @@ -0,0 +1,57 @@ +//// [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 !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)) + +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..6165c34ddf9d8 --- /dev/null +++ b/tests/baselines/reference/confusingExclamationBeforeInAndInstanceOf.types @@ -0,0 +1,131 @@ +//// [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 !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 +> : ^^^ + +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..66227cab625cd --- /dev/null +++ b/tests/cases/compiler/confusingExclamationBeforeInAndInstanceOf.ts @@ -0,0 +1,16 @@ +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 !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