• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

forst-lang / forst / 28827370804

06 Jul 2026 10:23PM UTC coverage: 78.432% (-0.7%) from 79.124%
28827370804

push

github

web-flow
perf(typechecker): add scope-key hashing, compat memo (#125)

678 of 1147 new or added lines in 34 files covered. (59.11%)

11 existing lines in 6 files now uncovered.

27994 of 35692 relevant lines covered (78.43%)

1553.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.19
/forst/internal/typechecker/infer_function_node.go
1
package typechecker
2

3
import (
4
        "fmt"
5
        "forst/internal/ast"
6

7
        logrus "github.com/sirupsen/logrus"
8
)
9

10
func (tc *TypeChecker) inferFunctionNode(node ast.Node) ([]ast.TypeNode, error) {
11,050✔
11
        var functionNode ast.FunctionNode
11,050✔
12
        switch n := node.(type) {
11,050✔
13
        case ast.FunctionNode:
11,050✔
14
                functionNode = n
11,050✔
NEW
15
        case *ast.FunctionNode:
×
NEW
16
                if n == nil {
×
NEW
17
                        return nil, nil
×
NEW
18
                }
×
NEW
19
                functionNode = *n
×
NEW
20
        default:
×
NEW
21
                return nil, fmt.Errorf("inferFunctionNode: unexpected node type %T", node)
×
22
        }
23
        prevFn := tc.currentFunction
11,050✔
24
        tc.currentFunction = &functionNode
11,050✔
25
        prevErrBranchDepth := tc.resultErrIfBranchDepth
11,050✔
26
        tc.resultErrIfBranchDepth = 0
11,050✔
27
        defer func() {
22,100✔
28
                tc.currentFunction = prevFn
11,050✔
29
                tc.resultErrIfBranchDepth = prevErrBranchDepth
11,050✔
30
        }()
11,050✔
31

32
        tc.log.WithFields(logrus.Fields{
11,050✔
33
                "function": "inferNodeType",
11,050✔
34
                "fn":       functionNode.Ident.ID,
11,050✔
35
                "phase":    "ENTER",
11,050✔
36
        }).Debug("Function node type inference")
11,050✔
37

11,050✔
38
        if err := tc.RestoreScope(node); err != nil {
11,050✔
39
                return nil, err
×
40
        }
×
41
        tc.log.WithFields(logrus.Fields{
11,050✔
42
                "function": "inferNodeType",
11,050✔
43
                "fn":       functionNode.Ident.ID,
11,050✔
44
        }).Debug("Restored function scope")
11,050✔
45

11,050✔
46
        for _, param := range functionNode.Params {
24,548✔
47
                switch typedParam := param.(type) {
13,498✔
48
                case ast.SimpleParamNode:
13,497✔
49
                        tc.scopeStack.currentScope().RegisterSymbol(
13,497✔
50
                                typedParam.Ident.ID,
13,497✔
51
                                []ast.TypeNode{typedParam.Type},
13,497✔
52
                                SymbolVariable)
13,497✔
53
                        tc.bindVariableGoTypeFromParamType(typedParam.Ident.ID, typedParam.Type)
13,497✔
54
                case ast.DestructuredParamNode:
1✔
55
                        tc.registerDestructuredParamSymbols(typedParam.Fields, typedParam.Type, SymbolVariable)
1✔
56
                }
57
        }
58
        tc.DebugPrintCurrentScope()
11,050✔
59

11,050✔
60
        params := make([]ast.Node, len(functionNode.Params))
11,050✔
61
        for index, param := range functionNode.Params {
24,548✔
62
                params[index] = param
13,498✔
63
        }
13,498✔
64

65
        paramTypes, err := tc.inferNodeTypes(params, node)
11,050✔
66
        if err != nil {
11,050✔
67
                return nil, err
×
68
        }
×
69

70
        for index, inferredParamTypes := range paramTypes {
24,548✔
71
                param := functionNode.Params[index]
13,498✔
72
                tc.log.WithFields(logrus.Fields{
13,498✔
73
                        "paramTypes": inferredParamTypes,
13,498✔
74
                        "param":      param.GetIdent(),
13,498✔
75
                        "function":   "inferNodeType",
13,498✔
76
                }).Trace("Storing param variable type")
13,498✔
77

13,498✔
78
                switch p := param.(type) {
13,498✔
79
                case ast.SimpleParamNode:
13,497✔
80
                        tc.scopeStack.currentScope().RegisterSymbol(
13,497✔
81
                                p.Ident.ID,
13,497✔
82
                                inferredParamTypes,
13,497✔
83
                                SymbolVariable)
13,497✔
84
                        if len(inferredParamTypes) > 0 {
26,994✔
85
                                tc.VariableTypes[p.Ident.ID] = append([]ast.TypeNode(nil), inferredParamTypes...)
13,497✔
86
                        }
13,497✔
87
                case ast.DestructuredParamNode:
1✔
88
                        if shapeFields, ok := tc.ShapeFieldsFromParamType(p.Type); ok {
2✔
89
                                for _, fieldName := range p.Fields {
3✔
90
                                        sf, ok := shapeFields[fieldName]
2✔
91
                                        if !ok {
2✔
92
                                                continue
×
93
                                        }
94
                                        if tn, ok := ShapeFieldTypeNode(sf); ok {
4✔
95
                                                fieldTypes := []ast.TypeNode{tn}
2✔
96
                                                tc.scopeStack.currentScope().RegisterSymbol(
2✔
97
                                                        ast.Identifier(fieldName),
2✔
98
                                                        fieldTypes,
2✔
99
                                                        SymbolVariable)
2✔
100
                                                tc.VariableTypes[ast.Identifier(fieldName)] = append([]ast.TypeNode(nil), fieldTypes...)
2✔
101
                                        }
2✔
102
                                }
103
                        }
104
                }
105
        }
106

107
        if signature, ok := tc.Functions[functionNode.Ident.ID]; ok {
22,065✔
108
                for index := range signature.Parameters {
24,488✔
109
                        if index < len(paramTypes) && len(paramTypes[index]) >= 1 {
26,946✔
110
                                signature.Parameters[index].Type = paramTypes[index][0]
13,473✔
111
                        }
13,473✔
112
                }
113
        }
114

115
        for _, bodyNode := range functionNode.Body {
47,657✔
116
                if _, err := tc.inferNodeType(bodyNode); err != nil {
36,644✔
117
                        return nil, err
37✔
118
                }
37✔
119
        }
120

121
        inferredType, err := tc.inferFunctionReturnType(functionNode)
11,013✔
122
        if err != nil {
11,014✔
123
                return nil, err
1✔
124
        }
1✔
125
        tc.storeInferredFunctionReturnType(&functionNode, inferredType)
11,012✔
126
        if err := tc.validateInferredReceiverMethodReturn(functionNode, inferredType); err != nil {
11,013✔
127
                return nil, err
1✔
128
        }
1✔
129
        tc.popScope()
11,011✔
130

11,011✔
131
        tc.log.WithFields(logrus.Fields{
11,011✔
132
                "function": "inferNodeType",
11,011✔
133
                "fn":       functionNode.Ident.ID,
11,011✔
134
                "phase":    "EXIT",
11,011✔
135
        }).Debug("Function node type inference")
11,011✔
136

11,011✔
137
        return inferredType, nil
11,011✔
138
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc