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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
]
|
||||
;
|
||||
|
||||
|
||||
137
test/testwave/default_hooks.cpp
Normal file
137
test/testwave/default_hooks.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user