Skip to content

Commit b857d53

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
Re-work collection literal strict-raw-type to strict-inference
Bug: #36445 #33749 Change-Id: I127d671840bfcfe3857225932164fa4098963715 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99085 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 229e00d commit b857d53

File tree

5 files changed

+111
-91
lines changed

5 files changed

+111
-91
lines changed

pkg/analyzer/lib/error/error.dart

+1
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ const List<ErrorCode> errorCodeValues = const [
296296
HintCode.FILE_IMPORT_INSIDE_LIB_REFERENCES_FILE_OUTSIDE,
297297
HintCode.FILE_IMPORT_OUTSIDE_LIB_REFERENCES_FILE_INSIDE,
298298
HintCode.IMPORT_DEFERRED_LIBRARY_WITH_LOAD_FUNCTION,
299+
HintCode.INFERENCE_FAILURE_ON_COLLECTION_LITERAL,
299300
HintCode.INFERENCE_FAILURE_ON_UNINITIALIZED_VARIABLE,
300301
HintCode.INVALID_FACTORY_ANNOTATION,
301302
HintCode.INVALID_FACTORY_METHOD_DECL,

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

+17-8
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,15 @@ class HintCode extends ErrorCode {
177177
correction: "Try changing the import to not be deferred, or "
178178
"rename the function in the imported library.");
179179

180+
/**
181+
* When "strict-inference" is enabled, collection literal types must be
182+
* inferred via the context type, or have type arguments.
183+
*/
184+
static const HintCode INFERENCE_FAILURE_ON_COLLECTION_LITERAL = HintCode(
185+
'INFERENCE_FAILURE_ON_COLLECTION_LITERAL',
186+
"The type argument(s) of '{0}' cannot be inferred.",
187+
correction: "Use explicit type argument(s) for '{0}'.");
188+
180189
/**
181190
* When "strict-inference" in enabled, uninitialized variables must be
182191
* declared with a specific type.
@@ -188,14 +197,6 @@ class HintCode extends ErrorCode {
188197
"initializer.",
189198
correction: "Try specifying the type of the variable.");
190199

191-
/**
192-
* When "strict-raw-types" is enabled, raw types must be inferred via the
193-
* context type, or have type arguments other than dynamic.
194-
*/
195-
static const HintCode STRICT_RAW_TYPE = HintCode('STRICT_RAW_TYPE',
196-
"The generic type '{0}' should have explicit type arguments but doesn't.",
197-
correction: "Use explicit type arguments for '{0}'.");
198-
199200
/**
200201
* This hint is generated anywhere a @factory annotation is associated with
201202
* anything other than a method.
@@ -628,6 +629,14 @@ class HintCode extends ErrorCode {
628629
"but this code is required to be able to run on earlier versions.",
629630
correction: "Try updating the SDK constraints.");
630631

632+
/**
633+
* When "strict-raw-types" is enabled, raw types must be inferred via the
634+
* context type, or have type arguments.
635+
*/
636+
static const HintCode STRICT_RAW_TYPE = HintCode('STRICT_RAW_TYPE',
637+
"The generic type '{0}' should have explicit type arguments but doesn't.",
638+
correction: "Use explicit type arguments for '{0}'.");
639+
631640
/**
632641
* This hint is generated anywhere where a `@sealed` class or mixin is used as
633642
* a super-type of a class.

pkg/analyzer/lib/src/generated/error_verifier.dart

+67-59
Original file line numberDiff line numberDiff line change
@@ -994,7 +994,7 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
994994
_checkTypeArgumentCount(typeArguments, 1,
995995
StaticTypeWarningCode.EXPECTED_ONE_LIST_TYPE_ARGUMENTS);
996996
}
997-
_checkForRawTypedLiteral(node);
997+
_checkForInferenceFailureOnCollectionLiteral(node);
998998
_checkForImplicitDynamicTypedLiteral(node);
999999
_checkForListElementTypeNotAssignable(node);
10001000

@@ -1201,7 +1201,7 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
12011201
_checkTypeArgumentCount(typeArguments, 2,
12021202
StaticTypeWarningCode.EXPECTED_TWO_MAP_TYPE_ARGUMENTS);
12031203
}
1204-
_checkForRawTypedLiteral(node);
1204+
_checkForInferenceFailureOnCollectionLiteral(node);
12051205
_checkForImplicitDynamicTypedLiteral(node);
12061206
_checkForMapTypeNotAssignable(node);
12071207
_checkForNonConstMapAsExpressionStatement3(node);
@@ -1217,7 +1217,7 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
12171217
_checkTypeArgumentCount(typeArguments, 1,
12181218
StaticTypeWarningCode.EXPECTED_ONE_SET_TYPE_ARGUMENTS);
12191219
}
1220-
_checkForRawTypedLiteral(node);
1220+
_checkForInferenceFailureOnCollectionLiteral(node);
12211221
_checkForImplicitDynamicTypedLiteral(node);
12221222
_checkForSetElementTypeNotAssignable3(node);
12231223
}
@@ -3666,6 +3666,25 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
36663666
[directive.uri.stringValue]);
36673667
}
36683668

3669+
/// Checks a collection literal for an inference failure, and reports the
3670+
/// appropriate error if [AnalysisOptionsImpl.strictInference] is set.
3671+
///
3672+
/// This checks if [node] does not have explicit or inferred type arguments.
3673+
/// When that happens, it reports a
3674+
/// HintCode.INFERENCE_FAILURE_ON_COLLECTION_LITERAL error.
3675+
void _checkForInferenceFailureOnCollectionLiteral(TypedLiteral node) {
3676+
if (!_options.strictInference || node == null) return;
3677+
if (node.typeArguments != null) {
3678+
// Type has explicit type arguments.
3679+
return;
3680+
}
3681+
var type = node.staticType;
3682+
if (_isMissingTypeArguments(node, type, type.element, node)) {
3683+
_errorReporter.reportErrorForNode(
3684+
HintCode.INFERENCE_FAILURE_ON_COLLECTION_LITERAL, node, [type.name]);
3685+
}
3686+
}
3687+
36693688
/**
36703689
* Check that the given [typeReference] is not a type reference and that then
36713690
* the [name] is reference to an instance member.
@@ -4692,60 +4711,6 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
46924711
CompileTimeErrorCode.PRIVATE_OPTIONAL_PARAMETER, parameter);
46934712
}
46944713

4695-
/// Similar to [_checkForRawTypeName] but for list/map/set literals.
4696-
void _checkForRawTypedLiteral(TypedLiteral node) {
4697-
if (!_options.strictRawTypes || node == null) return;
4698-
if (node.typeArguments != null) {
4699-
// Type has explicit type arguments.
4700-
return;
4701-
}
4702-
var type = node.staticType;
4703-
return _checkForRawTypeErrors(node, type, type.element, node);
4704-
}
4705-
4706-
/// Given a [node] without type arguments that refers to [element], issues
4707-
/// an error if [type] is a generic type, and the type arguments were not
4708-
/// supplied from inference or a non-dynamic default instantiation.
4709-
///
4710-
/// This function is used by other node-specific raw type checking functions
4711-
/// (for example [_checkForRawTypeName]), and should only be called when
4712-
/// we already know [AnalysisOptionsImpl.strictRawTypes] is true and [node]
4713-
/// has no explicit `typeArguments`.
4714-
///
4715-
/// [inferenceContextNode] is the node that has the downwards context type,
4716-
/// if any. For example an [InstanceCreationExpression].
4717-
///
4718-
/// The raw type error [HintCode.STRICT_RAW_TYPE] will *not* be reported when
4719-
/// any of the following are true:
4720-
///
4721-
/// - [inferenceContextNode] has an inference context type that does not
4722-
/// contain `?`
4723-
/// - [type] does not have any `dynamic` type arguments.
4724-
/// - the element is marked with `@optionalTypeArgs` from "package:meta".
4725-
void _checkForRawTypeErrors(AstNode node, DartType type, Element element,
4726-
Expression inferenceContextNode) {
4727-
assert(_options.strictRawTypes);
4728-
// Check if this type has type arguments and at least one is dynamic.
4729-
// If so, we may need to issue a strict-raw-types error.
4730-
if (type is ParameterizedType &&
4731-
type.typeArguments.any((t) => t.isDynamic)) {
4732-
// If we have an inference context node, check if the type was inferred
4733-
// from it. Some cases will not have a context type, such as the type
4734-
// annotation `List` in `List list;`
4735-
if (inferenceContextNode != null) {
4736-
var contextType = InferenceContext.getContext(inferenceContextNode);
4737-
if (contextType != null && UnknownInferredType.isKnown(contextType)) {
4738-
// Type was inferred from downwards context: not an error.
4739-
return;
4740-
}
4741-
}
4742-
if (element.hasOptionalTypeArgs) {
4743-
return;
4744-
}
4745-
_errorReporter.reportErrorForNode(HintCode.STRICT_RAW_TYPE, node, [type]);
4746-
}
4747-
}
4748-
47494714
/// Checks a type annotation for a raw generic type, and reports the
47504715
/// appropriate error if [AnalysisOptionsImpl.strictRawTypes] is set.
47514716
///
@@ -4766,8 +4731,11 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
47664731
inferenceContextNode = grandparent;
47674732
}
47684733
}
4769-
return _checkForRawTypeErrors(
4770-
node, node.type, node.name.staticElement, inferenceContextNode);
4734+
if (_isMissingTypeArguments(
4735+
node, node.type, node.name.staticElement, inferenceContextNode)) {
4736+
_errorReporter
4737+
.reportErrorForNode(HintCode.STRICT_RAW_TYPE, node, [node.type]);
4738+
}
47714739
}
47724740

47734741
/**
@@ -6111,6 +6079,46 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
61116079
return false;
61126080
}
61136081

6082+
/// Given a [node] without type arguments that refers to [element], issues
6083+
/// an error if [type] is a generic type, and the type arguments were not
6084+
/// supplied from inference or a non-dynamic default instantiation.
6085+
///
6086+
/// This function is used by other node-specific type checking functions, and
6087+
/// should only be called when [node] has no explicit `typeArguments`.
6088+
///
6089+
/// [inferenceContextNode] is the node that has the downwards context type,
6090+
/// if any. For example an [InstanceCreationExpression].
6091+
///
6092+
/// This function will return false if any of the following are true:
6093+
///
6094+
/// - [inferenceContextNode] has an inference context type that does not
6095+
/// contain `?`
6096+
/// - [type] does not have any `dynamic` type arguments.
6097+
/// - the element is marked with `@optionalTypeArgs` from "package:meta".
6098+
bool _isMissingTypeArguments(AstNode node, DartType type, Element element,
6099+
Expression inferenceContextNode) {
6100+
// Check if this type has type arguments and at least one is dynamic.
6101+
// If so, we may need to issue a strict-raw-types error.
6102+
if (type is ParameterizedType &&
6103+
type.typeArguments.any((t) => t.isDynamic)) {
6104+
// If we have an inference context node, check if the type was inferred
6105+
// from it. Some cases will not have a context type, such as the type
6106+
// annotation `List` in `List list;`
6107+
if (inferenceContextNode != null) {
6108+
var contextType = InferenceContext.getContext(inferenceContextNode);
6109+
if (contextType != null && UnknownInferredType.isKnown(contextType)) {
6110+
// Type was inferred from downwards context: not an error.
6111+
return false;
6112+
}
6113+
}
6114+
if (element.hasOptionalTypeArgs) {
6115+
return false;
6116+
}
6117+
return true;
6118+
}
6119+
return false;
6120+
}
6121+
61146122
/**
61156123
* Return `true` if the given 'this' [expression] is in a valid context.
61166124
*/

pkg/analyzer/test/src/task/strong/checker_test.dart

+19-22
Original file line numberDiff line numberDiff line change
@@ -3649,6 +3649,25 @@ class Child extends Base {
36493649
''');
36503650
}
36513651

3652+
test_strictInference_collectionLiterals() async {
3653+
addFile(r'''
3654+
main() {
3655+
var emptyList = /*info:INFERENCE_FAILURE_ON_COLLECTION_LITERAL*/[];
3656+
var emptyMap = /*info:INFERENCE_FAILURE_ON_COLLECTION_LITERAL*/{};
3657+
3658+
var upwardsInfersDynamicList = /*info:INFERENCE_FAILURE_ON_COLLECTION_LITERAL*/[42 as dynamic];
3659+
var upwardsInfersDynamicSet = /*info:INFERENCE_FAILURE_ON_COLLECTION_LITERAL*/{42 as dynamic};
3660+
3661+
3662+
dynamic d;
3663+
var upwardsInfersDynamicMap1 = /*info:INFERENCE_FAILURE_ON_COLLECTION_LITERAL*/{d: 2};
3664+
var upwardsInfersDynamicMap2 = /*info:INFERENCE_FAILURE_ON_COLLECTION_LITERAL*/{4: d};
3665+
var upwardsInfersDynamicMap3 = /*info:INFERENCE_FAILURE_ON_COLLECTION_LITERAL*/{d: d};
3666+
}
3667+
''');
3668+
await check(strictInference: true);
3669+
}
3670+
36523671
test_strictRawTypes_classes() async {
36533672
addFile(r'''
36543673
class C<T> {
@@ -3702,11 +3721,8 @@ main() {
37023721
var upwardsInferNonDynamicIsOK = [42];
37033722
var explicitDynamicIsOK = <dynamic>[42];
37043723
3705-
var rawList = /*info:STRICT_RAW_TYPE*/[];
37063724
var rawListOfLists = </*info:STRICT_RAW_TYPE*/List>[];
37073725
/*info:STRICT_RAW_TYPE*/List rawListFromType = [];
3708-
3709-
var upwardsInfersDynamic = /*info:STRICT_RAW_TYPE*/[42 as dynamic];
37103726
}
37113727
37123728
{
@@ -3716,11 +3732,8 @@ main() {
37163732
var upwardsInferNonDynamicIsOK = [42];
37173733
var explicitDynamicIsOK = <dynamic>[42];
37183734
3719-
var rawList = /*info:STRICT_RAW_TYPE*/[];
37203735
var rawListOfLists = </*info:STRICT_RAW_TYPE*/List>[];
37213736
/*info:STRICT_RAW_TYPE*/List rawListFromType = [];
3722-
3723-
var upwardsInfersDynamic = /*info:STRICT_RAW_TYPE*/[42 as dynamic];
37243737
}
37253738
37263739
{
@@ -3732,8 +3745,6 @@ main() {
37323745
37333746
var rawSetOfSets = </*info:STRICT_RAW_TYPE*/Set>{};
37343747
/*info:STRICT_RAW_TYPE*/Set rawSetFromType = {};
3735-
3736-
var upwardsInfersDynamic = /*info:STRICT_RAW_TYPE*/{42 as dynamic};
37373748
}
37383749
37393750
{
@@ -3748,11 +3759,6 @@ main() {
37483759
37493760
var rawMapOfMaps = </*info:STRICT_RAW_TYPE*/Map>{};
37503761
/*info:STRICT_RAW_TYPE*/Map rawMapFromType = {};
3751-
3752-
dynamic d;
3753-
var upwardsInfersDynamic1 = /*info:STRICT_RAW_TYPE*/{d: 2};
3754-
var upwardsInfersDynamic2 = /*info:STRICT_RAW_TYPE*/{4: d};
3755-
var upwardsInfersDynamic3 = /*info:STRICT_RAW_TYPE*/{d: d};
37563762
}
37573763
37583764
{
@@ -3815,15 +3821,6 @@ main() {
38153821
await check(strictRawTypes: true);
38163822
}
38173823

3818-
test_strictRawTypes_emptyMap() async {
3819-
addFile('''
3820-
main() {
3821-
var rawMap = /*info:STRICT_RAW_TYPE*/{};
3822-
}
3823-
''');
3824-
await check(strictRawTypes: true);
3825-
}
3826-
38273824
test_strictRawTypes_typedefs() async {
38283825
addFile(r'''
38293826
typedef T F1<T>(T _);

pkg/analyzer/test/src/task/strong/strong_test_helper.dart

+7-2
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ class AbstractStrongTest with ResourceProviderMixin {
280280
Future<CompilationUnit> check(
281281
{bool implicitCasts: true,
282282
bool implicitDynamic: true,
283+
bool strictInference: false,
283284
bool strictRawTypes: false}) async {
284285
_checkCalled = true;
285286

@@ -290,6 +291,7 @@ class AbstractStrongTest with ResourceProviderMixin {
290291
analysisOptions.strongModeHints = true;
291292
analysisOptions.implicitCasts = implicitCasts;
292293
analysisOptions.implicitDynamic = implicitDynamic;
294+
analysisOptions.strictInference = strictInference;
293295
analysisOptions.strictRawTypes = strictRawTypes;
294296
analysisOptions.enabledExperiments = enabledExperiments;
295297

@@ -330,9 +332,11 @@ class AbstractStrongTest with ResourceProviderMixin {
330332
code == TodoCode.TODO) {
331333
return false;
332334
}
333-
if (strictRawTypes) {
334-
// When testing strict-raw-types, ignore anything else.
335+
if (strictInference || strictRawTypes) {
336+
// When testing strict-inference or strict-raw-types, ignore anything
337+
// else.
335338
return code.errorSeverity.ordinal > ErrorSeverity.INFO.ordinal ||
339+
code == HintCode.INFERENCE_FAILURE_ON_COLLECTION_LITERAL ||
336340
code == HintCode.STRICT_RAW_TYPE;
337341
}
338342
return true;
@@ -343,6 +347,7 @@ class AbstractStrongTest with ResourceProviderMixin {
343347
LibraryElement mainLibrary =
344348
resolutionMap.elementDeclaredByCompilationUnit(mainUnit).library;
345349
Set<LibraryElement> allLibraries = _reachableLibraries(mainLibrary);
350+
346351
for (LibraryElement library in allLibraries) {
347352
for (CompilationUnitElement unit in library.units) {
348353
var source = unit.source;

0 commit comments

Comments
 (0)