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.
@@ -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 *types.Named // The struct type that implements the interface
110
- Method *types.Func // The method object
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: make(map[types.Type]bool),
200
- AllPackages: allPackages,
201
- InterfaceImplementations: make(map[InterfaceMethodKey][]ImplementationInfo),
202
- InterfaceMethodAsyncStatus: make(map[InterfaceMethodKey]bool),
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 ptrType, ok := obj.Type().(*types.Pointer); ok {
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.currentFuncDecl != nil {
926
- // Check if the enclosing function declaration has named returns
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, isAsync)
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: analysis,
1186
- pkg: 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 := false
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
- for _, f := range funcs {
1294
- if f == funcName {
1295
- // Found the function in another file
1296
- if callsFromOtherFiles[sourceFile] == nil {
1297
- callsFromOtherFiles[sourceFile] = []string{}
1298
- }
1299
- // Check if already added to avoid duplicates
1300
- found := false
1301
- for _, existing := range callsFromOtherFiles[sourceFile] {
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 := false
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
- for _, t := range types {
1358
- if t == typeName {
1359
- // Found the type in another file
1360
- if typeRefsFromOtherFiles[sourceFile] == nil {
1361
- typeRefsFromOtherFiles[sourceFile] = []string{}
1362
- }
1363
- // Check if already added to avoid duplicates
1364
- found := false
1365
- for _, existing := range typeRefsFromOtherFiles[sourceFile] {
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, isAsync bool) {
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: structType,
1706
- Method: 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
- // Update implementations with current async status before checking
1734
- for i := range implementations {
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
- // Update with current async status from method analysis
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
- // Determine if this struct method is async using unified system
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
- // Note: Don't determine async status here - it will be determined later after method analysis
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
- // Determine if this implementation is async using unified system
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
- // getNamedReturns retrieves the named returns for a function
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
- // Initialize visitingMethods map
2254
- v.visitingMethods = make(map[MethodKey]bool)
2255
-
2256
- // Fixed-point iteration: keep analyzing until nothing changes
2257
- // This handles cases where method A calls method B, but B is analyzed after A
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
- // Save previous state
2267
- previousState := make(map[MethodKey]bool)
2268
- for k, v := range v.analysis.MethodAsyncStatus {
2269
- previousState[k] = v
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
- // Re-analyze methods in current package
2273
- v.analyzePackageMethodsAsync(v.pkg)
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
- // Re-analyze methods in all dependency packages
2276
- for _, pkg := range v.analysis.AllPackages {
2277
- if pkg != v.pkg {
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
- // Check if anything changed
2283
- for k, newValue := range v.analysis.MethodAsyncStatus {
2284
- oldValue, existed := previousState[k]
2285
- if !existed {
2286
- // New method added - check if it's async (if sync, no need to re-analyze dependents)
2287
- if newValue {
2288
- changed = true
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
- } else if oldValue != newValue {
2291
- // Method changed from sync to async (or vice versa)
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 nothing changed, we've reached a fixed point
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
- // analyzePackageMethodsAsync analyzes all methods in a specific package
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
- // analyzeMethodAsync determines if a method is async and stores the result
2354
- func (v *analysisVisitor) analyzeMethodAsync(funcDecl *ast.FuncDecl, pkg *packages.Package) {
2355
- methodKey := v.getMethodKey(funcDecl, pkg)
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
- // Check for cycles
2358
- if v.visitingMethods[methodKey] {
2359
- // Cycle detected, assume sync to break recursion
2360
- v.analysis.MethodAsyncStatus[methodKey] = false
2361
- return
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
- // Mark as visiting
2365
- v.visitingMethods[methodKey] = true
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
- // Determine if method is async
2368
- isAsync := false
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
- // Determine if this is a handwritten package (from gs/ directory)
2371
- // Handwritten packages should not have their bodies analyzed
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
- metadataKey := MethodKey{
2377
- PackagePath: methodKey.PackagePath,
2378
- ReceiverType: methodKey.ReceiverType,
2379
- MethodName: methodKey.MethodName,
2475
+ _, hasMetadata := v.analysis.MethodAsyncStatus[methodKey]
2476
+ if hasMetadata {
2477
+ // Already set from metadata, don't override
2478
+ return
2380
2479
  }
2381
- metadataIsAsync, hasMetadata := v.analysis.MethodAsyncStatus[metadataKey]
2480
+ // No metadata means assume sync
2481
+ v.analysis.MethodAsyncStatus[methodKey] = false
2482
+ return
2483
+ }
2382
2484
 
2383
- if hasMetadata {
2384
- // Use explicit metadata from handwritten packages (gs/)
2385
- isAsync = metadataIsAsync
2386
- } else {
2387
- // Handwritten package but no explicit metadata: assume sync
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
- // Store result in MethodAsyncStatus
2398
- v.analysis.MethodAsyncStatus[methodKey] = isAsync
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
- // Unmark as visiting
2401
- delete(v.visitingMethods, methodKey)
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
- // Get receiver type name
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
- return v.isFunctionAsync(funcObj, pkg)
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
- return v.analysis.IsInterfaceMethodAsync(interfaceType, methodName)
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
- return v.isMethodAsyncFromSelection(fun, methodObj, pkg)
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 yet, analyze now
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
- // Check if this is a method in the same package we're currently analyzing
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
- }