Repository: mgechev/revive
Pull request #1060 (2024-10-10 11:24:04)

prepareSkips: to resolve regexps in excludes
related #1011 

I know that we use https://github.com/mgechev/dots to process includePatterns and excludePatterns but I think there is better solution for that (https://github.com/bmatcuk/doublestar)

```func Glob(fsys fs.FS, pattern string, opts ...GlobOption) ([]string, error)``` - return matching paths wehre pattern can be regexp or normal file path

my PR uses that function and converts regexp pattern to list of paths which next can be used to process packages in ```dots.ResolvePackages``` (of course if we set more then one includepath it will be processed individualy)

example:

```./revive -exclude '**/linux*' -formatter friendly ./... vendor/...```


Patch:
diff --git a/go.mod b/go.mod
index 11ae7f6..91c3342 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.22.1
 
 require (
 	github.com/BurntSushi/toml v1.4.0
+	github.com/bmatcuk/doublestar/v4 v4.8.1
 	github.com/chavacava/garif v0.1.0
 	github.com/fatih/color v1.18.0
 	github.com/fatih/structtag v1.2.0
diff --git a/go.sum b/go.sum
index 157facc..4a6d130 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,7 @@
 github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
 github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
+github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
 github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
 github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
diff --git a/internal/astutils/ast_utils.go b/internal/astutils/ast_utils.go
index 3ca5d7f..bcd13d8 100644
--- a/internal/astutils/ast_utils.go
+++ b/internal/astutils/ast_utils.go
@@ -71,6 +71,8 @@ func getFieldTypeName(typ ast.Expr) string {
 		return f.Sel.Name + "." + getFieldTypeName(f.X)
 	case *ast.StarExpr:
 		return "*" + getFieldTypeName(f.X)
+	case *ast.IndexExpr:
+		return getFieldTypeName(f.X) + "[" + getFieldTypeName(f.Index) + "]"
 	default:
 		panic(fmt.Sprintf("not supported type %T", typ))
 	}
diff --git a/revivelib/core.go b/revivelib/core.go
old mode 100644
new mode 100755
index 31463fe..c327d90
--- a/revivelib/core.go
+++ b/revivelib/core.go
@@ -5,8 +5,10 @@ import (
 	"fmt"
 	"log"
 	"os"
+	"path/filepath"
 	"strings"
 
+	"github.com/bmatcuk/doublestar/v4"
 	"github.com/mgechev/dots"
 	"github.com/mgechev/revive/config"
 	"github.com/mgechev/revive/lint"
@@ -173,7 +175,12 @@ func getPackages(includePatterns []string, excludePatterns ArrayFlags) ([][]stri
 		globs = append(globs, ".")
 	}
 
-	packages, err := dots.ResolvePackages(globs, normalizeSplit(excludePatterns))
+	globs, skips, err := prepareSkips(globs, normalizeSplit(excludePatterns))
+	if err != nil {
+		return nil, fmt.Errorf("prepare skips - resolving excludes before dots: %w", err)
+	}
+
+	packages, err := dots.ResolvePackages(globs, skips)
 	if err != nil {
 		return nil, fmt.Errorf("getting packages - resolving packages in dots: %w", err)
 	}
@@ -181,6 +188,29 @@ func getPackages(includePatterns []string, excludePatterns ArrayFlags) ([][]stri
 	return packages, nil
 }
 
+func prepareSkips(globs, excludes []string) ([]string, []string, error) {
+	var skips []string
+	for _, path := range globs {
+		var basepath string
+		basepath, _ = doublestar.SplitPattern(path)
+		fsys := os.DirFS(basepath)
+		for _, skip := range excludes {
+			matches, err := doublestar.Glob(fsys, skip)
+			if err != nil {
+				return nil, nil, fmt.Errorf("Skips Error: %w", err)
+			}
+			for _, match := range matches {
+				path = basepath + "/" + match
+				// create skip only for .go files
+				if filepath.Ext(path) == ".go" {
+					skips = append(skips, path)
+				}
+			}
+		}
+	}
+	return globs, skips, nil
+}
+
 func normalizeSplit(strs []string) []string {
 	res := []string{}
 


Test patch:
diff --git a/revivelib/core_test.go b/revivelib/core_test.go
index d434660..144758a 100644
--- a/revivelib/core_test.go
+++ b/revivelib/core_test.go
@@ -36,6 +36,43 @@ func TestReviveLint(t *testing.T) {
 	}
 }
 
+func TestReviveLintExcludeWithRegexp(t *testing.T) {
+	// ARRANGE
+	revive := getMockRevive(t)
+
+	// ACT
+	files := []string{"../testdata/if_return.go"}
+	excludePatterns := []string{"*return*"}
+	packages := []*revivelib.LintPattern{}
+	for _, file := range files {
+		packages = append(packages, revivelib.Include(file))
+	}
+
+	for _, file := range excludePatterns {
+		packages = append(packages, revivelib.Exclude(file))
+	}
+
+	failures, err := revive.Lint(packages...)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// ASSERT
+	failureList := []lint.Failure{}
+
+	for failure := range failures {
+		failureList = append(failureList, failure)
+	}
+
+	const expected = 0
+
+	got := len(failureList)
+	if got != expected {
+		t.Fatalf("Expected failures to have %d failures, but it has %d.", expected, got)
+	}
+}
+
 func TestReviveFormat(t *testing.T) {
 	// ARRANGE
 	revive := getMockRevive(t)
diff --git a/testdata/golint/sort.go b/testdata/golint/sort.go
index 0a101fe..e4e5c98 100644
--- a/testdata/golint/sort.go
+++ b/testdata/golint/sort.go
@@ -41,3 +41,6 @@ func (vv Vv) Less(i int, j int) (result bool) { return w[i] < w[j] } // MATCH /e
 type X []int
 
 func (x X) Less(i *pkg.tip) (result bool) { return len(x) } // MATCH /exported method X.Less should have comment or be unexported/
+
+// Test for issue #1217
+func (s *synchronized[T]) Swap(other Synchronized[T]) {}