boostorg / wave

Boost.org wave module
http://boost.org/libs/wave
21 stars 49 forks source link

Parsing __has_include() fails with trailing tokens #143

Closed r0mai closed 2 years ago

r0mai commented 2 years ago

Lines containing a __has_include() operator will fail if any token (even whitespace) follows the __has_include() expression.

I believe the error can be traced back to this look-ahead loop, which implicitly expects the last token of the line to be the matching closing parenthesis of the __has_include expression:

https://github.com/boostorg/wave/blob/b8cbd86ab907f5dd42bc807a37b6729b84013382/include/boost/wave/util/cpp_macromap.hpp#L1789-L1797

Repro code:

#include <string>
#include <iostream>
#include <boost/wave.hpp>
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp>

int main() {
#define CASE_1
#if defined(CASE_1) // BUG: any token after a __has_include expression will fail parsing
    std::string input =
        "#if __has_include(<foo.h>) && 0\n"
        "#endif\n"
        ;
#elif defined(CASE_2) // BUG: even with trailing whitespace fails
    std::string input =
        "#if __has_include(<foo.h>) \n"
        "#endif\n"
        ;
#elif defined(CASE_3) // GOOD: no token after __has_include succeeds
    std::string input =
        "#if __has_include(<foo.h>)\n"
        "#endif\n"
        ;
#endif

    typedef boost::wave::cpplexer::lex_iterator<
            boost::wave::cpplexer::lex_token<>>
        lex_iterator_type;
    typedef boost::wave::context<
            std::string::iterator, lex_iterator_type>
        context_type;

    context_type ctx(input.begin(), input.end(), "input.cpp");
    ctx.set_language(boost::wave::language_support::support_option_has_include);

    auto first = ctx.begin();
    auto last = ctx.end();

    try {
        while (first != last) {
            std::cout << (*first).get_value();
            ++first;
        }
    } catch (boost::wave::preprocess_exception& e) {
        std::cout << boost::wave::preprocess_exception::error_text(e.get_errorcode()) << std::endl;
        return 1;
    }
    std::cout << "Success" << std::endl;
    return 0;
}
jefftrull commented 2 years ago

@r0mai Thanks for the excellent bug report. Your analysis was correct! I believe the PR branch resolves your issue. Please let me know if not.

r0mai commented 2 years ago

@jefftrull I tried the PR branch, and it works perfectly. Thank you very much for the quick fix!