build: no warnings in first-party C++ (#1408) - #1438
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
1d4-web | 8404df4 | Commit Preview URL Branch Preview URL |
Aug 23 2026, 12:54 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
iili | 50c5222 | Aug 23 2026, 03:11 AM |
|
It is the leftover from #1435: the Production is unaffected — iili.uk is serving current code (its live bundle calls Generated by Claude Code |
|
Correcting my comment above: the Workers setting is not stale — it reads The rest stands — the red check is unrelated to this diff, and Generated by Claude Code |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
iili | 8404df4 | Commit Preview URL Branch Preview URL |
Aug 23 2026, 12:55 PM |
-Wall adds nothing today — the tree already emits these 20 warnings and the build exits 0 anyway, so what the issue is really asking for is -Werror. Scoped to domains/ like the thread-safety flag three lines above, and for the same reason. All 20 were in one_d4_worker. Abseil deprecated MutexLock's pointer constructor; Or and PinTypeOf in pg_game_sink were dead copies of the live ones in occurrence_writer. The ignored [[nodiscard]] was hiding a bug: SimpleAtoi leaves its out param unspecified when it fails, which the parsed = 0 initialization does not protect against, so unparseable input returned garbage rather than the 0 the caller expects. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ST7XLGWstRE4CfUF84ijKD
Review panel: unanchored, domains/.* also matches the exec path of generated sources under bazel-out/*/bin/domains/, putting 21 of 199 compiled files under -Werror that nobody here writes — including cc_proto_library output, which has no copts attribute to opt back out with. Excluding them afterwards does not work: the filter matches the owner label as well as the path, so -bazel-out.* silently changes nothing. Anchoring does, and costs no hand-written file: 18 generated .pb.cc compile actions now carry no -Werror, 22 hand-written ones in the same closure carry it. The thread-safety line above carries the identical over-match, so it is anchored too. CLAUDE.md gets the escape hatch, since the trigger is a scheduled grouped dependency bump: a -Wno-error line below the flag, not copts on the target, which the flag's own -Wall would re-enable. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ST7XLGWstRE4CfUF84ijKD
The .bazelrc note argued for the flag and repeated the scoping rationale from the line above it; what a reader needs is the anchor, which is silently wrong to drop. The ToInt comments keep the one fact that stops the explicit 0 being simplified back into the bug it fixes. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ST7XLGWstRE4CfUF84ijKD
-Werror covers a translation unit, so a warning in a dependency's header was fatal in our own compile. external_include_paths puts those headers behind -isystem. First-party enforcement is unchanged: an unused function added to shortener.cc still fails the build. MonthAlreadyIndexed parsed games_count with std::stoi, which throws, and the worker catches nothing — a malformed row would take the process down rather than answer. It reads as a status now, like the other two parses. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ST7XLGWstRE4CfUF84ijKD
The toolchain passes -Wall but not -Wextra, so this one adds coverage rather than repeating it, and the tree is already clean under it: no source changes. Verified the probe bites first — an unused parameter errors with the flag and not without, -Wunused-parameter being in -Wextra and not -Wall. Affordable now because external_include_paths landed first; the demonstrated blocker was -Wsign-compare firing inside gtest.h. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ST7XLGWstRE4CfUF84ijKD
-Wextra turns up three checks -Wall does not, in 62 places: - unused parameter, on overrides and default no-op hooks that ignore part of their interface. [[maybe_unused]] keeps the name, which the doc comments on RunObserver and Detector refer to. - missing designated field initializer, on aggregates whose omitted field had no default member initializer. Fixed at the struct where the field is ours (OtelConfig::histogram_bounds, ChainOptions::allow_request, AppContext::selected), so every caller is covered; named at the call site for smithy's RejectedRequest. - missing field initializer, on tracy's UNSET sentinel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ST7XLGWstRE4CfUF84ijKD
325b945 to
8404df4
Compare
Closes #1408.
Scoped to
domains/like the thread-safety-Werrorabove it, for the reason that comment already gives: external code is not ours to fix.The anchor is the load-bearing character
--per_file_coptmatches the exec path, and generated sources live underbazel-out/*/bin/domains/…, so an unanchoreddomains/.*also gates code nobody here writes — 21 of the 199 compiled sources: smithy codegen, andcards.pb.cc.cc_proto_libraryhas nocoptsattribute, so proto codegen would have had no local way out at all.Excluding them afterwards does not work: the filter matches the owner label as well as the path, so
-bazel-out.*leaves the label match intact and changes nothing. Anchoring is the fix, measured per compile action:-Werrorcards/.pb.ccThe pre-existing thread-safety line carried the identical over-match, so it is anchored too — a deliberate change to a neighbouring flag, called out here rather than slipped in. Generated code loses
-Werror=thread-safety-analysis, which is a no-op: codegen carries noABSL_GUARDED_BYannotations.-Werror is per translation unit, so dependency headers needed exempting
Scoping by source path does not scope by code ownership. This repo emitted every dependency include as
-I/-iquote, never-isystem, so a warning inside a third-party header was fatal in our compile — demonstrated with a probe flag that produced a-Wsign-compareerror insidegtest.h.--features=external_include_pathsputs those headers behind-isystem.Verified three ways, because a change that suppresses warnings is exactly the kind that can quietly disarm the thing this PR exists to enforce:
gtest.hprobe now builds clean;aqueryshows 28-isystemflags on that target, where the count before was 0;shortener.ccstill fails witherror: unused function [-Werror,-Wunused-function].Across the tree, warnings fall from 1,664 to 248. The residue is external source files (186 postgres, 62 protobuf) —
-isystemexempts third-party headers, not third-party.c/.cc, which is the right boundary.-Wall does nothing, and never did
toolchains_llvmpasses-Wallto every compile unconditionally (cc_toolchain_config.bzl), whichaqueryconfirms — it appears twice on first-party sources, once on external. Stronger still: building the first-party tree with-Wno-allproduces zero diagnostics, so there are no-Wall-class findings here at all. The 20 findings below all came from clang's default-on set, which-Werrorpromotes independently.-Wallstays as portability insurance if the toolchain moves; it is not doing work today and I would rather say so than imply it swept something up.-Wextrais a different story — the toolchain does not pass it, and it is where most of this diff comes from.The 20 from -Werror alone, all in one_d4_worker
12 ×
-Wdeprecated-declarations— Abseil deprecatedMutexLock's pointer constructor. Six sites inlease_core.cc. Semantically identical, not merely compiling: the deprecated constructor delegates to the reference one (MutexLock(Mutex* mu) : MutexLock(*mu) {}), so there is no second code path. Verified thread-safety analysis still binds through the new form with a negative control — deleting a lock line still produces the expectedrequires holding mutexerrors.2 ×
-Wunused-function—OrandPinTypeOfinpg_game_sink.cc, dead copies of live helpers inoccurrence_writer.cc. Both were internal-linkage on both sides, so there was never a cross-TU relationship to break.2 ×
-Wunused-result— an ignored[[nodiscard]]onabsl::SimpleAtoi, inpg_queue.ccandreanalysis_queue.cc.The parse fixes are defensive, not live bugs
SimpleAtoileaves its out param unspecified on failure, and abseil's implementation writes meaningful wrong values into it — measured against this exact version:"12x""3.9""99999999999999"The
int parsed = 0initialiser looks like protection and isn't, and theINT_MAXcase fedreport.games_processed += …— signed-overflow UB and a negative counter.But it is unreachable. Every column feeding these is Postgres
INT(V015__dispatch_columns.sql,V017__reanalysis_requests.sql), whose text output is always well-formed int32, and NULL already mapped to 0 on both paths. So no caller relied on the old behaviour and none could have. An earlier draft of this description called it a live bug; it is a real defect in the function, on input the schema cannot currently produce.The same reasoning covers
MonthAlreadyIndexed, which parsedgames_countwithstd::stoi. That one throws, and the worker contains notry/catch— a malformed row would have taken the process down rather than answered wrong.games_countisINT NOT NULL, so equally unreachable, but the function already returnsabsl::StatusOr, so it costs nothing to answer with a status. That was the last throwing parse in first-party C++.Consequence worth stating: no test covers these branches — reverting them leaves CI green.
ToIntis file-local with no seam, and extracting a three-line helper purely to exercise a branch production cannot reach seemed the worse trade. Flagging it rather than leaving it implicit.-Wextra: 65 findings, three checks, 16 files
The toolchain does not pass
-Wextra, so these are genuinely new. They fall into three checks and none of them is a live bug — this is the cost of the flag, stated as a cost.55 ×
-Wunused-parameter. Overrides and default no-op hooks that ignore part of an interface they must still accept: test fakes inrun_ceiling_test,poller_test,hub_e2e_test, thecontextargument smithy hands every operation, and theRunObserver/Detectordefault hooks whose whole point is that a subclass overrides only what it cares about.[[maybe_unused]]rather than dropping the name, because the doc comments on those two interfaces refer to the parameters by name.This is the check with an ongoing tax: every future no-op override needs the annotation. It is the standard reason people adopt
-Wextrawith-Wno-unused-parameter, and it is a one-line change if it grates. I did not make it, because #1408 says no warnings, not no warnings except the tedious ones.9 ×
-Wmissing-designated-field-initializers, fixed in 4 places. Clang flags a designated initializer that omits a field — but only when that field has no default member initializer. So every one of these is fixed at the struct, which covers all present and future callers and states the default the code was already relying on:OtelConfig::histogram_boundsstd::map<…> histogram_bounds;= {}ChainOptions::allow_requeststd::function<…> allow_request;= nullptrAppContext::selectedSDL_FRect selected;= {}ChainOptions::allow_requestis the clearest case: its own doc comment says "Leaveallow_requestunset for services without a rate limiter", so the warning was firing on documented intent. The NSDMI makes the documented default the declared one.The fourth is smithy's
BeastServerTransport::RejectedRequest, whose threestd::stringfields have no NSDMIs. Not ours to edit, so the one call site names them explicitly — filed upstream as muchq/smithy-cpp#193, which would let that revert to the terse form.1 ×
-Wmissing-field-initializers.tracy::constants::UNSETwasRGB_Double{-1.0}, leavinggandbto zero-init. Spelled{-1.0, 0.0, 0.0}— the same value, becauseUNSETis compared withoperator==against real colours and changing the sentinel would change which colours match it.How this landed in two commits, and why the first one was wrong
I pushed
-Wextraclaiming zero first-party findings. That was false, and CI caught it within minutes:tracy.h:18failed the tsan job.The claim came from a grep over the build log anchored as
^domains/. Two things defeat it. Clang reports a header reached through a relative include as./domains/…, and bazel writes ANSI colour codes into a redirected log, so the line actually begins\x1b[1mdomains/…. The pattern matched neither, returned zero, and I read the zero as a result instead of as a pattern that had never matched anything. The build summary on the same screen said30 fail to build; I did not reconcile the two.The rebuilt measurement strips ANSI, accepts the
./prefix, and iterates to a fixed point — a compile aborts after roughly twenty errors, so one pass under-reports. It is mutation-checked: removing a single[[maybe_unused]]fromindex_run.hfails the build withunused parameter 'game' [-Werror,-Wunused-parameter]. And every subsequent zero-warning claim here was checked against a known-bad log first, so a zero means zero rather than a pattern that never matched.Review panel
Three lenses, run against the
-Werrorhalf of this change. The anchoring finding above is the one that changed the diff; the header exposure and thestd::stoisite were the other two survivors, both folded in here. Also cleared: no first-party C++ exists outside//domains/...(one vendored header, no compile action of its own); no exec-config C++ compiles, so--host_per_file_coptis not needed; sanitizer copts land before the per-file ones and compose fine; iOS carries no C++; there are noselect()ed C++ sources or OS#ifdefs; and the sweep is complete — zeroMutexLock(&remain underdomains/, and every parse of a database column now checks its result.The panel did not cover the
-Wextracommit, which came later — saying so rather than letting the section imply otherwise. That commit's evidence is the mutation check, the clean full build, and the test suite, all below.CLAUDE.mdgains the escape hatch under "Things that bite": add a-Wno-error=line below this one, since later flags win. The obvious alternative does not reliably work —coptson the target can be re-enabled by this flag's-Walllanding afterwards, and no C++ target in this repo has ever usedcopts.Verified
On the rebased base (
mainat f18cb09, which brings #1439's C++ under these flags for the first time):bazel test //domains/... //bazel/...→ 218 tests pass, and a fullbazel build --keep_going //domains/...over 626 targets reports zero diagnostics from first-party files. #1439's code needed no changes.One first-party source is not covered locally:
hello_raylib.cc, because@raylib//:raylib_cmakeneedslibxrandrheaders this sandbox lacks, so nothing downstream of it compiles here. It is 17 lines with no parameters and no aggregates, and CI builds it.CI runs the full
//...here —.bazelrcis inFULL_BUILD_PATHSPECS, sodiff-buildtakes the full-build branch rather than an impact subset — and the sanitizer jobs cover-c dbg, which my local-c optruns do not. That is the configuration that caughttracy.h, and all three sanitizers are green on the rebased head.🤖 Generated with Claude Code
https://claude.ai/code/session_01ST7XLGWstRE4CfUF84ijKD