Add a new warning when a colon is used as the separator for a default value

This also installs the existing fix for the new hint.

Change-Id: I32c4f3f50d1dfce4e2e37554cca7b81a84ca74d4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/254467
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Michael Thomsen <mit@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Brian Wilkerson 2022-11-07 21:20:27 +00:00 committed by Commit Queue
parent 8ef9c6aab4
commit 289aa509cd
27 changed files with 214 additions and 45 deletions

View file

@ -843,6 +843,10 @@ CompileTimeErrorCode.NULLABLE_TYPE_IN_WITH_CLAUSE:
status: hasFix
CompileTimeErrorCode.OBJECT_CANNOT_EXTEND_ANOTHER_CLASS:
status: noFix
CompileTimeErrorCode.OBSOLETE_COLON_FOR_DEFAULT_VALUE:
status: needsFix
notes: |-
ReplaceColonWithEquals should fix the issue, but it needs tests.
CompileTimeErrorCode.ON_REPEATED:
status: needsEvaluation
CompileTimeErrorCode.OPTIONAL_PARAMETER_IN_OPERATOR:
@ -1279,6 +1283,8 @@ HintCode.DEAD_CODE_CATCH_FOLLOWING_CATCH:
status: hasFix
HintCode.DEAD_CODE_ON_CATCH_SUBTYPE:
status: hasFix
HintCode.DEPRECATED_COLON_FOR_DEFAULT_VALUE:
status: hasFix
HintCode.DEPRECATED_EXPORT_USE:
status: needsEvaluation
HintCode.DEPRECATED_EXTENDS_FUNCTION:

View file

@ -1256,6 +1256,9 @@ class FixProcessor extends BaseProcessor {
// a place where it can be reached (when possible).
RemoveDeadCode.new,
],
HintCode.DEPRECATED_COLON_FOR_DEFAULT_VALUE: [
ReplaceColonWithEquals.new,
],
HintCode.DEPRECATED_NEW_IN_COMMENT_REFERENCE: [
RemoveDeprecatedNewInCommentReference.new,
],

View file

@ -452,7 +452,7 @@ void foo(int x) {}
void f() {
foo(ppp: 42);
}
int foo({int ppp: 0}) => ppp + 1;
int foo({int ppp = 0}) => ppp + 1;
''');
_createRefactoring(testCode.indexOf('42'), 0);
// check conditions
@ -542,7 +542,7 @@ void f() {
void f() {
foo(ppp: 42);
}
int foo({int ppp: 0}) => ppp + 1;
int foo({int ppp = 0}) => ppp + 1;
''');
_createRefactoring(testCode.indexOf('pp: 42'), 0);
// check conditions

View file

