Skip to content

Bug: Go method symbol resolution returns only function signature (1 line) instead of full body #461

@buger

Description

@buger

Summary

When using extract with a Go method receiver symbol (e.g. file.go#TykApi.GetOrgKeyList), probe returns only the function signature line (Lines: N-N) instead of the full function body (Lines: N-M). This makes edit with symbol mode catastrophically break Go files — it replaces/inserts at the wrong location, leaving orphaned code outside any function.

Probe Version

0.6.0

Impact

This affects all three symbol-based operations on Go receiver methods:

Operation Expected Actual Consequence
extract #Symbol Full function body Just signature line Incomplete code returned
edit symbol replace Replace entire function Replace just signature Original body left orphaned
edit symbol insert (position: "after") Insert after closing } Insert after signature (inside body) Code injected mid-function

Reproduction

File: dashboard/tyk_api_wrapper.go (lines 332-366)

func (t *TykApi) GetOrgKeyList() (AllKeys, error) {   // line 332
	t.prepareClient()                                     // line 333

	var req *http.Request
	req = t.prepareRequest("GET", t.ConstructEndpoint("org/keys/"), nil)

	response, err := t.client.Do(req)

	apiKeys := AllKeys{ApiKeys: []string{}}
	if err != nil {
		log.Error("Failed to get keys")
		log.Error(err)
		return apiKeys, err
	}

	body, ok := t.readBody(response)

	if !ok {
		return apiKeys, errors.New("could not read response body")
	}

	if response.StatusCode >= http.StatusBadRequest {
		log.Error("API Request failed: ", string(body))
		return apiKeys, errors.New("unexpected status code")
	}

	err = json.Unmarshal(body, &apiKeys)
	if err != nil {
		log.Error("Unmarshalling failed")
		log.Error(err)
		return apiKeys, err
	}

	return apiKeys, nil
}                                                        // line 366

Extract returns wrong range

extract file.go#TykApi.GetOrgKeyList
→ Lines: 332-332         ← BUG: should be 332-366
→ Type: text_search       ← should be method_declaration or similar
→ code: "func (t *TykApi) GetOrgKeyList() (AllKeys, error) {"

Note: Type: text_search indicates tree-sitter AST matching failed and it fell back to text search, which only finds the signature line.

Multiple Go methods affected (from same trace)

All Go receiver methods show the same behavior:

extract tyk/gateway/api.go#Gateway.keyHandler
→ Lines: 1642-1642    ← should span ~80 lines
→ Type: text_search

extract tyk/gateway/auth_manager.go#DefaultSessionManager.Sessions
→ Lines: 221-221      ← should span ~3 lines  
→ Type: text_search

Real-World Corruption Example

The agent performed two edit operations on the file above:

Edit 1 — Insert new function after GetOrgKeyList:

{
  "symbol": "TykApi.GetOrgKeyList",
  "position": "after",
  "new_string": "func (t *TykApi) GetAllCustomKeysForOrg() (AllKeys, error) { ... }"
}

Result: "inserted 39 lines after symbol TykApi.GetOrgKeyList at line 333"

Because the symbol resolved to line 332 only, position: "after" inserted at line 333 — inside the function body, right after the signature, instead of after the closing } at line 366.

Edit 2 — Replace GetOrgKeyList with updated version:

{
  "symbol": "TykApi.GetOrgKeyList",
  "new_string": "func (t *TykApi) GetOrgKeyList() (AllKeys, error) { ... new body with HashKeys logic ... }"
}

Result: "replaced symbol TykApi.GetOrgKeyList (was lines 332-332, now 45 lines)"

Only replaced line 332 (the signature). The original 34-line function body remained in the file, orphaned.

Resulting corrupted file

// NEW complete GetOrgKeyList (45 lines from Edit 2 — replaced just the signature)
func (t *TykApi) GetOrgKeyList() (AllKeys, error) {
    ... new body with if !config.Global().HashKeys logic ...
    return apiKeys, nil
}

// NEW GetAllCustomKeysForOrg (39 lines from Edit 1 — inserted at wrong location)
func (t *TykApi) GetAllCustomKeysForOrg() (AllKeys, error) {
    ...
    return apiKeys, nil
}

// ORPHANED: Original body of GetOrgKeyList — sitting OUTSIDE any function!
    t.prepareClient()
    var req *http.Request
    req = t.prepareRequest("GET", t.ConstructEndpoint("org/keys/"), nil)
    ...
    return apiKeys, nil
}

The actual git diff confirms this: https://github.com/TykTechnologies/tyk-analytics/pull/5326/files

Code Path

The symbol resolution flows through:

  1. tools/edit.js:handleSymbolEdit() (line 75) calls findAllSymbols()
  2. tools/symbolEdit.js:findAllSymbols() (line 56) calls extract({files: ['path#symbol'], format: 'json', json: true})
  3. extract returns {lines: [332, 332], node_type: 'text_search', code: 'func signature...'}
  4. Back in handleSymbolEdit, replace mode (line 134): lines.splice(332-1, 332-332+1, ...newLines) — splices only 1 line

Root Cause Hypothesis

The extract function's tree-sitter query for Go likely fails to match receiver methods with the Type.Method syntax (e.g., TykApi.GetOrgKeyList). It falls back to text_search (grep-style), which finds only the signature line. The Type: text_search in the output confirms AST matching failed.

Go receiver methods have a distinct tree-sitter node structure:

(method_declaration
  receiver: (parameter_list ...)
  name: (field_identifier) @name
  body: (block) ...)

The symbol query might be matching only function_declaration nodes (regular functions) and not method_declaration nodes (receiver methods), or the Type.Method qualified name lookup may not correctly traverse receiver types.

Trace ID

Jaeger trace: 84ffb80af393b4d13eddaa930899758b (spans ac2bb5d3ddc0308a and 33649d5854e015ce for the two edit calls, span 0c7431b52d1a8e26 for the parent ai_check with full iteration logs)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingexternal

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions