Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ require (
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgageot/rubocop-go v0.0.0-20260429095109-a9cea3bf3e72
github.com/dgageot/rubocop-go v0.0.0-20260429125723-198995cc80c9
github.com/distribution/reference v0.6.0 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgageot/rubocop-go v0.0.0-20260429095109-a9cea3bf3e72 h1:2BdfP/9nnmcCHUBZhOueTb6rQojfZdlh2hI3E65cY2I=
github.com/dgageot/rubocop-go v0.0.0-20260429095109-a9cea3bf3e72/go.mod h1:r8YOJV5+/30NZ8HW/2NbWUObBGDXGvfHrjgury5YlFI=
github.com/dgageot/rubocop-go v0.0.0-20260429125723-198995cc80c9 h1:LwBQXSuGbtHC3rzVOBQIsSofzTc7k9QWRqoQdfPbG+s=
github.com/dgageot/rubocop-go v0.0.0-20260429125723-198995cc80c9/go.mod h1:r8YOJV5+/30NZ8HW/2NbWUObBGDXGvfHrjgury5YlFI=
github.com/dgageot/ultraviolet v0.0.0-20260313154905-9451997d56b6 h1:88fWkkjwzuI4tRTqadbJIbA9O+gO67oyu+2OpHHuuT8=
github.com/dgageot/ultraviolet v0.0.0-20260313154905-9451997d56b6/go.mod h1:SQpCTRNBtzJkwku5ye4S3HEuthAlGy2n9VXZnWkEW98=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
Expand Down
22 changes: 7 additions & 15 deletions lint/config_latest_tag_consistency.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"go/ast"
"reflect"
"strconv"
"strings"

"github.com/dgageot/rubocop-go/cop"
Expand Down Expand Up @@ -47,33 +46,27 @@ func NewConfigLatestTagConsistency() *ConfigLatestTagConsistency {
}

func (c *ConfigLatestTagConsistency) Check(p *cop.Pass) {
if configDir(p.Filename()) != "latest" {
dir, _ := p.PathSegment("pkg/config")
if dir != "latest" {
return
}
// Black-box test files don't ship struct definitions for the wire format.
if p.IsBlackBoxTest() {
return
}

ast.Inspect(p.File, func(n ast.Node) bool {
field, ok := n.(*ast.Field)
if !ok || field.Tag == nil {
return true
}
raw, err := strconv.Unquote(field.Tag.Value)
if err != nil {
return true
p.ForEachStructField(func(_ *ast.TypeSpec, field *ast.Field, tag reflect.StructTag) {
if field.Tag == nil {
return
}
tag := reflect.StructTag(raw)

jsonTag, hasJSON := tag.Lookup("json")
yamlTag, hasYAML := tag.Lookup("yaml")
if !hasJSON || !hasYAML {
return true
return
}
// Embedded fields use ",inline" on both sides; nothing to compare.
if isInline(jsonTag) || isInline(yamlTag) {
return true
return
}

jsonOmit, jsonMod := jsonOmitModifier(jsonTag)
Expand All @@ -85,7 +78,6 @@ func (c *ConfigLatestTagConsistency) Check(p *cop.Pass) {
modifierLabel(jsonOmit, jsonMod), modifierLabel(yamlOmit, "omitempty"),
fieldNames(field))
}
return true
})
}

Expand Down
4 changes: 2 additions & 2 deletions lint/config_package_name.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ func NewConfigPackageName() *ConfigPackageName {
}

func (c *ConfigPackageName) Check(p *cop.Pass) {
dir := configDir(p.Filename())
if dir == "" {
dir, ok := p.PathSegment("pkg/config")
if !ok {
return
}

Expand Down
44 changes: 10 additions & 34 deletions lint/config_version_constant.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package main

import (
"go/ast"
"go/token"
"strconv"

"github.com/dgageot/rubocop-go/cop"
Expand Down Expand Up @@ -33,42 +31,20 @@ func NewConfigVersionConstant() *ConfigVersionConstant {
}

func (c *ConfigVersionConstant) Check(p *cop.Pass) {
dirVersion, ok := versionFromDir(configDir(p.Filename()))
dir, _ := p.PathSegment("pkg/config")
dirVersion, ok := versionFromDir(dir)
if !ok {
return
}
expected := strconv.Itoa(dirVersion)

for _, lit := range versionStringLiterals(p) {
got, err := strconv.Unquote(lit.Value)
if err != nil || got == expected {
continue
}
p.Report(lit, "Version in pkg/config/v%s/ must be %q, got %q", expected, expected, got)
lit, ok := p.StringConstNodes()["Version"]
if !ok {
return
}
}

// versionStringLiterals returns the value literal of every top-level
// `const Version = "<string>"` declaration in the file under inspection.
func versionStringLiterals(p *cop.Pass) []*ast.BasicLit {
var lits []*ast.BasicLit
p.ForEachConst(func(gen *ast.GenDecl) {
for _, spec := range gen.Specs {
vs, ok := spec.(*ast.ValueSpec)
if !ok {
continue
}
for i, name := range vs.Names {
if name.Name != "Version" || i >= len(vs.Values) {
continue
}
lit, ok := vs.Values[i].(*ast.BasicLit)
if !ok || lit.Kind != token.STRING {
continue
}
lits = append(lits, lit)
}
}
})
return lits
got, err := strconv.Unquote(lit.Value)
if err != nil || got == expected {
return
}
p.Report(lit, "Version in pkg/config/v%s/ must be %q, got %q", expected, expected, got)
}
4 changes: 2 additions & 2 deletions lint/config_version_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ func (c *ConfigVersionImport) Check(p *cop.Pass) {
return
}

dir := configDir(p.Filename())
if dir == "" {
dir, ok := p.PathSegment("pkg/config")
if !ok {
return
}
dirVersion, isVersioned := versionFromDir(dir)
Expand Down
33 changes: 3 additions & 30 deletions lint/config_versions_registered.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,15 @@ func NewConfigVersionsRegistered() *ConfigVersionsRegistered {
}

func (c *ConfigVersionsRegistered) Check(p *cop.Pass) {
filename := p.Filename()
if !isVersionsGo(filename) {
if !p.FileMatches("pkg/config/versions.go") {
return
}

want, err := versionPackagesOnDisk(filepath.Dir(filename))
want, err := versionPackagesOnDisk(filepath.Dir(p.Filename()))
if err != nil || len(want) == 0 {
return
}
got := registeredPackages(p)
got := p.SelectorReceivers("Register")

var missing []string
for _, name := range want {
Expand All @@ -70,13 +69,6 @@ func (c *ConfigVersionsRegistered) Check(p *cop.Pass) {
"pkg/config/versions.go is missing Register call(s) for: %s", strings.Join(missing, ", "))
}

// isVersionsGo reports whether filename is the canonical
// pkg/config/versions.go used by the dispatch table.
func isVersionsGo(filename string) bool {
slash := filepath.ToSlash(filename)
return strings.HasSuffix(slash, "/pkg/config/versions.go") || slash == "pkg/config/versions.go"
}

// versionPackagesOnDisk lists the package directories under pkg/config/ that
// the dispatch table is expected to register: every vN/ and latest/.
func versionPackagesOnDisk(dir string) ([]string, error) {
Expand All @@ -102,25 +94,6 @@ func versionPackagesOnDisk(dir string) ([]string, error) {
return names, nil
}

// registeredPackages returns the set of package selectors that appear as
// `<pkg>.Register(...)` call expressions anywhere in the file. The cop only
// cares about whether a name is mentioned, not how many times.
func registeredPackages(p *cop.Pass) map[string]bool {
got := map[string]bool{}
p.ForEachCall(func(call *ast.CallExpr) {
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok || sel.Sel.Name != "Register" {
return
}
ident, ok := sel.X.(*ast.Ident)
if !ok {
return
}
got[ident.Name] = true
})
return got
}

// registryAnchor picks the AST node used to position the offense. Preferring
// the `versions` function declaration keeps the diagnostic close to the
// dispatch table; if that function is absent (unexpected), the file's
Expand Down
15 changes: 0 additions & 15 deletions lint/configpath.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,6 @@ import (
// pkg/config/<dir>/ paths and pkg/config/vN import paths, so each cop can
// focus on its rule rather than on regular expressions.

// configDirRe matches files under pkg/config/<dir>/. The captured group is
// the directory name immediately under pkg/config/. It accepts both
// absolute and relative paths.
var configDirRe = regexp.MustCompile(`(?:^|/)pkg/config/([^/]+)/[^/]+\.go$`)

// configDir returns the directory name immediately under pkg/config/ for
// filename, or "" if filename is not under pkg/config/<dir>/.
func configDir(filename string) string {
m := configDirRe.FindStringSubmatch(filepath.ToSlash(filename))
if m == nil {
return ""
}
return m[1]
}

// versionFromDir parses a "vN" directory name and returns N. Returns false
// for any other name (latest, types, vendor, ...).
func versionFromDir(dir string) (int, bool) {
Expand Down
Loading
Loading