kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/indexer/markedsource.go (about)

     1  /*
     2   * Copyright 2019 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package indexer
    18  
    19  import (
    20  	"fmt"
    21  	"go/types"
    22  	"strings"
    23  
    24  	"kythe.io/kythe/go/util/kytheuri"
    25  	"kythe.io/kythe/go/util/log"
    26  	"kythe.io/kythe/go/util/schema/facts"
    27  
    28  	"github.com/golang/protobuf/proto"
    29  
    30  	cpb "kythe.io/kythe/proto/common_go_proto"
    31  	spb "kythe.io/kythe/proto/storage_go_proto"
    32  )
    33  
    34  // MarkedSource returns a MarkedSource message describing obj.
    35  // See: http://www.kythe.io/docs/schema/marked-source.html.
    36  func (e *emitter) MarkedSource(obj types.Object) *cpb.MarkedSource {
    37  	pi := e.pi
    38  	ms := &cpb.MarkedSource{
    39  		Child: []*cpb.MarkedSource{{
    40  			Kind:    cpb.MarkedSource_IDENTIFIER,
    41  			PreText: objectName(obj),
    42  			Link:    []*cpb.Link{{Definition: []string{kytheuri.ToString(pi.ObjectVName(obj))}}},
    43  		}},
    44  	}
    45  
    46  	// Include the package name as context, and for objects that hang off a
    47  	// named struct or interface, a label for that type.
    48  	//
    49  	// For example, given
    50  	//     package p
    51  	//     var v int                // context is "p"
    52  	//     type s struct { v int }  // context is "p.s"
    53  	//     func (v) f(x int) {}
    54  	//              ^ ^--------------- context is "p.v.f"
    55  	//              \----------------- context is "p.v"
    56  	//
    57  	// The tree structure is:
    58  	//
    59  	//                 (box)
    60  	//                   |
    61  	//         (ctx)-----+-------(id)
    62  	//           |                |
    63  	//     +----"."----+(".")    name
    64  	//     |           |
    65  	//    (id) pkg    type
    66  	//
    67  	if ctx := pi.typeContext(obj); len(ctx) != 0 {
    68  		ms.Child = append([]*cpb.MarkedSource{{
    69  			Kind:              cpb.MarkedSource_CONTEXT,
    70  			PostChildText:     ".",
    71  			AddFinalListToken: true,
    72  			Child:             ctx,
    73  		}}, ms.Child...)
    74  	}
    75  
    76  	// Handle types with "interesting" superstructure specially.
    77  	switch t := obj.(type) {
    78  	case *types.Func:
    79  		// For functions we include the parameters and return values, and for
    80  		// methods the receiver.
    81  		//
    82  		// Methods:   func (R) Name(p1, ...) (r0, ...)
    83  		// Functions: func Name(p0, ...) (r0, ...)
    84  		fn := &cpb.MarkedSource{
    85  			Kind: cpb.MarkedSource_BOX,
    86  			Child: []*cpb.MarkedSource{{
    87  				Kind:     cpb.MarkedSource_MODIFIER,
    88  				PreText:  "func",
    89  				PostText: " ",
    90  			}},
    91  		}
    92  		sig := t.Type().(*types.Signature)
    93  		firstParam := 0
    94  		if recv := sig.Recv(); recv != nil {
    95  			// Parenthesized receiver type, e.g. (R).
    96  			fn.Child = append(fn.Child, &cpb.MarkedSource{
    97  				Kind:     cpb.MarkedSource_PARAMETER,
    98  				PreText:  "(",
    99  				PostText: ") ",
   100  				Child: []*cpb.MarkedSource{{
   101  					Kind: cpb.MarkedSource_LOOKUP_BY_PARAM,
   102  				}},
   103  			})
   104  			firstParam = 1
   105  		}
   106  		fn.Child = append(fn.Child, ms)
   107  
   108  		if sig.TypeParams().Len() > 0 {
   109  			tps := &cpb.MarkedSource{
   110  				Kind:          cpb.MarkedSource_PARAMETER_LOOKUP_BY_TPARAM,
   111  				PreText:       "[",
   112  				PostText:      "]",
   113  				PostChildText: ", ",
   114  			}
   115  			fn.Child = append(fn.Child, tps)
   116  		}
   117  
   118  		// If there are no parameters, the lookup will not produce anything.
   119  		// Ensure when this happens we still get parentheses for notational
   120  		// purposes.
   121  		if sig.Params().Len() == 0 {
   122  			fn.Child = append(fn.Child, &cpb.MarkedSource{
   123  				Kind:    cpb.MarkedSource_PARAMETER,
   124  				PreText: "()",
   125  			})
   126  		} else {
   127  			fn.Child = append(fn.Child, &cpb.MarkedSource{
   128  				Kind:          cpb.MarkedSource_PARAMETER_LOOKUP_BY_PARAM,
   129  				PreText:       "(",
   130  				PostChildText: ", ",
   131  				PostText:      ")",
   132  				LookupIndex:   uint32(firstParam),
   133  			})
   134  		}
   135  		if res := sig.Results(); res != nil && res.Len() > 0 {
   136  			rms := &cpb.MarkedSource{Kind: cpb.MarkedSource_TYPE, PreText: " "}
   137  			var hasNamedReturn bool
   138  			for i := 0; i < res.Len(); i++ {
   139  				if v := res.At(i); v.Name() == "" {
   140  					rms.Child = append(rms.Child, &cpb.MarkedSource{
   141  						PreText: typeName(v.Type()),
   142  					})
   143  				} else {
   144  					hasNamedReturn = true
   145  					rms.Child = append(rms.Child, &cpb.MarkedSource{
   146  						PostChildText: " ",
   147  						Child: []*cpb.MarkedSource{{
   148  							Kind:    cpb.MarkedSource_IDENTIFIER,
   149  							PreText: v.Name(),
   150  							Link:    []*cpb.Link{{Definition: []string{kytheuri.ToString(pi.ObjectVName(v))}}},
   151  						}, {
   152  							Kind:    cpb.MarkedSource_TYPE,
   153  							PreText: typeName(v.Type()),
   154  						}},
   155  					})
   156  				}
   157  			}
   158  			if res.Len() > 1 || hasNamedReturn {
   159  				// If there is more than one result type (or the return is named), parenthesize.
   160  				rms.PreText = " ("
   161  				rms.PostText = ")"
   162  				rms.PostChildText = ", "
   163  			}
   164  			fn.Child = append(fn.Child, rms)
   165  		}
   166  		ms = fn
   167  
   168  	case *types.Var:
   169  		// For variables and fields, include the type.
   170  		repl := &cpb.MarkedSource{
   171  			Kind:          cpb.MarkedSource_BOX,
   172  			PostChildText: " ",
   173  			Child: []*cpb.MarkedSource{
   174  				ms,
   175  				{Kind: cpb.MarkedSource_LOOKUP_BY_TYPED},
   176  			},
   177  		}
   178  		ms = repl
   179  
   180  	case *types.TypeName:
   181  		switch tt := t.Type().(type) {
   182  		case *types.Named:
   183  			if pkg := tt.Obj().Pkg(); pkg != nil && pkg.Name() == "builtin" {
   184  				return ms
   185  			}
   186  			ms = &cpb.MarkedSource{
   187  				Kind:          cpb.MarkedSource_BOX,
   188  				PostChildText: " ",
   189  				Child: []*cpb.MarkedSource{
   190  					{
   191  						Kind:    cpb.MarkedSource_MODIFIER,
   192  						PreText: "type",
   193  						ExcludeOnInclude: []cpb.MarkedSource_Kind{
   194  							cpb.MarkedSource_LOOKUP_BY_TYPED,
   195  						},
   196  					},
   197  					ms,
   198  				},
   199  			}
   200  			if tt.TypeParams().Len() > 0 {
   201  				tps := &cpb.MarkedSource{
   202  					Kind:          cpb.MarkedSource_PARAMETER_LOOKUP_BY_TPARAM,
   203  					PreText:       "[",
   204  					PostText:      "]",
   205  					PostChildText: ", ",
   206  					ExcludeOnInclude: []cpb.MarkedSource_Kind{
   207  						cpb.MarkedSource_LOOKUP_BY_TYPED,
   208  					},
   209  				}
   210  				ms.Child = append(ms.Child[:len(ms.Child)-1], &cpb.MarkedSource{
   211  					Child: []*cpb.MarkedSource{ms.GetChild()[len(ms.GetChild())-1], tps},
   212  				})
   213  			}
   214  
   215  		case *types.TypeParam:
   216  			repl := &cpb.MarkedSource{
   217  				Kind:          cpb.MarkedSource_BOX,
   218  				PostChildText: " ",
   219  				Child: []*cpb.MarkedSource{
   220  					ms,
   221  					{
   222  						Kind:    cpb.MarkedSource_TYPE,
   223  						PreText: typeName(tt.Constraint()),
   224  						Link:    []*cpb.Link{{Definition: []string{kytheuri.ToString(e.emitType(tt.Constraint()))}}},
   225  						ExcludeOnInclude: []cpb.MarkedSource_Kind{
   226  							cpb.MarkedSource_LOOKUP_BY_TYPED,
   227  						},
   228  					},
   229  				},
   230  			}
   231  			ms = repl
   232  		}
   233  
   234  	default:
   235  		// TODO(fromberger): Handle other variations from go/types.
   236  	}
   237  	return ms
   238  }
   239  
   240  // objectName returns a human-readable name for obj if one can be inferred.  If
   241  // the object has its own non-blank name, that is used; otherwise if the object
   242  // is of a named type, that type's name is used. Otherwise the result is "_".
   243  func objectName(obj types.Object) string {
   244  	if name := obj.Name(); name != "" {
   245  		return name // the object's given name
   246  	} else if name := typeName(obj.Type()); name != "" {
   247  		return name // the object's type's name
   248  	}
   249  	return "_" // not sure what to call it
   250  }
   251  
   252  // typeName returns a human readable name for typ.
   253  func typeName(typ types.Type) string {
   254  	switch t := typ.(type) {
   255  	case *types.Named:
   256  		return t.Obj().Name() + typeArgs(typ)
   257  	case *types.Basic:
   258  		return t.Name()
   259  	case *types.Struct:
   260  		return "struct {...}"
   261  	case *types.Interface:
   262  		if t.String() == "any" {
   263  			return t.String()
   264  		}
   265  		return "interface {...}"
   266  	case *types.Pointer:
   267  		return "*" + typeName(t.Elem())
   268  	case *types.Chan:
   269  		switch t.Dir() {
   270  		case types.SendOnly:
   271  			return "chan<- " + typeName(t.Elem())
   272  		case types.RecvOnly:
   273  			return "<-chan " + typeName(t.Elem())
   274  		default:
   275  			return "chan " + typeName(t.Elem())
   276  		}
   277  	case *types.Array:
   278  		return fmt.Sprintf("[%d]%s", t.Len(), typeName(t.Elem()))
   279  	case *types.Map:
   280  		return fmt.Sprintf("map[%s]%s", typeName(t.Key()), typeName(t.Elem()))
   281  	case *types.Slice:
   282  		return "[]" + typeName(t.Elem())
   283  	}
   284  	return typ.String()
   285  }
   286  
   287  // typeArgs returns a human readable string for a type's list of type arguments
   288  // (or "" if the type does not have any).
   289  func typeArgs(typ types.Type) string {
   290  	n, ok := deref(typ).(*types.Named)
   291  	if !ok || n.TypeArgs().Len() == 0 {
   292  		return ""
   293  	}
   294  	args := n.TypeArgs()
   295  	var ss []string
   296  	for i := 0; i < args.Len(); i++ {
   297  		ss = append(ss, typeName(args.At(i)))
   298  	}
   299  	return "[" + strings.Join(ss, ", ") + "]"
   300  }
   301  
   302  // typeContext returns the package, type, and function context identifiers that
   303  // qualify the name of obj, if any are applicable. The result is empty if there
   304  // are no appropriate qualifiers.
   305  func (pi *PackageInfo) typeContext(obj types.Object) []*cpb.MarkedSource {
   306  	var ms []*cpb.MarkedSource
   307  	addID := func(s string, v *spb.VName) {
   308  		id := &cpb.MarkedSource{
   309  			Kind:    cpb.MarkedSource_IDENTIFIER,
   310  			PreText: s,
   311  		}
   312  		if v != nil {
   313  			id.Link = []*cpb.Link{{Definition: []string{kytheuri.ToString(v)}}}
   314  		}
   315  		ms = append(ms, id)
   316  	}
   317  	for cur := pi.owner[obj]; cur != nil; cur = pi.owner[cur] {
   318  		if t, ok := cur.(interface {
   319  			Name() string
   320  		}); ok {
   321  			addID(t.Name(), pi.ObjectVName(cur))
   322  		} else {
   323  			addID(typeName(cur.Type()), pi.ObjectVName(cur))
   324  		}
   325  	}
   326  	if pkg := obj.Pkg(); pkg != nil {
   327  		addID(pi.importPath(pkg), pi.PackageVName[pkg])
   328  	}
   329  	for i, j := 0, len(ms)-1; i < j; {
   330  		ms[i], ms[j] = ms[j], ms[i]
   331  		i++
   332  		j--
   333  	}
   334  	return ms
   335  }
   336  
   337  // rewriteMarkedSourceCorpus finds all tickets in the MarkedSource
   338  // and its children and rewrites them to use the given corpus.
   339  func rewriteMarkedSourceCorpus(ms *cpb.MarkedSource, corpus string) {
   340  	for _, link := range ms.Link {
   341  		for i, def := range link.Definition {
   342  			v, err := kytheuri.ToVName(def)
   343  			if err != nil {
   344  				log.Errorf("parsing ticket %q: %v", def, err)
   345  				continue
   346  			}
   347  			v.Corpus = corpus
   348  			link.Definition[i] = kytheuri.ToString(v)
   349  		}
   350  	}
   351  	for _, child := range ms.Child {
   352  		rewriteMarkedSourceCorpus(child, corpus)
   353  	}
   354  }
   355  
   356  // emitCode emits a code fact for the specified marked source message on the
   357  // target, or logs a diagnostic.
   358  func (e *emitter) emitCode(target *spb.VName, ms *cpb.MarkedSource) {
   359  	if ms != nil {
   360  		if e.opts.UseCompilationCorpusForAll {
   361  			rewriteMarkedSourceCorpus(ms, e.pi.VName.Corpus)
   362  		}
   363  		bits, err := proto.Marshal(ms)
   364  		if err != nil {
   365  			log.Errorf("Unable to marshal marked source: %v", err)
   366  			return
   367  		}
   368  		e.writeFact(target, facts.Code, string(bits))
   369  	}
   370  }
   371  
   372  func (e *emitter) emitPackageMarkedSource(pi *PackageInfo) {
   373  	if !e.opts.emitMarkedSource() || len(pi.Files) == 0 {
   374  		return
   375  	}
   376  
   377  	ipath := pi.ImportPath
   378  	ms := &cpb.MarkedSource{
   379  		Child: []*cpb.MarkedSource{{
   380  			Kind:    cpb.MarkedSource_IDENTIFIER,
   381  			PreText: ipath,
   382  		}},
   383  	}
   384  	if p := strings.LastIndex(ipath, "/"); p > 0 {
   385  		ms.Child[0].PreText = ipath[p+1:]
   386  		ms.Child = append([]*cpb.MarkedSource{{
   387  			Kind: cpb.MarkedSource_CONTEXT,
   388  			Child: []*cpb.MarkedSource{{
   389  				Kind:    cpb.MarkedSource_IDENTIFIER,
   390  				PreText: ipath[:p],
   391  			}},
   392  			PostText: "/",
   393  		}}, ms.Child...)
   394  	}
   395  	e.emitCode(pi.VName, ms)
   396  }
   397  
   398  func (e *emitter) emitBuiltinMarkedSource(b *spb.VName) {
   399  	if e.opts.emitMarkedSource() {
   400  		e.emitCode(b, &cpb.MarkedSource{
   401  			Kind:    cpb.MarkedSource_TYPE,
   402  			PreText: strings.TrimSuffix(b.Signature, "#builtin"),
   403  		})
   404  	}
   405  }
   406  
   407  var (
   408  	sliceTAppMS = &cpb.MarkedSource{
   409  		Kind: cpb.MarkedSource_TYPE,
   410  		Child: []*cpb.MarkedSource{{
   411  			Kind:        cpb.MarkedSource_LOOKUP_BY_PARAM,
   412  			LookupIndex: 1,
   413  		}},
   414  		PreText: "[]",
   415  	}
   416  	pointerTAppMS = &cpb.MarkedSource{
   417  		Kind: cpb.MarkedSource_TYPE,
   418  		Child: []*cpb.MarkedSource{{
   419  			Kind:        cpb.MarkedSource_LOOKUP_BY_PARAM,
   420  			LookupIndex: 1,
   421  		}},
   422  		PreText: "*",
   423  	}
   424  	mapTAppMS = &cpb.MarkedSource{
   425  		Kind:    cpb.MarkedSource_TYPE,
   426  		PreText: "map",
   427  		Child: []*cpb.MarkedSource{{
   428  			Kind:     cpb.MarkedSource_BOX,
   429  			PreText:  "[",
   430  			PostText: "]",
   431  			Child: []*cpb.MarkedSource{{
   432  				Kind:        cpb.MarkedSource_LOOKUP_BY_PARAM,
   433  				LookupIndex: 1,
   434  			}},
   435  		}, {
   436  			Kind:        cpb.MarkedSource_LOOKUP_BY_PARAM,
   437  			LookupIndex: 2,
   438  		}},
   439  	}
   440  	tupleTAppMS = &cpb.MarkedSource{
   441  		Kind: cpb.MarkedSource_TYPE,
   442  		Child: []*cpb.MarkedSource{{
   443  			Kind:          cpb.MarkedSource_PARAMETER_LOOKUP_BY_PARAM,
   444  			LookupIndex:   1,
   445  			PostChildText: ", ",
   446  		}},
   447  		PreText:  "(",
   448  		PostText: ")",
   449  	}
   450  	variadicTAppMS = &cpb.MarkedSource{
   451  		Kind: cpb.MarkedSource_TYPE,
   452  		Child: []*cpb.MarkedSource{{
   453  			Kind:        cpb.MarkedSource_LOOKUP_BY_PARAM,
   454  			LookupIndex: 1,
   455  		}},
   456  		PreText: "...",
   457  	}
   458  	chanOmniTAppMS = &cpb.MarkedSource{
   459  		Kind: cpb.MarkedSource_TYPE,
   460  		Child: []*cpb.MarkedSource{{
   461  			Kind:        cpb.MarkedSource_LOOKUP_BY_PARAM,
   462  			LookupIndex: 1,
   463  		}},
   464  		PreText: "chan ",
   465  	}
   466  	chanSendTAppMS = &cpb.MarkedSource{
   467  		Kind: cpb.MarkedSource_TYPE,
   468  		Child: []*cpb.MarkedSource{{
   469  			Kind:        cpb.MarkedSource_LOOKUP_BY_PARAM,
   470  			LookupIndex: 1,
   471  		}},
   472  		PreText: "chan<- ",
   473  	}
   474  	chanRecvTAppMS = &cpb.MarkedSource{
   475  		Kind: cpb.MarkedSource_TYPE,
   476  		Child: []*cpb.MarkedSource{{
   477  			Kind:        cpb.MarkedSource_LOOKUP_BY_PARAM,
   478  			LookupIndex: 1,
   479  		}},
   480  		PreText: "<-chan ",
   481  	}
   482  	genericTAppMS = &cpb.MarkedSource{
   483  		Kind: cpb.MarkedSource_TYPE,
   484  		Child: []*cpb.MarkedSource{{
   485  			Kind:        cpb.MarkedSource_LOOKUP_BY_PARAM,
   486  			LookupIndex: 0,
   487  		}, {
   488  			Kind:     cpb.MarkedSource_BOX,
   489  			PreText:  "[",
   490  			PostText: "]",
   491  			Child: []*cpb.MarkedSource{{
   492  				Kind:        cpb.MarkedSource_PARAMETER_LOOKUP_BY_PARAM,
   493  				LookupIndex: 1,
   494  			}},
   495  			PostChildText: ", ",
   496  		}},
   497  	}
   498  )
   499  
   500  func arrayTAppMS(length int64) *cpb.MarkedSource {
   501  	return &cpb.MarkedSource{
   502  		Kind: cpb.MarkedSource_TYPE,
   503  		Child: []*cpb.MarkedSource{{
   504  			Kind:        cpb.MarkedSource_LOOKUP_BY_PARAM,
   505  			LookupIndex: 1,
   506  		}},
   507  		PreText: fmt.Sprintf("[%d]", length),
   508  	}
   509  }
   510  
   511  func chanTAppMS(dir types.ChanDir) *cpb.MarkedSource {
   512  	switch dir {
   513  	case types.SendOnly:
   514  		return chanSendTAppMS
   515  	case types.RecvOnly:
   516  		return chanRecvTAppMS
   517  	default:
   518  		return chanOmniTAppMS
   519  	}
   520  }