module Noir::GoCalleeExtractor

Overview

Tree-sitter-backed Go 1-hop callee extractor. Parallels Noir::PythonCalleeExtractor but works at the AST level — Go can't share the Python convention of "give me a body string", because tree-sitter Go needs a complete source_file and a bare function body isn't one.

The extractor receives a full Go file plus the set of route call expression rows the analyzer cares about. For each match it locates the handler argument and walks the appropriate body:

Builtins (len, make, append, …) and Go's primitive type constructors (int, string, byte, …) are filtered to keep the per-endpoint list focused on signal that's actually useful to an AI reviewer.

Extended Modules

Defined in:

miniparsers/go_callee_extractor.cr

Constant Summary

BUILTINS = Set {"len", "cap", "make", "new", "append", "copy", "delete", "close", "panic", "recover", "print", "println", "string", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr", "byte", "rune", "float32", "float64", "complex64", "complex128", "complex", "bool", "error"}

Go builtins and primitive type-conversions that carry no useful signal. Anything framework-specific (c.JSON, c.Query, gin.H{...}, etc.) is kept on purpose — those tell a reviewer how the endpoint shapes input/output.

Instance Method Summary

Instance Method Detail

def callees_for_routes(source : String, file_path : String, route_rows : Set(Int32), external_functions : Hash(String, FunctionBody), external_methods : Hash(String, Array(FunctionBody)) = Hash(String, Array(FunctionBody)).new, imported_functions : Hash(String, Hash(String, FunctionBody)) = Hash(String, Hash(String, FunctionBody)).new, imported_methods : Hash(String, Hash(String, Array(FunctionBody))) = Hash(String, Hash(String, Array(FunctionBody))).new) #

For each call_expression at a row in route_rows, find the handler argument, walk its body, and return the 1-hop callees keyed by row. Each entry is a tuple {name, callee_file_path, file_line_1_based}.

external_methods maps a (package-unqualified) method name to the bodies that define it, so a method-value handler (as.Campaigns, s.handleOIDCRedirect — the dominant shape in gorilla/mux and chi apps) resolves to its method body when the name is unambiguous.


[View source]
def callees_for_routes_if(enabled : Bool, source : String, file_path : String, route_rows : Set(Int32), external_functions : Hash(String, FunctionBody), external_methods : Hash(String, Array(FunctionBody)) = Hash(String, Array(FunctionBody)).new, imported_functions : Hash(String, Hash(String, FunctionBody)) = Hash(String, Hash(String, FunctionBody)).new, imported_methods : Hash(String, Hash(String, Array(FunctionBody))) = Hash(String, Hash(String, Array(FunctionBody))).new) #

Like #callees_for_routes, but returns an empty map immediately when enabled is false. Lets analyzers skip the tree-sitter walk on default scans where callees won't be observed.


[View source]
def callees_in_body(fn : FunctionBody, external_functions : Hash(String, FunctionBody) = Hash(String, FunctionBody).new) : Array(Tuple(String, String, Int32)) #

Public entry for walking a captured function/method body and returning its 1-hop callees. Used by analyzers (Beego controller routing) that resolve a handler to a FunctionBody outside the standard route-row flow.


[View source]
def collect_function_bodies(source : String, file_path : String) : Hash(String, FunctionBody) #

Returns top-level function declarations in source, keyed by name. file_path is recorded on each FunctionBody so callees emitted by later re-parsing can report a useful path.


[View source]
def collect_method_bodies(source : String, file_path : String) : Hash(String, Array(FunctionBody)) #

Collects top-level method_declaration bodies keyed by method name. The value is a list because a name can be defined on several receiver types in one package; callers that need an unambiguous resolution should require size == 1. Used to attach callees to controller-style routes whose handler is referenced by method name only.


[View source]
def function_bodies_for_directory(package_bodies : Hash(String, Hash(String, FunctionBody)), dir : String) : Hash(String, FunctionBody) #

Returns the cross-file function-body map for the given directory, or an empty map. Mirrors GoEngine#ts_function_bodies_for_directory.


[View source]
def method_bodies_for_directory(package_method_bodies : Hash(String, Hash(String, Array(FunctionBody))), dir : String) : Hash(String, Array(FunctionBody)) #

Returns the per-directory method-body map, or an empty map.


[View source]
def package_function_bodies(file_contents : Hash(String, String)) : Hash(String, Hash(String, FunctionBody)) #

Walk every cached .go source in file_contents and collect top-level function_declaration nodes into a per-directory map so cross-file identifier-handler resolution is O(1) at lookup time. Keyed by directory because Go's name resolution is scoped to a single package (== single directory). Module-level twin of GoEngine#collect_package_function_bodies for analyzers (Chi) that don't inherit from GoEngine.


[View source]
def package_function_bodies_if(enabled : Bool, file_contents : Hash(String, String)) : Hash(String, Hash(String, FunctionBody)) #

Like #package_function_bodies, but returns an empty map immediately when enabled is false. Module-level twin of the GoEngine#collect_package_function_bodies gate for analyzers that don't inherit from GoEngine.


[View source]
def package_method_bodies_if(enabled : Bool, file_contents : Hash(String, String)) : Hash(String, Hash(String, Array(FunctionBody))) #

Per-directory {method_name => [FunctionBody, ...]} map so a method-value handler (as.Campaigns, ctrl.Index) can be resolved to its method body for callee extraction. Module-level twin of GoEngine#collect_package_controller_method_bodies for analyzers (Chi) that don't inherit from GoEngine. Returns an empty map immediately when enabled is false so default scans pay nothing.


[View source]