or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

compiler-protogen.mdcore.mdencoding.mdindex.mdreflection.mdruntime.mdtesting.mdtypes-descriptorpb.mdtypes-dynamicpb.mdtypes-known.mdtypes-pluginpb.md
tile.json

reflection.mddocs/

Message Reflection

The reflection packages provide powerful runtime introspection and manipulation capabilities for protocol buffer messages. These packages enable working with messages dynamically without compile-time knowledge of their types, building descriptor hierarchies, traversing message structures, and maintaining type registries.

Package Imports

import (
    "google.golang.org/protobuf/reflect/protoreflect"
    "google.golang.org/protobuf/reflect/protodesc"
    "google.golang.org/protobuf/reflect/protopath"
    "google.golang.org/protobuf/reflect/protorange"
    "google.golang.org/protobuf/reflect/protoregistry"
    "google.golang.org/protobuf/types/descriptorpb"
)

Descriptor Construction

FileDescriptor Creation

Create file descriptors from FileDescriptorProto messages with dependency resolution.

// NewFile creates a new protoreflect.FileDescriptor from the provided file
// descriptor message. See FileOptions.New for more information.
func NewFile(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error)

// NewFiles creates a new protoregistry.Files from the provided
// FileDescriptorSet message. See FileOptions.NewFiles for more information.
func NewFiles(fd *descriptorpb.FileDescriptorSet) (*protoregistry.Files, error)

FileOptions Configuration

Configure file descriptor construction with control over dependency resolution.

// FileOptions configures the construction of file descriptors
type FileOptions struct {
    pragma.NoUnkeyedLiterals

    // AllowUnresolvable configures New to permissively allow unresolvable
    // file, enum, or message dependencies. Unresolved dependencies are replaced
    // by placeholder equivalents.
    //
    // The following dependencies may be left unresolved:
    //  • Resolving an imported file.
    //  • Resolving the type for a message field or extension field.
    //    If the kind of the field is unknown, then a placeholder is used for both
    //    the Enum and Message accessors on the protoreflect.FieldDescriptor.
    //  • Resolving an enum value set as the default for an optional enum field.
    //    If unresolvable, the protoreflect.FieldDescriptor.Default is set to the
    //    first value in the associated enum (or zero if the also enum dependency
    //    is also unresolvable). The protoreflect.FieldDescriptor.DefaultEnumValue
    //    is populated with a placeholder.
    //  • Resolving the extended message type for an extension field.
    //  • Resolving the input or output message type for a service method.
    //
    // If the unresolved dependency uses a relative name,
    // then the placeholder will contain an invalid FullName with a "*." prefix,
    // indicating that the starting prefix of the full name is unknown.
    AllowUnresolvable bool
}

// New creates a new protoreflect.FileDescriptor from the provided file
// descriptor message. The file must represent a valid proto file according to
// protobuf semantics. The returned descriptor is a deep copy of the input.
//
// Any imported files, enum types, or message types referenced in the file are
// resolved using the provided registry. When looking up an import file path,
// the path must be unique. The newly created file descriptor is not registered
// back into the provided file registry.
func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (protoreflect.FileDescriptor, error)

// NewFiles creates a new protoregistry.Files from the provided
// FileDescriptorSet message. The descriptor set must include only valid files
// according to protobuf semantics. The returned descriptors are a deep copy of
// the input.
func (o FileOptions) NewFiles(fds *descriptorpb.FileDescriptorSet) (*protoregistry.Files, error)

Descriptor Resolution

Interface for resolving dependencies during descriptor construction.

// Resolver is the resolver used by NewFile to resolve dependencies
// The enums and messages provided must belong to some parent file, which is also
// registered.
//
// It is implemented by protoregistry.Files.
type Resolver interface {
    FindFileByPath(string) (protoreflect.FileDescriptor, error)
    FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error)
}

Descriptor to Proto Conversion

Convert reflection descriptors back to proto message representations.

// ToFileDescriptorProto copies a protoreflect.FileDescriptor into a
// google.protobuf.FileDescriptorProto message
func ToFileDescriptorProto(file protoreflect.FileDescriptor) *descriptorpb.FileDescriptorProto

// ToDescriptorProto copies a protoreflect.MessageDescriptor into a
// google.protobuf.DescriptorProto message
func ToDescriptorProto(message protoreflect.MessageDescriptor) *descriptorpb.DescriptorProto

// ToFieldDescriptorProto copies a protoreflect.FieldDescriptor into a
// google.protobuf.FieldDescriptorProto message
func ToFieldDescriptorProto(field protoreflect.FieldDescriptor) *descriptorpb.FieldDescriptorProto

