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  }