mirror of
https://github.com/logos-co/logos-plugin-qt.git
synced 2026-08-27 08:51:07 +00:00
fix(glue): a void method's REFUSAL is not a successful void call
The void arm discarded the dispatch reply. `value = QVariant(true)` was
unconditional: the JSON was parsed, then thrown away, so a provider answering
{"code":"invalid_args", ...} reached the caller as a successful void call.
Invisible until the providers gained something to refuse with. With the arity
upper bound in place (logos-cpp-sdk #150, logos-rust-sdk #50) the provider DOES
refuse `doVoid("junk")` -- the built module carries the `expected 0 arguments,
got ` literal, checked in the exact store path the failing run consumed -- and
the conformance cell still reported `true`, on both providers and all three
consumers. The twelve cells of the non-void arity cases went green in the same
run; these six did not, because their refusal died here rather than at the
provider.
Which is the same defect one layer down from the one the arity work started
from: an answer the caller cannot distinguish from a real one. There the
provider dropped an argument and answered anyway; here the provider refused
correctly and the glue reported success.
The closed set is the one logos-qt-sdk's consumer detector uses --
dispatch_failed / invalid_args / unknown_method -- matched only on an OBJECT
carrying a string `code`. A void method whose provider legitimately answers an
object is unaffected, and there is no such method: the contract says it returns
nothing. An ordinary reply still answers `true`; this is a branch, not a
replacement, and the test pins both sides.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c459e47787
commit
156e73f23e
@@ -328,8 +328,36 @@ QString lidlMakeCdylibGlueSource(const ModuleDecl& module, bool multi)
|
||||
s << " logos_module_string_free(result);\n";
|
||||
s << " if (!jResult.is_discarded()) {\n";
|
||||
if (!voidMethods.isEmpty()) {
|
||||
// A void method's reply is not "nothing to read" -- it is the only
|
||||
// place the provider can REFUSE the call, and this branch used to
|
||||
// discard it. `value = QVariant(true)` was unconditional: the
|
||||
// dispatch JSON was parsed, then thrown away, so a provider that
|
||||
// answered {"code":"invalid_args", ...} was reported to the caller
|
||||
// as a successful void call.
|
||||
//
|
||||
// Measured on failure/B/arity/too-many-zero-parameter: with the
|
||||
// arity upper bound in place (logos-cpp-sdk #150, logos-rust-sdk
|
||||
// #50) the provider DOES refuse `doVoid("junk")` -- the built
|
||||
// module carries the `expected 0 arguments, got ` literal -- and
|
||||
// the cell still reported `true` on both providers and all three
|
||||
// consumers, because the refusal died here rather than at the
|
||||
// provider. The same six cells stayed red while the twelve cells
|
||||
// of the non-void arity cases went green.
|
||||
//
|
||||
// The closed set is the one logos-qt-sdk's consumer detector uses
|
||||
// (dispatch_failed / invalid_args / unknown_method), matched only
|
||||
// on an OBJECT carrying a string `code`, so a void method whose
|
||||
// provider legitimately answers an object is unaffected -- and
|
||||
// there is no such method, since the contract says it returns
|
||||
// nothing.
|
||||
s << " if (isVoidMethod) {\n";
|
||||
s << " value = QVariant(true);\n";
|
||||
s << " const bool __rejected = jResult.is_object()\n";
|
||||
s << " && jResult.contains(\"code\") && jResult[\"code\"].is_string()\n";
|
||||
s << " && (jResult[\"code\"] == \"dispatch_failed\"\n";
|
||||
s << " || jResult[\"code\"] == \"invalid_args\"\n";
|
||||
s << " || jResult[\"code\"] == \"unknown_method\");\n";
|
||||
s << " value = __rejected ? logos::nlohmannToQVariant(jResult)\n";
|
||||
s << " : QVariant(true);\n";
|
||||
s << " } else\n";
|
||||
}
|
||||
if (!resultMethods.isEmpty()) {
|
||||
|
||||
@@ -150,6 +150,24 @@ pkgs.runCommand "logos-qt-host-generator-test" {
|
||||
|| { echo "multi worker lambda reads $flag but does not capture it: $worker_capture"; exit 1; }
|
||||
done
|
||||
|
||||
# A void method's REFUSAL must survive. This branch used to be an
|
||||
# unconditional `value = QVariant(true)`: the dispatch JSON was parsed and
|
||||
# then discarded, so a provider answering {"code":"invalid_args", ...} was
|
||||
# reported to the caller as a successful void call. With the arity upper
|
||||
# bound in the generated dispatches (logos-cpp-sdk #150, logos-rust-sdk #50)
|
||||
# the provider genuinely refuses `doVoid("junk")` -- and the conformance cell
|
||||
# still went green-on-`true` until this branch stopped throwing it away.
|
||||
grep -q '__rejected' $cm \
|
||||
|| { echo "the void arm does not test the reply for a rejection"; exit 1; }
|
||||
for code in dispatch_failed invalid_args unknown_method; do
|
||||
grep -q "\"$code\"" $cm \
|
||||
|| { echo "the void arm's rejection set omits $code"; exit 1; }
|
||||
done
|
||||
# It must still answer `true` for an ordinary void reply -- the fix is a
|
||||
# branch, not a replacement.
|
||||
grep -q 'QVariant(true)' $cm \
|
||||
|| { echo "the void arm no longer answers true for a normal reply"; exit 1; }
|
||||
|
||||
# void WITHOUT result — the fourth combination, and the one neither
|
||||
# hand-written capture list could express: it captured neither flag while the
|
||||
# body still named isVoidMethod, so `concurrency: "multi"` on any module with
|
||||
|
||||
Reference in New Issue
Block a user