class

Analyzer::Go::GoEngine

Inherits Analyzer < FileHelper < Reference < Object

Constants

GO_HTTP_ROUTE_CALL_RE = /\.(?:GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS|QUERY|Get|Post|Put|Delete|Patch|Head|Options|Query|ANY|Any|All)\s*\(/

Class methods

go_test_file?(relative_path : String) : Bool

*_test.go is Go's hard-wired build-tag for test-only source. go build excludes these files entirely; only go test pulls them in. Real route handlers never live there, but framework repos (echo, chi, gin) park hundreds of e.GET("/...", ...) calls in *_test.go to exercise the router. Skip both in the cross-file pre-passes (so test-file groups don't pollute the production group map) and in each analyzer's per-file loop.

Also skip Go's toolchain-ignored directory prefixes: any path component starting with _ (_examples/, _assets/, _testdata/) is excluded by go build/go list outright. kataras/iris alone parks 392 phantom endpoints under _examples/...; they're documented apps, not production routes the framework ships.

Takes the scan-base-relative path (Analyzer#base_relative_path), never the absolute one. Go's toolchain rule is relative to the module, so on an absolute path a single _-prefixed directory anywhere above the scan base — ~/_work/repo on a self-hosted CI runner — excluded every .go file in the project.

relative_path from base_relative_path is /-rooted, so "/_" is exactly "a path segment starts with _" without the per-call split allocation. The leading-_ check covers a caller that passes an unrooted relative path.

Source
strip_comments(text : String) : String

Blanks out // line comments and /* */ block comments, replacing them with spaces so every real line number stays put for PathInfo reporting. Without this a commented-out call reads as a live one — a // serveCmd.Flags().StringVar(&tok, "secret-token", ...) note registered a flag that the binary does not accept.

All three Go string forms are tracked so a // or /* inside a literal is never read as a comment opener: interpreted strings ("...", backslash escapes), rune literals ('.'), and raw strings (`...`) — the last take no escapes and may span newlines, which is why a shared C-family stripper is not enough.

Source

Instance methods

add_param_to_endpoint(param : Param, endpoint : Endpoint)
Source
add_static_path_if_valid(static_path : Hash(String, String), public_dirs : Array(Hash(String, String)))
Source
collect_import_path_function_bodies(package_function_bodies : Hash(String, Hash(String, Noir::GoCalleeExtractor::FunctionBody))) : Hash(String, Hash(String, Noir::GoCalleeExtractor::FunctionBody))

Builds {import_path => {function_name => body}} for local Go packages under the configured base paths. This is gated on callee demand and reuses package_function_bodies, so default route scans do not pay for go.mod discovery. It lets generated routers like Hertz resolve feed.Feed when feed imports a sibling package from the same module.

Source

Builds {import_path => {method_name => [body, ...]}} for local Go packages under the configured base paths. This complements collect_import_path_function_bodies for route handlers referenced as method values on imported package instances, e.g. handler := api.NewHandler(svc); app.Get("/x", handler.Show).

Source
collect_package_controller_method_bodies(file_contents : Hash(String, String)) : Hash(String, Hash(String, Array(Noir::GoCalleeExtractor::FunctionBody)))

Per-directory {method_name => [FunctionBody, ...]} map for controller-style routes whose handler is referenced by method name (Beego's web.Router("/x", &Ctrl{}, "get:Method")). Lets the analyzer walk the controller method's body for callees even though the registration call doesn't pass the handler as an argument. Gated on callees_needed? so default scans pay nothing.

Source
collect_package_controller_methods(file_contents : Hash(String, String)) : Hash(String, Hash(String, Array(String)))

--- Beego controller-method pre-pass --------------------------------

Builds a per-directory {controller_type => [http_verb_methods]} map so mapping-less web.Router("/x", &Ctrl{}) registrations resolve to the exact methods the controller implements. Keyed by directory because Beego controllers and their router registrations share a Go package (== directory); a controller defined in a sibling file is still found.

Source
collect_package_function_bodies(file_contents : Hash(String, String)) : Hash(String, Hash(String, Noir::GoCalleeExtractor::FunctionBody))

--- Cross-file function body pre-pass --------------------------------

Walks every cached .go source in file_contents and collects top-level function_declaration nodes into a per-directory map so cross-file identifier-handler resolution in Noir::GoCalleeExtractor is O(1) at lookup time. The map is keyed by directory because Go's name resolution rules are scoped to a single package (== single directory), so we never have to worry about cross-package leakage.

Source
collect_package_groups_ts(group_method : String = "Group", import_marker : String | Array(String) | Nil = nil) : Tuple(Hash(String, Hash(String, String)), Hash(String, String))

--- Tree-sitter group/route pre-pass --------------------------------

Does a cross-file fixpoint over every .go file in each package directory, extracting group declarations (x := r.Group("/x") and friends). Returns:

  • package_groups — per-directory {group_name => resolved_prefix}
  • file_contents — cached source strings keyed by path, so the per-file second pass doesn't read files twice

group_method is the method name used for grouping — "Group" for Gin/Echo/Fiber/Hertz (default), "Party" for Iris, "Subrouter" for Mux.

The fixpoint handles the routes.go calling v1.GET(...) under a v1 := r.Group("/v1") declared in main.go case — iterate until no new entries land. In-file declarations win over external ones. import_marker, when given, restricts the cross-file group/engine pre-pass to files that actually import the framework. Group variables (v1 := r.Group("/v1")) and root-engine bindings only ever appear in framework-importing files, so skipping the rest avoids parsing unrelated .go files — a large saving in mixed repos (e.g. an example monorepo where most dirs use other routers). file_contents is still populated for every file so the per-file route pass and callee pre-pass keep their read cache.

Source
framework_package_dirs(file_contents : Hash(String, String), import_marker : String | Array(String)) : Set(String)

Per-package directories that import a target framework. Some real projects hide the concrete framework type behind a local interface, so the file that calls router.GET(...) may not import Gin/Echo itself.

Source
framework_route_source_candidate?(content : String, dir : String, framework_dirs : Set(String), import_marker : String | Array(String), extra_methods : Array(String)) : Bool
Source
go_route_source_candidate?(content : String, extra_methods : Array(String)) : Bool

Cheap gate before the tree-sitter route pass. Handler-only files often import Gin/Echo for *gin.Context / echo.Context, but they cannot emit endpoints unless they contain a route/static registration call.

Source
read_package_file_contents

Read every .go file once into a {path => source} hash. Used by analyzers (Goyave) that don't otherwise call collect_package_groups_ts but still need the file_contents hash as input to collect_package_function_bodies.

Source
resolve_public_dirs(public_dirs : Array(Hash(String, String)))
Source
resolve_public_dirs_with_glob(public_dirs : Array(Hash(String, String)))
Source
static_dir_entry(source_path : String, static_path : String, file_path : String) : Hash(String, String)
Source
ts_controller_method_bodies_for_directory(package_controller_method_bodies : Hash(String, Hash(String, Array(Noir::GoCalleeExtractor::FunctionBody))), dir : String) : Hash(String, Array(Noir::GoCalleeExtractor::FunctionBody))
Source
ts_controller_methods_for_directory(package_controller_methods : Hash(String, Hash(String, Array(String))), dir : String) : Hash(String, Array(String))

Returns the cross-file controller-method map for the given directory, or an empty map.

Source
ts_function_bodies_for_directory(package_function_bodies : Hash(String, Hash(String, Noir::GoCalleeExtractor::FunctionBody)), dir : String) : Hash(String, Noir::GoCalleeExtractor::FunctionBody)

Returns the cross-file function-body map for the given directory, or an empty map when the directory has no captured functions.

Source
ts_groups_for_directory(package_groups : Hash(String, Hash(String, String)), dir : String) : Hash(String, String)

Returns the cross-file group map for the given directory, or an empty map when the directory has no registered groups.

Source