Skip to content

Linting: reject more use-after-moves - #7451

Merged
jtolio merged 17 commits into
mainfrom
jolio/linting-reje-yw7unu
Sep 25, 2026
Merged

jtolio merged 17 commits into
mainfrom
jolio/linting-reje-yw7unu

Conversation

@jtolio

@jtolio jtolio commented Sep 21, 2026

Copy link
Copy Markdown
Member

The upstream "bugprone-use-after-move" linter has two problems:

  1. First, it has not been configured to recognize kj::mv!

  2. but, we can't just tell it to recognize kj::mv without causing a huge amount of false positives.

So, we fork it to be more kj friendly and tell it about kj::mv.

Confirmed that this identified a few existing use after moves.

@jtolio
jtolio requested review from a team as code owners September 21, 2026 15:30
@ask-bonk

ask-bonk Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

@jtolio Bonk workflow was cancelled.

View workflow run · To retry, trigger Bonk again.

@fhanau

fhanau commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Out of curiosity, which false positives did you get when adding kj::mv to use-after-move.InvalidationFunctions? Wonder if this is something we could upstream

@jtolio
jtolio force-pushed the jolio/linting-reje-yw7unu branch 2 times, most recently from bf4dd99 to 1a65b64 Compare September 21, 2026 18:50
@jtolio

jtolio commented Sep 21, 2026

Copy link
Copy Markdown
Member Author

Out of curiosity, which false positives did you get when adding kj::mv to use-after-move.InvalidationFunctions? Wonder if this is something we could upstream

I'm still working through it, but the main problem is KJ_CASE_ONEOF, which expands to a loop, which executes at most once, but the linter doesn't understand there won't be another iteration. There's hundreds of issues due to that. I think I can fix everything else.

@jtolio

jtolio commented Sep 21, 2026

Copy link
Copy Markdown
Member Author

This change would benefit from capnproto/capnproto#2833, but does not need it right now.

@codecov-commenter

codecov-commenter commented Sep 21, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 38.43%. Comparing base (7a7ad83) to head (13aea4b).

Files with missing lines Patch % Lines
src/workerd/jsg/unwrap-args-test.c++ 83.92% 0 Missing and 9 partials ⚠️
src/workerd/api/capnp.c++ 0.00% 4 Missing ⚠️
src/workerd/jsg/weakref-test.c++ 20.00% 0 Missing and 4 partials ⚠️
src/workerd/io/io-own-test.c++ 0.00% 0 Missing and 3 partials ⚠️
src/workerd/io/trace-stream.c++ 50.00% 0 Missing and 1 partial ⚠️
src/workerd/server/server.c++ 90.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7451   +/-   ##
=======================================
  Coverage   38.42%   38.43%           
=======================================
  Files         858      858           
  Lines      264343   264389   +46     
  Branches    24596    24602    +6     
=======================================
+ Hits       101577   101619   +42     
+ Misses     148948   148947    -1     
- Partials    13818    13823    +5     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/workerd/api/kv.c++ Outdated
Comment thread tools/clang-tidy/use-after-move.c++ Outdated
@jtolio
jtolio force-pushed the jolio/linting-reje-yw7unu branch 2 times, most recently from f16e110 to 0a94273 Compare September 22, 2026 18:32
Comment thread src/workerd/io/incoming-request-test.c++ Outdated
@jtolio
jtolio force-pushed the jolio/linting-reje-yw7unu branch from 102a04a to ca56b7e Compare September 25, 2026 15:06
@jtolio

jtolio commented Sep 25, 2026

Copy link
Copy Markdown
Member Author

Rebase (some conflicts) ^

@jtolio
jtolio force-pushed the jolio/linting-reje-yw7unu branch from ca56b7e to 3ef9816 Compare September 25, 2026 15:07
@jtolio

jtolio commented Sep 25, 2026

Copy link
Copy Markdown
Member Author

Add support for kj::Own, kj::Rc, kj::Arc as standard smart pointers ^

@jtolio
jtolio force-pushed the jolio/linting-reje-yw7unu branch from 3ef9816 to 3e63510 Compare September 25, 2026 15:08
@jtolio

jtolio commented Sep 25, 2026

Copy link
Copy Markdown
Member Author

Trivial rebase ^

