diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 77ec82ffef760..53290a7d0cda3 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -280,7 +280,7 @@ namespace ts { /** * Test for whether a single line comment's text contains a directive. */ - const commentDirectiveRegExSingleLine = /^\s*\/\/\/?\s*@(ts-expect-error|ts-ignore)/; + const commentDirectiveRegExSingleLine = /^\s*\/\/\/?\s*@(ts-expect-error|ts-ignore(?=($|[^-]))|ts-ignore-enable|ts-ignore-disable)/; /** * Test for whether a multi-line comment's last line contains a directive. @@ -951,7 +951,63 @@ namespace ts { isIdentifier: () => token === SyntaxKind.Identifier || token > SyntaxKind.LastReservedWord, isReservedWord: () => token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord, isUnterminated: () => (tokenFlags & TokenFlags.Unterminated) !== 0, - getCommentDirectives: () => commentDirectives, + getCommentDirectives: () => { + // Or we could not handle the condition here, rather than like createCommentDirectivesMap. + // Put here, we could reduce couple + // Put there, we could reduce `magic` + let isIgnoreStart = false; + if (commentDirectives) { + // It is obvious the code time could be improved, but I leave it here for reading and understanding. + + const removedIndex = new Set(); + commentDirectives.forEach((d, index) => { + switch (d.type) { + case CommentDirectiveType.IgnoreStart: + if (isIgnoreStart) removedIndex.add(index); + else isIgnoreStart = true; + break; + case CommentDirectiveType.IgnoreEnd: + if (!isIgnoreStart) removedIndex.add(index); + else isIgnoreStart = false; + break; + default: + // this line make expect-error not work. + if (isIgnoreStart) removedIndex.add(index); + break; + } + }); + const cleanInput = commentDirectives.filter((_, i) => !removedIndex.has(i)); + + let curRegionEnd = end; + for (let i = cleanInput.length - 1; i >= 0; i--) { + if (cleanInput[i].type === CommentDirectiveType.IgnoreEnd) { + curRegionEnd = cleanInput[i].range.pos - 1; + } + if (cleanInput[i].type === CommentDirectiveType.IgnoreStart) { + const added = []; + let tmpPos = cleanInput[i].range.pos; + let tmpLineStart = tmpPos; + let lastOne; + while (tmpPos < curRegionEnd) { + if (isLineBreak(text.charCodeAt(tmpPos))) { + if (tmpLineStart !== tmpPos && lastOne) { + added.push(lastOne); + } + lastOne = { + range: { pos: tmpLineStart, end: tmpPos }, + type: CommentDirectiveType.Ignore, + }; + tmpLineStart = tmpPos + 1; + } + tmpPos++; + } + cleanInput.splice(i, curRegionEnd === end ? 1 : 2, ...added); + } + } + return cleanInput; + } + return commentDirectives; + }, getNumericLiteralFlags: () => tokenFlags & TokenFlags.NumericLiteralFlags, getTokenFlags: () => tokenFlags, reScanGreaterToken, @@ -2180,6 +2236,12 @@ namespace ts { case "ts-ignore": return CommentDirectiveType.Ignore; + + case "ts-ignore-enable": + return CommentDirectiveType.IgnoreStart; + + case "ts-ignore-disable": + return CommentDirectiveType.IgnoreEnd; } return undefined; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d9468bd51234e..fed9235ea0553 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3482,6 +3482,10 @@ namespace ts { export const enum CommentDirectiveType { ExpectError, Ignore, + + // this pair would be replaced by CommentDirectiveType.Ignore when anything would be returned from scanner. + IgnoreStart, + IgnoreEnd } /*@internal*/ diff --git a/tests/baselines/reference/ts-ignore-enable.errors.txt b/tests/baselines/reference/ts-ignore-enable.errors.txt new file mode 100644 index 0000000000000..fd51434ad917c --- /dev/null +++ b/tests/baselines/reference/ts-ignore-enable.errors.txt @@ -0,0 +1,22 @@ +tests/cases/conformance/directives/ts-ignore-enable.ts(11,5): error TS2322: Type 'string' is not assignable to type 'number'. + + +==== tests/cases/conformance/directives/ts-ignore-enable.ts (1 errors) ==== + // @ts-ignore-enable with additional commenting + var invalidCommentedFancy: number = 'nope'; + + // @ts-expect-error this line would be overwritten by @ts-ignore-enable + var validCommentedFancy: string = 'nope'; + + var invalidCommentedPlain: number = 'nope'; + + // @ts-ignore-disable + + var invalidPlain: number = 'nope'; + ~~~~~~~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. + + var validCommentedPlain: string = 'nope'; + + var validPlain: string = 'nope'; + \ No newline at end of file diff --git a/tests/baselines/reference/ts-ignore-enable.js b/tests/baselines/reference/ts-ignore-enable.js new file mode 100644 index 0000000000000..aa9f556248b79 --- /dev/null +++ b/tests/baselines/reference/ts-ignore-enable.js @@ -0,0 +1,28 @@ +//// [ts-ignore-enable.ts] +// @ts-ignore-enable with additional commenting +var invalidCommentedFancy: number = 'nope'; + +// @ts-expect-error this line would be overwritten by @ts-ignore-enable +var validCommentedFancy: string = 'nope'; + +var invalidCommentedPlain: number = 'nope'; + +// @ts-ignore-disable + +var invalidPlain: number = 'nope'; + +var validCommentedPlain: string = 'nope'; + +var validPlain: string = 'nope'; + + +//// [ts-ignore-enable.js] +// @ts-ignore-enable with additional commenting +var invalidCommentedFancy = 'nope'; +// @ts-expect-error this line would be overwritten by @ts-ignore-enable +var validCommentedFancy = 'nope'; +var invalidCommentedPlain = 'nope'; +// @ts-ignore-disable +var invalidPlain = 'nope'; +var validCommentedPlain = 'nope'; +var validPlain = 'nope'; diff --git a/tests/baselines/reference/ts-ignore-enable.symbols b/tests/baselines/reference/ts-ignore-enable.symbols new file mode 100644 index 0000000000000..a351f14c9f3a5 --- /dev/null +++ b/tests/baselines/reference/ts-ignore-enable.symbols @@ -0,0 +1,23 @@ +=== tests/cases/conformance/directives/ts-ignore-enable.ts === +// @ts-ignore-enable with additional commenting +var invalidCommentedFancy: number = 'nope'; +>invalidCommentedFancy : Symbol(invalidCommentedFancy, Decl(ts-ignore-enable.ts, 1, 3)) + +// @ts-expect-error this line would be overwritten by @ts-ignore-enable +var validCommentedFancy: string = 'nope'; +>validCommentedFancy : Symbol(validCommentedFancy, Decl(ts-ignore-enable.ts, 4, 3)) + +var invalidCommentedPlain: number = 'nope'; +>invalidCommentedPlain : Symbol(invalidCommentedPlain, Decl(ts-ignore-enable.ts, 6, 3)) + +// @ts-ignore-disable + +var invalidPlain: number = 'nope'; +>invalidPlain : Symbol(invalidPlain, Decl(ts-ignore-enable.ts, 10, 3)) + +var validCommentedPlain: string = 'nope'; +>validCommentedPlain : Symbol(validCommentedPlain, Decl(ts-ignore-enable.ts, 12, 3)) + +var validPlain: string = 'nope'; +>validPlain : Symbol(validPlain, Decl(ts-ignore-enable.ts, 14, 3)) + diff --git a/tests/baselines/reference/ts-ignore-enable.types b/tests/baselines/reference/ts-ignore-enable.types new file mode 100644 index 0000000000000..2800c1f2599ed --- /dev/null +++ b/tests/baselines/reference/ts-ignore-enable.types @@ -0,0 +1,29 @@ +=== tests/cases/conformance/directives/ts-ignore-enable.ts === +// @ts-ignore-enable with additional commenting +var invalidCommentedFancy: number = 'nope'; +>invalidCommentedFancy : number +>'nope' : "nope" + +// @ts-expect-error this line would be overwritten by @ts-ignore-enable +var validCommentedFancy: string = 'nope'; +>validCommentedFancy : string +>'nope' : "nope" + +var invalidCommentedPlain: number = 'nope'; +>invalidCommentedPlain : number +>'nope' : "nope" + +// @ts-ignore-disable + +var invalidPlain: number = 'nope'; +>invalidPlain : number +>'nope' : "nope" + +var validCommentedPlain: string = 'nope'; +>validCommentedPlain : string +>'nope' : "nope" + +var validPlain: string = 'nope'; +>validPlain : string +>'nope' : "nope" + diff --git a/tests/cases/conformance/directives/ts-ignore-enable.ts b/tests/cases/conformance/directives/ts-ignore-enable.ts new file mode 100644 index 0000000000000..5f0018129d6b9 --- /dev/null +++ b/tests/cases/conformance/directives/ts-ignore-enable.ts @@ -0,0 +1,15 @@ +// @ts-ignore-enable with additional commenting +var invalidCommentedFancy: number = 'nope'; + +// @ts-expect-error this line would be overwritten by @ts-ignore-enable +var validCommentedFancy: string = 'nope'; + +var invalidCommentedPlain: number = 'nope'; + +// @ts-ignore-disable + +var invalidPlain: number = 'nope'; + +var validCommentedPlain: string = 'nope'; + +var validPlain: string = 'nope';