// ToOneofDescriptorProto copies a protoreflect.OneofDescriptor into a
// google.protobuf.OneofDescriptorProto message
func ToOneofDescriptorProto(oneof protoreflect.OneofDescriptor) *descriptorpb.OneofDescriptorProto

// ToEnumDescriptorProto copies a protoreflect.EnumDescriptor into a
// google.protobuf.EnumDescriptorProto message
func ToEnumDescriptorProto(enum protoreflect.EnumDescriptor) *descriptorpb.EnumDescriptorProto

// ToEnumValueDescriptorProto copies a protoreflect.EnumValueDescriptor into a
// google.protobuf.EnumValueDescriptorProto message
func ToEnumValueDescriptorProto(value protoreflect.EnumValueDescriptor) *descriptorpb.EnumValueDescriptorProto

// ToServiceDescriptorProto copies a protoreflect.ServiceDescriptor into a
// google.protobuf.ServiceDescriptorProto message
func ToServiceDescriptorProto(service protoreflect.ServiceDescriptor) *descriptorpb.ServiceDescriptorProto

// ToMethodDescriptorProto copies a protoreflect.MethodDescriptor into a
// google.protobuf.MethodDescriptorProto message
func ToMethodDescriptorProto(method protoreflect.MethodDescriptor) *descriptorpb.MethodDescriptorProto

Message Paths

Path Representation

Represent sequences of reflection operations as structured paths through message hierarchies.

// Path is a sequence of protobuf reflection steps applied to some root
// protobuf message value to arrive at the current value
// The first step must be a Root step
type Path []Step

// Index returns the ith step in the path and supports negative indexing
// A negative index starts counting from the tail of the Path such that -1
// refers to the last step, -2 refers to the second-to-last step, and so on
// It returns a zero Step value if the index is out-of-bounds
func (p Path) Index(i int) Step

// String returns a structured representation of the path by concatenating the
// string representation of every path step
func (p Path) String() string

Path Steps

Define individual steps in a path through a message structure.

// Step is a union where only one step operation may be specified at a time
// The different kinds of steps are specified by the constants defined for the
// StepKind type
type Step struct {
    // Has unexported fields.
}

// Root indicates the root message that a path is relative to
// It should always (and only ever) be the first step in a path
func Root(md protoreflect.MessageDescriptor) Step

// FieldAccess describes access of a field within a message
// Extension field accesses are also represented using a FieldAccess and must be
// provided with a protoreflect.FieldDescriptor
func FieldAccess(fd protoreflect.FieldDescriptor) Step

// UnknownAccess describes access to the unknown fields within a message
func UnknownAccess() Step

// ListIndex describes index of an element within a list
func ListIndex(i int) Step

// MapIndex describes index of an entry within a map
// The key type is determined by field descriptor that the map belongs to
func MapIndex(k protoreflect.MapKey) Step

// AnyExpand describes expansion of a google.protobuf.Any message into a
// structured representation of the underlying message
func AnyExpand(md protoreflect.MessageDescriptor) Step

// Kind reports which kind of step this is
func (s Step) Kind() StepKind

// MessageDescriptor returns the message descriptor for Root or AnyExpand
// steps, otherwise it returns nil
func (s Step) MessageDescriptor() protoreflect.MessageDescriptor

// FieldDescriptor returns the field descriptor for FieldAccess steps,
// otherwise it returns nil
func (s Step) FieldDescriptor() protoreflect.FieldDescriptor

// ListIndex returns the list index for ListIndex steps, otherwise it returns 0
func (s Step) ListIndex() int

// MapIndex returns the map key for MapIndex steps, otherwise it returns an
// invalid map key
func (s Step) MapIndex() protoreflect.MapKey

// String returns a string representation of the step
func (s Step) String() string

Step Kinds

// StepKind identifies the kind of step operation
// Each kind of step corresponds with some protobuf reflection operation
type StepKind int

const (
    // RootStep identifies a step as the Root step operation
    RootStep StepKind
    // FieldAccessStep identifies a step as the FieldAccess step operation
    FieldAccessStep
    // UnknownAccessStep identifies a step as the UnknownAccess step operation
    UnknownAccessStep
    // ListIndexStep identifies a step as the ListIndex step operation
    ListIndexStep
    // MapIndexStep identifies a step as the MapIndex step operation
    MapIndexStep
    // AnyExpandStep identifies a step as the AnyExpand step operation
    AnyExpandStep
)

