Documentation
¶
Overview ¶
Package semantic performs semantic analysis on a parsed Kukicha program: scope construction, symbol resolution, type checking, lint collection, and diagnostic emission. It is the final pre-codegen stage in the compiler pipeline (lexer → parser → semantic → codegen).
Analysis pipeline ¶
Analyze() runs the following phases in order on a single merged *ast.Program. The ordering is load-bearing — later phases assume earlier phases have finished.
- Expression maps init. a.exprTypes and a.exprReturnCounts are fresh for this run. Codegen reads them post-Analyze; nothing may read them before Pass 2 finishes writing.
- Package name + skill declaration checks. These are cheap standalone validations that don't touch the symbol table.
- Directive pre-pass. CollectDirectives walks every declaration's attached directives (# kuki:deprecated, # kuki:security, etc.) and emits TODO lint warnings immediately. The resulting DirectiveResult is consulted by both passes below.
- Pass 1 — collectDeclarations. Registers every top-level name (imports, types, interfaces, functions, enums, constants) in the global scope. Does NOT walk function bodies, method bodies, or any expression. Exists solely so Pass 2 can resolve forward references.
- Pass 2 — analyzeDeclarations. Walks each declaration and function body. Validates types, type-checks expressions, records ExprTypes / ExprReturnCounts. By this point the global scope is complete; cross-file forward references within a merged program resolve cleanly.
- Lint emission. emitLintWarnings flushes any LintCandidate collected during Pass 2 as warnings.
Invariants ¶
- Pass 1 MUST complete before Pass 2 starts. Moving any logic between them risks forward-reference failures that only surface on large programs.
- Pass 1 may register symbols but must not analyze bodies, expressions, or do type checking. Adding body-walking to Pass 1 silently breaks mutually recursive type declarations.
- a.exprTypes / a.exprReturnCounts are populated during Pass 2 and frozen thereafter. Codegen reads them by AST pointer identity — any AST rewrite during or after analysis must copy the entry across or carry the original node through.
- a.inOnerr / a.currentOnerrAlias are block-scoped conceptually but stored on the Analyzer; save/restore is caller-managed.
Multi-file programs ¶
Today the CLI merges every .kuki file in a target into a single *ast.Program (via cmd/kukicha.mergePrograms) and passes the merged whole here. The two-pass design ensures cross-file references resolve regardless of iteration order.
Phase B of Plan 3 (per-file cache) will split this: each file will be analyzed independently given an Environment of externally- resolved symbols. The Environment type and NewWithEnvironment constructor are the seam where that data will enter the analyzer — see environment.go.
Index ¶
- Constants
- Variables
- func CollectEnumTypes(prog *ast.Program) map[string]bool
- func DeclaredVarTypes(fn *ast.FunctionDecl) map[string]string
- func DidYouMean(cand string) string
- func ExportsListHash(exportSets []*Exports) string
- func ExternalRefsValidAgainst(externalEnvs map[string]*Environment, refs []SymbolRef) bool
- func ForeignIdiomFix(name string) *diagnostics.Fix
- func FuncKey(fn *ast.FunctionDecl) string
- func GetAllSecurityFunctions() map[string]string
- func GetAllStdlibEntries() map[string]goStdlibEntry
- func GetAllStdlibEnums() map[string][]string
- func GetSecurityCategory(qualifiedName string) string
- func GetSliceGenericClass(qualifiedName string) string
- func GetStdlibEntry(name string) (goStdlibEntry, bool)
- func GetStdlibEnum(qualifiedName string) ([]string, bool)
- func GetStdlibStructField(qualifiedType, fieldName string) (goStdlibType, bool)
- func GetStdlibVariant(qualifiedName string) (goStdlibVariant, bool)
- func GetStdlibVariantCaseParent(qualifiedName string) (string, bool)
- func HashSymbol(sym *Symbol) string
- func IsKnownInterface(qualifiedName string) bool
- func IsValidGoIdentifier(s string) bool
- func LookupParamTypeName(qualifiedName string, idx int) string
- func RefsValidAgainst(env *Environment, refs []SymbolRef) bool
- func ResolveImportBaseName(imp *ast.ImportDecl, sourceFile string) string
- func ResolveImportName(imp *ast.ImportDecl, sourceFile string) string
- func StdlibDocs() map[string]StdlibDocEntry
- func Suggest(needle string, candidates []string) string
- func SuggestForeignIdiom(name string) string
- type AdapterPair
- type AdapterParam
- type AnalysisResult
- type Analyzer
- func (a *Analyzer) Analyze() []error
- func (a *Analyzer) AnalyzeResult() *AnalysisResult
- func (a *Analyzer) Diagnostics() []Diagnostic
- func (a *Analyzer) ExternalReferences() []SymbolRef
- func (a *Analyzer) FieldShapeOf(ti *TypeInfo) FieldShape
- func (a *Analyzer) MarkStdlib()
- func (a *Analyzer) References() []SymbolRef
- func (a *Analyzer) SetExternalEnvironments(envs map[string]*Environment)
- func (a *Analyzer) SymbolTable() *SymbolTable
- type CachedFileResult
- type CachedTargetResult
- type CallEdge
- type CallGraph
- type CallGraphNode
- type CodegenMaps
- type Diagnostic
- type DirectiveResult
- type Environment
- type Exports
- type FieldShape
- type FunctionEffects
- type GoStdlibDoc
- type LintCandidate
- type LintKind
- type Scope
- type SecurityChecker
- type StdlibDocEntry
- type Symbol
- type SymbolKind
- type SymbolRef
- type SymbolTable
- func (st *SymbolTable) CurrentScope() *Scope
- func (st *SymbolTable) Define(symbol *Symbol) error
- func (st *SymbolTable) EnterScope()
- func (st *SymbolTable) ExitScope()
- func (st *SymbolTable) Resolve(name string) *Symbol
- func (st *SymbolTable) ResolveLocal(name string) *Symbol
- func (st *SymbolTable) TypeNames() []string
- func (st *SymbolTable) VisibleNames() []string
- type TypeInfo
- type TypeKind
- type ValidateRule
Constants ¶
const ( // Names and declarations CodeInvalidName = "semantic/invalid-name" CodeRedeclared = "semantic/redeclared" CodeDeclNotTopLevel = "semantic/decl-not-top-level" CodeDuplicateStructField = "semantic/duplicate-struct-field" CodeDuplicateVariantField = "semantic/duplicate-variant-case-field" CodeDuplicateInterfaceMethod = "semantic/duplicate-interface-method" CodeDuplicateFunction = "semantic/duplicate-function" CodeDuplicateMethod = "semantic/duplicate-method" CodeDuplicateType = "semantic/duplicate-type" // References CodeUndefined = "semantic/undefined" CodeMissingImport = "semantic/missing-import" CodeInvalidImport = "semantic/invalid-import" CodePackageNameConflict = "semantic/package-name-conflict" // Types and operands CodeTypeMismatch = "semantic/type-mismatch" CodeInvalidOperand = "semantic/invalid-operand" CodeInvalidType = "semantic/invalid-type" CodePlaceholderType = "semantic/placeholder-type" // Arguments and calls CodeArgumentCount = "semantic/argument-count" CodeArgumentType = "semantic/argument-type" CodeDuplicateNamedArg = "semantic/duplicate-named-arg" CodeUnknownNamedArg = "semantic/unknown-named-arg" CodeNamedArgUnsupported = "semantic/named-arg-unsupported" // Return statements CodeReturnCount = "semantic/return-count" CodeMisplacedReturn = "semantic/misplaced-return" // Assignment CodeAssignmentMismatch = "semantic/assignment-mismatch" CodeAssignToConst = "semantic/assign-to-const" // Control flow CodeMisplacedBreak = "semantic/misplaced-break" CodeMisplacedContinue = "semantic/misplaced-continue" CodeInvalidCondition = "semantic/invalid-condition" // Const CodeIotaOutsideConst = "semantic/iota-outside-const" CodeConstInvalid = "semantic/const-invalid" // For loop CodeForRangeType = "semantic/for-range-type" // Struct and field access CodeUnknownField = "semantic/unknown-field" CodeUnknownMethod = "semantic/unknown-method" // Enum CodeEnumMixedCases = "semantic/enum-mixed-cases" CodeEnumInvalidCase = "semantic/enum-invalid-case" CodeEnumEmpty = "semantic/enum-empty" CodeEnumUnknownCase = "semantic/enum-unknown-case" CodeEnumParseHelperConflict = "semantic/enum-parse-helper-conflict" CodeEnumIterHelperConflict = "semantic/enum-iter-helper-conflict" // Type / package name used where a runtime value is expected CodeTypeAsValue = "semantic/type-as-value" // Variant enum and is-expression CodeVariantUnknownCase = "semantic/variant-unknown-case" CodeVariantTypeMismatch = "semantic/variant-type-mismatch" CodeVariantLiteralInvalid = "semantic/variant-literal-invalid" CodeIsBindingInvalid = "semantic/is-binding-invalid" // Lambda CodeLambdaMultiReturn = "semantic/lambda-multi-return" // Onerr and fallback CodeOnerrInvalid = "semantic/onerr-invalid" CodeOnerrUnresolvedCall = "semantic/onerr-unresolved-call" CodeFallbackInvalid = "semantic/fallback-invalid" // Pipe CodePipeMultiplePlaceholder = "semantic/pipe-multiple-placeholder" CodeShorthandOutsidePipe = "semantic/shorthand-outside-pipe" CodeBlankIdentifierRead = "semantic/blank-identifier-read" CodeReturnInIfExpression = "semantic/return-in-if-expression" CodeReturnInDiscardedPipedSwitch = "semantic/return-in-discarded-piped-switch" // Directives CodeReturnsDirectiveInvalid = "semantic/returns-directive-invalid" // Skill CodeSkillInvalid = "semantic/skill-invalid" // Lint / warnings CodeDeprecated = "semantic/deprecated" CodePotentialPanic = "semantic/potential-panic" CodePipeDiscardsError = "semantic/pipe-discards-error" CodeEnumMissingCase = "semantic/enum-missing-case" CodeEnumRedundantCase = "semantic/enum-redundant-case" CodeEnumZeroMissing = "semantic/enum-zero-missing" CodeOnerrDiscard = "semantic/onerr-discard" CodeOnerrShadow = "semantic/onerr-shadow" CodeOnerrPanic = "semantic/onerr-panic-in-library" CodePipedSwitchMixedTypes = "semantic/piped-switch-mixed-types" CodeShadowBuiltin = "semantic/shadow-builtin" CodeValueCtorCapture = "semantic/value-ctor-capture" // Nullable references (#120) CodeDerefNullable = "semantic/deref-nullable" CodeUninitializedReference = "semantic/uninitialized-reference" CodeNullableInNonNullable = "semantic/nullable-required" )
Diagnostic codes emitted by the semantic analyzer. Each code is a slash-separated string "semantic/<rule>", stable across releases. The human-readable message wording may change; the code will not. Codes appear in the Code field of diagnostics.Diagnostic and in --json output.
Variables ¶
var GeneratedStdlibDocs = map[string]GoStdlibDoc{}/* 1373 elements not displayed */
GeneratedStdlibDocs maps qualified Kukicha stdlib symbol names to their docs. Populated from leading "#" comment blocks and pre-rendered Kukicha signatures so consumers don't need to re-parse stdlib .kuki sources.
Functions ¶
func CollectEnumTypes ¶ added in v0.7.1
CollectEnumTypes returns the set of enum type names visible to prog: local enums from its declarations and stdlib enums from its imports (keyed by the local package alias, e.g. "http.Status" when imported as "http"). This is the single authoritative source for both semantic analysis and codegen.
func DeclaredVarTypes ¶ added in v0.26.0
func DeclaredVarTypes(fn *ast.FunctionDecl) map[string]string
DeclaredVarTypes maps each of a function's receiver and explicitly-typed parameters to its simple type name (reference wrappers stripped), e.g. `func use(r: Repo)` yields {"r": "Repo"}. Only NamedType/PrimitiveType/ ReferenceType annotations resolve; other forms (list, map, func) are skipped. This is the same conservative, declaration-only resolution the call-graph walk uses to attribute method calls to a concrete receiver type — exported so consumers (e.g. the LSP) can resolve a `recv.Method()` call site to its ReceiverType.Method key without reimplementing the rules.
func DidYouMean ¶ added in v0.6.8
DidYouMean formats a "did you mean X?" hint, or "" if cand is empty.
func ExportsListHash ¶ added in v0.7.0
ExportsListHash returns a deterministic SHA256 of the union of all supplied Exports. The result is the natural per-file cache key component for "what external symbols does this file see?" — two invocations hash equal iff the input set carries the same exported symbols regardless of which file each came from or what order they were passed in.
Today this is conservative: any change in any other file's exports invalidates this file's cache entry, even if this file referenced none of the changed symbols. Phase C narrows that to per-symbol dependency tracking.
func ExternalRefsValidAgainst ¶ added in v0.19.7
func ExternalRefsValidAgainst(externalEnvs map[string]*Environment, refs []SymbolRef) bool
ExternalRefsValidAgainst reports whether every cross-package ref still resolves to a symbol with the same hash in externalEnvs. Refs without a Pkg are ignored (those belong to RefsValidAgainst). A missing package, missing symbol, or any hash drift returns false — forcing a re-analysis.
func ForeignIdiomFix ¶ added in v0.13.0
func ForeignIdiomFix(name string) *diagnostics.Fix
ForeignIdiomFix returns a machine-applicable fix for a known foreign-idiom identifier whose rewrite is a same-span string substitution, or nil when no trivial replacement exists. The Title is the editor menu-item label; the Replacement is substituted over the diagnostic's own span.
func FuncKey ¶ added in v0.26.0
func FuncKey(fn *ast.FunctionDecl) string
FuncKey returns the call-graph / effects-map key for a function or method: the bare name for free functions, "ReceiverType.MethodName" for methods, and "" when the receiver type cannot be resolved to a simple name.
func GetAllSecurityFunctions ¶ added in v0.0.30
GetAllSecurityFunctions returns the full map of security-checked function names to their category. Callers must not modify the returned map.
func GetAllStdlibEntries ¶ added in v0.0.30
func GetAllStdlibEntries() map[string]goStdlibEntry
GetAllStdlibEntries returns the full Kukicha stdlib registry as a read-only map. Callers must not modify the returned map.
func GetAllStdlibEnums ¶ added in v0.0.26
GetAllStdlibEnums returns the full map of stdlib enum types to their case names.
func GetSecurityCategory ¶
GetSecurityCategory returns the security check category for a stdlib function (e.g., "sql", "html", "fetch", "files", "redirect", "shell"), or "" if none.
func GetSliceGenericClass ¶
GetSliceGenericClass returns the generic classification for a stdlib/slice function: "T" (uses any), "K" (uses key), "TK" (uses both), or "" (not generic).
func GetStdlibEntry ¶
GetStdlibEntry returns the Kukicha stdlib registry entry for the given qualified name (e.g., "string.PadRight"). Returns the entry and true if found.
func GetStdlibEnum ¶ added in v0.0.26
GetStdlibEnum returns the case names for a qualified enum type (e.g., "http.Status") and true if found, or nil and false if not an enum.
func GetStdlibStructField ¶ added in v0.48.1
GetStdlibStructField returns the declared field type for fieldName on the qualified stdlib plain-struct type qualifiedType (e.g. "chat.Message", "Content"). Returns (zero, false) when the type isn't a registered stdlib struct or has no such exported field. Variant enums are not covered here — use GetStdlibVariant for those.
func GetStdlibVariant ¶ added in v0.17.3
GetStdlibVariant returns the variant enum declaration for a qualified name (e.g., "result.Result") if it is a stdlib variant enum, otherwise zero+false.
func GetStdlibVariantCaseParent ¶ added in v0.17.3
GetStdlibVariantCaseParent returns the parent variant enum's qualified name for a qualified case name (e.g., "result.Ok" → "result.Result"). Returns ("", false) when the name is not a stdlib variant case.
func HashSymbol ¶ added in v0.7.2
HashSymbol returns a deterministic SHA256 of a single Symbol's (name, kind, exported, type-shape) tuple. Used by Plan 3 Phase C to record per-reference hashes for fine-grained cache invalidation — each cached SymbolRef carries the hash of the symbol the analysis resolved, so a later run can detect a shape change without re-analyzing the whole file.
HashSymbol(sym) is the same hash contributed by sym to (*Exports).Hash; keeping the two definitions consistent means an Exports change that flips a single symbol's hash also flips that symbol's HashSymbol.
func IsKnownInterface ¶
IsKnownInterface returns true if the qualified type name is a known interface from either the Go stdlib or the Kukicha stdlib registries.
func IsValidGoIdentifier ¶ added in v0.19.9
IsValidGoIdentifier reports whether s is a syntactically valid Go identifier (used by codegen to validate auto-aliases).
func LookupParamTypeName ¶ added in v0.19.5
LookupParamTypeName returns the qualified type name of the parameter at position idx for the named stdlib function (Go stdlib first, then Kukicha stdlib). Returns "" if the parameter type is unknown or the function is not registered. Used by codegen to detect when an argument's expected type is a specific interface (e.g., http.Handler) for auto-wrapping.
func RefsValidAgainst ¶ added in v0.7.2
func RefsValidAgainst(env *Environment, refs []SymbolRef) bool
RefsValidAgainst reports whether every ref still resolves to a symbol with the same hash in env. A nil-or-missing symbol, or any hash drift, returns false — forcing a re-analysis.
Refs carrying a non-empty Pkg are skipped: they belong to the cross-package validator (ExternalRefsValidAgainst) and have no meaning against a same-package Environment.
func ResolveImportBaseName ¶ added in v0.19.9
func ResolveImportBaseName(imp *ast.ImportDecl, sourceFile string) string
ResolveImportBaseName is like ResolveImportName but ignores any alias — it always returns the canonical package name of the imported module.
func ResolveImportName ¶ added in v0.19.9
func ResolveImportName(imp *ast.ImportDecl, sourceFile string) string
ResolveImportName returns the package identifier for an import declaration. An explicit alias always wins. Otherwise the canonical `package <name>` declaration is read from the imported module via the Go module graph — necessary because the import path's last segment often diverges from the actual package name (e.g., "github.com/redis/go-redis/v9" → "redis"). Falls back to a path-basename heuristic when module resolution isn't available.
func StdlibDocs ¶ added in v0.23.0
func StdlibDocs() map[string]StdlibDocEntry
StdlibDocs returns the full stdlib doc surface keyed by qualified name (e.g., "slice.GroupBy"). Joined from GeneratedStdlibDocs (kind, signature, doc) plus the separately tracked security/deprecated/panics maps.
func Suggest ¶ added in v0.6.8
Suggest returns the closest candidate to needle within a small edit distance, or "" if no reasonable match exists. Case-insensitive exact matches are prioritized and returned in the candidate's original casing.
Thresholds are intentionally tight to avoid noisy false-positive hints: needles shorter than 3 runes are not corrected (too many spurious hits), and distance caps at 1 for needles shorter than 6 runes, 2 otherwise.
func SuggestForeignIdiom ¶ added in v0.13.0
SuggestForeignIdiom returns a hint message for a known Python or bash identifier, or "" if the name is not a recognized foreign idiom.
Callers should prefer this hint over Levenshtein suggestions when both are available: a user who typed `range` did not typo `Range`, they reached for the wrong tool. Levenshtein remains the fallback for misspellings of real in-scope names.
Types ¶
type AdapterPair ¶ added in v0.19.6
type AdapterPair struct {
Adapter string
ImportPath string
MethodName string
Params []AdapterParam
}
AdapterPair carries everything codegen needs to auto-wrap a lambda at a single-method interface parameter position: the adapter func type to wrap with, the package's Go import path so codegen can request the auto-import, the single interface method's name, and the method's parameter types.
func LookupAdapter ¶ added in v0.19.6
func LookupAdapter(interfaceQualName string) (AdapterPair, bool)
LookupAdapter returns the adapter pair registered for the single-method interface named interfaceQualName (e.g. "http.Handler" → {"http.HandlerFunc", "ServeHTTP", …}). The second return is false when no pairing is registered.
type AdapterParam ¶ added in v0.19.6
AdapterParam describes one parameter of a single-method interface's method, used by callers (notably codegen) to match a lambda's type annotations against an adapter pair.
type AnalysisResult ¶ added in v0.1.0
type AnalysisResult struct {
Errors []Diagnostic
Warnings []Diagnostic
// CodegenMaps holds the seven AST-keyed maps consumed by codegen. The fields
// are promoted (accessible as r.ExprTypes, r.ClosedTypeSwitches, etc.) for
// backward compatibility; new code may also use r.CodegenMaps directly.
CodegenMaps
// AllEnumTypes is the authoritative set of enum type names visible to the
// analyzed program: local enums (plain name) and imported stdlib enums
// (qualified as "pkg.EnumName", respecting import aliases). Codegen
// consumes this instead of re-scanning declarations and re-querying
// GetAllStdlibEnums so enum collection has a single point of truth.
AllEnumTypes map[string]bool
// CrossPkgUnitVariantCases is the set of cross-package unit variant case
// references visible to this file, keyed as "alias.CaseName" (e.g.,
// "myenums.RED"). Codegen uses this to emit `pkg.Case{}` zero-value
// literals instead of the bare type name `pkg.Case` that Go rejects as
// "type, not expression". Only populated when SetExternalEnvironments was
// called with the file's import dependencies.
CrossPkgUnitVariantCases map[string]bool
// CrossPkgValueEnumCases maps a cross-package value-enum (string-/int-backed)
// case reference, keyed as "alias.CaseName" (e.g. "shared.ACTIVE"), to its
// enum type name (e.g. "Status"). Codegen uses this to emit the qualified
// generated constant `pkg.StatusACTIVE` instead of the bare `pkg.ACTIVE`
// that Go rejects as undefined (#220). Only populated when
// SetExternalEnvironments was called with the file's import dependencies.
CrossPkgValueEnumCases map[string]string
}
AnalysisResult bundles all outputs from semantic analysis.
type Analyzer ¶
type Analyzer struct {
// contains filtered or unexported fields
}
Analyzer performs semantic analysis on the AST
func NewWithEnvironment ¶ added in v0.6.8
func NewWithEnvironment(program *ast.Program, sourceFile string, env *Environment) *Analyzer
NewWithEnvironment creates an Analyzer seeded with externally- resolved symbols from env. The environment's symbols are registered in the global scope before collectDeclarations runs (see seedFromEnvironment), allowing cross-file references to resolve in per-file analysis without seeing the other files' source.
A nil env is equivalent to EmptyEnvironment().
func NewWithFile ¶
NewWithFile creates a new semantic analyzer with the source file path. The file path is used to allow Kukicha stdlib packages to use Go stdlib names.
func (*Analyzer) AnalyzeResult ¶ added in v0.1.0
func (a *Analyzer) AnalyzeResult() *AnalysisResult
AnalyzeResult runs Analyze() and returns all outputs in a single struct.
func (*Analyzer) Diagnostics ¶ added in v0.0.30
func (a *Analyzer) Diagnostics() []Diagnostic
Diagnostics returns all errors and warnings from the most recent Analyze() call as structured Diagnostic values. The errors come first, then warnings.
Call after Analyze().
func (*Analyzer) ExternalReferences ¶ added in v0.19.7
ExternalReferences returns the cross-package symbols this analysis resolved through externalEnvs, each paired with a hash of its current shape. Suitable for caching alongside the resulting diagnostics so a later run can detect that a sibling-package symbol's shape changed without re-analyzing the consumer from scratch.
Returns nil when no externalEnvs lookup fired (single-package targets or targets that don't reach any cross-package resolution path). The returned slice is sorted by (Pkg, Name) for determinism.
Call after Analyze().
func (*Analyzer) FieldShapeOf ¶ added in v0.8.9
func (a *Analyzer) FieldShapeOf(ti *TypeInfo) FieldShape
FieldShapeOf returns the validation-relevant shape of a field type. reference T transparently uses the underlying T's shape (the rule will apply to the dereferenced value at codegen time).
func (*Analyzer) MarkStdlib ¶ added in v0.17.2
func (a *Analyzer) MarkStdlib()
MarkStdlib forces the analyzer into stdlib mode regardless of source path. Use only from tests or call sites that already know they are loading bundled stdlib sources.
func (*Analyzer) References ¶ added in v0.7.2
References returns the env-seeded symbols this analysis actually resolved, each paired with a hash of its current shape. Suitable for caching alongside the resulting diagnostics. Returns nil when the analyzer was constructed without an environment, or when the env was empty.
Call after Analyze().
func (*Analyzer) SetExternalEnvironments ¶ added in v0.19.7
func (a *Analyzer) SetExternalEnvironments(envs map[string]*Environment)
SetExternalEnvironments installs per-import-alias Environments for cross-package qualified-call resolution. The keys must match the alias as it appears in this file's import declarations (the bare last path segment when no alias is given, otherwise the alias identifier). Passing nil clears any previously installed envs.
This is the seam for Phase 3 of #145 — the project loader walks the target's imports, loads each non-stdlib sibling package, extracts its Exports, and hands the resulting Environments here before Analyze() runs. Inside the analyzer, qualified calls that miss the stdlib registries fall through to this map instead of being marked unresolved-external.
func (*Analyzer) SymbolTable ¶ added in v0.1.6
func (a *Analyzer) SymbolTable() *SymbolTable
SymbolTable returns the analyzer's symbol table (read-only after Analyze).
type CachedFileResult ¶ added in v0.7.2
type CachedFileResult struct {
Diagnostics []Diagnostic `json:"diagnostics"`
References []SymbolRef `json:"references"`
}
CachedFileResult is the on-disk shape stored under each per-file cache key. Pairs the file's diagnostics with the env symbols it actually consumed, so a later run can check whether any referenced symbol's shape changed before deciding it can reuse the cached diagnostics.
type CachedTargetResult ¶ added in v0.19.7
type CachedTargetResult struct {
Diagnostics []Diagnostic `json:"diagnostics"`
ExternalRefs []SymbolRef `json:"external_refs,omitempty"`
}
CachedTargetResult is the on-disk shape stored under each merged- target cache key. Pairs the target's full diagnostics slice with the cross-package symbols the merged analysis consumed through externalEnvs. A later run re-loads cross-package envs and validates each ref via ExternalRefsValidAgainst before deciding the cached diagnostics are still trustworthy.
Same-package staleness is covered by the cache key (which fingerprints every target source byte); ExternalRefs covers the gap previously papered over by the "skip cache when the target imports a sibling" gate.
type CallEdge ¶ added in v0.26.0
CallEdge is a directed caller→callee call relationship; From and To are node keys.
type CallGraph ¶ added in v0.26.0
type CallGraph struct {
Nodes map[string]CallGraphNode
Edges []CallEdge
}
CallGraph is the project-local call graph: function/method nodes plus the directed caller→callee edges between them. It surfaces the edge set that AnalyzeEffects computes internally and discards. Node keys match the effects map: bare name for free functions, "ReceiverType.Method" for methods. Both endpoints of every edge are present in Nodes.
func AnalyzeCallGraph ¶ added in v0.26.0
AnalyzeCallGraph builds the project-local call graph from one or more parsed programs. It shares the call-site walk and effect fixpoint with AnalyzeEffects, so its nodes carry the same transitive effect labels. Edges are project-local calls only — calls into stdlib surface as effect labels on the caller node, not as edges. The same resolution limits apply (receiver types only from explicit declarations; no interface or function-value dispatch).
type CallGraphNode ¶ added in v0.26.0
CallGraphNode is one function or method in the graph, carrying its transitive effect set (sorted, possibly empty).
type CodegenMaps ¶ added in v0.48.0
type CodegenMaps struct {
ExprReturnCounts map[ast.Expression]int
ExprTypes map[ast.Expression]*TypeInfo
// ClosedTypeSwitches holds type-switch nodes whose expression resolves to
// a variant enum. Codegen uses it to emit a `default: panic("unreachable")`
// backstop without breaking open switches over `any` / interface types.
ClosedTypeSwitches map[*ast.TypeSwitchStmt]bool
// ClosedEnumSwitches holds value-switch nodes whose expression resolves to a
// non-variant enum (string- or int-backed) with every case covered. Codegen
// emits `default: panic("unreachable")` so Go's return analyzer treats the
// enclosing function as terminating on all paths.
ClosedEnumSwitches map[*ast.SwitchStmt]bool
// EnumValueTypeSwitches holds type-switch nodes whose expression resolves to
// a non-variant enum (string- or int-backed). Codegen emits a value switch
// (`switch v := expr; v { case Const: ... }`) for these instead of Go's
// `.(type)` form.
EnumValueTypeSwitches map[*ast.TypeSwitchStmt]bool
// FuncPtrFieldCalls holds method-call nodes whose callee is a
// `reference func(...)` struct field. Codegen dereferences the pointer at the
// call site: `obj.field(args)` → `(*obj.field)(args)`.
FuncPtrFieldCalls map[*ast.MethodCallExpr]bool
// PipedSwitchAsMultiReturn lists piped-switch expressions used as the sole
// return value of a multi-return function with uniformly multi-value arms.
// Codegen reads this to emit `func() (T1, T2, ...) { ... }()` instead of the
// single-return IIFE form.
PipedSwitchAsMultiReturn map[*ast.PipedSwitchExpr][]*TypeInfo
}
CodegenMaps bundles the seven AST-keyed maps that semantic analysis produces and codegen consumes. They are grouped here so the handoff between the two passes has a single named unit: AnalysisResult embeds CodegenMaps, and Generator.SetAnalysisResult copies it in one assignment.
All maps are keyed by AST node pointer (frozen after Analyze returns) and are read-only during code generation.
type Diagnostic ¶ added in v0.0.30
type Diagnostic = diagnostics.Diagnostic
Diagnostic is a structured compiler diagnostic (error or warning). It is the machine-readable form of the errors and warnings produced by the Analyzer. Use Diagnostics() to obtain them after Analyze().
type DirectiveResult ¶ added in v0.1.0
type DirectiveResult struct {
DeprecatedFuncs map[string]string // Function name → deprecation message
DeprecatedTypes map[string]string // Type name → deprecation message
PanickedFuncs map[string]string // Function name → panic message
}
DirectiveResult holds directive data collected from AST declarations.
func CollectDirectives ¶ added in v0.1.0
func CollectDirectives(program *ast.Program) *DirectiveResult
CollectDirectives scans all declarations for # kuki:deprecated and # kuki:panics directives. Returns the collected results.
type Environment ¶ added in v0.6.8
type Environment struct {
// Types holds external type, interface, and enum declarations.
Types map[string]*Symbol
// Functions holds external function declarations.
Functions map[string]*Symbol
// Constants holds external const declarations.
Constants map[string]*Symbol
// Variables holds external top-level `var` declarations. Imports
// are deliberately excluded — they're file-local and re-derived
// during each file's analysis (see MergeExports).
Variables map[string]*Symbol
}
Environment bundles externally-resolved declarations that a file's analysis references but does not own. In today's merged-program flow it is always empty — every symbol the analyzer sees originates from the program it was constructed with. Phase B of Plan 3 (per-file semantic cache) will populate it with the exported symbols of the file's dependency closure so per-file analysis can resolve cross- file references without re-processing every dependency.
Keys are the unqualified name as it appears in source (e.g. "Point") or "pkg.Name" for symbols referenced through an imported package alias.
This type is deliberately inert today: NewWithEnvironment stores it on the analyzer but no pass consults it. Step 3 of Phase B will thread it through collectDeclarations so external symbols seed the global scope before local declarations are scanned.
func EmptyEnvironment ¶ added in v0.6.8
func EmptyEnvironment() *Environment
EmptyEnvironment returns an Environment with all maps initialised and empty. Useful as a placeholder where the API wants a non-nil *Environment but the caller has no external symbols to contribute.
func MergeExports ¶ added in v0.7.0
func MergeExports(exportSets []*Exports, skip map[string]struct{}) *Environment
MergeExports builds an Environment seeded with the union of all supplied exports, skipping any names listed in skip (typically the caller's own symbols, since a file shouldn't shadow itself with its own previous-pass entries).
On a duplicate name across exports the first occurrence wins. Per- file analysis with the resulting environment will report the conflict via the existing Define() collision path.
Each Symbol is shallow-cloned before being stored in the Environment. This prevents per-file analysis (which mutates sym.Type during const resolution in analyzeConstValue) from writing back through the shared pointer into the originating Exports.Symbols map, which would corrupt cached hash values and stale-entry detection.
func (*Environment) IsEmpty ¶ added in v0.6.8
func (env *Environment) IsEmpty() bool
IsEmpty reports whether env carries no external symbols. A nil receiver is also treated as empty.
func (*Environment) Lookup ¶ added in v0.7.2
func (env *Environment) Lookup(name string) *Symbol
Lookup finds a symbol by name across env's Types, Functions, and Constants maps. Returns nil if no symbol carries that name. Phase C's cache validator uses this to re-resolve a cached SymbolRef against the current environment so it can compare hashes.
type Exports ¶ added in v0.7.0
Exports captures the top-level declarations a file contributes to its package's scope. In Kukicha's directory-build model every .kuki file in a directory is part of the same package, so all top-level names — exported AND unexported — are visible across files. Exports records every such name with its inferred TypeInfo so other files can resolve cross-file references without re-parsing this file.
Exports is the input to MergeExports → Environment, which seeds an Analyzer constructed via NewWithEnvironment. The Hash() method produces a deterministic identifier suitable for use as the "external dependency" component of a per-file cache key.
func ExtractExports ¶ added in v0.7.0
ExtractExports parses prog and returns its top-level declarations as Symbols. It runs a restricted Pass 1 (collectDeclarations + import registration) on a fresh analyzer so behavior is independent of any caller-supplied environment. Bodies are not analyzed.
Errors emitted during the restricted pass (e.g. invalid identifier names) are intentionally discarded — those errors will be re-emitted by the full per-file analysis path that consumes these exports.
type FieldShape ¶ added in v0.8.9
type FieldShape int
FieldShape categorizes a field type for the rule allowlist.
const ( ShapeUnknown FieldShape = iota ShapeString ShapeNumeric // integer types: int, int64, uint, byte, rune, etc. ShapeFloat // float32, float64 — checked without int() truncation ShapeLengthBearing // list, map (length-bearing but not string) ShapeStruct // named struct types — nested validation )
type FunctionEffects ¶ added in v0.19.5
FunctionEffects maps a function or method name to the sorted, deduplicated set of security effect categories it transitively uses. Free functions are keyed by their bare name (e.g., "fetchUsers"). Methods are keyed as "ReceiverType.Method" (e.g., "Repo.Save"). Functions and methods with no observable effects are omitted.
Categories come from the same registry that gates `# kuki:security`-tagged stdlib calls: "sql", "html", "fetch", "files", "shell", "regex", "redirect".
This metadata is read-only — it never gates compilation. Consumers see at a glance which functions touch the database, the network, the filesystem, etc., without trusting a hand-written tag.
func AnalyzeEffects ¶ added in v0.19.5
func AnalyzeEffects(programs []*ast.Program) FunctionEffects
AnalyzeEffects computes per-function and per-method effect sets across one or more parsed programs. Phases: build a project-local call graph keyed by free-function name and by "ReceiverType.Method"; seed each entry's set from qualified stdlib calls; propagate to a fixpoint.
Limitations:
- Receiver types are resolved only from explicit parameter and receiver declarations (e.g. `func use(r: Repo)` yields r→Repo); local variables assigned from calls or composite literals are not tracked.
- Indirect calls through function-typed values are not tracked.
- Only stdlib `# kuki:security` categories are seeded; user-tagged effects are not supported by design (effect inference, not annotation).
type GoStdlibDoc ¶ added in v0.23.0
GoStdlibDoc is the agent-facing surface for a stdlib symbol: its kind ("func", "type", "enum", "interface"), a pre-rendered Kukicha signature, and the doc-comment block that preceded its declaration. Exported so the "kukicha context --stdlib" CLI can read it without depending on internals the rest of the package keeps private. Security and deprecation strings for the same symbol live in the generatedSecurityFunctions and generatedStdlibDeprecated maps and are joined at the consumer.
type LintCandidate ¶ added in v0.1.0
type LintCandidate struct {
Kind LintKind
Pos ast.Position
Message string
Category string // explicit category; empty means infer from message text
}
LintCandidate captures a potential warning during analysis, to be emitted in a separate pass after type checking completes.
type LintKind ¶ added in v0.1.0
type LintKind int
LintKind categorizes lint warnings for filtering and configuration.
type Scope ¶
type Scope struct {
// contains filtered or unexported fields
}
Scope represents a lexical scope
func (*Scope) ResolveLocal ¶ added in v0.48.1
ResolveLocal looks up a symbol in this scope only, without walking parents. Used to implement Go's short-var-decl (`:=`) redeclaration rule, which reassigns an existing name only when it lives in the *current* block scope (a name from an outer scope is shadowed by a fresh declaration instead).
func (*Scope) TypeNames ¶ added in v0.6.8
TypeNames walks the scope chain and returns the names of type and interface symbols (no variables, functions, or constants).
func (*Scope) VisibleNames ¶ added in v0.6.8
VisibleNames walks the scope chain and returns all defined names.
type SecurityChecker ¶ added in v0.1.0
type SecurityChecker struct {
// contains filtered or unexported fields
}
SecurityChecker performs security analysis on method calls. It detects SQL injection, XSS, SSRF, command injection, path traversal, and open redirect vulnerabilities.
type StdlibDocEntry ¶ added in v0.23.0
type StdlibDocEntry struct {
Kind string `json:"kind"`
Signature string `json:"signature"`
Doc string `json:"doc,omitempty"`
Security string `json:"security,omitempty"`
Deprecated string `json:"deprecated,omitempty"`
Panics string `json:"panics,omitempty"`
}
StdlibDocEntry is the agent-facing record for one stdlib symbol, joining the generator's doc map with the per-symbol security and deprecation strings tracked separately. Returned by StdlibDocs for the `kukicha context --stdlib` CLI.
type Symbol ¶
type Symbol struct {
Name string
Kind SymbolKind
Type *TypeInfo
Defined ast.Position
Mutable bool
Exported bool
IsAlias bool // true for transparent `type X = Y`; false for defined `type X Y`
// OriginEnum is set on symbols auto-generated by an enum declaration
// (currently the `Parse<EnumName>` helper from string-backed enums).
// Empty for everything else. Used to produce a targeted diagnostic when
// the user also writes a function with the generated name.
OriginEnum string
// IsImport marks symbols that were registered as the local binding for
// an `import` declaration (currently encoded as Kind=SymbolVariable
// because there is no dedicated SymbolImport). Cross-file/package
// export merging uses this to skip imports — they're file-local — while
// still propagating genuine top-level `var` declarations.
IsImport bool
}
Symbol represents a symbol in the symbol table
type SymbolKind ¶
type SymbolKind int
SymbolKind represents the kind of symbol
const ( SymbolVariable SymbolKind = iota SymbolParameter SymbolFunction SymbolType SymbolInterface SymbolConst )
func (SymbolKind) String ¶
func (sk SymbolKind) String() string
type SymbolRef ¶ added in v0.7.2
type SymbolRef struct {
Pkg string `json:"pkg,omitempty"`
Name string `json:"name"`
Hash string `json:"hash"`
}
SymbolRef is a name + symbol-hash pair recording that an analysis consumed an external symbol with a particular shape. Per-file cache entries store the slice of refs they produced; on lookup the validator re-resolves each name against the current environment and compares hashes — a mismatch (or missing symbol) means the cache entry is stale and must be regenerated.
Pkg, when non-empty, marks the ref as cross-package: the symbol came from externalEnvs[Pkg] (the consumer's import alias) rather than the same-package Environment. Same-package refs leave Pkg empty for backward compatibility with existing per-file cache entries.
type SymbolTable ¶
type SymbolTable struct {
// contains filtered or unexported fields
}
SymbolTable manages scopes and symbols
func (*SymbolTable) CurrentScope ¶
func (st *SymbolTable) CurrentScope() *Scope
CurrentScope returns the current scope
func (*SymbolTable) Define ¶
func (st *SymbolTable) Define(symbol *Symbol) error
Define adds a symbol to the current scope
func (*SymbolTable) ExitScope ¶
func (st *SymbolTable) ExitScope()
ExitScope removes the current scope
func (*SymbolTable) Resolve ¶
func (st *SymbolTable) Resolve(name string) *Symbol
Resolve looks up a symbol. Successful resolutions trigger onResolve (if set) so callers can observe which symbols this analysis actually consumed — used by the Phase C per-file cache to track outbound references against env-seeded symbols.
func (*SymbolTable) ResolveLocal ¶ added in v0.48.1
func (st *SymbolTable) ResolveLocal(name string) *Symbol
ResolveLocal looks up a name in the current scope only (no parent walk and no onResolve callback). Used by short-var-decl (`:=`) analysis to decide whether a name is a redeclaration in the same block.
func (*SymbolTable) TypeNames ¶ added in v0.6.8
func (st *SymbolTable) TypeNames() []string
TypeNames returns the names of every type/interface symbol reachable from the current scope. Used for "did you mean …?" hints on type references.
func (*SymbolTable) VisibleNames ¶ added in v0.6.8
func (st *SymbolTable) VisibleNames() []string
VisibleNames returns the names of every symbol reachable from the current scope walking parent scopes. Used for "did you mean …?" hints.
type TypeInfo ¶
type TypeInfo struct {
Kind TypeKind
Name string // For named types and placeholders
ElementType *TypeInfo // For lists, channels, references
KeyType *TypeInfo // For maps
ValueType *TypeInfo // For maps
Params []*TypeInfo // For functions
Returns []*TypeInfo // For functions
Constraint string // For placeholders: "any", "comparable", "cmp.Ordered"
Variadic bool // For functions: true if last param is variadic
ParamNames []string // For functions: parameter names (for named argument validation)
DefaultCount int // For functions: number of parameters with default values
Fields map[string]*TypeInfo // For structs: field name → field type
Methods map[string]*TypeInfo // For structs: method name → function TypeInfo
EnumCases map[string]*TypeInfo // For enums: case name → case type (the enum type itself)
VariantCases map[string]*TypeInfo // For variant enums: case name → struct TypeInfo with fields
VariantParent *TypeInfo // For variant case structs: pointer to the parent variant enum type
TypeParams []string // For generic variant enums and their cases: declared type parameter names (e.g., ["T"]). Empty for non-generic types.
TypeArgs []*TypeInfo // For instantiated generic types (e.g. `ApplyResult of GKEOutputs`): concrete type substituted into TypeParams. Populated by Unit 5.
Nullable bool // For references: true for `nullable reference T` (may hold empty). Codegen-irrelevant; both emit `*T`.
AliasOf string // For transparent named-type aliases: the underlying type name (e.g. "Status" for `type MyStatus = Status`). Empty otherwise.
}
TypeInfo represents type information
type TypeKind ¶
type TypeKind int
TypeKind represents the kind of type
const ( TypeKindUnknown TypeKind = iota TypeKindInt TypeKindFloat TypeKindString TypeKindBool TypeKindList TypeKindMap TypeKindChannel TypeKindReference TypeKindFunction TypeKindStruct TypeKindInterface TypeKindNamed TypeKindEnum TypeKindVariant // For variant enums (data-carrying sum types) TypeKindPlaceholder // For generic type placeholders (element, item, etc.) TypeKindNil // For the 'empty' keyword (nil) TypeKindTypeParam // For user-declared generic type parameters on variant enums (`enum X of T`). )
func (TypeKind) GoConstName ¶ added in v0.25.2
GoConstName returns the Go source identifier for this TypeKind constant (e.g. TypeKindInt → "TypeKindInt"). The stdlib generators (cmd/genstdlibregistry, cmd/gengostdlib) use it to emit goStdlibType literals whose Kind field is anchored to these constants: because every arm references a real TypeKind, a renamed or removed constant breaks the generators at compile time instead of silently emitting a stale string.
type ValidateRule ¶ added in v0.8.9
ValidateRule is a parsed `# kuki:validate` rule.
Examples (from the directive arg `"nonempty,min=1,oneof=a|b|c"`):
{Name: "nonempty"}
{Name: "min", Arg: "1"}
{Name: "oneof", Arg: "a|b|c"}
func ParseValidateRules ¶ added in v0.8.9
func ParseValidateRules(spec string) []ValidateRule
ParseValidateRules splits a directive arg like `"nonempty,min=1"` into individual ValidateRule entries. Whitespace around names and args is trimmed; pipe (|) inside an argument is kept intact so `oneof=a|b|c` works while keeping comma as the rule separator.
Source Files
¶
- cache_check.go
- codes.go
- crosslang_hints.go
- diagnostic.go
- doc.go
- effects.go
- environment.go
- exports.go
- foreign_idioms.go
- go_stdlib_gen.go
- go_stdlib_live.go
- is_const.go
- lint_value_ctor_capture.go
- qualified_import_suggest.go
- semantic_calls.go
- semantic_calls_methods.go
- semantic_calls_stdlib.go
- semantic_declarations.go
- semantic_directives.go
- semantic_expr_literal.go
- semantic_expr_ops.go
- semantic_expr_pipe.go
- semantic_expressions.go
- semantic_helpers.go
- semantic_lint.go
- semantic_nullcheck.go
- semantic_onerr.go
- semantic_pass.go
- semantic_security.go
- semantic_statements.go
- semantic_types.go
- semantic_validate_tags.go
- stdlib_gate.go
- stdlib_registry_gen.go
- stdlib_types.go
- suggest.go
- symbols.go
- type_names.go