processor

package
v0.0.0-...-5f54df4 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2020 License: Apache-2.0 Imports: 23 Imported by: 0

Documentation

Overview

Package processor contains the runtime library used by code that processes annotations.

This package defines an interface, Processor, which is implemented by things that can process annotations.

func(ctx *Context, output processor.OutputFactory) error

Processing is generally expected to validate annotation values and, optionally, generate code that is derived from the annotation values.

If a processor returns an error, processing has failed and the error should indicate why -- which can indicate that an annotation value is invalid for some reason. Validation errors should be constructed with processor.NewErrorWithPosition so that they can report locations in the source code, to aid users in resolving the error.

The OutputFactory passed to the processor may be used to generate code. When generating source code, a process should use the factory to create an output whose path includes both the Go import path and source file name. The factory can be used with the WriteGoFiles function in the github.com/jhump/gopoet package, making it easy to author Go source code from an annotation processor.

The remaining APIs and types in this package can be broken into three main categories: Processor Registration, Processor Invocation, and Mirrors.

Processor Registration

Processor implementations can be registered with this package using the RegisterProcessor method. All registered processors can later be queried with the AllRegisteredProcessors function. These can be used to create command-line tools that will run custom processors. The aptgo program (included in this repo) can load Go plugins, which can register custom processor implementations in their package init functions. This allows the aptgo to run custom processors, in addition to its default processing (which makes runtime-visible annotations accessible via the functions in the annogo package).

Processor Invocation

The package includes functions and types used to invoke processors. Key among them is processor.Config. This struct defines the packages that will be processed, the processors that will be invoked, and the output factory (which controls where generated output files are actually written).

After a processor.Config is constructed, its Execute method is used to actually invoke the configured processors. This process involves parsing the source code for all packages to process, performing full type analysis on the sources, and then extracting annotations. Once annotations are extracted, they are passed to each configured processor, via the processor.Context, one package at a time.

There are also some "shortcut" methods in this package: Process and ProcessAll. These functions create a processor.Config using the arguments given and using "typical" values for other settings and then call the resulting config's Execute method. The ProcessAll method will invoke all processors that have been registered with this package (via the RegisterProcessor function).

Mirrors

The "mirrors" API consists of several key types:

AnnotationMirror: The mirror is a representation of the annotation that processors can query. The mirror refers to an AnnotationMetadata instance, which describes the annotation type, and to an AnnotationValue instance, which describes the actual value.

AnnotationMetadata: The metadata describes the type of an annotation. It augments the "go/types" representation of the type by also providing resolved values for attributes interesting to an annotation processor, such as the values of the types' @annogo.Annotation annotations.

AnnotationValue: Since it is possible that the annotation types are not "known" to a processor (e.g. not compiled and linked into the processor implementation), these values must expose the data in a way that is similar to reflection, where the consumer need not know the actual type(s) ahead of time. Unlike reflect.Value, an AnnotationValue can only represent a valid annotation value (not all values and types in Go are valid as annotation values). An AnnotationValue also includes information about the position in source code where the values were defined (to assist with good error reporting). Finally, an AnnotationValue that refers to other program elements (such as a struct field, constant, or function) does so via values that implement types.Object (in particular, *types.Var for fields, *types.Const for constants, and *types.Func for functions).

AnnotatedElement: An annotated element is an element in Go source that has annotations. This struct provides access to the Go program element via the corresponding types.Object as well as references to the element in the program AST. It also provides access to AnnotationMirror instances for every annotation present on the element.

Processors can inspect the annotated elements and corresponding annotation mirrors, to inspect and validate values and/or to generate code derived from them. The entry point for this inspection is the processor.Context (provided to the processor when it is invoked), which provides several ways to query for annotated elements in a package.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsAnyType

func IsAnyType(t types.Type) bool

IsAnyType returns true if the given type is annogo.AnyType.

func IsSelfType

func IsSelfType(t types.Type) bool

IsSelfType returns true if the given type is annogo.SelfType.

func Process

func Process(pkgPaths []string, includeTest bool, outputDir string, procs ...Processor) error

Process invokes the given processors to process the given packages.

func ProcessAll

func ProcessAll(pkgPaths []string, includeTest bool, outputDir string) error

ProcessAll invokes all registered Processor instances to process the given packages. If the given outputDir is blank, the output will be the GOPATH directory that contains the sources for a particular package.

func RegisterProcessor

func RegisterProcessor(p Processor)

RegisterProcessor registers the given annotation processor.

func UnwrapMirror

func UnwrapMirror(val interface{}) interface{}

