Add basic concept support (#144)
This commit is contained in:
parent
f3e399573f
commit
a937efbcbc
21 changed files with 396 additions and 16 deletions
74
include/cppast/cpp_concept.hpp
Normal file
74
include/cppast/cpp_concept.hpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (C) 2017-2022 Jonathan Müller and cppast contributors
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef CPPAST_CPP_CONCEPT_HPP_INCLUDED
|
||||
#define CPPAST_CPP_CONCEPT_HPP_INCLUDED
|
||||
|
||||
#include <cppast/cpp_entity.hpp>
|
||||
#include <cppast/cpp_template_parameter.hpp>
|
||||
#include <cppast/cpp_expression.hpp>
|
||||
|
||||
namespace cppast
|
||||
{
|
||||
/// A [[cppast::cpp_entity]() modelling a c++ concept declaration
|
||||
/// \notes while concepts are technically templates,
|
||||
/// this is not a [cppast::cpp_template](),
|
||||
/// as concepts act very differently from other templates
|
||||
class cpp_concept final : public cpp_entity
|
||||
{
|
||||
public:
|
||||
static cpp_entity_kind kind() noexcept;
|
||||
|
||||
/// \returns the template parameters as a string
|
||||
const cpp_token_string& parameters() const noexcept
|
||||
{
|
||||
return parameters_;
|
||||
}
|
||||
|
||||
/// \returns the [cppast::cpp_expression]() defining the concept constraint
|
||||
const cpp_expression& constraint_expression() const noexcept
|
||||
{
|
||||
return *expression_;
|
||||
}
|
||||
|
||||
class builder
|
||||
{
|
||||
public:
|
||||
builder(std::string name)
|
||||
: concept_(new cpp_concept(std::move(name)))
|
||||
{}
|
||||
|
||||
cpp_token_string& set_parameters(cpp_token_string string) noexcept
|
||||
{
|
||||
concept_->parameters_ = std::move(string);
|
||||
return concept_->parameters_;
|
||||
}
|
||||
|
||||
cpp_expression& set_expression(std::unique_ptr<cpp_expression> expression) noexcept
|
||||
{
|
||||
concept_->expression_ = std::move(expression);
|
||||
return *concept_->expression_;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_concept> finish(const cpp_entity_index& idx, cpp_entity_id id);
|
||||
|
||||
private:
|
||||
std::unique_ptr<cpp_concept> concept_;
|
||||
};
|
||||
|
||||
private:
|
||||
cpp_concept(std::string name)
|
||||
: cpp_entity(std::move(name)), parameters_({})
|
||||
{}
|
||||
|
||||
cpp_entity_kind do_get_entity_kind() const noexcept override;
|
||||
|
||||
cpp_token_string parameters_;
|
||||
|
||||
std::unique_ptr<cpp_expression> expression_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cppast
|
||||
|
||||
#endif
|
||||
|
|
@ -57,6 +57,7 @@ enum class cpp_entity_kind
|
|||
function_template_specialization_t,
|
||||
class_template_t,
|
||||
class_template_specialization_t,
|
||||
concept_t,
|
||||
|
||||
static_assert_t,
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
namespace cppast
|
||||
{
|
||||
/// Base class for all entities modelling a C++ template of some kind.
|
||||
/// Base class for all entities modelling a C++ template of some kind, aside from concepts
|
||||
///
|
||||
/// It is a container of a single [cppast::cpp_entity]() that is the entity being templated.
|
||||
class cpp_template : public cpp_entity, public cpp_entity_container<cpp_template, cpp_entity>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ private:
|
|||
enum class cpp_template_keyword
|
||||
{
|
||||
keyword_class,
|
||||
keyword_typename
|
||||
keyword_typename,
|
||||
concept_contraint
|
||||
};
|
||||
|
||||
/// \returns The string associated of the keyword.
|
||||
|
|
@ -52,7 +53,8 @@ public:
|
|||
/// \notes The `default_type` may be `nullptr` in which case the parameter has no default.
|
||||
static std::unique_ptr<cpp_template_type_parameter> build(
|
||||
const cpp_entity_index& idx, cpp_entity_id id, std::string name, cpp_template_keyword kw,
|
||||
bool variadic, std::unique_ptr<cpp_type> default_type = nullptr);
|
||||
bool variadic, std::unique_ptr<cpp_type> default_type = nullptr,
|
||||
type_safe::optional<cpp_token_string> concept_constraint = type_safe::nullopt);
|
||||
|
||||
/// \returns A [ts::optional_ref]() to the default type.
|
||||
type_safe::optional_ref<const cpp_type> default_type() const noexcept
|
||||
|
|
@ -66,17 +68,24 @@ public:
|
|||
return keyword_;
|
||||
}
|
||||
|
||||
const type_safe::optional<cpp_token_string>& concept_constraint() const noexcept
|
||||
{
|
||||
return concept_constraint_;
|
||||
}
|
||||
|
||||
private:
|
||||
cpp_template_type_parameter(std::string name, cpp_template_keyword kw, bool variadic,
|
||||
std::unique_ptr<cpp_type> default_type)
|
||||
std::unique_ptr<cpp_type> default_type,
|
||||
type_safe::optional<cpp_token_string> concept_constraint)
|
||||
: cpp_template_parameter(std::move(name), variadic), default_type_(std::move(default_type)),
|
||||
keyword_(kw)
|
||||
keyword_(kw), concept_constraint_(concept_constraint)
|
||||
{}
|
||||
|
||||
cpp_entity_kind do_get_entity_kind() const noexcept override;
|
||||
|
||||
std::unique_ptr<cpp_type> default_type_;
|
||||
cpp_template_keyword keyword_;
|
||||
type_safe::optional<cpp_token_string> concept_constraint_;
|
||||
};
|
||||
|
||||
/// \exclude
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ class cpp_builtin_type;
|
|||
class cpp_class;
|
||||
class cpp_class_template;
|
||||
class cpp_class_template_specialization;
|
||||
class cpp_concept;
|
||||
class cpp_constructor;
|
||||
class cpp_conversion_op;
|
||||
class cpp_cv_qualified_type;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ set(header
|
|||
../include/cppast/cpp_attribute.hpp
|
||||
../include/cppast/cpp_class.hpp
|
||||
../include/cppast/cpp_class_template.hpp
|
||||
../include/cppast/cpp_concept.hpp
|
||||
../include/cppast/cpp_decltype_type.hpp
|
||||
../include/cppast/cpp_entity.hpp
|
||||
../include/cppast/cpp_entity_container.hpp
|
||||
|
|
@ -54,6 +55,7 @@ set(source
|
|||
cpp_attribute.cpp
|
||||
cpp_class.cpp
|
||||
cpp_class_template.cpp
|
||||
cpp_concept.cpp
|
||||
cpp_entity.cpp
|
||||
cpp_entity_index.cpp
|
||||
cpp_entity_kind.cpp
|
||||
|
|
@ -80,6 +82,7 @@ set(source
|
|||
visitor.cpp)
|
||||
set(libclang_source
|
||||
libclang/class_parser.cpp
|
||||
libclang/concept_parser.cpp
|
||||
libclang/cxtokenizer.cpp
|
||||
libclang/cxtokenizer.hpp
|
||||
libclang/debug_helper.cpp
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <cppast/cpp_alias_template.hpp>
|
||||
#include <cppast/cpp_class.hpp>
|
||||
#include <cppast/cpp_class_template.hpp>
|
||||
#include <cppast/cpp_concept.hpp>
|
||||
#include <cppast/cpp_entity_kind.hpp>
|
||||
#include <cppast/cpp_enum.hpp>
|
||||
#include <cppast/cpp_file.hpp>
|
||||
|
|
@ -879,7 +880,10 @@ bool generate_template_type_parameter(code_generator& generat
|
|||
code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access);
|
||||
if (output)
|
||||
{
|
||||
output << keyword(to_string(param.keyword()));
|
||||
if(param.keyword() == cpp_template_keyword::concept_contraint)
|
||||
detail::write_token_string(output, param.concept_constraint().value());
|
||||
else
|
||||
output << keyword(to_string(param.keyword()));
|
||||
if (param.is_variadic())
|
||||
output << operator_ws << punctuation("...");
|
||||
if (!param.name().empty())
|
||||
|
|
@ -1036,6 +1040,23 @@ bool generate_class_template_specialization(code_generator&
|
|||
return static_cast<bool>(output);
|
||||
}
|
||||
|
||||
bool generate_concept(code_generator& generator,
|
||||
const cpp_concept& con,
|
||||
cpp_access_specifier_kind cur_access)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(con), cur_access);
|
||||
if(output)
|
||||
{
|
||||
output << keyword("template") << operator_ws << punctuation("<") << bracket_ws;
|
||||
detail::write_token_string(output, con.parameters());
|
||||
output << bracket_ws << punctuation(">") << newl;
|
||||
output << keyword("concept") << operator_ws << identifier(con.name()) << operator_ws << punctuation("=") << operator_ws;
|
||||
detail::write_expression(output, con.constraint_expression());
|
||||
output << operator_ws << punctuation(";") << newl;
|
||||
}
|
||||
return static_cast<bool>(output);
|
||||
}
|
||||
|
||||
bool generate_static_assert(code_generator& generator, const cpp_static_assert& assert,
|
||||
cpp_access_specifier_kind cur_access)
|
||||
{
|
||||
|
|
@ -1115,6 +1136,7 @@ bool generate_code_impl(code_generator& generator, const cpp_entity& e,
|
|||
CPPAST_DETAIL_HANDLE(function_template_specialization)
|
||||
CPPAST_DETAIL_HANDLE(class_template)
|
||||
CPPAST_DETAIL_HANDLE(class_template_specialization)
|
||||
CPPAST_DETAIL_HANDLE(concept)
|
||||
|
||||
CPPAST_DETAIL_HANDLE(static_assert)
|
||||
|
||||
|
|
|
|||
24
src/cpp_concept.cpp
Normal file
24
src/cpp_concept.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (C) 2017-2022 Jonathan Müller and cppast contributors
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <cppast/cpp_concept.hpp>
|
||||
|
||||
#include <cppast/cpp_entity_kind.hpp>
|
||||
|
||||
using namespace cppast;
|
||||
|
||||
cpp_entity_kind cppast::cpp_concept::kind() noexcept
|
||||
{
|
||||
return cpp_entity_kind::concept_t;
|
||||
}
|
||||
|
||||
cpp_entity_kind cpp_concept::do_get_entity_kind() const noexcept
|
||||
{
|
||||
return cpp_entity_kind::concept_t;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_concept> cpp_concept::builder::finish(const cpp_entity_index& idx, cpp_entity_id id)
|
||||
{
|
||||
idx.register_definition(id, type_safe::ref(*concept_));
|
||||
return std::move(concept_);
|
||||
}
|
||||
|
|
@ -88,6 +88,8 @@ const char* cppast::to_string(cpp_entity_kind kind) noexcept
|
|||
return "class template";
|
||||
case cpp_entity_kind::class_template_specialization_t:
|
||||
return "class template specialization";
|
||||
case cpp_entity_kind::concept_t:
|
||||
return "concept";
|
||||
|
||||
case cpp_entity_kind::static_assert_t:
|
||||
return "static_assert";
|
||||
|
|
@ -142,6 +144,7 @@ bool cppast::is_function(cpp_entity_kind kind) noexcept
|
|||
case cpp_entity_kind::function_template_specialization_t:
|
||||
case cpp_entity_kind::class_template_t:
|
||||
case cpp_entity_kind::class_template_specialization_t:
|
||||
case cpp_entity_kind::concept_t:
|
||||
case cpp_entity_kind::static_assert_t:
|
||||
case cpp_entity_kind::unexposed_t:
|
||||
case cpp_entity_kind::count:
|
||||
|
|
@ -191,6 +194,7 @@ bool cppast::is_parameter(cpp_entity_kind kind) noexcept
|
|||
case cpp_entity_kind::function_template_specialization_t:
|
||||
case cpp_entity_kind::class_template_t:
|
||||
case cpp_entity_kind::class_template_specialization_t:
|
||||
case cpp_entity_kind::concept_t:
|
||||
case cpp_entity_kind::static_assert_t:
|
||||
case cpp_entity_kind::unexposed_t:
|
||||
case cpp_entity_kind::count:
|
||||
|
|
@ -209,6 +213,7 @@ bool cppast::is_template(cpp_entity_kind kind) noexcept
|
|||
case cpp_entity_kind::function_template_specialization_t:
|
||||
case cpp_entity_kind::class_template_t:
|
||||
case cpp_entity_kind::class_template_specialization_t:
|
||||
case cpp_entity_kind::concept_t:
|
||||
return true;
|
||||
|
||||
case cpp_entity_kind::file_t:
|
||||
|
|
@ -288,6 +293,7 @@ bool cppast::is_template_specialization(cpp_entity_kind kind) noexcept
|
|||
case cpp_entity_kind::variable_template_t:
|
||||
case cpp_entity_kind::function_template_t:
|
||||
case cpp_entity_kind::class_template_t:
|
||||
case cpp_entity_kind::concept_t:
|
||||
case cpp_entity_kind::static_assert_t:
|
||||
case cpp_entity_kind::unexposed_t:
|
||||
case cpp_entity_kind::count:
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ type_safe::optional_ref<const cpp_forward_declarable> get_declarable(const cpp_e
|
|||
case cpp_entity_kind::template_type_parameter_t:
|
||||
case cpp_entity_kind::non_type_template_parameter_t:
|
||||
case cpp_entity_kind::template_template_parameter_t:
|
||||
case cpp_entity_kind::concept_t:
|
||||
case cpp_entity_kind::alias_template_t:
|
||||
case cpp_entity_kind::variable_template_t:
|
||||
case cpp_entity_kind::static_assert_t:
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ const char* cppast::to_string(cpp_template_keyword kw) noexcept
|
|||
return "class";
|
||||
case cpp_template_keyword::keyword_typename:
|
||||
return "typename";
|
||||
case cpp_template_keyword::concept_contraint:
|
||||
return "concept constraint data lost";
|
||||
}
|
||||
|
||||
return "should not get here";
|
||||
|
|
@ -22,10 +24,11 @@ const char* cppast::to_string(cpp_template_keyword kw) noexcept
|
|||
|
||||
std::unique_ptr<cpp_template_type_parameter> cpp_template_type_parameter::build(
|
||||
const cpp_entity_index& idx, cpp_entity_id id, std::string name, cpp_template_keyword kw,
|
||||
bool variadic, std::unique_ptr<cpp_type> default_type)
|
||||
bool variadic, std::unique_ptr<cpp_type> default_type,
|
||||
type_safe::optional<cpp_token_string> concept_constraint)
|
||||
{
|
||||
std::unique_ptr<cpp_template_type_parameter> result(
|
||||
new cpp_template_type_parameter(std::move(name), kw, variadic, std::move(default_type)));
|
||||
new cpp_template_type_parameter(std::move(name), kw, variadic, std::move(default_type), std::move(concept_constraint)));
|
||||
idx.register_definition(std::move(id), type_safe::cref(*result));
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ bool detail::cpp_type_ref_predicate::operator()(const cpp_entity& e)
|
|||
case cpp_entity_kind::function_template_specialization_t:
|
||||
case cpp_entity_kind::class_template_t:
|
||||
case cpp_entity_kind::class_template_specialization_t:
|
||||
case cpp_entity_kind::concept_t:
|
||||
case cpp_entity_kind::static_assert_t:
|
||||
case cpp_entity_kind::unexposed_t:
|
||||
case cpp_entity_kind::count:
|
||||
|
|
|
|||
62
src/libclang/concept_parser.cpp
Normal file
62
src/libclang/concept_parser.cpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright (C) 2017-2022 Jonathan Müller and cppast contributors
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <cppast/cpp_concept.hpp>
|
||||
|
||||
#include <cppast/cpp_entity_kind.hpp>
|
||||
|
||||
#include "libclang_visitor.hpp"
|
||||
#include "parse_functions.hpp"
|
||||
|
||||
using namespace cppast;
|
||||
|
||||
std::unique_ptr<cpp_entity> detail::try_parse_cpp_concept(const detail::parse_context& context,
|
||||
const CXCursor& cur)
|
||||
{
|
||||
DEBUG_ASSERT(cur.kind == CXCursor_UnexposedDecl, detail::assert_handler{});
|
||||
|
||||
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::cxtoken_stream stream(tokenizer, cur);
|
||||
|
||||
if (!detail::skip_if(stream, "template"))
|
||||
return nullptr;
|
||||
|
||||
if (stream.peek() != "<")
|
||||
return nullptr;
|
||||
|
||||
|
||||
auto closing_bracket_iter = detail::find_closing_bracket(stream);
|
||||
auto params = to_string(stream, closing_bracket_iter);
|
||||
|
||||
if (!detail::skip_if(stream, ">"))
|
||||
return nullptr;
|
||||
|
||||
if (!detail::skip_if(stream, "concept"))
|
||||
return nullptr;
|
||||
|
||||
const auto& identifier_token = stream.get();
|
||||
if (identifier_token.kind() != CXTokenKind::CXToken_Identifier)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cpp_concept::builder builder(identifier_token.value().std_str());
|
||||
|
||||
if (!detail::skip_if(stream, "="))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (*(stream.end() - 1) != ";")
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
builder.set_expression(
|
||||
parse_raw_expression(context, stream, stream.end() - 1,
|
||||
cpp_builtin_type::build(cpp_builtin_type_kind::cpp_bool)));
|
||||
|
||||
builder.set_parameters(std::move(params));
|
||||
|
||||
return builder.finish(*context.idx, detail::get_entity_id(cur));
|
||||
}
|
||||
|
|
@ -532,6 +532,38 @@ void detail::skip_brackets(detail::cxtoken_stream& stream)
|
|||
stream.set_cur(std::next(closing));
|
||||
}
|
||||
|
||||
detail::cxtoken_iterator detail::find_sequence(detail::cxtoken_stream stream, detail::cxtoken_iterator start,
|
||||
detail::cxtoken_iterator end)
|
||||
{
|
||||
detail::cxtoken_iterator search_start = stream.cur();
|
||||
while(search_start != stream.end())
|
||||
{
|
||||
detail::cxtoken_iterator search_iter = search_start;
|
||||
detail::cxtoken_iterator seq_iter = start;
|
||||
bool failed = false;
|
||||
|
||||
while(!failed && search_iter != stream.end() && seq_iter != end)
|
||||
{
|
||||
if(search_iter->value() != seq_iter->value()
|
||||
|| search_iter->kind() != seq_iter->kind())
|
||||
{
|
||||
failed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++search_iter;
|
||||
++seq_iter;
|
||||
}
|
||||
}
|
||||
if(!failed)
|
||||
return search_start;
|
||||
|
||||
++search_start;
|
||||
}
|
||||
|
||||
return stream.end();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
type_safe::optional<std::string> parse_attribute_using(detail::cxtoken_stream& stream)
|
||||
|
|
|
|||
|
|
@ -184,6 +184,10 @@ namespace detail
|
|||
// note: < might not work in the arguments of a template specialization
|
||||
void skip_brackets(cxtoken_stream& stream);
|
||||
|
||||
// finds the location of the given sequence in a stream
|
||||
// returns an iterator to the first token of the found sequence, or stream.end() if not found
|
||||
cxtoken_iterator find_sequence(cxtoken_stream stream, cxtoken_iterator start, cxtoken_iterator end);
|
||||
|
||||
// parses attributes
|
||||
// if skip_anyway is true it will bump even if no attributes have been parsed
|
||||
cpp_attribute_list parse_attributes(cxtoken_stream& stream, bool skip_anyway = false);
|
||||
|
|
|
|||
|
|
@ -125,6 +125,8 @@ try
|
|||
// go through all the try_parse_XXX functions
|
||||
if (auto entity = try_parse_cpp_language_linkage(context, cur))
|
||||
return entity;
|
||||
if (auto entity = try_parse_cpp_concept(context, cur))
|
||||
return entity;
|
||||
break;
|
||||
|
||||
case CXCursor_MacroDefinition:
|
||||
|
|
|
|||
|
|
@ -91,6 +91,11 @@ namespace detail
|
|||
// unexposed
|
||||
std::unique_ptr<cpp_entity> try_parse_cpp_language_linkage(const parse_context& context,
|
||||
const CXCursor& cur);
|
||||
|
||||
//unexposed
|
||||
std::unique_ptr<cpp_entity> try_parse_cpp_concept(const parse_context& context,
|
||||
const CXCursor& cur);
|
||||
|
||||
// CXXMethod
|
||||
std::unique_ptr<cpp_entity> try_parse_static_cpp_function(const parse_context& context,
|
||||
const CXCursor& cur);
|
||||
|
|
|
|||
|
|
@ -42,22 +42,64 @@ type_safe::optional<typename TemplateT::builder> get_builder(const detail::parse
|
|||
std::unique_ptr<EntityT>(static_cast<EntityT*>(entity.release())));
|
||||
}
|
||||
|
||||
cpp_token_string extract_parameter_constraint(const detail::parse_context& context,
|
||||
const CXCursor& parent,
|
||||
detail::cxtoken_iterator target_range_start,
|
||||
detail::cxtoken_iterator target_range_end)
|
||||
{
|
||||
//search the parent context for the *exact* sequence in it's entirety
|
||||
detail::cxtokenizer tokenizer(context.tu, context.file, parent);
|
||||
detail::cxtoken_stream stream(tokenizer, parent);
|
||||
|
||||
detail::cxtoken_iterator found_start = detail::find_sequence(stream, target_range_start, target_range_end);
|
||||
if(found_start == stream.end())
|
||||
return detail::to_string(stream, stream.cur() + 1);
|
||||
|
||||
stream.set_cur(found_start);
|
||||
stream.bump();
|
||||
if (stream.peek() == "<")
|
||||
detail::skip_brackets(stream);
|
||||
|
||||
detail::cxtoken_iterator constraint_end = stream.cur();
|
||||
stream.set_cur(found_start);
|
||||
//seek backwards until we are at the start of the qualified name
|
||||
while(stream.peek().value() != "<" && stream.peek().value() != ",")
|
||||
{
|
||||
std::string str = stream.peek().value().std_str();
|
||||
stream.bump_back();
|
||||
}
|
||||
stream.bump();
|
||||
return detail::to_string(stream, constraint_end);
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_template_parameter> parse_type_parameter(const detail::parse_context& context,
|
||||
const CXCursor& cur)
|
||||
const CXCursor& cur,
|
||||
const CXCursor& parent)
|
||||
{
|
||||
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter,
|
||||
detail::assert_handler{});
|
||||
|
||||
|
||||
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
|
||||
detail::cxtoken_stream stream(tokenizer, cur);
|
||||
auto name = detail::get_cursor_name(cur);
|
||||
|
||||
// syntax: typename/class [...] name [= ...]
|
||||
auto keyword = cpp_template_keyword::keyword_class;
|
||||
// syntax: typename/class/constraint [...] name [= ...]
|
||||
auto keyword = cpp_template_keyword::keyword_class;
|
||||
type_safe::optional<cpp_token_string> constraint = type_safe::nullopt;
|
||||
|
||||
if (detail::skip_if(stream, "typename"))
|
||||
keyword = cpp_template_keyword::keyword_typename;
|
||||
else
|
||||
detail::skip(stream, "class");
|
||||
else if (!detail::skip_if(stream, "class"))
|
||||
{
|
||||
keyword = cpp_template_keyword::concept_contraint;
|
||||
|
||||
//try to extract the constraint token string
|
||||
constraint = extract_parameter_constraint(context, parent, stream.cur(), stream.end());
|
||||
stream.bump();
|
||||
if (stream.peek() == "<")
|
||||
detail::skip_brackets(stream);
|
||||
}
|
||||
|
||||
auto variadic = false;
|
||||
if (detail::skip_if(stream, "..."))
|
||||
|
|
@ -72,7 +114,7 @@ std::unique_ptr<cpp_template_parameter> parse_type_parameter(const detail::parse
|
|||
def = detail::parse_raw_type(context, stream, stream.end());
|
||||
|
||||
return cpp_template_type_parameter::build(*context.idx, detail::get_entity_id(cur),
|
||||
name.c_str(), keyword, variadic, std::move(def));
|
||||
name.c_str(), keyword, variadic, std::move(def), constraint);
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_template_parameter> parse_non_type_parameter(
|
||||
|
|
@ -147,7 +189,7 @@ std::unique_ptr<cpp_template_template_parameter> parse_template_parameter(
|
|||
detail::visit_children(cur, [&](const CXCursor& child) {
|
||||
auto kind = clang_getCursorKind(child);
|
||||
if (kind == CXCursor_TemplateTypeParameter)
|
||||
builder.add_parameter(parse_type_parameter(context, child));
|
||||
builder.add_parameter(parse_type_parameter(context, child, cur));
|
||||
else if (kind == CXCursor_NonTypeTemplateParameter)
|
||||
builder.add_parameter(parse_non_type_parameter(context, child));
|
||||
else if (kind == CXCursor_TemplateTemplateParameter)
|
||||
|
|
@ -188,7 +230,7 @@ void parse_parameters(Builder& builder, const detail::parse_context& context, co
|
|||
detail::visit_children(cur, [&](const CXCursor& child) {
|
||||
auto kind = clang_getCursorKind(child);
|
||||
if (kind == CXCursor_TemplateTypeParameter)
|
||||
builder.add_parameter(parse_type_parameter(context, child));
|
||||
builder.add_parameter(parse_type_parameter(context, child, cur));
|
||||
else if (kind == CXCursor_NonTypeTemplateParameter)
|
||||
builder.add_parameter(parse_non_type_parameter(context, child));
|
||||
else if (kind == CXCursor_TemplateTemplateParameter)
|
||||
|
|
@ -389,3 +431,4 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_class_template_specialization(
|
|||
return builder.finish(*context.idx, detail::get_entity_id(cur),
|
||||
builder.get().class_().is_definition());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ bool detail::visit(const cpp_entity& e, detail::visitor_callback_t cb, void* fun
|
|||
case cpp_entity_kind::template_type_parameter_t:
|
||||
case cpp_entity_kind::non_type_template_parameter_t:
|
||||
case cpp_entity_kind::template_template_parameter_t:
|
||||
case cpp_entity_kind::concept_t:
|
||||
case cpp_entity_kind::static_assert_t:
|
||||
case cpp_entity_kind::unexposed_t:
|
||||
return cb(functor, e, {visitor_info::leaf_entity, cur_access, last_child});
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ set(tests
|
|||
cpp_attribute.cpp
|
||||
cpp_class.cpp
|
||||
cpp_class_template.cpp
|
||||
cpp_concept.cpp
|
||||
cpp_enum.cpp
|
||||
cpp_friend.cpp
|
||||
cpp_function.cpp
|
||||
|
|
|
|||
85
test/cpp_concept.cpp
Normal file
85
test/cpp_concept.cpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
// Copyright (C) 2017-2022 Jonathan Müller and cppast contributors
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <cppast/cpp_concept.hpp>
|
||||
|
||||
#include <cppast/cpp_function_template.hpp>
|
||||
|
||||
#include "test_parser.hpp"
|
||||
|
||||
using namespace cppast;
|
||||
|
||||
TEST_CASE("cpp_concept")
|
||||
{
|
||||
if (libclang_parser::libclang_minor_version() < 60)
|
||||
return;
|
||||
|
||||
auto code = R"(
|
||||
#include <concepts>
|
||||
|
||||
/// template<typename T>
|
||||
/// concept a = requires(T t, int i)
|
||||
/// {
|
||||
/// {t.a()};
|
||||
/// {t.b()} -> std::copy_constructible;
|
||||
/// {t.c(i)} -> std::same_as<int>;
|
||||
/// typename T::inner;
|
||||
/// };
|
||||
template<typename T>
|
||||
concept a = requires(T t, int i)
|
||||
{
|
||||
{t.a()};
|
||||
{t.b()} -> std::copy_constructible;
|
||||
{t.c(i)} -> std::same_as<int>;
|
||||
typename T::inner;
|
||||
};
|
||||
|
||||
/// template<typename T>
|
||||
/// concept b = a<T> && std::constructible_from<T, int>;
|
||||
template<typename T>
|
||||
concept b = a<T> && std::constructible_from<T, int>;
|
||||
|
||||
/// template<typename T>
|
||||
/// void f1(T param);
|
||||
template<typename T>
|
||||
requires a<T>
|
||||
void f1(T param);
|
||||
|
||||
/// template<b T>
|
||||
/// void f2(T param);
|
||||
template<b T>
|
||||
void f2(T param);
|
||||
|
||||
/// template<std::convertible_to<int> T>
|
||||
/// void f3(T param);
|
||||
template<std::convertible_to<int> T>
|
||||
void f3(T param);
|
||||
|
||||
)";
|
||||
cpp_entity_index idx;
|
||||
auto file = parse(idx, "cpp_concept.cpp", code, false, cppast::cpp_standard::cpp_20);
|
||||
|
||||
auto count = test_visit<cpp_concept>(*file, [&](const cpp_concept& con) {}, false);
|
||||
REQUIRE(count == 2u);
|
||||
|
||||
count = test_visit<cpp_function_template>(*file, [&](const cpp_function_template& tfunc) {
|
||||
REQUIRE(is_templated(tfunc.function()));
|
||||
REQUIRE(!tfunc.scope_name());
|
||||
check_template_parameters(tfunc, {{cpp_entity_kind::template_type_parameter_t, "T"}});
|
||||
|
||||
if(tfunc.name() == "f1")
|
||||
{
|
||||
REQUIRE(
|
||||
static_cast<const cpp_template_type_parameter&>(*tfunc.parameters().begin()).keyword()
|
||||
== cpp_template_keyword::keyword_typename);
|
||||
}
|
||||
else if (tfunc.name() == "f2" || tfunc.name() == "f3")
|
||||
{
|
||||
REQUIRE(static_cast<const cpp_template_type_parameter&>(*tfunc.parameters().begin())
|
||||
.keyword()
|
||||
== cpp_template_keyword::concept_contraint);
|
||||
}
|
||||
});
|
||||
|
||||
REQUIRE(count == 3u);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue