github.com/jhump/protoreflect@v1.16.0/desc/protoprint/print.go (about) 1 package protoprint 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "math" 8 "os" 9 "path/filepath" 10 "reflect" 11 "sort" 12 "strings" 13 "unicode" 14 "unicode/utf8" 15 16 protov1 "github.com/golang/protobuf/proto" 17 "google.golang.org/protobuf/proto" 18 "google.golang.org/protobuf/reflect/protoreflect" 19 "google.golang.org/protobuf/reflect/protoregistry" 20 "google.golang.org/protobuf/types/descriptorpb" 21 "google.golang.org/protobuf/types/dynamicpb" 22 23 "github.com/jhump/protoreflect/desc" 24 "github.com/jhump/protoreflect/desc/internal" 25 ) 26 27 // Printer knows how to format file descriptors as proto source code. Its fields 28 // provide some control over how the resulting source file is constructed and 29 // formatted. 30 type Printer struct { 31 // If true, comments are rendered using "/*" style comments. Otherwise, they 32 // are printed using "//" style line comments. 33 PreferMultiLineStyleComments bool 34 35 // If true, elements are sorted into a canonical order. 36 // 37 // The canonical order for elements in a file follows: 38 // 1. Syntax 39 // 2. Package 40 // 3. Imports (sorted lexically) 41 // 4. Options (sorted by name, standard options before custom options) 42 // 5. Messages (sorted by name) 43 // 6. Enums (sorted by name) 44 // 7. Services (sorted by name) 45 // 8. Extensions (grouped by extendee, sorted by extendee+tag) 46 // 47 // The canonical order of elements in a message follows: 48 // 1. Options (sorted by name, standard options before custom options) 49 // 2. Fields and One-Ofs (sorted by tag; one-ofs interleaved based on the 50 // minimum tag therein) 51 // 3. Nested Messages (sorted by name) 52 // 4. Nested Enums (sorted by name) 53 // 5. Extension ranges (sorted by starting tag number) 54 // 6. Nested Extensions (grouped by extendee, sorted by extendee+tag) 55 // 7. Reserved ranges (sorted by starting tag number) 56 // 8. Reserved names (sorted lexically) 57 // 58 // Methods are sorted within a service by name and appear after any service 59 // options (which are sorted by name, standard options before custom ones). 60 // Enum values are sorted within an enum, first by numeric value then by 61 // name, and also appear after any enum options. 62 // 63 // Options for fields, enum values, and extension ranges are sorted by name, 64 // standard options before custom ones. 65 SortElements bool 66 67 // The "less" function used to sort elements when printing. It is given two 68 // elements, a and b, and should return true if a is "less than" b. In this 69 // case, "less than" means that element a should appear earlier in the file 70 // than element b. 71 // 72 // If this field is nil, no custom sorting is done and the SortElements 73 // field is consulted to decide how to order the output. If this field is 74 // non-nil, the SortElements field is ignored and this function is called to 75 // order elements. 76 CustomSortFunction func(a, b Element) bool 77 78 // The indentation used. Any characters other than spaces or tabs will be 79 // replaced with spaces. If unset/empty, two spaces will be used. 80 Indent string 81 82 // If true, detached comments (between elements) will be ignored. 83 // 84 // Deprecated: Use OmitComments bitmask instead. 85 OmitDetachedComments bool 86 87 // A bitmask of comment types to omit. If unset, all comments will be 88 // included. Use CommentsAll to not print any comments. 89 OmitComments CommentType 90 91 // If true, trailing comments that typically appear on the same line as an 92 // element (option, field, enum value, method) will be printed on a separate 93 // line instead. 94 // 95 // So, with this set, you'll get output like so: 96 // 97 // // leading comment for field 98 // repeated string names = 1; 99 // // trailing comment 100 // 101 // If left false, the printer will try to emit trailing comments on the same 102 // line instead: 103 // 104 // // leading comment for field 105 // repeated string names = 1; // trailing comment 106 // 107 // If the trailing comment has more than one line, it will automatically be 108 // forced to the next line. 109 TrailingCommentsOnSeparateLine bool 110 111 // If true, the printed output will eschew any blank lines, which otherwise 112 // appear between descriptor elements and comment blocks. Note that if 113 // detached comments are being printed, this will cause them to be merged 114 // into the subsequent leading comments. Similarly, any element trailing 115 // comments will be merged into the subsequent leading comments. 116 Compact bool 117 118 // If true, all references to messages, extensions, and enums (such as in 119 // options, field types, and method request and response types) will be 120 // fully-qualified. When left unset, the referenced elements will contain 121 // only as much qualifier as is required. 122 // 123 // For example, if a message is in the same package as the reference, the 124 // simple name can be used. If a message shares some context with the 125 // reference, only the unshared context needs to be included. For example: 126 // 127 // message Foo { 128 // message Bar { 129 // enum Baz { 130 // ZERO = 0; 131 // ONE = 1; 132 // } 133 // } 134 // 135 // // This field shares some context as the enum it references: they are 136 // // both inside of the namespace Foo: 137 // // field is "Foo.my_baz" 138 // // enum is "Foo.Bar.Baz" 139 // // So we only need to qualify the reference with the context that they 140 // // do NOT have in common: 141 // Bar.Baz my_baz = 1; 142 // } 143 // 144 // When printing fully-qualified names, they will be preceded by a dot, to 145 // avoid any ambiguity that they might be relative vs. fully-qualified. 146 ForceFullyQualifiedNames bool 147 148 // The number of options that trigger short options expressions to be 149 // rendered using multiple lines. Short options expressions are those 150 // found on fields and enum values, that use brackets ("[" and "]") and 151 // comma-separated options. If more options than this are present, they 152 // will be expanded to multiple lines (one option per line). 153 // 154 // If unset (e.g. if zero), a default threshold of 3 is used. 155 ShortOptionsExpansionThresholdCount int 156 157 // The length of printed options that trigger short options expressions to 158 // be rendered using multiple lines. If the short options contain more than 159 // one option and their printed length is longer than this threshold, they 160 // will be expanded to multiple lines (one option per line). 161 // 162 // If unset (e.g. if zero), a default threshold of 50 is used. 163 ShortOptionsExpansionThresholdLength int 164 165 // The length of a printed option value message literal that triggers the 166 // message literal to be rendered using multiple lines instead of using a 167 // compact single-line form. The message must include at least two fields 168 // or contain a field that is a nested message to be expanded. 169 // 170 // This value is further used to decide when to expand individual field 171 // values that are nested message literals or array literals (for repeated 172 // fields). 173 // 174 // If unset (e.g. if zero), a default threshold of 50 is used. 175 MessageLiteralExpansionThresholdLength int 176 } 177 178 // CommentType is a kind of comments in a proto source file. This can be used 179 // as a bitmask. 180 type CommentType int 181 182 const ( 183 // CommentsDetached refers to comments that are not "attached" to any 184 // source element. They are attributed to the subsequent element in the 185 // file as "detached" comments. 186 CommentsDetached CommentType = 1 << iota 187 // CommentsTrailing refers to a comment block immediately following an 188 // element in the source file. If another element immediately follows 189 // the trailing comment, it is instead considered a leading comment for 190 // that subsequent element. 191 CommentsTrailing 192 // CommentsLeading refers to a comment block immediately preceding an 193 // element in the source file. For high-level elements (those that have 194 // their own descriptor), these are used as doc comments for that element. 195 CommentsLeading 196 // CommentsTokens refers to any comments (leading, trailing, or detached) 197 // on low-level elements in the file. "High-level" elements have their own 198 // descriptors, e.g. messages, enums, fields, services, and methods. But 199 // comments can appear anywhere (such as around identifiers and keywords, 200 // sprinkled inside the declarations of a high-level element). This class 201 // of comments are for those extra comments sprinkled into the file. 202 CommentsTokens 203 204 // CommentsNonDoc refers to comments that are *not* doc comments. This is a 205 // bitwise union of everything other than CommentsLeading. If you configure 206 // a printer to omit this, only doc comments on descriptor elements will be 207 // included in the printed output. 208 CommentsNonDoc = CommentsDetached | CommentsTrailing | CommentsTokens 209 // CommentsAll indicates all kinds of comments. If you configure a printer 210 // to omit this, no comments will appear in the printed output, even if the 211 // input descriptors had source info and comments. 212 CommentsAll = -1 213 ) 214 215 // PrintProtoFiles prints all of the given file descriptors. The given open 216 // function is given a file name and is responsible for creating the outputs and 217 // returning the corresponding writer. 218 func (p *Printer) PrintProtoFiles(fds []*desc.FileDescriptor, open func(name string) (io.WriteCloser, error)) error { 219 for _, fd := range fds { 220 w, err := open(fd.GetName()) 221 if err != nil { 222 return fmt.Errorf("failed to open %s: %v", fd.GetName(), err) 223 } 224 err = func() error { 225 defer w.Close() 226 return p.PrintProtoFile(fd, w) 227 }() 228 if err != nil { 229 return fmt.Errorf("failed to write %s: %v", fd.GetName(), err) 230 } 231 } 232 return nil 233 } 234 235 // PrintProtosToFileSystem prints all of the given file descriptors to files in 236 // the given directory. If file names in the given descriptors include path 237 // information, they will be relative to the given root. 238 func (p *Printer) PrintProtosToFileSystem(fds []*desc.FileDescriptor, rootDir string) error { 239 return p.PrintProtoFiles(fds, func(name string) (io.WriteCloser, error) { 240 fullPath := filepath.Join(rootDir, name) 241 dir := filepath.Dir(fullPath) 242 if err := os.MkdirAll(dir, os.ModePerm); err != nil { 243 return nil, err 244 } 245 return os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) 246 }) 247 } 248 249 // pkg represents a package name 250 type pkg string 251 252 // imp represents an imported file name 253 type imp string 254 255 // ident represents an identifier 256 type ident string 257 258 // messageVal represents a message value for an option 259 type messageVal struct { 260 // the package and scope in which the option value is defined 261 pkg, scope string 262 // the option value 263 msg proto.Message 264 } 265 266 // option represents a resolved descriptor option 267 type option struct { 268 name string 269 val interface{} 270 } 271 272 // reservedRange represents a reserved range from a message or enum 273 type reservedRange struct { 274 start, end int32 275 } 276 277 // PrintProtoFile prints the given single file descriptor to the given writer. 278 func (p *Printer) PrintProtoFile(fd *desc.FileDescriptor, out io.Writer) error { 279 return p.printProto(fd, out) 280 } 281 282 // PrintProtoToString prints the given descriptor and returns the resulting 283 // string. This can be used to print proto files, but it can also be used to get 284 // the proto "source form" for any kind of descriptor, which can be a more 285 // user-friendly way to present descriptors that are intended for human 286 // consumption. 287 func (p *Printer) PrintProtoToString(dsc desc.Descriptor) (string, error) { 288 var buf bytes.Buffer 289 if err := p.printProto(dsc, &buf); err != nil { 290 return "", err 291 } 292 return buf.String(), nil 293 } 294 295 func (p *Printer) printProto(dsc desc.Descriptor, out io.Writer) error { 296 w := newWriter(out) 297 298 if p.Indent == "" { 299 // default indent to two spaces 300 p.Indent = " " 301 } else { 302 // indent must be all spaces or tabs, so convert other chars to spaces 303 ind := make([]rune, 0, len(p.Indent)) 304 for _, r := range p.Indent { 305 if r == '\t' { 306 ind = append(ind, r) 307 } else { 308 ind = append(ind, ' ') 309 } 310 } 311 p.Indent = string(ind) 312 } 313 if p.OmitDetachedComments { 314 p.OmitComments |= CommentsDetached 315 } 316 317 fdp := dsc.GetFile().AsFileDescriptorProto() 318 sourceInfo := internal.CreateSourceInfoMap(fdp) 319 extendOptionLocations(sourceInfo, fdp.GetSourceCodeInfo().GetLocation()) 320 321 var reg protoregistry.Types 322 internal.RegisterTypesVisibleToFile(®, dsc.GetFile().UnwrapFile()) 323 reparseUnknown(®, fdp.ProtoReflect()) 324 325 path := findElement(dsc) 326 switch d := dsc.(type) { 327 case *desc.FileDescriptor: 328 p.printFile(d, ®, w, sourceInfo) 329 case *desc.MessageDescriptor: 330 p.printMessage(d, ®, w, sourceInfo, path, 0) 331 case *desc.FieldDescriptor: 332 var scope string 333 if md, ok := d.GetParent().(*desc.MessageDescriptor); ok { 334 scope = md.GetFullyQualifiedName() 335 } else { 336 scope = d.GetFile().GetPackage() 337 } 338 if d.IsExtension() { 339 _, _ = fmt.Fprint(w, "extend ") 340 extNameSi := sourceInfo.Get(append(path, internal.Field_extendeeTag)) 341 p.printElementString(extNameSi, w, 0, p.qualifyName(d.GetFile().GetPackage(), scope, d.GetOwner().GetFullyQualifiedName())) 342 _, _ = fmt.Fprintln(w, "{") 343 344 p.printField(d, ®, w, sourceInfo, path, scope, 1) 345 346 _, _ = fmt.Fprintln(w, "}") 347 } else { 348 p.printField(d, ®, w, sourceInfo, path, scope, 0) 349 } 350 case *desc.OneOfDescriptor: 351 md := d.GetOwner() 352 elements := elementAddrs{dsc: md} 353 for i := range md.GetFields() { 354 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_fieldsTag, elementIndex: i}) 355 } 356 p.printOneOf(d, elements, 0, ®, w, sourceInfo, path[:len(path)-1], 0, path[len(path)-1]) 357 case *desc.EnumDescriptor: 358 p.printEnum(d, ®, w, sourceInfo, path, 0) 359 case *desc.EnumValueDescriptor: 360 p.printEnumValue(d, ®, w, sourceInfo, path, 0) 361 case *desc.ServiceDescriptor: 362 p.printService(d, ®, w, sourceInfo, path, 0) 363 case *desc.MethodDescriptor: 364 p.printMethod(d, ®, w, sourceInfo, path, 0) 365 } 366 367 return w.err 368 } 369 370 func findElement(dsc desc.Descriptor) []int32 { 371 if dsc.GetParent() == nil { 372 return nil 373 } 374 path := findElement(dsc.GetParent()) 375 switch d := dsc.(type) { 376 case *desc.MessageDescriptor: 377 if pm, ok := d.GetParent().(*desc.MessageDescriptor); ok { 378 return append(path, internal.Message_nestedMessagesTag, getMessageIndex(d, pm.GetNestedMessageTypes())) 379 } 380 return append(path, internal.File_messagesTag, getMessageIndex(d, d.GetFile().GetMessageTypes())) 381 382 case *desc.FieldDescriptor: 383 if d.IsExtension() { 384 if pm, ok := d.GetParent().(*desc.MessageDescriptor); ok { 385 return append(path, internal.Message_extensionsTag, getFieldIndex(d, pm.GetNestedExtensions())) 386 } 387 return append(path, internal.File_extensionsTag, getFieldIndex(d, d.GetFile().GetExtensions())) 388 } 389 return append(path, internal.Message_fieldsTag, getFieldIndex(d, d.GetOwner().GetFields())) 390 391 case *desc.OneOfDescriptor: 392 return append(path, internal.Message_oneOfsTag, getOneOfIndex(d, d.GetOwner().GetOneOfs())) 393 394 case *desc.EnumDescriptor: 395 if pm, ok := d.GetParent().(*desc.MessageDescriptor); ok { 396 return append(path, internal.Message_enumsTag, getEnumIndex(d, pm.GetNestedEnumTypes())) 397 } 398 return append(path, internal.File_enumsTag, getEnumIndex(d, d.GetFile().GetEnumTypes())) 399 400 case *desc.EnumValueDescriptor: 401 return append(path, internal.Enum_valuesTag, getEnumValueIndex(d, d.GetEnum().GetValues())) 402 403 case *desc.ServiceDescriptor: 404 return append(path, internal.File_servicesTag, getServiceIndex(d, d.GetFile().GetServices())) 405 406 case *desc.MethodDescriptor: 407 return append(path, internal.Service_methodsTag, getMethodIndex(d, d.GetService().GetMethods())) 408 409 default: 410 panic(fmt.Sprintf("unexpected descriptor type: %T", dsc)) 411 } 412 } 413 414 func getMessageIndex(md *desc.MessageDescriptor, list []*desc.MessageDescriptor) int32 { 415 for i := range list { 416 if md == list[i] { 417 return int32(i) 418 } 419 } 420 panic(fmt.Sprintf("unable to determine index of message %s", md.GetFullyQualifiedName())) 421 } 422 423 func getFieldIndex(fd *desc.FieldDescriptor, list []*desc.FieldDescriptor) int32 { 424 for i := range list { 425 if fd == list[i] { 426 return int32(i) 427 } 428 } 429 panic(fmt.Sprintf("unable to determine index of field %s", fd.GetFullyQualifiedName())) 430 } 431 432 func getOneOfIndex(ood *desc.OneOfDescriptor, list []*desc.OneOfDescriptor) int32 { 433 for i := range list { 434 if ood == list[i] { 435 return int32(i) 436 } 437 } 438 panic(fmt.Sprintf("unable to determine index of oneof %s", ood.GetFullyQualifiedName())) 439 } 440 441 func getEnumIndex(ed *desc.EnumDescriptor, list []*desc.EnumDescriptor) int32 { 442 for i := range list { 443 if ed == list[i] { 444 return int32(i) 445 } 446 } 447 panic(fmt.Sprintf("unable to determine index of enum %s", ed.GetFullyQualifiedName())) 448 } 449 450 func getEnumValueIndex(evd *desc.EnumValueDescriptor, list []*desc.EnumValueDescriptor) int32 { 451 for i := range list { 452 if evd == list[i] { 453 return int32(i) 454 } 455 } 456 panic(fmt.Sprintf("unable to determine index of enum value %s", evd.GetFullyQualifiedName())) 457 } 458 459 func getServiceIndex(sd *desc.ServiceDescriptor, list []*desc.ServiceDescriptor) int32 { 460 for i := range list { 461 if sd == list[i] { 462 return int32(i) 463 } 464 } 465 panic(fmt.Sprintf("unable to determine index of service %s", sd.GetFullyQualifiedName())) 466 } 467 468 func getMethodIndex(mtd *desc.MethodDescriptor, list []*desc.MethodDescriptor) int32 { 469 for i := range list { 470 if mtd == list[i] { 471 return int32(i) 472 } 473 } 474 panic(fmt.Sprintf("unable to determine index of method %s", mtd.GetFullyQualifiedName())) 475 } 476 477 func (p *Printer) newLine(w io.Writer) { 478 if !p.Compact { 479 _, _ = fmt.Fprintln(w) 480 } 481 } 482 483 func reparseUnknown(reg *protoregistry.Types, msg protoreflect.Message) { 484 msg.Range(func(fld protoreflect.FieldDescriptor, val protoreflect.Value) bool { 485 if fld.Kind() != protoreflect.MessageKind && fld.Kind() != protoreflect.GroupKind { 486 return true 487 } 488 if fld.IsList() { 489 l := val.List() 490 for i := 0; i < l.Len(); i++ { 491 reparseUnknown(reg, l.Get(i).Message()) 492 } 493 } else if fld.IsMap() { 494 mapVal := fld.MapValue() 495 if mapVal.Kind() != protoreflect.MessageKind && mapVal.Kind() != protoreflect.GroupKind { 496 return true 497 } 498 m := val.Map() 499 m.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { 500 reparseUnknown(reg, v.Message()) 501 return true 502 }) 503 } else { 504 reparseUnknown(reg, val.Message()) 505 } 506 return true 507 }) 508 509 unk := msg.GetUnknown() 510 if len(unk) > 0 { 511 other := msg.New().Interface() 512 if err := (proto.UnmarshalOptions{Resolver: reg}).Unmarshal(unk, other); err == nil { 513 msg.SetUnknown(nil) 514 proto.Merge(msg.Interface(), other) 515 } 516 } 517 } 518 519 func (p *Printer) printFile(fd *desc.FileDescriptor, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap) { 520 opts, err := p.extractOptions(fd, protov1.MessageV2(fd.GetOptions())) 521 if err != nil { 522 return 523 } 524 525 fdp := fd.AsFileDescriptorProto() 526 path := make([]int32, 1) 527 528 path[0] = internal.File_packageTag 529 sourceInfo.PutIfAbsent(append(path, 0), sourceInfo.Get(path)) 530 531 path[0] = internal.File_syntaxTag 532 si := sourceInfo.Get(path) 533 p.printElement(false, si, w, 0, func(w *writer) { 534 syn := fdp.GetSyntax() 535 if syn == "editions" { 536 _, _ = fmt.Fprintf(w, "edition = %q;", strings.TrimPrefix(fdp.GetEdition().String(), "EDITION_")) 537 return 538 } 539 if syn == "" { 540 syn = "proto2" 541 } 542 _, _ = fmt.Fprintf(w, "syntax = %q;", syn) 543 }) 544 p.newLine(w) 545 546 skip := map[interface{}]bool{} 547 548 elements := elementAddrs{dsc: fd, opts: opts} 549 if fdp.Package != nil { 550 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_packageTag, elementIndex: 0, order: -3}) 551 } 552 for i := range fdp.GetDependency() { 553 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_dependencyTag, elementIndex: i, order: -2}) 554 } 555 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.File_optionsTag, -1, opts)...) 556 for i := range fd.GetMessageTypes() { 557 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_messagesTag, elementIndex: i}) 558 } 559 for i := range fd.GetEnumTypes() { 560 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_enumsTag, elementIndex: i}) 561 } 562 for i := range fd.GetServices() { 563 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_servicesTag, elementIndex: i}) 564 } 565 exts := p.computeExtensions(sourceInfo, fd.GetExtensions(), []int32{internal.File_extensionsTag}) 566 for i, extd := range fd.GetExtensions() { 567 if extd.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP { 568 // we don't emit nested messages for groups since 569 // they get special treatment 570 skip[extd.GetMessageType()] = true 571 } 572 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.File_extensionsTag, elementIndex: i}) 573 } 574 575 p.sort(elements, sourceInfo, nil) 576 577 pkgName := fd.GetPackage() 578 579 for i, el := range elements.addrs { 580 d := elements.at(el) 581 582 // skip[d] will panic if d is a slice (which it could be for []option), 583 // so just ignore it since we don't try to skip options 584 if reflect.TypeOf(d).Kind() != reflect.Slice && skip[d] { 585 // skip this element 586 continue 587 } 588 589 if i > 0 { 590 p.newLine(w) 591 } 592 593 path = []int32{el.elementType, int32(el.elementIndex)} 594 595 switch d := d.(type) { 596 case pkg: 597 si := sourceInfo.Get(path) 598 p.printElement(false, si, w, 0, func(w *writer) { 599 _, _ = fmt.Fprintf(w, "package %s;", d) 600 }) 601 case imp: 602 si := sourceInfo.Get(path) 603 var modifier string 604 for _, idx := range fdp.PublicDependency { 605 if fdp.Dependency[idx] == string(d) { 606 modifier = "public " 607 break 608 } 609 } 610 if modifier == "" { 611 for _, idx := range fdp.WeakDependency { 612 if fdp.Dependency[idx] == string(d) { 613 modifier = "weak " 614 break 615 } 616 } 617 } 618 p.printElement(false, si, w, 0, func(w *writer) { 619 _, _ = fmt.Fprintf(w, "import %s%q;", modifier, d) 620 }) 621 case []option: 622 p.printOptionsLong(d, reg, w, sourceInfo, path, 0) 623 case *desc.MessageDescriptor: 624 p.printMessage(d, reg, w, sourceInfo, path, 0) 625 case *desc.EnumDescriptor: 626 p.printEnum(d, reg, w, sourceInfo, path, 0) 627 case *desc.ServiceDescriptor: 628 p.printService(d, reg, w, sourceInfo, path, 0) 629 case *desc.FieldDescriptor: 630 extDecl := exts[d] 631 p.printExtensions(extDecl, exts, elements, i, reg, w, sourceInfo, nil, internal.File_extensionsTag, pkgName, pkgName, 0) 632 // we printed all extensions in the group, so we can skip the others 633 for _, fld := range extDecl.fields { 634 skip[fld] = true 635 } 636 } 637 } 638 } 639 640 func findExtSi(fieldSi *descriptorpb.SourceCodeInfo_Location, extSis []*descriptorpb.SourceCodeInfo_Location) *descriptorpb.SourceCodeInfo_Location { 641 if len(fieldSi.GetSpan()) == 0 { 642 return nil 643 } 644 for _, extSi := range extSis { 645 if isSpanWithin(fieldSi.Span, extSi.Span) { 646 return extSi 647 } 648 } 649 return nil 650 } 651 652 func isSpanWithin(span, enclosing []int32) bool { 653 start := enclosing[0] 654 var end int32 655 if len(enclosing) == 3 { 656 end = enclosing[0] 657 } else { 658 end = enclosing[2] 659 } 660 if span[0] < start || span[0] > end { 661 return false 662 } 663 664 if span[0] == start { 665 return span[1] >= enclosing[1] 666 } else if span[0] == end { 667 return span[1] <= enclosing[len(enclosing)-1] 668 } 669 return true 670 } 671 672 type extensionDecl struct { 673 extendee string 674 sourceInfo *descriptorpb.SourceCodeInfo_Location 675 fields []*desc.FieldDescriptor 676 } 677 678 type extensions map[*desc.FieldDescriptor]*extensionDecl 679 680 func (p *Printer) computeExtensions(sourceInfo internal.SourceInfoMap, exts []*desc.FieldDescriptor, path []int32) extensions { 681 extsMap := map[string]map[*descriptorpb.SourceCodeInfo_Location]*extensionDecl{} 682 extSis := sourceInfo.GetAll(path) 683 for _, extd := range exts { 684 name := extd.GetOwner().GetFullyQualifiedName() 685 extSi := findExtSi(extd.GetSourceInfo(), extSis) 686 extsBySi := extsMap[name] 687 if extsBySi == nil { 688 extsBySi = map[*descriptorpb.SourceCodeInfo_Location]*extensionDecl{} 689 extsMap[name] = extsBySi 690 } 691 extDecl := extsBySi[extSi] 692 if extDecl == nil { 693 extDecl = &extensionDecl{ 694 sourceInfo: extSi, 695 extendee: name, 696 } 697 extsBySi[extSi] = extDecl 698 } 699 extDecl.fields = append(extDecl.fields, extd) 700 } 701 702 ret := extensions{} 703 for _, extsBySi := range extsMap { 704 for _, extDecl := range extsBySi { 705 for _, extd := range extDecl.fields { 706 ret[extd] = extDecl 707 } 708 } 709 } 710 return ret 711 } 712 713 func (p *Printer) sort(elements elementAddrs, sourceInfo internal.SourceInfoMap, path []int32) { 714 if p.CustomSortFunction != nil { 715 sort.Stable(customSortOrder{elementAddrs: elements, less: p.CustomSortFunction}) 716 } else if p.SortElements { 717 // canonical sorted order 718 sort.Stable(elements) 719 } else { 720 // use source order (per location information in SourceCodeInfo); or 721 // if that isn't present use declaration order, but grouped by type 722 sort.Stable(elementSrcOrder{ 723 elementAddrs: elements, 724 sourceInfo: sourceInfo, 725 prefix: path, 726 }) 727 } 728 } 729 730 func (p *Printer) qualifyMessageOptionName(pkg, scope string, fqn string) string { 731 // Message options must at least include the message scope, even if the option 732 // is inside that message. We do that by requiring we have at least one 733 // enclosing skip in the qualified name. 734 return p.qualifyElementName(pkg, scope, fqn, 1) 735 } 736 737 func (p *Printer) qualifyExtensionLiteralName(pkg, scope string, fqn string) string { 738 // In message literals, extensions can have package name omitted but may not 739 // have any other scopes omitted. We signal that via negative arg. 740 return p.qualifyElementName(pkg, scope, fqn, -1) 741 } 742 743 func (p *Printer) qualifyName(pkg, scope string, fqn string) string { 744 return p.qualifyElementName(pkg, scope, fqn, 0) 745 } 746 747 func (p *Printer) qualifyElementName(pkg, scope string, fqn string, required int) string { 748 if p.ForceFullyQualifiedNames { 749 // forcing fully-qualified names; make sure to include preceding dot 750 if fqn[0] == '.' { 751 return fqn 752 } 753 return fmt.Sprintf(".%s", fqn) 754 } 755 756 // compute relative name (so no leading dot) 757 if fqn[0] == '.' { 758 fqn = fqn[1:] 759 } 760 if required < 0 { 761 scope = pkg + "." 762 } else if len(scope) > 0 && scope[len(scope)-1] != '.' { 763 scope = scope + "." 764 } 765 count := 0 766 for scope != "" { 767 if strings.HasPrefix(fqn, scope) && count >= required { 768 return fqn[len(scope):] 769 } 770 if scope == pkg+"." { 771 break 772 } 773 pos := strings.LastIndex(scope[:len(scope)-1], ".") 774 scope = scope[:pos+1] 775 count++ 776 } 777 return fqn 778 } 779 780 func (p *Printer) typeString(fld *desc.FieldDescriptor, scope string) string { 781 if fld.IsMap() { 782 return fmt.Sprintf("map<%s, %s>", p.typeString(fld.GetMapKeyType(), scope), p.typeString(fld.GetMapValueType(), scope)) 783 } 784 fldProto := fld.AsFieldDescriptorProto() 785 if fldProto.Type == nil && fldProto.TypeName != nil { 786 // In an unlinked proto, the type may be absent because it is not known 787 // whether the symbol is a message or an enum. In that case, just return 788 // the type name. 789 return fldProto.GetTypeName() 790 } 791 switch fld.GetType() { 792 case descriptorpb.FieldDescriptorProto_TYPE_INT32: 793 return "int32" 794 case descriptorpb.FieldDescriptorProto_TYPE_INT64: 795 return "int64" 796 case descriptorpb.FieldDescriptorProto_TYPE_UINT32: 797 return "uint32" 798 case descriptorpb.FieldDescriptorProto_TYPE_UINT64: 799 return "uint64" 800 case descriptorpb.FieldDescriptorProto_TYPE_SINT32: 801 return "sint32" 802 case descriptorpb.FieldDescriptorProto_TYPE_SINT64: 803 return "sint64" 804 case descriptorpb.FieldDescriptorProto_TYPE_FIXED32: 805 return "fixed32" 806 case descriptorpb.FieldDescriptorProto_TYPE_FIXED64: 807 return "fixed64" 808 case descriptorpb.FieldDescriptorProto_TYPE_SFIXED32: 809 return "sfixed32" 810 case descriptorpb.FieldDescriptorProto_TYPE_SFIXED64: 811 return "sfixed64" 812 case descriptorpb.FieldDescriptorProto_TYPE_FLOAT: 813 return "float" 814 case descriptorpb.FieldDescriptorProto_TYPE_DOUBLE: 815 return "double" 816 case descriptorpb.FieldDescriptorProto_TYPE_BOOL: 817 return "bool" 818 case descriptorpb.FieldDescriptorProto_TYPE_STRING: 819 return "string" 820 case descriptorpb.FieldDescriptorProto_TYPE_BYTES: 821 return "bytes" 822 case descriptorpb.FieldDescriptorProto_TYPE_ENUM: 823 return p.qualifyName(fld.GetFile().GetPackage(), scope, fld.GetEnumType().GetFullyQualifiedName()) 824 case descriptorpb.FieldDescriptorProto_TYPE_MESSAGE: 825 return p.qualifyName(fld.GetFile().GetPackage(), scope, fld.GetMessageType().GetFullyQualifiedName()) 826 case descriptorpb.FieldDescriptorProto_TYPE_GROUP: 827 return fld.GetMessageType().GetName() 828 } 829 panic(fmt.Sprintf("invalid type: %v", fld.GetType())) 830 } 831 832 func (p *Printer) printMessage(md *desc.MessageDescriptor, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) { 833 si := sourceInfo.Get(path) 834 p.printBlockElement(true, si, w, indent, func(w *writer, trailer func(int, bool)) { 835 p.indent(w, indent) 836 837 _, _ = fmt.Fprint(w, "message ") 838 nameSi := sourceInfo.Get(append(path, internal.Message_nameTag)) 839 p.printElementString(nameSi, w, indent, md.GetName()) 840 _, _ = fmt.Fprintln(w, "{") 841 trailer(indent+1, true) 842 843 p.printMessageBody(md, reg, w, sourceInfo, path, indent+1) 844 p.indent(w, indent) 845 _, _ = fmt.Fprintln(w, "}") 846 }) 847 } 848 849 func (p *Printer) printMessageBody(md *desc.MessageDescriptor, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) { 850 opts, err := p.extractOptions(md, protov1.MessageV2(md.GetOptions())) 851 if err != nil { 852 if w.err == nil { 853 w.err = err 854 } 855 return 856 } 857 858 skip := map[interface{}]bool{} 859 maxTag := internal.GetMaxTag(md.GetMessageOptions().GetMessageSetWireFormat()) 860 861 elements := elementAddrs{dsc: md, opts: opts} 862 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.Message_optionsTag, -1, opts)...) 863 for i := range md.AsDescriptorProto().GetReservedRange() { 864 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_reservedRangeTag, elementIndex: i}) 865 } 866 for i := range md.AsDescriptorProto().GetReservedName() { 867 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_reservedNameTag, elementIndex: i}) 868 } 869 for i := range md.AsDescriptorProto().GetExtensionRange() { 870 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_extensionRangeTag, elementIndex: i}) 871 } 872 for i, fld := range md.GetFields() { 873 if fld.IsMap() || fld.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP { 874 // we don't emit nested messages for map types or groups since 875 // they get special treatment 876 skip[fld.GetMessageType()] = true 877 } 878 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_fieldsTag, elementIndex: i}) 879 } 880 for i := range md.GetNestedMessageTypes() { 881 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_nestedMessagesTag, elementIndex: i}) 882 } 883 for i := range md.GetNestedEnumTypes() { 884 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_enumsTag, elementIndex: i}) 885 } 886 exts := p.computeExtensions(sourceInfo, md.GetNestedExtensions(), append(path, internal.Message_extensionsTag)) 887 for i, extd := range md.GetNestedExtensions() { 888 if extd.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP { 889 // we don't emit nested messages for groups since 890 // they get special treatment 891 skip[extd.GetMessageType()] = true 892 } 893 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Message_extensionsTag, elementIndex: i}) 894 } 895 896 p.sort(elements, sourceInfo, path) 897 898 pkg := md.GetFile().GetPackage() 899 scope := md.GetFullyQualifiedName() 900 901 for i, el := range elements.addrs { 902 d := elements.at(el) 903 904 // skip[d] will panic if d is a slice (which it could be for []option), 905 // so just ignore it since we don't try to skip options 906 if reflect.TypeOf(d).Kind() != reflect.Slice && skip[d] { 907 // skip this element 908 continue 909 } 910 911 if i > 0 { 912 p.newLine(w) 913 } 914 915 childPath := append(path, el.elementType, int32(el.elementIndex)) 916 917 switch d := d.(type) { 918 case []option: 919 p.printOptionsLong(d, reg, w, sourceInfo, childPath, indent) 920 case *desc.FieldDescriptor: 921 if d.IsExtension() { 922 extDecl := exts[d] 923 p.printExtensions(extDecl, exts, elements, i, reg, w, sourceInfo, path, internal.Message_extensionsTag, pkg, scope, indent) 924 // we printed all extensions in the group, so we can skip the others 925 for _, fld := range extDecl.fields { 926 skip[fld] = true 927 } 928 } else { 929 ood := d.GetOneOf() 930 if ood == nil || ood.IsSynthetic() { 931 p.printField(d, reg, w, sourceInfo, childPath, scope, indent) 932 } else { 933 // print the one-of, including all of its fields 934 p.printOneOf(ood, elements, i, reg, w, sourceInfo, path, indent, d.AsFieldDescriptorProto().GetOneofIndex()) 935 for _, fld := range ood.GetChoices() { 936 skip[fld] = true 937 } 938 } 939 } 940 case *desc.MessageDescriptor: 941 p.printMessage(d, reg, w, sourceInfo, childPath, indent) 942 case *desc.EnumDescriptor: 943 p.printEnum(d, reg, w, sourceInfo, childPath, indent) 944 case *descriptorpb.DescriptorProto_ExtensionRange: 945 // collapse ranges into a single "extensions" block 946 ranges := []*descriptorpb.DescriptorProto_ExtensionRange{d} 947 addrs := []elementAddr{el} 948 for idx := i + 1; idx < len(elements.addrs); idx++ { 949 elnext := elements.addrs[idx] 950 if elnext.elementType != el.elementType { 951 break 952 } 953 extr := elements.at(elnext).(*descriptorpb.DescriptorProto_ExtensionRange) 954 if !proto.Equal(d.Options, extr.Options) { 955 break 956 } 957 ranges = append(ranges, extr) 958 addrs = append(addrs, elnext) 959 skip[extr] = true 960 } 961 p.printExtensionRanges(md, ranges, maxTag, addrs, reg, w, sourceInfo, path, indent) 962 case reservedRange: 963 // collapse reserved ranges into a single "reserved" block 964 ranges := []reservedRange{d} 965 addrs := []elementAddr{el} 966 for idx := i + 1; idx < len(elements.addrs); idx++ { 967 elnext := elements.addrs[idx] 968 if elnext.elementType != el.elementType { 969 break 970 } 971 rr := elements.at(elnext).(reservedRange) 972 ranges = append(ranges, rr) 973 addrs = append(addrs, elnext) 974 skip[rr] = true 975 } 976 p.printReservedRanges(ranges, maxTag, addrs, w, sourceInfo, path, indent) 977 case string: // reserved name 978 // collapse reserved names into a single "reserved" block 979 names := []string{d} 980 addrs := []elementAddr{el} 981 for idx := i + 1; idx < len(elements.addrs); idx++ { 982 elnext := elements.addrs[idx] 983 if elnext.elementType != el.elementType { 984 break 985 } 986 rn := elements.at(elnext).(string) 987 names = append(names, rn) 988 addrs = append(addrs, elnext) 989 skip[rn] = true 990 } 991 p.printReservedNames(names, addrs, w, sourceInfo, path, indent, useQuotedReserved(md.GetFile())) 992 } 993 } 994 } 995 996 func (p *Printer) printField(fld *desc.FieldDescriptor, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, path []int32, scope string, indent int) { 997 var groupPath []int32 998 var si *descriptorpb.SourceCodeInfo_Location 999 1000 group := isGroup(fld) 1001 1002 if group { 1003 // compute path to group message type 1004 groupPath = make([]int32, len(path)-2) 1005 copy(groupPath, path) 1006 1007 var candidates []*desc.MessageDescriptor 1008 var parentTag int32 1009 switch parent := fld.GetParent().(type) { 1010 case *desc.MessageDescriptor: 1011 // group in a message 1012 candidates = parent.GetNestedMessageTypes() 1013 parentTag = internal.Message_nestedMessagesTag 1014 case *desc.FileDescriptor: 1015 // group that is a top-level extension 1016 candidates = parent.GetMessageTypes() 1017 parentTag = internal.File_messagesTag 1018 } 1019 1020 var groupMsgIndex int32 1021 for i, nmd := range candidates { 1022 if nmd == fld.GetMessageType() { 1023 // found it 1024 groupMsgIndex = int32(i) 1025 break 1026 } 1027 } 1028 groupPath = append(groupPath, parentTag, groupMsgIndex) 1029 1030 // the group message is where the field's comments and position are stored 1031 si = sourceInfo.Get(groupPath) 1032 } else { 1033 si = sourceInfo.Get(path) 1034 } 1035 1036 p.printBlockElement(true, si, w, indent, func(w *writer, trailer func(int, bool)) { 1037 p.indent(w, indent) 1038 if shouldEmitLabel(fld) { 1039 locSi := sourceInfo.Get(append(path, internal.Field_labelTag)) 1040 p.printElementString(locSi, w, indent, labelString(fld.GetLabel())) 1041 } 1042 1043 if group { 1044 _, _ = fmt.Fprint(w, "group ") 1045 } 1046 1047 typeSi := sourceInfo.Get(append(path, internal.Field_typeTag)) 1048 p.printElementString(typeSi, w, indent, p.typeString(fld, scope)) 1049 1050 if !group { 1051 nameSi := sourceInfo.Get(append(path, internal.Field_nameTag)) 1052 p.printElementString(nameSi, w, indent, fld.GetName()) 1053 } 1054 1055 _, _ = fmt.Fprint(w, "= ") 1056 numSi := sourceInfo.Get(append(path, internal.Field_numberTag)) 1057 p.printElementString(numSi, w, indent, fmt.Sprintf("%d", fld.GetNumber())) 1058 1059 opts, err := p.extractOptions(fld, protov1.MessageV2(fld.GetOptions())) 1060 if err != nil { 1061 if w.err == nil { 1062 w.err = err 1063 } 1064 return 1065 } 1066 1067 // we use negative values for "extras" keys so they can't collide 1068 // with legit option tags 1069 1070 if fld.UnwrapField().HasPresence() && fld.AsFieldDescriptorProto().DefaultValue != nil { 1071 defVal := fld.GetDefaultValue() 1072 if fld.GetEnumType() != nil { 1073 defVal = ident(fld.GetEnumType().FindValueByNumber(defVal.(int32)).GetName()) 1074 } 1075 opts[-internal.Field_defaultTag] = []option{{name: "default", val: defVal}} 1076 } 1077 1078 jsn := fld.AsFieldDescriptorProto().GetJsonName() 1079 if jsn != "" && jsn != internal.JsonName(fld.GetName()) { 1080 opts[-internal.Field_jsonNameTag] = []option{{name: "json_name", val: jsn}} 1081 } 1082 1083 p.printOptionsShort(fld, opts, internal.Field_optionsTag, reg, w, sourceInfo, path, indent) 1084 1085 if group { 1086 _, _ = fmt.Fprintln(w, "{") 1087 trailer(indent+1, true) 1088 1089 p.printMessageBody(fld.GetMessageType(), reg, w, sourceInfo, groupPath, indent+1) 1090 1091 p.indent(w, indent) 1092 _, _ = fmt.Fprintln(w, "}") 1093 1094 } else { 1095 _, _ = fmt.Fprint(w, ";") 1096 trailer(indent, false) 1097 } 1098 }) 1099 } 1100 1101 func shouldEmitLabel(fld *desc.FieldDescriptor) bool { 1102 return fld.IsProto3Optional() || 1103 (!fld.IsMap() && fld.GetOneOf() == nil && 1104 (fld.GetLabel() != descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL || 1105 fld.GetFile().UnwrapFile().Syntax() == protoreflect.Proto2)) 1106 } 1107 1108 func labelString(lbl descriptorpb.FieldDescriptorProto_Label) string { 1109 switch lbl { 1110 case descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL: 1111 return "optional" 1112 case descriptorpb.FieldDescriptorProto_LABEL_REQUIRED: 1113 return "required" 1114 case descriptorpb.FieldDescriptorProto_LABEL_REPEATED: 1115 return "repeated" 1116 } 1117 panic(fmt.Sprintf("invalid label: %v", lbl)) 1118 } 1119 1120 func isGroup(fld *desc.FieldDescriptor) bool { 1121 return fld.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP 1122 } 1123 1124 func (p *Printer) printOneOf(ood *desc.OneOfDescriptor, parentElements elementAddrs, startFieldIndex int, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int, ooIndex int32) { 1125 oopath := append(parentPath, internal.Message_oneOfsTag, ooIndex) 1126 oosi := sourceInfo.Get(oopath) 1127 p.printBlockElement(true, oosi, w, indent, func(w *writer, trailer func(int, bool)) { 1128 p.indent(w, indent) 1129 _, _ = fmt.Fprint(w, "oneof ") 1130 extNameSi := sourceInfo.Get(append(oopath, internal.OneOf_nameTag)) 1131 p.printElementString(extNameSi, w, indent, ood.GetName()) 1132 _, _ = fmt.Fprintln(w, "{") 1133 indent++ 1134 trailer(indent, true) 1135 1136 opts, err := p.extractOptions(ood, protov1.MessageV2(ood.GetOptions())) 1137 if err != nil { 1138 if w.err == nil { 1139 w.err = err 1140 } 1141 return 1142 } 1143 1144 elements := elementAddrs{dsc: ood, opts: opts} 1145 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.OneOf_optionsTag, -1, opts)...) 1146 1147 count := len(ood.GetChoices()) 1148 for idx := startFieldIndex; count > 0 && idx < len(parentElements.addrs); idx++ { 1149 el := parentElements.addrs[idx] 1150 if el.elementType != internal.Message_fieldsTag { 1151 continue 1152 } 1153 if parentElements.at(el).(*desc.FieldDescriptor).GetOneOf() == ood { 1154 // negative tag indicates that this element is actually a sibling, not a child 1155 elements.addrs = append(elements.addrs, elementAddr{elementType: -internal.Message_fieldsTag, elementIndex: el.elementIndex}) 1156 count-- 1157 } 1158 } 1159 1160 // the fields are already sorted, but we have to re-sort in order to 1161 // interleave the options (in the event that we are using file location 1162 // order and the option locations are interleaved with the fields) 1163 p.sort(elements, sourceInfo, oopath) 1164 scope := ood.GetOwner().GetFullyQualifiedName() 1165 1166 for i, el := range elements.addrs { 1167 if i > 0 { 1168 p.newLine(w) 1169 } 1170 1171 switch d := elements.at(el).(type) { 1172 case []option: 1173 childPath := append(oopath, el.elementType, int32(el.elementIndex)) 1174 p.printOptionsLong(d, reg, w, sourceInfo, childPath, indent) 1175 case *desc.FieldDescriptor: 1176 childPath := append(parentPath, -el.elementType, int32(el.elementIndex)) 1177 p.printField(d, reg, w, sourceInfo, childPath, scope, indent) 1178 } 1179 } 1180 1181 p.indent(w, indent-1) 1182 _, _ = fmt.Fprintln(w, "}") 1183 }) 1184 } 1185 1186 func (p *Printer) printExtensions(exts *extensionDecl, allExts extensions, parentElements elementAddrs, startFieldIndex int, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, extTag int32, pkg, scope string, indent int) { 1187 path := append(parentPath, extTag) 1188 p.printLeadingComments(exts.sourceInfo, w, indent) 1189 p.indent(w, indent) 1190 _, _ = fmt.Fprint(w, "extend ") 1191 extNameSi := sourceInfo.Get(append(path, 0, internal.Field_extendeeTag)) 1192 p.printElementString(extNameSi, w, indent, p.qualifyName(pkg, scope, exts.extendee)) 1193 _, _ = fmt.Fprintln(w, "{") 1194 1195 if p.printTrailingComments(exts.sourceInfo, w, indent+1) && !p.Compact { 1196 // separator line between trailing comment and next element 1197 _, _ = fmt.Fprintln(w) 1198 } 1199 1200 count := len(exts.fields) 1201 first := true 1202 for idx := startFieldIndex; count > 0 && idx < len(parentElements.addrs); idx++ { 1203 el := parentElements.addrs[idx] 1204 if el.elementType != extTag { 1205 continue 1206 } 1207 fld := parentElements.at(el).(*desc.FieldDescriptor) 1208 if allExts[fld] == exts { 1209 if first { 1210 first = false 1211 } else { 1212 p.newLine(w) 1213 } 1214 childPath := append(path, int32(el.elementIndex)) 1215 p.printField(fld, reg, w, sourceInfo, childPath, scope, indent+1) 1216 count-- 1217 } 1218 } 1219 1220 p.indent(w, indent) 1221 _, _ = fmt.Fprintln(w, "}") 1222 } 1223 1224 func (p *Printer) printExtensionRanges(parent *desc.MessageDescriptor, ranges []*descriptorpb.DescriptorProto_ExtensionRange, maxTag int32, addrs []elementAddr, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int) { 1225 p.indent(w, indent) 1226 _, _ = fmt.Fprint(w, "extensions ") 1227 1228 var opts *descriptorpb.ExtensionRangeOptions 1229 var elPath []int32 1230 first := true 1231 for i, extr := range ranges { 1232 if first { 1233 first = false 1234 } else { 1235 _, _ = fmt.Fprint(w, ", ") 1236 } 1237 opts = extr.Options 1238 el := addrs[i] 1239 elPath = append(parentPath, el.elementType, int32(el.elementIndex)) 1240 si := sourceInfo.Get(elPath) 1241 p.printElement(true, si, w, inline(indent), func(w *writer) { 1242 if extr.GetStart() == extr.GetEnd()-1 { 1243 _, _ = fmt.Fprintf(w, "%d ", extr.GetStart()) 1244 } else if extr.GetEnd()-1 == maxTag { 1245 _, _ = fmt.Fprintf(w, "%d to max ", extr.GetStart()) 1246 } else { 1247 _, _ = fmt.Fprintf(w, "%d to %d ", extr.GetStart(), extr.GetEnd()-1) 1248 } 1249 }) 1250 } 1251 dsc := extensionRange{owner: parent, extRange: ranges[0]} 1252 p.extractAndPrintOptionsShort(dsc, opts, reg, internal.ExtensionRange_optionsTag, w, sourceInfo, elPath, indent) 1253 1254 _, _ = fmt.Fprintln(w, ";") 1255 } 1256 1257 func (p *Printer) printReservedRanges(ranges []reservedRange, maxVal int32, addrs []elementAddr, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int) { 1258 p.indent(w, indent) 1259 _, _ = fmt.Fprint(w, "reserved ") 1260 1261 first := true 1262 for i, rr := range ranges { 1263 if first { 1264 first = false 1265 } else { 1266 _, _ = fmt.Fprint(w, ", ") 1267 } 1268 el := addrs[i] 1269 si := sourceInfo.Get(append(parentPath, el.elementType, int32(el.elementIndex))) 1270 p.printElement(false, si, w, inline(indent), func(w *writer) { 1271 if rr.start == rr.end { 1272 _, _ = fmt.Fprintf(w, "%d ", rr.start) 1273 } else if rr.end == maxVal { 1274 _, _ = fmt.Fprintf(w, "%d to max ", rr.start) 1275 } else { 1276 _, _ = fmt.Fprintf(w, "%d to %d ", rr.start, rr.end) 1277 } 1278 }) 1279 } 1280 1281 _, _ = fmt.Fprintln(w, ";") 1282 } 1283 1284 func useQuotedReserved(fd *desc.FileDescriptor) bool { 1285 return fd.AsFileDescriptorProto().GetEdition() < descriptorpb.Edition_EDITION_2023 1286 } 1287 1288 func (p *Printer) printReservedNames(names []string, addrs []elementAddr, w *writer, sourceInfo internal.SourceInfoMap, parentPath []int32, indent int, useQuotes bool) { 1289 p.indent(w, indent) 1290 _, _ = fmt.Fprint(w, "reserved ") 1291 1292 first := true 1293 for i, name := range names { 1294 if first { 1295 first = false 1296 } else { 1297 _, _ = fmt.Fprint(w, ", ") 1298 } 1299 el := addrs[i] 1300 si := sourceInfo.Get(append(parentPath, el.elementType, int32(el.elementIndex))) 1301 if useQuotes { 1302 p.printElementString(si, w, indent, quotedString(name)) 1303 } else { 1304 p.printElementString(si, w, indent, name) 1305 } 1306 } 1307 1308 _, _ = fmt.Fprintln(w, ";") 1309 } 1310 1311 func (p *Printer) printEnum(ed *desc.EnumDescriptor, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) { 1312 si := sourceInfo.Get(path) 1313 p.printBlockElement(true, si, w, indent, func(w *writer, trailer func(int, bool)) { 1314 p.indent(w, indent) 1315 1316 _, _ = fmt.Fprint(w, "enum ") 1317 nameSi := sourceInfo.Get(append(path, internal.Enum_nameTag)) 1318 p.printElementString(nameSi, w, indent, ed.GetName()) 1319 _, _ = fmt.Fprintln(w, "{") 1320 indent++ 1321 trailer(indent, true) 1322 1323 opts, err := p.extractOptions(ed, protov1.MessageV2(ed.GetOptions())) 1324 if err != nil { 1325 if w.err == nil { 1326 w.err = err 1327 } 1328 return 1329 } 1330 1331 skip := map[interface{}]bool{} 1332 1333 elements := elementAddrs{dsc: ed, opts: opts} 1334 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.Enum_optionsTag, -1, opts)...) 1335 for i := range ed.GetValues() { 1336 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Enum_valuesTag, elementIndex: i}) 1337 } 1338 for i := range ed.AsEnumDescriptorProto().GetReservedRange() { 1339 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Enum_reservedRangeTag, elementIndex: i}) 1340 } 1341 for i := range ed.AsEnumDescriptorProto().GetReservedName() { 1342 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Enum_reservedNameTag, elementIndex: i}) 1343 } 1344 1345 p.sort(elements, sourceInfo, path) 1346 1347 for i, el := range elements.addrs { 1348 d := elements.at(el) 1349 1350 // skip[d] will panic if d is a slice (which it could be for []option), 1351 // so just ignore it since we don't try to skip options 1352 if reflect.TypeOf(d).Kind() != reflect.Slice && skip[d] { 1353 // skip this element 1354 continue 1355 } 1356 1357 if i > 0 { 1358 p.newLine(w) 1359 } 1360 1361 childPath := append(path, el.elementType, int32(el.elementIndex)) 1362 1363 switch d := d.(type) { 1364 case []option: 1365 p.printOptionsLong(d, reg, w, sourceInfo, childPath, indent) 1366 case *desc.EnumValueDescriptor: 1367 p.printEnumValue(d, reg, w, sourceInfo, childPath, indent) 1368 case reservedRange: 1369 // collapse reserved ranges into a single "reserved" block 1370 ranges := []reservedRange{d} 1371 addrs := []elementAddr{el} 1372 for idx := i + 1; idx < len(elements.addrs); idx++ { 1373 elnext := elements.addrs[idx] 1374 if elnext.elementType != el.elementType { 1375 break 1376 } 1377 rr := elements.at(elnext).(reservedRange) 1378 ranges = append(ranges, rr) 1379 addrs = append(addrs, elnext) 1380 skip[rr] = true 1381 } 1382 p.printReservedRanges(ranges, math.MaxInt32, addrs, w, sourceInfo, path, indent) 1383 case string: // reserved name 1384 // collapse reserved names into a single "reserved" block 1385 names := []string{d} 1386 addrs := []elementAddr{el} 1387 for idx := i + 1; idx < len(elements.addrs); idx++ { 1388 elnext := elements.addrs[idx] 1389 if elnext.elementType != el.elementType { 1390 break 1391 } 1392 rn := elements.at(elnext).(string) 1393 names = append(names, rn) 1394 addrs = append(addrs, elnext) 1395 skip[rn] = true 1396 } 1397 p.printReservedNames(names, addrs, w, sourceInfo, path, indent, useQuotedReserved(ed.GetFile())) 1398 } 1399 } 1400 1401 p.indent(w, indent-1) 1402 _, _ = fmt.Fprintln(w, "}") 1403 }) 1404 } 1405 1406 func (p *Printer) printEnumValue(evd *desc.EnumValueDescriptor, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) { 1407 si := sourceInfo.Get(path) 1408 p.printElement(true, si, w, indent, func(w *writer) { 1409 p.indent(w, indent) 1410 1411 nameSi := sourceInfo.Get(append(path, internal.EnumVal_nameTag)) 1412 p.printElementString(nameSi, w, indent, evd.GetName()) 1413 _, _ = fmt.Fprint(w, "= ") 1414 1415 numSi := sourceInfo.Get(append(path, internal.EnumVal_numberTag)) 1416 p.printElementString(numSi, w, indent, fmt.Sprintf("%d", evd.GetNumber())) 1417 1418 p.extractAndPrintOptionsShort(evd, protov1.MessageV2(evd.GetOptions()), reg, internal.EnumVal_optionsTag, w, sourceInfo, path, indent) 1419 1420 _, _ = fmt.Fprint(w, ";") 1421 }) 1422 } 1423 1424 func (p *Printer) printService(sd *desc.ServiceDescriptor, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) { 1425 si := sourceInfo.Get(path) 1426 p.printBlockElement(true, si, w, indent, func(w *writer, trailer func(int, bool)) { 1427 p.indent(w, indent) 1428 1429 _, _ = fmt.Fprint(w, "service ") 1430 nameSi := sourceInfo.Get(append(path, internal.Service_nameTag)) 1431 p.printElementString(nameSi, w, indent, sd.GetName()) 1432 _, _ = fmt.Fprintln(w, "{") 1433 indent++ 1434 trailer(indent, true) 1435 1436 opts, err := p.extractOptions(sd, protov1.MessageV2(sd.GetOptions())) 1437 if err != nil { 1438 if w.err == nil { 1439 w.err = err 1440 } 1441 return 1442 } 1443 1444 elements := elementAddrs{dsc: sd, opts: opts} 1445 elements.addrs = append(elements.addrs, optionsAsElementAddrs(internal.Service_optionsTag, -1, opts)...) 1446 for i := range sd.GetMethods() { 1447 elements.addrs = append(elements.addrs, elementAddr{elementType: internal.Service_methodsTag, elementIndex: i}) 1448 } 1449 1450 p.sort(elements, sourceInfo, path) 1451 1452 for i, el := range elements.addrs { 1453 if i > 0 { 1454 p.newLine(w) 1455 } 1456 1457 childPath := append(path, el.elementType, int32(el.elementIndex)) 1458 1459 switch d := elements.at(el).(type) { 1460 case []option: 1461 p.printOptionsLong(d, reg, w, sourceInfo, childPath, indent) 1462 case *desc.MethodDescriptor: 1463 p.printMethod(d, reg, w, sourceInfo, childPath, indent) 1464 } 1465 } 1466 1467 p.indent(w, indent-1) 1468 _, _ = fmt.Fprintln(w, "}") 1469 }) 1470 } 1471 1472 func (p *Printer) printMethod(mtd *desc.MethodDescriptor, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) { 1473 si := sourceInfo.Get(path) 1474 pkg := mtd.GetFile().GetPackage() 1475 p.printBlockElement(true, si, w, indent, func(w *writer, trailer func(int, bool)) { 1476 p.indent(w, indent) 1477 1478 _, _ = fmt.Fprint(w, "rpc ") 1479 nameSi := sourceInfo.Get(append(path, internal.Method_nameTag)) 1480 p.printElementString(nameSi, w, indent, mtd.GetName()) 1481 1482 _, _ = fmt.Fprint(w, "( ") 1483 inSi := sourceInfo.Get(append(path, internal.Method_inputTag)) 1484 inName := p.qualifyName(pkg, pkg, mtd.GetInputType().GetFullyQualifiedName()) 1485 if mtd.IsClientStreaming() { 1486 inName = "stream " + inName 1487 } 1488 p.printElementString(inSi, w, indent, inName) 1489 1490 _, _ = fmt.Fprint(w, ") returns ( ") 1491 1492 outSi := sourceInfo.Get(append(path, internal.Method_outputTag)) 1493 outName := p.qualifyName(pkg, pkg, mtd.GetOutputType().GetFullyQualifiedName()) 1494 if mtd.IsServerStreaming() { 1495 outName = "stream " + outName 1496 } 1497 p.printElementString(outSi, w, indent, outName) 1498 _, _ = fmt.Fprint(w, ") ") 1499 1500 opts, err := p.extractOptions(mtd, protov1.MessageV2(mtd.GetOptions())) 1501 if err != nil { 1502 if w.err == nil { 1503 w.err = err 1504 } 1505 return 1506 } 1507 1508 if len(opts) > 0 { 1509 _, _ = fmt.Fprintln(w, "{") 1510 indent++ 1511 trailer(indent, true) 1512 1513 elements := elementAddrs{dsc: mtd, opts: opts} 1514 elements.addrs = optionsAsElementAddrs(internal.Method_optionsTag, 0, opts) 1515 p.sort(elements, sourceInfo, path) 1516 1517 for i, el := range elements.addrs { 1518 if i > 0 { 1519 p.newLine(w) 1520 } 1521 o := elements.at(el).([]option) 1522 childPath := append(path, el.elementType, int32(el.elementIndex)) 1523 p.printOptionsLong(o, reg, w, sourceInfo, childPath, indent) 1524 } 1525 1526 p.indent(w, indent-1) 1527 _, _ = fmt.Fprintln(w, "}") 1528 } else { 1529 _, _ = fmt.Fprint(w, ";") 1530 trailer(indent, false) 1531 } 1532 }) 1533 } 1534 1535 func (p *Printer) printOptionsLong(opts []option, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) { 1536 p.printOptions(opts, w, indent, 1537 func(i int32) *descriptorpb.SourceCodeInfo_Location { 1538 return sourceInfo.Get(append(path, i)) 1539 }, 1540 func(w *writer, indent int, opt option, _ bool) { 1541 p.indent(w, indent) 1542 _, _ = fmt.Fprint(w, "option ") 1543 p.printOption(reg, opt.name, opt.val, w, indent) 1544 _, _ = fmt.Fprint(w, ";") 1545 }, 1546 false) 1547 } 1548 1549 func (p *Printer) extractAndPrintOptionsShort(dsc interface{}, optsMsg proto.Message, reg *protoregistry.Types, optsTag int32, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) { 1550 d, ok := dsc.(desc.Descriptor) 1551 if !ok { 1552 d = dsc.(extensionRange).owner 1553 } 1554 opts, err := p.extractOptions(d, protov1.MessageV2(optsMsg)) 1555 if err != nil { 1556 if w.err == nil { 1557 w.err = err 1558 } 1559 return 1560 } 1561 p.printOptionsShort(dsc, opts, optsTag, reg, w, sourceInfo, path, indent) 1562 } 1563 1564 func (p *Printer) printOptionsShort(dsc interface{}, opts map[int32][]option, optsTag int32, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int) { 1565 elements := elementAddrs{dsc: dsc, opts: opts} 1566 elements.addrs = optionsAsElementAddrs(optsTag, 0, opts) 1567 if len(elements.addrs) == 0 { 1568 return 1569 } 1570 p.sort(elements, sourceInfo, path) 1571 1572 // we render expanded form if there are many options 1573 count := 0 1574 for _, addr := range elements.addrs { 1575 opts := elements.at(addr).([]option) 1576 count += len(opts) 1577 } 1578 threshold := p.ShortOptionsExpansionThresholdCount 1579 if threshold <= 0 { 1580 threshold = 3 1581 } 1582 1583 if count > threshold { 1584 p.printOptionElementsShort(elements, reg, w, sourceInfo, path, indent, true) 1585 } else { 1586 var tmp bytes.Buffer 1587 tmpW := *w 1588 tmpW.Writer = &tmp 1589 p.printOptionElementsShort(elements, reg, &tmpW, sourceInfo, path, indent, false) 1590 threshold := p.ShortOptionsExpansionThresholdLength 1591 if threshold <= 0 { 1592 threshold = 50 1593 } 1594 // we subtract 3 so we don't consider the leading " [" and trailing "]" 1595 if tmp.Len()-3 > threshold { 1596 p.printOptionElementsShort(elements, reg, w, sourceInfo, path, indent, true) 1597 } else { 1598 // not too long: commit what we rendered 1599 b := tmp.Bytes() 1600 if w.space && len(b) > 0 && b[0] == ' ' { 1601 // don't write extra space 1602 b = b[1:] 1603 } 1604 _, _ = w.Write(b) 1605 w.newline = tmpW.newline 1606 w.space = tmpW.space 1607 } 1608 } 1609 } 1610 1611 func (p *Printer) printOptionElementsShort(addrs elementAddrs, reg *protoregistry.Types, w *writer, sourceInfo internal.SourceInfoMap, path []int32, indent int, expand bool) { 1612 if expand { 1613 _, _ = fmt.Fprintln(w, "[") 1614 indent++ 1615 } else { 1616 _, _ = fmt.Fprint(w, "[") 1617 } 1618 for i, addr := range addrs.addrs { 1619 opts := addrs.at(addr).([]option) 1620 var childPath []int32 1621 if addr.elementIndex < 0 { 1622 // pseudo-option 1623 childPath = append(path, int32(-addr.elementIndex)) 1624 } else { 1625 childPath = append(path, addr.elementType, int32(addr.elementIndex)) 1626 } 1627 optIndent := indent 1628 if !expand { 1629 optIndent = inline(indent) 1630 } 1631 p.printOptions(opts, w, optIndent, 1632 func(i int32) *descriptorpb.SourceCodeInfo_Location { 1633 p := childPath 1634 if addr.elementIndex >= 0 { 1635 p = append(p, i) 1636 } 1637 return sourceInfo.Get(p) 1638 }, 1639 func(w *writer, indent int, opt option, more bool) { 1640 if expand { 1641 p.indent(w, indent) 1642 } 1643 p.printOption(reg, opt.name, opt.val, w, indent) 1644 if more { 1645 if expand { 1646 _, _ = fmt.Fprintln(w, ",") 1647 } else { 1648 _, _ = fmt.Fprint(w, ", ") 1649 } 1650 } 1651 }, 1652 i < len(addrs.addrs)-1) 1653 } 1654 if expand { 1655 p.indent(w, indent-1) 1656 } 1657 _, _ = fmt.Fprint(w, "] ") 1658 } 1659 1660 func (p *Printer) printOptions(opts []option, w *writer, indent int, siFetch func(i int32) *descriptorpb.SourceCodeInfo_Location, fn func(w *writer, indent int, opt option, more bool), haveMore bool) { 1661 for i, opt := range opts { 1662 more := haveMore 1663 if !more { 1664 more = i < len(opts)-1 1665 } 1666 si := siFetch(int32(i)) 1667 p.printElement(false, si, w, indent, func(w *writer) { 1668 fn(w, indent, opt, more) 1669 }) 1670 } 1671 } 1672 1673 func inline(indent int) int { 1674 if indent < 0 { 1675 // already inlined 1676 return indent 1677 } 1678 // negative indent means inline; indent 2 stops further in case value wraps 1679 return -indent - 2 1680 } 1681 1682 func sortKeys(m protoreflect.Map) []protoreflect.MapKey { 1683 res := make([]protoreflect.MapKey, m.Len()) 1684 i := 0 1685 m.Range(func(k protoreflect.MapKey, _ protoreflect.Value) bool { 1686 res[i] = k 1687 i++ 1688 return true 1689 }) 1690 sort.Slice(res, func(i, j int) bool { 1691 switch i := res[i].Interface().(type) { 1692 case int32: 1693 return i < int32(res[j].Int()) 1694 case uint32: 1695 return i < uint32(res[j].Uint()) 1696 case int64: 1697 return i < res[j].Int() 1698 case uint64: 1699 return i < res[j].Uint() 1700 case string: 1701 return i < res[j].String() 1702 case bool: 1703 return !i && res[j].Bool() 1704 default: 1705 panic(fmt.Sprintf("invalid type for map key: %T", i)) 1706 } 1707 }) 1708 return res 1709 } 1710 1711 func (p *Printer) printOption(reg *protoregistry.Types, name string, optVal interface{}, w *writer, indent int) { 1712 _, _ = fmt.Fprintf(w, "%s = ", name) 1713 1714 switch optVal := optVal.(type) { 1715 case int32, uint32, int64, uint64: 1716 _, _ = fmt.Fprintf(w, "%d", optVal) 1717 case float32, float64: 1718 _, _ = fmt.Fprintf(w, "%f", optVal) 1719 case string: 1720 _, _ = fmt.Fprintf(w, "%s", quotedString(optVal)) 1721 case []byte: 1722 _, _ = fmt.Fprintf(w, "%s", quotedBytes(string(optVal))) 1723 case bool: 1724 _, _ = fmt.Fprintf(w, "%v", optVal) 1725 case ident: 1726 _, _ = fmt.Fprintf(w, "%s", optVal) 1727 case messageVal: 1728 threshold := p.MessageLiteralExpansionThresholdLength 1729 if threshold == 0 { 1730 threshold = 50 1731 } 1732 var buf bytes.Buffer 1733 p.printMessageLiteralToBufferMaybeCompact(&buf, optVal.msg.ProtoReflect(), reg, optVal.pkg, optVal.scope, threshold, indent) 1734 _, _ = w.Write(buf.Bytes()) 1735 1736 default: 1737 panic(fmt.Sprintf("unknown type of value %T for field %s", optVal, name)) 1738 } 1739 } 1740 1741 type edgeKind int 1742 1743 const ( 1744 edgeKindOption edgeKind = iota 1745 edgeKindFile 1746 edgeKindMessage 1747 edgeKindField 1748 edgeKindOneOf 1749 edgeKindExtensionRange 1750 edgeKindReservedRange 1751 edgeKindReservedName 1752 edgeKindEnum 1753 edgeKindEnumVal 1754 edgeKindService 1755 edgeKindMethod 1756 ) 1757 1758 // edges in simple state machine for matching options paths 1759 // whose prefix should be included in source info to handle 1760 // the way options are printed (which cannot always include 1761 // the full path from original source) 1762 var edges = map[edgeKind]map[int32]edgeKind{ 1763 edgeKindFile: { 1764 internal.File_optionsTag: edgeKindOption, 1765 internal.File_messagesTag: edgeKindMessage, 1766 internal.File_enumsTag: edgeKindEnum, 1767 internal.File_extensionsTag: edgeKindField, 1768 internal.File_servicesTag: edgeKindService, 1769 }, 1770 edgeKindMessage: { 1771 internal.Message_optionsTag: edgeKindOption, 1772 internal.Message_fieldsTag: edgeKindField, 1773 internal.Message_oneOfsTag: edgeKindOneOf, 1774 internal.Message_nestedMessagesTag: edgeKindMessage, 1775 internal.Message_enumsTag: edgeKindEnum, 1776 internal.Message_extensionsTag: edgeKindField, 1777 internal.Message_extensionRangeTag: edgeKindExtensionRange, 1778 internal.Message_reservedRangeTag: edgeKindReservedRange, 1779 internal.Message_reservedNameTag: edgeKindReservedName, 1780 }, 1781 edgeKindField: { 1782 internal.Field_optionsTag: edgeKindOption, 1783 }, 1784 edgeKindOneOf: { 1785 internal.OneOf_optionsTag: edgeKindOption, 1786 }, 1787 edgeKindExtensionRange: { 1788 internal.ExtensionRange_optionsTag: edgeKindOption, 1789 }, 1790 edgeKindEnum: { 1791 internal.Enum_optionsTag: edgeKindOption, 1792 internal.Enum_valuesTag: edgeKindEnumVal, 1793 internal.Enum_reservedRangeTag: edgeKindReservedRange, 1794 internal.Enum_reservedNameTag: edgeKindReservedName, 1795 }, 1796 edgeKindEnumVal: { 1797 internal.EnumVal_optionsTag: edgeKindOption, 1798 }, 1799 edgeKindService: { 1800 internal.Service_optionsTag: edgeKindOption, 1801 internal.Service_methodsTag: edgeKindMethod, 1802 }, 1803 edgeKindMethod: { 1804 internal.Method_optionsTag: edgeKindOption, 1805 }, 1806 } 1807 1808 func extendOptionLocations(sc internal.SourceInfoMap, locs []*descriptorpb.SourceCodeInfo_Location) { 1809 // we iterate in the order that locations appear in descriptor 1810 // for determinism (if we ranged over the map, order and thus 1811 // potentially results are non-deterministic) 1812 for _, loc := range locs { 1813 allowed := edges[edgeKindFile] 1814 for i := 0; i+1 < len(loc.Path); i += 2 { 1815 nextKind, ok := allowed[loc.Path[i]] 1816 if !ok { 1817 break 1818 } 1819 if nextKind == edgeKindOption { 1820 // We've found an option entry. This could be arbitrarily deep 1821 // (for options that are nested messages) or it could end 1822 // abruptly (for non-repeated fields). But we need a path that 1823 // is exactly the path-so-far plus two: the option tag and an 1824 // optional index for repeated option fields (zero for 1825 // non-repeated option fields). This is used for querying source 1826 // info when printing options. 1827 newPath := make([]int32, i+3) 1828 copy(newPath, loc.Path) 1829 sc.PutIfAbsent(newPath, loc) 1830 // we do another path of path-so-far plus two, but with 1831 // explicit zero index -- just in case this actual path has 1832 // an extra path element, but it's not an index (e.g the 1833 // option field is not repeated, but the source info we are 1834 // looking at indicates a tag of a nested field) 1835 newPath[len(newPath)-1] = 0 1836 sc.PutIfAbsent(newPath, loc) 1837 // finally, we need the path-so-far plus one, just the option 1838 // tag, for sorting option groups 1839 newPath = newPath[:len(newPath)-1] 1840 sc.PutIfAbsent(newPath, loc) 1841 1842 break 1843 } else { 1844 allowed = edges[nextKind] 1845 } 1846 } 1847 } 1848 } 1849 1850 func (p *Printer) extractOptions(dsc desc.Descriptor, opts proto.Message) (map[int32][]option, error) { 1851 pkg := dsc.GetFile().GetPackage() 1852 var scope string 1853 isMessage := false 1854 if _, ok := dsc.(*desc.FileDescriptor); ok { 1855 scope = pkg 1856 } else { 1857 _, isMessage = dsc.(*desc.MessageDescriptor) 1858 scope = dsc.GetFullyQualifiedName() 1859 } 1860 1861 ref := opts.ProtoReflect() 1862 1863 options := map[int32][]option{} 1864 ref.Range(func(fld protoreflect.FieldDescriptor, val protoreflect.Value) bool { 1865 var name string 1866 if fld.IsExtension() { 1867 var n string 1868 if isMessage { 1869 n = p.qualifyMessageOptionName(pkg, scope, string(fld.FullName())) 1870 } else { 1871 n = p.qualifyName(pkg, scope, string(fld.FullName())) 1872 } 1873 name = fmt.Sprintf("(%s)", n) 1874 } else { 1875 name = string(fld.Name()) 1876 } 1877 opts := valueToOptions(fld, name, val.Interface()) 1878 if len(opts) > 0 { 1879 for i := range opts { 1880 if msg, ok := opts[i].val.(proto.Message); ok { 1881 opts[i].val = messageVal{pkg: pkg, scope: scope, msg: msg} 1882 } 1883 } 1884 options[int32(fld.Number())] = opts 1885 } 1886 return true 1887 }) 1888 return options, nil 1889 } 1890 1891 func valueToOptions(fld protoreflect.FieldDescriptor, name string, val interface{}) []option { 1892 switch val := val.(type) { 1893 case protoreflect.List: 1894 if fld.Number() == internal.UninterpretedOptionsTag { 1895 // we handle uninterpreted options differently 1896 uninterp := make([]*descriptorpb.UninterpretedOption, 0, val.Len()) 1897 for i := 0; i < val.Len(); i++ { 1898 uo := toUninterpretedOption(val.Get(i).Message().Interface()) 1899 if uo != nil { 1900 uninterp = append(uninterp, uo) 1901 } 1902 } 1903 return uninterpretedToOptions(uninterp) 1904 } 1905 opts := make([]option, 0, val.Len()) 1906 for i := 0; i < val.Len(); i++ { 1907 elem := valueForOption(fld, val.Get(i).Interface()) 1908 if elem != nil { 1909 opts = append(opts, option{name: name, val: elem}) 1910 } 1911 } 1912 return opts 1913 case protoreflect.Map: 1914 opts := make([]option, 0, val.Len()) 1915 for _, k := range sortKeys(val) { 1916 v := val.Get(k) 1917 vf := fld.MapValue() 1918 if vf.Kind() == protoreflect.EnumKind { 1919 if vf.Enum().Values().ByNumber(v.Enum()) == nil { 1920 // have to skip unknown enum values :( 1921 continue 1922 } 1923 } 1924 entry := dynamicpb.NewMessage(fld.Message()) 1925 entry.Set(fld.Message().Fields().ByNumber(1), k.Value()) 1926 entry.Set(fld.Message().Fields().ByNumber(2), v) 1927 opts = append(opts, option{name: name, val: entry}) 1928 } 1929 return opts 1930 default: 1931 v := valueForOption(fld, val) 1932 if v == nil { 1933 return nil 1934 } 1935 return []option{{name: name, val: v}} 1936 } 1937 } 1938 1939 func valueForOption(fld protoreflect.FieldDescriptor, val interface{}) interface{} { 1940 switch val := val.(type) { 1941 case protoreflect.EnumNumber: 1942 ev := fld.Enum().Values().ByNumber(val) 1943 if ev == nil { 1944 // if enum val is unknown, we'll return nil and have to skip it :( 1945 return nil 1946 } 1947 return ident(ev.Name()) 1948 case protoreflect.Message: 1949 return val.Interface() 1950 default: 1951 return val 1952 } 1953 } 1954 1955 func toUninterpretedOption(message proto.Message) *descriptorpb.UninterpretedOption { 1956 if uo, ok := message.(*descriptorpb.UninterpretedOption); ok { 1957 return uo 1958 } 1959 // marshal and unmarshal to convert; if we fail to convert, skip it 1960 var uo descriptorpb.UninterpretedOption 1961 data, err := proto.Marshal(message) 1962 if err != nil { 1963 return nil 1964 } 1965 if proto.Unmarshal(data, &uo) != nil { 1966 return nil 1967 } 1968 return &uo 1969 } 1970 1971 func uninterpretedToOptions(uninterp []*descriptorpb.UninterpretedOption) []option { 1972 opts := make([]option, len(uninterp)) 1973 for i, unint := range uninterp { 1974 var buf bytes.Buffer 1975 for ni, n := range unint.Name { 1976 if ni > 0 { 1977 buf.WriteByte('.') 1978 } 1979 if n.GetIsExtension() { 1980 _, _ = fmt.Fprintf(&buf, "(%s)", n.GetNamePart()) 1981 } else { 1982 buf.WriteString(n.GetNamePart()) 1983 } 1984 } 1985 1986 var v interface{} 1987 switch { 1988 case unint.IdentifierValue != nil: 1989 v = ident(unint.GetIdentifierValue()) 1990 case unint.StringValue != nil: 1991 v = string(unint.GetStringValue()) 1992 case unint.DoubleValue != nil: 1993 v = unint.GetDoubleValue() 1994 case unint.PositiveIntValue != nil: 1995 v = unint.GetPositiveIntValue() 1996 case unint.NegativeIntValue != nil: 1997 v = unint.GetNegativeIntValue() 1998 case unint.AggregateValue != nil: 1999 v = ident("{ " + unint.GetAggregateValue() + " }") 2000 } 2001 2002 opts[i] = option{name: buf.String(), val: v} 2003 } 2004 return opts 2005 } 2006 2007 func optionsAsElementAddrs(optionsTag int32, order int, opts map[int32][]option) []elementAddr { 2008 optAddrs := make([]elementAddr, 0, len(opts)) 2009 for tag := range opts { 2010 optAddrs = append(optAddrs, elementAddr{elementType: optionsTag, elementIndex: int(tag), order: order}) 2011 } 2012 // We want stable output. So, if the printer can't sort these a better way, 2013 // they'll at least be in a deterministic order (by name). 2014 sort.Sort(optionsByName{addrs: optAddrs, opts: opts}) 2015 return optAddrs 2016 } 2017 2018 // quotedBytes implements the text format for string literals for protocol 2019 // buffers. Since the underlying data is a bytes field, this encodes all 2020 // bytes outside the 7-bit ASCII printable range. To preserve unicode strings 2021 // without byte escapes, use quotedString. 2022 func quotedBytes(s string) string { 2023 var b bytes.Buffer 2024 b.WriteByte('"') 2025 // Loop over the bytes, not the runes. 2026 for i := 0; i < len(s); i++ { 2027 // Divergence from C++: we don't escape apostrophes. 2028 // There's no need to escape them, and the C++ parser 2029 // copes with a naked apostrophe. 2030 switch c := s[i]; c { 2031 case '\n': 2032 b.WriteString("\\n") 2033 case '\r': 2034 b.WriteString("\\r") 2035 case '\t': 2036 b.WriteString("\\t") 2037 case '"': 2038 b.WriteString("\\\"") 2039 case '\\': 2040 b.WriteString("\\\\") 2041 default: 2042 if c >= 0x20 && c < 0x7f { 2043 b.WriteByte(c) 2044 } else { 2045 _, _ = fmt.Fprintf(&b, "\\%03o", c) 2046 } 2047 } 2048 } 2049 b.WriteByte('"') 2050 2051 return b.String() 2052 } 2053 2054 // quotedString implements the text format for string literals for protocol 2055 // buffers. This form is also acceptable for string literals in option values 2056 // by the protocol buffer compiler, protoc. 2057 func quotedString(s string) string { 2058 var b bytes.Buffer 2059 b.WriteByte('"') 2060 // Loop over the bytes, not the runes. 2061 for { 2062 r, n := utf8.DecodeRuneInString(s) 2063 if n == 0 { 2064 break // end of string 2065 } 2066 if r == utf8.RuneError && n == 1 { 2067 // Invalid UTF8! Use an octal byte escape to encode the bad byte. 2068 _, _ = fmt.Fprintf(&b, "\\%03o", s[0]) 2069 s = s[1:] 2070 continue 2071 } 2072 2073 // Divergence from C++: we don't escape apostrophes. 2074 // There's no need to escape them, and the C++ parser 2075 // copes with a naked apostrophe. 2076 switch r { 2077 case '\n': 2078 b.WriteString("\\n") 2079 case '\r': 2080 b.WriteString("\\r") 2081 case '\t': 2082 b.WriteString("\\t") 2083 case '"': 2084 b.WriteString("\\\"") 2085 case '\\': 2086 b.WriteString("\\\\") 2087 default: 2088 if unicode.IsPrint(r) { 2089 b.WriteRune(r) 2090 } else { 2091 // if it's not printable, use a unicode escape 2092 if r > 0xffff { 2093 _, _ = fmt.Fprintf(&b, "\\U%08X", r) 2094 } else if r > 0x7F { 2095 _, _ = fmt.Fprintf(&b, "\\u%04X", r) 2096 } else { 2097 _, _ = fmt.Fprintf(&b, "\\%03o", byte(r)) 2098 } 2099 } 2100 } 2101 2102 s = s[n:] 2103 } 2104 2105 b.WriteByte('"') 2106 2107 return b.String() 2108 } 2109 2110 type elementAddr struct { 2111 elementType int32 2112 elementIndex int 2113 order int 2114 } 2115 2116 type elementAddrs struct { 2117 addrs []elementAddr 2118 dsc interface{} 2119 opts map[int32][]option 2120 } 2121 2122 func (a elementAddrs) Len() int { 2123 return len(a.addrs) 2124 } 2125 2126 func (a elementAddrs) Less(i, j int) bool { 2127 // explicit order is considered first 2128 if a.addrs[i].order < a.addrs[j].order { 2129 return true 2130 } else if a.addrs[i].order > a.addrs[j].order { 2131 return false 2132 } 2133 // if order is equal, sort by element type 2134 if a.addrs[i].elementType < a.addrs[j].elementType { 2135 return true 2136 } else if a.addrs[i].elementType > a.addrs[j].elementType { 2137 return false 2138 } 2139 2140 di := a.at(a.addrs[i]) 2141 dj := a.at(a.addrs[j]) 2142 2143 switch vi := di.(type) { 2144 case *desc.FieldDescriptor: 2145 // fields are ordered by tag number 2146 vj := dj.(*desc.FieldDescriptor) 2147 // regular fields before extensions; extensions grouped by extendee 2148 if !vi.IsExtension() && vj.IsExtension() { 2149 return true 2150 } else if vi.IsExtension() && !vj.IsExtension() { 2151 return false 2152 } else if vi.IsExtension() && vj.IsExtension() { 2153 if vi.GetOwner() != vj.GetOwner() { 2154 return vi.GetOwner().GetFullyQualifiedName() < vj.GetOwner().GetFullyQualifiedName() 2155 } 2156 } 2157 return vi.GetNumber() < vj.GetNumber() 2158 2159 case *desc.EnumValueDescriptor: 2160 // enum values ordered by number then name, 2161 // but first value number must be 0 for open enums 2162 vj := dj.(*desc.EnumValueDescriptor) 2163 if vi.GetNumber() == vj.GetNumber() { 2164 return vi.GetName() < vj.GetName() 2165 } 2166 if !vi.GetEnum().UnwrapEnum().IsClosed() { 2167 if vj.GetNumber() == 0 { 2168 return false 2169 } 2170 if vi.GetNumber() == 0 { 2171 return true 2172 } 2173 } 2174 return vi.GetNumber() < vj.GetNumber() 2175 2176 case *descriptorpb.DescriptorProto_ExtensionRange: 2177 // extension ranges ordered by tag 2178 return vi.GetStart() < dj.(*descriptorpb.DescriptorProto_ExtensionRange).GetStart() 2179 2180 case reservedRange: 2181 // reserved ranges ordered by tag, too 2182 return vi.start < dj.(reservedRange).start 2183 2184 case string: 2185 // reserved names lexically sorted 2186 return vi < dj.(string) 2187 2188 case pkg: 2189 // reserved names lexically sorted 2190 return vi < dj.(pkg) 2191 2192 case imp: 2193 // reserved names lexically sorted 2194 return vi < dj.(imp) 2195 2196 case []option: 2197 // options sorted by name, extensions last 2198 return optionLess(vi, dj.([]option)) 2199 2200 default: 2201 // all other descriptors ordered by name 2202 return di.(desc.Descriptor).GetName() < dj.(desc.Descriptor).GetName() 2203 } 2204 } 2205 2206 func (a elementAddrs) Swap(i, j int) { 2207 a.addrs[i], a.addrs[j] = a.addrs[j], a.addrs[i] 2208 } 2209 2210 func (a elementAddrs) at(addr elementAddr) interface{} { 2211 switch dsc := a.dsc.(type) { 2212 case *desc.FileDescriptor: 2213 switch addr.elementType { 2214 case internal.File_packageTag: 2215 return pkg(dsc.GetPackage()) 2216 case internal.File_dependencyTag: 2217 return imp(dsc.AsFileDescriptorProto().GetDependency()[addr.elementIndex]) 2218 case internal.File_optionsTag: 2219 return a.opts[int32(addr.elementIndex)] 2220 case internal.File_messagesTag: 2221 return dsc.GetMessageTypes()[addr.elementIndex] 2222 case internal.File_enumsTag: 2223 return dsc.GetEnumTypes()[addr.elementIndex] 2224 case internal.File_servicesTag: 2225 return dsc.GetServices()[addr.elementIndex] 2226 case internal.File_extensionsTag: 2227 return dsc.GetExtensions()[addr.elementIndex] 2228 } 2229 case *desc.MessageDescriptor: 2230 switch addr.elementType { 2231 case internal.Message_optionsTag: 2232 return a.opts[int32(addr.elementIndex)] 2233 case internal.Message_fieldsTag: 2234 return dsc.GetFields()[addr.elementIndex] 2235 case internal.Message_nestedMessagesTag: 2236 return dsc.GetNestedMessageTypes()[addr.elementIndex] 2237 case internal.Message_enumsTag: 2238 return dsc.GetNestedEnumTypes()[addr.elementIndex] 2239 case internal.Message_extensionsTag: 2240 return dsc.GetNestedExtensions()[addr.elementIndex] 2241 case internal.Message_extensionRangeTag: 2242 return dsc.AsDescriptorProto().GetExtensionRange()[addr.elementIndex] 2243 case internal.Message_reservedRangeTag: 2244 rng := dsc.AsDescriptorProto().GetReservedRange()[addr.elementIndex] 2245 return reservedRange{start: rng.GetStart(), end: rng.GetEnd() - 1} 2246 case internal.Message_reservedNameTag: 2247 return dsc.AsDescriptorProto().GetReservedName()[addr.elementIndex] 2248 } 2249 case *desc.FieldDescriptor: 2250 if addr.elementType == internal.Field_optionsTag { 2251 return a.opts[int32(addr.elementIndex)] 2252 } 2253 case *desc.OneOfDescriptor: 2254 switch addr.elementType { 2255 case internal.OneOf_optionsTag: 2256 return a.opts[int32(addr.elementIndex)] 2257 case -internal.Message_fieldsTag: 2258 return dsc.GetOwner().GetFields()[addr.elementIndex] 2259 } 2260 case *desc.EnumDescriptor: 2261 switch addr.elementType { 2262 case internal.Enum_optionsTag: 2263 return a.opts[int32(addr.elementIndex)] 2264 case internal.Enum_valuesTag: 2265 return dsc.GetValues()[addr.elementIndex] 2266 case internal.Enum_reservedRangeTag: 2267 rng := dsc.AsEnumDescriptorProto().GetReservedRange()[addr.elementIndex] 2268 return reservedRange{start: rng.GetStart(), end: rng.GetEnd()} 2269 case internal.Enum_reservedNameTag: 2270 return dsc.AsEnumDescriptorProto().GetReservedName()[addr.elementIndex] 2271 } 2272 case *desc.EnumValueDescriptor: 2273 if addr.elementType == internal.EnumVal_optionsTag { 2274 return a.opts[int32(addr.elementIndex)] 2275 } 2276 case *desc.ServiceDescriptor: 2277 switch addr.elementType { 2278 case internal.Service_optionsTag: 2279 return a.opts[int32(addr.elementIndex)] 2280 case internal.Service_methodsTag: 2281 return dsc.GetMethods()[addr.elementIndex] 2282 } 2283 case *desc.MethodDescriptor: 2284 if addr.elementType == internal.Method_optionsTag { 2285 return a.opts[int32(addr.elementIndex)] 2286 } 2287 case extensionRange: 2288 if addr.elementType == internal.ExtensionRange_optionsTag { 2289 return a.opts[int32(addr.elementIndex)] 2290 } 2291 } 2292 2293 panic(fmt.Sprintf("location for unknown field %d of %T", addr.elementType, a.dsc)) 2294 } 2295 2296 type extensionRange struct { 2297 owner *desc.MessageDescriptor 2298 extRange *descriptorpb.DescriptorProto_ExtensionRange 2299 } 2300 2301 type elementSrcOrder struct { 2302 elementAddrs 2303 sourceInfo internal.SourceInfoMap 2304 prefix []int32 2305 } 2306 2307 func (a elementSrcOrder) Less(i, j int) bool { 2308 ti := a.addrs[i].elementType 2309 ei := a.addrs[i].elementIndex 2310 2311 tj := a.addrs[j].elementType 2312 ej := a.addrs[j].elementIndex 2313 2314 var si, sj *descriptorpb.SourceCodeInfo_Location 2315 if ei < 0 { 2316 si = a.sourceInfo.Get(append(a.prefix, -int32(ei))) 2317 } else if ti < 0 { 2318 p := make([]int32, len(a.prefix)-2) 2319 copy(p, a.prefix) 2320 si = a.sourceInfo.Get(append(p, ti, int32(ei))) 2321 } else { 2322 si = a.sourceInfo.Get(append(a.prefix, ti, int32(ei))) 2323 } 2324 if ej < 0 { 2325 sj = a.sourceInfo.Get(append(a.prefix, -int32(ej))) 2326 } else if tj < 0 { 2327 p := make([]int32, len(a.prefix)-2) 2328 copy(p, a.prefix) 2329 sj = a.sourceInfo.Get(append(p, tj, int32(ej))) 2330 } else { 2331 sj = a.sourceInfo.Get(append(a.prefix, tj, int32(ej))) 2332 } 2333 2334 if (si == nil) != (sj == nil) { 2335 // generally, we put unknown elements after known ones; 2336 // except package, imports, and option elements go first 2337 2338 // i will be unknown and j will be known 2339 swapped := false 2340 if si != nil { 2341 ti, tj = tj, ti 2342 swapped = true 2343 } 2344 switch a.dsc.(type) { 2345 case *desc.FileDescriptor: 2346 // NB: These comparisons are *trying* to get things ordered so that 2347 // 1) If the package element has no source info, it appears _first_. 2348 // 2) If any import element has no source info, it appears _after_ 2349 // the package element but _before_ any other element. 2350 // 3) If any option element has no source info, it appears _after_ 2351 // the package and import elements but _before_ any other element. 2352 // If the package, imports, and options are all missing source info, 2353 // this will sort them all to the top in expected order. But if they 2354 // are mixed (some _do_ have source info, some do not), and elements 2355 // with source info have spans that positions them _after_ other 2356 // elements in the file, then this Less function will be unstable 2357 // since the above dual objectives for imports and options ("before 2358 // this but after that") may be in conflict with one another. This 2359 // should not cause any problems, other than elements being possibly 2360 // sorted in a confusing order. 2361 // 2362 // Well-formed descriptors should instead have consistent source 2363 // info: either all elements have source info or none do. So this 2364 // should not be an issue in practice. 2365 if ti == internal.File_packageTag { 2366 return !swapped 2367 } 2368 if ti == internal.File_dependencyTag { 2369 if tj == internal.File_packageTag { 2370 // imports will come *after* package 2371 return swapped 2372 } 2373 return !swapped 2374 } 2375 if ti == internal.File_optionsTag { 2376 if tj == internal.File_packageTag || tj == internal.File_dependencyTag { 2377 // options will come *after* package and imports 2378 return swapped 2379 } 2380 return !swapped 2381 } 2382 case *desc.MessageDescriptor: 2383 if ti == internal.Message_optionsTag { 2384 return !swapped 2385 } 2386 case *desc.EnumDescriptor: 2387 if ti == internal.Enum_optionsTag { 2388 return !swapped 2389 } 2390 case *desc.ServiceDescriptor: 2391 if ti == internal.Service_optionsTag { 2392 return !swapped 2393 } 2394 } 2395 return swapped 2396 2397 } else if si == nil || sj == nil { 2398 // let stable sort keep unknown elements in same relative order 2399 return false 2400 } 2401 2402 for idx := 0; idx < len(sj.Span); idx++ { 2403 if idx >= len(si.Span) { 2404 return true 2405 } 2406 if si.Span[idx] < sj.Span[idx] { 2407 return true 2408 } 2409 if si.Span[idx] > sj.Span[idx] { 2410 return false 2411 } 2412 } 2413 return false 2414 } 2415 2416 type customSortOrder struct { 2417 elementAddrs 2418 less func(a, b Element) bool 2419 } 2420 2421 func (cso customSortOrder) Less(i, j int) bool { 2422 // Regardless of the custom sort order, for proto3 files, 2423 // the enum value zero MUST be first. So we override the 2424 // custom sort order to make sure the file will be valid 2425 // and can compile. 2426 addri := cso.addrs[i] 2427 addrj := cso.addrs[j] 2428 di := cso.at(addri) 2429 dj := cso.at(addrj) 2430 if addri.elementType == addrj.elementType { 2431 if vi, ok := di.(*desc.EnumValueDescriptor); ok { 2432 vj := dj.(*desc.EnumValueDescriptor) 2433 if !vi.GetEnum().UnwrapEnum().IsClosed() { 2434 if vi.GetNumber() == 0 { 2435 return true 2436 } 2437 if vj.GetNumber() == 0 { 2438 return false 2439 } 2440 } 2441 } 2442 } 2443 2444 ei := asElement(di) 2445 ej := asElement(dj) 2446 return cso.less(ei, ej) 2447 } 2448 2449 type optionsByName struct { 2450 addrs []elementAddr 2451 opts map[int32][]option 2452 } 2453 2454 func (o optionsByName) Len() int { 2455 return len(o.addrs) 2456 } 2457 2458 func (o optionsByName) Less(i, j int) bool { 2459 oi := o.opts[int32(o.addrs[i].elementIndex)] 2460 oj := o.opts[int32(o.addrs[j].elementIndex)] 2461 return optionLess(oi, oj) 2462 } 2463 2464 func (o optionsByName) Swap(i, j int) { 2465 o.addrs[i], o.addrs[j] = o.addrs[j], o.addrs[i] 2466 } 2467 2468 func optionLess(i, j []option) bool { 2469 ni := i[0].name 2470 nj := j[0].name 2471 if ni[0] != '(' && nj[0] == '(' { 2472 return true 2473 } else if ni[0] == '(' && nj[0] != '(' { 2474 return false 2475 } 2476 return ni < nj 2477 } 2478 2479 func (p *Printer) printBlockElement(isDecriptor bool, si *descriptorpb.SourceCodeInfo_Location, w *writer, indent int, el func(w *writer, trailer func(indent int, wantTrailingNewline bool))) { 2480 includeComments := isDecriptor || p.includeCommentType(CommentsTokens) 2481 2482 if includeComments && si != nil { 2483 p.printLeadingComments(si, w, indent) 2484 } 2485 el(w, func(indent int, wantTrailingNewline bool) { 2486 if includeComments && si != nil { 2487 if p.printTrailingComments(si, w, indent) && wantTrailingNewline && !p.Compact { 2488 // separator line between trailing comment and next element 2489 _, _ = fmt.Fprintln(w) 2490 } 2491 } 2492 }) 2493 if indent >= 0 && !w.newline { 2494 // if we're not printing inline but element did not have trailing newline, add one now 2495 _, _ = fmt.Fprintln(w) 2496 } 2497 } 2498 2499 func (p *Printer) printElement(isDecriptor bool, si *descriptorpb.SourceCodeInfo_Location, w *writer, indent int, el func(*writer)) { 2500 includeComments := isDecriptor || p.includeCommentType(CommentsTokens) 2501 2502 if includeComments && si != nil { 2503 p.printLeadingComments(si, w, indent) 2504 } 2505 el(w) 2506 if includeComments && si != nil { 2507 p.printTrailingComments(si, w, indent) 2508 } 2509 if indent >= 0 && !w.newline { 2510 // if we're not printing inline but element did not have trailing newline, add one now 2511 _, _ = fmt.Fprintln(w) 2512 } 2513 } 2514 2515 func (p *Printer) printElementString(si *descriptorpb.SourceCodeInfo_Location, w *writer, indent int, str string) { 2516 p.printElement(false, si, w, inline(indent), func(w *writer) { 2517 _, _ = fmt.Fprintf(w, "%s ", str) 2518 }) 2519 } 2520 2521 func (p *Printer) includeCommentType(c CommentType) bool { 2522 return (p.OmitComments & c) == 0 2523 } 2524 2525 func (p *Printer) printLeadingComments(si *descriptorpb.SourceCodeInfo_Location, w *writer, indent int) bool { 2526 endsInNewLine := false 2527 2528 if p.includeCommentType(CommentsDetached) { 2529 for _, c := range si.GetLeadingDetachedComments() { 2530 if p.printComment(c, w, indent, true) { 2531 // if comment ended in newline, add another newline to separate 2532 // this comment from the next 2533 p.newLine(w) 2534 endsInNewLine = true 2535 } else if indent < 0 { 2536 // comment did not end in newline and we are trying to inline? 2537 // just add a space to separate this comment from what follows 2538 _, _ = fmt.Fprint(w, " ") 2539 endsInNewLine = false 2540 } else { 2541 // comment did not end in newline and we are *not* trying to inline? 2542 // add newline to end of comment and add another to separate this 2543 // comment from what follows 2544 _, _ = fmt.Fprintln(w) // needed to end comment, regardless of p.Compact 2545 p.newLine(w) 2546 endsInNewLine = true 2547 } 2548 } 2549 } 2550 2551 if p.includeCommentType(CommentsLeading) && si.GetLeadingComments() != "" { 2552 endsInNewLine = p.printComment(si.GetLeadingComments(), w, indent, true) 2553 if !endsInNewLine { 2554 if indent >= 0 { 2555 // leading comment didn't end with newline but needs one 2556 // (because we're *not* inlining) 2557 _, _ = fmt.Fprintln(w) // needed to end comment, regardless of p.Compact 2558 endsInNewLine = true 2559 } else { 2560 // space between comment and following element when inlined 2561 _, _ = fmt.Fprint(w, " ") 2562 } 2563 } 2564 } 2565 2566 return endsInNewLine 2567 } 2568 2569 func (p *Printer) printTrailingComments(si *descriptorpb.SourceCodeInfo_Location, w *writer, indent int) bool { 2570 if p.includeCommentType(CommentsTrailing) && si.GetTrailingComments() != "" { 2571 if !p.printComment(si.GetTrailingComments(), w, indent, p.TrailingCommentsOnSeparateLine) && indent >= 0 { 2572 // trailing comment didn't end with newline but needs one 2573 // (because we're *not* inlining) 2574 _, _ = fmt.Fprintln(w) // needed to end comment, regardless of p.Compact 2575 } else if indent < 0 { 2576 _, _ = fmt.Fprint(w, " ") 2577 } 2578 return true 2579 } 2580 2581 return false 2582 } 2583 2584 func (p *Printer) printComment(comments string, w *writer, indent int, forceNextLine bool) bool { 2585 if comments == "" { 2586 return false 2587 } 2588 2589 var multiLine bool 2590 if indent < 0 { 2591 // use multi-line style when inlining 2592 multiLine = true 2593 } else { 2594 multiLine = p.PreferMultiLineStyleComments 2595 } 2596 if multiLine && strings.Contains(comments, "*/") { 2597 // can't emit '*/' in a multi-line style comment 2598 multiLine = false 2599 } 2600 2601 lines := strings.Split(comments, "\n") 2602 2603 // first, remove leading and trailing blank lines 2604 if lines[0] == "" { 2605 lines = lines[1:] 2606 } 2607 if len(lines) > 0 && lines[len(lines)-1] == "" { 2608 lines = lines[:len(lines)-1] 2609 } 2610 if len(lines) == 0 { 2611 return false 2612 } 2613 2614 if indent >= 0 && !w.newline { 2615 // last element did not have trailing newline, so we 2616 // either need to tack on newline or, if comment is 2617 // just one line, inline it on the end 2618 if forceNextLine || len(lines) > 1 { 2619 _, _ = fmt.Fprintln(w) 2620 } else { 2621 if !w.space { 2622 _, _ = fmt.Fprint(w, " ") 2623 } 2624 indent = inline(indent) 2625 } 2626 } 2627 2628 if len(lines) == 1 && multiLine { 2629 p.indent(w, indent) 2630 line := lines[0] 2631 if line[0] == ' ' && line[len(line)-1] != ' ' { 2632 // add trailing space for symmetry 2633 line += " " 2634 } 2635 _, _ = fmt.Fprintf(w, "/*%s*/", line) 2636 if indent >= 0 { 2637 _, _ = fmt.Fprintln(w) 2638 return true 2639 } 2640 return false 2641 } 2642 2643 if multiLine { 2644 // multi-line style comments that actually span multiple lines 2645 // get a blank line before and after so that comment renders nicely 2646 lines = append(lines, "", "") 2647 copy(lines[1:], lines) 2648 lines[0] = "" 2649 } 2650 2651 for i, l := range lines { 2652 if l != "" && !strings.HasPrefix(l, " ") { 2653 l = " " + l 2654 } 2655 p.maybeIndent(w, indent, i > 0) 2656 if multiLine { 2657 if i == 0 { 2658 // first line 2659 _, _ = fmt.Fprintf(w, "/*%s\n", strings.TrimRight(l, " \t")) 2660 } else if i == len(lines)-1 { 2661 // last line 2662 if strings.TrimSpace(l) == "" { 2663 _, _ = fmt.Fprint(w, " */") 2664 } else { 2665 _, _ = fmt.Fprintf(w, " *%s*/", l) 2666 } 2667 if indent >= 0 { 2668 _, _ = fmt.Fprintln(w) 2669 } 2670 } else { 2671 _, _ = fmt.Fprintf(w, " *%s\n", strings.TrimRight(l, " \t")) 2672 } 2673 } else { 2674 _, _ = fmt.Fprintf(w, "//%s\n", strings.TrimRight(l, " \t")) 2675 } 2676 } 2677 2678 // single-line comments always end in newline; multi-line comments only 2679 // end in newline for non-negative (e.g. non-inlined) indentation 2680 return !multiLine || indent >= 0 2681 } 2682 2683 func (p *Printer) indent(w io.Writer, indent int) { 2684 for i := 0; i < indent; i++ { 2685 _, _ = fmt.Fprint(w, p.Indent) 2686 } 2687 } 2688 2689 func (p *Printer) maybeIndent(w io.Writer, indent int, requireIndent bool) { 2690 if indent < 0 && requireIndent { 2691 p.indent(w, -indent) 2692 } else { 2693 p.indent(w, indent) 2694 } 2695 } 2696 2697 type writer struct { 2698 io.Writer 2699 err error 2700 space bool 2701 newline bool 2702 } 2703 2704 func newWriter(w io.Writer) *writer { 2705 return &writer{Writer: w, newline: true} 2706 } 2707 2708 func (w *writer) Write(p []byte) (int, error) { 2709 if len(p) == 0 { 2710 return 0, nil 2711 } 2712 2713 w.newline = false 2714 2715 if w.space { 2716 // skip any trailing space if the following 2717 // character is semicolon, comma, or close bracket 2718 if p[0] != ';' && p[0] != ',' { 2719 _, err := w.Writer.Write([]byte{' '}) 2720 if err != nil { 2721 w.err = err 2722 return 0, err 2723 } 2724 } 2725 w.space = false 2726 } 2727 2728 if p[len(p)-1] == ' ' { 2729 w.space = true 2730 p = p[:len(p)-1] 2731 } 2732 if len(p) > 0 && p[len(p)-1] == '\n' { 2733 w.newline = true 2734 } 2735 2736 num, err := w.Writer.Write(p) 2737 if err != nil { 2738 w.err = err 2739 } else if w.space { 2740 // pretend space was written 2741 num++ 2742 } 2743 return num, err 2744 }