2
0
mirror of https://github.com/boostorg/wave.git synced 2026-01-19 04:42:16 +00:00

Fix line directives for ifdef and ifndef when default hooks are used (#140)

At some point in the past the handling for #if and #ifdef diverged. The code that handles emitting a line directive when a conditional section is skipped worked for #if but not ifdef/ifndef.

This problem was not observable when the eat_whitespace hooks were used instead of the default_preprocessing hooks, because the former
signals skipped newlines through the may_skip_whitespace hook, hiding the problem. Furthermore, the majority of Wave tests use the eat_whitespace hooks, so it wasn't visible there.

This change restores ifdef/ifndef to the same section as #if, so any changes to conditional handling will happen uniformly. Also, a test case is added to cover the default hooks and this particular case.
This commit is contained in:
Jeff Trull
2022-01-02 07:09:32 -08:00
committed by GitHub
parent 07f853eda7
commit f2957045b5
3 changed files with 183 additions and 60 deletions

View File

@@ -327,10 +327,12 @@ protected:
void on_define(parse_node_type const &node);
void on_undefine(lexer_type const &it);
void on_ifdef(result_type const& found_directive, lexer_type const &it);
// typename parse_tree_type::const_iterator const &end);
void on_ifndef(result_type const& found_directive, lexer_type const& it);
// typename parse_tree_type::const_iterator const &end);
void on_ifdef(result_type const& found_directive,
typename parse_tree_type::const_iterator const &begin,
typename parse_tree_type::const_iterator const &end);
void on_ifndef(result_type const& found_directive,
typename parse_tree_type::const_iterator const &begin,
typename parse_tree_type::const_iterator const &end);
void on_else();
void on_endif();
void on_illformed(typename result_type::string_type s);
@@ -946,7 +948,7 @@ namespace impl {
util::impl::call_skipped_token_hook(ctx, *it);
for (++it; it != end; ++it) {
token_id id = token_id(*it);
token_id id = token_id(*it);
if (T_CPPCOMMENT == id || T_NEWLINE == id ||
context_policies::util::ccomment_has_newline(*it))
@@ -1045,8 +1047,7 @@ pp_iterator_functor<ContextT>::ensure_is_last_on_line(IteratorT& it, bool call_h
// enable error recovery (start over with the next line)
impl::skip_to_eol(ctx, it, iter_ctx->last);
string_type str(util::impl::as_string<string_type>(
iter_ctx->first, it));
string_type str(util::impl::as_string<string_type>(iter_ctx->first, it));
seen_newline = true;
iter_ctx->first = it;
@@ -1254,24 +1255,6 @@ pp_iterator_functor<ContextT>::handle_pp_directive(IteratorT &it)
call_hook_in_skip = false;
break;
case T_PP_IFDEF: // #ifdef
if (!impl::call_found_directive_hook(ctx, *it) &&
extract_identifier(it))
{
on_ifdef(directive, it);
}
call_hook_in_skip = false;
break;
case T_PP_IFNDEF: // #ifndef
if (!impl::call_found_directive_hook(ctx, *it) &&
extract_identifier(it))
{
on_ifndef(directive, it);
}
call_hook_in_skip = false;
break;
#if BOOST_WAVE_SUPPORT_MS_EXTENSIONS != 0
// case T_MSEXT_PP_REGION: // #region ...
// break;
@@ -1466,13 +1449,13 @@ pp_iterator_functor<ContextT>::dispatch_directive(
// on_undefine(*nodeval.begin());
// break;
//
// case T_PP_IFDEF: // #ifdef
// on_ifdef(found_directive, begin_child_it, end_child_it);
// break;
//
// case T_PP_IFNDEF: // #ifndef
// on_ifndef(found_directive, begin_child_it, end_child_it);
// break;
case T_PP_IFDEF: // #ifdef
on_ifdef(found_directive, begin_child_it, end_child_it);
break;
case T_PP_IFNDEF: // #ifndef
on_ifndef(found_directive, begin_child_it, end_child_it);
break;
case T_PP_IF: // #if
on_if(found_directive, begin_child_it, end_child_it);
@@ -1860,26 +1843,23 @@ pp_iterator_functor<ContextT>::on_undefine (lexer_type const &it)
template <typename ContextT>
inline void
pp_iterator_functor<ContextT>::on_ifdef(
result_type const& found_directive, lexer_type const &it)
// typename parse_tree_type::const_iterator const &it)
// typename parse_tree_type::const_iterator const &end)
result_type const& found_directive,
typename parse_tree_type::const_iterator const &begin,
typename parse_tree_type::const_iterator const &end)
{
// get_token_value<result_type, parse_node_type> get_value;
// token_sequence_type toexpand;
//
// std::copy(make_ref_transform_iterator((*begin).children.begin(), get_value),
// make_ref_transform_iterator((*begin).children.end(), get_value),
// std::inserter(toexpand, toexpand.end()));
get_token_value<result_type, parse_node_type> get_value;
token_sequence_type toexpand;
std::copy(make_ref_transform_iterator((*begin).children.begin(), get_value),
make_ref_transform_iterator((*begin).children.end(), get_value),
std::inserter(toexpand, toexpand.end()));
bool is_defined = false;
token_sequence_type directive;
directive.insert(directive.end(), *it);
do {
is_defined = ctx.is_defined_macro((*it).get_value()); // toexpand.begin(), toexpand.end());
is_defined = ctx.is_defined_macro(toexpand.begin(), toexpand.end());
} while (ctx.get_hooks().evaluated_conditional_expression(ctx.derived(),
found_directive, directive, is_defined));
found_directive, toexpand, is_defined));
ctx.enter_if_block(is_defined);
}
@@ -1891,26 +1871,23 @@ pp_iterator_functor<ContextT>::on_ifdef(
template <typename ContextT>
inline void
pp_iterator_functor<ContextT>::on_ifndef(
result_type const& found_directive, lexer_type const &it)
// typename parse_tree_type::const_iterator const &it)
// typename parse_tree_type::const_iterator const &end)
result_type const& found_directive,
typename parse_tree_type::const_iterator const &begin,
typename parse_tree_type::const_iterator const &end)
{
// get_token_value<result_type, parse_node_type> get_value;
// token_sequence_type toexpand;
//
// std::copy(make_ref_transform_iterator((*begin).children.begin(), get_value),
// make_ref_transform_iterator((*begin).children.end(), get_value),
// std::inserter(toexpand, toexpand.end()));
get_token_value<result_type, parse_node_type> get_value;
token_sequence_type toexpand;
std::copy(make_ref_transform_iterator((*begin).children.begin(), get_value),
make_ref_transform_iterator((*begin).children.end(), get_value),
std::inserter(toexpand, toexpand.end()));
bool is_defined = false;
token_sequence_type directive;
directive.insert(directive.end(), *it);
do {
is_defined = ctx.is_defined_macro((*it).get_value()); // toexpand.begin(), toexpand.end());
is_defined = ctx.is_defined_macro(toexpand.begin(), toexpand.end());
} while (ctx.get_hooks().evaluated_conditional_expression(ctx.derived(),
found_directive, directive, is_defined));
found_directive, toexpand, is_defined));
ctx.enter_if_block(!is_defined);
}

