Repository: mgechev/revive
Pull request #1405 (2025-06-16 20:10:32)

var-naming: handle possible panic
This PR fixes a panic when running revive with the following configuration:

```toml
[rule.var-naming]
arguments = [[], [], [{ extra-bad-package-names = ["helpers", 123] }]]
```

Before:
```console
❯ ./revive -config revive.toml ./...
panic: interface conversion: interface {} is int64, not string

goroutine 1 [running]:
github.com/mgechev/revive/rule.(*VarNamingRule).Configure(0x1053e2ec0, {0x140001d37d0, 0x3, 0xa?})
        /Users/alexandear/src/github.com/mgechev/revive/rule/var_naming.go:94 +0x554
github.com/mgechev/revive/config.GetLintingRules(0x140001cdab0, {0x10540a7e0, 0x0, 0x8?})
        /Users/alexandear/src/github.com/mgechev/revive/config/config.go:158 +0x3c8
github.com/mgechev/revive/revivelib.New(0x140001cdab0, 0x0, 0x0, {0x0, 0x0, 0x140000021c0?})
        /Users/alexandear/src/github.com/mgechev/revive/revivelib/core.go:56 +0x240
github.com/mgechev/revive/cli.RunRevive({0x0, 0x0, 0x0})
        /Users/alexandear/src/github.com/mgechev/revive/cli/main.go:55 +0x8c
main.main()
        /Users/alexandear/src/github.com/mgechev/revive/main.go:7 +0x28
```

After:
```console
❯ ./revive -config revive.toml ./...
initializing revive - getting lint rules: cannot configure rule: "var-naming": invalid third argument to the var-naming rule: expected element 1 of extraBadPackageNames to be a string, but got type int64
```

Follows #1404


Patch:
diff --git a/rule/unexported_return.go b/rule/unexported_return.go
index 7a51d71..e82dcff 100644
--- a/rule/unexported_return.go
+++ b/rule/unexported_return.go
@@ -78,7 +78,18 @@ func (*UnexportedReturnRule) Name() string {
 // It is imprecise, and will err on the side of returning true,
 // such as for composite types.
 func exportedType(typ types.Type) bool {
-	switch t := types.Unalias(typ).(type) {
+	switch t := typ.(type) {
+	case *types.Alias:
+		obj := t.Obj()
+		switch {
+		// Builtin types have no package.
+		case obj.Pkg() == nil:
+		case obj.Exported():
+		default:
+			_, ok := t.Underlying().(*types.Interface)
+			return ok
+		}
+		return true
 	case *types.Named:
 		obj := t.Obj()
 		switch {
diff --git a/rule/var_naming.go b/rule/var_naming.go
index 1ee85a7..b1c2489 100644
--- a/rule/var_naming.go
+++ b/rule/var_naming.go
@@ -87,11 +87,15 @@ func (r *VarNamingRule) Configure(arguments lint.Arguments) error {
 				if !ok {
 					return fmt.Errorf("invalid third argument to the var-naming rule. Expecting extraBadPackageNames of type slice of strings, but %T", v)
 				}
-				for _, name := range extraBadPackageNames {
+				for i, name := range extraBadPackageNames {
 					if r.extraBadPackageNames == nil {
 						r.extraBadPackageNames = map[string]struct{}{}
 					}
-					r.extraBadPackageNames[strings.ToLower(name.(string))] = struct{}{}
+					n, ok := name.(string)
+					if !ok {
+						return fmt.Errorf("invalid third argument to the var-naming rule: expected element %d of extraBadPackageNames to be a string, but got %v(%T)", i, name, name)
+					}
+					r.extraBadPackageNames[strings.ToLower(n)] = struct{}{}
 				}
 			}
 		}


Test patch:
diff --git a/rule/var_naming_test.go b/rule/var_naming_test.go
index e8d5eec..183ea95 100644
--- a/rule/var_naming_test.go
+++ b/rule/var_naming_test.go
@@ -117,6 +117,11 @@ func TestVarNamingRule_Configure(t *testing.T) {
 			arguments: lint.Arguments{[]any{""}, []any{""}, []any{map[string]any{"extraBadPackageNames": []int{1}}}},
 			wantErr:   errors.New("invalid third argument to the var-naming rule. Expecting extraBadPackageNames of type slice of strings, but []int"),
 		},
+		{
+			name:      "invalid third argument value type for extraBadPackageNames",
+			arguments: lint.Arguments{[]any{""}, []any{""}, []any{map[string]any{"extraBadPackageNames": []any{"helpers", 5}}}},
+			wantErr:   errors.New("invalid third argument to the var-naming rule: expected element 1 of extraBadPackageNames to be a string, but got 5(int)"),
+		},
 	}
 
 	for _, tt := range tests {
diff --git a/testdata/golint/unexported_return.go b/testdata/golint/unexported_return.go
index 226e5d3..56329d2 100644
--- a/testdata/golint/unexported_return.go
+++ b/testdata/golint/unexported_return.go
@@ -58,3 +58,31 @@ type int struct{}
 func ExportedIntReturner() int { // MATCH /exported func ExportedIntReturner returns unexported type foo.int, which can be annoying to use/
 	return int{}
 }
+
+type config struct {
+	N int
+}
+
+// Option ...
+type Option = option
+
+type option func(*config)
+
+// WithN ...
+func WithN(n int) Option {
+	return func(c *config) {
+		c.N = n
+	}
+}
+
+type b = A
+
+// A ...
+type A func(*config)
+
+// WithA ...
+func WithA(n int) b { // MATCH /exported func WithA returns unexported type foo.b, which can be annoying to use/
+	return func(c *config) {
+		c.N = n
+	}
+}