jtolio and others added 9 commits September 25, 2026 13:26
Argument evaluation order is actually undefined, so, this
could not end up working out right. Force the order instead.
This was essentially:

    return context.attachSpans(
        js,
        context.awaitIo(...,
            [traceContext = kj::mv(traceContext)](...) { ... }),
        kj::mv(traceContext));

so, let's remove the second traceContext attachment since it is
already inside the continuation.
The upstream "bugprone-use-after-move" linter has two problems:

 1) First, it has not been configured to recognize kj::mv!

 2) but, we can't just tell it to recognize kj::mv without causing
    a huge amount of false positives.

So, we fork it to be more kj friendly and tell it about kj::mv.

Confirmed that this identified a few existing use after moves.
RemotePromise stores its promise and pipeline as separate base
subobjects. Consuming both by moving the same RemotePromise twice
is correct but trips use-after-move linting; releaseParts() (from
capnproto/capnproto#2833) releases both at once instead.
Keep the upstream source and header verbatim so the adaptation can be
reviewed separately. They are deliberately outside the build until the
following commit supplies the local interface and CFG adjustment hook.

Source: llvmorg-22.1.5, clang-tools-extra/clang-tidy/bugprone.
https://github.com/llvm/llvm-project/tree/llvmorg-22.1.5

The original copyright notices and LLVM license are retained.

Assisted-by: Codex:gpt-6-astra
KJ_CASE_ONEOF clears its loop sentinel after the selected body, but
Clang's CFG still permits another iteration. Suppressing moves inside
the macro hides real uses; deciding from later references also admits
false positives for unevaluated expressions. Redirect the generated
back edge to the loop exit before sequencing and reachability analysis.

LLVM 22 keeps this CFG private to its checker implementation. Preserve
its analysis in an attributed local snapshot with a CFG adjustment hook,
leaving the shared AST unchanged. Exercise the actual dependency macros
with uses inside and after cases, reinitialization, unevaluated uses,
continue, nested cases, lambdas, and genuine loops and goto edges.

Assisted-by: Codex:gpt-6-astra
Exercise coroutine move-captures with the real KJ_CASE_ONEOF
macro. Accept a safe capture while retaining diagnostics for
use after the capture and move-then-use inside its lambda.
Consuming each slot with `kj::mv(unwrapped).template take<I>()...`
repeats an rvalue cast of the same object, which the use-after-move
check reports. Add an rvalue-qualified apply() that forwards every
slot to a callback in one consuming call and returns its result.

Convert the extraction tests to apply(), preserving value, rvalue-
and lvalue-reference coverage, and add tests for return forwarding,
void callbacks and an empty argument pack.
Replace the repeated `kj::mv(unwrapped).template take<indexes>()...`
expansions in constructor, method, static-method and functor
callbacks with a single `kj::mv(unwrapped).apply(...)` call.
Behavior, including void results and return-value wrapping, is
unchanged.
Only `prefix` is consumed; `reverse` and `limit` are read afterwards.
Write `kj::mv(options.prefix)` so the partial consumption is explicit
rather than moving the whole options struct.
makeWorkerImpl() read `containerEngineConf`, `isDynamic` and
`localActorConfigs` from `def` after moving it into the link
callback. Save them beforehand, keeping a reference to the existing
actor-config map rather than copying it.
kj::Own, kj::Rc and kj::Arc null out their source on move, just as
std::unique_ptr, std::shared_ptr and std::weak_ptr are specified to.
Extend the use-after-move check's smart-pointer exemption to them, so
only dereferencing a moved-from KJ pointer is flagged.
reportImpl() clones the event for earlier recipients and moves it
only for the last one, but the checker assumes another iteration can
follow the move. Annotate the read with NOLINT and explain the
invariant, preserving the no-copy path for a single tail worker.
The WeakRef and ReverseIoOwn tests inspect moved-from objects to
verify ownership transfer and invalid-access behavior. Annotate those
assertions with NOLINT.
@jtolio
jtolio force-pushed the jolio/linting-reje-yw7unu branch from 3e63510 to 13aea4b Compare September 25, 2026 17:26
@jtolio
jtolio merged commit 4d84c2c into main Sep 25, 2026
36 of 38 checks passed
@jtolio
jtolio deleted the jolio/linting-reje-yw7unu branch September 25, 2026 20:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants