From 4648d6aeb24aad5d10713c4a93d5fea4050d7c7d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 27 Sep 2019 15:05:13 -0700 Subject: [PATCH] Revise error messages + related spans + no errors on never-returning functions --- src/compiler/checker.ts | 35 +++++++++++++++++----------- src/compiler/diagnosticMessages.json | 4 ++-- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9546463c009..3bc8d88f4fa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17191,30 +17191,38 @@ namespace ts { getEffectiveTypeAnnotationNode(declaration as VariableDeclaration | ParameterDeclaration | PropertyDeclaration | PropertySignature)); } - function getExplicitTypeOfSymbol(symbol: Symbol) { - return symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.ValueModule) || - symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property) && isDeclarationWithExplicitTypeAnnotation(symbol.valueDeclaration) ? - getTypeOfSymbol(symbol) : undefined; + function getExplicitTypeOfSymbol(symbol: Symbol, diagnostic?: Diagnostic) { + if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.ValueModule)) { + return getTypeOfSymbol(symbol); + } + if (symbol.flags & (SymbolFlags.Variable | SymbolFlags.Property)) { + if (isDeclarationWithExplicitTypeAnnotation(symbol.valueDeclaration)) { + return getTypeOfSymbol(symbol); + } + if (diagnostic && symbol.valueDeclaration) { + addRelatedInfo(diagnostic, createDiagnosticForNode(symbol.valueDeclaration, Diagnostics._0_is_declared_here, symbolToString(symbol))); + } + } } // We require the dotted function name in an assertion expression to be comprised of identifiers // that reference function, method, class or value module symbols; or variable, property or // parameter symbols with declarations that have explicit type annotations. Such references are // resolvable with no possibility of triggering circularities in control flow analysis. - function getTypeOfDottedName(node: Expression): Type | undefined { + function getTypeOfDottedName(node: Expression, diagnostic: Diagnostic | undefined): Type | undefined { if (!(node.flags & NodeFlags.InWithStatement)) { switch (node.kind) { case SyntaxKind.Identifier: const symbol = getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(node)); - return getExplicitTypeOfSymbol(symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol); + return getExplicitTypeOfSymbol(symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol, diagnostic); case SyntaxKind.ThisKeyword: return getExplicitThisType(node); case SyntaxKind.PropertyAccessExpression: - const type = getTypeOfDottedName((node).expression); + const type = getTypeOfDottedName((node).expression, diagnostic); const prop = type && getPropertyOfType(type, (node).name.escapedText); - return prop && getExplicitTypeOfSymbol(prop); + return prop && getExplicitTypeOfSymbol(prop, diagnostic); case SyntaxKind.ParenthesizedExpression: - return getTypeOfDottedName((node).expression); + return getTypeOfDottedName((node).expression, diagnostic); } } } @@ -17227,7 +17235,7 @@ namespace ts { // expressions are potential type predicate function calls. In order to avoid triggering // circularities in control flow analysis, we use getTypeOfDottedName when resolving the call // target expression of an assertion. - const funcType = node.parent.kind === SyntaxKind.ExpressionStatement ? getTypeOfDottedName(node.expression) : + const funcType = node.parent.kind === SyntaxKind.ExpressionStatement ? getTypeOfDottedName(node.expression, /*diagnostic*/ undefined) : node.expression.kind !== SyntaxKind.SuperKeyword ? checkNonNullExpression(node.expression) : undefined; const signatures = getSignaturesOfType(funcType && getApparentType(funcType) || unknownType, SignatureKind.Call); @@ -23417,12 +23425,13 @@ namespace ts { return getESSymbolLikeTypeForNode(walkUpParenthesizedExpressions(node.parent)); } if (node.kind === SyntaxKind.CallExpression && node.parent.kind === SyntaxKind.ExpressionStatement && - returnType.flags & (TypeFlags.Void | TypeFlags.Never) && hasTypePredicateOrNeverReturnType(signature)) { + returnType.flags & TypeFlags.Void && getTypePredicateOfSignature(signature)) { if (!isDottedName(node.expression)) { - error(node.expression, Diagnostics.Control_flow_effects_of_calls_to_assertion_and_never_returning_functions_are_reflected_only_when_the_function_expression_is_an_identifier_or_qualified_name); + error(node.expression, Diagnostics.Assertions_require_the_call_target_to_be_an_identifier_or_qualified_name); } else if (!getEffectsSignature(node)) { - error(node.expression, Diagnostics.Control_flow_effects_of_calls_to_assertion_and_never_returning_functions_are_reflected_only_when_every_variable_or_property_referenced_in_the_function_expression_is_declared_with_an_explicit_type_annotation); + const diagnostic = error(node.expression, Diagnostics.Assertions_require_every_name_in_the_call_target_to_be_declared_with_an_explicit_type_annotation); + getTypeOfDottedName(node.expression, diagnostic); } } let jsAssignmentType: Type | undefined; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 056b02f7cb3..ff8df4b5b95 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2726,11 +2726,11 @@ "category": "Error", "code": 2774 }, - "Control flow effects of calls to assertion and never-returning functions are reflected only when every variable or property referenced in the function expression is declared with an explicit type annotation.": { + "Assertions require every name in the call target to be declared with an explicit type annotation.": { "category": "Error", "code": 2775 }, - "Control flow effects of calls to assertion and never-returning functions are reflected only when the function expression is an identifier or qualified-name.": { + "Assertions require the call target to be an identifier or qualified-name.": { "category": "Error", "code": 2776 },