Repository: kythe/kythe
Pull request #5001 (2021-07-30 17:25:37)

Load inline metadata
Modify the Kythe Golang Indexer to add support for inline base64 encoded GeneratedCodeInfo protos specified in a generated Golang file.

These changes allow generated Go files with inline metadata to have Kythe edges to their source files.

shahms: /gcbrun
shahms: Please run `gofmt` and `buildifier` on the modified files.
shahms: /gcbrun
shahms: This looks good to me, once Cody is satisfied :-)
schroederc: /gcbrun
schroederc: /gcbrun
schroederc: You can setup pre-commit (https://kythe.io/contributing/)
schroederc: /gcbrun
schroederc: /gcbrun

Patch:
diff --git a/kythe/go/indexer/BUILD b/kythe/go/indexer/BUILD
index 32107b539..f8f013032 100644
--- a/kythe/go/indexer/BUILD
+++ b/kythe/go/indexer/BUILD
@@ -22,6 +22,7 @@ go_library(
         "//kythe/proto:analysis_go_proto",
         "//kythe/proto:common_go_proto",
         "//kythe/proto:go_go_proto",
+        "//kythe/proto:metadata_go_proto",
         "//kythe/proto:storage_go_proto",
         "@com_github_golang_protobuf//proto:go_default_library",
         "@org_bitbucket_creachadair_stringset//:go_default_library",
@@ -134,6 +135,12 @@ go_indexer_test(
     srcs = ["testdata/basic/structref.go"],
 )
 
+go_indexer_test(
+    name = "inline_test",
+    srcs = ["testdata/basic/inline.go"],
+    metadata_suffix = ".linkage",
+)
+
 go_indexer_test(
     name = "code_function_test",
     srcs = ["testdata/code/funcdecl.go"],
diff --git a/kythe/go/indexer/indexer.go b/kythe/go/indexer/indexer.go
index 5794f7ace..3e94cbdc0 100644
--- a/kythe/go/indexer/indexer.go
+++ b/kythe/go/indexer/indexer.go
@@ -35,6 +35,7 @@ package indexer // import "kythe.io/kythe/go/indexer"
 
 import (
 	"bytes"
+	"encoding/base64"
 	"errors"
 	"fmt"
 	"go/ast"
@@ -52,6 +53,7 @@ import (
 	"kythe.io/kythe/go/extractors/govname"
 	"kythe.io/kythe/go/util/metadata"
 	"kythe.io/kythe/go/util/ptypes"
+	"kythe.io/kythe/go/util/schema/edges"
 
 	"bitbucket.org/creachadair/stringset"
 	"github.com/golang/protobuf/proto"
@@ -59,6 +61,7 @@ import (
 
 	apb "kythe.io/kythe/proto/analysis_go_proto"
 	gopb "kythe.io/kythe/proto/go_go_proto"
+	mpb "kythe.io/kythe/proto/metadata_go_proto"
 	spb "kythe.io/kythe/proto/storage_go_proto"
 )
 
@@ -301,6 +304,23 @@ func Resolve(unit *apb.CompilationUnit, f Fetcher, opts *ResolveOptions) (*Packa
 			filev[parsed] = vname
 			srcs[parsed] = string(data)
 			smap[fpath] = parsed
+
+			// If the file has inlined encoded metadata, add to rules.
+			var lastComment string
+			if len(parsed.Comments) > 0 {
+				lastComment = parsed.Comments[len(parsed.Comments)-1].Text()
+			}
+			const delimiter = "gokythe-inline-metadata:"
+
+			if strings.HasPrefix(lastComment, delimiter) {
+				encodedMetadata := strings.TrimPrefix(lastComment, delimiter)
+				newRule, err := loadInlineMetadata(ri, encodedMetadata)
+				if err != nil {
+					log.Printf("Error loading metadata in %q: %v", ri.Info.GetPath(), err)
+				} else {
+					rules = append(rules, newRule)
+				}
+			}
 			continue
 		}
 
@@ -839,6 +859,33 @@ func goPackageInfo(details []*ptypes.Any) *gopb.GoPackageInfo {
 	return nil
 }
 
+// loadInlineMetadata unmarshals the encoded metadata string and returns
+// the corresponding metadata rules which link source definitions
+// to the file fi. An error is returned if unmarshalling fails.
+func loadInlineMetadata(fi *apb.CompilationUnit_FileInput, encodedMetadata string) (*Ruleset, error) {
+	var gci mpb.GeneratedCodeInfo
+	err := unmarshalProtoBase64(encodedMetadata, &gci)
+	if err != nil {
+		return nil, err
+	}
+
+	rules := make(metadata.Rules, 0, len(gci.GetMeta()))
+	for _, r := range gci.GetMeta() {
+		rules = append(rules, metadata.Rule{
+			EdgeIn:  edges.DefinesBinding,
+			EdgeOut: edges.Generates,
+			VName:   r.GetVname(),
+			Reverse: true,
+			Begin:   int(r.GetBegin()),
+			End:     int(r.GetEnd()),
+		})
+	}
+	return &Ruleset{
+		Path:  fi.Info.GetPath(),
+		Rules: rules,
+	}, nil
+}
+
 // matchesBuildTags reports whether the file at fpath, whose content is in
 // data, would be matched by the settings in bc.
 func matchesBuildTags(fpath string, data []byte, bc *build.Context) bool {
@@ -853,6 +900,13 @@ func matchesBuildTags(fpath string, data []byte, bc *build.Context) bool {
 	return err == nil && match
 }
 
+// unmarshalProtoBase64 parses a base64 encoded proto string into the supplied
+// message, mutating msg.
+func unmarshalProtoBase64(base64Str string, msg proto.Message) error {
+	b, _ := base64.StdEncoding.DecodeString(base64Str)
+	return proto.Unmarshal(b, msg)
+}
+
 // AllTypeInfo creates a new types.Info value with empty maps for each of the
 // fields that can be filled in by the type-checker.
 func AllTypeInfo() *types.Info {
diff --git a/kythe/proto/BUILD b/kythe/proto/BUILD
index 22bcadb89..3adb0b62b 100644
--- a/kythe/proto/BUILD
+++ b/kythe/proto/BUILD
@@ -681,6 +681,11 @@ proto_library(
     deps = [":storage_proto"],
 )
 
+go_kythe_proto(
+    proto = ":metadata_proto",
+    deps = [":storage_go_proto"],
+)
+
 cc_proto_library(
     name = "metadata_cc_proto",
     deps = [":metadata_proto"],
diff --git a/kythe/proto/metadata_go_proto/metadata.pb.go b/kythe/proto/metadata_go_proto/metadata.pb.go
new file mode 100644
index 000000000..bc6d87b4c
--- /dev/null
+++ b/kythe/proto/metadata_go_proto/metadata.pb.go
@@ -0,0 +1,434 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.25.0
+// 	protoc        v3.15.3
+// source: kythe/proto/metadata.proto
+
+package kythe_proto_metadata
+
+import (
+	proto "github.com/golang/protobuf/proto"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	storage_go_proto "kythe.io/kythe/proto/storage_go_proto"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// This is a compile-time assertion that a sufficiently up-to-date version
+// of the legacy proto package is being used.
+const _ = proto.ProtoPackageIsVersion4
+
+type GeneratedCodeInfo_Type int32
+
+const (
+	GeneratedCodeInfo_NONE   GeneratedCodeInfo_Type = 0
+	GeneratedCodeInfo_KYTHE0 GeneratedCodeInfo_Type = 1
+)
+
+// Enum value maps for GeneratedCodeInfo_Type.
+var (
+	GeneratedCodeInfo_Type_name = map[int32]string{
+		0: "NONE",
+		1: "KYTHE0",
+	}
+	GeneratedCodeInfo_Type_value = map[string]int32{
+		"NONE":   0,
+		"KYTHE0": 1,
+	}
+)
+
+func (x GeneratedCodeInfo_Type) Enum() *GeneratedCodeInfo_Type {
+	p := new(GeneratedCodeInfo_Type)
+	*p = x
+	return p
+}
+
+func (x GeneratedCodeInfo_Type) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (GeneratedCodeInfo_Type) Descriptor() protoreflect.EnumDescriptor {
+	return file_kythe_proto_metadata_proto_enumTypes[0].Descriptor()
+}
+
+func (GeneratedCodeInfo_Type) Type() protoreflect.EnumType {
+	return &file_kythe_proto_metadata_proto_enumTypes[0]
+}
+
+func (x GeneratedCodeInfo_Type) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use GeneratedCodeInfo_Type.Descriptor instead.
+func (GeneratedCodeInfo_Type) EnumDescriptor() ([]byte, []int) {
+	return file_kythe_proto_metadata_proto_rawDescGZIP(), []int{0, 0}
+}
+
+type MappingRule_Type int32
+
+const (
+	MappingRule_NONE           MappingRule_Type = 0
+	MappingRule_NOP            MappingRule_Type = 1
+	MappingRule_ANCHOR_DEFINES MappingRule_Type = 2
+	MappingRule_ANCHOR_ANCHOR  MappingRule_Type = 3
+)
+
+// Enum value maps for MappingRule_Type.
+var (
+	MappingRule_Type_name = map[int32]string{
+		0: "NONE",
+		1: "NOP",
+		2: "ANCHOR_DEFINES",
+		3: "ANCHOR_ANCHOR",
+	}
+	MappingRule_Type_value = map[string]int32{
+		"NONE":           0,
+		"NOP":            1,
+		"ANCHOR_DEFINES": 2,
+		"ANCHOR_ANCHOR":  3,
+	}
+)
+
+func (x MappingRule_Type) Enum() *MappingRule_Type {
+	p := new(MappingRule_Type)
+	*p = x
+	return p
+}
+
+func (x MappingRule_Type) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (MappingRule_Type) Descriptor() protoreflect.EnumDescriptor {
+	return file_kythe_proto_metadata_proto_enumTypes[1].Descriptor()
+}
+
+func (MappingRule_Type) Type() protoreflect.EnumType {
+	return &file_kythe_proto_metadata_proto_enumTypes[1]
+}
+
+func (x MappingRule_Type) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use MappingRule_Type.Descriptor instead.
+func (MappingRule_Type) EnumDescriptor() ([]byte, []int) {
+	return file_kythe_proto_metadata_proto_rawDescGZIP(), []int{1, 0}
+}
+
+type GeneratedCodeInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Type GeneratedCodeInfo_Type `protobuf:"varint,1,opt,name=type,proto3,enum=kythe.proto.metadata.GeneratedCodeInfo_Type" json:"type,omitempty"`
+	Meta []*MappingRule         `protobuf:"bytes,2,rep,name=meta,proto3" json:"meta,omitempty"`
+}
+
+func (x *GeneratedCodeInfo) Reset() {
+	*x = GeneratedCodeInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_kythe_proto_metadata_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GeneratedCodeInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GeneratedCodeInfo) ProtoMessage() {}
+
+func (x *GeneratedCodeInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_kythe_proto_metadata_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GeneratedCodeInfo.ProtoReflect.Descriptor instead.
+func (*GeneratedCodeInfo) Descriptor() ([]byte, []int) {
+	return file_kythe_proto_metadata_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *GeneratedCodeInfo) GetType() GeneratedCodeInfo_Type {
+	if x != nil {
+		return x.Type
+	}
+	return GeneratedCodeInfo_NONE
+}
+
+func (x *GeneratedCodeInfo) GetMeta() []*MappingRule {
+	if x != nil {
+		return x.Meta
+	}
+	return nil
+}
+
+type MappingRule struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Type        MappingRule_Type        `protobuf:"varint,1,opt,name=type,proto3,enum=kythe.proto.metadata.MappingRule_Type" json:"type,omitempty"`
+	Edge        string                  `protobuf:"bytes,2,opt,name=edge,proto3" json:"edge,omitempty"`
+	Vname       *storage_go_proto.VName `protobuf:"bytes,3,opt,name=vname,proto3" json:"vname,omitempty"`
+	Begin       uint32                  `protobuf:"varint,4,opt,name=begin,proto3" json:"begin,omitempty"`
+	End         uint32                  `protobuf:"varint,5,opt,name=end,proto3" json:"end,omitempty"`
+	SourceVname *storage_go_proto.VName `protobuf:"bytes,6,opt,name=source_vname,json=sourceVname,proto3" json:"source_vname,omitempty"`
+	SourceBegin uint32                  `protobuf:"varint,7,opt,name=source_begin,json=sourceBegin,proto3" json:"source_begin,omitempty"`
+	SourceEnd   uint32                  `protobuf:"varint,8,opt,name=source_end,json=sourceEnd,proto3" json:"source_end,omitempty"`
+	TargetBegin uint32                  `protobuf:"varint,9,opt,name=target_begin,json=targetBegin,proto3" json:"target_begin,omitempty"`
+	TargetEnd   uint32                  `protobuf:"varint,10,opt,name=target_end,json=targetEnd,proto3" json:"target_end,omitempty"`
+}
+
+func (x *MappingRule) Reset() {
+	*x = MappingRule{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_kythe_proto_metadata_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MappingRule) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MappingRule) ProtoMessage() {}
+
+func (x *MappingRule) ProtoReflect() protoreflect.Message {
+	mi := &file_kythe_proto_metadata_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MappingRule.ProtoReflect.Descriptor instead.
+func (*MappingRule) Descriptor() ([]byte, []int) {
+	return file_kythe_proto_metadata_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *MappingRule) GetType() MappingRule_Type {
+	if x != nil {
+		return x.Type
+	}
+	return MappingRule_NONE
+}
+
+func (x *MappingRule) GetEdge() string {
+	if x != nil {
+		return x.Edge
+	}
+	return ""
+}
+
+func (x *MappingRule) GetVname() *storage_go_proto.VName {
+	if x != nil {
+		return x.Vname
+	}
+	return nil
+}
+
+func (x *MappingRule) GetBegin() uint32 {
+	if x != nil {
+		return x.Begin
+	}
+	return 0
+}
+
+func (x *MappingRule) GetEnd() uint32 {
+	if x != nil {
+		return x.End
+	}
+	return 0
+}
+
+func (x *MappingRule) GetSourceVname() *storage_go_proto.VName {
+	if x != nil {
+		return x.SourceVname
+	}
+	return nil
+}
+
+func (x *MappingRule) GetSourceBegin() uint32 {
+	if x != nil {
+		return x.SourceBegin
+	}
+	return 0
+}
+
+func (x *MappingRule) GetSourceEnd() uint32 {
+	if x != nil {
+		return x.SourceEnd
+	}
+	return 0
+}
+
+func (x *MappingRule) GetTargetBegin() uint32 {
+	if x != nil {
+		return x.TargetBegin
+	}
+	return 0
+}
+
+func (x *MappingRule) GetTargetEnd() uint32 {
+	if x != nil {
+		return x.TargetEnd
+	}
+	return 0
+}
+
+var File_kythe_proto_metadata_proto protoreflect.FileDescriptor
+
+var file_kythe_proto_metadata_proto_rawDesc = []byte{
+	0x0a, 0x1a, 0x6b, 0x79, 0x74, 0x68, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x65,
+	0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14, 0x6b, 0x79,
+	0x74, 0x68, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
+	0x74, 0x61, 0x1a, 0x19, 0x6b, 0x79, 0x74, 0x68, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
+	0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xaa, 0x01,
+	0x0a, 0x11, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x49,
+	0x6e, 0x66, 0x6f, 0x12, 0x40, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0e, 0x32, 0x2c, 0x2e, 0x6b, 0x79, 0x74, 0x68, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
+	0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74,
+	0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52,
+	0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6b, 0x79, 0x74, 0x68, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x69,
+	0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0x1c, 0x0a, 0x04,
+	0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0a,
+	0x0a, 0x06, 0x4b, 0x59, 0x54, 0x48, 0x45, 0x30, 0x10, 0x01, 0x22, 0xac, 0x03, 0x0a, 0x0b, 0x4d,
+	0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x3a, 0x0a, 0x04, 0x74, 0x79,
+	0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x6b, 0x79, 0x74, 0x68, 0x65,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e,
+	0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65,
+	0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x64, 0x67, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x65, 0x64, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x76, 0x6e,
+	0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6b, 0x79, 0x74, 0x68,
+	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x05, 0x76,
+	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x04, 0x20,
+	0x01, 0x28, 0x0d, 0x52, 0x05, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e,
+	0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x35, 0x0a, 0x0c,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6b, 0x79, 0x74, 0x68, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x2e, 0x56, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x56, 0x6e,
+	0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x62, 0x65,
+	0x67, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63,
+	0x65, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+	0x5f, 0x65, 0x6e, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x6f, 0x75, 0x72,
+	0x63, 0x65, 0x45, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f,
+	0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x74, 0x61, 0x72,
+	0x67, 0x65, 0x74, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x72, 0x67,
+	0x65, 0x74, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x74, 0x61,
+	0x72, 0x67, 0x65, 0x74, 0x45, 0x6e, 0x64, 0x22, 0x40, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
+	0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4e, 0x4f, 0x50,
+	0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x5f, 0x44, 0x45, 0x46,
+	0x49, 0x4e, 0x45, 0x53, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52,
+	0x5f, 0x41, 0x4e, 0x43, 0x48, 0x4f, 0x52, 0x10, 0x03, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x33,
+}
+
+var (
+	file_kythe_proto_metadata_proto_rawDescOnce sync.Once
+	file_kythe_proto_metadata_proto_rawDescData = file_kythe_proto_metadata_proto_rawDesc
+)
+
+func file_kythe_proto_metadata_proto_rawDescGZIP() []byte {
+	file_kythe_proto_metadata_proto_rawDescOnce.Do(func() {
+		file_kythe_proto_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_kythe_proto_metadata_proto_rawDescData)
+	})
+	return file_kythe_proto_metadata_proto_rawDescData
+}
+
+var file_kythe_proto_metadata_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
+var file_kythe_proto_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_kythe_proto_metadata_proto_goTypes = []interface{}{
+	(GeneratedCodeInfo_Type)(0),    // 0: kythe.proto.metadata.GeneratedCodeInfo.Type
+	(MappingRule_Type)(0),          // 1: kythe.proto.metadata.MappingRule.Type
+	(*GeneratedCodeInfo)(nil),      // 2: kythe.proto.metadata.GeneratedCodeInfo
+	(*MappingRule)(nil),            // 3: kythe.proto.metadata.MappingRule
+	(*storage_go_proto.VName)(nil), // 4: kythe.proto.VName
+}
+var file_kythe_proto_metadata_proto_depIdxs = []int32{
+	0, // 0: kythe.proto.metadata.GeneratedCodeInfo.type:type_name -> kythe.proto.metadata.GeneratedCodeInfo.Type
+	3, // 1: kythe.proto.metadata.GeneratedCodeInfo.meta:type_name -> kythe.proto.metadata.MappingRule
+	1, // 2: kythe.proto.metadata.MappingRule.type:type_name -> kythe.proto.metadata.MappingRule.Type
+	4, // 3: kythe.proto.metadata.MappingRule.vname:type_name -> kythe.proto.VName
+	4, // 4: kythe.proto.metadata.MappingRule.source_vname:type_name -> kythe.proto.VName
+	5, // [5:5] is the sub-list for method output_type
+	5, // [5:5] is the sub-list for method input_type
+	5, // [5:5] is the sub-list for extension type_name
+	5, // [5:5] is the sub-list for extension extendee
+	0, // [0:5] is the sub-list for field type_name
+}
+
+func init() { file_kythe_proto_metadata_proto_init() }
+func file_kythe_proto_metadata_proto_init() {
+	if File_kythe_proto_metadata_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_kythe_proto_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GeneratedCodeInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_kythe_proto_metadata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MappingRule); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_kythe_proto_metadata_proto_rawDesc,
+			NumEnums:      2,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_kythe_proto_metadata_proto_goTypes,
+		DependencyIndexes: file_kythe_proto_metadata_proto_depIdxs,
+		EnumInfos:         file_kythe_proto_metadata_proto_enumTypes,
+		MessageInfos:      file_kythe_proto_metadata_proto_msgTypes,
+	}.Build()
+	File_kythe_proto_metadata_proto = out.File
+	file_kythe_proto_metadata_proto_rawDesc = nil
+	file_kythe_proto_metadata_proto_goTypes = nil
+	file_kythe_proto_metadata_proto_depIdxs = nil
+}


Test patch:
diff --git a/kythe/go/indexer/indexer_test.go b/kythe/go/indexer/indexer_test.go
index 918dcc893..90516185a 100644
--- a/kythe/go/indexer/indexer_test.go
+++ b/kythe/go/indexer/indexer_test.go
@@ -30,6 +30,7 @@ import (
 	"kythe.io/kythe/go/test/testutil"
 	"kythe.io/kythe/go/util/metadata"
 	"kythe.io/kythe/go/util/ptypes"
+	"kythe.io/kythe/go/util/schema/edges"
 
 	"github.com/golang/protobuf/proto"
 
@@ -171,6 +172,57 @@ func init() { println(foo.Foo()) }
 	}
 }
 
+func TestResolveInlineMetadata(t *testing.T) {
+	const subject = "package subject\n\nvar Primary = struct {\n\tMyParam bool\n}{\n\tMyParam: true,\n}\n\n//gokythe-inline-metadata:ElISFS9reXRoZS9lZGdlL2dlbmVyYXRlcxo1ChJJREVOVElGSUVSOlByaW1hcnkSB2RlZmF1bHQiEHRlc3QvZXhhbXBsZS50eHQqBGxhbmcgFSgcElsSFS9reXRoZS9lZGdlL2dlbmVyYXRlcxo+ChtJREVOVElGSUVSOlByaW1hcnkubXlfcGFyYW0SB2RlZmF1bHQiEHRlc3QvZXhhbXBsZS50eHQqBGxhbmcgKSgw"
+	unit, digest := oneFileCompilation("testdata/subject.go", "subject", subject)
+	fetcher := memFetcher{
+		digest: subject,
+	}
+
+	featureRule := metadata.Rule{
+		EdgeIn:  edges.DefinesBinding,
+		EdgeOut: edges.Generates,
+		VName: &spb.VName{
+			Corpus:    "default",
+			Language:  "lang",
+			Signature: "IDENTIFIER:Primary",
+			Path:      "test/example.txt",
+		},
+		Reverse: true,
+		Begin:   21,
+		End:     28,
+	}
+
+	flagRule := metadata.Rule{
+		EdgeIn:  edges.DefinesBinding,
+		EdgeOut: edges.Generates,
+		VName: &spb.VName{
+			Corpus:    "default",
+			Language:  "lang",
+			Signature: "IDENTIFIER:Primary.my_param",
+			Path:      "test/example.txt",
+		},
+		Reverse: true,
+		Begin:   41,
+		End:     48,
+	}
+	wantRules := metadata.Rules{featureRule, flagRule}
+
+	pi, err := Resolve(unit, fetcher, nil)
+	if err != nil {
+		t.Fatalf("Resolve failed: %v\nInput unit:\n%s", err, proto.MarshalTextString(unit))
+	}
+
+	gotRules := pi.Rules[pi.Files[0]]
+
+	if len(pi.Rules) != 1 {
+		t.Errorf("Resolve failed to load package rules in %v", unit.SourceFile)
+	}
+	if err := testutil.DeepEqual(wantRules, gotRules); err != nil {
+		t.Errorf("Rules diff %s", err)
+	}
+}
+
 func TestResolveErrors(t *testing.T) {
 	unit, _ := oneFileCompilation("blah.a", "bogus", "package blah")
 	unit.SourceFile = nil
diff --git a/kythe/go/indexer/testdata/basic/inline.go b/kythe/go/indexer/testdata/basic/inline.go
new file mode 100644
index 000000000..125aae65d
--- /dev/null
+++ b/kythe/go/indexer/testdata/basic/inline.go
@@ -0,0 +1,24 @@
+package subject
+
+var Primary = struct {
+	MyParam bool
+}{
+	MyParam: true,
+}
+
+//- EA.node/kind anchor
+//- EA.loc/start 21
+//- EA.loc/end 28
+//- EA defines/binding Primary
+//- vname("", default, "", "test/example.txt", "") generates vname("", kythe, "", "go/indexer/inline_test/inline.go", "")
+//- _Alt=vname("IDENTIFIER:Primary", default, "", "test/example.txt", lang) generates Primary
+//- Primary.node/kind variable
+
+//- EB.node/kind anchor
+//- EB.loc/start 41
+//- EB.loc/end 48
+//- EB defines/binding MyParam
+//- _Alt2=vname("IDENTIFIER:Primary.my_param", default, "", "test/example.txt", lang) generates MyParam
+//- MyParam.node/kind variable
+
+//gokythe-inline-metadata:ElISFS9reXRoZS9lZGdlL2dlbmVyYXRlcxo1ChJJREVOVElGSUVSOlByaW1hcnkSB2RlZmF1bHQiEHRlc3QvZXhhbXBsZS50eHQqBGxhbmcgFSgcElsSFS9reXRoZS9lZGdlL2dlbmVyYXRlcxo+ChtJREVOVElGSUVSOlByaW1hcnkubXlfcGFyYW0SB2RlZmF1bHQiEHRlc3QvZXhhbXBsZS50eHQqBGxhbmcgKSgw