diff --git a/README.md b/README.md index 6c4cfad..b0af7a8 100644 --- a/README.md +++ b/README.md @@ -30,29 +30,29 @@ Here's how `revive` is different from `golint`: - [revive](#revive) - - [Usage](#usage) - - [Text Editors](#text-editors) - - [Installation](#installation) - - [Command Line Flags](#command-line-flags) - - [Sample Invocations](#sample-invocations) - - [Comment Annotations](#comment-annotations) - - [Configuration](#configuration) - - [Default Configuration](#default-configuration) - - [Recommended Configuration](#recommended-configuration) - - [Available Rules](#available-rules) - - [Available Formatters](#available-formatters) - - [Friendly](#friendly) - - [Stylish](#stylish) - - [Default](#default) - - [Extensibility](#extensibility) - - [Custom Rule](#custom-rule) - - [Example](#example) - - [Custom Formatter](#custom-formatter) - - [Speed Comparison](#speed-comparison) - - [golint](#golint) - - [revive](#revive-1) - - [Contributors](#contributors) - - [License](#license) + - [Usage](#usage) + - [Text Editors](#text-editors) + - [Installation](#installation) + - [Command Line Flags](#command-line-flags) + - [Sample Invocations](#sample-invocations) + - [Comment Annotations](#comment-annotations) + - [Configuration](#configuration) + - [Default Configuration](#default-configuration) + - [Recommended Configuration](#recommended-configuration) + - [Available Rules](#available-rules) + - [Available Formatters](#available-formatters) + - [Friendly](#friendly) + - [Stylish](#stylish) + - [Default](#default) + - [Extensibility](#extensibility) + - [Custom Rule](#custom-rule) + - [Example](#example) + - [Custom Formatter](#custom-formatter) + - [Speed Comparison](#speed-comparison) + - [golint](#golint) + - [revive](#revive-1) + - [Contributors](#contributors) + - [License](#license) @@ -211,6 +211,7 @@ List of all available rules. The rules ported from `golint` are left unchanged a | `empty-block` | n/a | Warns on empty code blocks | no | no | | `superfluous-else` | n/a | Prevents redundant else statements (extends `indent-error-flow`) | no | no | | `confusing-naming` | n/a | Warns on methods with names that differ only by capitalization | no | no | +| `get-return ` | n/a | Warns on getters that do not yield any result | no | no | ## Available Formatters diff --git a/config.go b/config.go index 31a0cdc..e9d7c3e 100644 --- a/config.go +++ b/config.go @@ -49,6 +49,7 @@ var allRules = append([]lint.Rule{ &rule.FileHeaderRule{}, &rule.EmptyBlockRule{}, &rule.SuperfluousElseRule{}, + &rule.GetReturnRule{}, }, defaultRules...) var allFormatters = []lint.Formatter{ diff --git a/fixtures/get-return.go b/fixtures/get-return.go new file mode 100644 index 0000000..a279ed7 --- /dev/null +++ b/fixtures/get-return.go @@ -0,0 +1,29 @@ +package fixtures + +func getfoo() { + +} + +func getBar(a, b int) { // MATCH /function 'getBar' seems to be a getter but it does not return any result/ + +} + +func Getbaz(a string, b int) { + +} + +func GetTaz(a string, b int) string { + +} + +func (t *t) GetTaz(a string, b int) string { + +} + +func (t *t) GetSaz(a string, b int) { // MATCH /function 'GetSaz' seems to be a getter but it does not return any result/ + +} + +func GetQux(a string, b int, c int, d string, e int64) { // MATCH /function 'GetQux' seems to be a getter but it does not return any result/ + +} diff --git a/rule/get-return.go b/rule/get-return.go new file mode 100644 index 0000000..ed2ee61 --- /dev/null +++ b/rule/get-return.go @@ -0,0 +1,71 @@ +package rule + +import ( + "fmt" + "go/ast" + "strings" + + "github.com/mgechev/revive/lint" +) + +// GetReturnRule lints given else constructs. +type GetReturnRule struct{} + +// Apply applies the rule to given file. +func (r *GetReturnRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + var failures []lint.Failure + + onFailure := func(failure lint.Failure) { + failures = append(failures, failure) + } + + w := lintReturnRule{onFailure} + ast.Walk(w, file.AST) + return failures +} + +// Name returns the rule name. +func (r *GetReturnRule) Name() string { + return "get-return" +} + +type lintReturnRule struct { + onFailure func(lint.Failure) +} + +func isGetter(name string) bool { + if strings.HasPrefix(strings.ToUpper(name), "GET") { + if len(name) > 3 { + c := name[3] + return !(c >= 'a' && c <= 'z') + } + } + + return false +} + +func hasResults(rs *ast.FieldList) bool { + return rs != nil && len(rs.List) > 0 +} + +func (w lintReturnRule) Visit(node ast.Node) ast.Visitor { + fd, ok := node.(*ast.FuncDecl) + if !ok { + return w + } + + if !isGetter(fd.Name.Name) { + return w + } + if !hasResults(fd.Type.Results) { + w.onFailure(lint.Failure{ + Confidence: 0.8, + Node: fd, + Category: "logic", + URL: "#get-return", + Failure: fmt.Sprintf("function '%s' seems to be a getter but it does not return any result", fd.Name.Name), + }) + } + + return w +} diff --git a/test/get-return_test.go b/test/get-return_test.go new file mode 100644 index 0000000..0b320dd --- /dev/null +++ b/test/get-return_test.go @@ -0,0 +1,11 @@ +package test + +import ( + "testing" + + "github.com/mgechev/revive/rule" +) + +func TestGetReturn(t *testing.T) { + testRule(t, "get-return", &rule.GetReturnRule{}) +}