Skip to content

Commit ed6a588

Browse files
committed
fix(golang): mark GlobalVar dependencies as IsInvoked when directly called
When a package-scope variable holds a function value (e.g. `var Foo = func() {...}`) and is invoked from another function body via `Foo()`, the parser previously only recorded `Foo` as a plain GlobalVar dependency. Extend the existing directCalls matching in parseFunc to also stamp Extra[IsInvoked]=true on GlobalVars, mirroring the behavior already in place for FunctionCalls and MethodCalls.
1 parent ef0f3e7 commit ed6a588

5 files changed

Lines changed: 58 additions & 2 deletions

File tree

docs/uniast-en.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ Represents a dependency relationship, containing the dependent node Id, dependen
432432
- Extra: Additional information for storing language-specific details or extra metadata
433433

434434

435-
- IsInvoked: For function/method dependencies, whether it is invoked or just referenced (not executed).
435+
- IsInvoked: For function/method dependencies, or for GlobalVars dependencies whose target is a func-typed global variable, whether it is invoked or just referenced (not executed). For example, given `var Foo = func() {...}`, if another function body contains `Foo()`, the corresponding dependency in that function's `GlobalVars` is marked `IsInvoked: true`; a plain reference such as `_ = Foo` is not marked.
436436

437437

438438
##### Type

docs/uniast-zh.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ Universal Abstract-Syntax-Tree 是 ABCoder 建立的一种 LLM 亲和、语言
432432
- Extra: 额外信息,用于存储一些语言特定的信息,或者是一些额外的元数据
433433

434434

435-
- IsInvoked: 对于函数 / 方法调用类依赖,用于说明该函数是被调用(invoke),还是仅获取其引用而不执行。
435+
- IsInvoked: 对于函数 / 方法调用类依赖,或函数型全局变量(GlobalVars)类依赖,用于说明该函数 / 全局变量是被调用(invoke),还是仅获取其引用而不执行。例如 `var Foo = func() {...}` 时,若另一函数体内出现 `Foo()`,则其在该函数 `GlobalVars` 中的依赖会被标记 `IsInvoked: true`;若仅出现 `_ = Foo` 等纯引用,则不会被标记
436436

437437

438438
##### Type

lang/golang/parser/file.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,11 @@ set_func:
584584
f.MethodCalls[i].SetExtra(ExtraKey_IsInvoked, true)
585585
}
586586
}
587+
for i, dep := range f.GlobalVars {
588+
if collects.directCalls[dep.FileLine] {
589+
f.GlobalVars[i].SetExtra(ExtraKey_IsInvoked, true)
590+
}
591+
}
587592
}
588593
if len(collects.anonymousFunctions) > 0 {
589594
f.SetExtra(ExtraKey_AnonymousFunctions, collects.anonymousFunctions)

lang/golang/parser/pkg_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,50 @@ func Test_goParser_ParseDirs(t *testing.T) {
114114
}
115115
}
116116

117+
func Test_goParser_GlobalFuncVar_IsInvoked(t *testing.T) {
118+
p := newGoParser("a.b/c", testutils.FirstTest("go"), Options{LoadByPackages: true})
119+
pkgPath := "a.b/c/pkg"
120+
if err := p.parsePackage(pkgPath); err != nil {
121+
t.Fatalf("parsePackage failed: %v", err)
122+
}
123+
var6 := NewIdentity("a.b/c", pkgPath, "Var6")
124+
125+
// Case_Invoke_GlobalFuncVar() directly calls Var6() -> IsInvoked must be true.
126+
invokeFn := p.repo.GetFunction(NewIdentity("a.b/c", pkgPath, "Case_Invoke_GlobalFuncVar"))
127+
if invokeFn == nil {
128+
t.Fatalf("function Case_Invoke_GlobalFuncVar not found")
129+
}
130+
dep := findDep(invokeFn.GlobalVars, var6)
131+
if dep == nil {
132+
t.Fatalf("Var6 not found in GlobalVars of Case_Invoke_GlobalFuncVar")
133+
}
134+
if v, _ := dep.GetExtra(ExtraKey_IsInvoked).(bool); !v {
135+
t.Fatalf("expected Var6 to be marked IsInvoked=true in Case_Invoke_GlobalFuncVar, got Extra=%+v", dep.Extra)
136+
}
137+
138+
// Case_Func_Global() only references Var6 (no call) -> IsInvoked must be unset.
139+
refFn := p.repo.GetFunction(NewIdentity("a.b/c", pkgPath, "Case_Func_Global"))
140+
if refFn == nil {
141+
t.Fatalf("function Case_Func_Global not found")
142+
}
143+
dep = findDep(refFn.GlobalVars, var6)
144+
if dep == nil {
145+
t.Fatalf("Var6 not found in GlobalVars of Case_Func_Global")
146+
}
147+
if v, _ := dep.GetExtra(ExtraKey_IsInvoked).(bool); v {
148+
t.Fatalf("expected Var6 to NOT be marked IsInvoked in Case_Func_Global")
149+
}
150+
}
151+
152+
func findDep(deps []Dependency, id Identity) *Dependency {
153+
for i := range deps {
154+
if deps[i].Identity == id {
155+
return &deps[i]
156+
}
157+
}
158+
return nil
159+
}
160+
117161
func TestGoAst(t *testing.T) {
118162
src := `
119163
package parse

testdata/go/0_golang/pkg/util.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ func Case_Func_Global() {
125125
_ = entity.V1
126126
}
127127

128+
// Case_Invoke_GlobalFuncVar directly invokes a global var that holds a func value.
129+
// Used to verify that the GlobalVars dependency for Var6 is marked IsInvoked=true,
130+
// while a pure reference (e.g. in Case_Func_Global) is not.
131+
func Case_Invoke_GlobalFuncVar() {
132+
Var6()
133+
}
134+
128135
// Type is Result type
129136
type Type int
130137

0 commit comments

Comments
 (0)