func (k StepKind) String() string

Path with Values

Pair paths with the values at each step for complete traversal state.

// Values is a Path paired with a sequence of values at each step
// The lengths of Values.Path and Values.Values must be identical
// The first step must be a Root step and the first value must be a concrete message value
type Values struct {
    Path   Path
    Values []protoreflect.Value
}

// Len reports the length of the path and values
// If the path and values have differing length, it returns the minimum length
func (p Values) Len() int

// Index returns the ith step and value and supports negative indexing
// A negative index starts counting from the tail of the Values such that -1
// refers to the last pair, -2 refers to the second-to-last pair, and so on
func (p Values) Index(i int) (out struct {
    Step  Step
    Value protoreflect.Value
})

// String returns a humanly readable representation of the path and last value
// Do not depend on the output being stable
func (p Values) String() string

Message Traversal

Depth-First Traversal

Perform depth-first traversal over message structures with callback-based navigation.

// Range performs a depth-first traversal over reachable values in a message
// See Options.Range for details
func Range(m protoreflect.Message, f func(protopath.Values) error) error

Traversal Control

Constants for controlling traversal behavior.

var (
    // Break breaks traversal of children in the current value
    // It has no effect when traversing values that are not composite types
    // (e.g., messages, lists, and maps)
    Break = errors.New("break traversal of children in current value")

    // Terminate terminates the entire range operation
    // All necessary Pop operations continue to be called
    Terminate = errors.New("terminate range operation")
)

Traversal Options

Configure message traversal behavior including ordering and type resolution.

// Options configures traversal of a message value tree
type Options struct {
    // Stable specifies whether to visit message fields and map entries
    // in a stable ordering. If false, then the ordering is undefined and
    // may be non-deterministic.
    //
    // Message fields are visited in ascending order by field number.
    // Map entries are visited in ascending order, where
    // boolean keys are ordered such that false sorts before true,
    // numeric keys are ordered based on the numeric value, and
    // string keys are lexicographically ordered by Unicode codepoints.
    Stable bool

    // Resolver is used for looking up types when expanding google.protobuf.Any
    // messages. If nil, this defaults to using protoregistry.GlobalTypes.
    // To prevent expansion of Any messages, pass an empty protoregistry.Types:
    //
    //  Options{Resolver: (*protoregistry.Types)(nil)}
    //
    Resolver interface {
        protoregistry.ExtensionTypeResolver
        protoregistry.MessageTypeResolver
    }
}

// Range performs a depth-first traversal over reachable values in a message
// The first push and the last pop are to push/pop a protopath.Root step
// If push or pop return any non-nil error (other than Break or Terminate),
// it terminates the traversal and is returned by Range
//
// The rules for traversing a message is as follows:
//
//   - For messages, iterate over every populated known and extension field.
//     Each field is preceded by a push of a protopath.FieldAccess step,
//     followed by recursive application of the rules on the field value,
//     and succeeded by a pop of that step. If the message has unknown fields,
//     then push an protopath.UnknownAccess step followed immediately by pop of
//     that step.
//
//   - As an exception to the above rule, if the current message is a
//     google.protobuf.Any message, expand the underlying message (if
//     resolvable). The expanded message is preceded by a push of a
//     protopath.AnyExpand step, followed by recursive application of the
//     rules on the underlying message, and succeeded by a pop of that step.
//     Mutations to the expanded message are written back to the Any message
//     when popping back out.
//
//   - For lists, iterate over every element. Each element is preceded by a
//     push of a protopath.ListIndex step, followed by recursive application of
//     the rules on the list element, and succeeded by a pop of that step.
//
//   - For maps, iterate over every entry. Each entry is preceded by a push
//     of a protopath.MapIndex step, followed by recursive application of the
//     rules on the map entry value, and succeeded by a pop of that step.
//
// Mutations should only be made to the last value, otherwise the effects
// on traversal will be undefined. If the mutation is made to the last value
// during to a push, then the effects of the mutation will affect traversal.
//
// The protopath.Values provided to push functions is only valid until the
// corresponding pop call and the values provided to a pop call is only valid
// for the duration of the pop call itself.
func (o Options) Range(m protoreflect.Message, push, pop func(protopath.Values) error) error

Usage example:

import (
    "fmt"
    "google.golang.org/protobuf/reflect/protorange"
    "google.golang.org/protobuf/reflect/protopath"
)

// Simple traversal
err := protorange.Range(msg.ProtoReflect(), func(p protopath.Values) error {
    fmt.Printf("Path: %v, Value: %v\n", p.Path, p.Index(-1).Value)
    return nil
})

