github.com/hoveychen/protoreflect@v1.4.7-0.20221103114119-0b4b3385ec76/desc/protoparse/source_code_info.go (about) 1 package protoparse 2 3 import ( 4 "bytes" 5 "strings" 6 7 "github.com/golang/protobuf/proto" 8 dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" 9 10 "github.com/hoveychen/protoreflect/desc/internal" 11 ) 12 13 func (r *parseResult) generateSourceCodeInfo() *dpb.SourceCodeInfo { 14 if r.nodes == nil { 15 // skip files that do not have AST info (these will be files 16 // that came from well-known descriptors, instead of from source) 17 return nil 18 } 19 20 sci := sourceCodeInfo{commentsUsed: map[*comment]struct{}{}} 21 path := make([]int32, 0, 10) 22 23 fn := r.getFileNode(r.fd).(*fileNode) 24 sci.newLocWithoutComments(fn, nil) 25 26 if fn.syntax != nil { 27 sci.newLoc(fn.syntax, append(path, internal.File_syntaxTag)) 28 } 29 30 var depIndex, optIndex, msgIndex, enumIndex, extendIndex, svcIndex int32 31 32 for _, child := range fn.decls { 33 switch { 34 case child.imp != nil: 35 sci.newLoc(child.imp, append(path, internal.File_dependencyTag, int32(depIndex))) 36 depIndex++ 37 case child.pkg != nil: 38 sci.newLoc(child.pkg, append(path, internal.File_packageTag)) 39 case child.option != nil: 40 r.generateSourceCodeInfoForOption(&sci, child.option, false, &optIndex, append(path, internal.File_optionsTag)) 41 case child.message != nil: 42 r.generateSourceCodeInfoForMessage(&sci, child.message, nil, append(path, internal.File_messagesTag, msgIndex)) 43 msgIndex++ 44 case child.enum != nil: 45 r.generateSourceCodeInfoForEnum(&sci, child.enum, append(path, internal.File_enumsTag, enumIndex)) 46 enumIndex++ 47 case child.extend != nil: 48 r.generateSourceCodeInfoForExtensions(&sci, child.extend, &extendIndex, &msgIndex, append(path, internal.File_extensionsTag), append(dup(path), internal.File_messagesTag)) 49 case child.service != nil: 50 r.generateSourceCodeInfoForService(&sci, child.service, append(path, internal.File_servicesTag, svcIndex)) 51 svcIndex++ 52 } 53 } 54 55 return &dpb.SourceCodeInfo{Location: sci.locs} 56 } 57 58 func (r *parseResult) generateSourceCodeInfoForOption(sci *sourceCodeInfo, n *optionNode, compact bool, uninterpIndex *int32, path []int32) { 59 if !compact { 60 sci.newLocWithoutComments(n, path) 61 } 62 subPath := r.interpretedOptions[n] 63 if len(subPath) > 0 { 64 p := path 65 if subPath[0] == -1 { 66 // used by "default" and "json_name" field pseudo-options 67 // to attribute path to parent element (since those are 68 // stored directly on the descriptor, not its options) 69 p = make([]int32, len(path)-1) 70 copy(p, path) 71 subPath = subPath[1:] 72 } 73 sci.newLoc(n, append(p, subPath...)) 74 return 75 } 76 77 // it's an uninterpreted option 78 optPath := append(path, internal.UninterpretedOptionsTag, *uninterpIndex) 79 *uninterpIndex++ 80 sci.newLoc(n, optPath) 81 var valTag int32 82 switch n.val.(type) { 83 case *identNode: 84 valTag = internal.Uninterpreted_identTag 85 case *intLiteralNode: 86 valTag = internal.Uninterpreted_posIntTag 87 case *negativeIntLiteralNode: 88 valTag = internal.Uninterpreted_negIntTag 89 case *floatLiteralNode: 90 valTag = internal.Uninterpreted_doubleTag 91 case *stringLiteralNode: 92 valTag = internal.Uninterpreted_stringTag 93 case *aggregateLiteralNode: 94 valTag = internal.Uninterpreted_aggregateTag 95 } 96 if valTag != 0 { 97 sci.newLoc(n.val, append(optPath, valTag)) 98 } 99 for j, nn := range n.name.parts { 100 optNmPath := append(optPath, internal.Uninterpreted_nameTag, int32(j)) 101 sci.newLoc(nn, optNmPath) 102 sci.newLoc(nn.text, append(optNmPath, internal.UninterpretedName_nameTag)) 103 } 104 } 105 106 func (r *parseResult) generateSourceCodeInfoForMessage(sci *sourceCodeInfo, n msgDecl, fieldPath []int32, path []int32) { 107 sci.newLoc(n, path) 108 109 var decls []*messageElement 110 switch n := n.(type) { 111 case *messageNode: 112 decls = n.decls 113 case *groupNode: 114 decls = n.decls 115 case *mapFieldNode: 116 // map entry so nothing else to do 117 return 118 } 119 120 sci.newLoc(n.messageName(), append(path, internal.Message_nameTag)) 121 // matching protoc, which emits the corresponding field type name (for group fields) 122 // right after the source location for the group message name 123 if fieldPath != nil { 124 sci.newLoc(n.messageName(), append(fieldPath, internal.Field_typeNameTag)) 125 } 126 127 var optIndex, fieldIndex, oneOfIndex, extendIndex, nestedMsgIndex int32 128 var nestedEnumIndex, extRangeIndex, reservedRangeIndex, reservedNameIndex int32 129 for _, child := range decls { 130 switch { 131 case child.option != nil: 132 r.generateSourceCodeInfoForOption(sci, child.option, false, &optIndex, append(path, internal.Message_optionsTag)) 133 case child.field != nil: 134 r.generateSourceCodeInfoForField(sci, child.field, append(path, internal.Message_fieldsTag, fieldIndex)) 135 fieldIndex++ 136 case child.group != nil: 137 fldPath := append(path, internal.Message_fieldsTag, fieldIndex) 138 r.generateSourceCodeInfoForField(sci, child.group, fldPath) 139 fieldIndex++ 140 r.generateSourceCodeInfoForMessage(sci, child.group, fldPath, append(dup(path), internal.Message_nestedMessagesTag, nestedMsgIndex)) 141 nestedMsgIndex++ 142 case child.mapField != nil: 143 r.generateSourceCodeInfoForField(sci, child.mapField, append(path, internal.Message_fieldsTag, fieldIndex)) 144 fieldIndex++ 145 case child.oneOf != nil: 146 r.generateSourceCodeInfoForOneOf(sci, child.oneOf, &fieldIndex, append(path, internal.Message_fieldsTag), append(dup(path), internal.Message_oneOfsTag, oneOfIndex)) 147 oneOfIndex++ 148 case child.nested != nil: 149 r.generateSourceCodeInfoForMessage(sci, child.nested, nil, append(path, internal.Message_nestedMessagesTag, nestedMsgIndex)) 150 nestedMsgIndex++ 151 case child.enum != nil: 152 r.generateSourceCodeInfoForEnum(sci, child.enum, append(path, internal.Message_enumsTag, nestedEnumIndex)) 153 nestedEnumIndex++ 154 case child.extend != nil: 155 r.generateSourceCodeInfoForExtensions(sci, child.extend, &extendIndex, &nestedMsgIndex, append(path, internal.Message_extensionsTag), append(dup(path), internal.Message_nestedMessagesTag)) 156 case child.extensionRange != nil: 157 r.generateSourceCodeInfoForExtensionRanges(sci, child.extensionRange, &extRangeIndex, append(path, internal.Message_extensionRangeTag)) 158 case child.reserved != nil: 159 if len(child.reserved.names) > 0 { 160 resPath := append(path, internal.Message_reservedNameTag) 161 sci.newLoc(child.reserved, resPath) 162 for _, rn := range child.reserved.names { 163 sci.newLoc(rn, append(resPath, reservedNameIndex)) 164 reservedNameIndex++ 165 } 166 } 167 if len(child.reserved.ranges) > 0 { 168 resPath := append(path, internal.Message_reservedRangeTag) 169 sci.newLoc(child.reserved, resPath) 170 for _, rr := range child.reserved.ranges { 171 r.generateSourceCodeInfoForReservedRange(sci, rr, append(resPath, reservedRangeIndex)) 172 reservedRangeIndex++ 173 } 174 } 175 } 176 } 177 } 178 179 func (r *parseResult) generateSourceCodeInfoForEnum(sci *sourceCodeInfo, n *enumNode, path []int32) { 180 sci.newLoc(n, path) 181 sci.newLoc(n.name, append(path, internal.Enum_nameTag)) 182 183 var optIndex, valIndex, reservedNameIndex, reservedRangeIndex int32 184 for _, child := range n.decls { 185 switch { 186 case child.option != nil: 187 r.generateSourceCodeInfoForOption(sci, child.option, false, &optIndex, append(path, internal.Enum_optionsTag)) 188 case child.value != nil: 189 r.generateSourceCodeInfoForEnumValue(sci, child.value, append(path, internal.Enum_valuesTag, valIndex)) 190 valIndex++ 191 case child.reserved != nil: 192 if len(child.reserved.names) > 0 { 193 resPath := append(path, internal.Enum_reservedNameTag) 194 sci.newLoc(child.reserved, resPath) 195 for _, rn := range child.reserved.names { 196 sci.newLoc(rn, append(resPath, reservedNameIndex)) 197 reservedNameIndex++ 198 } 199 } 200 if len(child.reserved.ranges) > 0 { 201 resPath := append(path, internal.Enum_reservedRangeTag) 202 sci.newLoc(child.reserved, resPath) 203 for _, rr := range child.reserved.ranges { 204 r.generateSourceCodeInfoForReservedRange(sci, rr, append(resPath, reservedRangeIndex)) 205 reservedRangeIndex++ 206 } 207 } 208 } 209 } 210 } 211 212 func (r *parseResult) generateSourceCodeInfoForEnumValue(sci *sourceCodeInfo, n *enumValueNode, path []int32) { 213 sci.newLoc(n, path) 214 sci.newLoc(n.name, append(path, internal.EnumVal_nameTag)) 215 sci.newLoc(n.getNumber(), append(path, internal.EnumVal_numberTag)) 216 217 // enum value options 218 if n.options != nil { 219 optsPath := append(path, internal.EnumVal_optionsTag) 220 sci.newLoc(n.options, optsPath) 221 var optIndex int32 222 for _, opt := range n.options.decls { 223 r.generateSourceCodeInfoForOption(sci, opt, true, &optIndex, optsPath) 224 } 225 } 226 } 227 228 func (r *parseResult) generateSourceCodeInfoForReservedRange(sci *sourceCodeInfo, n *rangeNode, path []int32) { 229 sci.newLoc(n, path) 230 sci.newLoc(n.stNode, append(path, internal.ReservedRange_startTag)) 231 if n.stNode != n.enNode { 232 sci.newLoc(n.enNode, append(path, internal.ReservedRange_endTag)) 233 } 234 } 235 236 func (r *parseResult) generateSourceCodeInfoForExtensions(sci *sourceCodeInfo, n *extendNode, extendIndex, msgIndex *int32, extendPath, msgPath []int32) { 237 sci.newLoc(n, extendPath) 238 for _, decl := range n.decls { 239 switch { 240 case decl.field != nil: 241 r.generateSourceCodeInfoForField(sci, decl.field, append(extendPath, *extendIndex)) 242 *extendIndex++ 243 case decl.group != nil: 244 fldPath := append(extendPath, *extendIndex) 245 r.generateSourceCodeInfoForField(sci, decl.group, fldPath) 246 *extendIndex++ 247 r.generateSourceCodeInfoForMessage(sci, decl.group, fldPath, append(msgPath, *msgIndex)) 248 *msgIndex++ 249 } 250 } 251 } 252 253 func (r *parseResult) generateSourceCodeInfoForOneOf(sci *sourceCodeInfo, n *oneOfNode, fieldIndex *int32, fieldPath, oneOfPath []int32) { 254 sci.newLoc(n, oneOfPath) 255 sci.newLoc(n.name, append(oneOfPath, internal.OneOf_nameTag)) 256 257 var optIndex int32 258 for _, child := range n.decls { 259 switch { 260 case child.option != nil: 261 r.generateSourceCodeInfoForOption(sci, child.option, false, &optIndex, append(oneOfPath, internal.OneOf_optionsTag)) 262 case child.field != nil: 263 r.generateSourceCodeInfoForField(sci, child.field, append(fieldPath, *fieldIndex)) 264 *fieldIndex++ 265 } 266 } 267 } 268 269 func (r *parseResult) generateSourceCodeInfoForField(sci *sourceCodeInfo, n fieldDecl, path []int32) { 270 isGroup := false 271 var opts *compactOptionsNode 272 var extendee *extendNode 273 var fieldType string 274 switch n := n.(type) { 275 case *fieldNode: 276 opts = n.options 277 extendee = n.extendee 278 fieldType = n.fldType.val 279 case *mapFieldNode: 280 opts = n.options 281 case *groupNode: 282 isGroup = true 283 extendee = n.extendee 284 case *syntheticMapField: 285 // shouldn't get here since we don't recurse into fields from a mapNode 286 // in generateSourceCodeInfoForMessage... but just in case 287 return 288 } 289 290 if isGroup { 291 // comments will appear on group message 292 sci.newLocWithoutComments(n, path) 293 if extendee != nil { 294 sci.newLoc(extendee.extendee, append(path, internal.Field_extendeeTag)) 295 } 296 if n.fieldLabel() != nil { 297 // no comments here either (label is first token for group, so we want 298 // to leave the comments to be associated with the group message instead) 299 sci.newLocWithoutComments(n.fieldLabel(), append(path, internal.Field_labelTag)) 300 } 301 sci.newLoc(n.fieldType(), append(path, internal.Field_typeTag)) 302 // let the name comments be attributed to the group name 303 sci.newLocWithoutComments(n.fieldName(), append(path, internal.Field_nameTag)) 304 } else { 305 sci.newLoc(n, path) 306 if extendee != nil { 307 sci.newLoc(extendee.extendee, append(path, internal.Field_extendeeTag)) 308 } 309 if n.fieldLabel() != nil { 310 sci.newLoc(n.fieldLabel(), append(path, internal.Field_labelTag)) 311 } 312 n.fieldType() 313 var tag int32 314 if _, isScalar := fieldTypes[fieldType]; isScalar { 315 tag = internal.Field_typeTag 316 } else { 317 // this is a message or an enum, so attribute type location 318 // to the type name field 319 tag = internal.Field_typeNameTag 320 } 321 sci.newLoc(n.fieldType(), append(path, tag)) 322 sci.newLoc(n.fieldName(), append(path, internal.Field_nameTag)) 323 } 324 sci.newLoc(n.fieldTag(), append(path, internal.Field_numberTag)) 325 326 if opts != nil { 327 optsPath := append(path, internal.Field_optionsTag) 328 sci.newLoc(opts, optsPath) 329 var optIndex int32 330 for _, opt := range opts.decls { 331 r.generateSourceCodeInfoForOption(sci, opt, true, &optIndex, optsPath) 332 } 333 } 334 } 335 336 func (r *parseResult) generateSourceCodeInfoForExtensionRanges(sci *sourceCodeInfo, n *extensionRangeNode, extRangeIndex *int32, path []int32) { 337 sci.newLoc(n, path) 338 for _, child := range n.ranges { 339 path := append(path, *extRangeIndex) 340 *extRangeIndex++ 341 sci.newLoc(child, path) 342 sci.newLoc(child.stNode, append(path, internal.ExtensionRange_startTag)) 343 if child.stNode != child.enNode { 344 sci.newLoc(child.enNode, append(path, internal.ExtensionRange_endTag)) 345 } 346 if n.options != nil { 347 optsPath := append(path, internal.ExtensionRange_optionsTag) 348 sci.newLoc(n.options, optsPath) 349 var optIndex int32 350 for _, opt := range n.options.decls { 351 r.generateSourceCodeInfoForOption(sci, opt, true, &optIndex, optsPath) 352 } 353 } 354 } 355 } 356 357 func (r *parseResult) generateSourceCodeInfoForService(sci *sourceCodeInfo, n *serviceNode, path []int32) { 358 sci.newLoc(n, path) 359 sci.newLoc(n.name, append(path, internal.Service_nameTag)) 360 var optIndex, rpcIndex int32 361 for _, child := range n.decls { 362 switch { 363 case child.option != nil: 364 r.generateSourceCodeInfoForOption(sci, child.option, false, &optIndex, append(path, internal.Service_optionsTag)) 365 case child.rpc != nil: 366 r.generateSourceCodeInfoForMethod(sci, child.rpc, append(path, internal.Service_methodsTag, rpcIndex)) 367 rpcIndex++ 368 } 369 } 370 } 371 372 func (r *parseResult) generateSourceCodeInfoForMethod(sci *sourceCodeInfo, n *methodNode, path []int32) { 373 sci.newLoc(n, path) 374 sci.newLoc(n.name, append(path, internal.Method_nameTag)) 375 if n.input.streamKeyword != nil { 376 sci.newLoc(n.input.streamKeyword, append(path, internal.Method_inputStreamTag)) 377 } 378 sci.newLoc(n.input.msgType, append(path, internal.Method_inputTag)) 379 if n.output.streamKeyword != nil { 380 sci.newLoc(n.output.streamKeyword, append(path, internal.Method_outputStreamTag)) 381 } 382 sci.newLoc(n.output.msgType, append(path, internal.Method_outputTag)) 383 384 optsPath := append(path, internal.Method_optionsTag) 385 var optIndex int32 386 for _, opt := range n.options { 387 r.generateSourceCodeInfoForOption(sci, opt, false, &optIndex, optsPath) 388 } 389 } 390 391 type sourceCodeInfo struct { 392 locs []*dpb.SourceCodeInfo_Location 393 commentsUsed map[*comment]struct{} 394 } 395 396 func (sci *sourceCodeInfo) newLocWithoutComments(n node, path []int32) { 397 dup := make([]int32, len(path)) 398 copy(dup, path) 399 sci.locs = append(sci.locs, &dpb.SourceCodeInfo_Location{ 400 Path: dup, 401 Span: makeSpan(n.start(), n.end()), 402 }) 403 } 404 405 func (sci *sourceCodeInfo) newLoc(n node, path []int32) { 406 leadingComments := n.leadingComments() 407 trailingComments := n.trailingComments() 408 if sci.commentUsed(leadingComments) { 409 leadingComments = nil 410 } 411 if sci.commentUsed(trailingComments) { 412 trailingComments = nil 413 } 414 detached := groupComments(leadingComments) 415 var trail *string 416 if str, ok := combineComments(trailingComments); ok { 417 trail = proto.String(str) 418 } 419 var lead *string 420 if len(leadingComments) > 0 && leadingComments[len(leadingComments)-1].end.Line >= n.start().Line-1 { 421 lead = proto.String(detached[len(detached)-1]) 422 detached = detached[:len(detached)-1] 423 } 424 dup := make([]int32, len(path)) 425 copy(dup, path) 426 sci.locs = append(sci.locs, &dpb.SourceCodeInfo_Location{ 427 LeadingDetachedComments: detached, 428 LeadingComments: lead, 429 TrailingComments: trail, 430 Path: dup, 431 Span: makeSpan(n.start(), n.end()), 432 }) 433 } 434 435 func makeSpan(start, end *SourcePos) []int32 { 436 if start.Line == end.Line { 437 return []int32{int32(start.Line) - 1, int32(start.Col) - 1, int32(end.Col) - 1} 438 } 439 return []int32{int32(start.Line) - 1, int32(start.Col) - 1, int32(end.Line) - 1, int32(end.Col) - 1} 440 } 441 442 func (sci *sourceCodeInfo) commentUsed(c []comment) bool { 443 if len(c) == 0 { 444 return false 445 } 446 if _, ok := sci.commentsUsed[&c[0]]; ok { 447 return true 448 } 449 450 sci.commentsUsed[&c[0]] = struct{}{} 451 return false 452 } 453 454 func groupComments(comments []comment) []string { 455 if len(comments) == 0 { 456 return nil 457 } 458 459 var groups []string 460 singleLineStyle := comments[0].text[:2] == "//" 461 line := comments[0].end.Line 462 start := 0 463 for i := 1; i < len(comments); i++ { 464 c := comments[i] 465 prevSingleLine := singleLineStyle 466 singleLineStyle = strings.HasPrefix(comments[i].text, "//") 467 if !singleLineStyle || prevSingleLine != singleLineStyle || c.start.Line > line+1 { 468 // new group! 469 if str, ok := combineComments(comments[start:i]); ok { 470 groups = append(groups, str) 471 } 472 start = i 473 } 474 line = c.end.Line 475 } 476 // don't forget last group 477 if str, ok := combineComments(comments[start:]); ok { 478 groups = append(groups, str) 479 } 480 return groups 481 } 482 483 func combineComments(comments []comment) (string, bool) { 484 if len(comments) == 0 { 485 return "", false 486 } 487 var buf bytes.Buffer 488 for _, c := range comments { 489 if c.text[:2] == "//" { 490 buf.WriteString(c.text[2:]) 491 } else { 492 lines := strings.Split(c.text[2:len(c.text)-2], "\n") 493 first := true 494 for _, l := range lines { 495 if first { 496 first = false 497 } else { 498 buf.WriteByte('\n') 499 } 500 501 // strip a prefix of whitespace followed by '*' 502 j := 0 503 for j < len(l) { 504 if l[j] != ' ' && l[j] != '\t' { 505 break 506 } 507 j++ 508 } 509 if j == len(l) { 510 l = "" 511 } else if l[j] == '*' { 512 l = l[j+1:] 513 } else if j > 0 { 514 l = " " + l[j:] 515 } 516 517 buf.WriteString(l) 518 } 519 } 520 } 521 return buf.String(), true 522 } 523 524 func dup(p []int32) []int32 { 525 return append(([]int32)(nil), p...) 526 }