Repository: mgechev/revive (go)

Issue:
nested-structs false positives with "struct{}"
I found some instances of "struct{}" causing the nested-structs check to fail. Based on other issues it looks like the intention is to not include struct{} in this check.
```
type test struct {
  foo []chan struct{}
  bar map[string]struct{}
}
```

If this is working as intended feel free to resolve.

p.s. Revive works great! Found multiple bugs in my code.

Pull request:
fix #824
Closes #824


Patch:
diff --git a/rule/nested-structs.go b/rule/nested-structs.go
index fd12269..147bd48 100644
--- a/rule/nested-structs.go
+++ b/rule/nested-structs.go
@@ -14,7 +14,6 @@ func (*NestedStructs) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
 	var failures []lint.Failure
 
 	walker := &lintNestedStructs{
-		fileAST: file.AST,
 		onFailure: func(failure lint.Failure) {
 			failures = append(failures, failure)
 		},
@@ -31,47 +30,46 @@ func (*NestedStructs) Name() string {
 }
 
 type lintNestedStructs struct {
-	fileAST   *ast.File
 	onFailure func(lint.Failure)
 }
 
 func (l *lintNestedStructs) Visit(n ast.Node) ast.Visitor {
-	switch v := n.(type) {
-	case *ast.TypeSpec:
-		_, isInterface := v.Type.(*ast.InterfaceType)
-		if isInterface {
-			return nil // do not analyze interface declarations
-		}
-	case *ast.FuncDecl:
-		if v.Body != nil {
-			ast.Walk(l, v.Body)
-		}
-		return nil
-	case *ast.Field:
-		_, isChannelField := v.Type.(*ast.ChanType)
-		if isChannelField {
-			return nil
-		}
+	if v, ok := n.(*ast.StructType); ok {
+		ls := &lintStruct{l.onFailure}
+		ast.Walk(ls, v.Fields)
+	}
 
-		filter := func(n ast.Node) bool {
-			switch n.(type) {
-			case *ast.StructType:
-				return true
-			default:
-				return false
-			}
-		}
-		structs := pick(v, filter, nil)
-		for _, s := range structs {
-			l.onFailure(lint.Failure{
-				Failure:    "no nested structs are allowed",
-				Category:   "style",
-				Node:       s,
-				Confidence: 1,
-			})
+	return l
+}
+
+type lintStruct struct {
+	onFailure func(lint.Failure)
+}
+
+func (l *lintStruct) Visit(n ast.Node) ast.Visitor {
+	switch s := n.(type) {
+	case *ast.StructType:
+		l.fail(s)
+		return nil
+	case *ast.ArrayType:
+		if _, ok := s.Elt.(*ast.StructType); ok {
+			l.fail(s)
 		}
-		return nil // no need to visit (again) the field
+		return nil
+	case *ast.ChanType:
+		return nil
+	case *ast.MapType:
+		return nil
+	default:
+		return l
 	}
+}
 
-	return l
+func (l *lintStruct) fail(n ast.Node) {
+	l.onFailure(lint.Failure{
+		Failure:    "no nested structs are allowed",
+		Category:   "style",
+		Node:       n,
+		Confidence: 1,
+	})
 }


Test patch:
diff --git a/testdata/nested-structs.go b/testdata/nested-structs.go
index b1e1102..18f5e2d 100644
--- a/testdata/nested-structs.go
+++ b/testdata/nested-structs.go
@@ -18,6 +18,17 @@ type Quux struct {
 type Quuz struct {
 }
 
+type Quiz struct {
+	s struct{} // MATCH /no nested structs are allowed/
+}
+
+type nestedStructInChan struct {
+	c chan struct {
+		a int
+		b struct{ c int } // MATCH /no nested structs are allowed/
+	}
+}
+
 func waldo() (s struct{ b bool }) { return s }
 
 func fred() interface{} {
@@ -45,3 +56,9 @@ type issue744 struct {
 type mySetInterface interface {
 	GetSet() map[string]struct{}
 }
+
+// issue 824
+type test struct {
+	foo []chan struct{}     // Must not match
+	bar map[string]struct{} // Must not match
+}