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