diff --git a/internal/glob/glob.go b/internal/glob/glob.go index 7b4f2852d..474d593d5 100644 --- a/internal/glob/glob.go +++ b/internal/glob/glob.go @@ -12,19 +12,22 @@ import ( type Glob struct { Pattern string Negated bool + + // compiled is the `gobwas/glob` form of Pattern, built once by NewGlob. + // It's nil for a `**` pattern, which doublestar matches instead. + compiled glob.Glob } // Match returns whether or not the Glob g matches the string query. func (g Glob) Match(query string) bool { q := filepath.ToSlash(query) - if strings.Contains(g.Pattern, "**") { + if g.compiled == nil { matched, _ := doublestar.Match(g.Pattern, q) return matched != g.Negated } - p := glob.MustCompile(g.Pattern) - return p.Match(q) != g.Negated + return g.compiled.Match(q) != g.Negated } // MatchAny returns whether or not the Glob g matches any of the strings in @@ -49,7 +52,18 @@ func NewGlob(pat string) (Glob, error) { if err != nil { return Glob{}, err } - return Glob{Pattern: pat, Negated: negate}, nil + + if strings.Contains(pat, "**") { + return Glob{Pattern: pat, Negated: negate}, nil + } + + // doublestar accepts patterns gobwas/glob rejects, and Match used to + // compile there with MustCompile -- so `--glob='[a-]*'` panicked mid-walk. + compiled, err := glob.Compile(pat) + if err != nil { + return Glob{}, err + } + return Glob{Pattern: pat, Negated: negate, compiled: compiled}, nil } // Compile is a wrapper around NewGlobal for backwards compatibility. diff --git a/internal/glob/glob_test.go b/internal/glob/glob_test.go index b3b20973b..fa62412b8 100644 --- a/internal/glob/glob_test.go +++ b/internal/glob/glob_test.go @@ -32,6 +32,20 @@ var globTests = []struct { }, } +// Patterns doublestar accepts and gobwas/glob rejects. NewGlob has to report +// them, since Match can't: it used to compile with MustCompile and panicked +// partway through the walk. +var invalidGlobs = []string{`[a-]`, `[a-b-c]`} + +func TestInvalidGlob(t *testing.T) { + for _, pat := range invalidGlobs { + g, err := NewGlob(pat) + if err == nil { + t.Errorf("%s: expected an error, got %+v", pat, g) + } + } +} + func TestGlob(t *testing.T) { for _, tt := range globTests { g, _ := NewGlob(tt.pattern) diff --git a/testdata/e2e/config-flags.yaml b/testdata/e2e/config-flags.yaml index 8e2d22497..9feeae6cd 100644 --- a/testdata/e2e/config-flags.yaml +++ b/testdata/e2e/config-flags.yaml @@ -94,6 +94,14 @@ cases: - py requires: asciidoctor, dita, rst2html, xsltproc + - name: glob-invalid + about: "a pattern gobwas/glob rejects used to panic mid-walk" + dir: ../glob + args: "--glob=[a-]* ." + exit: 2 + want: | + expected close range character + - name: no-config-found dir: ../../../.. args: .