// Traversal with push/pop callbacks
opts := protorange.Options{Stable: true}
err = opts.Range(msg.ProtoReflect(),
    func(p protopath.Values) error {
        // Called before visiting
        fmt.Printf("Entering: %v\n", p.Path)
        return nil
    },
    func(p protopath.Values) error {
        // Called after visiting
        fmt.Printf("Leaving: %v\n", p.Path)
        return nil
    },
)

Type Registry

Global Type Registry

Access the global registry for message types, extension types, and enum types.

// GlobalTypes is a global registry of types
var GlobalTypes *Types

// GlobalFiles is a global registry of files
var GlobalFiles *Files

Files Registry

Registry for looking up and registering file descriptors.

// Files is a registry for looking up file descriptors
type Files struct {
    // Has unexported fields.
}

// NewFiles creates a new file registry
func NewFiles(files ...protoreflect.FileDescriptor) (*Files, error)

// RegisterFile registers the provided file descriptor
// If any descriptor within the file conflicts with the descriptor already
// registered in files, then files is unchanged and an error is returned
func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error

// FindFileByPath looks up a file by the path
// The path must be a proto import path
func (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error)

// FindDescriptorByName looks up a descriptor by its full name
func (r *Files) FindDescriptorByName(name protoreflect.FullName) (protoreflect.Descriptor, error)

// NumFiles reports the number of registered files
func (r *Files) NumFiles() int

// RangeFiles iterates over all registered files
// The iteration order is undefined
func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool)

// NumFilesByPackage reports the number of registered files in a proto package
func (r *Files) NumFilesByPackage(name protoreflect.FullName) int

// RangeFilesByPackage iterates over all registered files in a proto package
// The iteration order is undefined
func (r *Files) RangeFilesByPackage(name protoreflect.FullName, f func(protoreflect.FileDescriptor) bool)

Types Registry

Registry for looking up and registering message types, extension types, and enum types.

// Types is a registry for looking up types
type Types struct {
    // Has unexported fields.
}

// NewTypes creates a new type registry
func NewTypes(typs ...interface{}) (*Types, error)

// RegisterMessage registers the provided message type
func (r *Types) RegisterMessage(mt protoreflect.MessageType) error

// FindMessageByName looks up a message by its full name
func (r *Types) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error)

// FindMessageByURL looks up a message by a URL identifier
// The URL is of the form "type.googleapis.com/package.Message"
func (r *Types) FindMessageByURL(url string) (protoreflect.MessageType, error)

// RegisterEnum registers the provided enum type
func (r *Types) RegisterEnum(et protoreflect.EnumType) error

// FindEnumByName looks up an enum by its full name
func (r *Types) FindEnumByName(enum protoreflect.FullName) (protoreflect.EnumType, error)

// RegisterExtension registers the provided extension type
func (r *Types) RegisterExtension(xt protoreflect.ExtensionType) error

// FindExtensionByName looks up an extension by its full name
func (r *Types) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)

// FindExtensionByNumber looks up an extension by field number within some parent message
func (r *Types) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)

// NumMessages reports the number of registered messages
func (r *Types) NumMessages() int

// RangeMessages iterates over all registered messages
// The iteration order is undefined
func (r *Types) RangeMessages(f func(protoreflect.MessageType) bool)

// NumEnums reports the number of registered enums
func (r *Types) NumEnums() int

// RangeEnums iterates over all registered enums
// The iteration order is undefined
func (r *Types) RangeEnums(f func(protoreflect.EnumType) bool)

// NumExtensions reports the number of registered extensions
func (r *Types) NumExtensions() int

// RangeExtensions iterates over all registered extensions
// The iteration order is undefined
func (r *Types) RangeExtensions(f func(protoreflect.ExtensionType) bool)

// NumExtensionsByMessage reports the number of registered extensions for a given message
func (r *Types) NumExtensionsByMessage(message protoreflect.FullName) int

// RangeExtensionsByMessage iterates over all registered extensions for a given message
// The iteration order is undefined
func (r *Types) RangeExtensionsByMessage(message protoreflect.FullName, f func(protoreflect.ExtensionType) bool)

Type Conflict Errors

// NotFound is a sentinel error value to indicate that the type was not found
var NotFound = errors.New("not found")

Usage example:

import (
    "google.golang.org/protobuf/reflect/protoreflect"
    "google.golang.org/protobuf/reflect/protoregistry"
)