UnwrapMirror tries to unwrap a MirroredValue and return the raw value therein. If the given value does not wrap a MirroredVal, it is returned unchanged.

Types

type AnnotatedElement

type AnnotatedElement struct {
	// The actual source element, as a types.Object.
	Obj types.Object
	// The element's name/identifier in the source AST.
	Ident *ast.Ident
	// The AST for the file in which this element is defined.
	File *ast.File

	// Child elements. The children of structs are fields. The children of
	// interfaces are methods and embeds. Other elements do not have children.
	Children []*AnnotatedElement
	// The element's parent. The parent of a field will be the enclosing struct.
	// The parent of an interface method or embed will be the enclosing
	// interface.
	Parent *AnnotatedElement

	// The processor context for the package in which this element is defined.
	Context *Context

	// The element types that apply to this element.
	ApplicableTypes []annogo.ElementType
	// The annotations defined on this element.
	Annotations []AnnotationMirror
}

AnnotatedElement is a view of an element in source that has annotations. It can represent any Go source element on which annotations are allowed: a type, a struct field, an interface method or embed, a function or method, a variable, or a constant.

func (*AnnotatedElement) FindAnnotations

func (e *AnnotatedElement) FindAnnotations(packagePath, name string) []AnnotationMirror

FindAnnotations returns annotation mirrors whose annotation type is the given type. The given type is described by its package path and name.

func (*AnnotatedElement) GetDeclaringFilename

func (e *AnnotatedElement) GetDeclaringFilename() string

GetDeclaringFilename gets the name of the file that declared this element.

func (*AnnotatedElement) IsElementType

func (e *AnnotatedElement) IsElementType(et annogo.ElementType) bool

IsElementType returns true if this element is the given element type. It is considered to be the given type if the given type appears in the element's set of applicable types.

type AnnotationMapEntry

type AnnotationMapEntry struct {
	// The key for this map entry.
	Key AnnotationValue
	// The value for this map entry.
	Value AnnotationValue
}

AnnotationMapEntry represents an entry, key-value pair, in an annotation whose type is a map.

type AnnotationMetadata

type AnnotationMetadata struct {
	// The annotation type.
	Type *types.TypeName
	// The type used to describe the annotation. This can differ from the actual
	// annotation type if it defines a FactoryFunc, which transforms the
	// representation (data in an instance of the annotation) into the right
	// type.
	Representation types.Type

	// Since annotation types are themselves annotated (they have, at a minimum,
	// @annogo.Annotation), this field is a view of the type as an
	// annotated element.
	Element *AnnotatedElement

	// A function used to transform a value, as it appears in source, into the
	// right type. Corresponds to the value of an @annogo.FactoryFunc
	// annotation.
	FactoryFunc types.Object
	// If true, the annotation can be extracted at runtime using annotation
	// registration APIs. Corresponds to the field of the same name on the
	// @annogo.Annotation.
	RuntimeVisible bool
	// The kinds of elements on which this annotation is allowed to appear.
	// Corresponds to the field of the same name on the @annogo.Annotation.
	AllowedElements []annogo.ElementType
	// If true, this annotation can appear more than once on a single annotated
	// element. Corresponds to the field of the same name on the
	// @annogo.Annotation.
	AllowRepeated bool

	// The set of fields that are required. These fields must be present in all
	// instances of the annotation in source. Fields that are not required will
	// take a default value (if specified) or the type's zero value if they are
	// not specified in source. These are all fields that had an
	// @annogo.Required annotation.
	RequiredFields map[string]bool
	// The default values, for fields that have defaults other than their type's
	// zero value. These are all fields that had an @annogo.DefaultValue
	// annotation.
	DefaultFieldValues map[string]AnnotationValue
}

AnnotationMetadata is metadata that describes an annotation type. This contains some of the same attributes as found on annotations.Annotation, but it describes an annotation that exists in source, not as a runtime type. It includes all metadata that an annotation processor might find relevant.

type AnnotationMirror

type AnnotationMirror struct {
	// Metadata for the type of the annotation.
	Metadata *AnnotationMetadata
	// The location in source where this annotation is defined.
	Pos token.Position
	// The actual value of the annotation.
	Value AnnotationValue
}

AnnotationMirror is a view of an annotation instance that appears in source.

func (AnnotationMirror) Reify

func (m AnnotationMirror) Reify(target interface{}) error

Reify attempts to populate the given annotation value with the data in this mirror. It requires that the given value be a pointer and the pointed-to type must match the type of the mirror. If the type matches, it then delegates to the mirror's value:

m.Value.Reify(target).

