Documentation
¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BraceDepth ¶
BraceDepth returns the net brace depth ({ minus }) of code, counting only real `{`/`}` tokens -- unlike a plain character count, one sitting inside a string/rune literal or a comment is correctly ignored, since this tokenizes with go/scanner (the real Go lexer) instead of scanning raw characters. A positive result means code has more unclosed `{` than `}` (a statement or declaration isn't finished yet); zero or negative means it's balanced (or over-closed, a real syntax error the compiler will report on its own terms -- this function only answers "is it still open," not "is it valid").
Used both by ParseCell (to tell a still-open type/func block from a finished one) and by cmd/gocell-repl (to tell when to keep showing the "...>" continuation prompt) -- previously two separate, naive `strings.Count(line, "{")` implementations, each breaking the same way on a `{` inside a string (e.g. `fmt.Println("Result: {")` desynced both: ParseCell silently dropped the rest of the cell, and the REPL hung forever waiting for a `}` that already existed, textually, inside the string).
code need not be syntactically valid Go -- it's frequently mid-typed, incomplete input by design (that's the whole reason this function exists). The scanner is given a nil error handler, so it never stops or panics on malformed input (an unterminated string literal, for instance, is tolerated: it's treated as running to EOF rather than emitting a hard error), it just does its best-effort token classification.
func ResolveMembers ¶
func ResolveMembers(codeBeforeDot string, reg *runtime.Registry, importTracker *ImportTracker, typeRegistry *runtime.TypeRegistry) ([]string, error)
ResolveMembers answers "what's valid after `foo.`" for a cell that's still being typed: codeBeforeDot is everything up to (not including) the dot, which must end in a bare expression statement naming the value being completed (typically a single identifier, e.g. "x := &Foo{}\nx" for completing "x."). It reuses AnalyzeCell's own machinery (buildAnalysisFile, rewriteTopLevelRedefinitions) so `foo` resolves exactly as it would in a real cell -- whether it's an existing Registry symbol from an earlier cell or one just declared earlier in this same, not-yet-submitted one -- then reads its go/types type off the trailing expression and lists that type's fields and methods.
Returns (nil, err) if codeBeforeDot doesn't parse, has no statements, or its type can't be resolved -- callers should treat that as "no member completions available", not an error to surface to the user (the code is, by definition, still being typed).
Types ¶
type AnalysisResult ¶
type AnalysisResult struct {
UsedSymbols map[string]*runtime.Symbol
NewVariables []string
// InjectedInterruptLines[i] holds the original line (within cell.Fset) of every loop in
// cell.Stmts[i] that injectInterruptChecks added a cooperative interrupt check to, sorted
// ascending. GeneratePluginCode copies this into the matching LineMapping entry so
// remapPanicError can correct for the extra generated lines.
InjectedInterruptLines [][]int
// LastExprIsConversion reports that the cell's last statement is a type conversion --
// `int64(5)`, `[]byte("hi")`, `Celsius(20)`. Syntactically that is an *ast.CallExpr, which
// the generator otherwise leaves alone on the grounds that a bare call is already a valid
// statement; a conversion is not, and would fail to compile with "is not used". Only
// go/types can tell the two apart, hence recording it here.
LastExprIsConversion bool
}
AnalysisResult holds the results of analyzing a cell.
func AnalyzeCell ¶
func AnalyzeCell(cell *CellContent, reg *runtime.Registry, importTracker *ImportTracker, typeRegistry *runtime.TypeRegistry) (*AnalysisResult, error)
AnalyzeCell decides, for every symbol the cell touches, whether it refers to a variable already in the Registry (to hydrate, and for value types to write back) or declares a new top-level one (to export).
It answers that by type-checking a throwaway copy of the cell rather than by matching identifier names: an identifier only counts as referring to a Registry symbol when go/types resolves it to that exact object. Name matching cannot make that call -- an identifier may coincide with a Registry symbol's name while referring to something else entirely (a range variable, a closure parameter, a `:=` nested in a block, a struct literal's field key, a label), and hydrating a symbol that the generated code never really references fails to compile ("declared and not used") for pointer-typed symbols, which have no write-back to otherwise reference them. It returns an error only if the cell could not be analyzed at all, which leaves no honest answer to give: guessing by name would resurrect exactly the ambiguity described above, and an empty analysis would silently drop the session's state from the generated code.
type Builder ¶
type Builder struct {
// contains filtered or unexported fields
}
Builder orchestrates compiling a Go file into a .so plugin.
func NewBuilder ¶
NewBuilder creates a new Builder by discovering the host gocell module.
func (*Builder) BuildPlugin ¶
BuildPlugin writes go.mod, main.go, and runs 'go build -mod=mod -buildmode=plugin'.
func (*Builder) GoVersion ¶
GoVersion returns the Go toolchain version every cell plugin is built with -- the same one read from the host module's go.mod at NewBuilder time (see detectGoVersion), so callers reporting the kernel's language version don't need to duplicate that detection themselves.
func (*Builder) ModuleRoot ¶
ModuleRoot returns the root directory of the host gocell module used to build cells.
type CellContent ¶
type CellContent struct {
Imports []*ast.ImportSpec
TypeDecls []ast.Decl
FuncDecls []ast.Decl
Stmts []ast.Stmt
RawCode string
// Fset is the FileSet that Imports/TypeDecls/FuncDecls/Stmts' positions are
// relative to. AnalyzeCell reuses it (rather than reparsing the cell) so that
// go/types resolves identifiers against these same node objects, not disconnected
// copies -- required for AnalyzeCell's rewrite pass to mutate the real nodes that
// GeneratePluginCode will later print.
Fset *token.FileSet
}
CellContent holds the result of breaking a cell down into an AST.
func ParseCell ¶
func ParseCell(code string) (*CellContent, error)
ParseCell parses a cell's code, intelligently splitting imports, types, functions and statements.
type ImportSpec ¶
ImportSpec represents an import specification with its optional alias.
type ImportTracker ¶
type ImportTracker struct {
// contains filtered or unexported fields
}
ImportTracker thread-safely tracks the set of imports for a session.
func NewImportTracker ¶
func NewImportTracker() *ImportTracker
NewImportTracker creates a new import manager.
func (*ImportTracker) AddImport ¶
func (it *ImportTracker) AddImport(alias, importPath string)
AddImport adds an import to the session.
func (*ImportTracker) AllImports ¶
func (it *ImportTracker) AllImports() map[string]*ImportSpec
AllImports returns the full list of registered imports.
func (*ImportTracker) CollectImportsFromFile ¶
func (it *ImportTracker) CollectImportsFromFile(node *ast.File)
CollectImportsFromFile extracts the imports from an AST file and adds them to the tracker.
func (*ImportTracker) GenerateImportBlockForCode ¶
func (it *ImportTracker) GenerateImportBlockForCode(codeBody string) string
GenerateImportBlockForCode produces the Go import block for the cell. goimports takes care of sanitizing and removing unused imports via the AST.
type LineMapping ¶
type LineMapping struct {
GeneratedLine int
OriginalLine int
// InjectedAtOriginalLines holds the original (pre-injection) line, within this statement,
// of every loop injectInterruptChecks added a check to -- sorted ascending. Each one added
// exactly 3 generated lines that don't exist in the cell's own source, so
// remapPanicError's otherwise-uniform generated->original interpolation needs to subtract
// 3 for every entry at or before the line it's resolving.
InjectedAtOriginalLines []int
}
LineMapping records that GeneratedLine, in the plugin source GeneratePluginCode produced, prints the start of the same statement that begins at OriginalLine in the cell's own source (cell.RawCode). Only top-level cell statements are covered -- not re-injected declarations from earlier cells, whose position in the generated file doesn't correspond to any single cell's line numbers at all.
func GeneratePluginCode ¶
func GeneratePluginCode( cell *CellContent, analysis *AnalysisResult, importTracker *ImportTracker, typeRegistry *runtime.TypeRegistry, ) (string, []LineMapping)
GeneratePluginCode generates the complete Go source of a cell plugin for compilation, along with a mapping from generated-file line numbers back to the cell's own source lines (used to report a panic's location in terms the user actually typed, not the generated file's).