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