Using Hawk
August 21, 2026 ยท View on GitHub
Hawk analyzes public declarations in workspace library crates against
configured production targets and workspace non-production targets. This guide
covers invoking the tool; see Configuration for the
hawk.toml reference and Architecture for the analysis
model.
Install a prebuilt release
Hawk is pinned to Rust 1.98.0 and uses rustc_private. A prebuilt release
still requires the exact normal Rust toolchain, but it does not require
rustc-dev, RUSTC_BOOTSTRAP, or a source build:
rustup toolchain install 1.98.0
Install the latest release with the standalone shell installer:
curl --proto '=https' --tlsv1.2 -LsSf \
https://github.com/astral-sh/hawk/releases/latest/download/cargo-hawk-installer.sh | sh
The installer places cargo-hawk and cargo-hawk-driver on your PATH in
the same directory. You can instead download the archive for your platform
from GitHub Releases and place
both executables on your PATH manually.
Run the Cargo subcommand with the pinned toolchain:
cargo +1.98.0 hawk check --manifest-path /path/to/workspace/Cargo.toml
Build Hawk
Hawk is pinned to Rust 1.98.0 and uses rustc_private; the repository
toolchain configuration installs rustc-dev when necessary. A source build
produces a cargo-hawk frontend and a cargo-hawk-driver compiler wrapper.
cargo build
Configure production targets
Without a hawk.toml, Hawk treats every workspace binary as a production
target. To select specific binaries or audit internal libraries, declare the
desired production targets in hawk.toml at the root of the workspace being
analyzed:
[[production]]
package = "app"
bin = "app"
reason = "shipped application binary"
Every configured package and binary must be a target of that workspace. Once a configuration file exists, its production targets are authoritative: an API used only by an omitted binary can be reported as unnecessary or dead. See Configuration for multiple binaries, target-scoped entries, and accepted findings.
Run analysis
Run cargo hawk without a subcommand to see the available commands. Use
check to analyze a workspace:
./target/debug/cargo-hawk check \
--manifest-path /path/to/workspace/Cargo.toml
Configured production targets and workspace non-production targets are
analyzed under --all-features --locked on the host target by default. A
[[feature-profile]] matrix in hawk.toml can replace that single feature
selection; Hawk unions evidence from every profile before producing
diagnostics. The non-production surface includes tests, benches, examples, and
compile-only doctests, which can be restricted to explicit packages with
[[doctest]] entries. Diagnostics apply to workspace library crates compiled
for those targets, including declarations enabled only under cfg(test).
Workspace libraries are treated as internal unless exempted. Exclude a library crate whose public API is consumed outside the configured production targets:
./target/debug/cargo-hawk check \
--manifest-path /path/to/workspace/Cargo.toml \
--exclude-crate supported_library
Instrumented Cargo artifacts are reused under
cargo-hawk-target/<workspace-name>-<path-hash> in the platform temporary
directory by default. Including the workspace path prevents same-named
checkouts and worktrees from sharing Cargo locks and artifacts. Use
--target-dir to override that location and --graph-dir to retain serialized
compiler fragments for investigation. Diagnostics are colored automatically
in a terminal; use --color=always or --color=never to override terminal
detection.
Use --output-format=json for a machine-readable diagnostic report. For a
completed analysis, Cargo progress and compiler output remain on stderr, so
stdout contains exactly one JSON object. Operational failures are reported on
stderr and do not produce a JSON report:
./target/debug/cargo-hawk check \
--manifest-path /path/to/workspace/Cargo.toml \
--output-format=json > hawk-report.json
The JSON report is versioned with schema_version (currently 5); breaking
schema changes increment this version. Its summary describes the compilation
target, configured production binaries or libraries, feature profiles,
non-production coverage, and emitted diagnostic count.
Every entry in diagnostics includes its category, lint code, and severity.
Finding entries additionally include their finding kind, semantic identity
(package, crate, item, definition kind, parent, and module scope),
target-independent source-qualified identity (identity.id), compiler identity
(identity.compiler_id), available source and expansion locations,
and the test_only and test_compiled_only flags. Source locations include
line, column, end_line, end_column, byte_start, and byte_end when a
complete declaration range is available. Lines and columns are one-based,
columns count Unicode scalar values, and byte offsets are zero-based UTF-8
offsets into the original source file. end_line/end_column and byte_end
are exclusive; ranges include source-spanned attributes, documentation, and
trailing field, variant, or re-export separators.
The stable identity uses versioned, length-prefixed package, crate, item,
definition kind, and source-location components, so cfg and path alternatives
remain distinct while the same declaration can be correlated across targets;
the compiler identity can change with the target or feature set.
When rustc cannot retain a complete range for a parsed attribute, such as
#[cold] or #[unsafe(link_section = "...")], the location intentionally
falls back to file, line, and column; ending locations and byte offsets
are omitted.
Configuration diagnostics identify either the referenced lint and item or the
excluded scope, along with the configuration location and reason.
--fix supports the default profile or one explicitly configured feature
profile. Hawk rejects fixing runs with a multi-profile matrix; run analysis
without --fix to review the combined findings.
Enforce diagnostics
Hawk reports diagnostics as warnings by default, so it can be introduced
without changing build status. Deny the warnings group to use Hawk as a CI
gate:
./target/debug/cargo-hawk check \
--manifest-path /path/to/workspace/Cargo.toml \
-D warnings
Hawk accepts Clippy-style ordered -A/--allow, -W/--warn, and
-D/--deny lint levels. Later options take precedence:
./target/debug/cargo-hawk check \
--manifest-path /path/to/workspace/Cargo.toml \
-D warnings \
-W hawk::unnecessary_public
The supported selectors are warnings, hawk::dead_public,
hawk::unnecessary_public, hawk::unnecessary_restricted_visibility,
hawk::unnecessary_crate_visibility, hawk::unknown_item,
hawk::ambiguous_item, and hawk::unfulfilled_expectation. Denied diagnostics
are emitted as errors and cause a non-zero exit status. Invalid configuration
and failed instrumented Cargo builds fail independently of lint levels.
To focus on deletion candidates in either text or JSON output, pass
--only dead-public. This suppresses visibility-reduction findings while
preserving configuration diagnostics such as unknown items and unfulfilled
expectations:
./target/debug/cargo-hawk check \
--manifest-path /path/to/workspace/Cargo.toml \
--only dead-public
In text output, the final summary groups emitted findings by lint and Cargo
package. Hawk reports configuration diagnostics separately under
configuration.
hawk::unnecessary_crate_visibility is allow-by-default because preferring
pub(super) over pub(crate) is a style choice. Enable it explicitly with
-W hawk::unnecessary_crate_visibility or
-D hawk::unnecessary_crate_visibility. The warnings group does not enable
allow-by-default lints.
Apply fixes
Pass --fix to apply visibility reductions through Cargo's fix machinery:
./target/debug/cargo-hawk check \
--manifest-path /path/to/workspace/Cargo.toml \
--fix
Hawk emits machine-applicable suggestions for enabled, unsuppressed
visibility findings. hawk::unnecessary_public reduces pub to pub(crate).
hawk::unnecessary_restricted_visibility removes an explicit restricted
visibility modifier when the item can be private.
hawk::unnecessary_crate_visibility optionally reduces pub(crate) to
pub(super). hawk::dead_public remains report-only because a
visibility-only edit can activate rustc's dead_code lint; removing dead
surface may require editing its remaining internal uses. Hawk delegates edit
application and validation to cargo fix, including Cargo's source-control
safety checks; pass --allow-dirty, --allow-staged, or --allow-no-vcs
with --fix when the corresponding Cargo override is appropriate.
Fixes are limited to workspace library packages in the configured production or non-production surface. Hawk rechecks configured production targets and non-production targets, including compile-only doctests, after applying edits. Dead declarations and enum variants remain report-only.
Analyze another target
Pass --target TRIPLE to analyze another compilation target. Hawk forwards
the target to Cargo but does not install a target SDK or configure a cross
linker. When --target is omitted, Hawk explicitly analyzes the host target;
this overrides Cargo's build.target configuration and CARGO_BUILD_TARGET
so compilation, target-scoped configuration, and reported coverage agree.
For example, a macOS host can analyze Windows MSVC production targets using
cargo-xwin. From the Hawk
checkout, prepare the pinned toolchain once:
rustup target add x86_64-pc-windows-msvc
rustup component add llvm-tools-preview
cargo install cargo-xwin --locked
Then export the linker and Windows SDK configuration for Hawk's child Cargo process before running the analysis:
target=x86_64-pc-windows-msvc
eval "$(cargo xwin env --quiet \
--target "$target" \
--manifest-path /path/to/workspace/Cargo.toml)"
./target/debug/cargo-hawk check \
--manifest-path /path/to/workspace/Cargo.toml \
--target "$target"
Target-scoped production entries and expectations can keep platform-specific surfaces explicit; see Configuration.