Repository: mgechev/revive (go)

Issue:
getTagName function uses strings.TrimLeft instead of TrimPrefix
**Describe the bug**

In the code below the "name=" string is a cutset (a set of characters) to be removed, and not a prefix.
https://github.com/mgechev/revive/blob/b03e54f617491984b41f0411b69326f94d03a76f/rule/struct-tag.go#L139-L140

`getTagName` function will return `o` instead of `ameno` for the this struct tag: `protobuf:"varint,name=ameno"`.



Pull request description:
Fix prefix trimming in struct-tag
Fix #813


Patch:
diff --git a/config/config.go b/config/config.go
index 4fbab0e..04cd214 100644
--- a/config/config.go
+++ b/config/config.go
@@ -3,7 +3,7 @@ package config
 import (
 	"errors"
 	"fmt"
-	"io/ioutil"
+	"os"
 
 	"github.com/mgechev/revive/formatter"
 
@@ -140,7 +140,7 @@ func GetLintingRules(config *lint.Config, extraRules []lint.Rule) ([]lint.Rule,
 }
 
 func parseConfig(path string, config *lint.Config) error {
-	file, err := ioutil.ReadFile(path)
+	file, err := os.ReadFile(path)
 	if err != nil {
 		return errors.New("cannot read the config file")
 	}
diff --git a/logging/logger.go b/logging/logger.go
index c911b2e..9d603a4 100644
--- a/logging/logger.go
+++ b/logging/logger.go
@@ -2,7 +2,6 @@ package logging
 
 import (
 	"io"
-	"io/ioutil"
 	"log"
 	"os"
 )
@@ -27,7 +26,7 @@ func GetLogger() (*log.Logger, error) {
 		}
 	} else {
 		// Suppress all logging output if debug mode is disabled
-		writer = ioutil.Discard
+		writer = io.Discard
 	}
 
 	logger = log.New(writer, "", log.LstdFlags)
diff --git a/revivelib/core.go b/revivelib/core.go
index d3df579..a275e9e 100644
--- a/revivelib/core.go
+++ b/revivelib/core.go
@@ -1,8 +1,8 @@
 package revivelib
 
 import (
-	"io/ioutil"
 	"log"
+	"os"
 	"strings"
 
 	"github.com/mgechev/dots"
@@ -88,7 +88,7 @@ func (r *Revive) Lint(patterns ...*LintPattern) (<-chan lint.Failure, error) {
 	}
 
 	revive := lint.New(func(file string) ([]byte, error) {
-		contents, err := ioutil.ReadFile(file)
+		contents, err := os.ReadFile(file)
 
 		if err != nil {
 			return nil, errors.Wrap(err, "reading file "+file)
diff --git a/rule/struct-tag.go b/rule/struct-tag.go
index 45308d3..d1c8056 100644
--- a/rule/struct-tag.go
+++ b/rule/struct-tag.go
@@ -137,7 +137,7 @@ func (lintStructTagRule) getTagName(tag *structtag.Tag) string {
 	case keyProtobuf:
 		for _, option := range tag.Options {
 			if strings.HasPrefix(option, "name=") {
-				return strings.TrimLeft(option, "name=")
+				return strings.TrimPrefix(option, "name=")
 			}
 		}
 		return "" //protobuf tag lacks 'name' option


Test patch:
diff --git a/test/golint_test.go b/test/golint_test.go
index f443688..7d12625 100644
--- a/test/golint_test.go
+++ b/test/golint_test.go
@@ -2,7 +2,7 @@ package test
 
 import (
 	"flag"
-	"io/ioutil"
+	"os"
 	"path"
 	"regexp"
 	"testing"
@@ -41,9 +41,9 @@ func TestAll(t *testing.T) {
 		t.Fatalf("Bad -lint.match value %q: %v", *lintMatch, err)
 	}
 
-	fis, err := ioutil.ReadDir(baseDir)
+	fis, err := os.ReadDir(baseDir)
 	if err != nil {
-		t.Fatalf("ioutil.ReadDir: %v", err)
+		t.Fatalf("os.ReadDir: %v", err)
 	}
 	if len(fis) == 0 {
 		t.Fatalf("no files in %v", baseDir)
@@ -53,12 +53,18 @@ func TestAll(t *testing.T) {
 			continue
 		}
 		t.Run(fi.Name(), func(t *testing.T) {
-			src, err := ioutil.ReadFile(path.Join(baseDir, fi.Name()))
+			filePath := path.Join(baseDir, fi.Name())
+			src, err := os.ReadFile(filePath)
 			if err != nil {
 				t.Fatalf("Failed reading %s: %v", fi.Name(), err)
 			}
 
-			if err := assertFailures(t, baseDir, fi, src, rules, map[string]lint.RuleConfig{}); err != nil {
+			fileInfo, err := os.Stat(filePath)
+			if err != nil {
+				t.Fatalf("Failed reading %s: %v", fi.Name(), err)
+			}
+
+			if err := assertFailures(t, baseDir, fileInfo, src, rules, map[string]lint.RuleConfig{}); err != nil {
 				t.Errorf("Linting %s: %v", fi.Name(), err)
 			}
 		})
diff --git a/test/utils.go b/test/utils.go
index 3de6761..6ebb205 100644
--- a/test/utils.go
+++ b/test/utils.go
@@ -8,7 +8,6 @@ import (
 	"go/parser"
 	"go/token"
 	"go/types"
-	"io/ioutil"
 	"os"
 	"strconv"
 	"strings"
@@ -21,7 +20,7 @@ import (
 func testRule(t *testing.T, filename string, rule lint.Rule, config ...*lint.RuleConfig) {
 	baseDir := "../testdata/"
 	filename = filename + ".go"
-	src, err := ioutil.ReadFile(baseDir + filename)
+	src, err := os.ReadFile(baseDir + filename)
 	if err != nil {
 		t.Fatalf("Bad filename path in test for %s: %v", rule.Name(), err)
 	}
@@ -42,7 +41,7 @@ func testRule(t *testing.T, filename string, rule lint.Rule, config ...*lint.Rul
 
 func assertSuccess(t *testing.T, baseDir string, fi os.FileInfo, rules []lint.Rule, config map[string]lint.RuleConfig) error {
 	l := lint.New(func(file string) ([]byte, error) {
-		return ioutil.ReadFile(baseDir + file)
+		return os.ReadFile(baseDir + file)
 	}, 0)
 
 	ps, err := l.Lint([][]string{{fi.Name()}}, rules, lint.Config{
@@ -64,7 +63,7 @@ func assertSuccess(t *testing.T, baseDir string, fi os.FileInfo, rules []lint.Ru
 
 func assertFailures(t *testing.T, baseDir string, fi os.FileInfo, src []byte, rules []lint.Rule, config map[string]lint.RuleConfig) error {
 	l := lint.New(func(file string) ([]byte, error) {
-		return ioutil.ReadFile(baseDir + file)
+		return os.ReadFile(baseDir + file)
 	}, 0)
 
 	ins := parseInstructions(t, fi.Name(), src)
diff --git a/testdata/struct-tag.go b/testdata/struct-tag.go
index 21e73aa..93cbc14 100644
--- a/testdata/struct-tag.go
+++ b/testdata/struct-tag.go
@@ -116,6 +116,8 @@ type Simple struct {
 	ODouble              *float64 `protobuf:"fixed64,014,opt,name=o_double,json=oDouble"`      // MATCH /duplicated tag number 14/
 	ODoubleStr           *float64 `protobuf:"fixed6,17,opt,name=o_double_str,json=oDoubleStr"` // MATCH /invalid protobuf tag name 'fixed6'/
 	OString              *string  `protobuf:"bytes,18,opt,name=o_string,json=oString"`
+	OString2             *string  `protobuf:"bytes,name=ameno"`
+	OString3             *string  `protobuf:"bytes,name=ameno"` // MATCH /duplicate tag name: 'ameno'/
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
 	XXX_sizecache        int32    `json:"-"`
diff --git a/testdata/unused-param.go b/testdata/unused-param.go
index bd7acc3..a7ae1e8 100644
--- a/testdata/unused-param.go
+++ b/testdata/unused-param.go
@@ -3,7 +3,6 @@ package fixtures
 import (
 	"fmt"
 	"go/ast"
-	"io/ioutil"
 	"os"
 	"runtime"
 	"testing"
@@ -103,7 +102,7 @@ func getCompareFailCause(n *node, which int, prevValue string, prevIndex uint64)
 
 func assertSuccess(t *testing.T, baseDir string, fi os.FileInfo, src []byte, rules []lint.Rule, config map[string]lint.RuleConfig) error { // MATCH /parameter 'src' seems to be unused, consider removing or renaming it as _/
 	l := lint.New(func(file string) ([]byte, error) {
-		return ioutil.ReadFile(baseDir + file)
+		return os.ReadFile(baseDir + file)
 	})
 
 	ps, err := l.Lint([][]string{[]string{fi.Name()}}, rules, lint.Config{