If the types do not match, an error is returned. If the known type of the annotation -- reflect.TypeOf(target) -- is structurally different than the source form of the same type -- m.Metadata.Type -- then an error may be returned.

type AnnotationStructEntry

type AnnotationStructEntry struct {
	// The field in the struct.
	Field *types.Var
	// The position in source where this field value is defined.
	Pos token.Position
	// The value of the field.
	Value AnnotationValue
}

AnnotationStructEntry represents a field in an annotation value whose type is a struct.

type AnnotationValue

type AnnotationValue struct {
	// The type of the value.
	Type types.Type
	// The kind of the value.
	Kind ValueKind
	// The actual value. It will be an int64, uint64, float64, complex128, bool,
	// or string for scalar values. For function values, it will be a
	// *types.Func that refers to a top-level function or method. For aggregate
	// values, it will be a []AnnotationValue, []AnnotationMapEntry, or
	// []AnnotationStructEntry.
	Value interface{}
	// If the value is a reference to a constant, this is the constant that
	// was referenced. Expressions that include references to constants do not
	// count. E.g. the expression "someConst + 1" would result in a nil Ref, but
	// "someConst" would result in a non-nil Ref to someConst.
	Ref *types.Const

	// The position in source where this annotation value is defined.
	Pos token.Position
}

AnnotationValue represents the value of an annotation. All values are constant expressions, known at compile-time.

func (*AnnotationValue) AsBool

func (v *AnnotationValue) AsBool() bool

AsBool is a convenience function that type asserts the value as a bool.

func (*AnnotationValue) AsComplex

func (v *AnnotationValue) AsComplex() complex128

AsComplex is a convenience function that type asserts the value as a complex128.

func (*AnnotationValue) AsFloat

func (v *AnnotationValue) AsFloat() float64

AsFloat is a convenience function that type asserts the value as a float64.

func (*AnnotationValue) AsFunc

func (v *AnnotationValue) AsFunc() *types.Func

AsFunc is a convenience function that type asserts the value as a *types.Func.

func (*AnnotationValue) AsInt

func (v *AnnotationValue) AsInt() int64

AsInt is a convenience function that type asserts the value as an int64.

func (*AnnotationValue) AsMap

func (v *AnnotationValue) AsMap() []AnnotationMapEntry

AsMap is a convenience function that type asserts the value as a []AnnotationMapEntry.

func (*AnnotationValue) AsSlice

func (v *AnnotationValue) AsSlice() []AnnotationValue

AsSlice is a convenience function that type asserts the value as a []AnnotationValue.

func (*AnnotationValue) AsString

func (v *AnnotationValue) AsString() string

AsString is a convenience function that type asserts the value as a string.

func (*AnnotationValue) AsStruct

func (v *AnnotationValue) AsStruct() []AnnotationStructEntry

AsStruct is a convenience function that type asserts the value as a []AnnotationStructEntry.

func (*AnnotationValue) AsUint

func (v *AnnotationValue) AsUint() uint64

AsUint is a convenience function that type asserts the value as a uint64.

func (*AnnotationValue) Reify

func (v *AnnotationValue) Reify(target interface{}) error

Reify attempts to populate the given target value with the data in this value. An error is returned if the given value's type is not structurally compatible with the value (such as trying to reify a string value from a map annotation value).

The given value must be a non-nil pointer. The value to which it points is updated to reflect the value represented by v.

If the annotation value is a composite value (slice, array, struct, or map) then the elements are recursively reified.

If the annotation value is a function or contains any references to functions, the reified value will be a function that panics if called. The value recovered from the panic will be a *processor.ErrMirroredFunction, which provides access to the *types.Func representation of the actual referenced function.

Due to limitations in Go reflection (and the "reflect" package), reification is not able to produce values of concrete named types or unnamed structs that have unexported fields unless the declared type is also concrete. When the declared type is an interface, the reified value must instead use a MirroredValue. In some cases, the reified value will actually implement MirroredValue directly. In others, the reified value may have a synthetic type that *wraps* a MirroredValue. In both cases, the MirroredValue can be extracted using processor.GetMirroredValue().

The value will be a synthetic type when the declared type is an interface with one or more methods. In that case the synthetic type will implement the declared interface (not MirroredValue). But any attempt to invoke an interface method on the value will panic.

type Config

type Config struct {
	ImportPkgs    map[string]bool
	CreatePkgs    []loader.PkgSpec
	Processors    []Processor
	OutputFactory func(path string) (io.WriteCloser, error)
}

Config represents the configuration for running one or more Processors. Callers should configure all of the exported fields and then call the Execute method to actually invoke the processors.

func (*Config) Execute

func (cfg *Config) Execute() error

