github.com/syumai/protoreflect@v1.7.1-0.20200810020253-2ac7e3b3a321/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/syumai/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 *compoundIdentNode:
    84  		valTag = internal.Uninterpreted_identTag
    85  	case *intLiteralNode:
    86  		valTag = internal.Uninterpreted_posIntTag
    87  	case *compoundIntNode:
    88  		valTag = internal.Uninterpreted_negIntTag
    89  	case *compoundFloatNode:
    90  		valTag = internal.Uninterpreted_doubleTag
    91  	case *compoundStringNode:
    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, &nestedMsgIndex, append(path, internal.Message_fieldsTag), append(dup(path), internal.Message_nestedMessagesTag), 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.startNode, append(path, internal.ReservedRange_startTag))
   231  	if n.endNode != nil {
   232  		sci.newLoc(n.endNode, 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, nestedMsgIndex *int32, fieldPath, nestedMsgPath, 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  		case child.group != nil:
   266  			fldPath := append(fieldPath, *fieldIndex)
   267  			r.generateSourceCodeInfoForField(sci, child.group, fldPath)
   268  			*fieldIndex++
   269  			r.generateSourceCodeInfoForMessage(sci, child.group, fldPath, append(nestedMsgPath, *nestedMsgIndex))
   270  			*nestedMsgIndex++
   271  		}
   272  	}
   273  }
   274  
   275  func (r *parseResult) generateSourceCodeInfoForField(sci *sourceCodeInfo, n fieldDecl, path []int32) {
   276  	isGroup := false
   277  	var opts *compactOptionsNode
   278  	var extendee *extendNode
   279  	var fieldType string
   280  	switch n := n.(type) {
   281  	case *fieldNode:
   282  		opts = n.options
   283  		extendee = n.extendee
   284  		fieldType = n.fldType.val
   285  	case *mapFieldNode:
   286  		opts = n.options
   287  	case *groupNode:
   288  		isGroup = true
   289  		extendee = n.extendee
   290  	case *syntheticMapField:
   291  		// shouldn't get here since we don't recurse into fields from a mapNode
   292  		// in generateSourceCodeInfoForMessage... but just in case
   293  		return
   294  	}
   295  
   296  	if isGroup {
   297  		// comments will appear on group message
   298  		sci.newLocWithoutComments(n, path)
   299  		if extendee != nil {
   300  			sci.newLoc(extendee.extendee, append(path, internal.Field_extendeeTag))
   301  		}
   302  		if n.fieldLabel() != nil {
   303  			// no comments here either (label is first token for group, so we want
   304  			// to leave the comments to be associated with the group message instead)
   305  			sci.newLocWithoutComments(n.fieldLabel(), append(path, internal.Field_labelTag))
   306  		}
   307  		sci.newLoc(n.fieldType(), append(path, internal.Field_typeTag))
   308  		// let the name comments be attributed to the group name
   309  		sci.newLocWithoutComments(n.fieldName(), append(path, internal.Field_nameTag))
   310  	} else {
   311  		sci.newLoc(n, path)
   312  		if extendee != nil {
   313  			sci.newLoc(extendee.extendee, append(path, internal.Field_extendeeTag))
   314  		}
   315  		if n.fieldLabel() != nil {
   316  			sci.newLoc(n.fieldLabel(), append(path, internal.Field_labelTag))
   317  		}
   318  		n.fieldType()
   319  		var tag int32
   320  		if _, isScalar := fieldTypes[fieldType]; isScalar {
   321  			tag = internal.Field_typeTag
   322  		} else {
   323  			// this is a message or an enum, so attribute type location
   324  			// to the type name field
   325  			tag = internal.Field_typeNameTag
   326  		}
   327  		sci.newLoc(n.fieldType(), append(path, tag))
   328  		sci.newLoc(n.fieldName(), append(path, internal.Field_nameTag))
   329  	}
   330  	sci.newLoc(n.fieldTag(), append(path, internal.Field_numberTag))
   331  
   332  	if opts != nil {
   333  		optsPath := append(path, internal.Field_optionsTag)
   334  		sci.newLoc(opts, optsPath)
   335  		var optIndex int32
   336  		for _, opt := range opts.decls {
   337  			r.generateSourceCodeInfoForOption(sci, opt, true, &optIndex, optsPath)
   338  		}
   339  	}
   340  }
   341  
   342  func (r *parseResult) generateSourceCodeInfoForExtensionRanges(sci *sourceCodeInfo, n *extensionRangeNode, extRangeIndex *int32, path []int32) {
   343  	sci.newLoc(n, path)
   344  	for _, child := range n.ranges {
   345  		path := append(path, *extRangeIndex)
   346  		*extRangeIndex++
   347  		sci.newLoc(child, path)
   348  		sci.newLoc(child.startNode, append(path, internal.ExtensionRange_startTag))
   349  		if child.endNode != nil {
   350  			sci.newLoc(child.endNode, append(path, internal.ExtensionRange_endTag))
   351  		}
   352  		if n.options != nil {
   353  			optsPath := append(path, internal.ExtensionRange_optionsTag)
   354  			sci.newLoc(n.options, optsPath)
   355  			var optIndex int32
   356  			for _, opt := range n.options.decls {
   357  				r.generateSourceCodeInfoForOption(sci, opt, true, &optIndex, optsPath)
   358  			}
   359  		}
   360  	}
   361  }
   362  
   363  func (r *parseResult) generateSourceCodeInfoForService(sci *sourceCodeInfo, n *serviceNode, path []int32) {
   364  	sci.newLoc(n, path)
   365  	sci.newLoc(n.name, append(path, internal.Service_nameTag))
   366  	var optIndex, rpcIndex int32
   367  	for _, child := range n.decls {
   368  		switch {
   369  		case child.option != nil:
   370  			r.generateSourceCodeInfoForOption(sci, child.option, false, &optIndex, append(path, internal.Service_optionsTag))
   371  		case child.rpc != nil:
   372  			r.generateSourceCodeInfoForMethod(sci, child.rpc, append(path, internal.Service_methodsTag, rpcIndex))
   373  			rpcIndex++
   374  		}
   375  	}
   376  }
   377  
   378  func (r *parseResult) generateSourceCodeInfoForMethod(sci *sourceCodeInfo, n *methodNode, path []int32) {
   379  	sci.newLoc(n, path)
   380  	sci.newLoc(n.name, append(path, internal.Method_nameTag))
   381  	if n.input.streamKeyword != nil {
   382  		sci.newLoc(n.input.streamKeyword, append(path, internal.Method_inputStreamTag))
   383  	}
   384  	sci.newLoc(n.input.msgType, append(path, internal.Method_inputTag))
   385  	if n.output.streamKeyword != nil {
   386  		sci.newLoc(n.output.streamKeyword, append(path, internal.Method_outputStreamTag))
   387  	}
   388  	sci.newLoc(n.output.msgType, append(path, internal.Method_outputTag))
   389  
   390  	optsPath := append(path, internal.Method_optionsTag)
   391  	var optIndex int32
   392  	for _, opt := range n.options {
   393  		r.generateSourceCodeInfoForOption(sci, opt, false, &optIndex, optsPath)
   394  	}
   395  }
   396  
   397  type sourceCodeInfo struct {
   398  	locs         []*dpb.SourceCodeInfo_Location
   399  	commentsUsed map[*comment]struct{}
   400  }
   401  
   402  func (sci *sourceCodeInfo) newLocWithoutComments(n node, path []int32) {
   403  	dup := make([]int32, len(path))
   404  	copy(dup, path)
   405  	sci.locs = append(sci.locs, &dpb.SourceCodeInfo_Location{
   406  		Path: dup,
   407  		Span: makeSpan(n.start(), n.end()),
   408  	})
   409  }
   410  
   411  func (sci *sourceCodeInfo) newLoc(n node, path []int32) {
   412  	leadingComments := n.leadingComments()
   413  	trailingComments := n.trailingComments()
   414  	if sci.commentUsed(leadingComments) {
   415  		leadingComments = nil
   416  	}
   417  	if sci.commentUsed(trailingComments) {
   418  		trailingComments = nil
   419  	}
   420  	detached := groupComments(leadingComments)
   421  	var trail *string
   422  	if str, ok := combineComments(trailingComments); ok {
   423  		trail = proto.String(str)
   424  	}
   425  	var lead *string
   426  	if len(leadingComments) > 0 && leadingComments[len(leadingComments)-1].end.Line >= n.start().Line-1 {
   427  		lead = proto.String(detached[len(detached)-1])
   428  		detached = detached[:len(detached)-1]
   429  	}
   430  	dup := make([]int32, len(path))
   431  	copy(dup, path)
   432  	sci.locs = append(sci.locs, &dpb.SourceCodeInfo_Location{
   433  		LeadingDetachedComments: detached,
   434  		LeadingComments:         lead,
   435  		TrailingComments:        trail,
   436  		Path:                    dup,
   437  		Span:                    makeSpan(n.start(), n.end()),
   438  	})
   439  }
   440  
   441  func makeSpan(start, end *SourcePos) []int32 {
   442  	if start.Line == end.Line {
   443  		return []int32{int32(start.Line) - 1, int32(start.Col) - 1, int32(end.Col) - 1}
   444  	}
   445  	return []int32{int32(start.Line) - 1, int32(start.Col) - 1, int32(end.Line) - 1, int32(end.Col) - 1}
   446  }
   447  
   448  func (sci *sourceCodeInfo) commentUsed(c []comment) bool {
   449  	if len(c) == 0 {
   450  		return false
   451  	}
   452  	if _, ok := sci.commentsUsed[&c[0]]; ok {
   453  		return true
   454  	}
   455  
   456  	sci.commentsUsed[&c[0]] = struct{}{}
   457  	return false
   458  }
   459  
   460  func groupComments(comments []comment) []string {
   461  	if len(comments) == 0 {
   462  		return nil
   463  	}
   464  
   465  	var groups []string
   466  	singleLineStyle := comments[0].text[:2] == "//"
   467  	line := comments[0].end.Line
   468  	start := 0
   469  	for i := 1; i < len(comments); i++ {
   470  		c := comments[i]
   471  		prevSingleLine := singleLineStyle
   472  		singleLineStyle = strings.HasPrefix(comments[i].text, "//")
   473  		if !singleLineStyle || prevSingleLine != singleLineStyle || c.start.Line > line+1 {
   474  			// new group!
   475  			if str, ok := combineComments(comments[start:i]); ok {
   476  				groups = append(groups, str)
   477  			}
   478  			start = i
   479  		}
   480  		line = c.end.Line
   481  	}
   482  	// don't forget last group
   483  	if str, ok := combineComments(comments[start:]); ok {
   484  		groups = append(groups, str)
   485  	}
   486  	return groups
   487  }
   488  
   489  func combineComments(comments []comment) (string, bool) {
   490  	if len(comments) == 0 {
   491  		return "", false
   492  	}
   493  	var buf bytes.Buffer
   494  	for _, c := range comments {
   495  		if c.text[:2] == "//" {
   496  			buf.WriteString(c.text[2:])
   497  		} else {
   498  			lines := strings.Split(c.text[2:len(c.text)-2], "\n")
   499  			first := true
   500  			for _, l := range lines {
   501  				if first {
   502  					first = false
   503  				} else {
   504  					buf.WriteByte('\n')
   505  				}
   506  
   507  				// strip a prefix of whitespace followed by '*'
   508  				j := 0
   509  				for j < len(l) {
   510  					if l[j] != ' ' && l[j] != '\t' {
   511  						break
   512  					}
   513  					j++
   514  				}
   515  				if j == len(l) {
   516  					l = ""
   517  				} else if l[j] == '*' {
   518  					l = l[j+1:]
   519  				} else if j > 0 {
   520  					l = " " + l[j:]
   521  				}
   522  
   523  				buf.WriteString(l)
   524  			}
   525  		}
   526  	}
   527  	return buf.String(), true
   528  }
   529  
   530  func dup(p []int32) []int32 {
   531  	return append(([]int32)(nil), p...)
   532  }