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 3284b6f8281b0..6d408cd0d8450 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart @@ -500,6 +500,7 @@ class FixProcessor extends BaseProcessor { ImportLibrary.forType, ], CompileTimeErrorCode.EXTENDS_NON_CLASS: [ + DataDriven.newInstance, ImportLibrary.forType, ], CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS: [ @@ -509,6 +510,7 @@ class FixProcessor extends BaseProcessor { AddMissingParameter.newInstance, ], CompileTimeErrorCode.IMPLEMENTS_NON_CLASS: [ + DataDriven.newInstance, ImportLibrary.forType, ], CompileTimeErrorCode.INVALID_ANNOTATION: [ @@ -519,6 +521,7 @@ class FixProcessor extends BaseProcessor { DataDriven.newInstance, ], CompileTimeErrorCode.MIXIN_OF_NON_CLASS: [ + DataDriven.newInstance, ImportLibrary.forType, ], CompileTimeErrorCode.NEW_WITH_NON_TYPE: [ @@ -551,21 +554,25 @@ class FixProcessor extends BaseProcessor { ImportLibrary.forType, ], CompileTimeErrorCode.UNDEFINED_CLASS: [ + DataDriven.newInstance, ImportLibrary.forType, ], CompileTimeErrorCode.UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT: [ AddSuperConstructorInvocation.newInstance, ], CompileTimeErrorCode.UNDEFINED_FUNCTION: [ + DataDriven.newInstance, ImportLibrary.forExtension, ImportLibrary.forFunction, ImportLibrary.forType, ], CompileTimeErrorCode.UNDEFINED_GETTER: [ + DataDriven.newInstance, ImportLibrary.forTopLevelVariable, ImportLibrary.forType, ], CompileTimeErrorCode.UNDEFINED_IDENTIFIER: [ + DataDriven.newInstance, ImportLibrary.forExtension, ImportLibrary.forFunction, ImportLibrary.forTopLevelVariable, @@ -579,6 +586,10 @@ class FixProcessor extends BaseProcessor { CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER: [ ChangeArgumentName.newInstance, ], + CompileTimeErrorCode.UNDEFINED_SETTER: [ + DataDriven.newInstance, + // TODO(brianwilkerson) Support ImportLibrary + ], CompileTimeErrorCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS: [ DataDriven.newInstance, ], @@ -597,6 +608,9 @@ class FixProcessor extends BaseProcessor { HintCode.DEPRECATED_MEMBER_USE_WITH_MESSAGE: [ DataDriven.newInstance, ], + HintCode.OVERRIDE_ON_NON_OVERRIDING_METHOD: [ + DataDriven.newInstance, + ], HintCode.SDK_VERSION_ASYNC_EXPORTED_FROM_CORE: [ ImportLibrary.dartAsync, ], diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart index 8cd7bdbe583a3..cd29b71bad55e 100644 --- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart +++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart @@ -5,6 +5,8 @@ import 'package:analysis_server/src/services/correction/fix/data_driven/element_descriptor.dart'; import 'package:analysis_server/src/services/correction/fix/data_driven/rename.dart'; import 'package:analysis_server/src/services/correction/fix/data_driven/transform.dart'; +import 'package:analyzer/error/error.dart'; +import 'package:analyzer/src/dart/error/hint_codes.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; import 'data_driven_test_support.dart'; @@ -12,12 +14,87 @@ import 'data_driven_test_support.dart'; void main() { defineReflectiveSuite(() { defineReflectiveTests(Rename_DeprecatedMemberUseTest); + defineReflectiveTests(Rename_ExtendsNonClassTest); + defineReflectiveTests(Rename_ImplementsNonClassTest); + defineReflectiveTests(Rename_MixinOfNonClassTest); + defineReflectiveTests(Rename_OverrideOnNonOverridingMethodTest); + defineReflectiveTests(Rename_UndefinedClassTest); + defineReflectiveTests(Rename_UndefinedFunctionTest); + defineReflectiveTests(Rename_UndefinedGetterTest); + defineReflectiveTests(Rename_UndefinedIdentifierTest); + defineReflectiveTests(Rename_UndefinedMethodTest); }); } @reflectiveTest -class Rename_DeprecatedMemberUseTest extends DataDrivenFixProcessorTest { - Future test_class() async { +class Rename_DeprecatedMemberUseTest extends _AbstractRenameTest { + Future test_class_reference_inExtends() async { + addMetaPackage(); + setPackageContent(''' +import 'package:meta/meta.dart'; + +@deprecated +class Old {} +class New {} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +class C extends Old {} +'''); + await assertHasFix(''' +import '$importUri'; + +class C extends New {} +'''); + } + + Future test_class_reference_inImplements() async { + addMetaPackage(); + setPackageContent(''' +import 'package:meta/meta.dart'; + +@deprecated +class Old {} +class New {} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +class C implements Old {} +'''); + await assertHasFix(''' +import '$importUri'; + +class C implements New {} +'''); + } + + Future test_class_reference_inOn() async { + addMetaPackage(); + setPackageContent(''' +import 'package:meta/meta.dart'; + +@deprecated +class Old {} +class New {} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +extension E on Old {} +'''); + await assertHasFix(''' +import '$importUri'; + +extension E on New {} +'''); + } + + Future test_class_reference_inTypeAnnotation() async { addMetaPackage(); setPackageContent(''' import 'package:meta/meta.dart'; @@ -39,7 +116,55 @@ void f(New o) {} '''); } - Future test_constructor_named() async { + Future test_class_reference_inWith() async { + addMetaPackage(); + setPackageContent(''' +import 'package:meta/meta.dart'; + +@deprecated +class Old {} +class New {} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +class C with Old {} +'''); + await assertHasFix(''' +import '$importUri'; + +class C with New {} +'''); + } + + Future test_class_reference_staticField() async { + addMetaPackage(); + setPackageContent(''' +import 'package:meta/meta.dart'; + +@deprecated +class Old { + static String empty = ''; +} +class New { + static String empty = ''; +} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +var s = Old.empty; +'''); + await assertHasFix(''' +import '$importUri'; + +var s = New.empty; +'''); + } + + Future test_constructor_named_reference() async { addMetaPackage(); setPackageContent(''' import 'package:meta/meta.dart'; @@ -67,7 +192,7 @@ void f() { '''); } - Future test_constructor_unnamed() async { + Future test_constructor_unnamed_reference() async { addMetaPackage(); setPackageContent(''' import 'package:meta/meta.dart'; @@ -97,7 +222,59 @@ void f() { '''); } - Future test_field_instance() async { + Future test_extension_reference_override() async { + addMetaPackage(); + setPackageContent(''' +import 'package:meta/meta.dart'; + +@deprecated +extension Old on String { + int get double => length * 2; +} +extension New on String { + int get double => length * 2; +} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +var l = Old('a').double; +'''); + await assertHasFix(''' +import '$importUri'; + +var l = New('a').double; +'''); + } + + Future test_extension_reference_staticField() async { + addMetaPackage(); + setPackageContent(''' +import 'package:meta/meta.dart'; + +@deprecated +extension Old on String { + static String empty = ''; +} +extension New on String { + static String empty = ''; +} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +var s = Old.empty; +'''); + await assertHasFix(''' +import '$importUri'; + +var s = New.empty; +'''); + } + + Future test_field_instance_reference() async { addMetaPackage(); setPackageContent(''' import 'package:meta/meta.dart'; @@ -125,7 +302,35 @@ void f(C c) { '''); } - Future test_field_static() async { + Future test_field_static_assignment() async { + addMetaPackage(); + setPackageContent(''' +import 'package:meta/meta.dart'; + +class C { + @deprecated + static int old; + static int new; +} +'''); + setPackageData(_rename(['C', 'old'], 'new')); + await resolveTestUnit(''' +import '$importUri'; + +void f() { + C.old = 0; +} +'''); + await assertHasFix(''' +import '$importUri'; + +void f() { + C.new = 0; +} +'''); + } + + Future test_field_static_reference() async { addMetaPackage(); setPackageContent(''' import 'package:meta/meta.dart'; @@ -153,7 +358,38 @@ void f() { '''); } - Future test_method_instance() async { + @failingTest + Future test_method_instance_override() async { + addMetaPackage(); + setPackageContent(''' +import 'package:meta/meta.dart'; + +class C { + @deprecated + int old() => 0; + int new() => 0; +} +'''); + setPackageData(_rename(['C', 'old'], 'new')); + await resolveTestUnit(''' +import '$importUri'; + +class D extends C { + @override + int old() => 0; +} +'''); + await assertHasFix(''' +import '$importUri'; + +class D extends C { + @override + int new() => 0; +} +'''); + } + + Future test_method_instance_reference() async { addMetaPackage(); setPackageContent(''' import 'package:meta/meta.dart'; @@ -181,7 +417,7 @@ void f(C c) { '''); } - Future test_method_static() async { + Future test_method_static_reference() async { addMetaPackage(); setPackageContent(''' import 'package:meta/meta.dart'; @@ -209,7 +445,29 @@ void f() { '''); } - Future test_topLevelFunction() async { + Future test_mixin_reference_inWith() async { + addMetaPackage(); + setPackageContent(''' +import 'package:meta/meta.dart'; + +@deprecated +mixin Old {} +mixin New {} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +class C with Old {} +'''); + await assertHasFix(''' +import '$importUri'; + +class C with New {} +'''); + } + + Future test_topLevelFunction_reference() async { addMetaPackage(); setPackageContent(''' import 'package:meta/meta.dart'; @@ -235,6 +493,447 @@ void f() { '''); } + Future test_typedef_reference() async { + addMetaPackage(); + setPackageContent(''' +import 'package:meta/meta.dart'; + +@deprecated +typedef Old = int Function(int); +typedef New = int Function(int); +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +void f(Old o) {} +'''); + await assertHasFix(''' +import '$importUri'; + +void f(New o) {} +'''); + } +} + +@reflectiveTest +class Rename_ExtendsNonClassTest extends _AbstractRenameTest { + Future test_class_reference_inExtends() async { + setPackageContent(''' +class New {} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +class C extends Old {} +'''); + await assertHasFix(''' +import '$importUri'; + +class C extends New {} +''', errorFilter: _ignoreUnusedImport); + } +} + +@reflectiveTest +class Rename_ImplementsNonClassTest extends _AbstractRenameTest { + Future test_class_reference_inImplements() async { + setPackageContent(''' +class New {} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +class C implements Old {} +'''); + await assertHasFix(''' +import '$importUri'; + +class C implements New {} +''', errorFilter: _ignoreUnusedImport); + } +} + +@reflectiveTest +class Rename_MixinOfNonClassTest extends _AbstractRenameTest { + Future test_class_reference_inWith() async { + setPackageContent(''' +class New {} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +class C with Old {} +'''); + await assertHasFix(''' +import '$importUri'; + +class C with New {} +''', errorFilter: _ignoreUnusedImport); + } + + Future test_mixin_reference_inWith() async { + setPackageContent(''' +mixin New {} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +class C with Old {} +'''); + await assertHasFix(''' +import '$importUri'; + +class C with New {} +''', errorFilter: _ignoreUnusedImport); + } +} + +@reflectiveTest +class Rename_OverrideOnNonOverridingMethodTest extends _AbstractRenameTest { + Future test_method_instance_override() async { + setPackageContent(''' +class C { + int new() => 0; +} +'''); + setPackageData(_rename(['C', 'old'], 'new')); + await resolveTestUnit(''' +import '$importUri'; + +class D extends C { + @override + int old() => 0; +} +'''); + await assertHasFix(''' +import '$importUri'; + +class D extends C { + @override + int new() => 0; +} +'''); + } +} + +@reflectiveTest +class Rename_UndefinedClassTest extends _AbstractRenameTest { + Future test_class_reference_inOn() async { + setPackageContent(''' +class New {} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +extension E on Old {} +'''); + await assertHasFix(''' +import '$importUri'; + +extension E on New {} +''', errorFilter: _ignoreUnusedImport); + } + + Future test_class_reference_inTypeAnnotation() async { + setPackageContent(''' +class New {} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +void f(Old o) {} +'''); + await assertHasFix(''' +import '$importUri'; + +void f(New o) {} +''', errorFilter: _ignoreUnusedImport); + } + + Future test_typedef_reference() async { + setPackageContent(''' +typedef New = int Function(int); +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +void f(Old o) {} +'''); + await assertHasFix(''' +import '$importUri'; + +void f(New o) {} +''', errorFilter: _ignoreUnusedImport); + } +} + +@reflectiveTest +class Rename_UndefinedFunctionTest extends _AbstractRenameTest { + Future test_constructor_unnamed_reference() async { + setPackageContent(''' +class New { + New(); +} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +void f() { + Old(); +} +'''); + await assertHasFix(''' +import '$importUri'; + +void f() { + New(); +} +''', errorFilter: _ignoreUnusedImport); + } + + Future test_extension_reference_override() async { + setPackageContent(''' +extension New on String { + int get double => length * 2; +} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +var l = Old('a').double; +'''); + await assertHasFix(''' +import '$importUri'; + +var l = New('a').double; +''', errorFilter: _ignoreUnusedImport); + } + + Future test_field_instance_reference() async { + setPackageContent(''' +class C { + int new; +} +'''); + setPackageData(_rename(['C', 'old'], 'new')); + await resolveTestUnit(''' +import '$importUri'; + +void f(C c) { + c.old; +} +'''); + await assertHasFix(''' +import '$importUri'; + +void f(C c) { + c.new; +} +'''); + } + + Future test_topLevelFunction_reference() async { + setPackageContent(''' +int new() {} +'''); + setPackageData(_rename(['old'], 'new')); + await resolveTestUnit(''' +import '$importUri'; + +void f() { + old(); +} +'''); + await assertHasFix(''' +import '$importUri'; + +void f() { + new(); +} +''', errorFilter: _ignoreUnusedImport); + } +} + +@reflectiveTest +class Rename_UndefinedGetterTest extends _AbstractRenameTest { + Future test_field_static_reference() async { + setPackageContent(''' +class C { + static int new; +} +'''); + setPackageData(_rename(['C', 'old'], 'new')); + await resolveTestUnit(''' +import '$importUri'; + +void f() { + C.old; +} +'''); + await assertHasFix(''' +import '$importUri'; + +void f() { + C.new; +} +'''); + } +} + +@reflectiveTest +class Rename_UndefinedIdentifierTest extends _AbstractRenameTest { + Future test_class_reference_staticField() async { + // CompileTimeErrorCode.UNDEFINED_IDENTIFIER + setPackageContent(''' +class New { + static String empty = ''; +} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +var s = Old.empty; +'''); + await assertHasFix(''' +import '$importUri'; + +var s = New.empty; +''', errorFilter: _ignoreUnusedImport); + } + + Future test_extension_reference_staticField() async { + setPackageContent(''' +extension New on String { + static String empty = ''; +} +'''); + setPackageData(_rename(['Old'], 'New')); + await resolveTestUnit(''' +import '$importUri'; + +var s = Old.empty; +'''); + await assertHasFix(''' +import '$importUri'; + +var s = New.empty; +''', errorFilter: _ignoreUnusedImport); + } +} + +@reflectiveTest +class Rename_UndefinedMethodTest extends _AbstractRenameTest { + Future test_constructor_named_reference() async { + setPackageContent(''' +class C { + C.new(); +} +'''); + setPackageData(_rename(['C', 'old'], 'new')); + await resolveTestUnit(''' +import '$importUri'; + +void f() { + C.old(); +} +'''); + await assertHasFix(''' +import '$importUri'; + +void f() { + C.new(); +} +'''); + } + + Future test_method_instance_reference() async { + setPackageContent(''' +class C { + int new() {} +} +'''); + setPackageData(_rename(['C', 'old'], 'new')); + await resolveTestUnit(''' +import '$importUri'; + +void f(C c) { + c.old(); +} +'''); + await assertHasFix(''' +import '$importUri'; + +void f(C c) { + c.new(); +} +'''); + } + + Future test_method_static_reference() async { + setPackageContent(''' +class C { + static int new() {} +} +'''); + setPackageData(_rename(['C', 'old'], 'new')); + await resolveTestUnit(''' +import '$importUri'; + +void f() { + C.old(); +} +'''); + await assertHasFix(''' +import '$importUri'; + +void f() { + C.new(); +} +'''); + } +} + +@reflectiveTest +class Rename_UndefinedSetterTest extends _AbstractRenameTest { + Future test_field_static_assignment() async { + setPackageContent(''' +class C { + static int new; +} +'''); + setPackageData(_rename(['C', 'old'], 'new')); + await resolveTestUnit(''' +import '$importUri'; + +void f() { + C.old = 0; +} +'''); + await assertHasFix(''' +import '$importUri'; + +void f() { + C.new = 0; +} +'''); + } +} + +class _AbstractRenameTest extends DataDrivenFixProcessorTest { + bool _ignoreUnusedImport(AnalysisError error) => + error.errorCode != HintCode.UNUSED_IMPORT; + Transform _rename(List components, String newName) => Transform( title: 'title', element: ElementDescriptor( diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart index c37e6c61f49f4..54211be1a8e4e 100644 --- a/pkg/analyzer/lib/src/generated/error_verifier.dart +++ b/pkg/analyzer/lib/src/generated/error_verifier.dart @@ -834,13 +834,10 @@ class ErrorVerifier extends RecursiveAstVisitor { checkIndexExpression(node.auxiliaryElements?.staticElement); if (node.isNullAware) { - var target = node.realTarget; - if (_isExpressionWithType(target)) { - _checkForUnnecessaryNullAware( - target, - node.question ?? node.period ?? node.leftBracket, - ); - } + _checkForUnnecessaryNullAware( + node.realTarget, + node.question ?? node.period ?? node.leftBracket, + ); } super.visitIndexExpression(node); } @@ -934,9 +931,7 @@ class ErrorVerifier extends RecursiveAstVisitor { _checkForStaticAccessToInstanceMember(typeReference, methodName); _checkForInstanceAccessToStaticMember( typeReference, node.target, methodName); - if (_isExpressionWithType(target)) { - _checkForUnnecessaryNullAware(target, node.operator); - } + _checkForUnnecessaryNullAware(target, node.operator); } else { _checkForUnqualifiedReferenceToNonLocalStaticMember(methodName); } @@ -1039,9 +1034,7 @@ class ErrorVerifier extends RecursiveAstVisitor { _checkForStaticAccessToInstanceMember(typeReference, propertyName); _checkForInstanceAccessToStaticMember( typeReference, node.target, propertyName); - if (_isExpressionWithType(target)) { - _checkForUnnecessaryNullAware(target, node.operator); - } + _checkForUnnecessaryNullAware(target, node.operator); super.visitPropertyAccess(node); } @@ -4364,33 +4357,37 @@ class ErrorVerifier extends RecursiveAstVisitor { var type = operator.type; if (type == TokenType.QUESTION_PERIOD) { var realTarget = target.realTarget; - if (_isExpressionWithType(realTarget)) { - return previousShortCircuitingOperator(realTarget) ?? operator; - } + return previousShortCircuitingOperator(realTarget) ?? operator; } } else if (target is IndexExpression) { if (target.question != null) { var realTarget = target.realTarget; - if (_isExpressionWithType(realTarget)) { - return previousShortCircuitingOperator(realTarget) ?? - target.question; - } + return previousShortCircuitingOperator(realTarget) ?? target.question; } } else if (target is MethodInvocation) { var operator = target.operator; var type = operator?.type; if (type == TokenType.QUESTION_PERIOD) { var realTarget = target.realTarget; - if (_isExpressionWithType(realTarget)) { - return previousShortCircuitingOperator(realTarget) ?? operator; - } - return operator; + return previousShortCircuitingOperator(realTarget) ?? operator; } } return null; } - if (_typeSystem.isStrictlyNonNullable(target.staticType)) { + var targetType = target.staticType; + if (target is ExtensionOverride) { + var arguments = target.argumentList.arguments; + if (arguments.length == 1) { + targetType = arguments[0].staticType; + } else { + return; + } + } else if (targetType == null) { + return; + } + + if (_typeSystem.isStrictlyNonNullable(targetType)) { if (errorCode == StaticWarningCode.INVALID_NULL_AWARE_OPERATOR) { var previousOperator = previousShortCircuitingOperator(target); if (previousOperator != null) { @@ -5331,20 +5328,6 @@ class ErrorVerifier extends RecursiveAstVisitor { } return null; } - - static bool _isExpressionWithType(Expression node) { - if (node is ExtensionOverride) { - return false; - } - - // For `foo?.bar`, `foo` must be an identifier with a value. - if (node is Identifier) { - var element = node.staticElement; - return element is PropertyAccessorElement || element is VariableElement; - } - - return true; - } } /// A record of the elements that will be declared in some scope (block), but diff --git a/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart index 3f27eb13d57e6..bce4df1930529 100644 --- a/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart +++ b/pkg/analyzer/test/src/diagnostics/invalid_null_aware_operator_test.dart @@ -89,6 +89,88 @@ void f(String? s) { @reflectiveTest class InvalidNullAwareOperatorTest extends PubPackageResolutionTest with WithNullSafetyMixin { + test_extensionOverride_assignmentExpression_indexExpression() async { + await assertErrorsInCode(''' +extension E on int { + operator[]=(int index, bool _) {} +} + +void f(int? a, int b) { + E(a)?[0] = true; + E(b)?[0] = true; +} +''', [ + error(StaticWarningCode.INVALID_NULL_AWARE_OPERATOR, 109, 2), + ]); + } + + test_extensionOverride_assignmentExpression_propertyAccess() async { + await assertErrorsInCode(''' +extension E on int { + set foo(bool _) {} +} + +void f(int? a, int b) { + E(a)?.foo = true; + E(b)?.foo = true; +} +''', [ + error(StaticWarningCode.INVALID_NULL_AWARE_OPERATOR, 95, 2), + ]); + } + + test_extensionOverride_indexExpression() async { + await assertErrorsInCode(''' +extension E on int { + bool operator[](int index) => true; +} + +void f(int? a, int b) { + E(a)?[0]; + E(b)?[0]; +} +''', [ + error(StaticWarningCode.INVALID_NULL_AWARE_OPERATOR, 104, 2), + ]); + assertType(findNode.index('E(a)'), 'bool?'); + assertType(findNode.index('E(b)'), 'bool?'); + } + + test_extensionOverride_methodInvocation() async { + await assertErrorsInCode(''' +extension E on int { + bool foo() => true; +} + +void f(int? a, int b) { + E(a)?.foo(); + E(b)?.foo(); +} +''', [ + error(StaticWarningCode.INVALID_NULL_AWARE_OPERATOR, 91, 2), + ]); + + assertType(findNode.methodInvocation('E(a)'), 'bool?'); + assertType(findNode.methodInvocation('E(b)'), 'bool?'); + } + + test_extensionOverride_propertyAccess() async { + await assertErrorsInCode(''' +extension E on int { + bool get foo => true; +} + +void f(int? a, int b) { + E(a)?.foo; + E(b)?.foo; +} +''', [ + error(StaticWarningCode.INVALID_NULL_AWARE_OPERATOR, 91, 2), + ]); + assertType(findNode.propertyAccess('E(a)'), 'bool?'); + assertType(findNode.propertyAccess('E(b)'), 'bool?'); + } + test_getter_class() async { await assertNoErrorsInCode(''' class C { diff --git a/pkg/analyzer/test/src/diagnostics/unnecessary_non_null_assertion_test.dart b/pkg/analyzer/test/src/diagnostics/unnecessary_non_null_assertion_test.dart index a72ca9d19394c..3104f0557fea3 100644 --- a/pkg/analyzer/test/src/diagnostics/unnecessary_non_null_assertion_test.dart +++ b/pkg/analyzer/test/src/diagnostics/unnecessary_non_null_assertion_test.dart @@ -31,7 +31,33 @@ f() { '''); } - test_nonNull() async { + test_nonNull_function() async { + await assertErrorsInCode(''' +void g() {} + +void f() { + g!(); +} +''', [ + error(StaticWarningCode.UNNECESSARY_NON_NULL_ASSERTION, 27, 1), + ]); + } + + test_nonNull_method() async { + await assertErrorsInCode(''' +class A { + static void foo() {} +} + +void f() { + A.foo!(); +} +''', [ + error(StaticWarningCode.UNNECESSARY_NON_NULL_ASSERTION, 54, 1), + ]); + } + + test_nonNull_parameter() async { await assertErrorsInCode(''' f(int x) { x!; diff --git a/pkg/dartdev/README.md b/pkg/dartdev/README.md index 25e92b12d803b..7b9d987f38f5d 100644 --- a/pkg/dartdev/README.md +++ b/pkg/dartdev/README.md @@ -6,11 +6,12 @@ A command-line utility for Dart development. Usage: dart [] [] Global options: --h, --help Print this usage information. --v, --verbose Show additional command output. - --version Print the Dart SDK version. - --enable-analytics Enable anonymous analytics. - --disable-analytics Disable anonymous analytics. +-h, --help Print this usage information. +-v, --verbose Show additional command output. + --version Print the Dart SDK version. + --enable-analytics Enable anonymous analytics. + --disable-analytics Disable anonymous analytics. + --enable-experiment= Enable one or more experimental features (see dart.dev/go/experiments). Available commands: analyze Analyze the project's Dart code. @@ -19,7 +20,7 @@ Available commands: format Idiomatically formats Dart source code. pub Work with packages. run Run a Dart program. - test Runs tests in this project. + test Run tests in this package. Run "dart help " for more information about a command. ``` diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart index 72df662e49ae7..b63580ab654ea 100644 --- a/pkg/dartdev/lib/dartdev.dart +++ b/pkg/dartdev/lib/dartdev.dart @@ -230,6 +230,10 @@ class DartdevRunner extends CommandRunner { addCommand(TestCommand()); } + @override + String get usageFooter => + 'See https://dart.dev/tools/dart-tool for detailed documentation.'; + @override String get invocation => 'dart [] []'; diff --git a/pkg/dartdev/lib/src/commands/compile.dart b/pkg/dartdev/lib/src/commands/compile.dart index 873680b1f9f9c..669d4be8ef897 100644 --- a/pkg/dartdev/lib/src/commands/compile.dart +++ b/pkg/dartdev/lib/src/commands/compile.dart @@ -28,7 +28,7 @@ final Map commonOptions = { abbr: 'o', help: ''' Write the output to . -This can be an absolute or reletive path. +This can be an absolute or relative path. ''', ), }; @@ -175,7 +175,7 @@ class CompileNativeCommand extends DartdevCommand { ) ..addMultiOption('define', abbr: 'D', valueHelp: 'key=value', help: ''' Define an environment declaration. To specify multiple declarations, use multiple options or use commas to separate key-value pairs. -For example, 'dart compile $commandName -Da=1,b=2 main.dart'.''') +For example: dart compile $commandName -Da=1,b=2 main.dart.''') ..addFlag('enable-asserts', negatable: false, help: 'Enable assert statements.') ..addOption('packages', @@ -184,7 +184,7 @@ For example, 'dart compile $commandName -Da=1,b=2 main.dart'.''') help: '''Get package locations from the specified file instead of .packages. can be relative or absolute. -For example, 'dart compile $commandName --packages=/tmp/pkgs main.dart'.''') +For example: dart compile $commandName --packages=/tmp/pkgs main.dart.''') ..addOption('save-debugging-info', abbr: 'S', valueHelp: 'path', help: ''' Remove debugging information from the output and save it separately to the specified file. can be relative or absolute.'''); diff --git a/pkg/dartdev/lib/src/commands/run.dart b/pkg/dartdev/lib/src/commands/run.dart index b0296d4948fc2..8f28c9ffe5738 100644 --- a/pkg/dartdev/lib/src/commands/run.dart +++ b/pkg/dartdev/lib/src/commands/run.dart @@ -82,7 +82,7 @@ class RunCommand extends DartdevCommand { ' when running with --enable-vm-service.', ) ..addSeparator( - 'Other debugging options include:', + 'Other debugging options:', ) ..addFlag( 'pause-isolates-on-start', diff --git a/pkg/dartdev/lib/src/commands/test.dart b/pkg/dartdev/lib/src/commands/test.dart index cfaf4f288a2d8..f655ed4621421 100644 --- a/pkg/dartdev/lib/src/commands/test.dart +++ b/pkg/dartdev/lib/src/commands/test.dart @@ -18,7 +18,7 @@ import '../sdk.dart'; import '../vm_interop_handler.dart'; class TestCommand extends DartdevCommand { - TestCommand() : super('test', 'Runs tests in this project.') { + TestCommand() : super('test', 'Run tests in this package.') { generateParser(argParser); } diff --git a/pkg/dartdev/pubspec.yaml b/pkg/dartdev/pubspec.yaml index a42480f0e4394..be43698d35557 100644 --- a/pkg/dartdev/pubspec.yaml +++ b/pkg/dartdev/pubspec.yaml @@ -8,7 +8,8 @@ environment: dependencies: analysis_server_client: path: ../analysis_server_client - analyzer: any + analyzer: + path: ../analyzer args: ^1.6.0 cli_util: '>=0.1.4 <0.3.0' dart2native: diff --git a/pkg/dartdev/test/commands/test_test.dart b/pkg/dartdev/test/commands/test_test.dart index 9dbb186c8c690..304f598a46dd6 100644 --- a/pkg/dartdev/test/commands/test_test.dart +++ b/pkg/dartdev/test/commands/test_test.dart @@ -20,7 +20,7 @@ void defineTest() { var result = p.runSync('test', ['--help']); expect(result.exitCode, 0); expect(result.stderr, isEmpty); - expect(result.stdout, contains('Runs tests in this project.')); + expect(result.stdout, contains('Run tests in this package.')); expect(result.stdout, contains('Usage: dart test [arguments]')); expect(result.stdout, contains('======== Selecting Tests')); }); diff --git a/pkg/dartfix/lib/src/migrate/apply.dart b/pkg/dartfix/lib/src/migrate/apply.dart index 290c34540475d..b4c7ab21b221e 100644 --- a/pkg/dartfix/lib/src/migrate/apply.dart +++ b/pkg/dartfix/lib/src/migrate/apply.dart @@ -6,6 +6,8 @@ import 'package:analysis_server_client/protocol.dart'; import '../util.dart'; +// TODO(devoncarew): This is only referenced from tests. + /// Perform the indicated source edits to the given source, returning the /// resulting transformed text. String applyEdits(SourceFileEdit sourceFileEdit, String source) { diff --git a/pkg/dartfix/lib/src/migrate/display.dart b/pkg/dartfix/lib/src/migrate/display.dart index d14fd8e973cd1..68561fbdd74e1 100644 --- a/pkg/dartfix/lib/src/migrate/display.dart +++ b/pkg/dartfix/lib/src/migrate/display.dart @@ -6,6 +6,8 @@ import 'package:analysis_server_client/protocol.dart'; import 'package:cli_util/cli_logging.dart'; import 'package:path/path.dart' as path; +// TODO(devoncarew): This is only referenced from tests. + /// Given a Logger and an analysis issue, render the issue to the logger. class IssueRenderer { final Logger logger; diff --git a/pkg/dartfix/lib/src/migrate/options.dart b/pkg/dartfix/lib/src/migrate/options.dart index f19908d1ef435..e3c565e007d94 100644 --- a/pkg/dartfix/lib/src/migrate/options.dart +++ b/pkg/dartfix/lib/src/migrate/options.dart @@ -8,6 +8,8 @@ import 'package:args/args.dart'; import 'package:args/src/arg_parser.dart'; import 'package:path/path.dart' as path; +// TODO(devoncarew): This class is unused. + class MigrateOptions { static const applyChangesOption = 'apply-changes'; static const debugOption = 'debug'; @@ -62,8 +64,8 @@ class MigrateOptions { ignoreErrorsOption, defaultsTo: false, negatable: false, - help: 'Attempt to perform null safety analysis even if there are ' - 'analysis errors in the project.', + help: 'Attempt to perform null safety analysis even if the package has ' + 'analysis errors.', ); argParser.addOption( sdkPathOption, diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart index a8efa5e88f0b9..26821cb9e3106 100644 --- a/pkg/nnbd_migration/lib/migration_cli.dart +++ b/pkg/nnbd_migration/lib/migration_cli.dart @@ -393,14 +393,12 @@ class MigrationCli { negatable: true, help: 'Show an interactive preview of the proposed null safety changes ' 'in a browser window.\n' - 'With --no-web-preview, the proposed changes are instead printed to ' - 'the console.'); + '--no-web-preview prints proposed changes to the console.'); parser.addOption(CommandLineOptions.sdkPathOption, help: 'The path to the Dart SDK.', hide: hide); parser.addOption(CommandLineOptions.summaryOption, - help: - 'Output path for a machine-readable summary of migration changes'); + help: 'Output a machine-readable summary of migration changes.'); } static void addCoreOptions(ArgParser parser, bool hide) { @@ -412,8 +410,8 @@ class MigrationCli { CommandLineOptions.ignoreErrorsFlag, defaultsTo: false, negatable: false, - help: 'Attempt to perform null safety analysis even if there are ' - 'analysis errors in the project.', + help: 'Attempt to perform null safety analysis even if the package has ' + 'analysis errors.', ); parser.addFlag(CommandLineOptions.ignoreExceptionsFlag, defaultsTo: false, @@ -424,7 +422,7 @@ class MigrationCli { parser.addFlag(CommandLineOptions.verboseFlag, abbr: 'v', defaultsTo: false, - help: 'Verbose output.', + help: 'Show additional command output.', negatable: false); parser.addOption(CommandLineOptions.previewHostnameOption, defaultsTo: 'localhost', diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart index a376634acf87f..c61bc5382e903 100644 --- a/pkg/nnbd_migration/lib/src/edge_builder.dart +++ b/pkg/nnbd_migration/lib/src/edge_builder.dart @@ -3371,6 +3371,13 @@ mixin _AssignmentChecker { // Nothing else to do. return; } + } else if (destinationType.isDartCoreNull) { + // There's not really much we can infer from trying to assign a type to + // Null. We could say that the source of the assignment must be nullable, + // but that's not really useful because the nullability won't propagate + // anywhere. Besides, the code is probably erroneous (e.g. the user is + // trying to store a value into a `List`). So do nothing. + return; } else if (destinationType is TypeParameterType) { if (source.type is! TypeParameterType) { // Assume an assignment to the type parameter's bound. diff --git a/pkg/nnbd_migration/lib/src/front_end/region_renderer.dart b/pkg/nnbd_migration/lib/src/front_end/region_renderer.dart index 89f0f848773b8..e6be786ad7658 100644 --- a/pkg/nnbd_migration/lib/src/front_end/region_renderer.dart +++ b/pkg/nnbd_migration/lib/src/front_end/region_renderer.dart @@ -36,10 +36,9 @@ class RegionRenderer { path.Context get pathContext => migrationInfo.pathContext; EditDetails render() { - var unitDir = pathContext.dirname(pathMapper.map(unitInfo.path)); - TargetLink linkForTarget(NavigationTarget target) { - var relativePath = _relativePathToTarget(target, unitDir); + var relativePath = + _relativePathToTarget(target, pathContext.dirname(unitInfo.path)); var targetUri = _uriForPath(target.filePath, target); return TargetLink( path: relativePath, diff --git a/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart b/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart index 131fabfe4fc42..ec45e53bd1e42 100644 --- a/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart +++ b/pkg/nnbd_migration/lib/src/utilities/hint_utils.dart @@ -65,7 +65,7 @@ HintComment getPrefixHint(Token token) { /// Information about a hint found in a source file. class HintComment { - static final _alphaNumericRegexp = RegExp('[a-zA-Z0-9]'); + static final _identifierCharRegexp = RegExp('[a-zA-Z0-9_]'); /// What kind of hint this is. final HintCommentKind kind; @@ -108,8 +108,8 @@ class HintComment { bool appendSpace = false; var removeOffset = _removeOffset; var removeEnd = _removeEnd; - if (_isAlphaNumericBeforeOffset(sourceText, removeOffset) && - _isAlphaNumericAtOffset(sourceText, _keepOffset)) { + if (_isIdentifierCharBeforeOffset(sourceText, removeOffset) && + _isIdentifierCharAtOffset(sourceText, _keepOffset)) { if (sourceText[removeOffset] == ' ') { // We can just keep this space. removeOffset++; @@ -117,8 +117,8 @@ class HintComment { prependSpace = true; } } - if (_isAlphaNumericBeforeOffset(sourceText, _keepEnd) && - _isAlphaNumericAtOffset(sourceText, removeEnd)) { + if (_isIdentifierCharBeforeOffset(sourceText, _keepEnd) && + _isIdentifierCharAtOffset(sourceText, removeEnd)) { if (sourceText[removeEnd - 1] == ' ') { // We can just keep this space. removeEnd--; @@ -143,8 +143,8 @@ class HintComment { {AtomicEditInfo info}) { bool appendSpace = false; var removeOffset = this._removeOffset; - if (_isAlphaNumericBeforeOffset(sourceText, removeOffset) && - _isAlphaNumericAtOffset(sourceText, _removeEnd)) { + if (_isIdentifierCharBeforeOffset(sourceText, removeOffset) && + _isIdentifierCharAtOffset(sourceText, _removeEnd)) { if (sourceText[removeOffset] == ' ') { // We can just keep this space. removeOffset++; @@ -173,13 +173,13 @@ class HintComment { }; } - static bool _isAlphaNumericAtOffset(String sourceText, int offset) { + static bool _isIdentifierCharAtOffset(String sourceText, int offset) { return offset < sourceText.length && - _alphaNumericRegexp.hasMatch(sourceText[offset]); + _identifierCharRegexp.hasMatch(sourceText[offset]); } - static bool _isAlphaNumericBeforeOffset(String sourceText, int offset) { - return offset > 0 && _alphaNumericRegexp.hasMatch(sourceText[offset - 1]); + static bool _isIdentifierCharBeforeOffset(String sourceText, int offset) { + return offset > 0 && _identifierCharRegexp.hasMatch(sourceText[offset - 1]); } } diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart index a4ab81c7aa503..cbdf04c04fc93 100644 --- a/pkg/nnbd_migration/test/api_test.dart +++ b/pkg/nnbd_migration/test/api_test.dart @@ -1631,6 +1631,24 @@ void f(dynamic a) { await _checkSingleFileChanges(content, expected); } + Future test_downcast_to_null() async { + // This probably doesn't arise too often for real-world code, since it is + // most likely a mistake. Still, we want to make sure we don't crash. + var content = ''' +test() { + var x = List.filled(3, null); + x[0] = 1; +} +'''; + var expected = ''' +test() { + var x = List.filled(3, null); + x[0] = 1 as Null; +} +'''; + await _checkSingleFileChanges(content, expected); + } + Future test_downcast_type_argument_preserve_nullability() async { // There are no examples in front of us yet where anyone downcasts a type // with a nullable type parameter. This is maybe correct, maybe not, and it @@ -3996,6 +4014,18 @@ void repro(){ await _checkSingleFileChanges(content, expected); } + Future test_late_hint_followed_by_underscore() async { + var content = ''' +class _C {} +/*late*/ _C c; +'''; + var expected = ''' +class _C {} +late _C c; +'''; + await _checkSingleFileChanges(content, expected); + } + Future test_late_hint_instance_field_with_constructor() async { var content = ''' class C { diff --git a/pkg/nnbd_migration/test/front_end/region_renderer_test.dart b/pkg/nnbd_migration/test/front_end/region_renderer_test.dart index 9b2cd101b19bb..7fad2e6aae190 100644 --- a/pkg/nnbd_migration/test/front_end/region_renderer_test.dart +++ b/pkg/nnbd_migration/test/front_end/region_renderer_test.dart @@ -22,17 +22,9 @@ void main() { class RegionRendererTest extends NnbdMigrationTestBase { PathMapper pathMapper; - /// Returns the path of [testFile] used in traces. - /// - /// On Windows, we display the absolute path of the test file. - /// On Posix, we display the path of the target file relative to the current - /// file. - // TODO(srawlins): I doubt this is intentional. While I don't see a bug, - // the discrepancy could lead to confusion and may be an indicator of bugs. - String get _testFilePathForTrace => - resourceProvider.pathContext.style == p.Style.windows - ? testFile - : resourceProvider.pathContext.basename(testFile); + /// Returns the basename of [testFile], used in traces. + String get _testFileBasename => + resourceProvider.pathContext.basename(testFile); /// Render the region at [offset], using a [MigrationInfo] which knows only /// about the library at `infos.single`. @@ -64,7 +56,7 @@ class RegionRendererTest extends NnbdMigrationTestBase { var trace = response.traces[0]; expect(trace.entries, hasLength(2)); expect(trace.entries[0].description, - equals('parameter 0 of f ($_testFilePathForTrace:1:3)')); + equals('parameter 0 of f ($_testFileBasename:1:3)')); expect(trace.entries[1].description, equals('data flow')); } @@ -80,7 +72,7 @@ class RegionRendererTest extends NnbdMigrationTestBase { expect(entry.link.href, equals('$testFileUriPath?offset=2&line=1&authToken=AUTH_TOKEN')); expect(entry.link.path, - equals(resourceProvider.pathContext.toUri(_testFilePathForTrace).path)); + equals(resourceProvider.pathContext.toUri(_testFileBasename).path)); } Future @@ -151,11 +143,10 @@ g() { var trace = response.traces[0]; expect(trace.description, equals('Nullability reason')); expect(trace.entries, hasLength(4)); - expect( - trace.entries[0].description, equals('a ($_testFilePathForTrace:1:1)')); + expect(trace.entries[0].description, equals('a ($_testFileBasename:1:1)')); expect(trace.entries[1].description, equals('data flow')); expect(trace.entries[2].description, - equals('null literal ($_testFilePathForTrace:1:9)')); + equals('null literal ($_testFileBasename:1:9)')); expect(trace.entries[3].description, equals('literal expression')); } diff --git a/pkg/test_runner/analysis_options.yaml b/pkg/test_runner/analysis_options.yaml index 788c451113db3..406b0a11140bc 100644 --- a/pkg/test_runner/analysis_options.yaml +++ b/pkg/test_runner/analysis_options.yaml @@ -8,8 +8,6 @@ linter: # - non_constant_identifier_names # - only_throw_errors # - prefer_interpolation_to_compose_strings -# - prefer_is_empty -# - prefer_is_not_empty # - prefer_single_quotes - avoid_bool_literals_in_conditional_expressions - avoid_empty_else @@ -60,6 +58,8 @@ linter: - prefer_final_fields - prefer_generic_function_type_aliases - prefer_initializing_formals + - prefer_is_empty + - prefer_is_not_empty - prefer_null_aware_operators - prefer_typing_uninitialized_variables - recursive_getters diff --git a/pkg/test_runner/lib/src/android.dart b/pkg/test_runner/lib/src/android.dart index 8f1c4d3bfdabc..aa8fa5cd90c2d 100644 --- a/pkg/test_runner/lib/src/android.dart +++ b/pkg/test_runner/lib/src/android.dart @@ -382,7 +382,7 @@ class AdbDevicePool { static Future create() async { var names = await AdbHelper.listDevices(); var devices = names.map((id) => AdbDevice(id)).toList(); - if (devices.length == 0) { + if (devices.isEmpty) { throw Exception('No android devices found. ' 'Please make sure "adb devices" shows your device!'); } diff --git a/pkg/test_runner/lib/src/browser.dart b/pkg/test_runner/lib/src/browser.dart index 67238df3c8c8e..521e3c98e0e5c 100644 --- a/pkg/test_runner/lib/src/browser.dart +++ b/pkg/test_runner/lib/src/browser.dart @@ -54,7 +54,7 @@ String pathToJSIdentifier(String path) { /// Escape [name] to make it into a valid identifier. String _toJSIdentifier(String name) { - if (name.length == 0) return r'$'; + if (name.isEmpty) return r'$'; // Escape any invalid characters StringBuffer buffer; diff --git a/pkg/test_runner/lib/src/browser_controller.dart b/pkg/test_runner/lib/src/browser_controller.dart index 75a89ad95e570..708d185873ffc 100644 --- a/pkg/test_runner/lib/src/browser_controller.dart +++ b/pkg/test_runner/lib/src/browser_controller.dart @@ -1105,7 +1105,7 @@ class BrowserTestRunner { } void printDoubleReportingTests() { - if (doubleReportingOutputs.length == 0) return; + if (doubleReportingOutputs.isEmpty) return; // TODO(ricow): die on double reporting. // Currently we just report this here, we could have a callback to the // encapsulating environment. diff --git a/pkg/test_runner/lib/src/command_output.dart b/pkg/test_runner/lib/src/command_output.dart index c55896c7a594d..64996c0fe93ce 100644 --- a/pkg/test_runner/lib/src/command_output.dart +++ b/pkg/test_runner/lib/src/command_output.dart @@ -969,6 +969,11 @@ class CompilationCommandOutput extends CommandOutput { class Dart2jsCompilerCommandOutput extends CompilationCommandOutput with _StaticErrorOutput { + static void parseErrors(String stdout, List errors) { + _StaticErrorOutput._parseCfeErrors( + ErrorSource.web, _errorRegexp, stdout, errors); + } + /// Matches the location and message of a dart2js error message, which looks /// like: /// @@ -996,8 +1001,7 @@ class Dart2jsCompilerCommandOutput extends CompilationCommandOutput @override void _parseErrors() { var errors = []; - _StaticErrorOutput._parseCfeErrors( - ErrorSource.web, _errorRegexp, decodeUtf8(stdout), errors); + parseErrors(decodeUtf8(stdout), errors); errors.forEach(addError); } } diff --git a/pkg/test_runner/lib/src/options.dart b/pkg/test_runner/lib/src/options.dart index 14eb334bbd069..3ebeaee511197 100644 --- a/pkg/test_runner/lib/src/options.dart +++ b/pkg/test_runner/lib/src/options.dart @@ -504,7 +504,7 @@ compiler.''', case _OptionValueType.string: // Validate against the allowed values. - if (!option.values.isEmpty) { + if (option.values.isNotEmpty) { validate(String value) { if (!option.values.contains(value)) { _fail('Unknown value "$value" for option "$command".'); diff --git a/pkg/test_runner/lib/src/path.dart b/pkg/test_runner/lib/src/path.dart index 5c9003d8d9bbb..ecf982f102a25 100644 --- a/pkg/test_runner/lib/src/path.dart +++ b/pkg/test_runner/lib/src/path.dart @@ -200,7 +200,7 @@ class Path { var isAbs = isAbsolute; var segs = segments(); String drive; - if (isAbs && !segs.isEmpty && segs[0].length == 2 && segs[0][1] == ':') { + if (isAbs && segs.isNotEmpty && segs[0].length == 2 && segs[0][1] == ':') { drive = segs[0]; segs.removeRange(0, 1); } diff --git a/pkg/test_runner/lib/src/process_queue.dart b/pkg/test_runner/lib/src/process_queue.dart index 7be0d7481a418..6d7a58c911878 100644 --- a/pkg/test_runner/lib/src/process_queue.dart +++ b/pkg/test_runner/lib/src/process_queue.dart @@ -405,7 +405,7 @@ class CommandQueue { void _tryRunNextCommand() { _checkDone(); - if (_numProcesses < _maxProcesses && !_runQueue.isEmpty) { + if (_numProcesses < _maxProcesses && _runQueue.isNotEmpty) { var command = _runQueue.removeFirst(); var isBrowserCommand = command is BrowserTestCommand; diff --git a/pkg/test_runner/lib/src/reset_safari.dart b/pkg/test_runner/lib/src/reset_safari.dart index 3d5791021744d..212de216d5008 100644 --- a/pkg/test_runner/lib/src/reset_safari.dart +++ b/pkg/test_runner/lib/src/reset_safari.dart @@ -143,7 +143,7 @@ Future killSafari({Uri bundle}) async { if (result.exitCode == 0) { var stdout = result.stdout as String; var pids = - stdout.split("\n").where((String line) => !line.isEmpty).toList(); + stdout.split("\n").where((String line) => line.isNotEmpty).toList(); var timer = Timer(const Duration(seconds: 10), () { print("Kill -9 Safari $pids"); kill(pids, force: true); diff --git a/pkg/test_runner/lib/src/test_case.dart b/pkg/test_runner/lib/src/test_case.dart index fb4a908d38b83..159b6366e3b80 100644 --- a/pkg/test_runner/lib/src/test_case.dart +++ b/pkg/test_runner/lib/src/test_case.dart @@ -113,7 +113,7 @@ class TestCase { } CommandOutput get lastCommandOutput { - if (commandOutputs.length == 0) { + if (commandOutputs.isEmpty) { throw Exception("CommandOutputs is empty, maybe no command was run? (" "displayName: '$displayName', " "configurationString: '$configurationString')"); @@ -122,7 +122,7 @@ class TestCase { } Command get lastCommandExecuted { - if (commandOutputs.length == 0) { + if (commandOutputs.isEmpty) { throw Exception("CommandOutputs is empty, maybe no command was run? (" "displayName: '$displayName', " "configurationString: '$configurationString')"); diff --git a/pkg/test_runner/lib/src/test_configurations.dart b/pkg/test_runner/lib/src/test_configurations.dart index 93de2109ba0f8..dbd8d79ee25f1 100644 --- a/pkg/test_runner/lib/src/test_configurations.dart +++ b/pkg/test_runner/lib/src/test_configurations.dart @@ -254,7 +254,7 @@ Future testConfigurations(List configurations) async { } // Start all the HTTP servers required before starting the process queue. - if (!serverFutures.isEmpty) { + if (serverFutures.isNotEmpty) { await Future.wait(serverFutures); } diff --git a/pkg/test_runner/lib/src/test_suite.dart b/pkg/test_runner/lib/src/test_suite.dart index 2a2bfb4ac2317..02d1f3e2c7d42 100644 --- a/pkg/test_runner/lib/src/test_suite.dart +++ b/pkg/test_runner/lib/src/test_suite.dart @@ -595,14 +595,14 @@ class StandardTestSuite extends TestSuite { var commonArguments = _commonArgumentsFromFile(testFile); var vmOptionsList = getVmOptions(testFile); - assert(!vmOptionsList.isEmpty); + assert(vmOptionsList.isNotEmpty); for (var vmOptionsVariant = 0; vmOptionsVariant < vmOptionsList.length; vmOptionsVariant++) { var vmOptions = vmOptionsList[vmOptionsVariant]; var allVmOptions = vmOptions; - if (!extraVmOptions.isEmpty) { + if (extraVmOptions.isNotEmpty) { allVmOptions = vmOptions.toList()..addAll(extraVmOptions); } @@ -623,7 +623,7 @@ class StandardTestSuite extends TestSuite { var commonArguments = _commonArgumentsFromFile(testFile); var vmOptionsList = getVmOptions(testFile); - assert(!vmOptionsList.isEmpty); + assert(vmOptionsList.isNotEmpty); var emitDdsTest = false; for (var i = 0; i < 2; ++i) { @@ -632,7 +632,7 @@ class StandardTestSuite extends TestSuite { vmOptionsVariant++) { var vmOptions = vmOptionsList[vmOptionsVariant]; var allVmOptions = vmOptions; - if (!extraVmOptions.isEmpty) { + if (extraVmOptions.isNotEmpty) { allVmOptions = vmOptions.toList()..addAll(extraVmOptions); } if (emitDdsTest) { diff --git a/pkg/test_runner/lib/src/testing_servers.dart b/pkg/test_runner/lib/src/testing_servers.dart index f46956c3028dd..5e9df6842d3ec 100644 --- a/pkg/test_runner/lib/src/testing_servers.dart +++ b/pkg/test_runner/lib/src/testing_servers.dart @@ -241,7 +241,7 @@ class TestingServers { Uri _getFileUriFromRequestUri(Uri request) { // Go to the top of the file to see an explanation of the URL path scheme. var pathSegments = request.normalizePath().pathSegments; - if (pathSegments.length == 0) return null; + if (pathSegments.isEmpty) return null; var packagesIndex = pathSegments.indexOf('packages'); if (packagesIndex != -1) { var packageUri = Uri( diff --git a/pkg/test_runner/lib/src/vendored_pkg/args/src/parser.dart b/pkg/test_runner/lib/src/vendored_pkg/args/src/parser.dart index d3ca35683a55d..990dbf7cbc49f 100644 --- a/pkg/test_runner/lib/src/vendored_pkg/args/src/parser.dart +++ b/pkg/test_runner/lib/src/vendored_pkg/args/src/parser.dart @@ -50,7 +50,7 @@ class Parser { }); // Parse the args. - while (args.length > 0) { + while (args.isNotEmpty) { if (current == '--') { // Reached the argument terminator, so stop here. args.removeAt(0); @@ -98,7 +98,7 @@ class Parser { /// that there is a valid value there. void readNextArgAsValue(Option option) { // Take the option argument from the next command line arg. - validate(args.length > 0, 'Missing argument for "${option.name}".'); + validate(args.isNotEmpty, 'Missing argument for "${option.name}".'); // Make sure it isn't an option itself. validate(!_ABBR_OPT.hasMatch(current) && !_LONG_OPT.hasMatch(current), diff --git a/pkg/test_runner/lib/src/vendored_pkg/args/src/usage.dart b/pkg/test_runner/lib/src/vendored_pkg/args/src/usage.dart index 27504f39b1f54..60d4a9ee027b8 100644 --- a/pkg/test_runner/lib/src/vendored_pkg/args/src/usage.dart +++ b/pkg/test_runner/lib/src/vendored_pkg/args/src/usage.dart @@ -144,11 +144,11 @@ class Usage { var lines = text.split('\n'); // Strip leading and trailing empty lines. - while (lines.length > 0 && lines[0].trim() == '') { + while (lines.isNotEmpty && lines[0].trim() == '') { lines.removeRange(0, 1); } - while (lines.length > 0 && lines[lines.length - 1].trim() == '') { + while (lines.isNotEmpty && lines[lines.length - 1].trim() == '') { lines.removeLast(); } diff --git a/pkg/test_runner/tool/update_static_error_tests.dart b/pkg/test_runner/tool/update_static_error_tests.dart index eaa1bf84483c5..de25ab3424183 100644 --- a/pkg/test_runner/tool/update_static_error_tests.dart +++ b/pkg/test_runner/tool/update_static_error_tests.dart @@ -19,7 +19,9 @@ import 'package:test_runner/src/update_errors.dart'; const _usage = "Usage: dart update_static_error_tests.dart [flags...] "; -final String _analyzerPath = _findAnalyzer(); +final _dartPath = _findBinary("dart"); +final _analyzerPath = _findBinary("dartanalyzer"); +final _dart2jsPath = _findBinary("dart2js"); Future main(List args) async { var sources = ErrorSource.all.map((e) => e.marker).toList(); @@ -176,20 +178,31 @@ Future _processFile(File file, } } - if (insert.contains(ErrorSource.cfe)) { + // If we're inserting web errors, we also need to gather the CFE errors to + // tell which web errors are web-specific. + List cfeErrors; + if (insert.contains(ErrorSource.cfe) || insert.contains(ErrorSource.web)) { // Clear the previous line. stdout.write("\r${file.path} "); stdout.write("\r${file.path} (Running CFE...)"); - var fileErrors = await runCfe(file.absolute.path, options); - if (fileErrors == null) { + cfeErrors = await runCfe(file.absolute.path, options); + if (cfeErrors == null) { print("Error: failed to update ${file.path}"); - } else { - errors.addAll(fileErrors); + } else if (insert.contains(ErrorSource.cfe)) { + errors.addAll(cfeErrors); } } if (insert.contains(ErrorSource.web)) { - // TODO(rnystrom): Run DDC and collect web errors. + // Clear the previous line. + stdout.write("\r${file.path} "); + stdout.write("\r${file.path} (Running dart2js...)"); + var fileErrors = await runDart2js(file.absolute.path, options, cfeErrors); + if (fileErrors == null) { + print("Error: failed to update ${file.path}"); + } else { + errors.addAll(fileErrors); + } } errors = StaticError.simplify(errors); @@ -235,8 +248,7 @@ Future> runCfe(String path, List options) async { // TODO(rnystrom): Running the CFE command line each time is slow and wastes // time generating code, which we don't care about. Import it as a library or // at least run it in batch mode. - var result = await Process.run( - Platform.isWindows ? "sdk\\bin\\dart.bat" : "sdk/bin/dart", [ + var result = await Process.run(_dartPath, [ "pkg/front_end/tool/_fasta/compile.dart", ...options, "--verify", @@ -260,33 +272,63 @@ Future> runCfe(String path, List options) async { return errors; } -/// Find the most recently-built analyzer. -String _findAnalyzer() { - String newestAnalyzer; - DateTime newestAnalyzerTime; +/// Invoke dart2js on [path] and gather all static errors it reports. +Future> runDart2js( + String path, List options, List cfeErrors) async { + var result = await Process.run(_dart2jsPath, [ + ...options, + "-o", + "dev:null", // Output is only created for file URIs. + path, + ]); + + var errors = []; + Dart2jsCompilerCommandOutput.parseErrors(result.stdout as String, errors); + + // We only want the web-specific errors from dart2js, so filter out any errors + // that are also reported by the CFE. + errors.removeWhere((dart2jsError) { + return cfeErrors.any((cfeError) { + return dart2jsError.line == cfeError.line && + dart2jsError.column == cfeError.column && + dart2jsError.length == cfeError.length && + dart2jsError.errorFor(ErrorSource.web) == + cfeError.errorFor(ErrorSource.cfe); + }); + }); + + return errors; +} + +/// Find the most recently-built [binary] in any of the build directories. +String _findBinary(String binary) { + if (Platform.isWindows) binary += ".bat"; + + String newestPath; + DateTime newestTime; var buildDirectory = Directory(Platform.isMacOS ? "xcodebuild" : "out"); if (buildDirectory.existsSync()) { for (var config in buildDirectory.listSync()) { - var analyzerPath = p.join(config.path, "dart-sdk", "bin", "dartanalyzer"); + var analyzerPath = p.join(config.path, "dart-sdk", "bin", binary); var analyzerFile = File(analyzerPath); if (!analyzerFile.existsSync()) continue; var modified = analyzerFile.lastModifiedSync(); - if (newestAnalyzerTime == null || modified.isAfter(newestAnalyzerTime)) { - newestAnalyzer = analyzerPath; - newestAnalyzerTime = modified; + if (newestTime == null || modified.isAfter(newestTime)) { + newestPath = analyzerPath; + newestTime = modified; } } } - if (newestAnalyzer == null) { + if (newestPath == null) { // Clear the current line since we're in the middle of a progress line. print(""); - print("Could not find a built SDK with a dartanalyzer to run."); + print("Could not find a built SDK with a $binary to run."); print("Make sure to build the Dart SDK before running this tool."); exit(1); } - return newestAnalyzer; + return newestPath; } diff --git a/tests/co19/co19-co19.status b/tests/co19/co19-co19.status index 6c796d4f95840..a0fc6ddbd5976 100644 --- a/tests/co19/co19-co19.status +++ b/tests/co19/co19-co19.status @@ -58,26 +58,6 @@ LibTest/html/IFrameElement/*: Skip # Not migrated to NNBD LibTest/html/Node/*: Skip # Not migrated to NNBD LibTest/html/Window/*: Skip # Not migrated to NNBD LibTest/io/RawDatagramSocket/*: Skip # https://github.com/dart-lang/co19/issues/195 -LibTest/typed_data/*: Skip # Not migrated to NNBD -LibTest/typed_data/ByteBuffer/*: Skip # Not migrated to NNBD -LibTest/typed_data/ByteData/*: Skip # Not migrated to NNBD -LibTest/typed_data/Float32List/*: Skip # Not migrated to NNBD -LibTest/typed_data/Float32x4/*: Skip # Not migrated to NNBD -LibTest/typed_data/Float32x4List/*: Skip # Not migrated to NNBD -LibTest/typed_data/Float64List/*: Skip # Not migrated to NNBD -LibTest/typed_data/Float64x2/*: Skip # Not migrated to NNBD -LibTest/typed_data/Float64x2List/*: Skip # Not migrated to NNBD -LibTest/typed_data/Int16List/*: Skip # Not migrated to NNBD -LibTest/typed_data/Int32List/*: Skip # Not migrated to NNBD -LibTest/typed_data/Int32x4/*: Skip # Not migrated to NNBD -LibTest/typed_data/Int32x4List/*: Skip # Not migrated to NNBD -LibTest/typed_data/Int64List/*: Skip # Not migrated to NNBD -LibTest/typed_data/Int8List/*: Skip # Not migrated to NNBD -LibTest/typed_data/Uint16List/*: Skip # Not migrated to NNBD -LibTest/typed_data/Uint32List/*: Skip # Not migrated to NNBD -LibTest/typed_data/Uint64List/*: Skip # Not migrated to NNBD -LibTest/typed_data/Uint8ClampedList/*: Skip # Not migrated to NNBD -LibTest/typed_data/Uint8List/*: Skip # Not migrated to NNBD Utils/tests/Expect/*: Skip # Not migrated to NNBD [ $compiler == dart2analyzer ] diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status index fe8ad135dcdea..2cf6be3618d14 100644 --- a/tests/co19/co19-dart2js.status +++ b/tests/co19/co19-dart2js.status @@ -33,6 +33,24 @@ LibTest/core/int/remainder_A01_t03: SkipByDesign # Division by zero is not an er LibTest/io/*: SkipByDesign # dart:io not supported. LibTest/isolate/*: SkipByDesign # dart:isolate not supported. LibTest/mirrors/*: SkipByDesign # dart:mirrors is not supported +LibTest/typed_data/ByteBuffer/*: SkipByDesign # not supported on the web +LibTest/typed_data/ByteData/getInt64_A01_t01: Skip # Big integers cannot be represented in JS +LibTest/typed_data/ByteData/getInt64_A02_t01: Skip # Int64 accessor not supported by dart2js +LibTest/typed_data/ByteData/getInt64_A02_t02: Skip # Int64 accessor not supported by dart2js +LibTest/typed_data/ByteData/getUint64_A01_t01: Skip # Big integers cannot be represented in JS +LibTest/typed_data/ByteData/getUint64_A02_t01: Skip # Int64 accessor not supported by dart2js +LibTest/typed_data/ByteData/getUint64_A02_t02: Skip # Int64 accessor not supported by dart2js +LibTest/typed_data/ByteData/setInt64_A01_t01: Skip # Big integers cannot be represented in JS +LibTest/typed_data/ByteData/setInt64_A02_t01: Skip # Int64 accessor not supported by dart2js +LibTest/typed_data/ByteData/setInt64_A02_t02: Skip # Int64 accessor not supported by dart2js +LibTest/typed_data/ByteData/setUint64_A01_t01: Skip # Big integers cannot be represented in JS +LibTest/typed_data/ByteData/setUint64_A02_t01: Skip # Uint64 accessor not supported by dart2js +LibTest/typed_data/ByteData/setUint64_A02_t02: Skip # Uint64 accessor not supported by dart2js +LibTest/typed_data/Int32x4/operator_OR_A01_t01: Skip # Bitwise operations in JS are unsigned. +LibTest/typed_data/Int32x4List/join_A01_t01: Skip # Differen string represrntation on VM and in JS +LibTest/typed_data/Int32x4List/join_A01_t02: Skip # Differen string represrntation on VM and in JS +LibTest/typed_data/Int64List/*: SkipByDesign # Int64List not supported on the web +LibTest/typed_data/Uint64List/*: SkipByDesign # Uint64List not supported on the web [ $compiler == dart2js && $runtime == d8 ] LibTest/html/*: SkipByDesign # d8 is not a browser diff --git a/tests/co19/co19-dartdevc.status b/tests/co19/co19-dartdevc.status index 6388aaf14ccfb..23dd0d4948056 100644 --- a/tests/co19/co19-dartdevc.status +++ b/tests/co19/co19-dartdevc.status @@ -106,4 +106,21 @@ LibTest/html/IFrameElement/onTransitionEnd_A01_t01: Skip # Times out LibTest/io/*: SkipByDesign # dart:io not supported. LibTest/isolate/*: SkipByDesign # dart:isolate not supported. LibTest/mirrors/*: SkipByDesign # dart:mirrors is not supported - +LibTest/typed_data/ByteBuffer/*: SkipByDesign # not supported on the web +LibTest/typed_data/ByteData/getInt64_A01_t01: Skip # Big integers cannot be represented in JS +LibTest/typed_data/ByteData/getInt64_A02_t01: Skip # Int64 accessor not supported by dart2js +LibTest/typed_data/ByteData/getInt64_A02_t02: Skip # Int64 accessor not supported by dart2js +LibTest/typed_data/ByteData/getUint64_A01_t01: Skip # Big integers cannot be represented in JS +LibTest/typed_data/ByteData/getUint64_A02_t01: Skip # Int64 accessor not supported by dart2js +LibTest/typed_data/ByteData/getUint64_A02_t02: Skip # Int64 accessor not supported by dart2js +LibTest/typed_data/ByteData/setInt64_A01_t01: Skip # Big integers cannot be represented in JS +LibTest/typed_data/ByteData/setInt64_A02_t01: Skip # Int64 accessor not supported by dart2js +LibTest/typed_data/ByteData/setInt64_A02_t02: Skip # Int64 accessor not supported by dart2js +LibTest/typed_data/ByteData/setUint64_A01_t01: Skip # Big integers cannot be represented in JS +LibTest/typed_data/ByteData/setUint64_A02_t01: Skip # Uint64 accessor not supported by dart2js +LibTest/typed_data/ByteData/setUint64_A02_t02: Skip # Uint64 accessor not supported by dart2js +LibTest/typed_data/Int32x4/operator_OR_A01_t01: Skip # Bitwise operations in JS are unsigned. +LibTest/typed_data/Int32x4List/join_A01_t01: Skip # Differen string represrntation on VM and in JS +LibTest/typed_data/Int32x4List/join_A01_t02: Skip # Differen string represrntation on VM and in JS +LibTest/typed_data/Int64List/*: SkipByDesign # Int64List not supported on the web +LibTest/typed_data/Uint64List/*: SkipByDesign # Uint64List not supported on the web diff --git a/tools/VERSION b/tools/VERSION index 8359e448a4f99..2492ead049fbb 100644 --- a/tools/VERSION +++ b/tools/VERSION @@ -27,5 +27,5 @@ CHANNEL dev MAJOR 2 MINOR 10 PATCH 0 -PRERELEASE 78 +PRERELEASE 79 PRERELEASE_PATCH 0 \ No newline at end of file diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json index 5a01c59f17b11..d540f0fa56fdc 100644 --- a/tools/bots/test_matrix.json +++ b/tools/bots/test_matrix.json @@ -1373,7 +1373,7 @@ "co19" ], "fileset": "vm-kernel", - "shards": 4 + "shards": 6 }, { "name": "co19 nnbd tests in weak mode with asserts", @@ -1382,7 +1382,7 @@ "co19" ], "fileset": "vm-kernel", - "shards": 4 + "shards": 6 } ] },