Skip to content

Commit e120159

Browse files
authored
+ unnecessary_breaks (dart-archive/linter#3981)
* + unnecessary_breaks * ++ * -- * feedback * typo * + pattern feature checking
1 parent 0a672f5 commit e120159

File tree

5 files changed

+216
-0
lines changed

5 files changed

+216
-0
lines changed

example/all.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ linter:
167167
- unawaited_futures
168168
- unnecessary_await_in_return
169169
- unnecessary_brace_in_string_interps
170+
- unnecessary_breaks
170171
- unnecessary_const
171172
- unnecessary_constructor_name
172173
- unnecessary_final

lib/src/rules.dart

+2
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ import 'rules/type_init_formals.dart';
176176
import 'rules/unawaited_futures.dart';
177177
import 'rules/unnecessary_await_in_return.dart';
178178
import 'rules/unnecessary_brace_in_string_interps.dart';
179+
import 'rules/unnecessary_breaks.dart';
179180
import 'rules/unnecessary_const.dart';
180181
import 'rules/unnecessary_constructor_name.dart';
181182
import 'rules/unnecessary_final.dart';
@@ -399,6 +400,7 @@ void registerLintRules({bool inTestMode = false}) {
399400
..register(UnawaitedFutures())
400401
..register(UnnecessaryAwaitInReturn())
401402
..register(UnnecessaryBraceInStringInterps())
403+
..register(UnnecessaryBreaks())
402404
..register(UnnecessaryConst())
403405
..register(UnnecessaryConstructorName())
404406
..register(UnnecessaryFinal())

lib/src/rules/unnecessary_breaks.dart

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/dart/analysis/features.dart';
6+
import 'package:analyzer/dart/ast/ast.dart';
7+
import 'package:analyzer/dart/ast/visitor.dart';
8+
9+
import '../analyzer.dart';
10+
11+
const _desc = r"Don't use explicit `break`s when a break is implied.";
12+
13+
const _details = r'''
14+
Only use a `break` in a non-empty switch case statement if you need to break
15+
before the end of the case body. Dart does not support fallthrough execution
16+
for non-empty cases, so `break`s at the end of non-empty switch case statements
17+
are unnecessary.
18+
19+
**BAD:**
20+
```dart
21+
switch (1) {
22+
case 1:
23+
print("one");
24+
break;
25+
case 2:
26+
print("two");
27+
break;
28+
}
29+
```
30+
31+
**GOOD:**
32+
```dart
33+
switch (1) {
34+
case 1:
35+
print("one");
36+
case 2:
37+
print("two");
38+
}
39+
```
40+
41+
```dart
42+
switch (1) {
43+
case 1:
44+
case 2:
45+
print("one or two");
46+
}
47+
```
48+
49+
```dart
50+
switch (1) {
51+
case 1:
52+
break;
53+
case 2:
54+
print("just two");
55+
}
56+
```
57+
''';
58+
59+
class UnnecessaryBreaks extends LintRule {
60+
static const LintCode code = LintCode(
61+
'unnecessary_breaks', "Unnecessary 'break' statement.",
62+
correctionMessage: "Try removing the 'break'.");
63+
64+
UnnecessaryBreaks()
65+
: super(
66+
name: 'unnecessary_breaks',
67+
description: _desc,
68+
details: _details,
69+
group: Group.style);
70+
71+
@override
72+
LintCode get lintCode => code;
73+
74+
@override
75+
void registerNodeProcessors(
76+
NodeLintRegistry registry, LinterContext context) {
77+
if (!context.isEnabled(Feature.patterns)) return;
78+
var visitor = _Visitor(this);
79+
registry.addBreakStatement(this, visitor);
80+
}
81+
}
82+
83+
class _Visitor extends SimpleAstVisitor {
84+
final LintRule rule;
85+
86+
_Visitor(this.rule);
87+
88+
@override
89+
visitBreakStatement(BreakStatement node) {
90+
if (node.label != null) return;
91+
var parent = node.parent;
92+
if (parent is SwitchPatternCase) {
93+
var statements = parent.statements;
94+
if (statements.length == 1) return;
95+
if (node == statements.last) {
96+
rule.reportLint(node);
97+
}
98+
}
99+
}
100+
}

test/rules/all.dart

+2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ import 'type_init_formals_test.dart' as type_init_formals;
8484
import 'unawaited_futures_test.dart' as unawaited_futures;
8585
import 'unnecessary_brace_in_string_interps_test.dart'
8686
as unnecessary_brace_in_string_interps;
87+
import 'unnecessary_breaks_test.dart' as unnecessary_breaks;
8788
import 'unnecessary_const_test.dart' as unnecessary_const;
8889
import 'unnecessary_null_checks_test.dart' as unnecessary_null_checks;
8990
import 'unnecessary_overrides_test.dart' as unnecessary_overrides;
@@ -155,6 +156,7 @@ void main() {
155156
type_init_formals.main();
156157
unawaited_futures.main();
157158
unnecessary_brace_in_string_interps.main();
159+
unnecessary_breaks.main();
158160
unnecessary_const.main();
159161
unnecessary_null_checks.main();
160162
unnecessary_overrides.main();
+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:test_reflective_loader/test_reflective_loader.dart';
6+
7+
import '../rule_test_support.dart';
8+
9+
main() {
10+
defineReflectiveSuite(() {
11+
defineReflectiveTests(UnnecessaryBreaksTest);
12+
});
13+
}
14+
15+
@reflectiveTest
16+
class UnnecessaryBreaksTest extends LintRuleTest {
17+
@override
18+
List<String> get experiments => ['patterns', 'records'];
19+
20+
@override
21+
String get lintRule => 'unnecessary_breaks';
22+
23+
test_switch_pre30_ok() async {
24+
await assertNoDiagnostics(r'''
25+
// @dart=2.19
26+
f() {
27+
switch (1) {
28+
case 1:
29+
f();
30+
break;
31+
}
32+
}
33+
''');
34+
}
35+
36+
test_switchPatternCase() async {
37+
await assertDiagnostics(r'''
38+
f() {
39+
switch (1) {
40+
case 1:
41+
f();
42+
break;
43+
case 2:
44+
f();
45+
}
46+
}
47+
''', [
48+
lint(50, 6),
49+
]);
50+
}
51+
52+
test_switchPatternCase_empty_ok() async {
53+
await assertNoDiagnostics(r'''
54+
f() {
55+
switch (1) {
56+
case 1:
57+
break;
58+
case 2:
59+
f();
60+
}
61+
}
62+
''');
63+
}
64+
65+
test_switchPatternCase_labeled_ok() async {
66+
await assertNoDiagnostics(
67+
r'''
68+
f() {
69+
l:
70+
switch (1) {
71+
case 1:
72+
break l;
73+
case 2:
74+
f();
75+
}
76+
}
77+
''',
78+
);
79+
}
80+
81+
test_switchPatternCase_notDirectChild_ok() async {
82+
await assertNoDiagnostics(r'''
83+
f(bool c) {
84+
switch (1) {
85+
case 1:
86+
if (c) break;
87+
f(true);
88+
case 2:
89+
f(true);
90+
}
91+
}
92+
''');
93+
}
94+
95+
test_switchPatternCase_notLast_ok() async {
96+
await assertDiagnostics(r'''
97+
f(bool c) {
98+
switch (1) {
99+
case 1:
100+
break;
101+
f(true);
102+
case 2:
103+
f(true);
104+
}
105+
}
106+
''', [
107+
// No lint.
108+
error(HintCode.DEAD_CODE, 58, 8),
109+
]);
110+
}
111+
}

0 commit comments

Comments
 (0)