FAQ / troubleshooting
Real questions with real answers — every claim here is checked against the actual source or a real run, not written from memory. If something here turns out stale, open an issue.
General
A rule didn't fire on code I expected it to flag — is that a bug?
Usually not — check these first, in order:
- Is the rule reachability-scoped, and did you configure it?
float_outside_fixed_stepis opt-in on the Rust and Unreal sides (needstick_reachable_rootsindylint.toml/config.toml) — without it, that one rule does nothing at all, silently. Godot auto-detects_process/_physics_processby name instead, so this doesn't apply there. - On Rust specifically: configuring
tick_reachable_rootsat all scopes every rule exceptusize_in_hashed_state, not justfloat_outside_fixed_step— a real, easy-to-miss behavior. If your root is too narrow, the other five rules can stop firing on code that isn't reachable from it. See the Rust walkthrough for the full explanation. - Did you run against the whole project, not just one file? On Godot specifically,
_process/_physics_processbodies are only counted if the tool actually walked that file — run withDRIFT_GODOT_STATS=1(see below) to confirm every file parsed and how many functions were resolved reachable, rather than trusting a clean run. - Type-inference limits (Godot only): GDScript is dynamically typed, and
drift-godot-linthas no type checker —float_outside_fixed_steponly recognizes a variable as float-ish if it's a literal or explicitly typed: floatin the same function. Untyped arithmetic (var c = a + b, neither side typed) is invisible to it. This is real and permanent, not a bug — see the rule catalog for the exact rule. - Syntactic matching, not full symbol resolution (Unreal/Godot):
unseeded_rng/wallclock_read/unordered_parallelismmatch by call-site spelling (FMath::Rand,Time.get_ticks_msec, etc.), not resolved declarations. An aliased import or an unusual call shape can slip past. The Rust and C# sides use real compiler type information instead and don't have this limitation.
The tool reported zero findings — is that trustworthy?
Don't just trust a clean run — verify it actually processed your code:
- Godot: run with
DRIFT_GODOT_STATS=1— it prints files scanned, parse errors, function count, and reachable-function count to stderr. Zero findings alongside0 parse errorsand a plausible function count is real evidence; zero findings with parse errors or a suspiciously low function count means the tool silently skipped something. - Unreal: run with
DRIFT_UNREAL_STATS=1— printsN/M translation units parsed. IfN < M, some files failed to parse (a real, disclosed cost of parsing real Unreal C++ without a precompiled header) and their findings are missing, not absent. - Rust:
cargo dylintfails loudly on a compile error rather than silently skipping files, so a clean exit is stronger evidence here than on the other two.
How do I suppress a specific finding?
Depends on the binding — and this is genuinely uneven, not a design choice:
- Rust:
#[allow(drift_hashmap_iter)](or the matching attribute name for any other rule) on the item. - C# / Unity:
#pragma warning disable DRIFT0001/#pragma warning restore DRIFT0001. - Unreal / Godot: no suppression mechanism exists yet — no comment-based ignore, no config-level allowlist. If you have a confirmed false positive, your only options today are to restructure the code so it doesn't match the rule's pattern, or accept the finding and don't fail your CI build on it. This is a real, disclosed gap, not a hidden one — worth an issue if it's blocking you.
Rust (drift-lint)
cargo dylint fails to build with a linker error mentioning dylint-link
Install it: cargo install dylint-link (alongside cargo-dylint). crates/drift-lint's own .cargo/config.toml points rustc at dylint-link as the linker — without it on PATH, even an unrelated dependency's build script fails to link.
My dylint.toml config isn't being picked up
Root paths are matched against TyCtxt::def_path_str, which is crate-relative — it never includes your crate's own name. tick_reachable_roots = ["my_crate::tick"] silently matches nothing; use ["tick"] for a top-level function or ["module::path::tick"] for a nested one. Confirmed the hard way this session — a first attempt with a crate-name prefix produced zero reachable functions with no error at all.
Building crates/drift-lint is slow / seems to hang on a clean checkout
Its dylint_driver build script does a full, uncached clone of rust-lang/rust-clippy on every clean build (needed to extract symbol data for recent nightlies) — a real operational fragility, not a bug in this project. Swatinem/rust-cache (already used in this repo's own CI) caches target/ across runs so this only bites on a genuinely clean cache.
Do I need to publish this to crates.io to use it?
No, and you can't — drift-lint depends on clippy_utils via git (rust-lang/rust-clippy), which is itself never published to crates.io (its own README says it provides no stability guarantees). crates.io rejects any crate with an unversioned git dependency, confirmed via a real cargo publish --dry-run. Install via cargo dylint --path <clone> or --git https://github.com/FelixMiddelhoff/drift instead — see the Rust quickstart.
C# / Unity (Drift.Analyzers / GameDeterminism.Analyzers)
Why is the NuGet package name different from the project name?
Drift is a NuGet ID prefix reserved by an unrelated company — confirmed via nuget.org's own upload UI, which returned "The package ID is reserved" on every version pushed under Drift.Analyzers. Published as GameDeterminism.Analyzers instead; the underlying project (bindings/csharp/Drift.Analyzers) and DLL filename are unchanged. See the C# quickstart.
The analyzer isn't showing up in my IDE after installing
Roslyn analyzers need to be referenced in the correct NuGet layout (analyzers/dotnet/cs/) to auto-load — the published package does this correctly. If you're referencing the DLL directly instead of via NuGet, double-check the <Analyzer Include="..."> item group, not <Reference> or <PackageReference> styles meant for ordinary libraries.
Unreal (drift-unreal-lint)
LIBCLANG_PATH errors, or the tool can't find libclang
Point it at your LLVM install's bin directory explicitly — the clang crate doesn't reliably find a system libclang on its own across platforms. Needed both to build the tool and to run it (a prebuilt release binary still needs this set at runtime; it links libclang dynamically, doesn't bundle it).
A file's findings seem to be missing entirely
Run with DRIFT_UNREAL_STATS=1 and check N/M translation units parsed — a real libclang crash (OOM, a genuine parse failure) costs that one file's findings, reported to stderr as a warning, not silently dropped from the count. This was a real bug found dogfooding a 388-file sweep (see the rule catalog) and is now crash-isolated per file via a worker-subprocess architecture, but a crash still means that one file contributes nothing.
The sweep is slow against a large codebase
Workers run concurrently by default (a thread pool sized to available_parallelism()). Cap it with DRIFT_UNREAL_JOBS=<n> if you need to control peak memory instead — each worker is a full libclang parse, which is genuinely expensive without a precompiled header.
Godot (drift-godot-lint)
The tool found the same finding twice at the same location
Should be fixed — a real duplicate-finding bug (flat, name-only call graph resolving two same-named functions to one, scanning the same body twice) was found and fixed with a dedup pass. If you still see this, it's a regression worth reporting with the exact command and file.
A finding is reported on the wrong line
Should also be fixed — a real bug where a compound-assignment (x += y) finding got attributed to the previous statement's line whenever it wasn't the first statement in its function (a gdck-syntax parser checkpoint quirk). Fixed and locked in with a regression test. If you see a location that clearly doesn't match the reported rule, it's worth reporting with the exact file.
hashmap_iter/usize_in_hashed_state don't exist for Godot — why?
Not deferred, genuinely N/A: Godot's Dictionary is insertion-ordered by engine guarantee (unlike Rust's HashMap or C#'s Dictionary), so iteration-order desync isn't a real hazard there. GDScript's int is always 64-bit — there's no fixed/pointer-width distinction for usize_in_hashed_state to have a bug in.