Skip to content

Commit fd8607c

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
Implement inference_failure_on_function_invocation checks
The "inference failure" checks implemented here are long overdue. They are part of the strict-inference spec [1]. I think I caught most function invocation cases. All of the work done to determine which error to report and whether @optionalTypeArgs is annotated is done _after_ the check for whether strict-inference is enabled, so this should have no effect on code which does not opt in to that mode. [1] https://github.com/dart-lang/language/blob/master/resources/type-system/strict-inference.md#function-call Bug: #33749 and Change-Id: Ic1d4321fb289acb118e0dbddd48ff917ad39d69a #45371 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/201321 Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]> Reviewed-by: Phil Quitslund <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent 6de639b commit fd8607c

File tree

7 files changed

+405
-5
lines changed

7 files changed

+405
-5
lines changed

pkg/analyzer/lib/error/error.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,9 @@ const List<ErrorCode> errorCodeValues = [
519519
HintCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION,
520520
HintCode.IMPORT_OF_LEGACY_LIBRARY_INTO_NULL_SAFE,
521521
HintCode.INFERENCE_FAILURE_ON_COLLECTION_LITERAL,
522+
HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION,
522523
HintCode.INFERENCE_FAILURE_ON_FUNCTION_RETURN_TYPE,
524+
HintCode.INFERENCE_FAILURE_ON_GENERIC_INVOCATION,
523525
HintCode.INFERENCE_FAILURE_ON_INSTANCE_CREATION,
524526
HintCode.INFERENCE_FAILURE_ON_UNINITIALIZED_VARIABLE,
525527
HintCode.INFERENCE_FAILURE_ON_UNTYPED_PARAMETER,

pkg/analyzer/lib/src/dart/analysis/context_builder.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ class ContextBuilderImpl implements ContextBuilder {
100100
// AnalysisDriver reports results into streams.
101101
// We need to drain these streams to avoid memory leak.
102102
if (drainStreams) {
103-
driver.results.drain();
104-
driver.exceptions.drain();
103+
driver.results.drain<void>();
104+
driver.exceptions.drain<void>();
105105
}
106106

107107
DriverBasedAnalysisContext context =

pkg/analyzer/lib/src/dart/element/generic_inferrer.dart

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@
55
import 'dart:math' as math;
66

77
import 'package:analyzer/dart/ast/ast.dart'
8-
show Annotation, AstNode, ConstructorName;
8+
show
9+
Annotation,
10+
AsExpression,
11+
AstNode,
12+
ConstructorName,
13+
Expression,
14+
InvocationExpression,
15+
SimpleIdentifier;
916
import 'package:analyzer/dart/element/element.dart';
1017
import 'package:analyzer/dart/element/type.dart';
1118
import 'package:analyzer/error/listener.dart' show ErrorReporter;
@@ -488,6 +495,13 @@ class GenericInferrer {
488495
if (errorReporter == null || errorNode == null) {
489496
return;
490497
}
498+
if (errorNode.parent is InvocationExpression &&
499+
errorNode.parent?.parent is AsExpression) {
500+
// Casts via `as` do not play a part in downward inference. We allow an
501+
// exception when inference has "failed" but the return value is
502+
// immediately cast with `as`.
503+
return;
504+
}
491505
if (errorNode is ConstructorName &&
492506
!(errorNode.type.type as InterfaceType).element.hasOptionalTypeArgs) {
493507
String constructorName = errorNode.name == null
@@ -511,9 +525,42 @@ class GenericInferrer {
511525
[constructorName]);
512526
}
513527
}
528+
} else if (errorNode is SimpleIdentifier) {
529+
var element = errorNode.staticElement;
530+
if (element != null) {
531+
if (element is VariableElement) {
532+
// For variable elements, we check their type and possible alias type.
533+
var type = element.type;
534+
var typeElement = type.element;
535+
if (typeElement != null && typeElement.hasOptionalTypeArgs) {
536+
return;
537+
}
538+
var typeAliasElement = type.aliasElement;
539+
if (typeAliasElement != null &&
540+
typeAliasElement.hasOptionalTypeArgs) {
541+
return;
542+
}
543+
}
544+
if (!element.hasOptionalTypeArgs) {
545+
errorReporter.reportErrorForNode(
546+
HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION,
547+
errorNode,
548+
[errorNode.name]);
549+
return;
550+
}
551+
}
552+
} else if (errorNode is Expression) {
553+
var type = errorNode.staticType;
554+
if (type != null) {
555+
var typeDisplayString = type.getDisplayString(
556+
withNullability: _typeSystem.isNonNullableByDefault);
557+
errorReporter.reportErrorForNode(
558+
HintCode.INFERENCE_FAILURE_ON_GENERIC_INVOCATION,
559+
errorNode,
560+
[typeDisplayString]);
561+
return;
562+
}
514563
}
515-
// TODO(srawlins): More inference failure cases, like functions, and
516-
// function expressions.
517564
}
518565