Execute invokes the configured processors for the configured packages, writing outputs using the configured OutputFactory.

type Context

type Context struct {
	// Package holds all information about the package being processed. It
	// provides access to the ASTs of files in the package as well as the
	// results of type analysis, to allow for introspection of package elements.
	Package *loader.PackageInfo

	// Program holds information about an entire program being processed, which
	// includes any packages that are being processed as well as their
	// dependencies (including indirect dependencies, e.g. the full transitive
	// closure). This also provides access to the token.FileSet, which can be
	// used to resolve details for source code locations.
	Program *loader.Program

	// AllElementsByObject is map of all elements in the package (represented by
	// types.Object instances) that have annotations to a corresponding
	// AnnotatedElement structure.
	//
	// Also see methods Context.NumElements, Context.GetElement, and
	// Context.ElementsOfType.
	AllElementsByObject map[types.Object]*AnnotatedElement
	// AllAnnotationTypes indicates the names of all annotation types found in
	// the package's sources. The map is keyed by package import path, with the
	// values being slices of unqualified names of annotation types in that
	// package.
	AllAnnotationTypes map[string][]string
	// contains filtered or unexported fields
}

Context represents the environment for an annotation processor. It represents a single package (for which the processors were invoked). It provides access to all annotations and annotated elements encountered in the package.

func (*Context) ElementsAnnotatedWith

func (c *Context) ElementsAnnotatedWith(packagePath, typeName string) []*AnnotatedElement

ElementsAnnotatedWith returns a slice of elements that have been annotated with the given annotation type.

Note that only packages configured to be processed will have a non-zero number of elements. Other packages (such as dependencies of those being processed), may actually have annotations therein, but since they will not have been processed, the context will not include them.

func (*Context) ElementsOfType

func (c *Context) ElementsOfType(t annogo.ElementType) []*AnnotatedElement

ElementsOfType returns a slice of annotated elements of the given type.

Note that only packages configured to be processed will have a non-zero number of elements. Other packages (such as dependencies of those being processed), may actually have annotations therein, but since they will not have been processed, the context will not include them.

func (*Context) GetElement

func (c *Context) GetElement(index int) *AnnotatedElement

GetElement returns the annotation element at the given index. The given index must be greater than or equal to zero and less than c.NumElements().

Note that only packages configured to be processed will have a non-zero number of elements. Other packages (such as dependencies of those being processed), may actually have annotations therein, but since they will not have been processed, the context will not include them.

func (*Context) GetMetadata

func (c *Context) GetMetadata(packagePath, name string) (*AnnotationMetadata, error)

GetMetadata returns annotation metadata for the given annotation type. If the given type's package has not yet been parsed (possible if the requested annotation type is not in the transitive dependencies of the context's package), it is parsed and processed.

func (*Context) GetMetadataForTypeName

func (c *Context) GetMetadataForTypeName(t *types.TypeName) (*AnnotationMetadata, error)

GetMetadataForTypeName returns annotation metadata for the given annotation type. If the given type's package has not yet been parsed (possible if the requested annotation type is not in the transitive dependencies of the context's package), it is parsed and processed.

func (*Context) GetValue

func (c *Context) GetValue(av AnnotationValue, forceArray bool) interface{}

func (*Context) NumElements

func (c *Context) NumElements() int

NumElements returns the number of annotated elements for the context's package.

Note that only packages configured to be processed will have a non-zero number of elements. Other packages (such as dependencies of those being processed), may actually have annotations therein, but since they will not have been processed, the context will not include them.

type ErrMirroredFunction

type ErrMirroredFunction types.Func

ErrMirroredFunction is an error that is caused by invoking a function whose underlying function cannot be executed because it is present only in source form. The source form, a *types.Func, can be retrieved using the AsFunc() method.

When a value is reified from an AnnotationMirror or AnnotationValue, and that value is or contains a function reference, the reified function value, if called, will panic. The value recovered from that panic will be of type *ErrMirroredFunction.

func (*ErrMirroredFunction) AsFunc

func (e *ErrMirroredFunction) AsFunc() *types.Func

AsFunc returns the function that was referenced by the source of the error.

func (*ErrMirroredFunction) Error

func (e *ErrMirroredFunction) Error() string

Error implements the error interface, returning a string that describes the error.

type ErrorWithPosition

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

ErrorWithPosition is an error that has source position information associated with it. The position indicates the location in a source file where the error was encountered.

func NewErrorWithPosition

func NewErrorWithPosition(pos token.Position, err error) *ErrorWithPosition

NewErrorWithPosition returns the given error, but associates it with the given source code location.

