walk.go raw

   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  }
 342