v0.13.0 Latest Latest

This package is not in the latest version of its module.

Go to latest
Published: Apr 23, 2024 License: Apache-2.0 Imports: 23 Imported by: 14



Package linker contains logic and APIs related to linking a protobuf file. The process of linking involves resolving all symbol references to the referenced descriptor. The result of linking is a "rich" descriptor that is more useful than just a descriptor proto since the links allow easy traversal of a protobuf type schema and the relationships between elements.


This package uses an augmentation to protoreflect.FileDescriptor instances in the form of the File interface. There are also factory functions for promoting a FileDescriptor into a linker.File. This new interface provides additional methods for resolving symbols in the file.

This interface is both the result of linking but also an input to the linking process, as all dependencies of a file to be linked must be provided in this form. The actual result of the Link function, a Result, is an even broader interface than File: The linker.Result interface provides even more functions, which are needed for subsequent compilation steps: interpreting options and generating source code info.


This package has a type named Symbols which represents a symbol table. This is usually an internal detail when linking, but callers can provide an instance so that symbols across multiple compile/link operations all have access to the same table. This allows for detection of cases where multiple files try to declare elements with conflicting fully-qualified names or declare extensions for a particular extendable message that have conflicting tag numbers.

The calling code simply uses the same Symbols instance across all compile operations and if any files processed have such conflicts, they can be reported.



This section is empty.


This section is empty.


This section is empty.


type ErrorUnusedImport