View File

@@ -224,5 +224,14 @@ test-suite wave
/boost/thread//boost_thread
/boost/filesystem//boost_filesystem
]
[
run
# sources
../testwave/default_hooks.cpp
/boost/wave//boost_wave
/boost/thread//boost_thread
/boost/filesystem//boost_filesystem
]
;

View File

@@ -0,0 +1,137 @@
// Copyright 2018 Peter Dimov.
// Copyrigth 2022 Jeff Trull.
//
// Distributed under the Boost Software License, Version 1.0.
//
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
// Most test coverage of Wave happens with the "eat_whitespace" hooks in effect
// This adds some coverage for Wave with the (minimal) default hooks
#include <boost/wave.hpp>
#include <boost/wave/token_ids.hpp>
#include <boost/wave/cpplexer/cpp_lex_token.hpp>
#include <boost/wave/cpplexer/cpp_lex_iterator.hpp>
#include <iostream>
#include <vector>
#include <utility>
#include <string>
int main()
{
std::string input(
// check LINE directives after conditional (false) sections
"#if 0\n"
"int aa;\n"
"#endif\n"
"char c1;\n" // #LINE 4
"#ifdef FOO\n"
"int bb;\n"
"#endif\n"
"char c2;\n" // #LINE 8
"#ifndef __FILE__\n"
"int cc;\n"
"#endif\n"
"char c3;\n" // #LINE 12
);
using namespace boost::wave;
typedef std::pair<token_id, char const*> tokdata_type;
typedef std::vector<tokdata_type> tokdata_list_type;
tokdata_list_type expected{
{T_PP_LINE, "#line"},
{T_SPACE, " "},
{T_INTLIT, "4"},
{T_SPACE, " "},
{T_STRINGLIT, nullptr}, // exact filepath is not interesting
{T_NEWLINE, "\n"},
{T_CHAR, "char"},
{T_SPACE, " "},
{T_IDENTIFIER, "c1"},
{T_SEMICOLON, ";"},
{T_NEWLINE, "\n"},
{T_PP_LINE, "#line"},
{T_SPACE, " "},
{T_INTLIT, "8"},
{T_SPACE, " "},
{T_STRINGLIT, nullptr},
{T_NEWLINE, "\n"},
{T_CHAR, "char"},
{T_SPACE, " "},
{T_IDENTIFIER, "c2"},
{T_SEMICOLON, ";"},
{T_NEWLINE, "\n"},
{T_PP_LINE, "#line"},
{T_SPACE, " "},
{T_INTLIT, "12"},
{T_SPACE, " "},
{T_STRINGLIT, nullptr},
{T_NEWLINE, "\n"},
{T_CHAR, "char"},
{T_SPACE, " "},
{T_IDENTIFIER, "c3"},
{T_SEMICOLON, ";"},
{T_NEWLINE, "\n"},
{T_EOF, nullptr}
};
try
{
typedef cpplexer::lex_token<> token_type;
typedef cpplexer::lex_iterator<token_type> lex_iterator_type;
typedef context<std::string::iterator, lex_iterator_type> context_type;
context_type ctx( input.begin(), input.end(), "input.cpp" );
tokdata_list_type::const_iterator first_expect = expected.begin();
tokdata_list_type::const_iterator last_expect = expected.end();
for( context_type::iterator_type first = ctx.begin(), last = ctx.end(); first != last; ++first, ++first_expect )
{
if (first_expect == last_expect)
{
std::cerr << "more tokens were produced than expected\n";
return 3;
}
if (token_id(*first) != first_expect->first)
{
std::cerr << "expected " << get_token_name(first_expect->first) << ", got " << get_token_name(token_id(*first)) << "\n";
return 3;
}
if ((first_expect->second != nullptr) && (first_expect->second != first->get_value()))
{
std::cerr << "expected token value " << first_expect->second << ", got " << first->get_value() << "\n";
return 3;
}
std::cout << first->get_value();
}
if (first_expect != last_expect)
{
std::cerr << "fewer tokens were produced than expected\n";
return 3;
}
return 0;
}
catch( cpp_exception const & x )
{
std::cerr << x.file_name() << "(" << x.line_no() << "): " << x.description() << std::endl;
return 1;
}
catch( std::exception const & x )
{
std::cerr << "Exception: " << x.what() << std::endl;
return 2;
}
}