Skip to content
This repository was archived by the owner on Nov 20, 2024. It is now read-only.

+ unnecessary_breaks #3981

Merged
merged 6 commits into from
Jan 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions example/all.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ linter:
- unawaited_futures
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_breaks
- unnecessary_const
- unnecessary_constructor_name
- unnecessary_final
Expand Down
2 changes: 2 additions & 0 deletions lib/src/rules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ import 'rules/type_init_formals.dart';
import 'rules/unawaited_futures.dart';
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_constructor_name.dart';
import 'rules/unnecessary_final.dart';
Expand Down Expand Up @@ -399,6 +400,7 @@ void registerLintRules({bool inTestMode = false}) {
..register(UnawaitedFutures())
..register(UnnecessaryAwaitInReturn())
..register(UnnecessaryBraceInStringInterps())
..register(UnnecessaryBreaks())
..register(UnnecessaryConst())
..register(UnnecessaryConstructorName())
..register(UnnecessaryFinal())
Expand Down
100 changes: 100 additions & 0 deletions lib/src/rules/unnecessary_breaks.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) 2023, 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/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';

import '../analyzer.dart';

const _desc = r"Don't use explicit `break`s when a break is implied.";

const _details = r'''
Only use a `break` in a non-empty switch case statement if you need to break
before the end of the case body. Dart does not support fallthrough execution
for non-empty cases, so `break`s at the end of non-empty switch case statements
are unnecessary.

**BAD:**
```dart
switch (1) {
case 1:
print("one");
break;
case 2:
print("two");
break;
}
```

**GOOD:**
```dart
switch (1) {
case 1:
print("one");
case 2:
print("two");
}
```

```dart
switch (1) {
case 1:
case 2:
print("one or two");
}
```

```dart
switch (1) {
case 1:
break;
case 2:
print("just two");
}
```
''';

class UnnecessaryBreaks extends LintRule {
static const LintCode code = LintCode(
'unnecessary_breaks', "Unnecessary 'break' statement.",
correctionMessage: "Try removing the 'break'.");

UnnecessaryBreaks()
: super(
name: 'unnecessary_breaks',
description: _desc,
details: _details,
group: Group.style);

@override
LintCode get lintCode => code;

@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
if (!context.isEnabled(Feature.patterns)) return;
var visitor = _Visitor(this);
registry.addBreakStatement(this, visitor);
}
}

class _Visitor extends SimpleAstVisitor {
final LintRule rule;

_Visitor(this.rule);

@override
visitBreakStatement(BreakStatement node) {
if (node.label != null) return;
var parent = node.parent;
if (parent is SwitchPatternCase) {
var statements = parent.statements;
if (statements.length == 1) return;
if (node == statements.last) {
rule.reportLint(node);
}
}
}
}
2 changes: 2 additions & 0 deletions test/rules/all.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import 'type_init_formals_test.dart' as type_init_formals;
import 'unawaited_futures_test.dart' as unawaited_futures;
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_test.dart' as unnecessary_const;
import 'unnecessary_null_checks_test.dart' as unnecessary_null_checks;
import 'unnecessary_overrides_test.dart' as unnecessary_overrides;
Expand Down Expand Up @@ -155,6 +156,7 @@ void main() {
type_init_formals.main();
unawaited_futures.main();
unnecessary_brace_in_string_interps.main();
unnecessary_breaks.main();
unnecessary_const.main();
unnecessary_null_checks.main();
unnecessary_overrides.main();
Expand Down
111 changes: 111 additions & 0 deletions test/rules/unnecessary_breaks_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) 2023, 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';

main() {
defineReflectiveSuite(() {
defineReflectiveTests(UnnecessaryBreaksTest);
});
}

@reflectiveTest
class UnnecessaryBreaksTest extends LintRuleTest {
@override
List<String> get experiments => ['patterns', 'records'];

@override
String get lintRule => 'unnecessary_breaks';

test_switch_pre30_ok() async {
await assertNoDiagnostics(r'''
// @dart=2.19
f() {
switch (1) {
case 1:
f();
break;
}
}
''');
}

test_switchPatternCase() async {
await assertDiagnostics(r'''
f() {
switch (1) {
case 1:
f();
break;
case 2:
f();
}
}
''', [
lint(50, 6),
]);
}

test_switchPatternCase_empty_ok() async {
await assertNoDiagnostics(r'''
f() {
switch (1) {
case 1:
break;
case 2:
f();
}
}
''');
}

test_switchPatternCase_labeled_ok() async {
await assertNoDiagnostics(
r'''
f() {
l:
switch (1) {
case 1:
break l;
case 2:
f();
}
}
''',
);
}

test_switchPatternCase_notDirectChild_ok() async {
await assertNoDiagnostics(r'''
f(bool c) {
switch (1) {
case 1:
if (c) break;
f(true);
case 2:
f(true);
}
}
''');
}

test_switchPatternCase_notLast_ok() async {
await assertDiagnostics(r'''
f(bool c) {
switch (1) {
case 1:
break;
f(true);
case 2:
f(true);
}
}
''', [
// No lint.
error(HintCode.DEAD_CODE, 58, 8),
]);
}
}