linker

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 13, 2022 License: Apache-2.0 Imports: 21 Imported by: 16

Documentation

Overview

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.

Files

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.

Symbols

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.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type ErrorUnusedImport

type ErrorUnusedImport interface {
	error
	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 {
	protoreflect.FileDescriptor
	// 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
	// contains filtered or unexported methods
}

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 all in the file's transitive closure of 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.

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 uses the given file plus its full set of transitive dependencies as the source of descriptors. If a given query cannot be answered with these files, the query will fail with a protoregistry.NotFound error.

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 in the transitive closure of the given file. Queries for an extension by number are linear with the number of messages and extensions defined across all the files.

type Result

type Result interface {
	File
	parser.Result
	// ResolveEnumType returns an enum descriptor for the given named enum that
	// is available in this file. If no such element is available or if the
	// named element is not an enum, nil is returned.
	ResolveEnumType(protoreflect.FullName) protoreflect.EnumDescriptor
	// ResolveMessageType returns a message descriptor for the given named
	// message that is available in this file. If no such element is available
	// or if the named element is not a message, nil is returned.
	ResolveMessageType(protoreflect.FullName) protoreflect.MessageDescriptor
	// ResolveExtension returns an extension descriptor for the given named
	// extension that is available in this file. If no such element is available
	// or if the named element is not an extension, nil is returned.
	ResolveExtension(protoreflect.FullName) protoreflect.ExtensionTypeDescriptor
	// 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) 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).
	PopulateSourceCodeInfo()

	// CanonicalProto returns the file descriptor proto in a form that
	// will be serialized in a canonical way. The "canonical" way matches
	// the way that "protoc" emits option values, which is a way that
	// mostly matches the way options are defined in source, including
	// ordering and de-structuring. Unlike the FileDescriptorProto() method, this
	// method is more expensive and results in a new descriptor proto
	// being constructed with each call.
	//
	// The returned value will have all options (fields of the various
	// descriptorpb.*Options message types) represented via unrecognized
	// fields. So the returned value will serialize as desired, but it
	// is otherwise not useful since all option values are treated as
	// unknown.
	CanonicalProto() *descriptorpb.FileDescriptorProto

	// RemoveAST drops the AST information from this result.
	RemoveAST()
}

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, pos ast.SourcePos, handler *reporter.Handler) error

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.

Jump to

Keyboard shortcuts

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