Add hints to check the SDK constraints for both set literals and for the ui-as-code features
Change-Id: I1046e57304d56e6d7f088ea1a8a054318a45ac37
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96837
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index adeb0f9..a5f14e7 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -335,7 +335,13 @@
// }
if (errorCode == HintCode.SDK_VERSION_ASYNC_EXPORTED_FROM_CORE) {
await _addFix_importAsync();
- await _addFix_updateSdkConstraints();
+ await _addFix_updateSdkConstraints('2.1.0');
+ }
+ if (errorCode == HintCode.SDK_VERSION_SET_LITERAL) {
+ await _addFix_updateSdkConstraints('2.2.0');
+ }
+ if (errorCode == HintCode.SDK_VERSION_UI_AS_CODE) {
+ await _addFix_updateSdkConstraints('2.2.2');
}
if (errorCode == HintCode.TYPE_CHECK_IS_NOT_NULL) {
await _addFix_isNotNull();
@@ -3203,16 +3209,6 @@
}
}
- Future<void> _addFix_replaceVarWithDynamic() async {
- // TODO(brianwilkerson) Determine whether this await is necessary.
- await null;
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addSimpleReplacement(range.error(error), 'dynamic');
- });
- _addFixFromBuilder(changeBuilder, DartFixKind.REPLACE_VAR_WITH_DYNAMIC);
- }
-
Future<void> _addFix_replaceNullWithClosure() async {
var nodeToFix;
var parameters = const <ParameterElement>[];
@@ -3246,6 +3242,16 @@
}
}
+ Future<void> _addFix_replaceVarWithDynamic() async {
+ // TODO(brianwilkerson) Determine whether this await is necessary.
+ await null;
+ var changeBuilder = _newDartChangeBuilder();
+ await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addSimpleReplacement(range.error(error), 'dynamic');
+ });
+ _addFixFromBuilder(changeBuilder, DartFixKind.REPLACE_VAR_WITH_DYNAMIC);
+ }
+
Future<void> _addFix_replaceWithConditionalAssignment() async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
@@ -3301,16 +3307,6 @@
}
}
- Future<void> _addFix_replaceWithRethrow() async {
- if (coveredNode is ThrowExpression) {
- var changeBuilder = _newDartChangeBuilder();
- await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
- builder.addSimpleReplacement(range.node(coveredNode), 'rethrow');
- });
- _addFixFromBuilder(changeBuilder, DartFixKind.USE_RETHROW);
- }
- }
-
Future<void> _addFix_replaceWithIdentifier() async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
@@ -3328,6 +3324,16 @@
}
}
+ Future<void> _addFix_replaceWithRethrow() async {
+ if (coveredNode is ThrowExpression) {
+ var changeBuilder = _newDartChangeBuilder();
+ await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) {
+ builder.addSimpleReplacement(range.node(coveredNode), 'rethrow');
+ });
+ _addFixFromBuilder(changeBuilder, DartFixKind.USE_RETHROW);
+ }
+ }
+
Future<void> _addFix_replaceWithTearOff() async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
@@ -3746,7 +3752,7 @@
_addFixFromBuilder(changeBuilder, DartFixKind.ADD_FIELD_FORMAL_PARAMETERS);
}
- Future<void> _addFix_updateSdkConstraints() async {
+ Future<void> _addFix_updateSdkConstraints(String minimumVersion) async {
Context context = resourceProvider.pathContext;
File pubspecFile = null;
Folder folder = resourceProvider.getFolder(context.dirname(file));
@@ -3774,13 +3780,13 @@
length = spaceOffset;
}
if (text == 'any') {
- newText = '^2.1.0';
+ newText = '^$minimumVersion';
} else if (text.startsWith('^')) {
- newText = '^2.1.0';
+ newText = '^$minimumVersion';
} else if (text.startsWith('>=')) {
- newText = '>=2.1.0';
+ newText = '>=$minimumVersion';
} else if (text.startsWith('>')) {
- newText = '>=2.1.0';
+ newText = '>=$minimumVersion';
}
if (newText == null) {
return;
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 8475ef8..202f77f 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -329,6 +329,8 @@
HintCode.OVERRIDE_ON_NON_OVERRIDING_SETTER,
HintCode.PACKAGE_IMPORT_CONTAINS_DOT_DOT,
HintCode.SDK_VERSION_ASYNC_EXPORTED_FROM_CORE,
+ HintCode.SDK_VERSION_SET_LITERAL,
+ HintCode.SDK_VERSION_UI_AS_CODE,
HintCode.STRICT_RAW_TYPE,
HintCode.SUBTYPE_OF_SEALED_CLASS,
HintCode.TYPE_CHECK_IS_NOT_NULL,
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index 39f9dee..950e5ea 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -550,6 +550,26 @@
"Try either importing 'dart:async' or updating the SDK constraints.");
/**
+ * A set literal is being used in code that is expected to run on versions of
+ * the SDK that did not support them.
+ */
+ static const HintCode SDK_VERSION_SET_LITERAL = const HintCode(
+ 'SDK_VERSION_SET_LITERAL',
+ "Set literals were not supported until version 2.2, "
+ "but this code is required to be able to run on earlier versions.",
+ correction: "Try updating the SDK constraints.");
+
+ /**
+ * The for, if or spread element is being used in code that is expected to run
+ * on versions of the SDK that did not support them.
+ */
+ static const HintCode SDK_VERSION_UI_AS_CODE = const HintCode(
+ 'SDK_VERSION_UI_AS_CODE',
+ "The for, if and spread elements were not supported until version 2.2.2, "
+ "but this code is required to be able to run on earlier versions.",
+ correction: "Try updating the SDK constraints.");
+
+ /**
* This hint is generated anywhere where a `@sealed` class or mixin is used as
* a super-type of a class.
*/
diff --git a/pkg/analyzer/lib/src/hint/sdk_constraint_verifier.dart b/pkg/analyzer/lib/src/hint/sdk_constraint_verifier.dart
index cc2231a..b7a9c21 100644
--- a/pkg/analyzer/lib/src/hint/sdk_constraint_verifier.dart
+++ b/pkg/analyzer/lib/src/hint/sdk_constraint_verifier.dart
@@ -27,9 +27,26 @@
final VersionConstraint _versionConstraint;
/// A cached flag indicating whether references to Future and Stream need to
- /// be checked. Use [] to access this field.
+ /// be checked. Use [checkFutureAndStream] to access this field.
bool _checkFutureAndStream;
+ /// A cached flag indicating whether references to set literals need to
+ /// be checked. Use [checkSetLiterals] to access this field.
+ bool _checkSetLiterals;
+
+ /// A flag indicating whether we are visiting code inside a set literal. Used
+ /// to prevent over-reporting uses of set literals.
+ bool _inSetLiteral = false;
+
+ /// A cached flag indicating whether references to the ui-as-code features
+ /// need to be checked. Use [checkUiAsCode] to access this field.
+ bool _checkUiAsCode;
+
+ /// A flag indicating whether we are visiting code inside one of the
+ /// ui-as-code features. Used to prevent over-reporting uses of these
+ /// features.
+ bool _inUiAsCode = false;
+
/// Initialize a newly created verifier to use the given [_errorReporter] to
/// report errors.
SdkConstraintVerifier(this._errorReporter, this._containingLibrary,
@@ -39,16 +56,62 @@
VersionRange get before_2_1_0 =>
new VersionRange(max: Version.parse('2.1.0'), includeMax: false);
+ /// Return a range covering every version up to, but not including, 2.2.0.
+ VersionRange get before_2_2_0 =>
+ new VersionRange(max: Version.parse('2.2.0'), includeMax: false);
+
+ /// Return a range covering every version up to, but not including, 2.2.2.
+ VersionRange get before_2_2_2 =>
+ new VersionRange(max: Version.parse('2.2.2'), includeMax: false);
+
/// Return `true` if references to Future and Stream need to be checked.
bool get checkFutureAndStream => _checkFutureAndStream ??=
!before_2_1_0.intersect(_versionConstraint).isEmpty;
+ /// Return `true` if references to set literals need to be checked.
+ bool get checkSetLiterals =>
+ _checkSetLiterals ??= !before_2_2_0.intersect(_versionConstraint).isEmpty;
+
+ /// Return `true` if references to the ui-as-code features (control flow and
+ /// spread collections) need to be checked.
+ bool get checkUiAsCode =>
+ _checkUiAsCode ??= !before_2_2_2.intersect(_versionConstraint).isEmpty;
+
+ @override
+ void visitForElement(ForElement node) {
+ _validateUiAsCode(node);
+ bool wasInUiAsCode = _inUiAsCode;
+ _inUiAsCode = true;
+ super.visitForElement(node);
+ _inUiAsCode = wasInUiAsCode;
+ }
+
@override
void visitHideCombinator(HideCombinator node) {
// Don't flag references to either `Future` or `Stream` within a combinator.
}
@override
+ void visitIfElement(IfElement node) {
+ _validateUiAsCode(node);
+ bool wasInUiAsCode = _inUiAsCode;
+ _inUiAsCode = true;
+ super.visitIfElement(node);
+ _inUiAsCode = wasInUiAsCode;
+ }
+
+ @override
+ void visitSetOrMapLiteral(SetOrMapLiteral node) {
+ if (node.isSet && checkSetLiterals && !_inSetLiteral) {
+ _errorReporter.reportErrorForNode(HintCode.SDK_VERSION_SET_LITERAL, node);
+ }
+ bool wasInSetLiteral = _inSetLiteral;
+ _inSetLiteral = true;
+ super.visitSetOrMapLiteral(node);
+ _inSetLiteral = wasInSetLiteral;
+ }
+
+ @override
void visitShowCombinator(ShowCombinator node) {
// Don't flag references to either `Future` or `Stream` within a combinator.
}
@@ -75,4 +138,22 @@
HintCode.SDK_VERSION_ASYNC_EXPORTED_FROM_CORE, node, [element.name]);
}
}
+
+ @override
+ void visitSpreadElement(SpreadElement node) {
+ _validateUiAsCode(node);
+ bool wasInUiAsCode = _inUiAsCode;
+ _inUiAsCode = true;
+ super.visitSpreadElement(node);
+ _inUiAsCode = wasInUiAsCode;
+ }
+
+ /// Given that the [node] is only valid when the ui-as-code feature is
+ /// enabled, check that the code will not be executed with a version of the
+ /// SDK that does not support the feature.
+ void _validateUiAsCode(AstNode node) {
+ if (checkUiAsCode && !_inUiAsCode) {
+ _errorReporter.reportErrorForNode(HintCode.SDK_VERSION_UI_AS_CODE, node);
+ }
+ }
}
diff --git a/pkg/analyzer/test/src/diagnostics/sdk_constraint_verifier_support.dart b/pkg/analyzer/test/src/diagnostics/sdk_constraint_verifier_support.dart
new file mode 100644
index 0000000..81658b9
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/sdk_constraint_verifier_support.dart
@@ -0,0 +1,20 @@
+// Copyright (c) 2018, 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/error/error.dart';
+import 'package:pub_semver/pub_semver.dart';
+
+import '../dart/resolution/driver_resolution.dart';
+
+/// A base class designed to be used by tests of the hints produced by an
+/// SdkConstraintVerifier.
+class SdkConstraintVerifierTest extends DriverResolutionTest {
+ verifyVersion(String version, String source,
+ {List<ErrorCode> errorCodes}) async {
+ driver.configure(
+ analysisOptions: analysisOptions
+ ..sdkVersionConstraint = VersionConstraint.parse(version));
+ await assertErrorsInCode(source, errorCodes ?? []);
+ }
+}
diff --git a/pkg/analyzer/test/src/hint/sdk_version_async_exported_from_core_test.dart b/pkg/analyzer/test/src/diagnostics/sdk_version_async_exported_from_core_test.dart
similarity index 94%
rename from pkg/analyzer/test/src/hint/sdk_version_async_exported_from_core_test.dart
rename to pkg/analyzer/test/src/diagnostics/sdk_version_async_exported_from_core_test.dart
index a9f42b6..7e04d6d 100644
--- a/pkg/analyzer/test/src/hint/sdk_version_async_exported_from_core_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/sdk_version_async_exported_from_core_test.dart
@@ -5,7 +5,7 @@
import 'package:analyzer/src/dart/error/hint_codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import 'sdk_constraint_verifier.dart';
+import 'sdk_constraint_verifier_support.dart';
main() {
defineReflectiveSuite(() {
@@ -32,7 +32,7 @@
}
test_equals_explicitImportOfExportingLibrary() async {
- addNamedSource('/exporter.dart', '''
+ newFile('/test/lib/exporter.dart', content: '''
export 'dart:async';
''');
await verifyVersion('2.1.0', '''
@@ -76,7 +76,7 @@
}
test_lessThan_explicitImportOfExportingLibrary() async {
- addNamedSource('/exporter.dart', '''
+ newFile('/test/lib/exporter.dart', content: '''
export 'dart:async';
''');
await verifyVersion('2.0.0', '''
diff --git a/pkg/analyzer/test/src/diagnostics/sdk_version_set_literal_test.dart b/pkg/analyzer/test/src/diagnostics/sdk_version_set_literal_test.dart
new file mode 100644
index 0000000..4a7b294
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/sdk_version_set_literal_test.dart
@@ -0,0 +1,35 @@
+// Copyright (c) 2018, 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/dart/error/hint_codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'sdk_constraint_verifier_support.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(SdkVersionSetLiteralTest);
+ });
+}
+
+@reflectiveTest
+class SdkVersionSetLiteralTest extends SdkConstraintVerifierTest {
+ test_equals() async {
+ await verifyVersion('2.2.0', '''
+Set<int> zero() => <int>{0};
+''');
+ }
+
+ test_greaterThan() async {
+ await verifyVersion('2.3.0', '''
+Set<int> zero() => <int>{0};
+''');
+ }
+
+ test_lessThan() async {
+ await verifyVersion('2.1.0', '''
+Set<int> zero() => <int>{0};
+''', errorCodes: [HintCode.SDK_VERSION_SET_LITERAL]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/sdk_version_ui_as_code_test.dart b/pkg/analyzer/test/src/diagnostics/sdk_version_ui_as_code_test.dart
new file mode 100644
index 0000000..d14a9a2
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/sdk_version_ui_as_code_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2018, 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/dart/analysis/experiments.dart';
+import 'package:analyzer/src/dart/error/hint_codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'sdk_constraint_verifier_support.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(SdkVersionUiAsCodeTest);
+ });
+}
+
+@reflectiveTest
+class SdkVersionUiAsCodeTest extends SdkConstraintVerifierTest {
+ @override
+ AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+ ..enabledExperiments = [
+ EnableString.control_flow_collections,
+ EnableString.spread_collections
+ ];
+
+ test_equals() async {
+ await verifyVersion('2.2.2', '''
+List<int> zero() => [for (var e in [0]) e];
+''');
+ }
+
+ test_greaterThan() async {
+ await verifyVersion('2.3.0', '''
+List<int> zero() => [...[0]];
+''');
+ }
+
+ test_lessThan() async {
+ await verifyVersion('2.2.1', '''
+List<int> zero() => [if (0 < 1) 0];
+''', errorCodes: [HintCode.SDK_VERSION_UI_AS_CODE]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index e9f000b..1d6030e 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -41,6 +41,10 @@
import 'must_call_super_test.dart' as must_call_super;
import 'non_bool_condition_test.dart' as non_bool_condition;
import 'non_constant_map_element_test.dart' as non_constant_map_element;
+import 'sdk_version_async_exported_from_core_test.dart'
+ as sdk_version_async_exported_from_core;
+import 'sdk_version_set_literal_test.dart' as sdk_version_set_literal;
+import 'sdk_version_ui_as_code_test.dart' as sdk_version_ui_as_code;
import 'set_element_type_not_assignable_test.dart'
as set_element_type_not_assignable;
import 'subtype_of_sealed_class_test.dart' as subtype_of_sealed_class;
@@ -101,6 +105,9 @@
must_call_super.main();
non_bool_condition.main();
non_constant_map_element.main();
+ sdk_version_async_exported_from_core.main();
+ sdk_version_set_literal.main();
+ sdk_version_ui_as_code.main();
set_element_type_not_assignable.main();
subtype_of_sealed_class.main();
top_level_instance_getter.main();
diff --git a/pkg/analyzer/test/src/hint/sdk_constraint_verifier.dart b/pkg/analyzer/test/src/hint/sdk_constraint_verifier.dart
deleted file mode 100644
index ad910f0..0000000
--- a/pkg/analyzer/test/src/hint/sdk_constraint_verifier.dart
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2018, 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/error/error.dart';
-import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
-import 'package:pub_semver/pub_semver.dart';
-
-import '../../generated/resolver_test_case.dart';
-import '../../generated/test_support.dart';
-
-/// A base class designed to be used by tests of the hints produced by an
-/// SdkConstraintVerifier.
-class SdkConstraintVerifierTest extends ResolverTestCase {
- bool get enableNewAnalysisDriver => true;
-
- verifyVersion(String version, String source,
- {List<ErrorCode> errorCodes}) async {
- driver.configure(
- analysisOptions: AnalysisOptionsImpl()
- ..sdkVersionConstraint = VersionConstraint.parse(version));
-
- TestAnalysisResult result = await computeTestAnalysisResult(source);
- GatheringErrorListener listener = new GatheringErrorListener();
- listener.addAll(result.errors);
- if (errorCodes == null) {
- listener.assertNoErrors();
- } else {
- listener.assertErrorsWithCodes(errorCodes);
- }
- }
-}
diff --git a/pkg/analyzer/test/src/hint/test_all.dart b/pkg/analyzer/test/src/hint/test_all.dart
index 0234ba4..0933671 100644
--- a/pkg/analyzer/test/src/hint/test_all.dart
+++ b/pkg/analyzer/test/src/hint/test_all.dart
@@ -5,12 +5,9 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'sdk_constraint_extractor_test.dart' as sdk_constraint_extractor;
-import 'sdk_version_async_exported_from_core_test.dart'
- as sdk_version_async_exported_from_core;
main() {
defineReflectiveSuite(() {
sdk_constraint_extractor.main();
- sdk_version_async_exported_from_core.main();
}, name: 'hint');
}