diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart index 37074c3177f4..bdc7ebfdaab1 100644 --- a/pkg/analyzer/lib/error/error.dart +++ b/pkg/analyzer/lib/error/error.dart @@ -519,7 +519,9 @@ const List errorCodeValues = [ HintCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION, HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE, HintCode.INFERENCE_FAILURE_ON_COLLECTION_LITERAL, + HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, HintCode.INFERENCE_FAILURE_ON_FUNCTION_RETURN_TYPE, + HintCode.INFERENCE_FAILURE_ON_GENERIC_INVOCATION, HintCode.INFERENCE_FAILURE_ON_INSTANCE_CREATION, HintCode.INFERENCE_FAILURE_ON_UNINITIALIZED_VARIABLE, HintCode.INFERENCE_FAILURE_ON_UNTYPED_PARAMETER, diff --git a/pkg/analyzer/lib/src/dart/analysis/context_builder.dart b/pkg/analyzer/lib/src/dart/analysis/context_builder.dart index 9bec8666d7eb..0ca758d30085 100644 --- a/pkg/analyzer/lib/src/dart/analysis/context_builder.dart +++ b/pkg/analyzer/lib/src/dart/analysis/context_builder.dart @@ -100,8 +100,8 @@ class ContextBuilderImpl implements ContextBuilder { // AnalysisDriver reports results into streams. // We need to drain these streams to avoid memory leak. if (drainStreams) { - driver.results.drain(); - driver.exceptions.drain(); + driver.results.drain(); + driver.exceptions.drain(); } DriverBasedAnalysisContext context = diff --git a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart index cc4db0c47416..0de0c579dbd1 100644 --- a/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart +++ b/pkg/analyzer/lib/src/dart/element/generic_inferrer.dart @@ -5,7 +5,14 @@ import 'dart:math' as math; import 'package:analyzer/dart/ast/ast.dart' - show Annotation, AstNode, ConstructorName; + show + Annotation, + AsExpression, + AstNode, + ConstructorName, + Expression, + InvocationExpression, + SimpleIdentifier; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/error/listener.dart' show ErrorReporter; @@ -488,6 +495,13 @@ class GenericInferrer { if (errorReporter == null || errorNode == null) { return; } + if (errorNode.parent is InvocationExpression && + errorNode.parent?.parent is AsExpression) { + // Casts via `as` do not play a part in downward inference. We allow an + // exception when inference has "failed" but the return value is + // immediately cast with `as`. + return; + } if (errorNode is ConstructorName && !(errorNode.type.type as InterfaceType).element.hasOptionalTypeArgs) { String constructorName = errorNode.name == null @@ -511,9 +525,42 @@ class GenericInferrer { [constructorName]); } } + } else if (errorNode is SimpleIdentifier) { + var element = errorNode.staticElement; + if (element != null) { + if (element is VariableElement) { + // For variable elements, we check their type and possible alias type. + var type = element.type; + var typeElement = type.element; + if (typeElement != null && typeElement.hasOptionalTypeArgs) { + return; + } + var typeAliasElement = type.aliasElement; + if (typeAliasElement != null && + typeAliasElement.hasOptionalTypeArgs) { + return; + } + } + if (!element.hasOptionalTypeArgs) { + errorReporter.reportErrorForNode( + HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, + errorNode, + [errorNode.name]); + return; + } + } + } else if (errorNode is Expression) { + var type = errorNode.staticType; + if (type != null) { + var typeDisplayString = type.getDisplayString( + withNullability: _typeSystem.isNonNullableByDefault); + errorReporter.reportErrorForNode( + HintCode.INFERENCE_FAILURE_ON_GENERIC_INVOCATION, + errorNode, + [typeDisplayString]); + return; + } } - // TODO(srawlins): More inference failure cases, like functions, and - // function expressions. } /// If in a legacy library, return the legacy version of the [type]. diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart index cb5d4eb91616..12b9301e7620 100644 --- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart +++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart @@ -759,6 +759,15 @@ class HintCode extends AnalyzerErrorCode { "The type argument(s) of '{0}' can't be inferred.", correction: "Use explicit type argument(s) for '{0}'."); + /** + * When "strict-inference" is enabled, types in function invocations must be + * inferred via the context type, or have type arguments. + */ + static const HintCode INFERENCE_FAILURE_ON_FUNCTION_INVOCATION = HintCode( + 'INFERENCE_FAILURE_ON_FUNCTION_INVOCATION', + "The type argument(s) of the function '{0}' can't be inferred.", + correction: "Use explicit type argument(s) for '{0}'."); + /** * When "strict-inference" is enabled, recursive local functions, top-level * functions, methods, and function-typed function parameters must all @@ -771,6 +780,16 @@ class HintCode extends AnalyzerErrorCode { "The return type of '{0}' cannot be inferred.", correction: "Declare the return type of '{0}'."); + /** + * When "strict-inference" is enabled, types in function invocations must be + * inferred via the context type, or have type arguments. + */ + static const HintCode INFERENCE_FAILURE_ON_GENERIC_INVOCATION = HintCode( + 'INFERENCE_FAILURE_ON_GENERIC_INVOCATION', + "The type argument(s) of the generic function type '{0}' can't be " + "inferred.", + correction: "Use explicit type argument(s) for '{0}'."); + /** * When "strict-inference" is enabled, types in instance creation * (constructor calls) must be inferred via the context type, or have type diff --git a/pkg/analyzer/test/src/diagnostics/inference_failure_on_function_invocation_test.dart b/pkg/analyzer/test/src/diagnostics/inference_failure_on_function_invocation_test.dart new file mode 100644 index 000000000000..e048093321aa --- /dev/null +++ b/pkg/analyzer/test/src/diagnostics/inference_failure_on_function_invocation_test.dart @@ -0,0 +1,252 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:analyzer/src/error/codes.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import '../dart/resolution/context_collection_resolution.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(InferenceFailureOnFunctionInvocationTest); + }); +} + +/// Tests of HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION with the +/// "strict-inference" static analysis option. +@reflectiveTest +class InferenceFailureOnFunctionInvocationTest + extends PubPackageResolutionTest { + @override + void setUp() { + super.setUp(); + writeTestPackageAnalysisOptionsFile( + AnalysisOptionsFileConfig( + strictInference: true, + ), + ); + writeTestPackageConfigWithMeta(); + } + + test_functionType_noInference() async { + await assertErrorsInCode(''' +void f(void Function() m) { + m(); +} +''', [ + error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 33, 1), + ]); + } + + test_functionType_notGeneric() async { + await assertNoErrorsInCode(''' +void f(void Function() m) { + m(); +} +'''); + } + + test_functionType_optionalTypeArgs() async { + await assertNoErrorsInCode(''' +import 'package:meta/meta.dart'; +void f(@optionalTypeArgs void Function() m) { + m(); +} +'''); + } + + test_genericFunctionExpression_explicitTypeArg() async { + await assertNoErrorsInCode(''' +void f(void Function()? m, void Function() n) { + (m ?? n)(); +} +'''); + } + + test_genericMethod_downwardsInference() async { + await assertNoErrorsInCode(''' +abstract class C { + T m(); +} + +int f(C c) { + return c.m(); +} +'''); + } + + test_genericMethod_explicitTypeArgs() async { + await assertNoErrorsInCode(''' +abstract class C { + void m(); +} + +void f(C c) { + c.m(); +} +'''); + } + + test_genericMethod_immediatelyCast() async { + await assertNoErrorsInCode(''' +abstract class C { + T m(); +} + +void f(C c) { + c.m() as int; +} +'''); + } + + test_genericMethod_noInference() async { + await assertErrorsInCode(''' +abstract class C { + void m(); +} + +void f(C c) { + c.m(); +} +''', [ + error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 55, 1), + ]); + } + + test_genericMethod_optionalTypeArgs() async { + await assertNoErrorsInCode(''' +import 'package:meta/meta.dart'; +abstract class C { + @optionalTypeArgs + void m(); +} + +void f(C c) { + c.m(); +} +'''); + } + + test_genericMethod_upwardsInference() async { + await assertNoErrorsInCode(''' +abstract class C { + void m(T a); +} + +void f(C c) { + c.m(7); +} +'''); + } + + test_genericStaticMethod_noInference() async { + await assertErrorsInCode(''' +class C { + static void m() {} +} + +void f() { + C.m(); +} +''', [ + error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 52, 1), + ]); + } + + test_genericTypedef_noInference() async { + await assertErrorsInCode(''' +typedef Fn = void Function(); +void g(Fn fn) { + fn(); +} +''', [ + error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 51, 2), + ]); + } + + test_genericTypedef_optionalTypeArgs() async { + await assertNoErrorsInCode(''' +import 'package:meta/meta.dart'; +@optionalTypeArgs +typedef Fn = void Function(); +void g(Fn fn) { + fn(); +} +'''); + } + + test_localFunction_noInference() async { + await assertErrorsInCode(''' +void f() { + void g() {} + g(); +} +''', [ + error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 30, 1), + ]); + } + + test_localFunctionVariable_noInference() async { + await assertErrorsInCode(''' +void f() { + var m = () {}; + m(); +} +''', [ + error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 33, 1), + ]); + } + + test_nonGenericMethod() async { + await assertNoErrorsInCode(''' +abstract class C { + void m(); +} + +void f(C c) { + c.m(); +} +'''); + } + + test_topLevelFunction_noInference() async { + await assertErrorsInCode(''' +void f() {} + +void g() { + f(); +} +''', [ + error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 29, 1), + ]); + } + + test_topLevelFunction_withImportPrefix_noInference() async { + newFile('$testPackageLibPath/a.dart', content: ''' +void f() {} +'''); + await assertErrorsInCode(''' +import 'a.dart' as a; +void g() { + a.f(); +} +''', [ + error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 37, 1), + ]); + } + + test_topLevelFunction_withImportPrefix_optionalTypeArgs() async { + newFile('$testPackageLibPath/a.dart', content: ''' +import 'package:meta/meta.dart'; +@optionalTypeArgs +void f() {} +'''); + await assertNoErrorsInCode(''' +import 'a.dart' as a; +void g() { + a.f(); +} +'''); + } +} diff --git a/pkg/analyzer/test/src/diagnostics/inference_failure_on_generic_invocation_test.dart b/pkg/analyzer/test/src/diagnostics/inference_failure_on_generic_invocation_test.dart new file mode 100644 index 000000000000..bc644c9c953a --- /dev/null +++ b/pkg/analyzer/test/src/diagnostics/inference_failure_on_generic_invocation_test.dart @@ -0,0 +1,74 @@ +// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:analyzer/src/error/codes.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import '../dart/resolution/context_collection_resolution.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(InferenceFailureOnGenericInvocationTest); + }); +} + +/// Tests of HintCode.INFERENCE_FAILURE_ON_GENERIC_INVOCATION with the +/// "strict-inference" static analysis option. +@reflectiveTest +class InferenceFailureOnGenericInvocationTest extends PubPackageResolutionTest { + @override + void setUp() { + super.setUp(); + writeTestPackageAnalysisOptionsFile( + AnalysisOptionsFileConfig( + strictInference: true, + ), + ); + writeTestPackageConfigWithMeta(); + } + + test_genericFunctionExpression_downwardsInference() async { + await assertNoErrorsInCode(''' +int f(T Function()? m, T Function() n) { + return (m ?? n)(); +} +'''); + } + + test_genericFunctionExpression_explicitTypeArg() async { + await assertNoErrorsInCode(''' +void f(void Function()? m, void Function() n) { + (m ?? n)(); +} +'''); + } + + test_genericFunctionExpression_noInference() async { + await assertErrorsInCode(''' +void f(void Function()? m, void Function() n) { + (m ?? n)(); +} +''', [ + error(HintCode.INFERENCE_FAILURE_ON_GENERIC_INVOCATION, 56, 8), + ]); + } + + test_genericFunctionExpression_upwardsInference() async { + await assertNoErrorsInCode(''' +void f(void Function(T a)? m, void Function(T a) n) { + (m ?? n)(1); +} +'''); + } + + test_genericFunctionExpressionLiteral_noInference() async { + await assertErrorsInCode(''' +void f() { + (() {})(); +} +''', [ + error(HintCode.INFERENCE_FAILURE_ON_GENERIC_INVOCATION, 13, 10), + ]); + } +} diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart index 3cfce381ba1e..715812c1afcb 100644 --- a/pkg/analyzer/test/src/diagnostics/test_all.dart +++ b/pkg/analyzer/test/src/diagnostics/test_all.dart @@ -262,8 +262,12 @@ import 'inconsistent_language_version_override_test.dart' as inconsistent_language_version_override; import 'inference_failure_on_collection_literal_test.dart' as inference_failure_on_collection_literal; +import 'inference_failure_on_function_invocation_test.dart' + as inference_failure_on_function_invocation; import 'inference_failure_on_function_return_type_test.dart' as inference_failure_on_function_return_type; +import 'inference_failure_on_generic_invocation_test.dart' + as inference_failure_on_generic_invocation; import 'inference_failure_on_instance_creation_test.dart' as inference_failure_on_instance_creation; import 'inference_failure_on_uninitialized_variable_test.dart' @@ -871,7 +875,9 @@ main() { inconsistent_inheritance.main(); inconsistent_language_version_override.main(); inference_failure_on_collection_literal.main(); + inference_failure_on_function_invocation.main(); inference_failure_on_function_return_type.main(); + inference_failure_on_generic_invocation.main(); inference_failure_on_instance_creation.main(); inference_failure_on_uninitialized_variable.main(); inference_failure_on_untyped_parameter.main();