Documentation ¶
Overview ¶
Package contract defines the extension points for creating new contracts.
Contracts are associated with a specific object by using a magic comment of the form
contract:SomeContractName
It is acceptable for multiple contracts to be applied to the same object.
Additional configuration may be provided to contract instances by writing a json object, which will be unmarshalled into an instance of the contract struct.
contract:ConfigurableContract { "someKey" : "someValue", ... }
The entirety of the json literal must occur within the same comment. A multiline configuration can be specified when using the /* comment syntax:
/* contract:ConfigurableContract { "someKey" : "someValue" } */
There is a one-to-one mapping of an instance of a Contract with a contract declaration in the underlying source code. The specific objects returned from Context.Declaration() and Context.Objects() will vary based on where the contract declaration occurs.
In the simplest case, a contract declared directly upon a struct type will have a single *ssa.Type upon which the contract is enforced.
// contract:FooContract type SomeStruct struct { ... } context.Declaration() := SomeStruct context.Objects() := [ SomeStruct ]
Similarly, contract declarations placed upon individual function or method declarations will have a singleton *ssa.Function presented.
// contract:SomeContract func (r Receiver) MyMethod() { ... } context.Declaration() := MyMethod context.Objects() := [ MyMethod ]
In the case of interfaces, all structs that implement the interface and which have a type-asserting assignment will be aggregated. In the following example, the objects presented would be *ssa.Type instances for both Impl1 and Impl2. Note that because there is no explicit type assertion for NotSeen, it will not be part of the collection.
// contract:FooContract type SomeIntf interface { ... } var ( _ SomeIntf = Impl1{} _ SomeIntf = Impl2{} ) type Impl1 struct { ... } type Impl2 struct { ... } type NotSeen struct { ... } context.Declaration() := SomeIntf context.Objects() := [ Impl1, Impl2 ]
Contract declarations placed upon an interface method declaration will aggregate all implementing methods and present them as a slice of *ssa.Function. As with the interface case above, only (*Impl1).SomeMethod() and (*Impl2).SomeMethod() will be presented, because an explicit type assertion for those structs exist.
type SomeIntf { // controct:FooContract SomeMethod() } var ( _ SomeIntf = &Impl1{} _ SomeIntf = &Impl2{} ) func (*Impl1) SomeMethod() { ... } func (*Impl2) SomeMethod() { ... } func (*NotSeen) SomeMethod() { ... } context.Declaration() := (SomeIntf).SomeMethod() context.Objects() := [ (*Impl1).SomeMethod(), (*Impl2).SomeMethod() ]
Reusable contracts may be declared by declaring a type derived from Contract.
//contract:ReturnConcrete { "AllowedTypeNames" : ["github.../mypackage.Error"], "TargetInterface":"error" } type ReturnMyError Contract
TODO: Contracts may be placed on a package
Lastly, failing to abide by a contract results in BigEddie being unhappy. You wouldn't want BigEddie to be unhappy, would you?
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Assertions ¶
Assertions define type relationships that have been explicitly asserted in source. Generally, these are declarations of the form
var _ A = B{}
type Context ¶
type Context interface { context.Context // The name of the contract. Contract() string // Declaration returns the object that the contract declaration is // defined on. See additional discussion on the Contract type. Declaration() ssa.Member // Hints returns a reference to a shared hint store that allows // contracts to store and share information. Hints() *Hints // Kind returns the kind of contract to be enforced. Kind() Kind // Objects returns a collection of objects that a specific contract // declaration maps to. In general, this will contain at least one // element, the value returned from Declaration(). See additional // discussion on the Kind type. Objects() []ssa.Member // Oracle returns a reference to a shared TypeOracle for answering // questions about the program's typesystem. Oracle() *TypeOracle // Program returns the SSA Program object which is driving the // analysis. Program() *ssa.Program // By default, the Context will associate any messages with the // Declaration's position. Reporter() Reporter }
Context defines the interface between a Contract and the supporting framework.
type Contract ¶
type Contract interface { // Enforce will be called on an instance of the Contract automatically // by the runtime. Any error returned by this method will be reported // against the declaration object. Enforce(ctx Context) error }
A Contract implements some correctness-checking logic.
type Hints ¶
type Hints struct {
// contains filtered or unexported fields
}
Hints allows contracts to store and to share information about program elements. Every contract instance defined in a program will be available through the hints mechanism, allowing contracts to make assumptions based solely on source-code declaration.
The methods on hints are safe to call from multiple goroutines, however no guarantees are made about the enclosed hint data.
func NewHints ¶
func NewHints() *Hints
NewHints constructs a new Hints helper. Contract implementations should prefer to use the shared instance available from Context.
func (*Hints) Add ¶
Add associates the hint with the program member.
Add panics if the supplied hint value cannot be dereferenced to a struct type.
type ImpliesHints ¶
type ImpliesHints interface {
ImpliedHints() []interface{}
}
ImpliesHints allows a hint or Contract to add additional hints when it is associated with an object.
type Kind ¶
type Kind int
The Kind of a contract is a representation of how and where the contract binding was declared.
const ( // A method declaration like: // func (r Receiver) Foo() { ... } // presents just the function. KindMethod Kind = iota + 1 // A top-level function declaration: // func Foo () { .... } // presents just the function. KindFunction // An interface declaration: // type I interface { ... } // presents the interface and all types which are known to // implement it. KindInterface // A method defined in an interface: // type I interface { Foo() } // presents the interface method declaration and all functions which // are known to implement it. KindInterfaceMethod // All types other than interface declarations: // type Foo int // presents the type declaration. KindType )
The various kinds will inform the contract implementation as to what values it can expect to receive from the various Context methods.
| Kind | Context.Declaration() | Context.Objects() | ------------------------------------------------------------------------------------ | Method | *ssa.Function | { Declaration() } | | Function | *ssa.Function | { Declaration() } | | Interface | *ssa.Type (the interface) | []*ssa.Type (implementations) | | InterfaceMethod | *ssa.Type (the interface) | []*ssa.Function (implementations) | | Type | *ssa.Type | { Declaration() } |
type Located ¶
A Located object is associated with an opaque source location. Most types from the ssa package will implement this interface, as do all of the underlying AST objects.
type Reporter ¶
type Reporter interface { io.Writer // Detail creates a nested report to associate output with another // source location. It is valid to create a tree of reports. Detail(l Located) Reporter Print(args ...interface{}) Printf(format string, args ...interface{}) Println(args ...interface{}) }
A Reporter allows a contract to produce output messages that are associated with one or more source locations.
type TypeOracle ¶
type TypeOracle struct {
// contains filtered or unexported fields
}
A TypeOracle answers questions about a program's typesystem. All methods are safe to call from multiple goroutines.
func NewOracle ¶
func NewOracle(pgm *ssa.Program, assertions Assertions) *TypeOracle
NewOracle constructs a TypeOracle. In general, contract implementations should prefer the shared instance provided by Context, rather than constructing a new one.
func (*TypeOracle) MethodImplementors ¶
func (o *TypeOracle) MethodImplementors( intf *types.Interface, name string, assertedOnly bool, ) []*ssa.Function
MethodImplementors finds the named method on all types which implement the given interface.
func (*TypeOracle) TypeImplementors ¶
TypeImplementors returns the runtime times which implement the given interface, according to explicit assertions made by the user.