Involved Source Files Package discover is plumb's first phase: it scans loaded packages for
//plumb:<name> directives and turns each tagged declaration into a Provider,
the surface-independent description the solve phase wires together. It reads
syntax and type information but makes no wiring decisions.provider.go
Package-Level Type Names (total 3, all are exported)
ProviderKind enumerates the surface forms a directive can tag. They all reduce
to the same abstraction (inputs, results, a render rule), which is what lets
the wiring code in the solve package ignore the kind almost entirely.( ProviderKind) String() string
ProviderKind : expvar.Var
ProviderKind : fmt.Stringer
ProviderKind : context.stringer
ProviderKind : runtime.stringer
const KindConvert
const KindField
const KindFunc
const KindMethod
const KindStruct
const KindSymbol
Package-Level Functions (total 22, in which 2 are exported)
Analyze scans every package for directives and builds the provider list. It
returns a located error for any malformed provider.
Packages are scanned in import-path order and files in name order, and scanFile
returns the source-earliest fault within a file, so when the input has more than
one fault the diagnostic returned is a fixed choice: the source-earliest fault
of the first file (in that order) to have one. This choice does not depend on the order
the loader presented packages and files (which it does not promise); every phase
anchors ordering to source position. The sorts run over copies, leaving the
loaded packages untouched. Provider order is irrelevant downstream (solve
re-sorts each set by position), so only diagnostic selection is affected.
The file being overwritten (the prior generated output, identified by the
base name of -output within the destination package) is skipped, so plumb
never feeds its own previous output back as input. (plumb does not emit
directives, so this only matters for a hand-edited or stray directive in that
file.)
FileBase returns the base name of the file containing the given syntax tree.
allBlank reports whether every declared name is the blank identifier, so the
spec declares nothing that could be wired.
appendDirectiveNames appends the set names from doc to prior. prior holds the
sets the declaration already joins (an enclosing group's directives, or an
earlier comment in the same doc), so a directive naming one of them (whether
the repeat is within doc or across the group/spec boundary) is a duplicate and
is rejected identically. The returned slice reuses prior's backing array; the
caller must not retain prior separately.
collectProviderDocs returns the set of comment groups the targeted scan reads
for directives: the doc of each top-level func, of each non-import GenDecl and
its value/type specs, and of each struct field and interface method. A plumb
directive in any other group is in an unsupported position.
directiveNames extracts the set names from a comment group attached to a
declaration. It returns the recognized plumb set names in order, plus any
located error for a recognized-but-invalid name (e.g. //plumb:123).
Recognition uses go/ast.ParseDirective, the same rule gofmt applies: a comment
gofmt would demote by inserting a space after "//" (and block comments) is not
a directive and is silently ignored. A //plumb: directive whose name is not a
Go identifier, or that carries arguments, is rejected.
isConversionExpr reports whether e is written as a conversion (a call whose
operand names a type), so MyInt(5) keeps the identity-conversion wording
while a bare constant gets the constant-specific one.
memberDirective returns the first //plumb: directive comment attached to a
member of a struct or interface literal, or nil.
receiverNamed unwraps a method receiver type to its origin named type, or nil
if the receiver is not a defined (named) type.
reportStrayDirectives flags any //plumb: directive that sits in a position the
targeted scan above never reads, so it would otherwise be silently ignored: a
directive in a function body, on an import, or floating free between
declarations. The scan only consults the doc comments of declarations and
members it recognizes, so a directive anywhere else is a mistake worth a
located error rather than a no-op.
The sweep walks file.Comments (every comment group in the file) because
comments inside bodies and free-floating ones live only there and are never
attached to a node's Doc; an AST node walk would miss exactly the positions we
want to catch. collectProviderDocs marks the groups the scan does read, and
anything else carrying a plumb directive is reported.
scanFile walks one file's top-level declarations (locals are never providers)
and the type members nested in them, returning the source-earliest fault it
finds. A stray directive is caught only by the whole-file sweep, which must run
after the declaration scan, so the two can surface out of source order: a stray
in an early function body precedes a duplicate directive on a later declaration,
yet the declaration scan produces its fault first. Comparing the two by position
makes the reported diagnostic the one a reader would fix first. Within each phase
the earliest already comes first (declarations are visited in source order, so
the first declaration fault is the earliest of them, and the sweep walks comments
in position order), so only the cross-phase pair needs comparing.
scanInterfaceMethods scans the methods of an interface declaration for member
directives. owner is the declared type (a *types.Named or, for an alias to an
anonymous interface, a *types.Alias), which the method provider binds to as its
receiver.
scanStructFields scans the fields of a struct declaration for member
directives. owner is the declared type (a *types.Named or, for an alias to an
anonymous struct, a *types.Alias), which the field provider binds to as its
receiver.
specDirectiveNames returns the sets a spec joins: the union of the directives on
the enclosing GenDecl (which apply to every spec in the group) and the spec's
own directives. A group directive that precedes the keyword attaches to
GenDecl.Doc for both a single declaration and a parenthesized group; a directive
inside the group attaches to the spec's own doc. Unioning them (rather than
letting the spec's doc shadow the group's) means a spec that joins an extra set
does not silently drop out of the group's set. A set named at both levels is a
duplicate (the group already joins the spec to it), so it is rejected like any
other repeated directive rather than silently deduped.