// Register a message type
registry := &protoregistry.Types{}
err := registry.RegisterMessage((&pb.MyMessage{}).ProtoReflect().Type())

// Look up a message type
mt, err := protoregistry.GlobalTypes.FindMessageByName("example.MyMessage")
if err != nil {
    log.Fatal(err)
}

// Create a new instance
msg := mt.New().Interface()

Core Reflection Types

Message Interface

The primary interface for reflective message manipulation.

// Message is a reflective view over a concrete message instance
type Message interface {
    // Descriptor returns the message descriptor
    Descriptor() MessageDescriptor

    // Type returns the message type which contains both Go and protobuf type information
    Type() MessageType

    // New returns a newly allocated and mutable empty message
    New() Message

    // Interface unwraps the message reflection interface and
    // returns the underlying ProtoMessage interface
    Interface() ProtoMessage

    // Range iterates over every populated field in the message
    // The iteration order is undefined
    Range(func(FieldDescriptor, Value) bool)

    // Has reports whether a field is populated
    Has(FieldDescriptor) bool

    // Clear clears the field such that a subsequent Has call reports false
    Clear(FieldDescriptor)

    // Get retrieves the value for a field
    Get(FieldDescriptor) Value

    // Set stores the value for a field
    Set(FieldDescriptor, Value)

    // Mutable returns a mutable reference to a composite type
    Mutable(FieldDescriptor) Value

    // NewField returns a new value that is assignable to the field for the given descriptor
    NewField(FieldDescriptor) Value

    // WhichOneof reports which field within the oneof is populated, returning nil if none are populated
    WhichOneof(OneofDescriptor) FieldDescriptor

    // GetUnknown retrieves the entire list of unknown fields
    GetUnknown() RawFields

    // SetUnknown stores an entire list of unknown fields
    SetUnknown(RawFields)

    // IsValid reports whether the message is valid
    IsValid() bool
}

ProtoMessage Interface

The top-level interface that all generated messages implement.

// ProtoMessage is the top-level interface that all messages must implement
type ProtoMessage interface {
    // ProtoReflect returns a reflective view of the message
    ProtoReflect() Message
}

Value Type

Represents a value in the protobuf type system.

// Value is a union type that can represent any protobuf value
type Value value

// ValueOf returns a Value for a Go value
func ValueOf(v interface{}) Value

// ValueOfBool returns a new bool value
func ValueOfBool(v bool) Value

// ValueOfInt32 returns a new int32 value
func ValueOfInt32(v int32) Value

// ValueOfInt64 returns a new int64 value
func ValueOfInt64(v int64) Value

// ValueOfUint32 returns a new uint32 value
func ValueOfUint32(v uint32) Value

// ValueOfUint64 returns a new uint64 value
func ValueOfUint64(v uint64) Value

// ValueOfFloat32 returns a new float32 value
func ValueOfFloat32(v float32) Value

// ValueOfFloat64 returns a new float64 value
func ValueOfFloat64(v float64) Value

// ValueOfString returns a new string value
func ValueOfString(v string) Value

// ValueOfBytes returns a new bytes value
func ValueOfBytes(v []byte) Value

// ValueOfEnum returns a new enum value
func ValueOfEnum(v EnumNumber) Value

// ValueOfMessage returns a new message value
func ValueOfMessage(v Message) Value

// ValueOfList returns a new list value
func ValueOfList(v List) Value

// ValueOfMap returns a new map value
func ValueOfMap(v Map) Value

// IsValid reports whether v is populated with a value
func (v Value) IsValid() bool

// Interface returns v as an interface{}
func (v Value) Interface() interface{}

// Bool returns the value as a bool
func (v Value) Bool() bool

// Int returns the value as an int64
func (v Value) Int() int64

// Uint returns the value as a uint64
func (v Value) Uint() uint64

// Float returns the value as a float64
func (v Value) Float() float64

// String returns the value as a string
func (v Value) String() string

// Bytes returns the value as a []byte
func (v Value) Bytes() []byte

// Enum returns the value as an EnumNumber
func (v Value) Enum() EnumNumber

// Message returns the value as a Message
func (v Value) Message() Message

// List returns the value as a List
func (v Value) List() List

// Map returns the value as a Map
func (v Value) Map() Map

// MapKey returns the value as a MapKey
func (v Value) MapKey() MapKey

// Equal reports whether v and w are equal
func (v Value) Equal(w Value) bool

This comprehensive reflection documentation covers all major APIs for working with protocol buffers at runtime through reflection, including descriptor construction, path-based traversal, message introspection, and type registries.