func (*ErrorWithPosition) Error

func (e *ErrorWithPosition) Error() string

Error implements the error interface. It includes position information in the returned message.

func (*ErrorWithPosition) Pos

func (e *ErrorWithPosition) Pos() token.Position

Pos returns the location in source where the underlying error was encountered.

func (*ErrorWithPosition) Underlying

func (e *ErrorWithPosition) Underlying() error

Underlying returns the underlying error.

type MirroredValue

type MirroredValue interface {
	// GetRawValue returns the underlying "raw" value.
	//
	// Where possible, it will have the same structure and underlying type as
	// the unresolved type found in GetAnnotationValue().Type, but will simply
	// be unnamed. This will not be possible if the underlying type includes
	// references to other unresolved types (such as a slice or map whose
	// elements are of unresolvable types, or a struct that has a field with an
	// unresolvable type). In those cases, all unresolvable types are replaced
	// with MirroredValue.
	GetRawValue() interface{}
	// GetAnnotationValue returns the AnnotationValue from which this mirrored
	// value came.
	GetAnnotationValue() AnnotationValue
	// contains filtered or unexported methods
}

MirroredValue represents a "malformed" value, reified from a mirror. This happens when a mirror (e.g. AnnotationMirror or AnnotationValue) refers to a type that cannot be resolved to a runtime type. In that case, a MirroredValue is used instead. From the MirroredValue, the original AnnotationValue may be accessed as well as a "best effort" representation of the value, via the GetRawValue() method.

If the raw value is a struct, it will have no unexported fields, regardless of whether the intended struct type has unexported fields. Typically, converting an unexported field name to an exported one just involves capitalizing the first letter. But if the unexported field name does not start with a letter, it will get an "X" prefix to make it exported. As necessary, field names may be changed further to avoid naming conflicts. Field names are changed in order (e.g. first field renamed first), and if the changes cause later field names to conflict with earlier field names, the later fields will have one or more underscores ("_") appended to their name, to ensure all fields have unique names.

func GetMirroredValue

func GetMirroredValue(val interface{}) (MirroredValue, bool)

GetMirroredValue unwraps a MirroredValue from the given val. The returned bool is true on success. If val directly implemented MirroredValue, it is returned unchanged. If val is a synthetic value, that wraps a MirroredValue while also implementing some other interface, the MirroredValue therein is returned. Otherwise, the given value does not wrap a MirroredValue, and this function will return nil, false.

type OutputFactory

type OutputFactory func(path string) (io.WriteCloser, error)

OutputFactory is a function that creates a writer to an output for the given location. Output factories typically use os.OpenFile to create files but this function allows the behavior to be customized.

func DefaultOutputFactory

func DefaultOutputFactory(rootDir string) OutputFactory

DefaultOutputFactory returns the default OutputFactory used by Process and ProcessAll. If the given rootDir is blank, a GOPATH directory will be chosen, based on the location of a package's input sources. The actual full path will be <rootDir>/src/<path> (note the implicit "src" path element, just like when looking for sources in GOPATH).

After computing the destination path, os.OpenFile is used to open the file for writing (creating the file if necessary, truncating it if it already exists).

type Processor

type Processor func(ctx *Context, output OutputFactory) error

Processor is a function that acts on annotations and is invoked from the annotation processor tool. Typical processor implementations generate code based on the annotations present in source.

func AllRegisteredProcessors

func AllRegisteredProcessors() []Processor

AllRegisteredProcessors returns the list of all registered processors.

type ValueKind

type ValueKind int

ValueKind indicates the type of underlying value for an annotation.

const (
	// KindInvalid should not be used and indicates an incorrectly uninitialized
	// kind.
	KindInvalid ValueKind = iota
	// KindInt is for values whose type is a signed integer.
	KindInt
	// KindUint is for values whose type is an unsigned integer.
	KindUint
	// KindFloat is for values whose type is a floating point number.
	KindFloat
	// KindComplex is for values whose type is a complex number.
	KindComplex
	// KindString is for values whose type is a string.
	KindString
	// KindBool is for values whose type is a boolean.
	KindBool
	// KindNil is for values whose type is nil.
	KindNil
	// KindFunc is for values whose type is the name of a function or method.
	KindFunc
	// KindSlice is for values whose type is a slice or array.
	KindSlice
	// KindMap is for values whose type is a map.
	KindMap
	// KindStruct is for values whose type is a struct.
	KindStruct
)

func (ValueKind) IsScalar

func (k ValueKind) IsScalar() bool

func (ValueKind) String

func (k ValueKind) String() string

Jump to

Keyboard shortcuts

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