From 41d939b2bce5763cb657e730d49e125c3860fc44 Mon Sep 17 00:00:00 2001 From: Zach Laine Date: Sun, 16 Nov 2025 09:31:38 +0100 Subject: [PATCH] In the out-argument overload of rule_parser::call(), always parse into a local attr_type attribute ('attr'), rather than the one supplied ('retval'). Then, take the parsed attribute and either combine it with retval (if they are both containers), or assign attr to retval (if attr_type is not none). Reproducer by Andreas Buhr. Fixes #294. --- include/boost/parser/parser.hpp | 22 ++++++++++++++---- test/github_issues.cpp | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/include/boost/parser/parser.hpp b/include/boost/parser/parser.hpp index ef0d469e..caf72e85 100644 --- a/include/boost/parser/parser.hpp +++ b/include/boost/parser/parser.hpp @@ -5549,8 +5549,9 @@ namespace boost { namespace parser { locals_type locals = detail::make_locals(context); auto params = detail::resolve_rule_params(context, params_); tag_type * const tag_ptr = nullptr; + attr_type attr{}; auto const rule_context = detail::make_rule_context( - context, tag_ptr, retval, locals, params); + context, tag_ptr, attr, locals, params); [[maybe_unused]] auto _ = detail::scoped_trace( *this, first, last, rule_context, flags, retval); @@ -5565,9 +5566,22 @@ namespace boost { namespace parser { flags, success, dont_assign, - retval); - if (!success || dont_assign) - retval = Attribute_(); + attr); + + if (dont_assign) + return; + + if (!success) + attr = attr_type{}; + + if constexpr (detail::is_nope_v) { + return; + } else if constexpr ( + container && container) { + detail::move_back(retval, attr, detail::gen_attrs(flags)); + } else { + detail::assign(retval, attr); + } } } diff --git a/test/github_issues.cpp b/test/github_issues.cpp index 23ace318..b36d872c 100644 --- a/test/github_issues.cpp +++ b/test/github_issues.cpp @@ -512,6 +512,45 @@ void github_pr_290() BOOST_TEST(*result == "foo"); } +namespace github_issue_294_ { + namespace bp = boost::parser; + struct Foo + {}; + constexpr bp::rule> foo_parser = + "foo_parser"; + constexpr auto foo_parser_action = [](auto & ctx) { + std::shared_ptr & val = _val(ctx); + val = std::shared_ptr(new Foo{}); + }; + constexpr auto foo_parser_def = bp::eps[foo_parser_action]; + struct Bar + { + std::shared_ptr foo; + }; + constexpr bp::rule> bar_parser = + "bar_parser"; + constexpr auto bar_parser_action = [](auto & ctx) { + std::shared_ptr & val = _val(ctx); + val = std::shared_ptr(new Bar{}); + std::optional> & attr = _attr(ctx); + if (attr) { + val->foo = attr.value(); + } + }; + constexpr auto bar_parser_def = + (bp::lit("(") > -foo_parser > bp::lit(")"))[bar_parser_action]; + + BOOST_PARSER_DEFINE_RULES(bar_parser, foo_parser); +} + +void github_issue_294() +{ + namespace bp = boost::parser; + using namespace github_issue_294_; + + bp::parse("()", bar_parser, bp::blank); +} + namespace github_pr_297_ { namespace bp = boost::parser; constexpr auto bar_required_f = [](auto& ctx) -> bool { @@ -558,6 +597,7 @@ void github_pr_297() } } + int main() { github_issue_36(); @@ -573,6 +613,7 @@ int main() github_issue_279(); github_issue_285(); github_pr_290(); + github_issue_294(); github_pr_297(); return boost::report_errors(); }