PtmfHelper: Use NOLINT to disable UndefinedBinaryOperatorResult error#2334
Merged
Conversation
Disable clang-analyzer-core.UndefinedBinaryOperatorResult error in PtmfHelper to appease clang-tidy. This project does not use clang-tidy, but downstream projects may. Normally errors from Cap'n Proto headers will not show up in downstream projects as long as they are included via -isystem, but in some cases it is impossible to hide errors even from system headers and other excluded headers if clang-tidy decides the errors are associated with the "main file" of the translation unit. This seems to be a longstanding issue that's discussed in https://reviews.llvm.org/D26418. Adding NOLINT here is helpful to downstream projects that don't currently have a way of suppressing this error without turning it off more globally, and could be helpful to Cap'n Proto if it uses clang-tidy or clang-analyzer in the future. This particular tidy error is a false positive from clang-analzyer (https://clang-analyzer.llvm.org/) warning that the vtable pointer `*(char**)obj` is a garbage value rather than a valid memory address. The analyzer has no way of knowing it could be a valid address since it's only valid due to assumptions about the ABI. The complete error message from a downstream project looks like: /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: error: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult,-warnings-as-errors] 609 | return *(void**)(*(char**)obj + voff); | ^ build/test/mp/test/foo.capnp.proxy-server.c++:93:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>, mp::ServerField<0, mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>>' 93 | return serverInvoke(*this, call_context, MakeServerField<0, Accessor<foo_fields::Context, FIELD_IN | FIELD_BOXED>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT>>(ServerCall()))); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 740 | [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); }) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:19: note: 'is_same_v' is false 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:5: note: Taking false branch 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^ include/mp/proxy-types.h:704:16: note: Calling 'operator()' 704 | return fn(); | ^~~~ include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:563:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>' 563 | return PassField<Accessor>(Priority<2>(), | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 564 | typename Split<argc, ArgTypes>::First(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 565 | server_context, | ~~~~~~~~~~~~~~~ 566 | this->parent(), | ~~~~~~~~~~~~~~~ 567 | typename Split<argc, ArgTypes>::Second(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 568 | std::forward<Args>(args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/type-context.h:151:12: note: Calling 'CapabilityServerSet::getLocalServer' 151 | return server.m_context.connection->m_threads.getLocalServer(thread_client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/capability.h:1274:10: note: Calling 'Promise::then' 1274 | return getLocalServerInternal(client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1275 | .then([](void* server) -> kj::Maybe<typename T::Server&> { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1276 | if (server == nullptr) { | ~~~~~~~~~~~~~~~~~~~~~~~~ 1277 | return nullptr; | ~~~~~~~~~~~~~~~ 1278 | } else { | ~~~~~~~~ 1279 | return *reinterpret_cast<typename T::Server*>(server); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1280 | } | ~ 1281 | }); | ~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:1295:32: note: Calling 'GetFunctorStartAddress::apply' 1295 | void* continuationTracePtr = _::GetFunctorStartAddress<_::FixVoid<T>&&>::apply(func); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:677:12: note: Calling 'PtmfHelper::apply' 677 | return PtmfHelper::from<ReturnType, Decay<Func>, ParamTypes...>( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 678 | &Decay<Func>::operator()).apply(&func); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:9: note: Assuming the condition is true 606 | if (voff & 1) { | ^~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:5: note: Taking true branch 606 | if (voff & 1) { | ^ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: note: The left operand of '+' is a garbage value 609 | return *(void**)(*(char**)obj + voff); | ~~~~~~~~~~~~ ^
ryanofsky
added a commit
to ryanofsky/libmultiprocess
that referenced
this pull request
Jun 20, 2025
Reported by fanquake <fanquake@gmail.com> bitcoin/bitcoin#31802 (comment) https://cirrus-ci.com/task/6552721135763456 https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log Error is spurious and comes from kj/async-inl.h and it seems to be shown because clang-tidy considers this error to come from "main file" of the translation unit (see https://clang.llvm.org/extra/clang-tidy/, https://stackoverflow.com/a/47611238, https://reviews.llvm.org/D26418) even though this is not the case. Even though the header is included via -isystem and clang-tidy --dump-config shows "SystemHeaders: false" the error is still shown. I also tried to suppress the error by editing .clang-tidy to include: HeaderFilterRegex: '.*' ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$' and many variations but the error is always shown, I assume because ExcludeHeaderFilterRegex does not ignore the isInMainFile condition (https://github.com/llvm/llvm-project/pull/91400/files). Adding NOLINT to the getLocalServer() line in types-context.h at the boundary between libmultiprocess and Cap'n Proto code also does not suppress the error. It does suppress clang-tidy "note:" lines below the NOLINT point in the call stack, making the error messages shorter, but the only way of suppressing the error completely seems to be either adding NOLINT inside the Cap'n Proto header, which requires a patch, or adding it to all top-level callers of the getLocalServer() function in .cpp files, which seems impractical and overbroad, and I didn't attempt. The actual warning is a false positive that comes from ABI-specific code in Cap'n Proto that gets the starting function address (that can be passed to addr2line) from a lambda or function object. This code calls a helper to get the starting function address from a pointer-to-member-function, which in this case is the the operator() member function. That code handles pointers to virtual member functions, so it checks if the pointer is virtual by testing its low-order bit, and if set, assumes the first bytes of the object are a vtable pointer, and does pointer arithmetic with the vtable address. Clang-tidy complains about this because it does not know the vtable address is valid, assuming incorrectly it is a "garbage value". I wrote patch adding NOLINT to Cap'n Proto in capnproto/capnproto#2334 which could be good long-term fix for this issue if clang-tidy does not provide a better way of suppressing the error. Complete error output is: /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: error: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult,-warnings-as-errors] 609 | return *(void**)(*(char**)obj + voff); | ^ build/test/mp/test/foo.capnp.proxy-server.c++:93:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>, mp::ServerField<0, mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>>' 93 | return serverInvoke(*this, call_context, MakeServerField<0, Accessor<foo_fields::Context, FIELD_IN | FIELD_BOXED>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT>>(ServerCall()))); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 740 | [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); }) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:19: note: 'is_same_v' is false 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:5: note: Taking false branch 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^ include/mp/proxy-types.h:704:16: note: Calling 'operator()' 704 | return fn(); | ^~~~ include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:563:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>' 563 | return PassField<Accessor>(Priority<2>(), | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 564 | typename Split<argc, ArgTypes>::First(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 565 | server_context, | ~~~~~~~~~~~~~~~ 566 | this->parent(), | ~~~~~~~~~~~~~~~ 567 | typename Split<argc, ArgTypes>::Second(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 568 | std::forward<Args>(args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/type-context.h:151:12: note: Calling 'CapabilityServerSet::getLocalServer' 151 | return server.m_context.connection->m_threads.getLocalServer(thread_client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/capability.h:1274:10: note: Calling 'Promise::then' 1274 | return getLocalServerInternal(client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1275 | .then([](void* server) -> kj::Maybe<typename T::Server&> { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1276 | if (server == nullptr) { | ~~~~~~~~~~~~~~~~~~~~~~~~ 1277 | return nullptr; | ~~~~~~~~~~~~~~~ 1278 | } else { | ~~~~~~~~ 1279 | return *reinterpret_cast<typename T::Server*>(server); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1280 | } | ~ 1281 | }); | ~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:1295:32: note: Calling 'GetFunctorStartAddress::apply' 1295 | void* continuationTracePtr = _::GetFunctorStartAddress<_::FixVoid<T>&&>::apply(func); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:677:12: note: Calling 'PtmfHelper::apply' 677 | return PtmfHelper::from<ReturnType, Decay<Func>, ParamTypes...>( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 678 | &Decay<Func>::operator()).apply(&func); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:9: note: Assuming the condition is true 606 | if (voff & 1) { | ^~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:5: note: Taking true branch 606 | if (voff & 1) { | ^ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: note: The left operand of '+' is a garbage value 609 | return *(void**)(*(char**)obj + voff); | ~~~~~~~~~~~~ ^
Member
|
Thanks! |
ryanofsky
added a commit
to ryanofsky/libmultiprocess
that referenced
this pull request
Jun 23, 2025
Reported by fanquake <fanquake@gmail.com> bitcoin/bitcoin#31802 (comment) https://cirrus-ci.com/task/6552721135763456 https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log Error is spurious and comes from kj/async-inl.h and should be suppressed in the next version of capnproto capnproto/capnproto#2334 The error is a clang-analyzer false positive that comes from ABI-specific code in Cap'n Proto that gets the starting function address (that can be passed to addr2line) from a lambda or function object. This code calls a helper to get the starting function address from a pointer-to-member-function, which in this case is the the operator() member function. That code handles pointers to virtual member functions, so it checks if the pointer is virtual by testing its low-order bit, and if set, assumes the first bytes of the object are a vtable pointer, and does pointer arithmetic with the vtable address. Clang-tidy complains about this because it does not know the vtable address is valid, assuming incorrectly it is a "garbage value". This change turns off the UndefinedBinaryOperatorResult altogether instead of suppressing this one instance because clang-tidy incorrectly considers this error to come from "main file" of the translation unit (see https://clang.llvm.org/extra/clang-tidy/, https://stackoverflow.com/a/47611238, https://reviews.llvm.org/D26418). So it is not suppressed even though the header is included via -isystem and clang-tidy --dump-config shows "SystemHeaders: false". It is also not suppressed when exclude patterns are added to the .clang-tidy configuration like: HeaderFilterRegex: '.*' ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$' This has no effect because ExcludeHeaderFilterRegex does not override the isInMainFile condition (https://github.com/llvm/llvm-project/pull/91400/files). Adding NOLINT to the getLocalServer() line in types-context.h at the boundary between libmultiprocess and Cap'n Proto code also does not suppress the error. It does suppress clang-tidy "note:" lines below the NOLINT point in the call stack, making the error messages shorter, but the only way of suppressing the error completely seems to be either adding NOLINT inside the Cap'n Proto header, which requires a patch, or adding it to all top-level callers of the getLocalServer() function in .cpp files, which seems impractical and overbroad, and I didn't attempt. Complete error output is: /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: error: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult,-warnings-as-errors] 609 | return *(void**)(*(char**)obj + voff); | ^ build/test/mp/test/foo.capnp.proxy-server.c++:93:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>, mp::ServerField<0, mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>>' 93 | return serverInvoke(*this, call_context, MakeServerField<0, Accessor<foo_fields::Context, FIELD_IN | FIELD_BOXED>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT>>(ServerCall()))); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 740 | [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); }) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:19: note: 'is_same_v' is false 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:5: note: Taking false branch 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^ include/mp/proxy-types.h:704:16: note: Calling 'operator()' 704 | return fn(); | ^~~~ include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:563:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>' 563 | return PassField<Accessor>(Priority<2>(), | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 564 | typename Split<argc, ArgTypes>::First(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 565 | server_context, | ~~~~~~~~~~~~~~~ 566 | this->parent(), | ~~~~~~~~~~~~~~~ 567 | typename Split<argc, ArgTypes>::Second(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 568 | std::forward<Args>(args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/type-context.h:151:12: note: Calling 'CapabilityServerSet::getLocalServer' 151 | return server.m_context.connection->m_threads.getLocalServer(thread_client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/capability.h:1274:10: note: Calling 'Promise::then' 1274 | return getLocalServerInternal(client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1275 | .then([](void* server) -> kj::Maybe<typename T::Server&> { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1276 | if (server == nullptr) { | ~~~~~~~~~~~~~~~~~~~~~~~~ 1277 | return nullptr; | ~~~~~~~~~~~~~~~ 1278 | } else { | ~~~~~~~~ 1279 | return *reinterpret_cast<typename T::Server*>(server); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1280 | } | ~ 1281 | }); | ~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:1295:32: note: Calling 'GetFunctorStartAddress::apply' 1295 | void* continuationTracePtr = _::GetFunctorStartAddress<_::FixVoid<T>&&>::apply(func); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:677:12: note: Calling 'PtmfHelper::apply' 677 | return PtmfHelper::from<ReturnType, Decay<Func>, ParamTypes...>( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 678 | &Decay<Func>::operator()).apply(&func); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:9: note: Assuming the condition is true 606 | if (voff & 1) { | ^~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:5: note: Taking true branch 606 | if (voff & 1) { | ^ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: note: The left operand of '+' is a garbage value 609 | return *(void**)(*(char**)obj + voff); | ~~~~~~~~~~~~ ^
ryanofsky
added a commit
to ryanofsky/libmultiprocess
that referenced
this pull request
Jun 25, 2025
Reported by fanquake <fanquake@gmail.com> bitcoin/bitcoin#31802 (comment) https://cirrus-ci.com/task/6552721135763456 https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log Error is spurious and comes from kj/async-inl.h and should be suppressed in the next version of capnproto capnproto/capnproto#2334 The error is a clang-analyzer false positive that comes from ABI-specific code in Cap'n Proto that gets the starting function address (that can be passed to addr2line) from a lambda or function object. This code calls a helper to get the starting function address from a pointer-to-member-function, which in this case is the the operator() member function. That code handles pointers to virtual member functions, so it checks if the pointer is virtual by testing its low-order bit, and if set, assumes the first bytes of the object are a vtable pointer, and does pointer arithmetic with the vtable address. Clang-tidy complains about this because it does not know the vtable address is valid, assuming incorrectly it is a "garbage value". This change turns off the UndefinedBinaryOperatorResult altogether instead of suppressing this one instance because clang-tidy incorrectly considers this error to come from "main file" of the translation unit (see https://clang.llvm.org/extra/clang-tidy/, https://stackoverflow.com/a/47611238, https://reviews.llvm.org/D26418). So it is not suppressed even though the header is included via -isystem and clang-tidy --dump-config shows "SystemHeaders: false". It is also not suppressed when exclude patterns are added to the .clang-tidy configuration like: HeaderFilterRegex: '.*' ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$' This has no effect because ExcludeHeaderFilterRegex does not override the isInMainFile condition (https://github.com/llvm/llvm-project/pull/91400/files). Adding NOLINT to the getLocalServer() line in types-context.h at the boundary between libmultiprocess and Cap'n Proto code also does not suppress the error. It does suppress clang-tidy "note:" lines below the NOLINT point in the call stack, making the error messages shorter, but the only way of suppressing the error completely seems to be either adding NOLINT inside the Cap'n Proto header, which requires a patch, or adding it to all top-level callers of the getLocalServer() function in .cpp files, which seems impractical and overbroad, and I didn't attempt. Complete error output is: /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: error: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult,-warnings-as-errors] 609 | return *(void**)(*(char**)obj + voff); | ^ build/test/mp/test/foo.capnp.proxy-server.c++:93:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>, mp::ServerField<0, mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>>' 93 | return serverInvoke(*this, call_context, MakeServerField<0, Accessor<foo_fields::Context, FIELD_IN | FIELD_BOXED>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT>>(ServerCall()))); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 740 | [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); }) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:19: note: 'is_same_v' is false 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:5: note: Taking false branch 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^ include/mp/proxy-types.h:704:16: note: Calling 'operator()' 704 | return fn(); | ^~~~ include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:563:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>' 563 | return PassField<Accessor>(Priority<2>(), | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 564 | typename Split<argc, ArgTypes>::First(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 565 | server_context, | ~~~~~~~~~~~~~~~ 566 | this->parent(), | ~~~~~~~~~~~~~~~ 567 | typename Split<argc, ArgTypes>::Second(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 568 | std::forward<Args>(args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/type-context.h:151:12: note: Calling 'CapabilityServerSet::getLocalServer' 151 | return server.m_context.connection->m_threads.getLocalServer(thread_client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/capability.h:1274:10: note: Calling 'Promise::then' 1274 | return getLocalServerInternal(client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1275 | .then([](void* server) -> kj::Maybe<typename T::Server&> { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1276 | if (server == nullptr) { | ~~~~~~~~~~~~~~~~~~~~~~~~ 1277 | return nullptr; | ~~~~~~~~~~~~~~~ 1278 | } else { | ~~~~~~~~ 1279 | return *reinterpret_cast<typename T::Server*>(server); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1280 | } | ~ 1281 | }); | ~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:1295:32: note: Calling 'GetFunctorStartAddress::apply' 1295 | void* continuationTracePtr = _::GetFunctorStartAddress<_::FixVoid<T>&&>::apply(func); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:677:12: note: Calling 'PtmfHelper::apply' 677 | return PtmfHelper::from<ReturnType, Decay<Func>, ParamTypes...>( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 678 | &Decay<Func>::operator()).apply(&func); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:9: note: Assuming the condition is true 606 | if (voff & 1) { | ^~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:5: note: Taking true branch 606 | if (voff & 1) { | ^ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: note: The left operand of '+' is a garbage value 609 | return *(void**)(*(char**)obj + voff); | ~~~~~~~~~~~~ ^
ryanofsky
added a commit
to ryanofsky/libmultiprocess
that referenced
this pull request
Jun 25, 2025
Reported by fanquake <fanquake@gmail.com> bitcoin/bitcoin#31802 (comment) https://cirrus-ci.com/task/6552721135763456 https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log Error is spurious and comes from kj/async-inl.h and should be suppressed in the next version of capnproto capnproto/capnproto#2334 The error is a clang-analyzer false positive that comes from ABI-specific code in Cap'n Proto that gets the starting function address (that can be passed to addr2line) from a lambda or function object. This code calls a helper to get the starting function address from a pointer-to-member-function, which in this case is the the operator() member function. That code handles pointers to virtual member functions, so it checks if the pointer is virtual by testing its low-order bit, and if set, assumes the first bytes of the object are a vtable pointer, and does pointer arithmetic with the vtable address. Clang-tidy complains about this because it does not know the vtable address is valid, assuming incorrectly it is a "garbage value". This change turns off the UndefinedBinaryOperatorResult altogether instead of suppressing this one instance because clang-tidy incorrectly considers this error to come from "main file" of the translation unit (see https://clang.llvm.org/extra/clang-tidy/, https://stackoverflow.com/a/47611238, https://reviews.llvm.org/D26418). So it is not suppressed even though the header is included via -isystem and clang-tidy --dump-config shows "SystemHeaders: false". It is also not suppressed when exclude patterns are added to the .clang-tidy configuration like: HeaderFilterRegex: '.*' ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$' This has no effect because ExcludeHeaderFilterRegex does not override the isInMainFile condition (https://github.com/llvm/llvm-project/pull/91400/files). Adding NOLINT to the getLocalServer() line in types-context.h at the boundary between libmultiprocess and Cap'n Proto code also does not suppress the error. It does suppress clang-tidy "note:" lines below the NOLINT point in the call stack, making the error messages shorter, but the only way of suppressing the error completely seems to be either adding NOLINT inside the Cap'n Proto header, which requires a patch, or adding it to all top-level callers of the getLocalServer() function in .cpp files, which seems impractical and overbroad, and I didn't attempt. Complete error output is: /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: error: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult,-warnings-as-errors] 609 | return *(void**)(*(char**)obj + voff); | ^ build/test/mp/test/foo.capnp.proxy-server.c++:93:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>, mp::ServerField<0, mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>>' 93 | return serverInvoke(*this, call_context, MakeServerField<0, Accessor<foo_fields::Context, FIELD_IN | FIELD_BOXED>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT>>(ServerCall()))); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 740 | [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); }) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:19: note: 'is_same_v' is false 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:5: note: Taking false branch 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^ include/mp/proxy-types.h:704:16: note: Calling 'operator()' 704 | return fn(); | ^~~~ include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:563:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>' 563 | return PassField<Accessor>(Priority<2>(), | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 564 | typename Split<argc, ArgTypes>::First(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 565 | server_context, | ~~~~~~~~~~~~~~~ 566 | this->parent(), | ~~~~~~~~~~~~~~~ 567 | typename Split<argc, ArgTypes>::Second(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 568 | std::forward<Args>(args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/type-context.h:151:12: note: Calling 'CapabilityServerSet::getLocalServer' 151 | return server.m_context.connection->m_threads.getLocalServer(thread_client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/capability.h:1274:10: note: Calling 'Promise::then' 1274 | return getLocalServerInternal(client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1275 | .then([](void* server) -> kj::Maybe<typename T::Server&> { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1276 | if (server == nullptr) { | ~~~~~~~~~~~~~~~~~~~~~~~~ 1277 | return nullptr; | ~~~~~~~~~~~~~~~ 1278 | } else { | ~~~~~~~~ 1279 | return *reinterpret_cast<typename T::Server*>(server); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1280 | } | ~ 1281 | }); | ~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:1295:32: note: Calling 'GetFunctorStartAddress::apply' 1295 | void* continuationTracePtr = _::GetFunctorStartAddress<_::FixVoid<T>&&>::apply(func); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:677:12: note: Calling 'PtmfHelper::apply' 677 | return PtmfHelper::from<ReturnType, Decay<Func>, ParamTypes...>( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 678 | &Decay<Func>::operator()).apply(&func); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:9: note: Assuming the condition is true 606 | if (voff & 1) { | ^~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:5: note: Taking true branch 606 | if (voff & 1) { | ^ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: note: The left operand of '+' is a garbage value 609 | return *(void**)(*(char**)obj + voff); | ~~~~~~~~~~~~ ^
ryanofsky
added a commit
to ryanofsky/libmultiprocess
that referenced
this pull request
Jun 25, 2025
Reported by fanquake <fanquake@gmail.com> bitcoin/bitcoin#31802 (comment) https://cirrus-ci.com/task/6552721135763456 https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log Error is spurious and comes from kj/async-inl.h and should be suppressed in the next version of capnproto capnproto/capnproto#2334 The error is a clang-analyzer false positive that comes from ABI-specific code in Cap'n Proto that gets the starting function address (that can be passed to addr2line) from a lambda or function object. This code calls a helper to get the starting function address from a pointer-to-member-function, which in this case is the the operator() member function. That code handles pointers to virtual member functions, so it checks if the pointer is virtual by testing its low-order bit, and if set, assumes the first bytes of the object are a vtable pointer, and does pointer arithmetic with the vtable address. Clang-tidy complains about this because it does not know the vtable address is valid, assuming incorrectly it is a "garbage value". This change turns off the UndefinedBinaryOperatorResult altogether instead of suppressing this one instance because clang-tidy incorrectly considers this error to come from "main file" of the translation unit (see https://clang.llvm.org/extra/clang-tidy/, https://stackoverflow.com/a/47611238, https://reviews.llvm.org/D26418). So it is not suppressed even though the header is included via -isystem and clang-tidy --dump-config shows "SystemHeaders: false". It is also not suppressed when exclude patterns are added to the .clang-tidy configuration like: HeaderFilterRegex: '.*' ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$' This has no effect because ExcludeHeaderFilterRegex does not override the isInMainFile condition (https://github.com/llvm/llvm-project/pull/91400/files). Adding NOLINT to the getLocalServer() line in types-context.h at the boundary between libmultiprocess and Cap'n Proto code also does not suppress the error. It does suppress clang-tidy "note:" lines below the NOLINT point in the call stack, making the error messages shorter, but the only way of suppressing the error completely seems to be either adding NOLINT inside the Cap'n Proto header, which requires a patch, or adding it to all top-level callers of the getLocalServer() function in .cpp files, which seems impractical and overbroad, and I didn't attempt. Complete error output is: /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: error: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult,-warnings-as-errors] 609 | return *(void**)(*(char**)obj + voff); | ^ build/test/mp/test/foo.capnp.proxy-server.c++:93:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>, mp::ServerField<0, mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>>' 93 | return serverInvoke(*this, call_context, MakeServerField<0, Accessor<foo_fields::Context, FIELD_IN | FIELD_BOXED>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT>>(ServerCall()))); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 740 | [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); }) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:19: note: 'is_same_v' is false 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:5: note: Taking false branch 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^ include/mp/proxy-types.h:704:16: note: Calling 'operator()' 704 | return fn(); | ^~~~ include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:563:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>' 563 | return PassField<Accessor>(Priority<2>(), | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 564 | typename Split<argc, ArgTypes>::First(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 565 | server_context, | ~~~~~~~~~~~~~~~ 566 | this->parent(), | ~~~~~~~~~~~~~~~ 567 | typename Split<argc, ArgTypes>::Second(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 568 | std::forward<Args>(args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/type-context.h:151:12: note: Calling 'CapabilityServerSet::getLocalServer' 151 | return server.m_context.connection->m_threads.getLocalServer(thread_client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/capability.h:1274:10: note: Calling 'Promise::then' 1274 | return getLocalServerInternal(client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1275 | .then([](void* server) -> kj::Maybe<typename T::Server&> { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1276 | if (server == nullptr) { | ~~~~~~~~~~~~~~~~~~~~~~~~ 1277 | return nullptr; | ~~~~~~~~~~~~~~~ 1278 | } else { | ~~~~~~~~ 1279 | return *reinterpret_cast<typename T::Server*>(server); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1280 | } | ~ 1281 | }); | ~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:1295:32: note: Calling 'GetFunctorStartAddress::apply' 1295 | void* continuationTracePtr = _::GetFunctorStartAddress<_::FixVoid<T>&&>::apply(func); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:677:12: note: Calling 'PtmfHelper::apply' 677 | return PtmfHelper::from<ReturnType, Decay<Func>, ParamTypes...>( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 678 | &Decay<Func>::operator()).apply(&func); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:9: note: Assuming the condition is true 606 | if (voff & 1) { | ^~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:5: note: Taking true branch 606 | if (voff & 1) { | ^ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: note: The left operand of '+' is a garbage value 609 | return *(void**)(*(char**)obj + voff); | ~~~~~~~~~~~~ ^
ryanofsky
added a commit
to ryanofsky/libmultiprocess
that referenced
this pull request
Jun 25, 2025
Reported by fanquake <fanquake@gmail.com> bitcoin/bitcoin#31802 (comment) https://cirrus-ci.com/task/6552721135763456 https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log Error is spurious and comes from kj/async-inl.h and should be suppressed in the next version of capnproto capnproto/capnproto#2334 The error is a clang-analyzer false positive that comes from ABI-specific code in Cap'n Proto that gets the starting function address (that can be passed to addr2line) from a lambda or function object. This code calls a helper to get the starting function address from a pointer-to-member-function, which in this case is the the operator() member function. That code handles pointers to virtual member functions, so it checks if the pointer is virtual by testing its low-order bit, and if set, assumes the first bytes of the object are a vtable pointer, and does pointer arithmetic with the vtable address. Clang-tidy complains about this because it does not know the vtable address is valid, assuming incorrectly it is a "garbage value". This change turns off the UndefinedBinaryOperatorResult altogether instead of suppressing this one instance because clang-tidy incorrectly considers this error to come from "main file" of the translation unit (see https://clang.llvm.org/extra/clang-tidy/, https://stackoverflow.com/a/47611238, https://reviews.llvm.org/D26418). So it is not suppressed even though the header is included via -isystem and clang-tidy --dump-config shows "SystemHeaders: false". It is also not suppressed when exclude patterns are added to the .clang-tidy configuration like: HeaderFilterRegex: '.*' ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$' This has no effect because ExcludeHeaderFilterRegex does not override the isInMainFile condition (https://github.com/llvm/llvm-project/pull/91400/files). Adding NOLINT to the getLocalServer() line in types-context.h at the boundary between libmultiprocess and Cap'n Proto code also does not suppress the error. It does suppress clang-tidy "note:" lines below the NOLINT point in the call stack, making the error messages shorter, but the only way of suppressing the error completely seems to be either adding NOLINT inside the Cap'n Proto header, which requires a patch, or adding it to all top-level callers of the getLocalServer() function in .cpp files, which seems impractical and overbroad, and I didn't attempt. Complete error output is: /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: error: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult,-warnings-as-errors] 609 | return *(void**)(*(char**)obj + voff); | ^ build/test/mp/test/foo.capnp.proxy-server.c++:93:12: note: Calling 'serverInvoke<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>, mp::ServerField<0, mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>>>' 93 | return serverInvoke(*this, call_context, MakeServerField<0, Accessor<foo_fields::Context, FIELD_IN | FIELD_BOXED>>(Make<ServerRet, Accessor<foo_fields::Result, FIELD_OUT>>(ServerCall()))); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 740 | [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); }) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:19: note: 'is_same_v' is false 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:5: note: Taking false branch 700 | if constexpr (std::is_same_v<decltype(fn()), void>) { | ^ include/mp/proxy-types.h:704:16: note: Calling 'operator()' 704 | return fn(); | ^~~~ include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:563:16: note: Calling 'PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooFn>, capnp::CallContext<mp::test::messages::FooFn::CallParams, mp::test::messages::FooFn::CallResults>>, mp::ServerRet<mp::Accessor<mp::foo_fields::Result, 2>, mp::ServerCall>, mp::TypeList<>>' 563 | return PassField<Accessor>(Priority<2>(), | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 564 | typename Split<argc, ArgTypes>::First(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 565 | server_context, | ~~~~~~~~~~~~~~~ 566 | this->parent(), | ~~~~~~~~~~~~~~~ 567 | typename Split<argc, ArgTypes>::Second(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 568 | std::forward<Args>(args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/type-context.h:151:12: note: Calling 'CapabilityServerSet::getLocalServer' 151 | return server.m_context.connection->m_threads.getLocalServer(thread_client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/capability.h:1274:10: note: Calling 'Promise::then' 1274 | return getLocalServerInternal(client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1275 | .then([](void* server) -> kj::Maybe<typename T::Server&> { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1276 | if (server == nullptr) { | ~~~~~~~~~~~~~~~~~~~~~~~~ 1277 | return nullptr; | ~~~~~~~~~~~~~~~ 1278 | } else { | ~~~~~~~~ 1279 | return *reinterpret_cast<typename T::Server*>(server); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1280 | } | ~ 1281 | }); | ~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:1295:32: note: Calling 'GetFunctorStartAddress::apply' 1295 | void* continuationTracePtr = _::GetFunctorStartAddress<_::FixVoid<T>&&>::apply(func); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:677:12: note: Calling 'PtmfHelper::apply' 677 | return PtmfHelper::from<ReturnType, Decay<Func>, ParamTypes...>( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 678 | &Decay<Func>::operator()).apply(&func); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:9: note: Assuming the condition is true 606 | if (voff & 1) { | ^~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:5: note: Taking true branch 606 | if (voff & 1) { | ^ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: note: The left operand of '+' is a garbage value 609 | return *(void**)(*(char**)obj + voff); | ~~~~~~~~~~~~ ^
ryanofsky
added a commit
to ryanofsky/bitcoin
that referenced
this pull request
Sep 2, 2025
Disable `clang-analyzer-core.UndefinedBinaryOperatorResult` warning because it produces a false positive in a Cap'n Proto header: /usr/include/kj/async-inl.h:609:37: warning: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult] 609 | return *(void**)(*(char**)obj + voff); This was reported by fanquake in bitcoin#33256 and was previously addressed in bitcoin-core/libmultiprocess@7631345 which changed the analyzer result to be a warning instead of an error. PR capnproto/capnproto#2334 was also merged upstream to prevent this in future versions of Cap'n Proto. Unfortunately it does not seem possible suppress the false-positive warning from this header without turning off the check for bitcoin code as well. The header included with -isystem so it is treated as a third party header, but clang-tidy has a longstanding issue discussed in https://reviews.llvm.org/D26418 where it ignores -isystem and ignores HeaderFilterRegex and ExcludeHeaderFilterRegex directives if it decides that errors are associated with the "main file" of the translation unit, which is the case here. (See "isInMainFile" in https://github.com/llvm/llvm-project/pull/91400/files) The tidy error is a false positive from clang-analzyer (https://clang-analyzer.llvm.org/) warning that the vtable pointer `*(char**)obj` is a garbage value rather than a valid memory address. The analyzer has no way of knowing it could be a valid address since it's only valid due to assumptions about the ABI. The purpose of the code is to get a starting function address that can be passed to addr2line from a lambda or function object. The function `GetFunctorStartAddress` calls a helper `PtmfHelper::apply` to get the starting function address from a pointer-to-member-function value pointing at the operator() method. The helper needs to handle virtual methods, so it checks if the pointer is virtual by testing its low-order bit, and if set, assumes the first bytes of the object are a vtable pointer, and does pointer arithmetic with the vtable address. Clang-tidy complains about this because it does not know the vtable address is valid, assuming incorrectly it is a "garbage value".
hebasto
added a commit
to hebasto/bitcoin
that referenced
this pull request
Sep 4, 2025
The warnings are false positive and have been fixed upstream. See: capnproto/capnproto#2334. This change disables the `UndefinedBinaryOperatorResult` clang-tidy check for source files generated by the `mpgen` tool.
achow101
added a commit
to bitcoin/bitcoin
that referenced
this pull request
Sep 8, 2025
…ck in `src/ipc` 589b65f clang-tidy: Disable `UndefinedBinaryOperatorResult` check in `src/ipc` (Hennadii Stepanov) Pull request description: The warnings are false positive and have been fixed upstream. See: capnproto/capnproto#2334. This PR: 1. Disables the `UndefinedBinaryOperatorResult` clang-tidy check for source files generated by the `mpgen` tool. 2. Is an alternative to the draft #33281. 3. Fixes #33256. ACKs for top commit: Sjors: ACK 589b65f fjahr: ACK 589b65f achow101: ACK 589b65f ryanofsky: Code review ACK 589b65f. Thanks for the fix! Tree-SHA512: 6d376a82641a5b85d4dd1fa164fdcbd8e15f1262e7d4f582f4d9959031d35852e28ff1b8268336e39ba6779fdd10ecdb986af42407d0545f4217f41d64556272
Kino1994
pushed a commit
to Kino1994/bitcoin-full-history
that referenced
this pull request
Jun 28, 2026
The warnings are false positive and have been fixed upstream. See: capnproto/capnproto#2334. This change disables the `UndefinedBinaryOperatorResult` clang-tidy check for source files generated by the `mpgen` tool.
Kino1994
pushed a commit
to Kino1994/bitcoin-full-history
that referenced
this pull request
Jun 28, 2026
…atorResult` check in `src/ipc` 96d0687 clang-tidy: Disable `UndefinedBinaryOperatorResult` check in `src/ipc` (Hennadii Stepanov) Pull request description: The warnings are false positive and have been fixed upstream. See: capnproto/capnproto#2334. This PR: 1. Disables the `UndefinedBinaryOperatorResult` clang-tidy check for source files generated by the `mpgen` tool. 2. Is an alternative to the draft bitcoin/bitcoin#33281. 3. Fixes bitcoin/bitcoin#33256. ACKs for top commit: Sjors: ACK 96d0687 fjahr: ACK 96d0687 achow101: ACK 96d0687 ryanofsky: Code review ACK 96d0687. Thanks for the fix! Tree-SHA512: 6d376a82641a5b85d4dd1fa164fdcbd8e15f1262e7d4f582f4d9959031d35852e28ff1b8268336e39ba6779fdd10ecdb986af42407d0545f4217f41d64556272
BigcoinBGC
pushed a commit
to BigcoinBGC/bigcoin
that referenced
this pull request
Jun 30, 2026
The warnings are false positive and have been fixed upstream. See: capnproto/capnproto#2334. This change disables the `UndefinedBinaryOperatorResult` clang-tidy check for source files generated by the `mpgen` tool.
BigcoinBGC
pushed a commit
to BigcoinBGC/bigcoin
that referenced
this pull request
Jun 30, 2026
…atorResult` check in `src/ipc` c15da4f clang-tidy: Disable `UndefinedBinaryOperatorResult` check in `src/ipc` (Hennadii Stepanov) Pull request description: The warnings are false positive and have been fixed upstream. See: capnproto/capnproto#2334. This PR: 1. Disables the `UndefinedBinaryOperatorResult` clang-tidy check for source files generated by the `mpgen` tool. 2. Is an alternative to the draft bitcoin/bitcoin#33281. 3. Fixes bitcoin/bitcoin#33256. ACKs for top commit: Sjors: ACK c15da4f fjahr: ACK c15da4f achow101: ACK c15da4f ryanofsky: Code review ACK c15da4f. Thanks for the fix! Tree-SHA512: 6d376a82641a5b85d4dd1fa164fdcbd8e15f1262e7d4f582f4d9959031d35852e28ff1b8268336e39ba6779fdd10ecdb986af42407d0545f4217f41d64556272
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Disable
clang-analyzer-core.UndefinedBinaryOperatorResulterror inPtmfHelperusing a NOLINT comment to appease clang-tidy.A complete description of the error and rationale for the change is in the commit message.