goscript 0.0.59 → 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 +465 -336
- 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.mod +4 -4
- package/go.sum +8 -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
|
@@ -2,11 +2,11 @@ package compiler
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"encoding/json"
|
|
5
|
-
"fmt"
|
|
6
5
|
"go/ast"
|
|
7
6
|
"go/token"
|
|
8
7
|
"go/types"
|
|
9
8
|
"path/filepath"
|
|
9
|
+
"slices"
|
|
10
10
|
"strings"
|
|
11
11
|
|
|
12
12
|
"github.com/aperturerobotics/goscript"
|
|
@@ -107,9 +107,8 @@ type MethodKey struct {
|
|
|
107
107
|
|
|
108
108
|
// ImplementationInfo tracks information about a struct that implements an interface method
|
|
109
109
|
type ImplementationInfo struct {
|
|
110
|
-
StructType
|
|
111
|
-
Method
|
|
112
|
-
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
|
|
113
112
|
}
|
|
114
113
|
|
|
115
114
|
// Analysis holds information gathered during the analysis phase of the Go code compilation.
|
|
@@ -154,10 +153,6 @@ type Analysis struct {
|
|
|
154
153
|
// This is used to determine interface method async status based on implementations
|
|
155
154
|
InterfaceImplementations map[InterfaceMethodKey][]ImplementationInfo
|
|
156
155
|
|
|
157
|
-
// InterfaceMethodAsyncStatus caches the async status determination for interface methods
|
|
158
|
-
// This is computed once during analysis and reused during code generation
|
|
159
|
-
InterfaceMethodAsyncStatus map[InterfaceMethodKey]bool
|
|
160
|
-
|
|
161
156
|
// MethodAsyncStatus stores the async status of all methods analyzed
|
|
162
157
|
// This is computed once during analysis and reused during code generation
|
|
163
158
|
MethodAsyncStatus map[MethodKey]bool
|
|
@@ -197,11 +192,10 @@ func NewAnalysis(allPackages map[string]*packages.Package) *Analysis {
|
|
|
197
192
|
ReflectedFunctions: make(map[ast.Node]*ReflectedFunctionInfo),
|
|
198
193
|
FunctionAssignments: make(map[types.Object]ast.Node),
|
|
199
194
|
// PackageMetadata removed - using MethodAsyncStatus only
|
|
200
|
-
NamedBasicTypes:
|
|
201
|
-
AllPackages:
|
|
202
|
-
InterfaceImplementations:
|
|
203
|
-
|
|
204
|
-
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),
|
|
205
199
|
}
|
|
206
200
|
}
|
|
207
201
|
|
|
@@ -237,6 +231,22 @@ func (a *Analysis) ensureFunctionData(obj types.Object) *FunctionInfo {
|
|
|
237
231
|
return a.FunctionData[obj]
|
|
238
232
|
}
|
|
239
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
|
+
|
|
240
250
|
// NeedsDefer returns whether the given node needs defer handling.
|
|
241
251
|
func (a *Analysis) NeedsDefer(node ast.Node) bool {
|
|
242
252
|
if node == nil {
|
|
@@ -371,7 +381,7 @@ func (a *Analysis) NeedsVarRefAccess(obj types.Object) bool {
|
|
|
371
381
|
}
|
|
372
382
|
|
|
373
383
|
// For pointer variables, check if they point to a variable-referenced value
|
|
374
|
-
if
|
|
384
|
+
if _, ok := obj.Type().(*types.Pointer); ok {
|
|
375
385
|
// Check all assignments to this pointer variable
|
|
376
386
|
for varObj, info := range a.VariableUsage {
|
|
377
387
|
if varObj == obj {
|
|
@@ -383,11 +393,6 @@ func (a *Analysis) NeedsVarRefAccess(obj types.Object) bool {
|
|
|
383
393
|
}
|
|
384
394
|
}
|
|
385
395
|
}
|
|
386
|
-
|
|
387
|
-
// Handle direct pointer initialization like: var p *int = &x
|
|
388
|
-
// Check if the pointer type's element type requires variable referencing
|
|
389
|
-
_ = ptrType.Elem()
|
|
390
|
-
// For now, conservatively return false for untracked cases
|
|
391
396
|
}
|
|
392
397
|
|
|
393
398
|
return false
|
|
@@ -454,9 +459,6 @@ type analysisVisitor struct {
|
|
|
454
459
|
|
|
455
460
|
// currentFuncLit tracks the *ast.FuncLit of the function literal we're currently analyzing.
|
|
456
461
|
currentFuncLit *ast.FuncLit
|
|
457
|
-
|
|
458
|
-
// visitingMethods tracks methods currently being analyzed to prevent infinite recursion
|
|
459
|
-
visitingMethods map[MethodKey]bool
|
|
460
462
|
}
|
|
461
463
|
|
|
462
464
|
// getOrCreateUsageInfo retrieves or creates the VariableUsageInfo for a given object.
|
|
@@ -923,18 +925,8 @@ func (v *analysisVisitor) visitReturnStmt(n *ast.ReturnStmt) ast.Visitor {
|
|
|
923
925
|
|
|
924
926
|
// Check if it's a bare return
|
|
925
927
|
if len(n.Results) == 0 {
|
|
926
|
-
if v.
|
|
927
|
-
|
|
928
|
-
if obj := v.pkg.TypesInfo.ObjectOf(v.currentFuncDecl.Name); obj != nil {
|
|
929
|
-
if _, ok := v.analysis.FunctionData[obj]; ok {
|
|
930
|
-
nodeInfo.IsBareReturn = true
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
} else if v.currentFuncLit != nil {
|
|
934
|
-
// Check if the enclosing function literal has named returns
|
|
935
|
-
if _, ok := v.analysis.FuncLitData[v.currentFuncLit]; ok {
|
|
936
|
-
nodeInfo.IsBareReturn = true
|
|
937
|
-
}
|
|
928
|
+
if v.analysis.GetFunctionInfoFromContext(nodeInfo, v.pkg) != nil {
|
|
929
|
+
nodeInfo.IsBareReturn = true
|
|
938
930
|
}
|
|
939
931
|
}
|
|
940
932
|
return v
|
|
@@ -1012,14 +1004,8 @@ func (v *analysisVisitor) visitTypeAssertExpr(typeAssert *ast.TypeAssertExpr) as
|
|
|
1012
1004
|
// Find the corresponding method in the struct type
|
|
1013
1005
|
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
1014
1006
|
if structMethod != nil {
|
|
1015
|
-
// Determine if this struct method is async using unified system
|
|
1016
|
-
isAsync := false
|
|
1017
|
-
if obj := structMethod; obj != nil {
|
|
1018
|
-
isAsync = v.analysis.IsAsyncFunc(obj)
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
1007
|
// Track this interface implementation
|
|
1022
|
-
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod
|
|
1008
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod)
|
|
1023
1009
|
}
|
|
1024
1010
|
}
|
|
1025
1011
|
return v
|
|
@@ -1183,9 +1169,8 @@ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages
|
|
|
1183
1169
|
|
|
1184
1170
|
// Create visitor for the entire package
|
|
1185
1171
|
visitor := &analysisVisitor{
|
|
1186
|
-
analysis:
|
|
1187
|
-
pkg:
|
|
1188
|
-
visitingMethods: make(map[MethodKey]bool),
|
|
1172
|
+
analysis: analysis,
|
|
1173
|
+
pkg: pkg,
|
|
1189
1174
|
}
|
|
1190
1175
|
|
|
1191
1176
|
// First pass: analyze all declarations and statements across all files
|
|
@@ -1277,13 +1262,7 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
|
|
|
1277
1262
|
|
|
1278
1263
|
// Check if this function is defined in the current file
|
|
1279
1264
|
currentFileFuncs := analysis.FunctionDefs[baseFileName]
|
|
1280
|
-
isDefinedInCurrentFile :=
|
|
1281
|
-
for _, f := range currentFileFuncs {
|
|
1282
|
-
if f == funcName {
|
|
1283
|
-
isDefinedInCurrentFile = true
|
|
1284
|
-
break
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1265
|
+
isDefinedInCurrentFile := slices.Contains(currentFileFuncs, funcName)
|
|
1287
1266
|
|
|
1288
1267
|
// If not defined in current file, find which file defines it
|
|
1289
1268
|
if !isDefinedInCurrentFile {
|
|
@@ -1291,24 +1270,15 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
|
|
|
1291
1270
|
if sourceFile == baseFileName {
|
|
1292
1271
|
continue // Skip current file
|
|
1293
1272
|
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
if existing == funcName {
|
|
1304
|
-
found = true
|
|
1305
|
-
break
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
if !found {
|
|
1309
|
-
callsFromOtherFiles[sourceFile] = append(callsFromOtherFiles[sourceFile], funcName)
|
|
1310
|
-
}
|
|
1311
|
-
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)
|
|
1312
1282
|
}
|
|
1313
1283
|
}
|
|
1314
1284
|
}
|
|
@@ -1341,13 +1311,7 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
|
|
|
1341
1311
|
|
|
1342
1312
|
// Check if this type is defined in the current file
|
|
1343
1313
|
currentFileTypes := analysis.TypeDefs[baseFileName]
|
|
1344
|
-
isDefinedInCurrentFile :=
|
|
1345
|
-
for _, t := range currentFileTypes {
|
|
1346
|
-
if t == typeName {
|
|
1347
|
-
isDefinedInCurrentFile = true
|
|
1348
|
-
break
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1314
|
+
isDefinedInCurrentFile := slices.Contains(currentFileTypes, typeName)
|
|
1351
1315
|
|
|
1352
1316
|
// If not defined in current file, find which file defines it
|
|
1353
1317
|
if !isDefinedInCurrentFile {
|
|
@@ -1355,24 +1319,15 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
|
|
|
1355
1319
|
if sourceFile == baseFileName {
|
|
1356
1320
|
continue // Skip current file
|
|
1357
1321
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
if existing == typeName {
|
|
1368
|
-
found = true
|
|
1369
|
-
break
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
if !found {
|
|
1373
|
-
typeRefsFromOtherFiles[sourceFile] = append(typeRefsFromOtherFiles[sourceFile], typeName)
|
|
1374
|
-
}
|
|
1375
|
-
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)
|
|
1376
1331
|
}
|
|
1377
1332
|
}
|
|
1378
1333
|
}
|
|
@@ -1472,6 +1427,14 @@ func (a *Analysis) loadGsMetadata(metaFilePath string) *GsMetadata {
|
|
|
1472
1427
|
return &metadata
|
|
1473
1428
|
}
|
|
1474
1429
|
|
|
1430
|
+
// isHandwrittenPackage checks if a package path corresponds to a handwritten package in gs/
|
|
1431
|
+
func (a *Analysis) isHandwrittenPackage(pkgPath string) bool {
|
|
1432
|
+
// Check if the package exists in the embedded gs/ directory
|
|
1433
|
+
metaFilePath := filepath.Join("gs", pkgPath, "meta.json")
|
|
1434
|
+
_, err := goscript.GsOverrides.ReadFile(metaFilePath)
|
|
1435
|
+
return err == nil
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1475
1438
|
// IsMethodAsync checks if a method call is async based on package metadata
|
|
1476
1439
|
func (a *Analysis) IsMethodAsync(pkgPath, typeName, methodName string) bool {
|
|
1477
1440
|
// First, check pre-computed method async status
|
|
@@ -1688,16 +1651,15 @@ func (v *analysisVisitor) findVariableUsageInExpr(expr ast.Expr, lhsVarNames map
|
|
|
1688
1651
|
}
|
|
1689
1652
|
|
|
1690
1653
|
// trackInterfaceImplementation records that a struct type implements an interface method
|
|
1691
|
-
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) {
|
|
1692
1655
|
key := InterfaceMethodKey{
|
|
1693
1656
|
InterfaceType: interfaceType.String(),
|
|
1694
1657
|
MethodName: method.Name(),
|
|
1695
1658
|
}
|
|
1696
1659
|
|
|
1697
1660
|
implementation := ImplementationInfo{
|
|
1698
|
-
StructType:
|
|
1699
|
-
Method:
|
|
1700
|
-
IsAsyncByFlow: isAsync,
|
|
1661
|
+
StructType: structType,
|
|
1662
|
+
Method: method,
|
|
1701
1663
|
}
|
|
1702
1664
|
|
|
1703
1665
|
a.InterfaceImplementations[key] = append(a.InterfaceImplementations[key], implementation)
|
|
@@ -1710,49 +1672,25 @@ func (a *Analysis) IsInterfaceMethodAsync(interfaceType *types.Interface, method
|
|
|
1710
1672
|
MethodName: methodName,
|
|
1711
1673
|
}
|
|
1712
1674
|
|
|
1713
|
-
// Check if we've already computed this
|
|
1714
|
-
if result, exists := a.InterfaceMethodAsyncStatus[key]; exists {
|
|
1715
|
-
return result
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
1675
|
// Find all implementations of this interface method
|
|
1719
1676
|
implementations, exists := a.InterfaceImplementations[key]
|
|
1720
1677
|
if !exists {
|
|
1721
|
-
// No implementations found, default to sync
|
|
1722
|
-
a.InterfaceMethodAsyncStatus[key] = false
|
|
1723
1678
|
return false
|
|
1724
1679
|
}
|
|
1725
1680
|
|
|
1726
|
-
//
|
|
1727
|
-
for
|
|
1728
|
-
impl := &implementations[i]
|
|
1729
|
-
|
|
1730
|
-
// Create method key for this implementation
|
|
1681
|
+
// If ANY implementation is async, the interface method is async
|
|
1682
|
+
for _, impl := range implementations {
|
|
1731
1683
|
methodKey := MethodKey{
|
|
1732
1684
|
PackagePath: impl.StructType.Obj().Pkg().Path(),
|
|
1733
1685
|
ReceiverType: impl.StructType.Obj().Name(),
|
|
1734
1686
|
MethodName: impl.Method.Name(),
|
|
1735
1687
|
}
|
|
1736
1688
|
|
|
1737
|
-
|
|
1738
|
-
if isAsync, exists := a.MethodAsyncStatus[methodKey]; exists {
|
|
1739
|
-
impl.IsAsyncByFlow = isAsync
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
// Store the updated implementations back to the map
|
|
1744
|
-
a.InterfaceImplementations[key] = implementations
|
|
1745
|
-
|
|
1746
|
-
// If ANY implementation is async, the interface method is async
|
|
1747
|
-
for _, impl := range implementations {
|
|
1748
|
-
if impl.IsAsyncByFlow {
|
|
1749
|
-
a.InterfaceMethodAsyncStatus[key] = true
|
|
1689
|
+
if isAsync, exists := a.MethodAsyncStatus[methodKey]; exists && isAsync {
|
|
1750
1690
|
return true
|
|
1751
1691
|
}
|
|
1752
1692
|
}
|
|
1753
1693
|
|
|
1754
|
-
// All implementations are sync
|
|
1755
|
-
a.InterfaceMethodAsyncStatus[key] = false
|
|
1756
1694
|
return false
|
|
1757
1695
|
}
|
|
1758
1696
|
|
|
@@ -1837,56 +1775,6 @@ func (a *Analysis) GetIdentifierMapping(ident *ast.Ident) string {
|
|
|
1837
1775
|
return ""
|
|
1838
1776
|
}
|
|
1839
1777
|
|
|
1840
|
-
// trackTypeAssertion analyzes type assertions and records interface implementations
|
|
1841
|
-
func (v *analysisVisitor) trackTypeAssertion(typeAssert *ast.TypeAssertExpr) {
|
|
1842
|
-
// Get the type being asserted to
|
|
1843
|
-
assertedType := v.pkg.TypesInfo.TypeOf(typeAssert.Type)
|
|
1844
|
-
if assertedType == nil {
|
|
1845
|
-
return
|
|
1846
|
-
}
|
|
1847
|
-
|
|
1848
|
-
// Check if the asserted type is an interface
|
|
1849
|
-
interfaceType, isInterface := assertedType.Underlying().(*types.Interface)
|
|
1850
|
-
if !isInterface {
|
|
1851
|
-
return
|
|
1852
|
-
}
|
|
1853
|
-
|
|
1854
|
-
// Get the type of the expression being asserted
|
|
1855
|
-
exprType := v.pkg.TypesInfo.TypeOf(typeAssert.X)
|
|
1856
|
-
if exprType == nil {
|
|
1857
|
-
return
|
|
1858
|
-
}
|
|
1859
|
-
|
|
1860
|
-
// Handle pointer types by getting the element type
|
|
1861
|
-
if ptrType, isPtr := exprType.(*types.Pointer); isPtr {
|
|
1862
|
-
exprType = ptrType.Elem()
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
// Check if the expression type is a named struct type
|
|
1866
|
-
namedType, isNamed := exprType.(*types.Named)
|
|
1867
|
-
if !isNamed {
|
|
1868
|
-
return
|
|
1869
|
-
}
|
|
1870
|
-
|
|
1871
|
-
// For each method in the interface, check if the struct implements it
|
|
1872
|
-
for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
|
|
1873
|
-
interfaceMethod := interfaceType.ExplicitMethod(i)
|
|
1874
|
-
|
|
1875
|
-
// Find the corresponding method in the struct type
|
|
1876
|
-
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
1877
|
-
if structMethod != nil {
|
|
1878
|
-
// Determine if this struct method is async using unified system
|
|
1879
|
-
isAsync := false
|
|
1880
|
-
if obj := structMethod; obj != nil {
|
|
1881
|
-
isAsync = v.analysis.IsAsyncFunc(obj)
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
// Track this interface implementation
|
|
1885
|
-
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
}
|
|
1889
|
-
|
|
1890
1778
|
// findStructMethod finds a method with the given name on a named type
|
|
1891
1779
|
func (v *analysisVisitor) findStructMethod(namedType *types.Named, methodName string) *types.Func {
|
|
1892
1780
|
// Check methods directly on the type
|
|
@@ -1999,10 +1887,7 @@ func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt)
|
|
|
1999
1887
|
|
|
2000
1888
|
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
2001
1889
|
if structMethod != nil {
|
|
2002
|
-
|
|
2003
|
-
isAsync := v.analysis.IsAsyncFunc(structMethod)
|
|
2004
|
-
|
|
2005
|
-
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
1890
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod)
|
|
2006
1891
|
}
|
|
2007
1892
|
}
|
|
2008
1893
|
}
|
|
@@ -2061,9 +1946,7 @@ func (v *analysisVisitor) trackInterfaceCallArguments(callExpr *ast.CallExpr) {
|
|
|
2061
1946
|
|
|
2062
1947
|
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
2063
1948
|
if structMethod != nil {
|
|
2064
|
-
|
|
2065
|
-
// For now, just track the implementation relationship without async status
|
|
2066
|
-
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, false)
|
|
1949
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod)
|
|
2067
1950
|
}
|
|
2068
1951
|
}
|
|
2069
1952
|
}
|
|
@@ -2209,10 +2092,7 @@ func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *type
|
|
|
2209
2092
|
// Find the method in the implementing type
|
|
2210
2093
|
structMethod := v.findMethodInType(namedType, interfaceMethod.Name())
|
|
2211
2094
|
if structMethod != nil {
|
|
2212
|
-
|
|
2213
|
-
isAsync := v.analysis.IsAsyncFunc(structMethod)
|
|
2214
|
-
|
|
2215
|
-
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
2095
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod)
|
|
2216
2096
|
}
|
|
2217
2097
|
}
|
|
2218
2098
|
}
|
|
@@ -2228,52 +2108,77 @@ func (v *interfaceImplementationVisitor) findMethodInType(namedType *types.Named
|
|
|
2228
2108
|
return nil
|
|
2229
2109
|
}
|
|
2230
2110
|
|
|
2231
|
-
//
|
|
2232
|
-
func (v *analysisVisitor)
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2111
|
+
// analyzeAllMethodsAsync performs comprehensive async analysis on all methods in all packages using topological sort
|
|
2112
|
+
func (v *analysisVisitor) analyzeAllMethodsAsync() {
|
|
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
|
|
2125
|
+
maxIterations := 10
|
|
2126
|
+
for iteration := 0; iteration < maxIterations; iteration++ {
|
|
2127
|
+
changed := false
|
|
2128
|
+
|
|
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
|
|
2238
2134
|
}
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
return namedReturns
|
|
2242
|
-
}
|
|
2243
2135
|
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
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
|
+
}
|
|
2248
2143
|
|
|
2249
|
-
|
|
2250
|
-
|
|
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)
|
|
2251
2147
|
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
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
|
|
2155
|
+
}
|
|
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
|
|
2161
|
+
changed = true
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
// If no changes in this iteration, we're done
|
|
2166
|
+
if !changed {
|
|
2167
|
+
break
|
|
2256
2168
|
}
|
|
2257
2169
|
}
|
|
2258
2170
|
|
|
2171
|
+
// Analyze methods in dependency order (dependencies analyzed before dependents)
|
|
2172
|
+
for _, methodKey := range sorted {
|
|
2173
|
+
v.analyzeMethodAsyncTopological(methodKey, methodCalls[methodKey])
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2259
2176
|
// Finally, analyze function literals in the current package only
|
|
2260
2177
|
// (external packages' function literals are not accessible)
|
|
2261
2178
|
v.analyzeFunctionLiteralsAsync(v.pkg)
|
|
2262
2179
|
}
|
|
2263
2180
|
|
|
2264
|
-
//
|
|
2265
|
-
func (v *analysisVisitor) analyzePackageMethodsAsync(pkg *packages.Package) {
|
|
2266
|
-
// Analyze function declarations
|
|
2267
|
-
for _, file := range pkg.Syntax {
|
|
2268
|
-
for _, decl := range file.Decls {
|
|
2269
|
-
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
|
|
2270
|
-
v.analyzeMethodAsync(funcDecl, pkg)
|
|
2271
|
-
}
|
|
2272
|
-
}
|
|
2273
|
-
}
|
|
2274
|
-
}
|
|
2275
|
-
|
|
2276
|
-
// analyzeFunctionLiteralsAsync analyzes all function literals in a package for async operations
|
|
2181
|
+
// buildMethodCallGraph builds a graph of which methods call which other methods
|
|
2277
2182
|
func (v *analysisVisitor) analyzeFunctionLiteralsAsync(pkg *packages.Package) {
|
|
2278
2183
|
for _, file := range pkg.Syntax {
|
|
2279
2184
|
ast.Inspect(file, func(n ast.Node) bool {
|
|
@@ -2307,45 +2212,320 @@ func (v *analysisVisitor) analyzeFunctionLiteralAsync(funcLit *ast.FuncLit, pkg
|
|
|
2307
2212
|
nodeInfo.InAsyncContext = isAsync
|
|
2308
2213
|
}
|
|
2309
2214
|
|
|
2310
|
-
//
|
|
2311
|
-
func (v *analysisVisitor)
|
|
2312
|
-
|
|
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)
|
|
2313
2218
|
|
|
2314
|
-
//
|
|
2315
|
-
|
|
2316
|
-
|
|
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
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
|
|
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)
|
|
2232
|
+
|
|
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
|
+
})
|
|
2273
|
+
|
|
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
|
+
}
|
|
2317
2452
|
}
|
|
2318
2453
|
|
|
2319
|
-
//
|
|
2320
|
-
|
|
2321
|
-
|
|
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
|
|
2471
|
+
isHandwrittenPackage := v.analysis.isHandwrittenPackage(methodKey.PackagePath)
|
|
2472
|
+
|
|
2473
|
+
if isHandwrittenPackage {
|
|
2474
|
+
// For handwritten packages, check if we have pre-loaded metadata
|
|
2475
|
+
_, hasMetadata := v.analysis.MethodAsyncStatus[methodKey]
|
|
2476
|
+
if hasMetadata {
|
|
2477
|
+
// Already set from metadata, don't override
|
|
2478
|
+
return
|
|
2479
|
+
}
|
|
2480
|
+
// No metadata means assume sync
|
|
2322
2481
|
v.analysis.MethodAsyncStatus[methodKey] = false
|
|
2323
2482
|
return
|
|
2324
2483
|
}
|
|
2325
2484
|
|
|
2326
|
-
//
|
|
2327
|
-
v.
|
|
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
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2328
2492
|
|
|
2329
|
-
|
|
2330
|
-
|
|
2493
|
+
if pkg == nil {
|
|
2494
|
+
// Can't find package, assume sync
|
|
2495
|
+
v.analysis.MethodAsyncStatus[methodKey] = false
|
|
2496
|
+
return
|
|
2497
|
+
}
|
|
2331
2498
|
|
|
2332
|
-
|
|
2333
|
-
|
|
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
|
+
}
|
|
2334
2507
|
|
|
2335
|
-
if
|
|
2336
|
-
//
|
|
2337
|
-
|
|
2508
|
+
if funcDecl == nil || funcDecl.Body == nil {
|
|
2509
|
+
// No body to analyze, assume sync
|
|
2510
|
+
v.analysis.MethodAsyncStatus[methodKey] = false
|
|
2511
|
+
return
|
|
2338
2512
|
}
|
|
2339
|
-
|
|
2340
|
-
if
|
|
2341
|
-
|
|
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
|
+
}
|
|
2342
2526
|
}
|
|
2343
2527
|
|
|
2344
|
-
// Store result in MethodAsyncStatus
|
|
2345
2528
|
v.analysis.MethodAsyncStatus[methodKey] = isAsync
|
|
2346
|
-
|
|
2347
|
-
// Unmark as visiting
|
|
2348
|
-
delete(v.visitingMethods, methodKey)
|
|
2349
2529
|
}
|
|
2350
2530
|
|
|
2351
2531
|
// getMethodKey creates a unique key for a method
|
|
@@ -2355,14 +2535,19 @@ func (v *analysisVisitor) getMethodKey(funcDecl *ast.FuncDecl, pkg *packages.Pac
|
|
|
2355
2535
|
receiverType := ""
|
|
2356
2536
|
|
|
2357
2537
|
if funcDecl.Recv != nil && len(funcDecl.Recv.List) > 0 {
|
|
2358
|
-
//
|
|
2359
|
-
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 {
|
|
2360
2540
|
if def := pkg.TypesInfo.Defs[funcDecl.Recv.List[0].Names[0]]; def != nil {
|
|
2361
2541
|
if vr, ok := def.(*types.Var); ok {
|
|
2362
2542
|
receiverType = v.getTypeName(vr.Type())
|
|
2363
2543
|
}
|
|
2364
2544
|
}
|
|
2365
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
|
+
}
|
|
2366
2551
|
}
|
|
2367
2552
|
|
|
2368
2553
|
return MethodKey{
|
|
@@ -2384,6 +2569,23 @@ func (v *analysisVisitor) getTypeName(t types.Type) string {
|
|
|
2384
2569
|
}
|
|
2385
2570
|
}
|
|
2386
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
|
+
|
|
2387
2589
|
// containsAsyncOperationsComplete is a comprehensive async detection that handles method calls
|
|
2388
2590
|
func (v *analysisVisitor) containsAsyncOperationsComplete(node ast.Node, pkg *packages.Package) bool {
|
|
2389
2591
|
var hasAsync bool
|
|
@@ -2417,16 +2619,8 @@ func (v *analysisVisitor) containsAsyncOperationsComplete(node ast.Node, pkg *pa
|
|
|
2417
2619
|
|
|
2418
2620
|
case *ast.CallExpr:
|
|
2419
2621
|
// Check if we're calling a function known to be async
|
|
2420
|
-
|
|
2421
|
-
if isCallAsyncResult {
|
|
2622
|
+
if v.isCallAsync(s, pkg) {
|
|
2422
2623
|
hasAsync = true
|
|
2423
|
-
callName := ""
|
|
2424
|
-
if ident, ok := s.Fun.(*ast.Ident); ok {
|
|
2425
|
-
callName = ident.Name
|
|
2426
|
-
} else if sel, ok := s.Fun.(*ast.SelectorExpr); ok {
|
|
2427
|
-
callName = sel.Sel.Name
|
|
2428
|
-
}
|
|
2429
|
-
asyncReasons = append(asyncReasons, fmt.Sprintf("async call: %s", callName))
|
|
2430
2624
|
return false
|
|
2431
2625
|
}
|
|
2432
2626
|
}
|
|
@@ -2444,7 +2638,8 @@ func (v *analysisVisitor) isCallAsync(callExpr *ast.CallExpr, pkg *packages.Pack
|
|
|
2444
2638
|
// Direct function call
|
|
2445
2639
|
if obj := pkg.TypesInfo.Uses[fun]; obj != nil {
|
|
2446
2640
|
if funcObj, ok := obj.(*types.Func); ok {
|
|
2447
|
-
|
|
2641
|
+
result := v.isFunctionAsync(funcObj, pkg)
|
|
2642
|
+
return result
|
|
2448
2643
|
}
|
|
2449
2644
|
}
|
|
2450
2645
|
|
|
@@ -2467,14 +2662,16 @@ func (v *analysisVisitor) isCallAsync(callExpr *ast.CallExpr, pkg *packages.Pack
|
|
|
2467
2662
|
if interfaceType, isInterface := receiverType.Underlying().(*types.Interface); isInterface {
|
|
2468
2663
|
methodName := fun.Sel.Name
|
|
2469
2664
|
// For interface method calls, check if the interface method is async
|
|
2470
|
-
|
|
2665
|
+
result := v.analysis.IsInterfaceMethodAsync(interfaceType, methodName)
|
|
2666
|
+
return result
|
|
2471
2667
|
}
|
|
2472
2668
|
}
|
|
2473
2669
|
|
|
2474
2670
|
// Method call on concrete objects
|
|
2475
2671
|
if selection := pkg.TypesInfo.Selections[fun]; selection != nil {
|
|
2476
2672
|
if methodObj := selection.Obj(); methodObj != nil {
|
|
2477
|
-
|
|
2673
|
+
result := v.isMethodAsyncFromSelection(fun, methodObj, pkg)
|
|
2674
|
+
return result
|
|
2478
2675
|
}
|
|
2479
2676
|
}
|
|
2480
2677
|
}
|
|
@@ -2489,7 +2686,7 @@ func (v *analysisVisitor) isFunctionAsync(funcObj *types.Func, pkg *packages.Pac
|
|
|
2489
2686
|
return v.analysis.IsMethodAsync(funcObj.Pkg().Path(), "", funcObj.Name())
|
|
2490
2687
|
}
|
|
2491
2688
|
|
|
2492
|
-
// Check internal method status
|
|
2689
|
+
// Check internal method status (should already be computed during analysis)
|
|
2493
2690
|
methodKey := MethodKey{
|
|
2494
2691
|
PackagePath: pkg.Types.Path(),
|
|
2495
2692
|
ReceiverType: "",
|
|
@@ -2500,14 +2697,7 @@ func (v *analysisVisitor) isFunctionAsync(funcObj *types.Func, pkg *packages.Pac
|
|
|
2500
2697
|
return status
|
|
2501
2698
|
}
|
|
2502
2699
|
|
|
2503
|
-
// Not analyzed
|
|
2504
|
-
if funcDecl := v.findFunctionDecl(funcObj.Name(), pkg); funcDecl != nil {
|
|
2505
|
-
v.analyzeMethodAsync(funcDecl, pkg)
|
|
2506
|
-
if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|
|
2507
|
-
return status
|
|
2508
|
-
}
|
|
2509
|
-
}
|
|
2510
|
-
|
|
2700
|
+
// Not found - should have been analyzed during analyzeAllMethodsAsync
|
|
2511
2701
|
return false
|
|
2512
2702
|
}
|
|
2513
2703
|
|
|
@@ -2527,7 +2717,12 @@ func (v *analysisVisitor) isMethodAsyncFromSelection(selExpr *ast.SelectorExpr,
|
|
|
2527
2717
|
}
|
|
2528
2718
|
}
|
|
2529
2719
|
case *ast.SelectorExpr:
|
|
2530
|
-
// 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
|
|
2531
2726
|
if typeExpr := pkg.TypesInfo.TypeOf(x); typeExpr != nil {
|
|
2532
2727
|
receiverType = v.getTypeName(typeExpr)
|
|
2533
2728
|
}
|
|
@@ -2540,7 +2735,6 @@ func (v *analysisVisitor) isMethodAsyncFromSelection(selExpr *ast.SelectorExpr,
|
|
|
2540
2735
|
}
|
|
2541
2736
|
}
|
|
2542
2737
|
|
|
2543
|
-
// If no package path found, use current package
|
|
2544
2738
|
if methodPkgPath == "" {
|
|
2545
2739
|
methodPkgPath = pkg.Types.Path()
|
|
2546
2740
|
}
|
|
@@ -2557,28 +2751,7 @@ func (v *analysisVisitor) isMethodAsyncFromSelection(selExpr *ast.SelectorExpr,
|
|
|
2557
2751
|
return status
|
|
2558
2752
|
}
|
|
2559
2753
|
|
|
2560
|
-
//
|
|
2561
|
-
if methodPkgPath == v.pkg.Types.Path() {
|
|
2562
|
-
// This is a method in the same package - we should analyze it if we haven't yet
|
|
2563
|
-
if funcDecl := v.findMethodDecl(receiverType, methodObj.Name(), v.pkg); funcDecl != nil {
|
|
2564
|
-
v.analyzeMethodAsync(funcDecl, v.pkg)
|
|
2565
|
-
if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|
|
2566
|
-
return status
|
|
2567
|
-
}
|
|
2568
|
-
}
|
|
2569
|
-
} else {
|
|
2570
|
-
// For methods in other packages that we're compiling together
|
|
2571
|
-
if targetPkg := v.analysis.AllPackages[methodPkgPath]; targetPkg != nil {
|
|
2572
|
-
// Try to analyze the method if we haven't already
|
|
2573
|
-
if funcDecl := v.findMethodDecl(receiverType, methodObj.Name(), targetPkg); funcDecl != nil {
|
|
2574
|
-
v.analyzeMethodAsync(funcDecl, targetPkg)
|
|
2575
|
-
if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|
|
2576
|
-
return status
|
|
2577
|
-
}
|
|
2578
|
-
}
|
|
2579
|
-
}
|
|
2580
|
-
}
|
|
2581
|
-
|
|
2754
|
+
// Not found - should have been analyzed during analyzeAllMethodsAsync
|
|
2582
2755
|
return false
|
|
2583
2756
|
}
|
|
2584
2757
|
|
|
@@ -2618,22 +2791,6 @@ func (v *analysisVisitor) findMethodDecl(receiverType, methodName string, pkg *p
|
|
|
2618
2791
|
return nil
|
|
2619
2792
|
}
|
|
2620
2793
|
|
|
2621
|
-
// checkExternalMethodMetadata checks if an external method is async based on pre-loaded metadata
|
|
2622
|
-
func (v *analysisVisitor) checkExternalMethodMetadata(pkgPath, receiverType, methodName string) bool {
|
|
2623
|
-
// Use MethodKey to check pre-loaded metadata in MethodAsyncStatus
|
|
2624
|
-
key := MethodKey{
|
|
2625
|
-
PackagePath: pkgPath,
|
|
2626
|
-
ReceiverType: receiverType,
|
|
2627
|
-
MethodName: methodName,
|
|
2628
|
-
}
|
|
2629
|
-
|
|
2630
|
-
if isAsync, exists := v.analysis.MethodAsyncStatus[key]; exists {
|
|
2631
|
-
return isAsync
|
|
2632
|
-
}
|
|
2633
|
-
|
|
2634
|
-
return false
|
|
2635
|
-
}
|
|
2636
|
-
|
|
2637
2794
|
// IsLocalMethodAsync checks if a local method is async using pre-computed analysis
|
|
2638
2795
|
func (a *Analysis) IsLocalMethodAsync(pkgPath, receiverType, methodName string) bool {
|
|
2639
2796
|
methodKey := MethodKey{
|
|
@@ -2648,31 +2805,3 @@ func (a *Analysis) IsLocalMethodAsync(pkgPath, receiverType, methodName string)
|
|
|
2648
2805
|
|
|
2649
2806
|
return false
|
|
2650
2807
|
}
|
|
2651
|
-
|
|
2652
|
-
// updateInterfaceImplementationAsyncStatus updates interface implementations with correct async status
|
|
2653
|
-
// This runs after method async analysis is complete
|
|
2654
|
-
func (v *analysisVisitor) updateInterfaceImplementationAsyncStatus() {
|
|
2655
|
-
// Iterate through all tracked interface implementations and update their async status
|
|
2656
|
-
for key, implementations := range v.analysis.InterfaceImplementations {
|
|
2657
|
-
// Remove duplicates first
|
|
2658
|
-
seenMethods := make(map[string]bool)
|
|
2659
|
-
uniqueImplementations := []ImplementationInfo{}
|
|
2660
|
-
|
|
2661
|
-
for _, impl := range implementations {
|
|
2662
|
-
methodKey := impl.StructType.Obj().Name() + "." + key.MethodName
|
|
2663
|
-
if !seenMethods[methodKey] {
|
|
2664
|
-
seenMethods[methodKey] = true
|
|
2665
|
-
|
|
2666
|
-
// Now that method async analysis is complete, get the correct async status
|
|
2667
|
-
isAsync := v.analysis.IsAsyncFunc(impl.Method)
|
|
2668
|
-
|
|
2669
|
-
// Update the implementation with the correct async status
|
|
2670
|
-
impl.IsAsyncByFlow = isAsync
|
|
2671
|
-
uniqueImplementations = append(uniqueImplementations, impl)
|
|
2672
|
-
}
|
|
2673
|
-
}
|
|
2674
|
-
|
|
2675
|
-
// Store the updated implementations without duplicates
|
|
2676
|
-
v.analysis.InterfaceImplementations[key] = uniqueImplementations
|
|
2677
|
-
}
|
|
2678
|
-
}
|