Repository: mgechev/revive
Pull request #549 (2021-08-02 08:15:55)

Update module github.com/BurntSushi/toml to v0.4.1
[![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [github.com/BurntSushi/toml](https://togithub.com/BurntSushi/toml) | require | minor | `v0.3.1` -> `v0.4.1` |

---

### Release Notes

<details>
<summary>BurntSushi/toml</summary>

### [`v0.4.1`](https://togithub.com/BurntSushi/toml/releases/v0.4.1)

[Compare Source](https://togithub.com/BurntSushi/toml/compare/v0.4.0...v0.4.1)

This fixes a cyclic module dependency issue with github.com/BurntSushi/toml-test that prevented some people from updating. See [#&#8203;313](https://togithub.com/BurntSushi/toml/issues/313) for some details.

### [`v0.4.0`](https://togithub.com/BurntSushi/toml/releases/v0.4.0)

[Compare Source](https://togithub.com/BurntSushi/toml/compare/v0.3.1...v0.4.0)

After some time of inactivity this package is now maintained again.

This release should support alll of TOML 1.0 and has various bugfixes and a few small improvements.

This requires Go 1.13 or newer; older Go versions will no longer work.

## TOML 1.0 support

Previously this library implemented TOML 0.3; now all of TOML 1.0 is supported:

-   Support dotted keys (`a.b = 1`, `a = {b.c = 2}`).

-   Mixed arrays: in previous TOML versions array values all had to be of the same type; you can now freely mix types, including inline tables.

-   Support hex (`0x2f9a`), binary (`0b0110`), and octal (`0o777`) literals, and support `nan` and `inf` for floats.

-   Support local datetimes, dates, and times. These are dates and times without a timezone and are parsed in the local timezone.

-   Allow accidental whitespace between backslash and newline in the line continuation operator in multi-line basic strings.

There should be no incompatibilities as such; all existing *valid* TOML files should continue to work. However, the parser previously allowed the following invalid values:

-   It would allow literal control characters in strings.

-   It would allow leading zeroes in decimal ints and floats.

Neither of these was ever valid TOML, and are explicitly forbidden by the specification. But the library erroneously permitted them.

## Other changes

-   Set up Go modules.

-   Allow escaping the `\`, and allow triple-quotes strings to end with a quote (e.g. `x="""x""""`).

-   All control characters inside strings are properly escaped when encoding.

-   Support encoding nested anonymous structs.

-   Encode toml.Primitive values.

-   You get a more helpful error on UTF-16 files (probably the most common non-UTF-8 compatible encoding). Also read over UTF-16 BOM in UTF-8 files.

-   Call `MarshalText` and `UnmarshalText` consistently on all types; before this didn't always happen in some cases.

-   Allow empty quoted keys (`"" = 1`); silly, but explicitly mentioned as valid.

-   Don't panic in encoder on unsupported types; return an error instead.

-   Don't panic on unclosed inline arrays.

-   Add `Decoder` and deprecate `DecodeReader()`; this is more consistent with how other decoders tend to work and allows adding decoding options.

-   Add `DecodeFS()` for Go 1.16 or newer.

-   Avoid creating new functions/allocations in lexSkip; small performance improvement.

</details>

---

### Configuration

📅 **Schedule**: At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box.

---

This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/mgechev/revive).


Patch:
diff --git a/README.md b/README.md
index 52efcb4..ef67f51 100644
--- a/README.md
+++ b/README.md
@@ -453,6 +453,7 @@ List of all available rules. The rules ported from `golint` are left unchanged a
 | [`unexported-naming`](./RULES_DESCRIPTIONS.md#unexported-naming)          |  n/a   |  Warns on wrongly named un-exported symbols       |    no    |  no   |
 | [`function-length`](./RULES_DESCRIPTIONS.md#function-length)          |  n/a   |  Warns on functions exceeding the statements or lines max |    no    |  no   |
 | [`nested-structs`](./RULES_DESCRIPTIONS.md#nested-structs)          |  n/a   |  Warns on structs within structs |    no    |  no   |
+| [`useless-break`](./RULES_DESCRIPTIONS.md#useless-break)          |  n/a   |  Warns on useless `break` statements in case clauses |    no    |  no   |
 
 ## Configurable rules
 
diff --git a/RULES_DESCRIPTIONS.md b/RULES_DESCRIPTIONS.md
index 7c3ba95..48fa412 100644
--- a/RULES_DESCRIPTIONS.md
+++ b/RULES_DESCRIPTIONS.md
@@ -67,6 +67,7 @@ List of all available rules.
   - [unreachable-code](#unreachable-code)
   - [unused-parameter](#unused-parameter)
   - [unused-receiver](#unused-receiver)
+  - [useless-break](#useless-break)
   - [waitgroup-by-value](#waitgroup-by-value)
 
 ## add-constant
@@ -612,6 +613,16 @@ _Description_: This rule warns on unused method receivers. Methods with unused r
 
 _Configuration_: N/A
 
+## useless-break
+
+_Description_: This rule warns on useless `break` statements in case clauses of switch and select statements. GO, unlike other programming languages like C, only executes statements of the selected case while ignoring the subsequent case clauses. 
+Therefore, inserting a `break` at the end of a case clause has no effect. 
+
+Because `break` statements are rarely used in case clauses, when switch or select statements are inside a for-loop, the programmer might wrongly assume that a `break` in a case clause will take the control out of the loop.
+The rule emits a specific warning for such cases.
+
+_Configuration_: N/A
+
 ## waitgroup-by-value
 
 _Description_: Function parameters that are passed by value, are in fact a copy of the original argument. Passing a copy of a `sync.WaitGroup` is usually not what the developer wants to do.
diff --git a/config/config.go b/config/config.go
index 1ca54d6..298c0bd 100644
--- a/config/config.go
+++ b/config/config.go
@@ -80,6 +80,7 @@ var allRules = append([]lint.Rule{
 	&rule.FunctionLength{},
 	&rule.NestedStructs{},
 	&rule.IfReturnRule{},
+	&rule.UselessBreak{},
 }, defaultRules...)
 
 var allFormatters = []lint.Formatter{
diff --git a/go.mod b/go.mod
index 2e07a26..34674ac 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/mgechev/revive
 go 1.12
 
 require (
-	github.com/BurntSushi/toml v0.3.1
+	github.com/BurntSushi/toml v0.4.1
 	github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af
 	github.com/fatih/color v1.12.0
 	github.com/fatih/structtag v1.2.0
diff --git a/go.sum b/go.sum
index 3cd67b9..7b5b638 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,7 @@
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
+github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
 github.com/chavacava/garif v0.0.0-20210405163807-87a70f3d418b h1:StHNkfM8nXnNQnk5/0uYYhIqvvENd14hoHPnZsakTNo=
 github.com/chavacava/garif v0.0.0-20210405163807-87a70f3d418b/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU=
 github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af h1:spmv8nSH9h5oCQf40jt/ufBCt9j0/58u4G+rkeMqXGI=
diff --git a/rule/useless-break.go b/rule/useless-break.go
new file mode 100644
index 0000000..9e9c829
--- /dev/null
+++ b/rule/useless-break.go
@@ -0,0 +1,77 @@
+package rule
+
+import (
+	"go/ast"
+	"go/token"
+
+	"github.com/mgechev/revive/lint"
+)
+
+// UselessBreak lint rule.
+type UselessBreak struct{}
+
+// Apply applies the rule to given file.
+func (r *UselessBreak) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
+	var failures []lint.Failure
+
+	onFailure := func(failure lint.Failure) {
+		failures = append(failures, failure)
+	}
+
+	astFile := file.AST
+	w := &lintUselessBreak{onFailure, false}
+	ast.Walk(w, astFile)
+	return failures
+}
+
+// Name returns the rule name.
+func (r *UselessBreak) Name() string {
+	return "useless-break"
+}
+
+type lintUselessBreak struct {
+	onFailure  func(lint.Failure)
+	inLoopBody bool
+}
+
+func (w *lintUselessBreak) Visit(node ast.Node) ast.Visitor {
+	switch v := node.(type) {
+	case *ast.ForStmt:
+		w.inLoopBody = true
+		ast.Walk(w, v.Body)
+		w.inLoopBody = false
+		return nil
+	case *ast.CommClause:
+		for _, n := range v.Body {
+			w.inspectCaseStatement(n)
+		}
+		return nil
+	case *ast.CaseClause:
+		for _, n := range v.Body {
+			w.inspectCaseStatement(n)
+		}
+		return nil
+	}
+	return w
+}
+
+func (w *lintUselessBreak) inspectCaseStatement(n ast.Stmt) {
+	switch s := n.(type) {
+	case *ast.BranchStmt:
+		if s.Tok != token.BREAK {
+			return // not a break statement
+		}
+		if s.Label != nil {
+			return // labeled break statement, usually affects a nesting loop
+		}
+		msg := "useless break in case clause"
+		if w.inLoopBody {
+			msg += " (WARN: this break statement affects this switch or select statement and not the loop enclosing it)"
+		}
+		w.onFailure(lint.Failure{
+			Confidence: 1,
+			Node:       s,
+			Failure:    msg,
+		})
+	}
+}


Test patch:
diff --git a/test/useless-break_test.go b/test/useless-break_test.go
new file mode 100644
index 0000000..d77dfc3
--- /dev/null
+++ b/test/useless-break_test.go
@@ -0,0 +1,12 @@
+package test
+
+import (
+	"testing"
+
+	"github.com/mgechev/revive/rule"
+)
+
+// UselessBreak rule.
+func TestUselessBreak(t *testing.T) {
+	testRule(t, "useless-break", &rule.UselessBreak{})
+}
diff --git a/testdata/useless-break.go b/testdata/useless-break.go
new file mode 100644
index 0000000..1e6457d
--- /dev/null
+++ b/testdata/useless-break.go
@@ -0,0 +1,87 @@
+package fixtures
+
+import "reflect"
+
+func UselessBreaks() {
+
+	switch {
+	case true:
+		break // MATCH /useless break in case clause/
+	case false:
+		if false {
+			break
+		}
+	}
+
+	select {
+	case c:
+		break // MATCH /useless break in case clause/
+	case n:
+		if true {
+			if false {
+				break
+			}
+			break
+		}
+	}
+
+	for {
+		switch {
+		case c1:
+			break // MATCH /useless break in case clause (WARN: this break statement affects the switch or select statement and not the loop enclosing it)/
+		}
+	}
+
+	for _, node := range desc.Args {
+		switch node := node.(type) {
+		case *ast.FuncLit:
+			found = true
+			funcLit = node
+			break // MATCH /useless break in case clause (WARN: this break statement affects the switch or select statement and not the loop enclosing it)/
+		}
+	}
+
+	switch val.Kind() {
+	case reflect.Array, reflect.Slice:
+		if val.Len() == 0 {
+			break
+		}
+		for i := 0; i < val.Len(); i++ {
+			oneIteration(reflect.ValueOf(i), val.Index(i))
+		}
+		return
+	case reflect.Map:
+		if val.Len() == 0 {
+			break
+		}
+		om := fmtsort.Sort(val)
+		for i, key := range om.Key {
+			oneIteration(key, om.Value[i])
+		}
+		return
+	case reflect.Chan:
+		if val.IsNil() {
+			break
+		}
+		if val.Type().ChanDir() == reflect.SendDir {
+			s.errorf("range over send-only channel %v", val)
+			break
+		}
+		i := 0
+		for ; ; i++ {
+			elem, ok := val.Recv()
+			if !ok {
+				break
+			}
+			oneIteration(reflect.ValueOf(i), elem)
+		}
+		if i == 0 {
+			break
+		}
+		return
+	case reflect.Invalid:
+		break // MATCH /useless break in case clause/
+	default:
+		s.errorf("range can't iterate over %v", val)
+	}
+}