From 74536cc6ed7064fa06df9ea109e3180ac7508d9d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 12 Sep 2014 07:28:49 -0700 Subject: [PATCH] Report circular type inference errors with -noImplicitAny --- src/compiler/checker.ts | 24 +++- .../diagnosticInformationMap.generated.ts | 3 + src/compiler/diagnosticMessages.json | 12 ++ src/compiler/emitter.ts | 2 +- src/compiler/parser.ts | 2 +- ...mplicitAnyFromCircularInference.errors.txt | 73 +++++++++++++ .../implicitAnyFromCircularInference.js | 103 ++++++++++++++++++ .../implicitAnyFromCircularInference.ts | 51 +++++++++ 8 files changed, 264 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/implicitAnyFromCircularInference.errors.txt create mode 100644 tests/baselines/reference/implicitAnyFromCircularInference.js create mode 100644 tests/cases/compiler/implicitAnyFromCircularInference.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c4f83f75a5f..fa1ab99f036 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -413,7 +413,7 @@ module ts { } } - function getFullyQualifiedName(symbol: Symbol) { + function getFullyQualifiedName(symbol: Symbol): string { return symbol.parent ? getFullyQualifiedName(symbol.parent) + "." + symbolToString(symbol) : symbolToString(symbol); } @@ -1420,6 +1420,9 @@ module ts { } else if (links.type === resolvingType) { links.type = anyType; + if (compilerOptions.noImplicitAny) { + error(symbol.valueDeclaration, Diagnostics._0_implicitly_has_type_any_because_type_inference_encountered_a_circularity, symbolToString(symbol)); + } } return links.type; } @@ -1475,7 +1478,7 @@ module ts { // Otherwise, fall back to 'any'. else { if (compilerOptions.noImplicitAny) { - error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_type_annotation, symbol.name); + error(setter, Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_type_annotation, symbolToString(symbol)); } type = anyType; @@ -1489,6 +1492,10 @@ module ts { } else if (links.type === resolvingType) { links.type = anyType; + if (compilerOptions.noImplicitAny) { + var getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); + error(getter, Diagnostics._0_implicitly_has_type_any_because_type_inference_encountered_a_circularity, symbolToString(symbol)); + } } } @@ -1552,7 +1559,7 @@ module ts { function hasBaseType(type: InterfaceType, checkBase: InterfaceType) { return check(type); - function check(type: InterfaceType) { + function check(type: InterfaceType): boolean { var target = getTargetType(type); return target === checkBase || forEach(target.baseTypes, check); } @@ -2036,6 +2043,15 @@ module ts { } else if (signature.resolvedReturnType === resolvingType) { signature.resolvedReturnType = anyType; + if (compilerOptions.noImplicitAny) { + var declaration = signature.declaration; + if (declaration.name) { + error(declaration.name, Diagnostics._0_implicitly_has_return_type_any_because_type_inference_encountered_a_circularity, identifierToString(declaration.name)); + } + else { + error(declaration, Diagnostics.Function_implicitly_has_return_type_any_because_type_inference_encountered_a_circularity); + } + } } return signature.resolvedReturnType; } @@ -6587,7 +6603,7 @@ module ts { // Language service support function getNodeAtPosition(sourceFile: SourceFile, position: number): Node { - function findChildAtPosition(parent: Node) { + function findChildAtPosition(parent: Node): Node { var child = forEachChild(parent, node => { if (position >= node.pos && position <= node.end && position >= getTokenPosOfNode(node)) { return findChildAtPosition(node); diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index aa1b793f6ac..cadf64f0140 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -390,6 +390,9 @@ module ts { Object_literal_s_property_0_implicitly_has_an_1_type: { code: 7018, category: DiagnosticCategory.Error, key: "Object literal's property '{0}' implicitly has an '{1}' type." }, Rest_parameter_0_implicitly_has_an_any_type: { code: 7019, category: DiagnosticCategory.Error, key: "Rest parameter '{0}' implicitly has an 'any[]' type." }, Call_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type: { code: 7020, category: DiagnosticCategory.Error, key: "Call signature, which lacks return-type annotation, implicitly has an 'any' return type." }, + _0_implicitly_has_type_any_because_type_inference_encountered_a_circularity: { code: 7021, category: DiagnosticCategory.Error, key: "'{0}' implicitly has type 'any' because type inference encountered a circularity." }, + _0_implicitly_has_return_type_any_because_type_inference_encountered_a_circularity: { code: 7022, category: DiagnosticCategory.Error, key: "'{0}' implicitly has return type 'any' because type inference encountered a circularity." }, + Function_implicitly_has_return_type_any_because_type_inference_encountered_a_circularity: { code: 7023, category: DiagnosticCategory.Error, key: "Function implicitly has return type 'any' because type inference encountered a circularity." }, You_cannot_rename_this_element: { code: 8000, category: DiagnosticCategory.Error, key: "You cannot rename this element." }, }; } \ No newline at end of file diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index eef8da8c857..d30c4e399fc 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1557,6 +1557,18 @@ "category": "Error", "code": 7020 }, + "'{0}' implicitly has type 'any' because type inference encountered a circularity.": { + "category": "Error", + "code": 7021 + }, + "'{0}' implicitly has return type 'any' because type inference encountered a circularity.": { + "category": "Error", + "code": 7022 + }, + "Function implicitly has return type 'any' because type inference encountered a circularity.": { + "category": "Error", + "code": 7023 + }, "You cannot rename this element.": { "category": "Error", "code": 8000 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 6b18ad4c133..317db201f3e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1973,7 +1973,7 @@ module ts { } } - function emitNode(node: Node) { + function emitNode(node: Node): void { if (!node) { return; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index dde11576817..be9047f3568 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3533,7 +3533,7 @@ module ts { return finishNode(node); } - function isDeclaration() { + function isDeclaration(): boolean { switch (token) { case SyntaxKind.VarKeyword: case SyntaxKind.FunctionKeyword: diff --git a/tests/baselines/reference/implicitAnyFromCircularInference.errors.txt b/tests/baselines/reference/implicitAnyFromCircularInference.errors.txt new file mode 100644 index 00000000000..f1d32b750f5 --- /dev/null +++ b/tests/baselines/reference/implicitAnyFromCircularInference.errors.txt @@ -0,0 +1,73 @@ +==== tests/cases/compiler/implicitAnyFromCircularInference.ts (9 errors) ==== + + // Error expected + var a: typeof a; + ~ +!!! 'a' implicitly has type 'any' because type inference encountered a circularity. + + // Error expected on b or c + var b: typeof c; + var c: typeof b; + ~ +!!! 'c' implicitly has type 'any' because type inference encountered a circularity. + + // Error expected + var d: Array; + ~ +!!! 'd' implicitly has type 'any' because type inference encountered a circularity. + + function f() { return f; } + + // Error expected + function g() { return g(); } + ~ +!!! 'g' implicitly has return type 'any' because type inference encountered a circularity. + + // Error expected + var f1 = function () { + ~~~~~~~~~~~~~ + return f1(); + ~~~~~~~~~~~~~~~~ + }; + ~ +!!! Function implicitly has return type 'any' because type inference encountered a circularity. + + // Error expected + var f2 = () => f2(); + ~~~~~~~~~~ +!!! Function implicitly has return type 'any' because type inference encountered a circularity. + + // Error expected + function h() { + ~ +!!! 'h' implicitly has return type 'any' because type inference encountered a circularity. + return foo(); + function foo() { + return h() || "hello"; + } + } + + interface A { + s: string; + } + + function foo(x: A): string { return "abc"; } + + class C { + // Error expected + s = foo(this); + ~~~~~~~~~~~~~~ +!!! 's' implicitly has type 'any' because type inference encountered a circularity. + } + + class D { + // Error expected + get x() { + ~~~~~~~~~ + return this.x; + ~~~~~~~~~~~~~~~~~~~~~~ + } + ~~~~~ +!!! 'x' implicitly has type 'any' because type inference encountered a circularity. + } + \ No newline at end of file diff --git a/tests/baselines/reference/implicitAnyFromCircularInference.js b/tests/baselines/reference/implicitAnyFromCircularInference.js new file mode 100644 index 00000000000..d97b8420ebf --- /dev/null +++ b/tests/baselines/reference/implicitAnyFromCircularInference.js @@ -0,0 +1,103 @@ +//// [implicitAnyFromCircularInference.ts] + +// Error expected +var a: typeof a; + +// Error expected on b or c +var b: typeof c; +var c: typeof b; + +// Error expected +var d: Array; + +function f() { return f; } + +// Error expected +function g() { return g(); } + +// Error expected +var f1 = function () { + return f1(); +}; + +// Error expected +var f2 = () => f2(); + +// Error expected +function h() { + return foo(); + function foo() { + return h() || "hello"; + } +} + +interface A { + s: string; +} + +function foo(x: A): string { return "abc"; } + +class C { + // Error expected + s = foo(this); +} + +class D { + // Error expected + get x() { + return this.x; + } +} + + +//// [implicitAnyFromCircularInference.js] +// Error expected +var a; +// Error expected on b or c +var b; +var c; +// Error expected +var d; +function f() { + return f; +} +// Error expected +function g() { + return g(); +} +// Error expected +var f1 = function () { + return f1(); +}; +// Error expected +var f2 = function () { return f2(); }; +// Error expected +function h() { + return foo(); + function foo() { + return h() || "hello"; + } +} +function foo(x) { + return "abc"; +} +var C = (function () { + function C() { + // Error expected + this.s = foo(this); + } + return C; +})(); +var D = (function () { + function D() { + } + Object.defineProperty(D.prototype, "x", { + // Error expected + get: function () { + return this.x; + }, + enumerable: true, + configurable: true + }); + return D; +})(); diff --git a/tests/cases/compiler/implicitAnyFromCircularInference.ts b/tests/cases/compiler/implicitAnyFromCircularInference.ts new file mode 100644 index 00000000000..a4c8b1ba78b --- /dev/null +++ b/tests/cases/compiler/implicitAnyFromCircularInference.ts @@ -0,0 +1,51 @@ +// @noimplicitany: true +// @target: es5 + +// Error expected +var a: typeof a; + +// Error expected on b or c +var b: typeof c; +var c: typeof b; + +// Error expected +var d: Array; + +function f() { return f; } + +// Error expected +function g() { return g(); } + +// Error expected +var f1 = function () { + return f1(); +}; + +// Error expected +var f2 = () => f2(); + +// Error expected +function h() { + return foo(); + function foo() { + return h() || "hello"; + } +} + +interface A { + s: string; +} + +function foo(x: A): string { return "abc"; } + +class C { + // Error expected + s = foo(this); +} + +class D { + // Error expected + get x() { + return this.x; + } +}