cuelang.org/go@v0.13.0/internal/astinternal/debug.go (about) 1 // Copyright 2021 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package astinternal 16 17 import ( 18 "fmt" 19 gotoken "go/token" 20 "reflect" 21 "strconv" 22 "strings" 23 24 "cuelang.org/go/cue/ast" 25 "cuelang.org/go/cue/token" 26 "cuelang.org/go/internal" 27 ) 28 29 // AppendDebug writes a multi-line Go-like representation of a syntax tree node, 30 // including node position information and any relevant Go types. 31 func AppendDebug(dst []byte, node ast.Node, config DebugConfig) []byte { 32 d := &debugPrinter{ 33 cfg: config, 34 buf: dst, 35 } 36 if config.IncludeNodeRefs { 37 d.nodeRefs = make(map[ast.Node]int) 38 d.addNodeRefs(reflect.ValueOf(node)) 39 } 40 if d.value(reflect.ValueOf(node), nil) { 41 d.newline() 42 } 43 return d.buf 44 } 45 46 // DebugConfig configures the behavior of [AppendDebug]. 47 type DebugConfig struct { 48 // Filter is called before each value in a syntax tree. 49 // Values for which the function returns false are omitted. 50 Filter func(reflect.Value) bool 51 52 // OmitEmpty causes empty strings, empty structs, empty lists, 53 // nil pointers, invalid positions, and missing tokens to be omitted. 54 OmitEmpty bool 55 56 // IncludeNodeRefs causes a Node reference in an identifier 57 // to indicate which (if any) ast.Node it refers to. 58 IncludeNodeRefs bool 59 60 // IncludePointers causes all nodes to be printed with their pointer 61 // values; setting this also implies [DebugConfig.IncludeNodeRefs] 62 // and references will be printed as pointers. 63 IncludePointers bool 64 } 65 66 type debugPrinter struct { 67 buf []byte 68 cfg DebugConfig 69 level int 70 nodeRefs map[ast.Node]int 71 refID int 72 } 73 74 // value produces the given value, omitting type information if 75 // its type is the same as implied type. It reports whether 76 // anything was produced. 77 func (d *debugPrinter) value(v reflect.Value, impliedType reflect.Type) bool { 78 start := d.pos() 79 d.value0(v, impliedType) 80 return d.pos() > start 81 } 82 83 func (d *debugPrinter) value0(v reflect.Value, impliedType reflect.Type) { 84 if d.cfg.Filter != nil && !d.cfg.Filter(v) { 85 return 86 } 87 // Skip over interfaces and pointers, stopping early if nil. 88 concreteType := v.Type() 89 refName := "" 90 ptrVal := uintptr(0) 91 for { 92 k := v.Kind() 93 if k != reflect.Interface && k != reflect.Pointer { 94 break 95 } 96 if v.IsNil() { 97 if !d.cfg.OmitEmpty { 98 d.printf("nil") 99 } 100 return 101 } 102 if k == reflect.Pointer { 103 if n, ok := v.Interface().(ast.Node); ok { 104 ptrVal = v.Pointer() 105 if id, ok := d.nodeRefs[n]; ok { 106 refName = refIDToName(id) 107 } 108 } 109 } 110 v = v.Elem() 111 if k == reflect.Interface { 112 // For example, *ast.Ident can be the concrete type behind an ast.Expr. 113 concreteType = v.Type() 114 } 115 } 116 117 if d.cfg.OmitEmpty && v.IsZero() { 118 return 119 } 120 121 t := v.Type() 122 switch v := v.Interface().(type) { 123 // Simple types which can stringify themselves. 124 case token.Pos: 125 d.printf("%s(%q", t, v) 126 // Show relative positions too, if there are any, as they affect formatting. 127 if v.HasRelPos() { 128 d.printf(", %v", v.RelPos()) 129 } 130 d.printf(")") 131 return 132 case token.Token: 133 d.printf("%s(%q)", t, v) 134 return 135 } 136 137 switch t.Kind() { 138 default: 139 // We assume all other kinds are basic in practice, like string or bool. 140 if t.PkgPath() != "" { 141 // Mention defined and non-predeclared types, for clarity. 142 d.printf("%s(%#v)", t, v) 143 } else { 144 d.printf("%#v", v) 145 } 146 147 case reflect.Slice, reflect.Struct: 148 valueStart := d.pos() 149 // We print the concrete type when it differs from an implied type. 150 if concreteType != impliedType { 151 d.printf("%s", concreteType) 152 } 153 if d.cfg.IncludePointers { 154 if ptrVal != 0 { 155 d.printf("@%#x", ptrVal) 156 } 157 } else if refName != "" { 158 d.printf("@%s", refName) 159 } 160 d.printf("{") 161 d.level++ 162 var anyElems bool 163 if t.Kind() == reflect.Slice { 164 anyElems = d.sliceElems(v, t.Elem()) 165 } else { 166 anyElems = d.structFields(v) 167 } 168 d.level-- 169 if !anyElems && d.cfg.OmitEmpty { 170 d.truncate(valueStart) 171 } else { 172 if anyElems { 173 d.newline() 174 } 175 d.printf("}") 176 } 177 } 178 } 179 180 func (d *debugPrinter) sliceElems(v reflect.Value, elemType reflect.Type) (anyElems bool) { 181 for i := 0; i < v.Len(); i++ { 182 ev := v.Index(i) 183 elemStart := d.pos() 184 d.newline() 185 // Note: a slice literal implies the type of its elements 186 // so we can avoid mentioning the type 187 // of each element if it matches. 188 if d.value(ev, elemType) { 189 anyElems = true 190 } else { 191 d.truncate(elemStart) 192 } 193 } 194 return anyElems 195 } 196 197 func (d *debugPrinter) structFields(v reflect.Value) (anyElems bool) { 198 t := v.Type() 199 for i := 0; i < v.NumField(); i++ { 200 f := t.Field(i) 201 if !gotoken.IsExported(f.Name) { 202 continue 203 } 204 if f.Name == "Node" { 205 nodeVal := v.Field(i) 206 if (!d.cfg.IncludeNodeRefs && !d.cfg.IncludePointers) || nodeVal.IsNil() { 207 continue 208 } 209 d.newline() 210 if d.cfg.IncludePointers { 211 if nodeVal.Kind() == reflect.Interface { 212 nodeVal = nodeVal.Elem() 213 } 214 d.printf("Node: @%#v (%v)", nodeVal.Pointer(), nodeVal.Elem().Type()) 215 } else { 216 d.printf("Node: @%s (%v)", refIDToName(d.nodeRefs[nodeVal.Interface().(ast.Node)]), nodeVal.Elem().Type()) 217 } 218 continue 219 } 220 switch f.Name { 221 // These fields are cyclic, and they don't represent the syntax anyway. 222 case "Scope", "Unresolved": 223 continue 224 } 225 elemStart := d.pos() 226 d.newline() 227 d.printf("%s: ", f.Name) 228 if d.value(v.Field(i), nil) { 229 anyElems = true 230 } else { 231 d.truncate(elemStart) 232 } 233 } 234 val := v.Addr().Interface() 235 if val, ok := val.(ast.Node); ok { 236 // Comments attached to a node aren't a regular field, but are still useful. 237 // The majority of nodes won't have comments, so skip them when empty. 238 if comments := ast.Comments(val); len(comments) > 0 { 239 anyElems = true 240 d.newline() 241 d.printf("Comments: ") 242 d.value(reflect.ValueOf(comments), nil) 243 } 244 } 245 return anyElems 246 } 247 248 func (d *debugPrinter) printf(format string, args ...any) { 249 d.buf = fmt.Appendf(d.buf, format, args...) 250 } 251 252 func (d *debugPrinter) newline() { 253 d.buf = fmt.Appendf(d.buf, "\n%s", strings.Repeat("\t", d.level)) 254 } 255 256 func (d *debugPrinter) pos() int { 257 return len(d.buf) 258 } 259 260 func (d *debugPrinter) truncate(pos int) { 261 d.buf = d.buf[:pos] 262 } 263 264 // addNodeRefs does a first pass over the value looking for 265 // [ast.Ident] nodes that refer to other nodes. 266 // This means when we find such a node, we can include 267 // an anchor name for it 268 func (d *debugPrinter) addNodeRefs(v reflect.Value) { 269 // Skip over interfaces and pointers, stopping early if nil. 270 for ; v.Kind() == reflect.Interface || v.Kind() == reflect.Pointer; v = v.Elem() { 271 if v.IsNil() { 272 return 273 } 274 } 275 276 t := v.Type() 277 switch v := v.Interface().(type) { 278 case token.Pos, token.Token: 279 // Simple types which can't contain an ast.Node. 280 return 281 case ast.Ident: 282 if v.Node != nil { 283 if _, ok := d.nodeRefs[v.Node]; !ok { 284 d.refID++ 285 d.nodeRefs[v.Node] = d.refID 286 } 287 } 288 return 289 } 290 291 switch t.Kind() { 292 case reflect.Slice: 293 for i := 0; i < v.Len(); i++ { 294 d.addNodeRefs(v.Index(i)) 295 } 296 case reflect.Struct: 297 t := v.Type() 298 for i := 0; i < v.NumField(); i++ { 299 f := t.Field(i) 300 if !gotoken.IsExported(f.Name) { 301 continue 302 } 303 switch f.Name { 304 // These fields don't point to any nodes that Node can refer to. 305 case "Scope", "Node", "Unresolved": 306 continue 307 } 308 d.addNodeRefs(v.Field(i)) 309 } 310 } 311 } 312 313 func refIDToName(id int) string { 314 if id == 0 { 315 return "unknown" 316 } 317 return fmt.Sprintf("ref%03d", id) 318 } 319 320 func DebugStr(x interface{}) (out string) { 321 if n, ok := x.(ast.Node); ok { 322 comments := "" 323 for _, g := range ast.Comments(n) { 324 comments += DebugStr(g) 325 } 326 if comments != "" { 327 defer func() { out = "<" + comments + out + ">" }() 328 } 329 } 330 switch v := x.(type) { 331 case *ast.File: 332 out := "" 333 out += DebugStr(v.Decls) 334 return out 335 336 case *ast.Package: 337 out := "package " 338 out += DebugStr(v.Name) 339 return out 340 341 case *ast.LetClause: 342 out := "let " 343 out += DebugStr(v.Ident) 344 out += "=" 345 out += DebugStr(v.Expr) 346 return out 347 348 case *ast.Alias: 349 out := DebugStr(v.Ident) 350 out += "=" 351 out += DebugStr(v.Expr) 352 return out 353 354 case *ast.BottomLit: 355 return "_|_" 356 357 case *ast.BasicLit: 358 return v.Value 359 360 case *ast.Interpolation: 361 for _, e := range v.Elts { 362 out += DebugStr(e) 363 } 364 return out 365 366 case *ast.EmbedDecl: 367 out += DebugStr(v.Expr) 368 return out 369 370 case *ast.ImportDecl: 371 out := "import " 372 if v.Lparen != token.NoPos { 373 out += "( " 374 out += DebugStr(v.Specs) 375 out += " )" 376 } else { 377 out += DebugStr(v.Specs) 378 } 379 return out 380 381 case *ast.Comprehension: 382 out := DebugStr(v.Clauses) 383 out += DebugStr(v.Value) 384 return out 385 386 case *ast.StructLit: 387 out := "{" 388 out += DebugStr(v.Elts) 389 out += "}" 390 return out 391 392 case *ast.ListLit: 393 out := "[" 394 out += DebugStr(v.Elts) 395 out += "]" 396 return out 397 398 case *ast.Ellipsis: 399 out := "..." 400 if v.Type != nil { 401 out += DebugStr(v.Type) 402 } 403 return out 404 405 case *ast.ForClause: 406 out := "for " 407 if v.Key != nil { 408 out += DebugStr(v.Key) 409 out += ": " 410 } 411 out += DebugStr(v.Value) 412 out += " in " 413 out += DebugStr(v.Source) 414 return out 415 416 case *ast.IfClause: 417 out := "if " 418 out += DebugStr(v.Condition) 419 return out 420 421 case *ast.Field: 422 out := DebugStr(v.Label) 423 if t, ok := internal.ConstraintToken(v); ok { 424 out += t.String() 425 } 426 if v.Value != nil { 427 switch v.Token { 428 case token.ILLEGAL, token.COLON: 429 out += ": " 430 default: 431 out += fmt.Sprintf(" %s ", v.Token) 432 } 433 out += DebugStr(v.Value) 434 for _, a := range v.Attrs { 435 out += " " 436 out += DebugStr(a) 437 } 438 } 439 return out 440 441 case *ast.Attribute: 442 return v.Text 443 444 case *ast.Ident: 445 return v.Name 446 447 case *ast.SelectorExpr: 448 return DebugStr(v.X) + "." + DebugStr(v.Sel) 449 450 case *ast.CallExpr: 451 out := DebugStr(v.Fun) 452 out += "(" 453 out += DebugStr(v.Args) 454 out += ")" 455 return out 456 457 case *ast.ParenExpr: 458 out := "(" 459 out += DebugStr(v.X) 460 out += ")" 461 return out 462 463 case *ast.UnaryExpr: 464 return v.Op.String() + DebugStr(v.X) 465 466 case *ast.BinaryExpr: 467 out := DebugStr(v.X) 468 op := v.Op.String() 469 if 'a' <= op[0] && op[0] <= 'z' { 470 op = fmt.Sprintf(" %s ", op) 471 } 472 out += op 473 out += DebugStr(v.Y) 474 return out 475 476 case []*ast.CommentGroup: 477 var a []string 478 for _, c := range v { 479 a = append(a, DebugStr(c)) 480 } 481 return strings.Join(a, "\n") 482 483 case *ast.CommentGroup: 484 str := "[" 485 if v.Doc { 486 str += "d" 487 } 488 if v.Line { 489 str += "l" 490 } 491 str += strconv.Itoa(int(v.Position)) 492 var a = []string{} 493 for _, c := range v.List { 494 a = append(a, c.Text) 495 } 496 return str + strings.Join(a, " ") + "] " 497 498 case *ast.IndexExpr: 499 out := DebugStr(v.X) 500 out += "[" 501 out += DebugStr(v.Index) 502 out += "]" 503 return out 504 505 case *ast.SliceExpr: 506 out := DebugStr(v.X) 507 out += "[" 508 out += DebugStr(v.Low) 509 out += ":" 510 out += DebugStr(v.High) 511 out += "]" 512 return out 513 514 case *ast.ImportSpec: 515 out := "" 516 if v.Name != nil { 517 out += DebugStr(v.Name) 518 out += " " 519 } 520 out += DebugStr(v.Path) 521 return out 522 523 case *ast.Func: 524 return fmt.Sprintf("func(%v): %v", DebugStr(v.Args), DebugStr(v.Ret)) 525 526 case []ast.Decl: 527 if len(v) == 0 { 528 return "" 529 } 530 out := "" 531 for _, d := range v { 532 out += DebugStr(d) 533 out += sep 534 } 535 return out[:len(out)-len(sep)] 536 537 case []ast.Clause: 538 if len(v) == 0 { 539 return "" 540 } 541 out := "" 542 for _, c := range v { 543 out += DebugStr(c) 544 out += " " 545 } 546 return out 547 548 case []ast.Expr: 549 if len(v) == 0 { 550 return "" 551 } 552 out := "" 553 for _, d := range v { 554 out += DebugStr(d) 555 out += sep 556 } 557 return out[:len(out)-len(sep)] 558 559 case []*ast.ImportSpec: 560 if len(v) == 0 { 561 return "" 562 } 563 out := "" 564 for _, d := range v { 565 out += DebugStr(d) 566 out += sep 567 } 568 return out[:len(out)-len(sep)] 569 570 default: 571 if v == nil { 572 return "" 573 } 574 return fmt.Sprintf("<%T>", x) 575 } 576 } 577 578 const sep = ", "