linker

package
v0.0.0-...-6cf7a4f Latest Latest
Warning

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

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

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

View Source
var (
	ErrFileStillInUse = errors.New("file still in use")
	ErrFileNotFound   = errors.New("file not found")
)

Functions

func IsRecoverable

func IsRecoverable(err error) bool

Types

type ErrorUndeclaredName

type ErrorUndeclaredName interface {
	error
	UndeclaredName() string
	ParentFile() *ast.FileNode
	Hint() string
}

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
	Dependencies() Files

	// 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 is an instance of File, it is returned unchanged.

func NewPlaceholderFile

func NewPlaceholderFile(path string) File

NewPlaceholderFile returns a new placeholder File. Its FileDescriptor is a valid instance of the internal filedesc.PlaceholderFile with the given path.

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 ComputeReflexiveTransitiveClosure

func ComputeReflexiveTransitiveClosure(roots Files) Files

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.

func (Files) RangeFilesByPackage

func (f Files) RangeFilesByPackage(pkg protoreflect.FullName, fn func(File) bool)

RangeFilesByPackage calls the given function for all files in f that have the given package.

func (Files) RangeFilesByPackagePrefix

func (f Files) RangeFilesByPackagePrefix(pkg protoreflect.FullName, fn func(File) bool)

RangeFilesByPackagePrefix calls the given function for all files in f whose package names are equal to or are prefixed by the given package name.

func (Files) RangeFilesByPrefix

func (f Files) RangeFilesByPrefix(prefix string, fn func(File) bool)

FindFilesByPrefix calls the given function for all files in f that have the given prefix.

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 {
	File
	parser.Result

	// 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, lenient bool) 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(sourceinfo.OptionIndex, sourceinfo.OptionDescriptorIndex)

	FindDescriptorsByPrefix(ctx context.Context, prefix string, filter ...func(protoreflect.Descriptor) bool) ([]protoreflect.Descriptor, error)
	RangeDescriptors(ctx context.Context, fn func(protoreflect.Descriptor) bool) error

	FindReferences(to protoreflect.Descriptor) []ast.NodeReference

	FindOptionSourceInfo(*ast.OptionNode) *sourceinfo.OptionSourceInfo
	FindOptionNameFieldDescriptor(name *descriptorpb.UninterpretedOption_NamePart) protoreflect.FieldDescriptor
	FindOptionFieldDescriptor(option *descriptorpb.UninterpretedOption) protoreflect.FieldDescriptor
	FindFieldDescriptorByFieldReferenceNode(node *ast.FieldReferenceNode) protoreflect.FieldDescriptor
	FindFieldDescriptorByMessageFieldNode(node *ast.MessageFieldNode) protoreflect.FieldDescriptor
	RangeFieldReferenceNodesWithDescriptors(func(node ast.Node, desc protoreflect.FieldDescriptor) bool)
	FindMessageDescriptorByTypeReferenceURLNode(node *ast.FieldReferenceNode) protoreflect.MessageDescriptor
	FindExtendeeDescriptorByName(fqn protoreflect.FullName) protoreflect.MessageDescriptor
	FindExtensionsByMessage(fqn protoreflect.FullName) []protoreflect.ExtensionDescriptor

	// 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, in the exact order they are present in the parsed descriptor.

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 SymbolCollisionError

type SymbolCollisionError struct {
	// contains filtered or unexported fields
}

func (*SymbolCollisionError) EntangledFiles

func (u *SymbolCollisionError) EntangledFiles() []string

func (*SymbolCollisionError) EntangledPackages

func (u *SymbolCollisionError) EntangledPackages() []string

func (*SymbolCollisionError) Unwrap

func (u *SymbolCollisionError) Unwrap() error

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 NewSymbolTable

func NewSymbolTable() *Symbols

func (*Symbols) AddExtension

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

func (*Symbols) Clone

func (s *Symbols) Clone() *Symbols

func (*Symbols) Delete

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

Deletes all symbols associated with the given file descriptor.

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) MarshalText

func (s *Symbols) MarshalText() string

Jump to

Keyboard shortcuts

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