Function Usage is not a standalone CLI tool — it is a lightweight, composable static analysis library designed to map how functions are used across a Go repository and live inside tests.
It provides a clear, type-checked view of direct call relationships between declared functions, allowing one to understand usage patterns and identify structural issues in your codebase.
Function Usage is intentionally a simple, deterministic, and transparent structural analysis tool, not a behavioral one.
It relies on Go’s type checker rather than heuristics or heavy call‑graph machinery, giving one predictable and actionable results.
It can help with below:
a. find functions with the highest number of direct calls
b. find functions with the lowest number of direct calls
c. find exported functions not used by direct calls
d. find exported functions that could be unexported
e. optionally include or exclude tests and external functions.
funcusage is not suitable for codebases where behavior is primarily expressed through interface dispatch, higher-order functions, or dynamic call construction. In such cases, direct call counts may significantly underrepresent actual runtime behavior.
The test files should provide examples on how to use.
A possible workflow could be:
The Usage data returned by the analyzer represents an expensive snapshot of the codebase (AST parsing, type checking). All filter and sort operations preserve this original data by returning new slices.
Run the analyzer from the module root as a test.
This way it can run both in CI and also resolve the module path and load all packages, including tests.
- Analysis Phase: Expensive (seconds, type checking)
- Query Phase: Cheap (microseconds, slice operations)
- Design Choice: Accept O(n) copying in where operations to guarantee:
- Test determinism (no flaky tests from mutations)
- Safe method chaining
- Predictable debugging (original data always available)
Configure whether to include:
- same‑package tests
- external test packages (
mypkg_test) - external packages outside the module
The analyzer returns a Usage slice.
Each FunctionUsage entry contains:
- canonical function identity
- short name
- source position
- internal and external call counts
- internal and external test call counts
Filters return a new Usage, allowing composition:
untestedExported := usage.
WhereNotTested().
WhereExported().
Limit(10)Example: find a specific untested exported function:
result := usage.
WhereNotTested().
WhereExported().
WhereNameIs("some function name")funcusage provides several mutually exclusive analysis modes. Each mode defines how production code and test code are interpreted during analysis.
Analyze only production code.
- Test files are ignored.
- Test-defined functions are not reported.
- Calls from tests do not count toward usage.
Use this mode for strict dead‑code detection in production.
Include calls from test files into production code, but do not report test-defined functions.
- Test helpers are ignored.
- Calls from tests increase usage counts for production functions.
Use this mode for a coverage‑like view of how tests exercise production code.
Include both production functions and test-defined functions.
- Test helpers appear in the output.
- Their usage is counted normally.
Use this mode to analyze or clean up test suites.
Analyze only functions defined in test files.
- Production code is ignored.
- Only test helpers and their usage are reported.
Use this mode to audit or refactor large test suites.
Report production functions, but count only calls originating from test files.
- Production functions are included.
- Only test-origin usage counts are considered.
Use this mode to identify production functions used exclusively by tests or over‑exported APIs.
Filters allow narrowing the analysis result (Usage) based on name, visibility, or usage patterns.
The number of values can be controlled with Limit as last element in the chain.
All filter methods start with Where and return new Usage slices:
Return only functions whose name matches exactly passed name.
Return functions with zero internal and zero external calls.
Return functions whose name starts with an upper‑case letter.
Return functions whose name starts with a lower‑case letter.
Return functions that have internal test calls.
Return functions that have external test calls.
Return exported functions that have no internal or external calls.
Return exported functions that have no external calls.
Useful for identifying candidates that should be unexported.
Allows injecting custom predicate for extensibility.
Sort by total calls (internal + external), highest first.
If totals are equal, sort by key.
Sort by number of calls, lowest number of calls first.
If counts are equal, sort by key.
Sort by external calls, highest number of calls first.
Sort by function name A → Z.
Sort by function name Z → A.
Returns the first N highest number of calls functions.
Returns the first N lowest number of calls functions.
funcusage analyzes declared functions and methods (func declarations) only.
It intentionally does not track:
- function literals or closures
- anonymous functions assigned to variables
- dynamically constructed call targets
- method usage through interfaces.
This is a deliberate design choice.
Declared functions form the stable, addressable API surface of a Go codebase — the part that is exported, refactored, reviewed, and reasoned about at scale.
Including function literals would significantly increase noise while providing little actionable insight for structural analysis.
As a result, funcusage may undercount usage in highly functional or closure-heavy code, but it avoids false positives and preserves deterministic, explainable results.