diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ee029b7249f..8a1f7dcdffc 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -179,6 +179,33 @@ namespace ts { return node.pos; } + export function getEndLinePosition(line: number, sourceFile: SourceFile): number { + Debug.assert(line >= 0); + const lineStarts = getLineStarts(sourceFile); + + const lineIndex = line; + const sourceText = sourceFile.text; + if (lineIndex + 1 === lineStarts.length) { + // last line - return EOF + return sourceText.length - 1; + } + else { + // current line start + const start = lineStarts[lineIndex]; + // take the start position of the next line - 1 = it should be some line break + let pos = lineStarts[lineIndex + 1] - 1; + Debug.assert(isLineBreak(sourceText.charCodeAt(pos))); + // walk backwards skipping line breaks, stop the the beginning of current line. + // i.e: + // + // $ <- end of line for this position should match the start position + while (start <= pos && isLineBreak(sourceText.charCodeAt(pos))) { + pos--; + } + return pos; + } + } + // Returns true if this node is missing from the actual source code. A 'missing' node is different // from 'undefined/defined'. When a node is undefined (which can happen for optional nodes // in the tree), it is definitely missing. However, a node may be defined, but still be @@ -364,6 +391,20 @@ namespace ts { return createTextSpanFromBounds(start, scanner.getTextPos()); } + function getErrorSpanForArrowFunction(sourceFile: SourceFile, node: ArrowFunction): TextSpan { + const pos = skipTrivia(sourceFile.text, node.pos); + if (node.body && node.body.kind === SyntaxKind.Block) { + const { line: startLine } = getLineAndCharacterOfPosition(sourceFile, node.body.pos); + const { line: endLine } = getLineAndCharacterOfPosition(sourceFile, node.body.end); + if (startLine < endLine) { + // The arrow function spans multiple lines, + // make the error span be the first line, inclusive. + return createTextSpan(pos, getEndLinePosition(startLine, sourceFile) - pos + 1); + } + } + return createTextSpanFromBounds(pos, node.end); + } + export function getErrorSpanForNode(sourceFile: SourceFile, node: Node): TextSpan { let errorNode = node; switch (node.kind) { @@ -392,6 +433,8 @@ namespace ts { case SyntaxKind.TypeAliasDeclaration: errorNode = (node).name; break; + case SyntaxKind.ArrowFunction: + return getErrorSpanForArrowFunction(sourceFile, node); } if (errorNode === undefined) { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 8006fea2c07..4d29cd99e12 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -6,32 +6,6 @@ namespace ts { list: Node; } - export function getEndLinePosition(line: number, sourceFile: SourceFile): number { - Debug.assert(line >= 0); - let lineStarts = sourceFile.getLineStarts(); - - let lineIndex = line; - if (lineIndex + 1 === lineStarts.length) { - // last line - return EOF - return sourceFile.text.length - 1; - } - else { - // current line start - let start = lineStarts[lineIndex]; - // take the start position of the next line -1 = it should be some line break - let pos = lineStarts[lineIndex + 1] - 1; - Debug.assert(isLineBreak(sourceFile.text.charCodeAt(pos))); - // walk backwards skipping line breaks, stop the the beginning of current line. - // i.e: - // - // $ <- end of line for this position should match the start position - while (start <= pos && isLineBreak(sourceFile.text.charCodeAt(pos))) { - pos--; - } - return pos; - } - } - export function getLineStartPositionForPosition(position: number, sourceFile: SourceFile): number { let lineStarts = sourceFile.getLineStarts(); let line = sourceFile.getLineAndCharacterOfPosition(position).line; diff --git a/tests/baselines/reference/arrowFunctionErrorSpan.errors.txt b/tests/baselines/reference/arrowFunctionErrorSpan.errors.txt new file mode 100644 index 00000000000..e258f56d5ed --- /dev/null +++ b/tests/baselines/reference/arrowFunctionErrorSpan.errors.txt @@ -0,0 +1,111 @@ +tests/cases/compiler/arrowFunctionErrorSpan.ts(4,3): error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. + Type 'void' is not assignable to type 'number'. +tests/cases/compiler/arrowFunctionErrorSpan.ts(7,3): error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. + Type 'void' is not assignable to type 'number'. +tests/cases/compiler/arrowFunctionErrorSpan.ts(12,3): error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. + Type 'void' is not assignable to type 'number'. +tests/cases/compiler/arrowFunctionErrorSpan.ts(17,3): error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. + Type 'void' is not assignable to type 'number'. +tests/cases/compiler/arrowFunctionErrorSpan.ts(18,5): error TS1200: Line terminator not permitted before arrow. +tests/cases/compiler/arrowFunctionErrorSpan.ts(21,3): error TS2345: Argument of type '(a: any, b: any, c: any, d: any) => void' is not assignable to parameter of type '() => number'. +tests/cases/compiler/arrowFunctionErrorSpan.ts(28,7): error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. + Type 'void' is not assignable to type 'number'. +tests/cases/compiler/arrowFunctionErrorSpan.ts(32,7): error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. + Type 'void' is not assignable to type 'number'. +tests/cases/compiler/arrowFunctionErrorSpan.ts(36,7): error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. + Type 'void' is not assignable to type 'number'. +tests/cases/compiler/arrowFunctionErrorSpan.ts(43,5): error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. + Type 'void' is not assignable to type 'number'. +tests/cases/compiler/arrowFunctionErrorSpan.ts(52,3): error TS2345: Argument of type '(_: any) => number' is not assignable to parameter of type '() => number'. + + +==== tests/cases/compiler/arrowFunctionErrorSpan.ts (11 errors) ==== + function f(a: () => number) { } + + // oneliner + f(() => { }); + ~~~~~~~~~ +!!! error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. +!!! error TS2345: Type 'void' is not assignable to type 'number'. + + // multiline, body + f(() => { + ~~~~~~~ +!!! error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. +!!! error TS2345: Type 'void' is not assignable to type 'number'. + + }); + + // multiline 2, body + f(() => { + ~~~~~~~ +!!! error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. +!!! error TS2345: Type 'void' is not assignable to type 'number'. + + }); + + // multiline 3, arrow on a new line + f(() + ~~ + => { }); + ~~~~~~~~~~ +!!! error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. +!!! error TS2345: Type 'void' is not assignable to type 'number'. + ~~ +!!! error TS1200: Line terminator not permitted before arrow. + + // multiline 4, arguments + f((a, + ~~~ + b, + ~~~~~~ + c, + ~~~~~~ + d) => { }); + ~~~~~~~~~~~~~ +!!! error TS2345: Argument of type '(a: any, b: any, c: any, d: any) => void' is not assignable to parameter of type '() => number'. + + // single line with a comment + f(/* + */() => { }); + ~~~~~~~~~ +!!! error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. +!!! error TS2345: Type 'void' is not assignable to type 'number'. + + // multi line with a comment + f(/* + */() => { }); + ~~~~~~~~~ +!!! error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. +!!! error TS2345: Type 'void' is not assignable to type 'number'. + + // multi line with a comment 2 + f(/* + */() => { + ~~~~~~~~ +!!! error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. +!!! error TS2345: Type 'void' is not assignable to type 'number'. + + }); + + // multi line with a comment 3 + f( // comment 1 + // comment 2 + () => + ~~~~~ +!!! error TS2345: Argument of type '() => void' is not assignable to parameter of type '() => number'. +!!! error TS2345: Type 'void' is not assignable to type 'number'. + // comment 3 + { + // comment 4 + } + // comment 5 + ); + + // body is not a block + f(_ => 1 + + ~~~~~~~~ + 2); + ~~~~~ +!!! error TS2345: Argument of type '(_: any) => number' is not assignable to parameter of type '() => number'. + \ No newline at end of file diff --git a/tests/baselines/reference/arrowFunctionErrorSpan.js b/tests/baselines/reference/arrowFunctionErrorSpan.js new file mode 100644 index 00000000000..2557113e89b --- /dev/null +++ b/tests/baselines/reference/arrowFunctionErrorSpan.js @@ -0,0 +1,89 @@ +//// [arrowFunctionErrorSpan.ts] +function f(a: () => number) { } + +// oneliner +f(() => { }); + +// multiline, body +f(() => { + +}); + +// multiline 2, body +f(() => { + +}); + +// multiline 3, arrow on a new line +f(() + => { }); + +// multiline 4, arguments +f((a, + b, + c, + d) => { }); + +// single line with a comment +f(/* + */() => { }); + +// multi line with a comment +f(/* + */() => { }); + +// multi line with a comment 2 +f(/* + */() => { + + }); + +// multi line with a comment 3 +f( // comment 1 + // comment 2 + () => + // comment 3 + { + // comment 4 + } + // comment 5 +); + +// body is not a block +f(_ => 1 + + 2); + + +//// [arrowFunctionErrorSpan.js] +function f(a) { } +// oneliner +f(function () { }); +// multiline, body +f(function () { +}); +// multiline 2, body +f(function () { +}); +// multiline 3, arrow on a new line +f(function () { }); +// multiline 4, arguments +f(function (a, b, c, d) { }); +// single line with a comment +f(/* + */ function () { }); +// multi line with a comment +f(/* + */ function () { }); +// multi line with a comment 2 +f(/* + */ function () { +}); +// multi line with a comment 3 +f(// comment 1 +// comment 2 +function () { + // comment 4 +}); +// body is not a block +f(function (_) { return 1 + + 2; }); diff --git a/tests/baselines/reference/functionImplementationErrors.errors.txt b/tests/baselines/reference/functionImplementationErrors.errors.txt index 86ecb6a6057..dc5ad5a2747 100644 --- a/tests/baselines/reference/functionImplementationErrors.errors.txt +++ b/tests/baselines/reference/functionImplementationErrors.errors.txt @@ -30,13 +30,10 @@ tests/cases/conformance/functions/functionImplementationErrors.ts(70,11): error }; var f3 = () => { ~~~~~~~ - return ''; - ~~~~~~~~~~~~~~ - return 3; - ~~~~~~~~~~~~~ - }; - ~ !!! error TS2354: No best common type exists among return expressions. + return ''; + return 3; + }; // FunctionExpression with no return type annotation with return branch of number[] and other of string[] var f4 = function () { @@ -94,13 +91,10 @@ tests/cases/conformance/functions/functionImplementationErrors.ts(70,11): error }; var f10 = () => { ~~~~~~~ - return new Derived1(); - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - return new Derived2(); - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - }; - ~ !!! error TS2354: No best common type exists among return expressions. + return new Derived1(); + return new Derived2(); + }; function f11() { ~~~ !!! error TS2354: No best common type exists among return expressions. @@ -115,11 +109,8 @@ tests/cases/conformance/functions/functionImplementationErrors.ts(70,11): error }; var f13 = () => { ~~~~~~~ - return new Base(); - ~~~~~~~~~~~~~~~~~~~~~~ - return new AnotherClass(); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - }; - ~ !!! error TS2354: No best common type exists among return expressions. + return new Base(); + return new AnotherClass(); + }; \ No newline at end of file diff --git a/tests/baselines/reference/overloadresolutionWithConstraintCheckingDeferred.errors.txt b/tests/baselines/reference/overloadresolutionWithConstraintCheckingDeferred.errors.txt index 5ca1e78de58..3c06fd43275 100644 --- a/tests/baselines/reference/overloadresolutionWithConstraintCheckingDeferred.errors.txt +++ b/tests/baselines/reference/overloadresolutionWithConstraintCheckingDeferred.errors.txt @@ -39,16 +39,13 @@ tests/cases/compiler/overloadresolutionWithConstraintCheckingDeferred.ts(19,14): var result3: string = foo(x => { // x has type D ~~~~~~~~~~~~~~~~~~~~~~ - var y: G; // error that D does not satisfy constraint, y is of type G, entire call to foo is an error - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ~~~~~~~~ -!!! error TS2344: Type 'D' does not satisfy the constraint 'A'. - return y; - ~~~~~~~~~~~~~ - }); - ~ !!! error TS2345: Argument of type '(x: D) => G' is not assignable to parameter of type '(x: B) => any'. !!! error TS2345: Types of parameters 'x' and 'x' are incompatible. !!! error TS2345: Type 'B' is not assignable to type 'D'. !!! error TS2345: Property 'q' is missing in type 'B'. + var y: G; // error that D does not satisfy constraint, y is of type G, entire call to foo is an error + ~~~~~~~~ +!!! error TS2344: Type 'D' does not satisfy the constraint 'A'. + return y; + }); \ No newline at end of file diff --git a/tests/baselines/reference/undeclaredModuleError.errors.txt b/tests/baselines/reference/undeclaredModuleError.errors.txt index 74e36318595..4a8248bfd6c 100644 --- a/tests/baselines/reference/undeclaredModuleError.errors.txt +++ b/tests/baselines/reference/undeclaredModuleError.errors.txt @@ -16,10 +16,9 @@ tests/cases/compiler/undeclaredModuleError.ts(11,41): error TS2304: Cannot find fs.readFile(originalFilePath, () => { readdir(covFileDir, () => { ~~~~~~~ - } , (error: Error, files: {}[]) => { - ~~~~~~~~~ !!! error TS2345: Argument of type '() => void' is not assignable to parameter of type '(stat: any, name: string) => boolean'. !!! error TS2345: Type 'void' is not assignable to type 'boolean'. + } , (error: Error, files: {}[]) => { files.forEach((file) => { var fullPath = join(IDoNotExist); ~~~~~~~~~~~ diff --git a/tests/cases/compiler/arrowFunctionErrorSpan.ts b/tests/cases/compiler/arrowFunctionErrorSpan.ts new file mode 100644 index 00000000000..4c7fff88a93 --- /dev/null +++ b/tests/cases/compiler/arrowFunctionErrorSpan.ts @@ -0,0 +1,53 @@ +function f(a: () => number) { } + +// oneliner +f(() => { }); + +// multiline, body +f(() => { + +}); + +// multiline 2, body +f(() => { + +}); + +// multiline 3, arrow on a new line +f(() + => { }); + +// multiline 4, arguments +f((a, + b, + c, + d) => { }); + +// single line with a comment +f(/* + */() => { }); + +// multi line with a comment +f(/* + */() => { }); + +// multi line with a comment 2 +f(/* + */() => { + + }); + +// multi line with a comment 3 +f( // comment 1 + // comment 2 + () => + // comment 3 + { + // comment 4 + } + // comment 5 +); + +// body is not a block +f(_ => 1 + + 2);