walk.go (7924B)
1 // Copyright 2025 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package inspector 6 7 // This file is a fork of ast.Inspect to reduce unnecessary dynamic 8 // calls and to gather edge information. 9 // 10 // Consistency with the original is ensured by TestInspectAllNodes. 11 12 import ( 13 "fmt" 14 "go/ast" 15 16 "golang.org/x/tools/go/ast/edge" 17 ) 18 19 func walkList[N ast.Node](v *visitor, ek edge.Kind, list []N) { 20 for i, node := range list { 21 walk(v, ek, i, node) 22 } 23 } 24 25 func walk(v *visitor, ek edge.Kind, index int, node ast.Node) { 26 v.push(ek, index, node) 27 28 // walk children 29 // (the order of the cases matches the order 30 // of the corresponding node types in ast.go) 31 switch n := node.(type) { 32 // Comments and fields 33 case *ast.Comment: 34 // nothing to do 35 36 case *ast.CommentGroup: 37 walkList(v, edge.CommentGroup_List, n.List) 38 39 case *ast.Field: 40 if n.Doc != nil { 41 walk(v, edge.Field_Doc, -1, n.Doc) 42 } 43 walkList(v, edge.Field_Names, n.Names) 44 if n.Type != nil { 45 walk(v, edge.Field_Type, -1, n.Type) 46 } 47 if n.Tag != nil { 48 walk(v, edge.Field_Tag, -1, n.Tag) 49 } 50 if n.Comment != nil { 51 walk(v, edge.Field_Comment, -1, n.Comment) 52 } 53 54 case *ast.FieldList: 55 walkList(v, edge.FieldList_List, n.List) 56 57 // Expressions 58 case *ast.BadExpr, *ast.Ident, *ast.BasicLit: 59 // nothing to do 60 61 case *ast.Ellipsis: 62 if n.Elt != nil { 63 walk(v, edge.Ellipsis_Elt, -1, n.Elt) 64 } 65 66 case *ast.FuncLit: 67 walk(v, edge.FuncLit_Type, -1, n.Type) 68 walk(v, edge.FuncLit_Body, -1, n.Body) 69 70 case *ast.CompositeLit: 71 if n.Type != nil { 72 walk(v, edge.CompositeLit_Type, -1, n.Type) 73 } 74 walkList(v, edge.CompositeLit_Elts, n.Elts) 75 76 case *ast.ParenExpr: 77 walk(v, edge.ParenExpr_X, -1, n.X) 78 79 case *ast.SelectorExpr: 80 walk(v, edge.SelectorExpr_X, -1, n.X) 81 walk(v, edge.SelectorExpr_Sel, -1, n.Sel) 82 83 case *ast.IndexExpr: 84 walk(v, edge.IndexExpr_X, -1, n.X) 85 walk(v, edge.IndexExpr_Index, -1, n.Index) 86 87 case *ast.IndexListExpr: 88 walk(v, edge.IndexListExpr_X, -1, n.X) 89 walkList(v, edge.IndexListExpr_Indices, n.Indices) 90 91 case *ast.SliceExpr: 92 walk(v, edge.SliceExpr_X, -1, n.X) 93 if n.Low != nil { 94 walk(v, edge.SliceExpr_Low, -1, n.Low) 95 } 96 if n.High != nil { 97 walk(v, edge.SliceExpr_High, -1, n.High) 98 } 99 if n.Max != nil { 100 walk(v, edge.SliceExpr_Max, -1, n.Max) 101 } 102 103 case *ast.TypeAssertExpr: 104 walk(v, edge.TypeAssertExpr_X, -1, n.X) 105 if n.Type != nil { 106 walk(v, edge.TypeAssertExpr_Type, -1, n.Type) 107 } 108 109 case *ast.CallExpr: 110 walk(v, edge.CallExpr_Fun, -1, n.Fun) 111 walkList(v, edge.CallExpr_Args, n.Args) 112 113 case *ast.StarExpr: 114 walk(v, edge.StarExpr_X, -1, n.X) 115 116 case *ast.UnaryExpr: 117 walk(v, edge.UnaryExpr_X, -1, n.X) 118 119 case *ast.BinaryExpr: 120 walk(v, edge.BinaryExpr_X, -1, n.X) 121 walk(v, edge.BinaryExpr_Y, -1, n.Y) 122 123 case *ast.KeyValueExpr: 124 walk(v, edge.KeyValueExpr_Key, -1, n.Key) 125 walk(v, edge.KeyValueExpr_Value, -1, n.Value) 126 127 // Types 128 case *ast.ArrayType: 129 if n.Len != nil { 130 walk(v, edge.ArrayType_Len, -1, n.Len) 131 } 132 walk(v, edge.ArrayType_Elt, -1, n.Elt) 133 134 case *ast.StructType: 135 walk(v, edge.StructType_Fields, -1, n.Fields) 136 137 case *ast.FuncType: 138 if n.TypeParams != nil { 139 walk(v, edge.FuncType_TypeParams, -1, n.TypeParams) 140 } 141 if n.Params != nil { 142 walk(v, edge.FuncType_Params, -1, n.Params) 143 } 144 if n.Results != nil { 145 walk(v, edge.FuncType_Results, -1, n.Results) 146 } 147 148 case *ast.InterfaceType: 149 walk(v, edge.InterfaceType_Methods, -1, n.Methods) 150 151 case *ast.MapType: 152 walk(v, edge.MapType_Key, -1, n.Key) 153 walk(v, edge.MapType_Value, -1, n.Value) 154 155 case *ast.ChanType: 156 walk(v, edge.ChanType_Value, -1, n.Value) 157 158 // Statements 159 case *ast.BadStmt: 160 // nothing to do 161 162 case *ast.DeclStmt: 163 walk(v, edge.DeclStmt_Decl, -1, n.Decl) 164 165 case *ast.EmptyStmt: 166 // nothing to do 167 168 case *ast.LabeledStmt: 169 walk(v, edge.LabeledStmt_Label, -1, n.Label) 170 walk(v, edge.LabeledStmt_Stmt, -1, n.Stmt) 171 172 case *ast.ExprStmt: 173 walk(v, edge.ExprStmt_X, -1, n.X) 174 175 case *ast.SendStmt: 176 walk(v, edge.SendStmt_Chan, -1, n.Chan) 177 walk(v, edge.SendStmt_Value, -1, n.Value) 178 179 case *ast.IncDecStmt: 180 walk(v, edge.IncDecStmt_X, -1, n.X) 181 182 case *ast.AssignStmt: 183 walkList(v, edge.AssignStmt_Lhs, n.Lhs) 184 walkList(v, edge.AssignStmt_Rhs, n.Rhs) 185 186 case *ast.GoStmt: 187 walk(v, edge.GoStmt_Call, -1, n.Call) 188 189 case *ast.DeferStmt: 190 walk(v, edge.DeferStmt_Call, -1, n.Call) 191 192 case *ast.ReturnStmt: 193 walkList(v, edge.ReturnStmt_Results, n.Results) 194 195 case *ast.BranchStmt: 196 if n.Label != nil { 197 walk(v, edge.BranchStmt_Label, -1, n.Label) 198 } 199 200 case *ast.BlockStmt: 201 walkList(v, edge.BlockStmt_List, n.List) 202 203 case *ast.IfStmt: 204 if n.Init != nil { 205 walk(v, edge.IfStmt_Init, -1, n.Init) 206 } 207 walk(v, edge.IfStmt_Cond, -1, n.Cond) 208 walk(v, edge.IfStmt_Body, -1, n.Body) 209 if n.Else != nil { 210 walk(v, edge.IfStmt_Else, -1, n.Else) 211 } 212 213 case *ast.CaseClause: 214 walkList(v, edge.CaseClause_List, n.List) 215 walkList(v, edge.CaseClause_Body, n.Body) 216 217 case *ast.SwitchStmt: 218 if n.Init != nil { 219 walk(v, edge.SwitchStmt_Init, -1, n.Init) 220 } 221 if n.Tag != nil { 222 walk(v, edge.SwitchStmt_Tag, -1, n.Tag) 223 } 224 walk(v, edge.SwitchStmt_Body, -1, n.Body) 225 226 case *ast.TypeSwitchStmt: 227 if n.Init != nil { 228 walk(v, edge.TypeSwitchStmt_Init, -1, n.Init) 229 } 230 walk(v, edge.TypeSwitchStmt_Assign, -1, n.Assign) 231 walk(v, edge.TypeSwitchStmt_Body, -1, n.Body) 232 233 case *ast.CommClause: 234 if n.Comm != nil { 235 walk(v, edge.CommClause_Comm, -1, n.Comm) 236 } 237 walkList(v, edge.CommClause_Body, n.Body) 238 239 case *ast.SelectStmt: 240 walk(v, edge.SelectStmt_Body, -1, n.Body) 241 242 case *ast.ForStmt: 243 if n.Init != nil { 244 walk(v, edge.ForStmt_Init, -1, n.Init) 245 } 246 if n.Cond != nil { 247 walk(v, edge.ForStmt_Cond, -1, n.Cond) 248 } 249 if n.Post != nil { 250 walk(v, edge.ForStmt_Post, -1, n.Post) 251 } 252 walk(v, edge.ForStmt_Body, -1, n.Body) 253 254 case *ast.RangeStmt: 255 if n.Key != nil { 256 walk(v, edge.RangeStmt_Key, -1, n.Key) 257 } 258 if n.Value != nil { 259 walk(v, edge.RangeStmt_Value, -1, n.Value) 260 } 261 walk(v, edge.RangeStmt_X, -1, n.X) 262 walk(v, edge.RangeStmt_Body, -1, n.Body) 263 264 // Declarations 265 case *ast.ImportSpec: 266 if n.Doc != nil { 267 walk(v, edge.ImportSpec_Doc, -1, n.Doc) 268 } 269 if n.Name != nil { 270 walk(v, edge.ImportSpec_Name, -1, n.Name) 271 } 272 walk(v, edge.ImportSpec_Path, -1, n.Path) 273 if n.Comment != nil { 274 walk(v, edge.ImportSpec_Comment, -1, n.Comment) 275 } 276 277 case *ast.ValueSpec: 278 if n.Doc != nil { 279 walk(v, edge.ValueSpec_Doc, -1, n.Doc) 280 } 281 walkList(v, edge.ValueSpec_Names, n.Names) 282 if n.Type != nil { 283 walk(v, edge.ValueSpec_Type, -1, n.Type) 284 } 285 walkList(v, edge.ValueSpec_Values, n.Values) 286 if n.Comment != nil { 287 walk(v, edge.ValueSpec_Comment, -1, n.Comment) 288 } 289 290 case *ast.TypeSpec: 291 if n.Doc != nil { 292 walk(v, edge.TypeSpec_Doc, -1, n.Doc) 293 } 294 walk(v, edge.TypeSpec_Name, -1, n.Name) 295 if n.TypeParams != nil { 296 walk(v, edge.TypeSpec_TypeParams, -1, n.TypeParams) 297 } 298 walk(v, edge.TypeSpec_Type, -1, n.Type) 299 if n.Comment != nil { 300 walk(v, edge.TypeSpec_Comment, -1, n.Comment) 301 } 302 303 case *ast.BadDecl: 304 // nothing to do 305 306 case *ast.GenDecl: 307 if n.Doc != nil { 308 walk(v, edge.GenDecl_Doc, -1, n.Doc) 309 } 310 walkList(v, edge.GenDecl_Specs, n.Specs) 311 312 case *ast.FuncDecl: 313 if n.Doc != nil { 314 walk(v, edge.FuncDecl_Doc, -1, n.Doc) 315 } 316 if n.Recv != nil { 317 walk(v, edge.FuncDecl_Recv, -1, n.Recv) 318 } 319 walk(v, edge.FuncDecl_Name, -1, n.Name) 320 walk(v, edge.FuncDecl_Type, -1, n.Type) 321 if n.Body != nil { 322 walk(v, edge.FuncDecl_Body, -1, n.Body) 323 } 324 325 case *ast.File: 326 if n.Doc != nil { 327 walk(v, edge.File_Doc, -1, n.Doc) 328 } 329 walk(v, edge.File_Name, -1, n.Name) 330 walkList(v, edge.File_Decls, n.Decls) 331 // don't walk n.Comments - they have been 332 // visited already through the individual 333 // nodes 334 335 default: 336 // (includes *ast.Package) 337 panic(fmt.Sprintf("Walk: unexpected node type %T", n)) 338 } 339 340 v.pop(node) 341 }