1package astwalk
2
3import (
4	"go/ast"
5	"go/token"
6	"go/types"
7
8	"github.com/go-toolsmith/astp"
9	"github.com/go-toolsmith/typep"
10)
11
12type typeExprWalker struct {
13	visitor TypeExprVisitor
14	info    *types.Info
15}
16
17func (w *typeExprWalker) WalkFile(f *ast.File) {
18	if !w.visitor.EnterFile(f) {
19		return
20	}
21
22	for _, decl := range f.Decls {
23		if decl, ok := decl.(*ast.FuncDecl); ok {
24			if !w.visitor.EnterFunc(decl) {
25				continue
26			}
27		}
28		switch decl := decl.(type) {
29		case *ast.FuncDecl:
30			if !w.visitor.EnterFunc(decl) {
31				continue
32			}
33			w.walkSignature(decl.Type)
34			ast.Inspect(decl.Body, w.walk)
35		case *ast.GenDecl:
36			if decl.Tok == token.IMPORT {
37				continue
38			}
39			ast.Inspect(decl, w.walk)
40		}
41	}
42}
43
44func (w *typeExprWalker) visit(x ast.Expr) bool {
45	w.visitor.VisitTypeExpr(x)
46	return !w.visitor.skipChilds()
47}
48
49func (w *typeExprWalker) walk(x ast.Node) bool {
50	switch x := x.(type) {
51	case *ast.ParenExpr:
52		if typep.IsTypeExpr(w.info, x.X) {
53			return w.visit(x)
54		}
55		return true
56	case *ast.CallExpr:
57		// Pointer conversions require parenthesis around pointer type.
58		// These casts are represented as call expressions.
59		// Because it's impossible for the visitor to distinguish such
60		// "required" parenthesis, walker skips outmost parenthesis in such cases.
61		return w.inspectInner(x.Fun)
62	case *ast.SelectorExpr:
63		// Like with conversions, method expressions are another special.
64		return w.inspectInner(x.X)
65	case *ast.StarExpr:
66		if typep.IsTypeExpr(w.info, x.X) {
67			return w.visit(x)
68		}
69		return true
70	case *ast.MapType:
71		return w.visit(x)
72	case *ast.FuncType:
73		return w.visit(x)
74	case *ast.StructType:
75		return w.visit(x)
76	case *ast.InterfaceType:
77		if !w.visit(x) {
78			return false
79		}
80		for _, method := range x.Methods.List {
81			switch x := method.Type.(type) {
82			case *ast.FuncType:
83				w.walkSignature(x)
84			default:
85				// Embedded interface.
86				w.walk(x)
87			}
88		}
89		return false
90	case *ast.ArrayType:
91		return w.visit(x)
92	}
93	return true
94}
95
96func (w *typeExprWalker) inspectInner(x ast.Expr) bool {
97	parens, ok := x.(*ast.ParenExpr)
98	if ok && typep.IsTypeExpr(w.info, parens.X) && astp.IsStarExpr(parens.X) {
99		ast.Inspect(parens.X, w.walk)
100		return false
101	}
102	return true
103}
104
105func (w *typeExprWalker) walkSignature(typ *ast.FuncType) {
106	for _, p := range typ.Params.List {
107		ast.Inspect(p.Type, w.walk)
108	}
109	if typ.Results != nil {
110		for _, p := range typ.Results.List {
111			ast.Inspect(p.Type, w.walk)
112		}
113	}
114}
115