type ErrorUnusedImport interface {
	UnusedImport() string

ErrorUnusedImport may be passed to a warning reporter when an unused import is detected. The error the reporter receives will be wrapped with source position that indicates the file and line where the import statement appeared.

type File

type File interface {
	// FindDescriptorByName returns the given named element that is defined in
	// this file. If no such element exists, nil is returned.
	FindDescriptorByName(name protoreflect.FullName) protoreflect.Descriptor
	// FindImportByPath returns the File corresponding to the given import path.
	// If this file does not import the given path, nil is returned.
	FindImportByPath(path string) File
	// FindExtensionByNumber returns the extension descriptor for the given tag
	// that extends the given message name. If no such extension is defined in this
	// file, nil is returned.
	FindExtensionByNumber(message protoreflect.FullName, tag protoreflect.FieldNumber) protoreflect.ExtensionTypeDescriptor

File is like a super-powered protoreflect.FileDescriptor. It includes helpful methods for looking up elements in the descriptor and can be used to create a resolver for the entire transitive closure of the file's dependencies. (See ResolverFromFile.)

func NewFile

func NewFile(f protoreflect.FileDescriptor, deps Files) (File, error)

NewFile converts a protoreflect.FileDescriptor to a File. The given deps must contain all dependencies/imports of f. Also see NewFileRecursive.

func NewFileRecursive

func NewFileRecursive(f protoreflect.FileDescriptor) (File, error)

NewFileRecursive recursively converts a protoreflect.FileDescriptor to a File. If f has any dependencies/imports, they are converted, too, including any and all transitive dependencies.

If f already implements File, it is returned unchanged.

type Files

type Files []File

Files represents a set of protobuf files. It is a slice of File values, but also provides a method for easily looking up files by path and name.

func (Files) AsResolver

func (f Files) AsResolver() Resolver

AsResolver returns a Resolver that uses f as the source of descriptors. If a given query cannot be answered with the files in f, the query will fail with a protoregistry.NotFound error. The implementation just delegates calls to each file until a result is found.

Also see ResolverFromFile.

func (Files) FindFileByPath

func (f Files) FindFileByPath(path string) File

FindFileByPath finds a file in f that has the given path and name. If f contains no such file, nil is returned.

type Resolver

Resolver is an interface that can resolve various kinds of queries about descriptors. It satisfies the resolver interfaces defined in protodesc and protoregistry packages.

func ResolverFromFile

func ResolverFromFile(f File) Resolver

ResolverFromFile returns a Resolver that can resolve any element that is visible to the given file. It will search the given file, its imports, and any transitive public imports.

Note that this function does not compute any additional indexes for efficient search, so queries generally take linear time, O(n) where n is the number of files whose elements are visible to the given file. Queries for an extension by number have runtime complexity that is linear with the number of messages and extensions defined across those files.

type Result

type Result interface {

	// ResolveMessageLiteralExtensionName returns the fully qualified name for
	// an identifier for extension field names in message literals.
	ResolveMessageLiteralExtensionName(ast.IdentValueNode) string
	// ValidateOptions runs some validation checks on the descriptor that can only
	// be done after options are interpreted. Any errors or warnings encountered
	// will be reported via the given handler. If any error is reported, this
	// function returns a non-nil error.
	ValidateOptions(handler *reporter.Handler, symbols *Symbols) error
	// CheckForUnusedImports is used to report warnings for unused imports. This
	// should be called after options have been interpreted. Otherwise, the logic
	// could incorrectly report imports as unused if the only symbol used were a
	// custom option.
	CheckForUnusedImports(handler *reporter.Handler)
	// PopulateSourceCodeInfo is used to populate source code info for the file
	// descriptor. This step requires that the underlying descriptor proto have
	// its `source_code_info` field populated. This is typically a post-process
	// step separate from linking, because computing source code info requires
	// interpreting options (which is done after linking).

	// RemoveAST drops the AST information from this result.

Result is the result of linking. This is a protoreflect.FileDescriptor, but with some additional methods for exposing additional information, such as the for accessing the input AST or file descriptor.

It also provides Resolve* methods, for looking up enums, messages, and extensions that are available to the protobuf source file this result represents. An element is "available" if it meets any of the following criteria:

  1. The element is defined in this file itself.
  2. The element is defined in a file that is directly imported by this file.
  3. The element is "available" to a file that is directly imported by this file as a public import.

Other elements, even if in the transitive closure of this file, are not available and thus won't be returned by these methods.

func Link(parsed parser.Result, dependencies Files, symbols *Symbols, handler *reporter.Handler) (Result, error)

Link handles linking a parsed descriptor proto into a fully-linked descriptor. If the given parser.Result has imports, they must all be present in the given dependencies.

The symbols value is optional and may be nil. If it is not nil, it must be the same instance used to create and link all of the given result's dependencies (or otherwise already have all dependencies imported). Otherwise, linking may fail with spurious errors resolving symbols.

The handler value is used to report any link errors. If any such errors are reported, this function returns a non-nil error. The Result value returned also implements protoreflect.FileDescriptor.

Note that linking does NOT interpret options. So options messages in the returned value have all values stored in UninterpretedOptions fields.

type Symbols

type Symbols struct {
	// contains filtered or unexported fields

Symbols is a symbol table that maps names for all program elements to their location in source. It also tracks extension tag numbers. This can be used to enforce uniqueness for symbol names and tag numbers across many files and many link operations.

This type is thread-safe.

func (*Symbols) AddExtension

func (s *Symbols) AddExtension(pkg, extendee protoreflect.FullName, tag protoreflect.FieldNumber, span ast.SourceSpan, handler *reporter.Handler) error

AddExtension records the given extension, which is used to ensure that no two files attempt to extend the same message using the same tag. The given pkg should be the package that defines extendee.

func (*Symbols) AddExtensionDeclaration added in v0.10.0

func (s *Symbols) AddExtensionDeclaration(extension, extendee protoreflect.FullName, tag protoreflect.FieldNumber, span ast.SourceSpan, handler *reporter.Handler) error

AddExtensionDeclaration records the given extension declaration, which is used to ensure that no two declarations refer to the same extension.

func (*Symbols) Import

func (s *Symbols) Import(fd protoreflect.FileDescriptor, handler *reporter.Handler) error

Import populates the symbol table with all symbols/elements and extension tags present in the given file descriptor. If s is nil or if fd has already been imported into s, this returns immediately without doing anything. If any collisions in symbol names or extension tags are identified, an error will be returned and the symbol table will not be updated.

func (*Symbols) Lookup added in v0.12.0

func (s *Symbols) Lookup(name protoreflect.FullName) ast.SourceSpan

Lookup finds the registered location of the given name. If the given name has not been seen/registered, nil is returned.

func (*Symbols) LookupExtension added in v0.12.0

func (s *Symbols) LookupExtension(messageName protoreflect.FullName, extensionNumber protoreflect.FieldNumber) ast.SourceSpan

LookupExtension finds the registered location of the given extension. If the given extension has not been seen/registered, nil is returned.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL