|
1 | 1 | namespace ts {
|
2 |
| - const ignoreDiagnosticCommentRegEx = /(^\s*$)|(^\s*\/\/\/?\s*(@ts-ignore)?)/; |
3 |
| - |
4 | 2 | export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string | undefined {
|
5 | 3 | return forEachAncestorDirectory(searchPath, ancestor => {
|
6 | 4 | const fileName = combinePaths(ancestor, configName);
|
@@ -1650,17 +1648,16 @@ namespace ts {
|
1650 | 1648 | const fileProcessingDiagnosticsInFile = fileProcessingDiagnostics.getDiagnostics(sourceFile.fileName);
|
1651 | 1649 | const programDiagnosticsInFile = programDiagnostics.getDiagnostics(sourceFile.fileName);
|
1652 | 1650 |
|
1653 |
| - let diagnostics: Diagnostic[] | undefined; |
1654 |
| - for (const diags of [fileProcessingDiagnosticsInFile, programDiagnosticsInFile]) { |
1655 |
| - if (diags) { |
1656 |
| - for (const diag of diags) { |
1657 |
| - if (shouldReportDiagnostic(diag)) { |
1658 |
| - diagnostics = append(diagnostics, diag); |
1659 |
| - } |
1660 |
| - } |
1661 |
| - } |
| 1651 | + return getMergedProgramDiagnostics(sourceFile, fileProcessingDiagnosticsInFile, programDiagnosticsInFile); |
| 1652 | + } |
| 1653 | + |
| 1654 | + function getMergedProgramDiagnostics(sourceFile: SourceFile, ...allDiagnostics: (readonly Diagnostic[] | undefined)[]) { |
| 1655 | + const flatDiagnostics = flatten(allDiagnostics); |
| 1656 | + if (!sourceFile.commentDirectives?.length) { |
| 1657 | + return flatDiagnostics; |
1662 | 1658 | }
|
1663 |
| - return diagnostics || emptyArray; |
| 1659 | + |
| 1660 | + return getDiagnosticsPastDirectives(sourceFile, sourceFile.commentDirectives, flatDiagnostics).diagnostics; |
1664 | 1661 | }
|
1665 | 1662 |
|
1666 | 1663 | function getDeclarationDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): readonly DiagnosticWithLocation[] {
|
@@ -1738,49 +1735,68 @@ namespace ts {
|
1738 | 1735 | const bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray;
|
1739 | 1736 | const checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray;
|
1740 | 1737 |
|
1741 |
| - let diagnostics: Diagnostic[] | undefined; |
1742 |
| - for (const diags of [bindDiagnostics, checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined]) { |
1743 |
| - if (diags) { |
1744 |
| - for (const diag of diags) { |
1745 |
| - if (shouldReportDiagnostic(diag)) { |
1746 |
| - diagnostics = append(diagnostics, diag); |
1747 |
| - } |
1748 |
| - } |
1749 |
| - } |
1750 |
| - } |
1751 |
| - return diagnostics || emptyArray; |
| 1738 | + return getMergedBindAndCheckDiagnostics(sourceFile, bindDiagnostics, checkDiagnostics, isCheckJs ? sourceFile.jsDocDiagnostics : undefined); |
1752 | 1739 | });
|
1753 | 1740 | }
|
1754 | 1741 |
|
| 1742 | + function getMergedBindAndCheckDiagnostics(sourceFile: SourceFile, ...allDiagnostics: (readonly Diagnostic[] | undefined)[]) { |
| 1743 | + const flatDiagnostics = flatten(allDiagnostics); |
| 1744 | + if (!sourceFile.commentDirectives?.length) { |
| 1745 | + return flatDiagnostics; |
| 1746 | + } |
| 1747 | + |
| 1748 | + const { diagnostics, directives } = getDiagnosticsPastDirectives(sourceFile, sourceFile.commentDirectives, flatDiagnostics); |
| 1749 | + |
| 1750 | + for (const errorExpectation of directives.getUnusedExpectations()) { |
| 1751 | + diagnostics.push(createDiagnosticForRange(sourceFile, errorExpectation.range, Diagnostics.Unused_ts_expect_error_directive)); |
| 1752 | + } |
| 1753 | + |
| 1754 | + return diagnostics; |
| 1755 | + } |
| 1756 | + |
| 1757 | + function getDiagnosticsPastDirectives(sourceFile: SourceFile, commentDirectives: CommentDirective[], flatDiagnostics: Diagnostic[]) { |
| 1758 | + // Diagnostics are only reported if there is no comment directive preceding them |
| 1759 | + // This will modify the directives map by marking "used" ones with a corresponding diagnostic |
| 1760 | + const directives = createCommentDirectivesMap(sourceFile, commentDirectives); |
| 1761 | + const diagnostics = flatDiagnostics.filter(diagnostic => markPrecedingCommentDirectiveLine(diagnostic, directives) === -1); |
| 1762 | + |
| 1763 | + return { diagnostics, directives }; |
| 1764 | + } |
| 1765 | + |
1755 | 1766 | function getSuggestionDiagnostics(sourceFile: SourceFile, cancellationToken: CancellationToken): readonly DiagnosticWithLocation[] {
|
1756 | 1767 | return runWithCancellationToken(() => {
|
1757 | 1768 | return getDiagnosticsProducingTypeChecker().getSuggestionDiagnostics(sourceFile, cancellationToken);
|
1758 | 1769 | });
|
1759 | 1770 | }
|
1760 | 1771 |
|
1761 | 1772 | /**
|
1762 |
| - * Skip errors if previous line start with '// @ts-ignore' comment, not counting non-empty non-comment lines |
| 1773 | + * @returns The line index marked as preceding the diagnostic, or -1 if none was. |
1763 | 1774 | */
|
1764 |
| - function shouldReportDiagnostic(diagnostic: Diagnostic) { |
| 1775 | + function markPrecedingCommentDirectiveLine(diagnostic: Diagnostic, directives: CommentDirectivesMap) { |
1765 | 1776 | const { file, start } = diagnostic;
|
1766 |
| - if (file) { |
1767 |
| - const lineStarts = getLineStarts(file); |
1768 |
| - let { line } = computeLineAndCharacterOfPosition(lineStarts, start!); // TODO: GH#18217 |
1769 |
| - while (line > 0) { |
1770 |
| - const previousLineText = file.text.slice(lineStarts[line - 1], lineStarts[line]); |
1771 |
| - const result = ignoreDiagnosticCommentRegEx.exec(previousLineText); |
1772 |
| - if (!result) { |
1773 |
| - // non-empty line |
1774 |
| - return true; |
1775 |
| - } |
1776 |
| - if (result[3]) { |
1777 |
| - // @ts-ignore |
1778 |
| - return false; |
1779 |
| - } |
1780 |
| - line--; |
| 1777 | + if (!file) { |
| 1778 | + return -1; |
| 1779 | + } |
| 1780 | + |
| 1781 | + // Start out with the line just before the text |
| 1782 | + const lineStarts = getLineStarts(file); |
| 1783 | + let line = computeLineAndCharacterOfPosition(lineStarts, start!).line - 1; // TODO: GH#18217 |
| 1784 | + while (line >= 0) { |
| 1785 | + // As soon as that line is known to have a comment directive, use that |
| 1786 | + if (directives.markUsed(line)) { |
| 1787 | + return line; |
1781 | 1788 | }
|
| 1789 | + |
| 1790 | + // Stop searching if the line is not empty and not a comment |
| 1791 | + const lineText = file.text.slice(lineStarts[line - 1], lineStarts[line]).trim(); |
| 1792 | + if (lineText !== "" && !/^(\s*)\/\/(.*)$/.test(lineText)) { |
| 1793 | + return -1; |
| 1794 | + } |
| 1795 | + |
| 1796 | + line--; |
1782 | 1797 | }
|
1783 |
| - return true; |
| 1798 | + |
| 1799 | + return -1; |
1784 | 1800 | }
|
1785 | 1801 |
|
1786 | 1802 | function getJSSyntacticDiagnosticsForFile(sourceFile: SourceFile): DiagnosticWithLocation[] {
|
|
0 commit comments