Fix unmunch for template arguments

Fixes #147.
This commit is contained in:
Jonathan Müller 2022-10-13 09:20:33 +02:00
commit 34e7bb4bf4
10 changed files with 67 additions and 36 deletions

View file

@ -115,7 +115,7 @@ void add_base_class(cpp_class::builder& builder, const detail::parse_context& co
else else
detail::skip_if(stream, to_string(access)); detail::skip_if(stream, to_string(access));
auto name = detail::to_string(stream, stream.end()).as_string(); auto name = detail::to_string(stream, stream.end(), false).as_string();
auto type = detail::parse_type(context, class_cur, clang_getCursorType(cur)); auto type = detail::parse_type(context, class_cur, clang_getCursorType(cur));
auto& base = builder.base_class(std::move(name), std::move(type), access, is_virtual); auto& base = builder.base_class(std::move(name), std::move(type), access, is_virtual);

View file

@ -24,9 +24,8 @@ std::unique_ptr<cpp_entity> detail::try_parse_cpp_concept(const detail::parse_co
if (stream.peek() != "<") if (stream.peek() != "<")
return nullptr; return nullptr;
auto closing_bracket = detail::find_closing_bracket(stream);
auto closing_bracket_iter = detail::find_closing_bracket(stream); auto params = to_string(stream, closing_bracket.bracket, closing_bracket.unmunch);
auto params = to_string(stream, closing_bracket_iter);
if (!detail::skip_if(stream, ">")) if (!detail::skip_if(stream, ">"))
return nullptr; return nullptr;

View file

@ -8,8 +8,6 @@
#include "libclang_visitor.hpp" #include "libclang_visitor.hpp"
#include "parse_error.hpp" #include "parse_error.hpp"
#include <iostream> // TODO
using namespace cppast; using namespace cppast;
detail::cxtoken::cxtoken(const CXTranslationUnit& tu_unit, const CXToken& token) detail::cxtoken::cxtoken(const CXTranslationUnit& tu_unit, const CXToken& token)
@ -474,7 +472,7 @@ bool is_comparison(CXTokenKind last_kind, const detail::cxtoken& cur, CXTokenKin
} }
} // namespace } // namespace
detail::cxtoken_iterator detail::find_closing_bracket(detail::cxtoken_stream stream) detail::closing_bracket_pos detail::find_closing_bracket(detail::cxtoken_stream stream)
{ {
auto template_bracket = false; auto template_bracket = false;
auto open_bracket = stream.peek().c_str(); auto open_bracket = stream.peek().c_str();
@ -494,11 +492,14 @@ detail::cxtoken_iterator detail::find_closing_bracket(detail::cxtoken_stream str
DEBUG_UNREACHABLE(parse_error_handler{}, stream.cursor(), DEBUG_UNREACHABLE(parse_error_handler{}, stream.cursor(),
format("expected a bracket, got '", stream.peek().c_str(), "'")); format("expected a bracket, got '", stream.peek().c_str(), "'"));
auto bracket_count = 1; auto bracket_count = 1;
auto paren_count = 0; // internal nested parenthesis auto paren_count = 0; // internal nested parenthesis
auto last_token = CXToken_Comment; auto last_token = CXToken_Comment;
auto last_was_double_angle = false;
while (!stream.done() && bracket_count != 0) while (!stream.done() && bracket_count != 0)
{ {
last_was_double_angle = false;
auto& cur = stream.get(); auto& cur = stream.get();
if (paren_count == 0 && cur == open_bracket if (paren_count == 0 && cur == open_bracket
&& !is_comparison(last_token, cur, stream.peek().kind())) && !is_comparison(last_token, cur, stream.peek().kind()))
@ -507,8 +508,11 @@ detail::cxtoken_iterator detail::find_closing_bracket(detail::cxtoken_stream str
&& !is_comparison(last_token, cur, stream.peek().kind())) && !is_comparison(last_token, cur, stream.peek().kind()))
--bracket_count; --bracket_count;
else if (paren_count == 0 && template_bracket && cur == ">>") else if (paren_count == 0 && template_bracket && cur == ">>")
{
// maximal munch // maximal munch
bracket_count -= 2; bracket_count -= 2;
last_was_double_angle = true;
}
else if (cur == "(" || cur == "{" || cur == "[") else if (cur == "(" || cur == "{" || cur == "[")
++paren_count; ++paren_count;
else if (cur == ")" || cur == "}" || cur == "]") else if (cur == ")" || cur == "}" || cur == "]")
@ -516,19 +520,25 @@ detail::cxtoken_iterator detail::find_closing_bracket(detail::cxtoken_stream str
last_token = cur.kind(); last_token = cur.kind();
} }
stream.bump_back(); DEBUG_ASSERT(bracket_count == 0 && paren_count == 0, parse_error_handler{}, stream.cursor(),
// only check first parameter, token might be ">>"
DEBUG_ASSERT(bracket_count == 0 && paren_count == 0
&& stream.peek().value()[0] == close_bracket[0],
parse_error_handler{}, stream.cursor(),
"find_closing_bracket() internal parse error"); "find_closing_bracket() internal parse error");
return stream.cur();
if (last_was_double_angle)
{
return {stream.cur(), stream.cur(), true};
}
else
{
auto after = stream.cur();
stream.bump_back();
return {stream.cur(), after, false};
}
} }
void detail::skip_brackets(detail::cxtoken_stream& stream) void detail::skip_brackets(detail::cxtoken_stream& stream)
{ {
auto closing = find_closing_bracket(stream); auto closing = find_closing_bracket(stream);
stream.set_cur(std::next(closing)); stream.set_cur(closing.after);
} }
detail::cxtoken_iterator detail::find_sequence(detail::cxtoken_stream stream, detail::cxtoken_iterator detail::find_sequence(detail::cxtoken_stream stream,
@ -605,9 +615,9 @@ cpp_token_string parse_attribute_arguments(detail::cxtoken_stream& stream)
auto end = find_closing_bracket(stream); auto end = find_closing_bracket(stream);
skip(stream, "("); skip(stream, "(");
auto arguments = detail::to_string(stream, end); auto arguments = detail::to_string(stream, end.bracket, end.unmunch);
stream.set_cur(end.bracket);
stream.set_cur(end);
skip(stream, ")"); skip(stream, ")");
return arguments; return arguments;
@ -769,7 +779,7 @@ cpp_token_kind get_kind(const detail::cxtoken& token)
} }
} // namespace } // namespace
cpp_token_string detail::to_string(cxtoken_stream& stream, cxtoken_iterator end) cpp_token_string detail::to_string(cxtoken_stream& stream, cxtoken_iterator end, bool unmunch)
{ {
cpp_token_string::builder builder; cpp_token_string::builder builder;
@ -779,6 +789,9 @@ cpp_token_string detail::to_string(cxtoken_stream& stream, cxtoken_iterator end)
builder.add_token(cpp_token(get_kind(token), token.c_str())); builder.add_token(cpp_token(get_kind(token), token.c_str()));
} }
if (unmunch)
builder.unmunch();
return builder.finish(); return builder.finish();
} }
@ -800,8 +813,8 @@ bool detail::append_scope(detail::cxtoken_stream& stream, std::string& scope)
} }
else if (stream.peek() == "<") else if (stream.peek() == "<")
{ {
auto iter = detail::find_closing_bracket(stream); auto pos = detail::find_closing_bracket(stream);
scope += detail::to_string(stream, iter).as_string(); scope += detail::to_string(stream, pos.bracket, pos.unmunch).as_string();
if (!detail::skip_if(stream, ">>")) if (!detail::skip_if(stream, ">>"))
detail::skip(stream, ">"); detail::skip(stream, ">");
scope += ">"; scope += ">";

View file

@ -158,10 +158,19 @@ namespace detail
// if multi_token == true, str can consist of multiple tokens optionally separated by whitespace // if multi_token == true, str can consist of multiple tokens optionally separated by whitespace
bool skip_if(cxtoken_stream& stream, const char* str, bool multi_token = false); bool skip_if(cxtoken_stream& stream, const char* str, bool multi_token = false);
struct closing_bracket_pos
{
// If unmunch == false: bracket points to the closing bracket, after is the iterator after
// that. If unmunch == true: bracket points to >>, after points to the same >>; only one
// bracket is part of the matching closing one.
cxtoken_iterator bracket, after;
bool unmunch;
};
// returns the location of the closing bracket // returns the location of the closing bracket
// the current token must be (,[,{ or < // the current token must be (,[,{ or <
// note: < might not work in the arguments of a template specialization // note: < might not work in the arguments of a template specialization
cxtoken_iterator find_closing_bracket(cxtoken_stream stream); closing_bracket_pos find_closing_bracket(cxtoken_stream stream);
// skips brackets // skips brackets
// the current token must be (,[,{ or < // the current token must be (,[,{ or <
@ -178,7 +187,7 @@ namespace detail
cpp_attribute_list parse_attributes(cxtoken_stream& stream, bool skip_anyway = false); cpp_attribute_list parse_attributes(cxtoken_stream& stream, bool skip_anyway = false);
// converts a token range to a string // converts a token range to a string
cpp_token_string to_string(cxtoken_stream& stream, cxtoken_iterator end); cpp_token_string to_string(cxtoken_stream& stream, cxtoken_iterator end, bool unmunch);
// appends token to scope, if it is still valid // appends token to scope, if it is still valid
// else clears it // else clears it

View file

@ -17,7 +17,7 @@ std::unique_ptr<cpp_expression> detail::parse_expression(const detail::parse_con
detail::cxtoken_stream stream(tokenizer, cur); detail::cxtoken_stream stream(tokenizer, cur);
auto type = parse_type(context, cur, clang_getCursorType(cur)); auto type = parse_type(context, cur, clang_getCursorType(cur));
auto expr = to_string(stream, stream.end()); auto expr = to_string(stream, stream.end(), false);
if (kind == CXCursor_CallExpr && (expr.empty() || expr.back().spelling != ")")) if (kind == CXCursor_CallExpr && (expr.empty() || expr.back().spelling != ")"))
{ {
// we have a call expression that doesn't end in a closing parentheses // we have a call expression that doesn't end in a closing parentheses
@ -42,6 +42,6 @@ std::unique_ptr<cpp_expression> detail::parse_raw_expression(const parse_context
if (stream.done()) if (stream.done())
return nullptr; return nullptr;
auto expr = to_string(stream, std::prev(end)->value() == ";" ? std::prev(end) : end); auto expr = to_string(stream, std::prev(end)->value() == ";" ? std::prev(end) : end, false);
return cpp_unexposed_expression::build(std::move(type), std::move(expr)); return cpp_unexposed_expression::build(std::move(type), std::move(expr));
} }

View file

@ -368,7 +368,7 @@ std::unique_ptr<cpp_expression> parse_noexcept(detail::cxtoken_stream& stre
auto closing = detail::find_closing_bracket(stream); auto closing = detail::find_closing_bracket(stream);
detail::skip(stream, "("); detail::skip(stream, "(");
auto expr = detail::parse_raw_expression(context, stream, closing, std::move(type)); auto expr = detail::parse_raw_expression(context, stream, closing.bracket, std::move(type));
detail::skip(stream, ")"); detail::skip(stream, ")");
return expr; return expr;
@ -745,7 +745,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_conversion_op(const detail::parse_
// read the type // read the type
stream.set_cur(type_start); stream.set_cur(type_start);
auto type_spelling = detail::to_string(stream, type_end).as_string(); auto type_spelling = detail::to_string(stream, type_end, false).as_string();
// parse arguments again // parse arguments again
detail::skip(stream, "("); detail::skip(stream, "(");

View file

@ -210,7 +210,7 @@ try
// build unexposed entity // build unexposed entity
detail::cxtokenizer tokenizer(context.tu, context.file, cur); detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur); detail::cxtoken_stream stream(tokenizer, cur);
auto spelling = detail::to_string(stream, stream.end()); auto spelling = detail::to_string(stream, stream.end(), false);
if (spelling.begin() + 1 == spelling.end() && spelling.front().spelling == ";") if (spelling.begin() + 1 == spelling.end() && spelling.front().spelling == ";")
// unnecessary semicolon // unnecessary semicolon
return nullptr; return nullptr;

View file

@ -54,7 +54,7 @@ cpp_token_string extract_parameter_constraint(const detail::parse_context& conte
detail::cxtoken_iterator found_start detail::cxtoken_iterator found_start
= detail::find_sequence(stream, target_range_start, target_range_end); = detail::find_sequence(stream, target_range_start, target_range_end);
if (found_start == stream.end()) if (found_start == stream.end())
return detail::to_string(stream, stream.cur() + 1); return detail::to_string(stream, stream.cur() + 1, false);
stream.set_cur(found_start); stream.set_cur(found_start);
stream.bump(); stream.bump();
@ -70,7 +70,7 @@ cpp_token_string extract_parameter_constraint(const detail::parse_context& conte
stream.bump_back(); stream.bump_back();
} }
stream.bump(); stream.bump();
return detail::to_string(stream, constraint_end); return detail::to_string(stream, constraint_end, false);
} }
std::unique_ptr<cpp_template_parameter> parse_type_parameter(const detail::parse_context& context, std::unique_ptr<cpp_template_parameter> parse_type_parameter(const detail::parse_context& context,
@ -316,10 +316,10 @@ void parse_arguments(Builder& b, const detail::parse_context& context, const CXC
if (stream.peek() == "<") if (stream.peek() == "<")
{ {
auto iter = detail::find_closing_bracket(stream); auto closing = detail::find_closing_bracket(stream);
stream.bump(); stream.bump();
auto args = detail::to_string(stream, iter); auto args = detail::to_string(stream, closing.bracket, closing.unmunch);
b.add_unexposed_arguments(std::move(args)); b.add_unexposed_arguments(std::move(args));
} }
else else

View file

@ -724,7 +724,7 @@ std::unique_ptr<cpp_type> detail::parse_raw_type(const detail::parse_context&,
detail::cxtoken_stream& stream, detail::cxtoken_stream& stream,
detail::cxtoken_iterator end) detail::cxtoken_iterator end)
{ {
auto result = detail::to_string(stream, end); auto result = detail::to_string(stream, end, false);
return cpp_unexposed_type::build(result.as_string()); return cpp_unexposed_type::build(result.as_string());
} }

View file

@ -73,6 +73,13 @@ struct e
template <> template <>
class a<int> {}; class a<int> {};
// full specialization with munch
/// template<>
/// class a<a<int>>{
/// };
template <>
class a<a<int>> {};
/// template<> /// template<>
/// struct b<0,int>{ /// struct b<0,int>{
/// }; /// };
@ -245,7 +252,10 @@ template class a<int>;
if (templ.is_full_specialization()) if (templ.is_full_specialization())
{ {
check_template_parameters(templ, {}); check_template_parameters(templ, {});
REQUIRE(templ.unexposed_arguments().as_string() == "int");
auto args = templ.unexposed_arguments().as_string();
if (args != "int" && args != "a<int>")
FAIL("invalid args for specialization");
} }
else else
{ {
@ -273,5 +283,5 @@ template class a<int>;
else else
REQUIRE(false); REQUIRE(false);
}); });
REQUIRE(count == 6u); REQUIRE(count == 7u);
} }