@ -1456,7 +1456,7 @@ class A {
Future<void> test_namedArgument_inBody() async {
await indexTestUnit(r'''
fa(pa) => fb(pb: true);
fb({pb: false}) {}
fb({pb = false}) {}
void f() {
fa(null);
}
@ -1465,7 +1465,7 @@ void f() {
// validate change
return _assertSuccessfulRefactoring(r'''
fa(pa) => fb(pb: true);
fb({pb: false}) {}
fb({pb = false}) {}
void f() {
fb(pb: true);
}
@ -1474,7 +1474,7 @@ void f() {
Future<void> test_namedArguments() async {
await indexTestUnit(r'''
test({a: 0, b: 2}) {
test({a = 0, b = 2}) {
print(a + b);
}
void f() {

View file

@ -165,17 +165,17 @@ class A {
test_checkFinalConditions_shadows_classMember_namedParameter() async {
await indexTestUnit('''
class A {
foo({test: 1}) { // in A
foo({test = 1}) { // in A
}
}
class B extends A {
var newName = 1;
foo({test: 1}) {
foo({test = 1}) {
print(newName);
}
}
''');
createRenameRefactoringAtString('test: 1}) { // in A');
createRenameRefactoringAtString('test = 1}) { // in A');
// check status
refactoring.newName = 'newName';
var status = await refactoring.checkFinalConditions();

View file

@ -105,14 +105,14 @@ void f() {
Future<void> test_function_hasNamed() async {
await resolveTestCode('''
test(int a, {int b: 0}) {}
test(int a, {int b = 0}) {}
void f() {
test(1, b: 2, named: 3.0);
}
''');
await assertHasFix('''
test(int a, {int b: 0, required double named}) {}
test(int a, {int b = 0, required double named}) {}
void f() {
test(1, b: 2, named: 3.0);
@ -157,7 +157,7 @@ void f() {
Future<void> test_method_hasNamed() async {
await resolveTestCode('''
class A {
test(int a, {int b: 0}) {}
test(int a, {int b = 0}) {}
void f() {
test(1, b: 2, named: 3.0);
@ -166,7 +166,7 @@ class A {
''');
await assertHasFix('''
class A {
test(int a, {int b: 0, required double named}) {}
test(int a, {int b = 0, required double named}) {}
void f() {
test(1, b: 2, named: 3.0);

View file

@ -363,7 +363,7 @@ abstract class A {
String m3(int p1, double p2, Map<int, List<String>> p3);
String m4(p1, p2);
String m5(p1, [int p2 = 2, int p3, p4 = 4]);
String m6(p1, {int p2 = 2, int p3, p4: 4});
String m6(p1, {int p2 = 2, int p3, p4 = 4});
}
class B extends A {
@ -376,7 +376,7 @@ abstract class A {
String m3(int p1, double p2, Map<int, List<String>> p3);
String m4(p1, p2);
String m5(p1, [int p2 = 2, int p3, p4 = 4]);
String m6(p1, {int p2 = 2, int p3, p4: 4});
String m6(p1, {int p2 = 2, int p3, p4 = 4});
}
class B extends A {

View file

@ -4,6 +4,7 @@
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server/src/services/linter/lint_names.dart';
import 'package:analyzer/src/dart/error/hint_codes.g.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@ -11,13 +12,15 @@ import 'fix_processor.dart';
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(ReplaceColonWithEqualsBulkLintTest);
defineReflectiveTests(ReplaceColonWithEqualsBulkTest);
defineReflectiveTests(ReplaceColonWithEqualsLintTest);
defineReflectiveTests(ReplaceColonWithEqualsTest);
});
}
@reflectiveTest
class ReplaceColonWithEqualsBulkTest extends BulkFixProcessorTest {
class ReplaceColonWithEqualsBulkLintTest extends BulkFixProcessorTest {
@override
String get lintCode => LintNames.prefer_equal_for_default_values;
@ -40,7 +43,27 @@ class C {
}
@reflectiveTest
class ReplaceColonWithEqualsTest extends FixProcessorLintTest {
class ReplaceColonWithEqualsBulkTest extends BulkFixProcessorTest {
Future<void> test_singleFile() async {
await resolveTestCode('''
void f({int a: 1}) => null;
class C {
void m({int a: 1, int b: 2}) => null;
}
''');
await assertHasFix('''
void f({int a = 1}) => null;
class C {
void m({int a = 1, int b = 2}) => null;
}
''');
}
}
@reflectiveTest
class ReplaceColonWithEqualsLintTest extends FixProcessorLintTest {
@override
FixKind get kind => DartFixKind.REPLACE_COLON_WITH_EQUALS;
@ -53,6 +76,44 @@ void f({int a: 1}) => null;
''');
await assertHasFix('''
void f({int a = 1}) => null;
''',
errorFilter: (error) =>
error.errorCode != HintCode.DEPRECATED_COLON_FOR_DEFAULT_VALUE);
}
Future<void> test_superParameter() async {
await resolveTestCode('''
class C {
C({int? i});
}
class D extends C {
D({int? super.i: 1});
}
''');
await assertHasFix('''
class C {
C({int? i});
}
class D extends C {
D({int? super.i = 1});
}
''',
errorFilter: (error) =>
error.errorCode != HintCode.DEPRECATED_COLON_FOR_DEFAULT_VALUE);
}
}
@reflectiveTest
class ReplaceColonWithEqualsTest extends FixProcessorTest {
@override
FixKind get kind => DartFixKind.REPLACE_COLON_WITH_EQUALS;
Future<void> test_method() async {
await resolveTestCode('''
void f({int a: 1}) => null;
''');
await assertHasFix('''
void f({int a = 1}) => null;
''');
}

View file

@ -117,6 +117,14 @@ class HintCode extends AnalyzerErrorCode {
hasPublishedDocs: true,
);
/// No parameters.
static const HintCode DEPRECATED_COLON_FOR_DEFAULT_VALUE = HintCode(
'DEPRECATED_COLON_FOR_DEFAULT_VALUE',
"Using a colon as a separator before a default value is deprecated and "
"will\nnot be supported in language version 3.0 and later.",
correctionMessage: "Try replacing the colon with an equal sign.",
);
/// Parameters:
/// 0: the name of the element
static const HintCode DEPRECATED_EXPORT_USE = HintCode(

View file

@ -455,6 +455,25 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
super.visitConstructorName(node);
}
@override
void visitDefaultFormalParameter(DefaultFormalParameter node) {
var separator = node.separator;
if (node.isNamed &&
separator != null &&
separator.type == TokenType.COLON) {
// This is a warning in code whose language version is < 3.0, but an error
// in code whose language version is >= 3.0.
if (_currentLibrary.languageVersion.effective.major < 3) {
_errorReporter.reportErrorForToken(
HintCode.DEPRECATED_COLON_FOR_DEFAULT_VALUE, separator);
} else {
_errorReporter.reportErrorForToken(
CompileTimeErrorCode.OBSOLETE_COLON_FOR_DEFAULT_VALUE, separator);
}
}
super.visitDefaultFormalParameter(node);
}
@override
void visitExportDirective(ExportDirective node) {
_deprecatedVerifier.exportDirective(node);

View file

@ -3579,6 +3579,15 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
"The class 'Object' can't extend any other class.",
);
/// No parameters.
static const CompileTimeErrorCode OBSOLETE_COLON_FOR_DEFAULT_VALUE =
CompileTimeErrorCode(
'OBSOLETE_COLON_FOR_DEFAULT_VALUE',
"Using a colon as a separator before a default value is no longer "
"supported.",
correctionMessage: "Try replacing the colon with an equal sign.",
);
/// Parameters:
/// 0: the name of the interface that is implemented more than once
static const CompileTimeErrorCode ON_REPEATED = CompileTimeErrorCode(

View file

@ -364,6 +364,7 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.NULLABLE_TYPE_IN_ON_CLAUSE,
CompileTimeErrorCode.NULLABLE_TYPE_IN_WITH_CLAUSE,
CompileTimeErrorCode.OBJECT_CANNOT_EXTEND_ANOTHER_CLASS,
CompileTimeErrorCode.OBSOLETE_COLON_FOR_DEFAULT_VALUE,
CompileTimeErrorCode.ON_REPEATED,
CompileTimeErrorCode.OPTIONAL_PARAMETER_IN_OPERATOR,
CompileTimeErrorCode.PART_OF_DIFFERENT_LIBRARY,
@ -547,6 +548,7 @@ const List<ErrorCode> errorCodeValues = [
HintCode.DEAD_CODE,
HintCode.DEAD_CODE_CATCH_FOLLOWING_CATCH,
HintCode.DEAD_CODE_ON_CATCH_SUBTYPE,
HintCode.DEPRECATED_COLON_FOR_DEFAULT_VALUE,
HintCode.DEPRECATED_EXPORT_USE,
HintCode.DEPRECATED_EXTENDS_FUNCTION,
HintCode.DEPRECATED_IMPLEMENTS_FUNCTION,

View file

@ -10895,6 +10895,10 @@ CompileTimeErrorCode:
comment: |-
7.9 Superclasses: It is a compile-time error to specify an extends clause
for class Object.
OBSOLETE_COLON_FOR_DEFAULT_VALUE:
problemMessage: "Using a colon as a separator before a default value is no longer supported."
correctionMessage: "Try replacing the colon with an equal sign."
comment: No parameters.
ON_REPEATED:
problemMessage: "The type '{0}' can be included in the superclass constraints only once."
correctionMessage: Try removing all except one occurrence of the type name.
@ -17835,6 +17839,12 @@ HintCode:
}
}
```
DEPRECATED_COLON_FOR_DEFAULT_VALUE:
problemMessage: |-
Using a colon as a separator before a default value is deprecated and will
not be supported in language version 3.0 and later.
correctionMessage: "Try replacing the colon with an equal sign."
comment: No parameters.
DEPRECATED_EXPORT_USE:
problemMessage: "The ability to import '{0}' indirectly has been deprecated."
correctionMessage: "Try importing '{0}' directly."

View file

@ -2508,7 +2508,7 @@ f([a = double.infinity]) {
test_nonConstantDefaultValue_function_named() async {
await assertNoErrorsInCode('''
f({x : 2 + 3}) {}
f({x = 2 + 3}) {}
''');
}
@ -2521,7 +2521,7 @@ f([x = 2 + 3]) {}
test_nonConstantDefaultValue_inConstructor_named() async {
await assertNoErrorsInCode(r'''
class A {
A({x : 2 + 3}) {}
A({x = 2 + 3}) {}
}
''');
}
@ -2537,7 +2537,7 @@ class A {
test_nonConstantDefaultValue_method_named() async {
await assertNoErrorsInCode(r'''
class A {
m({x : 2 + 3}) {}
m({x = 2 + 3}) {}
}
''');
}

View file

@ -1122,7 +1122,7 @@ const A = null;
test_metadata_namedParameter() async {
await assertNoErrorsInCode(r'''
const A = null;
f({@A int p : 0}) {}''');
f({@A int p = 0}) {}''');
verifyTestResolved();
var metadata = findElement.parameter('p').metadata;

View file

@ -1891,7 +1891,7 @@ class B {}
newFile(b, r'''
import 'a.dart';
main() {
foo({int p: C}) {}
foo({int p = C}) {}
foo();
}
''');

View file

@ -374,7 +374,7 @@ class A {
final bool f1;
final bool f2;
const A({this.f1: false}) : this.f2 = f1 && true;
const A({this.f1 = false}) : this.f2 = f1 && true;
}
''');

View file

@ -18,9 +18,9 @@ main() {
class DefaultValueInFunctionTypeTest extends PubPackageResolutionTest {
test_new_named() async {
await assertErrorsInCode('''
typedef F = int Function({Map<String, String> m: const {}});
typedef F = int Function({Map<String, String> m = const {}});
''', [
error(ParserErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPE, 47, 1),
error(ParserErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPE, 48, 1),
]);
}
@ -28,10 +28,10 @@ typedef F = int Function({Map<String, String> m: const {}});
// Test that the strong checker does not crash when given an ambiguous set
// or map literal.
await assertErrorsInCode('''
typedef F = int Function({Object m: const {1, 2: 3}});
typedef F = int Function({Object m = const {1, 2: 3}});
''', [
error(ParserErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPE, 34, 1),
error(CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH, 36, 15),
error(ParserErrorCode.DEFAULT_VALUE_IN_FUNCTION_TYPE, 35, 1),
error(CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH, 37, 15),
]);
}

View file

@ -17,7 +17,7 @@ main() {
class DefaultValueOnRequiredParameterTest extends PubPackageResolutionTest {
test_function_notRequired_default() async {
await assertNoErrorsInCode('''
void log({String message: 'no message'}) {}
void log({String message = 'no message'}) {}
''');
}
@ -29,7 +29,7 @@ void log({String? message}) {}
test_function_required_default() async {
await assertErrorsInCode('''
void log({required String? message: 'no message'}) {}
void log({required String? message = 'no message'}) {}
''', [
error(CompileTimeErrorCode.DEFAULT_VALUE_ON_REQUIRED_PARAMETER, 27, 7),
]);

View file

@ -0,0 +1,48 @@
// Copyright (c) 2020, 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.g.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(DeprecatedColonForDefaultValueTest);
});
}
@reflectiveTest
class DeprecatedColonForDefaultValueTest extends PubPackageResolutionTest {
test_noDefault() async {
await assertNoErrorsInCode('''
void f({int? x}) {}
''');
}
test_superFormalParameter() async {
await assertErrorsInCode('''
class A {
String? a;
A({this.a});
}
class B extends A {
B({super.a : ''});
}
''', [error(HintCode.DEPRECATED_COLON_FOR_DEFAULT_VALUE, 74, 1)]);
}
test_usesColon() async {
await assertErrorsInCode('''
void f({int x : 0}) {}
''', [error(HintCode.DEPRECATED_COLON_FOR_DEFAULT_VALUE, 14, 1)]);
}
test_usesEqual() async {
await assertNoErrorsInCode('''
void f({int x = 0}) {}
''');
}
}

View file

@ -564,16 +564,16 @@ void main() {
test_defaultValue_named() async {
await assertErrorsInCode(r'''
f({String x: 0}) {
f({String x = 0}) {
}
''', [
error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 13, 1),
error(CompileTimeErrorCode.INVALID_ASSIGNMENT, 14, 1),
]);
}
test_defaultValue_named_sameType() async {
await assertNoErrorsInCode(r'''
f({String x: '0'}) {
f({String x = '0'}) {
}''');
}

View file

@ -24,7 +24,7 @@ const V = 1;
await assertErrorsInCode('''
library root;
import 'lib1.dart' deferred as a;
f({x : a.V}) {}
f({x = a.V}) {}
''', [
error(
CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE_FROM_DEFERRED_LIBRARY,
@ -41,7 +41,7 @@ const V = 1;
await assertErrorsInCode('''
library root;
import 'lib1.dart' deferred as a;
f({x : a.V + 1}) {}
f({x = a.V + 1}) {}
''', [
error(
CompileTimeErrorCode.NON_CONSTANT_DEFAULT_VALUE_FROM_DEFERRED_LIBRARY,

View file

@ -19,7 +19,7 @@ class PrivateOptionalParameterTest extends PubPackageResolutionTest {
await assertErrorsInCode(r'''
class A {
var _p;
A({this._p: 0});
A({this._p = 0});
}
''', [
error(HintCode.UNUSED_FIELD, 16, 2),
@ -37,7 +37,7 @@ f({var _p}) {}
test_withDefaultValue() async {
await assertErrorsInCode('''
f({_p : 0}) {}
f({_p = 0}) {}
''', [
error(CompileTimeErrorCode.PRIVATE_OPTIONAL_PARAMETER, 3, 2),
]);

View file

@ -140,6 +140,8 @@ import 'default_value_on_required_parameter_test.dart'
import 'deferred_import_of_extension_test.dart' as deferred_import_of_extension;
import 'definitely_unassigned_late_local_variable_test.dart'
as definitely_unassigned_late_local_variable;
import 'deprecated_colon_for_default_value_test.dart'
as deprecated_colon_for_default_value;
import 'deprecated_export_use_test.dart' as deprecated_export_use;
import 'deprecated_extends_function_test.dart' as deprecated_extends_function;
import 'deprecated_implements_function_test.dart'
@ -901,6 +903,7 @@ main() {
default_value_on_required_parameter.main();
deferred_import_of_extension.main();
definitely_unassigned_late_local_variable.main();
deprecated_colon_for_default_value.main();
deprecated_export_use.main();
deprecated_extends_function.main();
deprecated_implements_function.main();

View file

@ -1849,11 +1849,11 @@ void df1([dynamic x = DYNAMIC_VALUE]) {}
void df2([x = 42]) {}
// default formal (named)
void nf0({x: DYNAMIC_VALUE}) {}
void nf1({dynamic x: DYNAMIC_VALUE}) {}
void nf0({x = DYNAMIC_VALUE}) {}
void nf1({dynamic x = DYNAMIC_VALUE}) {}
// https://github.com/dart-lang/sdk/issues/25794
void nf2({x: 42}) {}
void nf2({x = 42}) {}
// field formal
class C {
@ -1869,9 +1869,9 @@ void ftf1(void x(int y)) {}
error(LanguageCode.IMPLICIT_DYNAMIC_PARAMETER, 117, 1),
error(LanguageCode.IMPLICIT_DYNAMIC_PARAMETER, 241, 1),
error(LanguageCode.IMPLICIT_DYNAMIC_PARAMETER, 290, 1),
error(LanguageCode.IMPLICIT_DYNAMIC_PARAMETER, 412, 1),
error(LanguageCode.IMPLICIT_DYNAMIC_FIELD, 456, 1),
error(LanguageCode.IMPLICIT_DYNAMIC_PARAMETER, 517, 1),
error(LanguageCode.IMPLICIT_DYNAMIC_PARAMETER, 414, 1),
error(LanguageCode.IMPLICIT_DYNAMIC_FIELD, 459, 1),
error(LanguageCode.IMPLICIT_DYNAMIC_PARAMETER, 520, 1),
]);
}

View file

@ -3686,7 +3686,7 @@ void h() {
Future<void> test_functionInvocation_parameter_named() async {
await analyze('''
void f({int i: 0}) {}
void f({int i = 0}) {}
void g(int j) {
f(i: j/*check*/);
}
@ -5148,7 +5148,7 @@ void g(C<int/*3*/>/*4*/ c) {
Future<void> test_methodInvocation_parameter_named() async {
await analyze('''
class C {
void f({int i: 0}) {}
void f({int i = 0}) {}
}
void g(C c, int j) {
c.f(i: j/*check*/);

View file

@ -1860,7 +1860,7 @@ void f({/*required*/ String s}) {}
Future<void> test_topLevelFunction_parameterType_named_with_default() async {
await analyze('''
void f({String s: 'x'}) {}
void f({String s = 'x'}) {}
''');
var decoratedType = decoratedTypeAnnotation('String');
var functionType = decoratedFunctionType('f');