diff --git a/src/Parser/Parsing/Impl/DefinesStreamProxy.cpp b/src/Parser/Parsing/Impl/DefinesStreamProxy.cpp index 40270152..449280ad 100644 --- a/src/Parser/Parsing/Impl/DefinesStreamProxy.cpp +++ b/src/Parser/Parsing/Impl/DefinesStreamProxy.cpp @@ -26,11 +26,13 @@ DefinesStreamProxy::DefineParameterPosition::DefineParameterPosition(const unsig { } -DefinesStreamProxy::Define::Define() = default; +DefinesStreamProxy::Define::Define() + : m_contains_token_joining_operators(false){}; DefinesStreamProxy::Define::Define(std::string name, std::string value) : m_name(std::move(name)), - m_value(std::move(value)) + m_value(std::move(value)), + m_contains_token_joining_operators(false) { } @@ -39,10 +41,10 @@ DefinesStreamProxy::MacroParameterState::MacroParameterState() { } -bool DefinesStreamProxy::Define::IsStringizeParameterBackwardsLookup(const std::string& value, unsigned pos) +bool DefinesStreamProxy::Define::IsStringizeParameterForwardLookup(const std::string& value, unsigned pos) { // Check if # is prepended to the word - return pos > 0 && value[pos - 1] == '#'; + return pos + 1 && (isalpha(value[pos + 1]) || value[pos + 1] == '_'); } bool DefinesStreamProxy::Define::IsTokenJoiningOperatorForwardLookup(const std::string& value, unsigned pos) @@ -54,15 +56,10 @@ void DefinesStreamProxy::Define::IdentifyTokenJoinsOnly() { for (auto i = 0u; i < m_value.size(); i++) { - const auto c = m_value[i]; - if (!isalnum(c) && c != '_') + if (m_value[i] == '#' && IsTokenJoiningOperatorForwardLookup(m_value, i)) { - if (c == '#' && IsTokenJoiningOperatorForwardLookup(m_value, i)) - { - m_token_joins.push_back(i); - m_value.erase(i, 2); - i -= 1; - } + m_contains_token_joining_operators = true; + return; } } } @@ -76,6 +73,7 @@ void DefinesStreamProxy::Define::IdentifyParameters(const std::vector m_parameter_positions; - std::vector m_token_joins; + bool m_contains_token_joining_operators; Define(); Define(std::string name, std::string value); void IdentifyParameters(const std::vector& parameterNames); private: - static bool IsStringizeParameterBackwardsLookup(const std::string& value, unsigned pos); + static bool IsStringizeParameterForwardLookup(const std::string& value, unsigned pos); static bool IsTokenJoiningOperatorForwardLookup(const std::string& value, unsigned pos); void IdentifyTokenJoinsOnly(); }; diff --git a/test/ParserTests/Parsing/Impl/DefinesStreamProxyTests.cpp b/test/ParserTests/Parsing/Impl/DefinesStreamProxyTests.cpp index f7253f50..7300a3fe 100644 --- a/test/ParserTests/Parsing/Impl/DefinesStreamProxyTests.cpp +++ b/test/ParserTests/Parsing/Impl/DefinesStreamProxyTests.cpp @@ -1093,6 +1093,42 @@ namespace test::parsing::impl::defines_stream_proxy REQUIRE(proxy.Eof()); } + TEST_CASE("DefinesStreamProxy: Can use token-pasting operator after length deflating macros", "[parsing][parsingstream]") + { + const std::vector lines{ + "#define VERY_LONG_MACRO \"T\"", + "#define testMacro(a) VERY_LONG_MACRO VERY_LONG_MACRO \"Hello\"##a VERY_LONG_MACRO", + "testMacro(\"World\")", + }; + + MockParserLineStream mockStream(lines); + DefinesStreamProxy proxy(&mockStream); + + ExpectLine(&proxy, 1, ""); + ExpectLine(&proxy, 2, ""); + ExpectLine(&proxy, 3, "\"T\" \"T\" \"HelloWorld\" \"T\""); + + REQUIRE(proxy.Eof()); + } + + TEST_CASE("DefinesStreamProxy: Can use token-pasting operator after length inflating macros", "[parsing][parsingstream]") + { + const std::vector lines{ + "#define BLA \"This is very long\"", + "#define testMacro(a) BLA BLA \"Hello\"##a BLA", + "testMacro(\"World\")", + }; + + MockParserLineStream mockStream(lines); + DefinesStreamProxy proxy(&mockStream); + + ExpectLine(&proxy, 1, ""); + ExpectLine(&proxy, 2, ""); + ExpectLine(&proxy, 3, "\"This is very long\" \"This is very long\" \"HelloWorld\" \"This is very long\""); + + REQUIRE(proxy.Eof()); + } + TEST_CASE("DefinesStreamProxy: Interprets nested macros in context of top-level macro", "[parsing][parsingstream]") { const std::vector lines{