goscript 0.0.60 → 0.0.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/analysis.go +444 -360
- package/compiler/compiler.go +10 -4
- package/compiler/decl.go +47 -87
- package/compiler/expr-call-helpers.go +182 -0
- package/compiler/expr-call.go +9 -30
- package/compiler/expr-selector.go +38 -1
- package/compiler/expr-type.go +37 -1
- package/compiler/expr.go +0 -28
- package/compiler/spec-struct.go +12 -1
- package/compiler/spec.go +21 -1
- package/compiler/stmt.go +2 -10
- package/compiler/type-utils.go +0 -15
- package/dist/gs/builtin/slice.js +6 -7
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +5 -0
- package/dist/gs/builtin/type.js +7 -0
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/internal/byteorder/index.d.ts +6 -0
- package/dist/gs/internal/byteorder/index.js +34 -0
- package/dist/gs/internal/byteorder/index.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +3 -3
- package/dist/gs/reflect/index.js +2 -2
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +9 -0
- package/dist/gs/reflect/type.js +73 -0
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/slices/slices.d.ts +11 -0
- package/dist/gs/slices/slices.js +33 -0
- package/dist/gs/slices/slices.js.map +1 -1
- package/go.sum +0 -8
- package/gs/builtin/slice.ts +6 -7
- package/gs/builtin/type.ts +8 -0
- package/gs/internal/byteorder/index.ts +40 -0
- package/gs/reflect/index.ts +3 -1
- package/gs/reflect/type.ts +97 -0
- package/gs/slices/slices.ts +37 -0
- package/gs/sync/meta.json +1 -1
- package/package.json +1 -1
package/compiler/analysis.go
CHANGED
|
@@ -6,6 +6,7 @@ import (
|
|
|
6
6
|
"go/token"
|
|
7
7
|
"go/types"
|
|
8
8
|
"path/filepath"
|
|
9
|
+
"slices"
|
|
9
10
|
"strings"
|
|
10
11
|
|
|
11
12
|
"github.com/aperturerobotics/goscript"
|
|
@@ -106,9 +107,8 @@ type MethodKey struct {
|
|
|
106
107
|
|
|
107
108
|
// ImplementationInfo tracks information about a struct that implements an interface method
|
|
108
109
|
type ImplementationInfo struct {
|
|
109
|
-
StructType
|
|
110
|
-
Method
|
|
111
|
-
IsAsyncByFlow bool // Whether this implementation is async based on control flow analysis
|
|
110
|
+
StructType *types.Named // The struct type that implements the interface
|
|
111
|
+
Method *types.Func // The method object
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
// Analysis holds information gathered during the analysis phase of the Go code compilation.
|
|
@@ -153,10 +153,6 @@ type Analysis struct {
|
|
|
153
153
|
// This is used to determine interface method async status based on implementations
|
|
154
154
|
InterfaceImplementations map[InterfaceMethodKey][]ImplementationInfo
|
|
155
155
|
|
|
156
|
-
// InterfaceMethodAsyncStatus caches the async status determination for interface methods
|
|
157
|
-
// This is computed once during analysis and reused during code generation
|
|
158
|
-
InterfaceMethodAsyncStatus map[InterfaceMethodKey]bool
|
|
159
|
-
|
|
160
156
|
// MethodAsyncStatus stores the async status of all methods analyzed
|
|
161
157
|
// This is computed once during analysis and reused during code generation
|
|
162
158
|
MethodAsyncStatus map[MethodKey]bool
|
|
@@ -196,11 +192,10 @@ func NewAnalysis(allPackages map[string]*packages.Package) *Analysis {
|
|
|
196
192
|
ReflectedFunctions: make(map[ast.Node]*ReflectedFunctionInfo),
|
|
197
193
|
FunctionAssignments: make(map[types.Object]ast.Node),
|
|
198
194
|
// PackageMetadata removed - using MethodAsyncStatus only
|
|
199
|
-
NamedBasicTypes:
|
|
200
|
-
AllPackages:
|
|
201
|
-
InterfaceImplementations:
|
|
202
|
-
|
|
203
|
-
MethodAsyncStatus: make(map[MethodKey]bool),
|
|
195
|
+
NamedBasicTypes: make(map[types.Type]bool),
|
|
196
|
+
AllPackages: allPackages,
|
|
197
|
+
InterfaceImplementations: make(map[InterfaceMethodKey][]ImplementationInfo),
|
|
198
|
+
MethodAsyncStatus: make(map[MethodKey]bool),
|
|
204
199
|
}
|
|
205
200
|
}
|
|
206
201
|
|
|
@@ -236,6 +231,22 @@ func (a *Analysis) ensureFunctionData(obj types.Object) *FunctionInfo {
|
|
|
236
231
|
return a.FunctionData[obj]
|
|
237
232
|
}
|
|
238
233
|
|
|
234
|
+
// GetFunctionInfoFromContext returns FunctionInfo based on the enclosing function context
|
|
235
|
+
func (a *Analysis) GetFunctionInfoFromContext(nodeInfo *NodeInfo, pkg *packages.Package) *FunctionInfo {
|
|
236
|
+
if nodeInfo == nil {
|
|
237
|
+
return nil
|
|
238
|
+
}
|
|
239
|
+
if nodeInfo.EnclosingFuncDecl != nil {
|
|
240
|
+
if obj := pkg.TypesInfo.ObjectOf(nodeInfo.EnclosingFuncDecl.Name); obj != nil {
|
|
241
|
+
return a.FunctionData[obj]
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if nodeInfo.EnclosingFuncLit != nil {
|
|
245
|
+
return a.FuncLitData[nodeInfo.EnclosingFuncLit]
|
|
246
|
+
}
|
|
247
|
+
return nil
|
|
248
|
+
}
|
|
249
|
+
|
|
239
250
|
// NeedsDefer returns whether the given node needs defer handling.
|
|
240
251
|
func (a *Analysis) NeedsDefer(node ast.Node) bool {
|
|
241
252
|
if node == nil {
|
|
@@ -370,7 +381,7 @@ func (a *Analysis) NeedsVarRefAccess(obj types.Object) bool {
|
|
|
370
381
|
}
|
|
371
382
|
|
|
372
383
|
// For pointer variables, check if they point to a variable-referenced value
|
|
373
|
-
if
|
|
384
|
+
if _, ok := obj.Type().(*types.Pointer); ok {
|
|
374
385
|
// Check all assignments to this pointer variable
|
|
375
386
|
for varObj, info := range a.VariableUsage {
|
|
376
387
|
if varObj == obj {
|
|
@@ -382,11 +393,6 @@ func (a *Analysis) NeedsVarRefAccess(obj types.Object) bool {
|
|
|
382
393
|
}
|
|
383
394
|
}
|
|
384
395
|
}
|
|
385
|
-
|
|
386
|
-
// Handle direct pointer initialization like: var p *int = &x
|
|
387
|
-
// Check if the pointer type's element type requires variable referencing
|
|
388
|
-
_ = ptrType.Elem()
|
|
389
|
-
// For now, conservatively return false for untracked cases
|
|
390
396
|
}
|
|
391
397
|
|
|
392
398
|
return false
|
|
@@ -453,9 +459,6 @@ type analysisVisitor struct {
|
|
|
453
459
|
|
|
454
460
|
// currentFuncLit tracks the *ast.FuncLit of the function literal we're currently analyzing.
|
|
455
461
|
currentFuncLit *ast.FuncLit
|
|
456
|
-
|
|
457
|
-
// visitingMethods tracks methods currently being analyzed to prevent infinite recursion
|
|
458
|
-
visitingMethods map[MethodKey]bool
|
|
459
462
|
}
|
|
460
463
|
|
|
461
464
|
// getOrCreateUsageInfo retrieves or creates the VariableUsageInfo for a given object.
|
|
@@ -922,18 +925,8 @@ func (v *analysisVisitor) visitReturnStmt(n *ast.ReturnStmt) ast.Visitor {
|
|
|
922
925
|
|
|
923
926
|
// Check if it's a bare return
|
|
924
927
|
if len(n.Results) == 0 {
|
|
925
|
-
if v.
|
|
926
|
-
|
|
927
|
-
if obj := v.pkg.TypesInfo.ObjectOf(v.currentFuncDecl.Name); obj != nil {
|
|
928
|
-
if _, ok := v.analysis.FunctionData[obj]; ok {
|
|
929
|
-
nodeInfo.IsBareReturn = true
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
} else if v.currentFuncLit != nil {
|
|
933
|
-
// Check if the enclosing function literal has named returns
|
|
934
|
-
if _, ok := v.analysis.FuncLitData[v.currentFuncLit]; ok {
|
|
935
|
-
nodeInfo.IsBareReturn = true
|
|
936
|
-
}
|
|
928
|
+
if v.analysis.GetFunctionInfoFromContext(nodeInfo, v.pkg) != nil {
|
|
929
|
+
nodeInfo.IsBareReturn = true
|
|
937
930
|
}
|
|
938
931
|
}
|
|
939
932
|
return v
|
|
@@ -1011,14 +1004,8 @@ func (v *analysisVisitor) visitTypeAssertExpr(typeAssert *ast.TypeAssertExpr) as
|
|
|
1011
1004
|
// Find the corresponding method in the struct type
|
|
1012
1005
|
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
1013
1006
|
if structMethod != nil {
|
|
1014
|
-
// Determine if this struct method is async using unified system
|
|
1015
|
-
isAsync := false
|
|
1016
|
-
if obj := structMethod; obj != nil {
|
|
1017
|
-
isAsync = v.analysis.IsAsyncFunc(obj)
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
1007
|
// Track this interface implementation
|
|
1021
|
-
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod
|
|
1008
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod)
|
|
1022
1009
|
}
|
|
1023
1010
|
}
|
|
1024
1011
|
return v
|
|
@@ -1182,9 +1169,8 @@ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages
|
|
|
1182
1169
|
|
|
1183
1170
|
// Create visitor for the entire package
|
|
1184
1171
|
visitor := &analysisVisitor{
|
|
1185
|
-
analysis:
|
|
1186
|
-
pkg:
|
|
1187
|
-
visitingMethods: make(map[MethodKey]bool),
|
|
1172
|
+
analysis: analysis,
|
|
1173
|
+
pkg: pkg,
|
|
1188
1174
|
}
|
|
1189
1175
|
|
|
1190
1176
|
// First pass: analyze all declarations and statements across all files
|
|
@@ -1276,13 +1262,7 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
|
|
|
1276
1262
|
|
|
1277
1263
|
// Check if this function is defined in the current file
|
|
1278
1264
|
currentFileFuncs := analysis.FunctionDefs[baseFileName]
|
|
1279
|
-
isDefinedInCurrentFile :=
|
|
1280
|
-
for _, f := range currentFileFuncs {
|
|
1281
|
-
if f == funcName {
|
|
1282
|
-
isDefinedInCurrentFile = true
|
|
1283
|
-
break
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1265
|
+
isDefinedInCurrentFile := slices.Contains(currentFileFuncs, funcName)
|
|
1286
1266
|
|
|
1287
1267
|
// If not defined in current file, find which file defines it
|
|
1288
1268
|
if !isDefinedInCurrentFile {
|
|
@@ -1290,24 +1270,15 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
|
|
|
1290
1270
|
if sourceFile == baseFileName {
|
|
1291
1271
|
continue // Skip current file
|
|
1292
1272
|
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
if existing == funcName {
|
|
1303
|
-
found = true
|
|
1304
|
-
break
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
if !found {
|
|
1308
|
-
callsFromOtherFiles[sourceFile] = append(callsFromOtherFiles[sourceFile], funcName)
|
|
1309
|
-
}
|
|
1310
|
-
break
|
|
1273
|
+
if slices.Contains(funcs, funcName) {
|
|
1274
|
+
// Found the function in another file
|
|
1275
|
+
if callsFromOtherFiles[sourceFile] == nil {
|
|
1276
|
+
callsFromOtherFiles[sourceFile] = []string{}
|
|
1277
|
+
}
|
|
1278
|
+
// Check if already added to avoid duplicates
|
|
1279
|
+
found := slices.Contains(callsFromOtherFiles[sourceFile], funcName)
|
|
1280
|
+
if !found {
|
|
1281
|
+
callsFromOtherFiles[sourceFile] = append(callsFromOtherFiles[sourceFile], funcName)
|
|
1311
1282
|
}
|
|
1312
1283
|
}
|
|
1313
1284
|
}
|
|
@@ -1340,13 +1311,7 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
|
|
|
1340
1311
|
|
|
1341
1312
|
// Check if this type is defined in the current file
|
|
1342
1313
|
currentFileTypes := analysis.TypeDefs[baseFileName]
|
|
1343
|
-
isDefinedInCurrentFile :=
|
|
1344
|
-
for _, t := range currentFileTypes {
|
|
1345
|
-
if t == typeName {
|
|
1346
|
-
isDefinedInCurrentFile = true
|
|
1347
|
-
break
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1314
|
+
isDefinedInCurrentFile := slices.Contains(currentFileTypes, typeName)
|
|
1350
1315
|
|
|
1351
1316
|
// If not defined in current file, find which file defines it
|
|
1352
1317
|
if !isDefinedInCurrentFile {
|
|
@@ -1354,24 +1319,15 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
|
|
|
1354
1319
|
if sourceFile == baseFileName {
|
|
1355
1320
|
continue // Skip current file
|
|
1356
1321
|
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
if existing == typeName {
|
|
1367
|
-
found = true
|
|
1368
|
-
break
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
if !found {
|
|
1372
|
-
typeRefsFromOtherFiles[sourceFile] = append(typeRefsFromOtherFiles[sourceFile], typeName)
|
|
1373
|
-
}
|
|
1374
|
-
break
|
|
1322
|
+
if slices.Contains(types, typeName) {
|
|
1323
|
+
// Found the type in another file
|
|
1324
|
+
if typeRefsFromOtherFiles[sourceFile] == nil {
|
|
1325
|
+
typeRefsFromOtherFiles[sourceFile] = []string{}
|
|
1326
|
+
}
|
|
1327
|
+
// Check if already added to avoid duplicates
|
|
1328
|
+
found := slices.Contains(typeRefsFromOtherFiles[sourceFile], typeName)
|
|
1329
|
+
if !found {
|
|
1330
|
+
typeRefsFromOtherFiles[sourceFile] = append(typeRefsFromOtherFiles[sourceFile], typeName)
|
|
1375
1331
|
}
|
|
1376
1332
|
}
|
|
1377
1333
|
}
|
|
@@ -1695,16 +1651,15 @@ func (v *analysisVisitor) findVariableUsageInExpr(expr ast.Expr, lhsVarNames map
|
|
|
1695
1651
|
}
|
|
1696
1652
|
|
|
1697
1653
|
// trackInterfaceImplementation records that a struct type implements an interface method
|
|
1698
|
-
func (a *Analysis) trackInterfaceImplementation(interfaceType *types.Interface, structType *types.Named, method *types.Func
|
|
1654
|
+
func (a *Analysis) trackInterfaceImplementation(interfaceType *types.Interface, structType *types.Named, method *types.Func) {
|
|
1699
1655
|
key := InterfaceMethodKey{
|
|
1700
1656
|
InterfaceType: interfaceType.String(),
|
|
1701
1657
|
MethodName: method.Name(),
|
|
1702
1658
|
}
|
|
1703
1659
|
|
|
1704
1660
|
implementation := ImplementationInfo{
|
|
1705
|
-
StructType:
|
|
1706
|
-
Method:
|
|
1707
|
-
IsAsyncByFlow: isAsync,
|
|
1661
|
+
StructType: structType,
|
|
1662
|
+
Method: method,
|
|
1708
1663
|
}
|
|
1709
1664
|
|
|
1710
1665
|
a.InterfaceImplementations[key] = append(a.InterfaceImplementations[key], implementation)
|
|
@@ -1717,49 +1672,25 @@ func (a *Analysis) IsInterfaceMethodAsync(interfaceType *types.Interface, method
|
|
|
1717
1672
|
MethodName: methodName,
|
|
1718
1673
|
}
|
|
1719
1674
|
|
|
1720
|
-
// Check if we've already computed this
|
|
1721
|
-
if result, exists := a.InterfaceMethodAsyncStatus[key]; exists {
|
|
1722
|
-
return result
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
1675
|
// Find all implementations of this interface method
|
|
1726
1676
|
implementations, exists := a.InterfaceImplementations[key]
|
|
1727
1677
|
if !exists {
|
|
1728
|
-
// No implementations found, default to sync
|
|
1729
|
-
a.InterfaceMethodAsyncStatus[key] = false
|
|
1730
1678
|
return false
|
|
1731
1679
|
}
|
|
1732
1680
|
|
|
1733
|
-
//
|
|
1734
|
-
for
|
|
1735
|
-
impl := &implementations[i]
|
|
1736
|
-
|
|
1737
|
-
// Create method key for this implementation
|
|
1681
|
+
// If ANY implementation is async, the interface method is async
|
|
1682
|
+
for _, impl := range implementations {
|
|
1738
1683
|
methodKey := MethodKey{
|
|
1739
1684
|
PackagePath: impl.StructType.Obj().Pkg().Path(),
|
|
1740
1685
|
ReceiverType: impl.StructType.Obj().Name(),
|
|
1741
1686
|
MethodName: impl.Method.Name(),
|
|
1742
1687
|
}
|
|
1743
1688
|
|
|
1744
|
-
|
|
1745
|
-
if isAsync, exists := a.MethodAsyncStatus[methodKey]; exists {
|
|
1746
|
-
impl.IsAsyncByFlow = isAsync
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
// Store the updated implementations back to the map
|
|
1751
|
-
a.InterfaceImplementations[key] = implementations
|
|
1752
|
-
|
|
1753
|
-
// If ANY implementation is async, the interface method is async
|
|
1754
|
-
for _, impl := range implementations {
|
|
1755
|
-
if impl.IsAsyncByFlow {
|
|
1756
|
-
a.InterfaceMethodAsyncStatus[key] = true
|
|
1689
|
+
if isAsync, exists := a.MethodAsyncStatus[methodKey]; exists && isAsync {
|
|
1757
1690
|
return true
|
|
1758
1691
|
}
|
|
1759
1692
|
}
|
|
1760
1693
|
|
|
1761
|
-
// All implementations are sync
|
|
1762
|
-
a.InterfaceMethodAsyncStatus[key] = false
|
|
1763
1694
|
return false
|
|
1764
1695
|
}
|
|
1765
1696
|
|
|
@@ -1844,56 +1775,6 @@ func (a *Analysis) GetIdentifierMapping(ident *ast.Ident) string {
|
|
|
1844
1775
|
return ""
|
|
1845
1776
|
}
|
|
1846
1777
|
|
|
1847
|
-
// trackTypeAssertion analyzes type assertions and records interface implementations
|
|
1848
|
-
func (v *analysisVisitor) trackTypeAssertion(typeAssert *ast.TypeAssertExpr) {
|
|
1849
|
-
// Get the type being asserted to
|
|
1850
|
-
assertedType := v.pkg.TypesInfo.TypeOf(typeAssert.Type)
|
|
1851
|
-
if assertedType == nil {
|
|
1852
|
-
return
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
|
-
// Check if the asserted type is an interface
|
|
1856
|
-
interfaceType, isInterface := assertedType.Underlying().(*types.Interface)
|
|
1857
|
-
if !isInterface {
|
|
1858
|
-
return
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
// Get the type of the expression being asserted
|
|
1862
|
-
exprType := v.pkg.TypesInfo.TypeOf(typeAssert.X)
|
|
1863
|
-
if exprType == nil {
|
|
1864
|
-
return
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
|
-
// Handle pointer types by getting the element type
|
|
1868
|
-
if ptrType, isPtr := exprType.(*types.Pointer); isPtr {
|
|
1869
|
-
exprType = ptrType.Elem()
|
|
1870
|
-
}
|
|
1871
|
-
|
|
1872
|
-
// Check if the expression type is a named struct type
|
|
1873
|
-
namedType, isNamed := exprType.(*types.Named)
|
|
1874
|
-
if !isNamed {
|
|
1875
|
-
return
|
|
1876
|
-
}
|
|
1877
|
-
|
|
1878
|
-
// For each method in the interface, check if the struct implements it
|
|
1879
|
-
for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
|
|
1880
|
-
interfaceMethod := interfaceType.ExplicitMethod(i)
|
|
1881
|
-
|
|
1882
|
-
// Find the corresponding method in the struct type
|
|
1883
|
-
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
1884
|
-
if structMethod != nil {
|
|
1885
|
-
// Determine if this struct method is async using unified system
|
|
1886
|
-
isAsync := false
|
|
1887
|
-
if obj := structMethod; obj != nil {
|
|
1888
|
-
isAsync = v.analysis.IsAsyncFunc(obj)
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
|
-
// Track this interface implementation
|
|
1892
|
-
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
1893
|
-
}
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1897
1778
|
// findStructMethod finds a method with the given name on a named type
|
|
1898
1779
|
func (v *analysisVisitor) findStructMethod(namedType *types.Named, methodName string) *types.Func {
|
|
1899
1780
|
// Check methods directly on the type
|
|
@@ -2006,10 +1887,7 @@ func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt)
|
|
|
2006
1887
|
|
|
2007
1888
|
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
2008
1889
|
if structMethod != nil {
|
|
2009
|
-
|
|
2010
|
-
isAsync := v.analysis.IsAsyncFunc(structMethod)
|
|
2011
|
-
|
|
2012
|
-
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
1890
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod)
|
|
2013
1891
|
}
|
|
2014
1892
|
}
|
|
2015
1893
|
}
|
|
@@ -2068,9 +1946,7 @@ func (v *analysisVisitor) trackInterfaceCallArguments(callExpr *ast.CallExpr) {
|
|
|
2068
1946
|
|
|
2069
1947
|
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
2070
1948
|
if structMethod != nil {
|
|
2071
|
-
|
|
2072
|
-
// For now, just track the implementation relationship without async status
|
|
2073
|
-
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, false)
|
|
1949
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod)
|
|
2074
1950
|
}
|
|
2075
1951
|
}
|
|
2076
1952
|
}
|
|
@@ -2216,10 +2092,7 @@ func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *type
|
|
|
2216
2092
|
// Find the method in the implementing type
|
|
2217
2093
|
structMethod := v.findMethodInType(namedType, interfaceMethod.Name())
|
|
2218
2094
|
if structMethod != nil {
|
|
2219
|
-
|
|
2220
|
-
isAsync := v.analysis.IsAsyncFunc(structMethod)
|
|
2221
|
-
|
|
2222
|
-
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
2095
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod)
|
|
2223
2096
|
}
|
|
2224
2097
|
}
|
|
2225
2098
|
}
|
|
@@ -2235,88 +2108,77 @@ func (v *interfaceImplementationVisitor) findMethodInType(namedType *types.Named
|
|
|
2235
2108
|
return nil
|
|
2236
2109
|
}
|
|
2237
2110
|
|
|
2238
|
-
//
|
|
2239
|
-
func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
|
|
2240
|
-
var namedReturns []string
|
|
2241
|
-
if funcDecl.Type != nil && funcDecl.Type.Results != nil {
|
|
2242
|
-
for _, field := range funcDecl.Type.Results.List {
|
|
2243
|
-
for _, name := range field.Names {
|
|
2244
|
-
namedReturns = append(namedReturns, name.Name)
|
|
2245
|
-
}
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
|
-
return namedReturns
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
|
-
// analyzeAllMethodsAsync performs comprehensive async analysis on all methods in all packages
|
|
2111
|
+
// analyzeAllMethodsAsync performs comprehensive async analysis on all methods in all packages using topological sort
|
|
2252
2112
|
func (v *analysisVisitor) analyzeAllMethodsAsync() {
|
|
2253
|
-
//
|
|
2254
|
-
v.
|
|
2255
|
-
|
|
2256
|
-
//
|
|
2257
|
-
|
|
2113
|
+
// Build the method call graph for all packages
|
|
2114
|
+
methodCalls := v.buildMethodCallGraph()
|
|
2115
|
+
|
|
2116
|
+
// Topologically sort methods by their dependencies
|
|
2117
|
+
sorted, cycles := v.topologicalSortMethods(methodCalls)
|
|
2118
|
+
|
|
2119
|
+
// Mark methods in cycles - check if they contain async operations
|
|
2120
|
+
// We can't rely on the call graph for methods in cycles (circular dependency),
|
|
2121
|
+
// but we can still detect if they call async external methods
|
|
2122
|
+
//
|
|
2123
|
+
// We need to iterate multiple times because methods in cycles can call each other,
|
|
2124
|
+
// and we need to propagate async status until no changes occur
|
|
2258
2125
|
maxIterations := 10
|
|
2259
2126
|
for iteration := 0; iteration < maxIterations; iteration++ {
|
|
2260
|
-
// Clear visitingMethods map for this iteration
|
|
2261
|
-
v.visitingMethods = make(map[MethodKey]bool)
|
|
2262
|
-
|
|
2263
|
-
// Track if anything changed in this iteration
|
|
2264
2127
|
changed := false
|
|
2265
2128
|
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2129
|
+
for _, methodKey := range cycles {
|
|
2130
|
+
// For methods in cycles, we need to check their body directly for async operations
|
|
2131
|
+
pkg := v.analysis.AllPackages[methodKey.PackagePath]
|
|
2132
|
+
if pkg == nil && methodKey.PackagePath == v.pkg.Types.Path() {
|
|
2133
|
+
pkg = v.pkg
|
|
2134
|
+
}
|
|
2271
2135
|
|
|
2272
|
-
|
|
2273
|
-
|
|
2136
|
+
if pkg != nil {
|
|
2137
|
+
var funcDecl *ast.FuncDecl
|
|
2138
|
+
if methodKey.ReceiverType == "" {
|
|
2139
|
+
funcDecl = v.findFunctionDecl(methodKey.MethodName, pkg)
|
|
2140
|
+
} else {
|
|
2141
|
+
funcDecl = v.findMethodDecl(methodKey.ReceiverType, methodKey.MethodName, pkg)
|
|
2142
|
+
}
|
|
2274
2143
|
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
v.analyzePackageMethodsAsync(pkg)
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2144
|
+
if funcDecl != nil && funcDecl.Body != nil {
|
|
2145
|
+
// Check if the method contains async operations (including calls to async external methods)
|
|
2146
|
+
isAsync := v.containsAsyncOperationsComplete(funcDecl.Body, pkg)
|
|
2281
2147
|
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2148
|
+
// Check if status changed
|
|
2149
|
+
if oldStatus, exists := v.analysis.MethodAsyncStatus[methodKey]; !exists || oldStatus != isAsync {
|
|
2150
|
+
changed = true
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
v.analysis.MethodAsyncStatus[methodKey] = isAsync
|
|
2154
|
+
continue
|
|
2289
2155
|
}
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
// Fallback: mark as sync if we can't analyze the body
|
|
2159
|
+
if _, exists := v.analysis.MethodAsyncStatus[methodKey]; !exists {
|
|
2160
|
+
v.analysis.MethodAsyncStatus[methodKey] = false
|
|
2292
2161
|
changed = true
|
|
2293
2162
|
}
|
|
2294
2163
|
}
|
|
2295
2164
|
|
|
2296
|
-
// If
|
|
2165
|
+
// If no changes in this iteration, we're done
|
|
2297
2166
|
if !changed {
|
|
2298
2167
|
break
|
|
2299
2168
|
}
|
|
2300
2169
|
}
|
|
2301
2170
|
|
|
2171
|
+
// Analyze methods in dependency order (dependencies analyzed before dependents)
|
|
2172
|
+
for _, methodKey := range sorted {
|
|
2173
|
+
v.analyzeMethodAsyncTopological(methodKey, methodCalls[methodKey])
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2302
2176
|
// Finally, analyze function literals in the current package only
|
|
2303
2177
|
// (external packages' function literals are not accessible)
|
|
2304
2178
|
v.analyzeFunctionLiteralsAsync(v.pkg)
|
|
2305
2179
|
}
|
|
2306
2180
|
|
|
2307
|
-
//
|
|
2308
|
-
func (v *analysisVisitor) analyzePackageMethodsAsync(pkg *packages.Package) {
|
|
2309
|
-
// Analyze function declarations
|
|
2310
|
-
for _, file := range pkg.Syntax {
|
|
2311
|
-
for _, decl := range file.Decls {
|
|
2312
|
-
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
|
|
2313
|
-
v.analyzeMethodAsync(funcDecl, pkg)
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
}
|
|
2317
|
-
}
|
|
2318
|
-
|
|
2319
|
-
// analyzeFunctionLiteralsAsync analyzes all function literals in a package for async operations
|
|
2181
|
+
// buildMethodCallGraph builds a graph of which methods call which other methods
|
|
2320
2182
|
func (v *analysisVisitor) analyzeFunctionLiteralsAsync(pkg *packages.Package) {
|
|
2321
2183
|
for _, file := range pkg.Syntax {
|
|
2322
2184
|
ast.Inspect(file, func(n ast.Node) bool {
|
|
@@ -2350,55 +2212,320 @@ func (v *analysisVisitor) analyzeFunctionLiteralAsync(funcLit *ast.FuncLit, pkg
|
|
|
2350
2212
|
nodeInfo.InAsyncContext = isAsync
|
|
2351
2213
|
}
|
|
2352
2214
|
|
|
2353
|
-
//
|
|
2354
|
-
func (v *analysisVisitor)
|
|
2355
|
-
|
|
2215
|
+
// buildMethodCallGraph builds a graph of which methods call which other methods
|
|
2216
|
+
func (v *analysisVisitor) buildMethodCallGraph() map[MethodKey][]MethodKey {
|
|
2217
|
+
methodCalls := make(map[MethodKey][]MethodKey)
|
|
2356
2218
|
|
|
2357
|
-
//
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
v.
|
|
2361
|
-
|
|
2219
|
+
// Iterate through all packages
|
|
2220
|
+
allPkgs := []*packages.Package{v.pkg}
|
|
2221
|
+
for _, pkg := range v.analysis.AllPackages {
|
|
2222
|
+
if pkg != v.pkg {
|
|
2223
|
+
allPkgs = append(allPkgs, pkg)
|
|
2224
|
+
}
|
|
2362
2225
|
}
|
|
2363
2226
|
|
|
2364
|
-
|
|
2365
|
-
|
|
2227
|
+
for _, pkg := range allPkgs {
|
|
2228
|
+
for _, file := range pkg.Syntax {
|
|
2229
|
+
for _, decl := range file.Decls {
|
|
2230
|
+
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
|
|
2231
|
+
methodKey := v.getMethodKey(funcDecl, pkg)
|
|
2366
2232
|
|
|
2367
|
-
|
|
2368
|
-
|
|
2233
|
+
// Initialize the entry for this method
|
|
2234
|
+
if _, exists := methodCalls[methodKey]; !exists {
|
|
2235
|
+
methodCalls[methodKey] = []MethodKey{}
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
// Extract method calls from the function body
|
|
2239
|
+
if funcDecl.Body != nil {
|
|
2240
|
+
callees := v.extractMethodCalls(funcDecl.Body, pkg)
|
|
2241
|
+
methodCalls[methodKey] = callees
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
return methodCalls
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
// extractMethodCalls extracts all method and function calls from a node
|
|
2252
|
+
func (v *analysisVisitor) extractMethodCalls(node ast.Node, pkg *packages.Package) []MethodKey {
|
|
2253
|
+
var calls []MethodKey
|
|
2254
|
+
seen := make(map[MethodKey]bool)
|
|
2255
|
+
|
|
2256
|
+
ast.Inspect(node, func(n ast.Node) bool {
|
|
2257
|
+
if n == nil {
|
|
2258
|
+
return false
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
if callExpr, ok := n.(*ast.CallExpr); ok {
|
|
2262
|
+
methodKeys := v.extractMethodKeysFromCall(callExpr, pkg)
|
|
2263
|
+
for _, methodKey := range methodKeys {
|
|
2264
|
+
if !seen[methodKey] {
|
|
2265
|
+
seen[methodKey] = true
|
|
2266
|
+
calls = append(calls, methodKey)
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
return true
|
|
2272
|
+
})
|
|
2369
2273
|
|
|
2370
|
-
|
|
2371
|
-
|
|
2274
|
+
return calls
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
// extractMethodKeysFromCall extracts MethodKeys from a call expression
|
|
2278
|
+
// Returns multiple keys for interface method calls (one for each implementation)
|
|
2279
|
+
func (v *analysisVisitor) extractMethodKeysFromCall(callExpr *ast.CallExpr, pkg *packages.Package) []MethodKey {
|
|
2280
|
+
singleKey := v.extractMethodKeyFromCall(callExpr, pkg)
|
|
2281
|
+
if singleKey != nil {
|
|
2282
|
+
return []MethodKey{*singleKey}
|
|
2283
|
+
}
|
|
2284
|
+
|
|
2285
|
+
// Check if this is an interface method call
|
|
2286
|
+
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
|
2287
|
+
if receiverType := pkg.TypesInfo.TypeOf(selExpr.X); receiverType != nil {
|
|
2288
|
+
if interfaceType, isInterface := receiverType.Underlying().(*types.Interface); isInterface {
|
|
2289
|
+
methodName := selExpr.Sel.Name
|
|
2290
|
+
// Find all implementations of this interface method
|
|
2291
|
+
key := InterfaceMethodKey{
|
|
2292
|
+
InterfaceType: interfaceType.String(),
|
|
2293
|
+
MethodName: methodName,
|
|
2294
|
+
}
|
|
2295
|
+
if implementations, exists := v.analysis.InterfaceImplementations[key]; exists {
|
|
2296
|
+
var keys []MethodKey
|
|
2297
|
+
for _, impl := range implementations {
|
|
2298
|
+
keys = append(keys, MethodKey{
|
|
2299
|
+
PackagePath: impl.StructType.Obj().Pkg().Path(),
|
|
2300
|
+
ReceiverType: impl.StructType.Obj().Name(),
|
|
2301
|
+
MethodName: impl.Method.Name(),
|
|
2302
|
+
})
|
|
2303
|
+
}
|
|
2304
|
+
return keys
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
return nil
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
// extractMethodKeyFromCall extracts a MethodKey from a call expression
|
|
2314
|
+
func (v *analysisVisitor) extractMethodKeyFromCall(callExpr *ast.CallExpr, pkg *packages.Package) *MethodKey {
|
|
2315
|
+
switch fun := callExpr.Fun.(type) {
|
|
2316
|
+
case *ast.Ident:
|
|
2317
|
+
// Direct function call
|
|
2318
|
+
if obj := pkg.TypesInfo.Uses[fun]; obj != nil {
|
|
2319
|
+
if funcObj, ok := obj.(*types.Func); ok {
|
|
2320
|
+
pkgPath := pkg.Types.Path()
|
|
2321
|
+
if funcObj.Pkg() != nil {
|
|
2322
|
+
pkgPath = funcObj.Pkg().Path()
|
|
2323
|
+
}
|
|
2324
|
+
return &MethodKey{
|
|
2325
|
+
PackagePath: pkgPath,
|
|
2326
|
+
ReceiverType: "",
|
|
2327
|
+
MethodName: funcObj.Name(),
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
case *ast.SelectorExpr:
|
|
2333
|
+
// Package-level function call (e.g., time.Sleep)
|
|
2334
|
+
if ident, ok := fun.X.(*ast.Ident); ok {
|
|
2335
|
+
if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
2336
|
+
if pkgName, isPkg := obj.(*types.PkgName); isPkg {
|
|
2337
|
+
return &MethodKey{
|
|
2338
|
+
PackagePath: pkgName.Imported().Path(),
|
|
2339
|
+
ReceiverType: "",
|
|
2340
|
+
MethodName: fun.Sel.Name,
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
// Check if this is an interface method call - if so, return nil
|
|
2347
|
+
// so extractMethodKeysFromCall can expand it to all implementations
|
|
2348
|
+
if receiverType := pkg.TypesInfo.TypeOf(fun.X); receiverType != nil {
|
|
2349
|
+
if _, isInterface := receiverType.Underlying().(*types.Interface); isInterface {
|
|
2350
|
+
// This is an interface method call - return nil to let
|
|
2351
|
+
// extractMethodKeysFromCall handle expanding to implementations
|
|
2352
|
+
return nil
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
// Method call on concrete objects
|
|
2357
|
+
if selection := pkg.TypesInfo.Selections[fun]; selection != nil {
|
|
2358
|
+
if methodObj := selection.Obj(); methodObj != nil {
|
|
2359
|
+
receiverType := ""
|
|
2360
|
+
methodPkgPath := ""
|
|
2361
|
+
|
|
2362
|
+
// Get receiver type
|
|
2363
|
+
switch x := fun.X.(type) {
|
|
2364
|
+
case *ast.Ident:
|
|
2365
|
+
if obj := pkg.TypesInfo.Uses[x]; obj != nil {
|
|
2366
|
+
if varObj, ok := obj.(*types.Var); ok {
|
|
2367
|
+
receiverType = v.getTypeName(varObj.Type())
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
case *ast.SelectorExpr:
|
|
2371
|
+
if typeExpr := pkg.TypesInfo.TypeOf(x); typeExpr != nil {
|
|
2372
|
+
receiverType = v.getTypeName(typeExpr)
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
// Get method's package path
|
|
2377
|
+
if methodFunc, ok := methodObj.(*types.Func); ok {
|
|
2378
|
+
if methodFunc.Pkg() != nil {
|
|
2379
|
+
methodPkgPath = methodFunc.Pkg().Path()
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
if methodPkgPath == "" {
|
|
2384
|
+
methodPkgPath = pkg.Types.Path()
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
return &MethodKey{
|
|
2388
|
+
PackagePath: methodPkgPath,
|
|
2389
|
+
ReceiverType: receiverType,
|
|
2390
|
+
MethodName: methodObj.Name(),
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
return nil
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
// topologicalSortMethods performs a topological sort of methods based on their call dependencies
|
|
2400
|
+
// Returns sorted methods and methods involved in cycles
|
|
2401
|
+
func (v *analysisVisitor) topologicalSortMethods(methodCalls map[MethodKey][]MethodKey) ([]MethodKey, []MethodKey) {
|
|
2402
|
+
// Kahn's algorithm for topological sorting
|
|
2403
|
+
inDegree := make(map[MethodKey]int)
|
|
2404
|
+
graph := make(map[MethodKey][]MethodKey)
|
|
2405
|
+
|
|
2406
|
+
// Initialize in-degree counts and reverse graph
|
|
2407
|
+
for method := range methodCalls {
|
|
2408
|
+
inDegree[method] = 0
|
|
2409
|
+
graph[method] = []MethodKey{}
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
// Build reverse graph and count in-degrees
|
|
2413
|
+
// graph[dependency] = methods that depend on it
|
|
2414
|
+
for method, callees := range methodCalls {
|
|
2415
|
+
for _, callee := range callees {
|
|
2416
|
+
// Only create dependency edges for callees that exist in the call graph
|
|
2417
|
+
// This automatically excludes handwritten packages that aren't being compiled
|
|
2418
|
+
if _, exists := inDegree[callee]; exists {
|
|
2419
|
+
// Additionally, skip if the callee is from a handwritten package
|
|
2420
|
+
// This prevents our code from being blocked by unresolved handwritten package dependencies
|
|
2421
|
+
if !v.analysis.isHandwrittenPackage(callee.PackagePath) {
|
|
2422
|
+
graph[callee] = append(graph[callee], method)
|
|
2423
|
+
inDegree[method]++
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
// Find methods with no dependencies (in-degree == 0)
|
|
2430
|
+
var queue []MethodKey
|
|
2431
|
+
for method, degree := range inDegree {
|
|
2432
|
+
if degree == 0 {
|
|
2433
|
+
queue = append(queue, method)
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
var sorted []MethodKey
|
|
2438
|
+
|
|
2439
|
+
for len(queue) > 0 {
|
|
2440
|
+
// Remove method from queue
|
|
2441
|
+
current := queue[0]
|
|
2442
|
+
queue = queue[1:]
|
|
2443
|
+
sorted = append(sorted, current)
|
|
2444
|
+
|
|
2445
|
+
// For each method that depends on current
|
|
2446
|
+
for _, dependent := range graph[current] {
|
|
2447
|
+
inDegree[dependent]--
|
|
2448
|
+
if inDegree[dependent] == 0 {
|
|
2449
|
+
queue = append(queue, dependent)
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
// Find methods in cycles (not in sorted list)
|
|
2455
|
+
var cycles []MethodKey
|
|
2456
|
+
if len(sorted) != len(methodCalls) {
|
|
2457
|
+
for method := range methodCalls {
|
|
2458
|
+
found := slices.Contains(sorted, method)
|
|
2459
|
+
if !found {
|
|
2460
|
+
cycles = append(cycles, method)
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
return sorted, cycles
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
// analyzeMethodAsyncTopological analyzes a single method for async operations in topological order
|
|
2469
|
+
func (v *analysisVisitor) analyzeMethodAsyncTopological(methodKey MethodKey, callees []MethodKey) {
|
|
2470
|
+
// Check if method is from handwritten package
|
|
2372
2471
|
isHandwrittenPackage := v.analysis.isHandwrittenPackage(methodKey.PackagePath)
|
|
2373
2472
|
|
|
2374
2473
|
if isHandwrittenPackage {
|
|
2375
2474
|
// For handwritten packages, check if we have pre-loaded metadata
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2475
|
+
_, hasMetadata := v.analysis.MethodAsyncStatus[methodKey]
|
|
2476
|
+
if hasMetadata {
|
|
2477
|
+
// Already set from metadata, don't override
|
|
2478
|
+
return
|
|
2380
2479
|
}
|
|
2381
|
-
|
|
2480
|
+
// No metadata means assume sync
|
|
2481
|
+
v.analysis.MethodAsyncStatus[methodKey] = false
|
|
2482
|
+
return
|
|
2483
|
+
}
|
|
2382
2484
|
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
isAsync = false
|
|
2485
|
+
// Find the method declaration
|
|
2486
|
+
pkg := v.analysis.AllPackages[methodKey.PackagePath]
|
|
2487
|
+
if pkg == nil {
|
|
2488
|
+
if methodKey.PackagePath == v.pkg.Types.Path() {
|
|
2489
|
+
pkg = v.pkg
|
|
2389
2490
|
}
|
|
2390
|
-
} else if funcDecl.Body != nil {
|
|
2391
|
-
// Not a handwritten package and has body: always analyze for async operations
|
|
2392
|
-
// This allows fixed-point iteration to update results
|
|
2393
|
-
isAsync = v.containsAsyncOperationsComplete(funcDecl.Body, pkg)
|
|
2394
2491
|
}
|
|
2395
|
-
// Otherwise leave isAsync as false
|
|
2396
2492
|
|
|
2397
|
-
|
|
2398
|
-
|
|
2493
|
+
if pkg == nil {
|
|
2494
|
+
// Can't find package, assume sync
|
|
2495
|
+
v.analysis.MethodAsyncStatus[methodKey] = false
|
|
2496
|
+
return
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
var funcDecl *ast.FuncDecl
|
|
2500
|
+
if methodKey.ReceiverType == "" {
|
|
2501
|
+
// Package-level function
|
|
2502
|
+
funcDecl = v.findFunctionDecl(methodKey.MethodName, pkg)
|
|
2503
|
+
} else {
|
|
2504
|
+
// Method with receiver
|
|
2505
|
+
funcDecl = v.findMethodDecl(methodKey.ReceiverType, methodKey.MethodName, pkg)
|
|
2506
|
+
}
|
|
2399
2507
|
|
|
2400
|
-
|
|
2401
|
-
|
|
2508
|
+
if funcDecl == nil || funcDecl.Body == nil {
|
|
2509
|
+
// No body to analyze, assume sync
|
|
2510
|
+
v.analysis.MethodAsyncStatus[methodKey] = false
|
|
2511
|
+
return
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
// Check if method contains async operations (including calls to async external methods)
|
|
2515
|
+
isAsync := v.containsAsyncOperationsComplete(funcDecl.Body, pkg)
|
|
2516
|
+
|
|
2517
|
+
// If not directly async, check if any callee from the call graph is async
|
|
2518
|
+
// (This catches calls to other methods in the same codebase)
|
|
2519
|
+
if !isAsync {
|
|
2520
|
+
for _, callee := range callees {
|
|
2521
|
+
if calleeAsync, exists := v.analysis.MethodAsyncStatus[callee]; exists && calleeAsync {
|
|
2522
|
+
isAsync = true
|
|
2523
|
+
break
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
v.analysis.MethodAsyncStatus[methodKey] = isAsync
|
|
2402
2529
|
}
|
|
2403
2530
|
|
|
2404
2531
|
// getMethodKey creates a unique key for a method
|
|
@@ -2408,14 +2535,19 @@ func (v *analysisVisitor) getMethodKey(funcDecl *ast.FuncDecl, pkg *packages.Pac
|
|
|
2408
2535
|
receiverType := ""
|
|
2409
2536
|
|
|
2410
2537
|
if funcDecl.Recv != nil && len(funcDecl.Recv.List) > 0 {
|
|
2411
|
-
//
|
|
2412
|
-
if len(funcDecl.Recv.List[0].Names) > 0 {
|
|
2538
|
+
// Try to get receiver type from TypesInfo first
|
|
2539
|
+
if len(funcDecl.Recv.List[0].Names) > 0 && pkg.TypesInfo != nil {
|
|
2413
2540
|
if def := pkg.TypesInfo.Defs[funcDecl.Recv.List[0].Names[0]]; def != nil {
|
|
2414
2541
|
if vr, ok := def.(*types.Var); ok {
|
|
2415
2542
|
receiverType = v.getTypeName(vr.Type())
|
|
2416
2543
|
}
|
|
2417
2544
|
}
|
|
2418
2545
|
}
|
|
2546
|
+
|
|
2547
|
+
// Fallback to AST if TypesInfo is unavailable or failed
|
|
2548
|
+
if receiverType == "" {
|
|
2549
|
+
receiverType = v.getReceiverTypeFromAST(funcDecl.Recv.List[0].Type)
|
|
2550
|
+
}
|
|
2419
2551
|
}
|
|
2420
2552
|
|
|
2421
2553
|
return MethodKey{
|
|
@@ -2437,6 +2569,23 @@ func (v *analysisVisitor) getTypeName(t types.Type) string {
|
|
|
2437
2569
|
}
|
|
2438
2570
|
}
|
|
2439
2571
|
|
|
2572
|
+
// getReceiverTypeFromAST extracts the receiver type name from AST when TypesInfo is unavailable
|
|
2573
|
+
func (v *analysisVisitor) getReceiverTypeFromAST(expr ast.Expr) string {
|
|
2574
|
+
switch t := expr.(type) {
|
|
2575
|
+
case *ast.StarExpr:
|
|
2576
|
+
// Pointer receiver: *Type
|
|
2577
|
+
return v.getReceiverTypeFromAST(t.X)
|
|
2578
|
+
case *ast.Ident:
|
|
2579
|
+
// Simple type name
|
|
2580
|
+
return t.Name
|
|
2581
|
+
case *ast.SelectorExpr:
|
|
2582
|
+
// Qualified type: pkg.Type
|
|
2583
|
+
return t.Sel.Name
|
|
2584
|
+
default:
|
|
2585
|
+
return ""
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2440
2589
|
// containsAsyncOperationsComplete is a comprehensive async detection that handles method calls
|
|
2441
2590
|
func (v *analysisVisitor) containsAsyncOperationsComplete(node ast.Node, pkg *packages.Package) bool {
|
|
2442
2591
|
var hasAsync bool
|
|
@@ -2489,7 +2638,8 @@ func (v *analysisVisitor) isCallAsync(callExpr *ast.CallExpr, pkg *packages.Pack
|
|
|
2489
2638
|
// Direct function call
|
|
2490
2639
|
if obj := pkg.TypesInfo.Uses[fun]; obj != nil {
|
|
2491
2640
|
if funcObj, ok := obj.(*types.Func); ok {
|
|
2492
|
-
|
|
2641
|
+
result := v.isFunctionAsync(funcObj, pkg)
|
|
2642
|
+
return result
|
|
2493
2643
|
}
|
|
2494
2644
|
}
|
|
2495
2645
|
|
|
@@ -2512,14 +2662,16 @@ func (v *analysisVisitor) isCallAsync(callExpr *ast.CallExpr, pkg *packages.Pack
|
|
|
2512
2662
|
if interfaceType, isInterface := receiverType.Underlying().(*types.Interface); isInterface {
|
|
2513
2663
|
methodName := fun.Sel.Name
|
|
2514
2664
|
// For interface method calls, check if the interface method is async
|
|
2515
|
-
|
|
2665
|
+
result := v.analysis.IsInterfaceMethodAsync(interfaceType, methodName)
|
|
2666
|
+
return result
|
|
2516
2667
|
}
|
|
2517
2668
|
}
|
|
2518
2669
|
|
|
2519
2670
|
// Method call on concrete objects
|
|
2520
2671
|
if selection := pkg.TypesInfo.Selections[fun]; selection != nil {
|
|
2521
2672
|
if methodObj := selection.Obj(); methodObj != nil {
|
|
2522
|
-
|
|
2673
|
+
result := v.isMethodAsyncFromSelection(fun, methodObj, pkg)
|
|
2674
|
+
return result
|
|
2523
2675
|
}
|
|
2524
2676
|
}
|
|
2525
2677
|
}
|
|
@@ -2534,7 +2686,7 @@ func (v *analysisVisitor) isFunctionAsync(funcObj *types.Func, pkg *packages.Pac
|
|
|
2534
2686
|
return v.analysis.IsMethodAsync(funcObj.Pkg().Path(), "", funcObj.Name())
|
|
2535
2687
|
}
|
|
2536
2688
|
|
|
2537
|
-
// Check internal method status
|
|
2689
|
+
// Check internal method status (should already be computed during analysis)
|
|
2538
2690
|
methodKey := MethodKey{
|
|
2539
2691
|
PackagePath: pkg.Types.Path(),
|
|
2540
2692
|
ReceiverType: "",
|
|
@@ -2545,14 +2697,7 @@ func (v *analysisVisitor) isFunctionAsync(funcObj *types.Func, pkg *packages.Pac
|
|
|
2545
2697
|
return status
|
|
2546
2698
|
}
|
|
2547
2699
|
|
|
2548
|
-
// Not analyzed
|
|
2549
|
-
if funcDecl := v.findFunctionDecl(funcObj.Name(), pkg); funcDecl != nil {
|
|
2550
|
-
v.analyzeMethodAsync(funcDecl, pkg)
|
|
2551
|
-
if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|
|
2552
|
-
return status
|
|
2553
|
-
}
|
|
2554
|
-
}
|
|
2555
|
-
|
|
2700
|
+
// Not found - should have been analyzed during analyzeAllMethodsAsync
|
|
2556
2701
|
return false
|
|
2557
2702
|
}
|
|
2558
2703
|
|
|
@@ -2572,7 +2717,12 @@ func (v *analysisVisitor) isMethodAsyncFromSelection(selExpr *ast.SelectorExpr,
|
|
|
2572
2717
|
}
|
|
2573
2718
|
}
|
|
2574
2719
|
case *ast.SelectorExpr:
|
|
2575
|
-
// Field access (e.g., l.m.Lock())
|
|
2720
|
+
// Field access (e.g., l.m.Lock() or d.mu.Lock())
|
|
2721
|
+
if typeExpr := pkg.TypesInfo.TypeOf(x); typeExpr != nil {
|
|
2722
|
+
receiverType = v.getTypeName(typeExpr)
|
|
2723
|
+
}
|
|
2724
|
+
default:
|
|
2725
|
+
// For other cases, try to get type directly
|
|
2576
2726
|
if typeExpr := pkg.TypesInfo.TypeOf(x); typeExpr != nil {
|
|
2577
2727
|
receiverType = v.getTypeName(typeExpr)
|
|
2578
2728
|
}
|
|
@@ -2585,7 +2735,6 @@ func (v *analysisVisitor) isMethodAsyncFromSelection(selExpr *ast.SelectorExpr,
|
|
|
2585
2735
|
}
|
|
2586
2736
|
}
|
|
2587
2737
|
|
|
2588
|
-
// If no package path found, use current package
|
|
2589
2738
|
if methodPkgPath == "" {
|
|
2590
2739
|
methodPkgPath = pkg.Types.Path()
|
|
2591
2740
|
}
|
|
@@ -2602,28 +2751,7 @@ func (v *analysisVisitor) isMethodAsyncFromSelection(selExpr *ast.SelectorExpr,
|
|
|
2602
2751
|
return status
|
|
2603
2752
|
}
|
|
2604
2753
|
|
|
2605
|
-
//
|
|
2606
|
-
if methodPkgPath == v.pkg.Types.Path() {
|
|
2607
|
-
// This is a method in the same package - we should analyze it if we haven't yet
|
|
2608
|
-
if funcDecl := v.findMethodDecl(receiverType, methodObj.Name(), v.pkg); funcDecl != nil {
|
|
2609
|
-
v.analyzeMethodAsync(funcDecl, v.pkg)
|
|
2610
|
-
if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|
|
2611
|
-
return status
|
|
2612
|
-
}
|
|
2613
|
-
}
|
|
2614
|
-
} else {
|
|
2615
|
-
// For methods in other packages that we're compiling together
|
|
2616
|
-
if targetPkg := v.analysis.AllPackages[methodPkgPath]; targetPkg != nil {
|
|
2617
|
-
// Try to analyze the method if we haven't already
|
|
2618
|
-
if funcDecl := v.findMethodDecl(receiverType, methodObj.Name(), targetPkg); funcDecl != nil {
|
|
2619
|
-
v.analyzeMethodAsync(funcDecl, targetPkg)
|
|
2620
|
-
if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|
|
2621
|
-
return status
|
|
2622
|
-
}
|
|
2623
|
-
}
|
|
2624
|
-
}
|
|
2625
|
-
}
|
|
2626
|
-
|
|
2754
|
+
// Not found - should have been analyzed during analyzeAllMethodsAsync
|
|
2627
2755
|
return false
|
|
2628
2756
|
}
|
|
2629
2757
|
|
|
@@ -2663,22 +2791,6 @@ func (v *analysisVisitor) findMethodDecl(receiverType, methodName string, pkg *p
|
|
|
2663
2791
|
return nil
|
|
2664
2792
|
}
|
|
2665
2793
|
|
|
2666
|
-
// checkExternalMethodMetadata checks if an external method is async based on pre-loaded metadata
|
|
2667
|
-
func (v *analysisVisitor) checkExternalMethodMetadata(pkgPath, receiverType, methodName string) bool {
|
|
2668
|
-
// Use MethodKey to check pre-loaded metadata in MethodAsyncStatus
|
|
2669
|
-
key := MethodKey{
|
|
2670
|
-
PackagePath: pkgPath,
|
|
2671
|
-
ReceiverType: receiverType,
|
|
2672
|
-
MethodName: methodName,
|
|
2673
|
-
}
|
|
2674
|
-
|
|
2675
|
-
if isAsync, exists := v.analysis.MethodAsyncStatus[key]; exists {
|
|
2676
|
-
return isAsync
|
|
2677
|
-
}
|
|
2678
|
-
|
|
2679
|
-
return false
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
2794
|
// IsLocalMethodAsync checks if a local method is async using pre-computed analysis
|
|
2683
2795
|
func (a *Analysis) IsLocalMethodAsync(pkgPath, receiverType, methodName string) bool {
|
|
2684
2796
|
methodKey := MethodKey{
|
|
@@ -2693,31 +2805,3 @@ func (a *Analysis) IsLocalMethodAsync(pkgPath, receiverType, methodName string)
|
|
|
2693
2805
|
|
|
2694
2806
|
return false
|
|
2695
2807
|
}
|
|
2696
|
-
|
|
2697
|
-
// updateInterfaceImplementationAsyncStatus updates interface implementations with correct async status
|
|
2698
|
-
// This runs after method async analysis is complete
|
|
2699
|
-
func (v *analysisVisitor) updateInterfaceImplementationAsyncStatus() {
|
|
2700
|
-
// Iterate through all tracked interface implementations and update their async status
|
|
2701
|
-
for key, implementations := range v.analysis.InterfaceImplementations {
|
|
2702
|
-
// Remove duplicates first
|
|
2703
|
-
seenMethods := make(map[string]bool)
|
|
2704
|
-
uniqueImplementations := []ImplementationInfo{}
|
|
2705
|
-
|
|
2706
|
-
for _, impl := range implementations {
|
|
2707
|
-
methodKey := impl.StructType.Obj().Name() + "." + key.MethodName
|
|
2708
|
-
if !seenMethods[methodKey] {
|
|
2709
|
-
seenMethods[methodKey] = true
|
|
2710
|
-
|
|
2711
|
-
// Now that method async analysis is complete, get the correct async status
|
|
2712
|
-
isAsync := v.analysis.IsAsyncFunc(impl.Method)
|
|
2713
|
-
|
|
2714
|
-
// Update the implementation with the correct async status
|
|
2715
|
-
impl.IsAsyncByFlow = isAsync
|
|
2716
|
-
uniqueImplementations = append(uniqueImplementations, impl)
|
|
2717
|
-
}
|
|
2718
|
-
}
|
|
2719
|
-
|
|
2720
|
-
// Store the updated implementations without duplicates
|
|
2721
|
-
v.analysis.InterfaceImplementations[key] = uniqueImplementations
|
|
2722
|
-
}
|
|
2723
|
-
}
|