Documentation
¶
Overview ¶
Package nested handles nested function analysis coordination.
This package manages the analysis of nested function definitions, coordinating between parent and child scopes to propagate types and ensure consistent analysis ordering.
Nested Function Processing ¶
When a function contains nested definitions:
function outer()
local x = 1
local function inner()
return x -- captures x
end
end
The package ensures:
- Parent scope is analyzed first
- Captured variable types flow to nested functions
- Nested function results propagate back to parent
Constructor Analysis ¶
For functions that construct objects with methods:
function newCounter()
local count = 0
return {
inc = function() count = count + 1 end
}
end
The package coordinates method analysis with constructor return type inference.
Sibling Enrichment ¶
Nested functions in the same scope (siblings) may reference each other. The package ensures mutual visibility of sibling types.
Package nested provides pure helper functions for nested function discovery in Lua.
Lua allows functions to be defined anywhere: as local statements, inside table constructors, as method definitions, or as anonymous values. This package provides utilities for discovering and classifying these nested function definitions.
Data Types ¶
This package exports data types that describe nested functions:
- Child: A discovered nested function with its identity resolved
- FuncInfo: A Child extended with its synthesized function type
- ScopeGroup: A group of functions sharing the same parent scope
Pure Functions ¶
All functions in this package are pure: they take inputs and produce outputs without side effects. The orchestration of nested function processing is handled by the check package, which uses these helpers.
Scope Groups ¶
Functions defined at the same lexical level share a scope group. This enables mutual recursion: function A can call function B, and B can call A, even if B is defined after A in the source code.
Constructor Pattern ¶
The package detects the Lua OOP constructor pattern (see constructor.go):
function T.new()
local self = setmetatable({}, T)
self.field = value -- These become instance fields
return self
end
Self Type Resolution ¶
Helper functions support self-type resolution for methods:
- FindTableLiteralOwner: For table literal methods
- FindFieldAssignmentBase: For field assignment methods
- EnrichSelfTypeWithConstructorFields: For constructor-enriched self
Index ¶
- func CollectCapturedContainerMutations(graph *cfg.Graph, capturedSyms map[cfg.SymbolID]bool, ...) map[cfg.SymbolID][]api.ContainerMutation
- func CollectCapturedFieldAssignments(graph *cfg.Graph, capturedSyms map[cfg.SymbolID]bool, ...) map[cfg.SymbolID]map[string]typ.Type
- func CollectConstructorFields(graph *cfg.Graph, selfSym cfg.SymbolID, ...) map[string]typ.Type
- func DetectConstructorPattern(nestedGraph, parentGraph *cfg.Graph, fn *ast.FunctionExpr, ...) (classSymbol, selfSymbol cfg.SymbolID)
- func EnrichSelfTypeWithConstructorFields(selfType typ.Type, classSymbol cfg.SymbolID, store Store) typ.Type
- func EnrichTableTypeWithFuncTypes(rec *typ.Record, tableExpr *ast.TableExpr, graph *cfg.Graph, ...) typ.Type
- func FindFieldAssignmentBase(graph *cfg.Graph, fn *ast.FunctionExpr, point cfg.Point) (cfg.SymbolID, *ast.TableExpr, cfg.Point)
- func FindTableLiteralForSymbol(graph *cfg.Graph, sym cfg.SymbolID) (*ast.TableExpr, cfg.Point)
- func FindTableLiteralOwner(graph *cfg.Graph, fn *ast.FunctionExpr) (*ast.TableExpr, cfg.SymbolID)
- func NormalizeMethodSelfType(selfType typ.Type) typ.Type
- func ResolveNestedFuncIdentity(graph *cfg.Graph, nf cfg.NestedFunc, funcDef *cfg.FuncDefInfo) (string, cfg.SymbolID, bool)
- type Child
- type FuncInfo
- type ScopeGroup
- type Store
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CollectCapturedContainerMutations ¶ added in v1.5.2
func CollectCapturedContainerMutations( graph *cfg.Graph, capturedSyms map[cfg.SymbolID]bool, synth func(ast.Expr, cfg.Point) typ.Type, ) map[cfg.SymbolID][]api.ContainerMutation
CollectCapturedContainerMutations scans a nested function's graph for container mutations (e.g., channel.send) that target captured variables.
func CollectCapturedFieldAssignments ¶
func CollectCapturedFieldAssignments( graph *cfg.Graph, capturedSyms map[cfg.SymbolID]bool, synth func(ast.Expr, cfg.Point) typ.Type, ) map[cfg.SymbolID]map[string]typ.Type
CollectCapturedFieldAssignments scans a nested function's graph for field assignments to captured variables.
When a nested function assigns fields to a captured variable (e.g., `parent.field = v`), those assignments affect the type visible in the parent scope. This function collects such assignments for propagation back to the parent.
func CollectConstructorFields ¶
func CollectConstructorFields(graph *cfg.Graph, selfSym cfg.SymbolID, synth func(ast.Expr, cfg.Point) typ.Type) map[string]typ.Type
CollectConstructorFields collects field assignments to a self symbol in a constructor.
This scans the constructor's CFG for statements like `self.field = value` and builds a map of field names to their types. These fields become part of the class's instance type, enabling the type checker to validate field access on instances created by this constructor.
func DetectConstructorPattern ¶
func DetectConstructorPattern(nestedGraph, parentGraph *cfg.Graph, fn *ast.FunctionExpr, funcDef *cfg.FuncDefInfo) (classSymbol, selfSymbol cfg.SymbolID)
DetectConstructorPattern checks if a function is a constructor that: 1. Is named T.new (assigned to a field named "new" on a table) 2. Creates self via setmetatable({}, T) or setmetatable({}, {__index = T}) 3. Returns the self variable
The nestedGraph is the CFG of the function being analyzed. The parentGraph is the CFG where the function is defined (needed for T.new = function() pattern).
func EnrichSelfTypeWithConstructorFields ¶
func EnrichSelfTypeWithConstructorFields(selfType typ.Type, classSymbol cfg.SymbolID, store Store) typ.Type
EnrichSelfTypeWithConstructorFields merges constructor instance fields into a self-type.
When a method is defined on a class that has a constructor, the self-type should include fields assigned in the constructor. This function looks up constructor fields for the class and merges them into the self-type.
This enables the type checker to recognize instance fields in methods:
function T.new()
local self = setmetatable({}, T)
self.name = "" -- Collected as constructor field
return self
end
function T:greet()
print(self.name) -- self.name is recognized because of constructor fields
end
func EnrichTableTypeWithFuncTypes ¶
func EnrichTableTypeWithFuncTypes( rec *typ.Record, tableExpr *ast.TableExpr, graph *cfg.Graph, funcTypes map[cfg.SymbolID]typ.Type, ) typ.Type
EnrichTableTypeWithFuncTypes replaces method function types in a record with canonical function types derived from the interproc queries.
For table literals with method fields, the initially synthesized record may have function types without inferred return types. After analyzing the methods, canonical function types are available per symbol. This function updates the record with those more precise signatures.
func FindFieldAssignmentBase ¶
func FindFieldAssignmentBase(graph *cfg.Graph, fn *ast.FunctionExpr, point cfg.Point) (cfg.SymbolID, *ast.TableExpr, cfg.Point)
FindFieldAssignmentBase finds the base object symbol when a function is assigned via field assignment.
For patterns like `obj.method = function(self) ... end`, this function finds the base object (obj) so that self can be typed as the object's type. Also returns the table literal assigned to that symbol, if any.
func FindTableLiteralForSymbol ¶
FindTableLiteralForSymbol finds the TableExpr assigned to a symbol.
This is used to find the table literal that defines a class or object, enabling self-type resolution for methods defined on that table.
func FindTableLiteralOwner ¶
FindTableLiteralOwner finds the table literal containing fn as a field value.
For patterns like `local obj = { method = function(self) ... }`, this function finds the containing table so that self can be typed as the table's type. Returns both the TableExpr and its assigned symbol.
func NormalizeMethodSelfType ¶ added in v1.5.8
NormalizeMethodSelfType widens literal-heavy self shapes so method-local flow constraints do not treat mutable receiver state as compile-time constants.
func ResolveNestedFuncIdentity ¶
func ResolveNestedFuncIdentity(graph *cfg.Graph, nf cfg.NestedFunc, funcDef *cfg.FuncDefInfo) (string, cfg.SymbolID, bool)
ResolveNestedFuncIdentity determines the name, symbol, and locality of a nested function.
The identity is resolved by checking (in order):
- FuncDefInfo: Named function definitions provide name, symbol, and locality
- Local assignment: `local f = function()` provides target name and symbol
- NestedFunc symbol: Anonymous functions may still have an assigned symbol
Returns the function name (may be empty), symbol ID (may be 0), and whether the function is locally scoped.
Types ¶
type Child ¶
type Child struct {
NF cfg.NestedFunc
DefScope *scope.State
FuncDef *cfg.FuncDefInfo
FuncName string
FuncSym cfg.SymbolID
IsLocal bool
}
Child holds the resolved identity of a nested function definition.
Each Child represents a function discovered in the parent function's CFG. The Child captures the function's definition point, enclosing scope, and identity (name, symbol, locality).
func GatherChildren ¶
func GatherChildren(graph *cfg.Graph, scopes map[cfg.Point]*scope.State, fallback *scope.State) []Child
GatherChildren iterates the graph's nested functions and resolves each one's definition scope and identity.
For each nested function in the CFG, this function:
- Looks up the definition scope at the function's point
- Retrieves any FuncDefInfo (for named function definitions)
- Resolves the function's name, symbol, and locality
The result is a slice of Child structs ready for grouping and processing.
type FuncInfo ¶
type FuncInfo struct {
Child
}
FuncInfo extends Child with data needed during scope group processing.
type ScopeGroup ¶
ScopeGroup holds a group of functions sharing the same parent scope.
Functions in a scope group can see each other's types during analysis, enabling mutual recursion. The group is processed as a unit: sibling types are computed, then each function is checked with that context.