Add new unnecessary_const_in_enum_constructor lint
This adds a new lint that was requested in order to support the primary constructors feature. This also enables the existing fix to remove the keyword and adds some tests for the new use of the fix. Change-Id: Iae3c86ca87a2af6abb82488e218d839a06699778 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/496741 Reviewed-by: Keerti Parthasarathy <keertip@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
291d8ae07d
commit
78412eaeb8
@@ -2467,6 +2467,8 @@ unnecessary_breaks:
|
||||
status: hasFix
|
||||
unnecessary_const:
|
||||
status: hasFix
|
||||
unnecessary_const_in_enum_constructor:
|
||||
status: hasFix
|
||||
unnecessary_constructor_name:
|
||||
status: hasFix
|
||||
unnecessary_final_with_type:
|
||||
|
||||
@@ -439,6 +439,7 @@ final _builtInLintGenerators = <DiagnosticCode, List<ProducerGenerator>>{
|
||||
diag.unnecessaryBraceInStringInterps: [RemoveInterpolationBraces.new],
|
||||
diag.unnecessaryBreaks: [RemoveBreak.new],
|
||||
diag.unnecessaryConst: [RemoveUnnecessaryConst.new],
|
||||
diag.unnecessaryConstInEnumConstructor: [RemoveUnnecessaryConst.new],
|
||||
diag.unnecessaryConstructorName: [RemoveConstructorName.new],
|
||||
diag.unnecessaryFinalWithType: [ReplaceFinalWithVar.new],
|
||||
diag.unnecessaryFinalWithoutType: [ReplaceFinalWithVar.new],
|
||||
|
||||
+79
@@ -11,6 +11,8 @@ import 'fix_processor.dart';
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(RemoveUnnecessaryConstBulkTest);
|
||||
defineReflectiveTests(RemoveUnnecessaryConstInEnumConstructorBulkTest);
|
||||
defineReflectiveTests(RemoveUnnecessaryConstInEnumConstructorTest);
|
||||
defineReflectiveTests(RemoveUnnecessaryConstTest);
|
||||
});
|
||||
}
|
||||
@@ -38,6 +40,83 @@ var d = const D(C());
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class RemoveUnnecessaryConstInEnumConstructorBulkTest
|
||||
extends BulkFixProcessorTest {
|
||||
@override
|
||||
String get lintCode => LintNames.unnecessary_const_in_enum_constructor;
|
||||
|
||||
Future<void> test_singleFile() async {
|
||||
await resolveTestCode('''
|
||||
enum const E1(final int i) {
|
||||
a(1), b(2);
|
||||
}
|
||||
enum E2 {
|
||||
a(1), b(2);
|
||||
|
||||
const E2(this.i);
|
||||
|
||||
final int i;
|
||||
}
|
||||
''');
|
||||
await assertHasFix('''
|
||||
enum E1(final int i) {
|
||||
a(1), b(2);
|
||||
}
|
||||
enum E2 {
|
||||
a(1), b(2);
|
||||
|
||||
E2(this.i);
|
||||
|
||||
final int i;
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class RemoveUnnecessaryConstInEnumConstructorTest extends FixProcessorLintTest {
|
||||
@override
|
||||
FixKind get kind => DartFixKind.removeUnnecessaryConst;
|
||||
|
||||
@override
|
||||
String get lintCode => LintNames.unnecessary_const_in_enum_constructor;
|
||||
|
||||
Future<void> test_enumConstructor_primary() async {
|
||||
await resolveTestCode('''
|
||||
enum const E(final int i) {
|
||||
a(1), b(2);
|
||||
}
|
||||
''');
|
||||
await assertHasFix('''
|
||||
enum E(final int i) {
|
||||
a(1), b(2);
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_enumConstructor_secondary() async {
|
||||
await resolveTestCode('''
|
||||
enum E {
|
||||
a(1), b(2);
|
||||
|
||||
const E(this.i);
|
||||
|
||||
final int i;
|
||||
}
|
||||
''');
|
||||
await assertHasFix('''
|
||||
enum E {
|
||||
a(1), b(2);
|
||||
|
||||
E(this.i);
|
||||
|
||||
final int i;
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class RemoveUnnecessaryConstTest extends FixProcessorLintTest {
|
||||
@override
|
||||
|
||||
@@ -187,6 +187,7 @@ linter:
|
||||
- unnecessary_brace_in_string_interps
|
||||
- unnecessary_breaks
|
||||
- unnecessary_const
|
||||
- unnecessary_const_in_enum_constructor
|
||||
- unnecessary_constructor_name
|
||||
- unnecessary_final
|
||||
- unnecessary_getters_setters
|
||||
|
||||
@@ -3108,6 +3108,16 @@ const LinterLintWithoutArguments unnecessaryConst = LinterLintWithoutArguments(
|
||||
expectedTypes: [],
|
||||
);
|
||||
|
||||
/// No parameters.
|
||||
const LinterLintWithoutArguments unnecessaryConstInEnumConstructor =
|
||||
LinterLintWithoutArguments(
|
||||
name: 'unnecessary_const_in_enum_constructor',
|
||||
problemMessage: "Unnecessary 'const' keyword in an enum constructor.",
|
||||
correctionMessage: "Try removing the keyword.",
|
||||
uniqueName: 'unnecessary_const_in_enum_constructor',
|
||||
expectedTypes: [],
|
||||
);
|
||||
|
||||
/// No parameters.
|
||||
const LinterLintWithoutArguments unnecessaryConstructorName =
|
||||
LinterLintWithoutArguments(
|
||||
|
||||
@@ -529,6 +529,9 @@ abstract final class LintNames {
|
||||
|
||||
static const String unnecessary_const = 'unnecessary_const';
|
||||
|
||||
static const String unnecessary_const_in_enum_constructor =
|
||||
'unnecessary_const_in_enum_constructor';
|
||||
|
||||
static const String unnecessary_constructor_name =
|
||||
'unnecessary_constructor_name';
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ import 'rules/unnecessary_await_in_return.dart';
|
||||
import 'rules/unnecessary_brace_in_string_interps.dart';
|
||||
import 'rules/unnecessary_breaks.dart';
|
||||
import 'rules/unnecessary_const.dart';
|
||||
import 'rules/unnecessary_const_in_enum_constructor.dart';
|
||||
import 'rules/unnecessary_constructor_name.dart';
|
||||
import 'rules/unnecessary_final.dart';
|
||||
import 'rules/unnecessary_getters_setters.dart';
|
||||
@@ -466,6 +467,7 @@ void registerLintRules() {
|
||||
..registerLintRule(UnnecessaryBraceInStringInterps())
|
||||
..registerLintRule(UnnecessaryBreaks())
|
||||
..registerLintRule(UnnecessaryConst())
|
||||
..registerLintRule(UnnecessaryConstInEnumConstructor())
|
||||
..registerLintRule(UnnecessaryConstructorName())
|
||||
..registerLintRule(UnnecessaryFinal())
|
||||
..registerLintRule(UnnecessaryGettersSetters())
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2026, 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/analysis_rule/analysis_rule.dart';
|
||||
import 'package:analyzer/analysis_rule/rule_context.dart';
|
||||
import 'package:analyzer/analysis_rule/rule_visitor_registry.dart';
|
||||
import 'package:analyzer/dart/analysis/features.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/ast/visitor.dart';
|
||||
import 'package:analyzer/error/error.dart';
|
||||
|
||||
import '../analyzer.dart';
|
||||
import '../diagnostic.dart' as diag;
|
||||
|
||||
const _desc = "Don't use an explicit `const` in a generative enum constructor.";
|
||||
|
||||
class UnnecessaryConstInEnumConstructor extends AnalysisRule {
|
||||
UnnecessaryConstInEnumConstructor()
|
||||
: super(
|
||||
name: LintNames.unnecessary_const_in_enum_constructor,
|
||||
description: _desc,
|
||||
);
|
||||
|
||||
@override
|
||||
bool get canUseParsedResult => true;
|
||||
|
||||
@override
|
||||
DiagnosticCode get diagnosticCode => diag.unnecessaryConstInEnumConstructor;
|
||||
|
||||
@override
|
||||
void registerNodeProcessors(
|
||||
RuleVisitorRegistry registry,
|
||||
RuleContext context,
|
||||
) {
|
||||
if (!context.isFeatureEnabled(Feature.primary_constructors)) {
|
||||
return;
|
||||
}
|
||||
var visitor = _Visitor(this);
|
||||
registry.addConstructorDeclaration(this, visitor);
|
||||
registry.addPrimaryConstructorDeclaration(this, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
class _Visitor extends SimpleAstVisitor<void> {
|
||||
final AnalysisRule rule;
|
||||
_Visitor(this.rule);
|
||||
|
||||
@override
|
||||
void visitConstructorDeclaration(ConstructorDeclaration node) {
|
||||
var constKeyword = node.constKeyword;
|
||||
if (constKeyword != null) {
|
||||
rule.reportAtToken(constKeyword);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void visitPrimaryConstructorDeclaration(PrimaryConstructorDeclaration node) {
|
||||
var constKeyword = node.constKeyword;
|
||||
if (constKeyword != null) {
|
||||
rule.reportAtToken(constKeyword);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13759,6 +13759,106 @@ LinterLintCode:
|
||||
final b = const [A()];
|
||||
}
|
||||
```
|
||||
unnecessaryConstInEnumConstructor:
|
||||
type: lint
|
||||
parameters: none
|
||||
problemMessage: "Unnecessary 'const' keyword in an enum constructor."
|
||||
correctionMessage: "Try removing the keyword."
|
||||
state:
|
||||
experimental: "3.14"
|
||||
categories: [brevity, style]
|
||||
hasPublishedDocs: false
|
||||
documentation: |-
|
||||
#### Description
|
||||
|
||||
The analyzer produces this diagnostic when the keyword `const` is used in
|
||||
a generative enum constructor. Generative num constructors are implicitly
|
||||
`const`.
|
||||
|
||||
#### Examples
|
||||
|
||||
The following code produces this diagnostic because the keyword `const` in
|
||||
the enum's primary constructor isn't needed:
|
||||
|
||||
```dart
|
||||
%experiments=primary-constructors
|
||||
enum [!const!] E(final int i) {
|
||||
a(1), b(2);
|
||||
}
|
||||
```
|
||||
|
||||
The following code produces this diagnostic because the keyword `const` in
|
||||
the enum's secondary constructor isn't needed:
|
||||
|
||||
```dart
|
||||
%experiments=primary-constructors
|
||||
enum E {
|
||||
a(1), b(2);
|
||||
|
||||
[!const!] E(this.i);
|
||||
|
||||
final int i;
|
||||
}
|
||||
```
|
||||
|
||||
#### Common fixes
|
||||
|
||||
Remove the unnecessary keyword:
|
||||
|
||||
```dart
|
||||
%experiments=primary-constructors
|
||||
enum E(final int i) {
|
||||
a(1), b(2);
|
||||
}
|
||||
```
|
||||
|
||||
```dart
|
||||
%experiments=primary-constructors
|
||||
enum E {
|
||||
a(1), b(2);
|
||||
|
||||
E(this.i);
|
||||
|
||||
final int i;
|
||||
}
|
||||
```
|
||||
deprecatedDetails: |-
|
||||
Don't use an explicit `const` in a generative enum constructor. Generative
|
||||
enum constructors are implicitly `const`.
|
||||
|
||||
**BAD:**
|
||||
```dart
|
||||
enum const E(final int i) {
|
||||
a(1), b(2);
|
||||
}
|
||||
```
|
||||
|
||||
```dart
|
||||
enum E {
|
||||
a(1), b(2);
|
||||
|
||||
const E(this.i);
|
||||
|
||||
final int i;
|
||||
}
|
||||
```
|
||||
|
||||
**GOOD:**
|
||||
```dart
|
||||
enum E(final int i) {
|
||||
a(1), b(2);
|
||||
}
|
||||
```
|
||||
|
||||
```dart
|
||||
enum E {
|
||||
a(1), b(2);
|
||||
|
||||
E(this.i);
|
||||
|
||||
final int i;
|
||||
}
|
||||
```
|
||||
unnecessaryConstructorName:
|
||||
type: lint
|
||||
parameters: none
|
||||
|
||||
@@ -265,6 +265,8 @@ import 'unnecessary_await_in_return_test.dart' as unnecessary_await_in_return;
|
||||
import 'unnecessary_brace_in_string_interps_test.dart'
|
||||
as unnecessary_brace_in_string_interps;
|
||||
import 'unnecessary_breaks_test.dart' as unnecessary_breaks;
|
||||
import 'unnecessary_const_in_enum_constructor_test.dart'
|
||||
as unnecessary_const_in_enum_constructor;
|
||||
import 'unnecessary_const_test.dart' as unnecessary_const;
|
||||
import 'unnecessary_constructor_name_test.dart' as unnecessary_constructor_name;
|
||||
import 'unnecessary_final_test.dart' as unnecessary_final;
|
||||
@@ -528,6 +530,7 @@ void main() {
|
||||
unnecessary_brace_in_string_interps.main();
|
||||
unnecessary_breaks.main();
|
||||
unnecessary_const.main();
|
||||
unnecessary_const_in_enum_constructor.main();
|
||||
unnecessary_constructor_name.main();
|
||||
unnecessary_final.main();
|
||||
unnecessary_getters_setters.main();
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2026, 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:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../rule_test_support.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(UnnecessaryConstInEnumConstructorTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class UnnecessaryConstInEnumConstructorTest extends LintRuleTest {
|
||||
@override
|
||||
String get lintRule => LintNames.unnecessary_const_in_enum_constructor;
|
||||
|
||||
test_primary() async {
|
||||
await assertDiagnosticsFromMarkdown(r'''
|
||||
enum [!const!] E(final int i) {
|
||||
a(1), b(2);
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_secondary() async {
|
||||
await assertDiagnosticsFromMarkdown(r'''
|
||||
enum E {
|
||||
a(1), b(2);
|
||||
|
||||
[!const!] E(this.i);
|
||||
|
||||
final int i;
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user