519566
/// If in a legacy library, return the legacy version of the [type].

pkg/analyzer/lib/src/dart/error/hint_codes.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,15 @@ class HintCode extends AnalyzerErrorCode {
759759
"The type argument(s) of '{0}' can't be inferred.",
760760
correction: "Use explicit type argument(s) for '{0}'.");
761761

762+
/**
763+
* When "strict-inference" is enabled, types in function invocations must be
764+
* inferred via the context type, or have type arguments.
765+
*/
766+
static const HintCode INFERENCE_FAILURE_ON_FUNCTION_INVOCATION = HintCode(
767+
'INFERENCE_FAILURE_ON_FUNCTION_INVOCATION',
768+
"The type argument(s) of the function '{0}' can't be inferred.",
769+
correction: "Use explicit type argument(s) for '{0}'.");
770+
762771
/**
763772
* When "strict-inference" is enabled, recursive local functions, top-level
764773
* functions, methods, and function-typed function parameters must all
@@ -771,6 +780,16 @@ class HintCode extends AnalyzerErrorCode {
771780
"The return type of '{0}' cannot be inferred.",
772781
correction: "Declare the return type of '{0}'.");
773782

783+
/**
784+
* When "strict-inference" is enabled, types in function invocations must be
785+
* inferred via the context type, or have type arguments.
786+
*/
787+
static const HintCode INFERENCE_FAILURE_ON_GENERIC_INVOCATION = HintCode(
788+
'INFERENCE_FAILURE_ON_GENERIC_INVOCATION',
789+
"The type argument(s) of the generic function type '{0}' can't be "
790+
"inferred.",
791+
correction: "Use explicit type argument(s) for '{0}'.");
792+
774793
/**
775794
* When "strict-inference" is enabled, types in instance creation
776795
* (constructor calls) must be inferred via the context type, or have type
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/src/error/codes.dart';
6+
import 'package:test_reflective_loader/test_reflective_loader.dart';
7+
8+
import '../dart/resolution/context_collection_resolution.dart';
9+
10+
main() {
11+
defineReflectiveSuite(() {
12+
defineReflectiveTests(InferenceFailureOnFunctionInvocationTest);
13+
});
14+
}
15+
16+
/// Tests of HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION with the
17+
/// "strict-inference" static analysis option.
18+
@reflectiveTest
19+
class InferenceFailureOnFunctionInvocationTest
20+
extends PubPackageResolutionTest {
21+
@override
22+
void setUp() {
23+
super.setUp();
24+
writeTestPackageAnalysisOptionsFile(
25+
AnalysisOptionsFileConfig(
26+
strictInference: true,
27+
),
28+
);
29+
writeTestPackageConfigWithMeta();
30+
}
31+
32+
test_functionType_noInference() async {
33+
await assertErrorsInCode('''
34+
void f(void Function<T>() m) {
35+
m();
36+
}
37+
''', [
38+
error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 33, 1),
39+
]);
40+
}
41+
42+
test_functionType_notGeneric() async {
43+
await assertNoErrorsInCode('''
44+
void f(void Function() m) {
45+
m();
46+
}
47+
''');
48+
}
49+
50+
test_functionType_optionalTypeArgs() async {
51+
await assertNoErrorsInCode('''
52+
import 'package:meta/meta.dart';
53+
void f(@optionalTypeArgs void Function<T>() m) {
54+
m();
55+
}
56+
''');
57+
}
58+
59+
test_genericFunctionExpression_explicitTypeArg() async {
60+
await assertNoErrorsInCode('''
61+
void f(void Function<T>()? m, void Function<T>() n) {
62+
(m ?? n)<int>();
63+
}
64+
''');
65+
}
66+
67+
test_genericMethod_downwardsInference() async {
68+
await assertNoErrorsInCode('''
69+
abstract class C {
70+
T m<T>();
71+
}
72+
73+
int f(C c) {
74+
return c.m();
75+
}
76+
''');
77+
}
78+
79+
test_genericMethod_explicitTypeArgs() async {
80+
await assertNoErrorsInCode('''
81+
abstract class C {
82+
void m<T>();
83+
}
84+
85+
void f(C c) {
86+
c.m<int>();
87+
}
88+
''');
89+
}
90+
91+
test_genericMethod_immediatelyCast() async {
92+
await assertNoErrorsInCode('''
93+
abstract class C {
94+
T m<T>();
95+
}
96+
97+
void f(C c) {
98+
c.m() as int;
99+
}
100+
''');
101+
}
102+
103+
test_genericMethod_noInference() async {
104+
await assertErrorsInCode('''
105+
abstract class C {
106+
void m<T>();
107+
}
108+
109+
void f(C c) {
110+
c.m();
111+
}
112+
''', [
113+
error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 55, 1),
114+
]);
115+
}
116+
117+
test_genericMethod_optionalTypeArgs() async {
118+
await assertNoErrorsInCode('''
119+
import 'package:meta/meta.dart';
120+
abstract class C {
121+
@optionalTypeArgs
122+
void m<T>();
123+
}
124+
125+
void f(C c) {
126+
c.m();
127+
}
128+
''');
129+
}
130+
131+
test_genericMethod_upwardsInference() async {
132+
await assertNoErrorsInCode('''
133+
abstract class C {
134+
void m<T>(T a);
135+
}
136+
137+
void f(C c) {
138+
c.m(7);
139+
}
140+
''');
141+
}
142+
143+
test_genericStaticMethod_noInference() async {
144+
await assertErrorsInCode('''
145+
class C {
146+
static void m<T>() {}
147+
}
148+
149+
void f() {
150+
C.m();
151+
}
152+
''', [
153+
error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 52, 1),
154+
]);
155+
}
156+
157+
test_genericTypedef_noInference() async {
158+
await assertErrorsInCode('''
159+
typedef Fn = void Function<T>();
160+
void g(Fn fn) {
161+
fn();
162+
}
163+
''', [
164+
error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 51, 2),
165+
]);
166+
}
167+
168+
test_genericTypedef_optionalTypeArgs() async {
169+
await assertNoErrorsInCode('''
170+
import 'package:meta/meta.dart';
171+
@optionalTypeArgs
172+
typedef Fn = void Function<T>();
173+
void g(Fn fn) {
174+
fn();
175+
}
176+
''');
177+
}
178+
179+
test_localFunction_noInference() async {
180+
await assertErrorsInCode('''
181+
void f() {
182+
void g<T>() {}
183+
g();
184+
}
185+
''', [
186+
error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 30, 1),
187+
]);
188+
}
189+
190+
test_localFunctionVariable_noInference() async {
191+
await assertErrorsInCode('''
192+
void f() {
193+
var m = <T>() {};
194+
m();
195+
}
196+
''', [
197+
error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 33, 1),
198+
]);
199+
}
200+
201+
test_nonGenericMethod() async {
202+
await assertNoErrorsInCode('''
203+
abstract class C {
204+
void m();
205+
}
206+
207+
void f(C c) {
208+
c.m();
209+
}
210+
''');
211+
}
212+
213+
test_topLevelFunction_noInference() async {
214+
await assertErrorsInCode('''
215+
void f<T>() {}
216+
217+
void g() {
218+
f();
219+
}
220+
''', [
221+
error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 29, 1),
222+
]);
223+
}
224+
225+
test_topLevelFunction_withImportPrefix_noInference() async {
226+
newFile('$testPackageLibPath/a.dart', content: '''
227+
void f<T>() {}
228+
''');
229+
await assertErrorsInCode('''
230+
import 'a.dart' as a;
231+
void g() {
232+
a.f();
233+
}
234+
''', [
235+
error(HintCode.INFERENCE_FAILURE_ON_FUNCTION_INVOCATION, 37, 1),
236+
]);
237+
}
238+
239+
test_topLevelFunction_withImportPrefix_optionalTypeArgs() async {
240+
newFile('$testPackageLibPath/a.dart', content: '''
241+
import 'package:meta/meta.dart';
242+
@optionalTypeArgs
243+
void f<T>() {}
244+
''');
245+
await assertNoErrorsInCode('''
246+
import 'a.dart' as a;
247+
void g() {
248+
a.f();
249+
}
250+
''');
251+
}
252+
}

0 commit comments

Comments
 (0)