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,
|
function_template_specialization_t,
|
||||||
class_template_t,
|
class_template_t,
|
||||||
class_template_specialization_t,
|
class_template_specialization_t,
|
||||||
|
concept_t,
|
||||||
|
|
||||||
static_assert_t,
|
static_assert_t,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
namespace cppast
|
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.
|
/// 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>
|
class cpp_template : public cpp_entity, public cpp_entity_container<cpp_template, cpp_entity>
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,8 @@ private:
|
||||||
enum class cpp_template_keyword
|
enum class cpp_template_keyword
|
||||||
{
|
{
|
||||||
keyword_class,
|
keyword_class,
|
||||||
keyword_typename
|
keyword_typename,
|
||||||
|
concept_contraint
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \returns The string associated of the keyword.
|
/// \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.
|
/// \notes The `default_type` may be `nullptr` in which case the parameter has no default.
|
||||||
static std::unique_ptr<cpp_template_type_parameter> build(
|
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,
|
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.
|
/// \returns A [ts::optional_ref]() to the default type.
|
||||||
type_safe::optional_ref<const cpp_type> default_type() const noexcept
|
type_safe::optional_ref<const cpp_type> default_type() const noexcept
|
||||||
|
|
@ -66,17 +68,24 @@ public:
|
||||||
return keyword_;
|
return keyword_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const type_safe::optional<cpp_token_string>& concept_constraint() const noexcept
|
||||||
|
{
|
||||||
|
return concept_constraint_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cpp_template_type_parameter(std::string name, cpp_template_keyword kw, bool variadic,
|
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)),
|
: 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;
|
cpp_entity_kind do_get_entity_kind() const noexcept override;
|
||||||
|
|
||||||
std::unique_ptr<cpp_type> default_type_;
|
std::unique_ptr<cpp_type> default_type_;
|
||||||
cpp_template_keyword keyword_;
|
cpp_template_keyword keyword_;
|
||||||
|
type_safe::optional<cpp_token_string> concept_constraint_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// \exclude
|
/// \exclude
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ class cpp_builtin_type;
|
||||||
class cpp_class;
|
class cpp_class;
|
||||||
class cpp_class_template;
|
class cpp_class_template;
|
||||||
class cpp_class_template_specialization;
|
class cpp_class_template_specialization;
|
||||||
|
class cpp_concept;
|
||||||
class cpp_constructor;
|
class cpp_constructor;
|
||||||
class cpp_conversion_op;
|
class cpp_conversion_op;
|
||||||
class cpp_cv_qualified_type;
|
class cpp_cv_qualified_type;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ set(header
|
||||||
../include/cppast/cpp_attribute.hpp
|
../include/cppast/cpp_attribute.hpp
|
||||||
../include/cppast/cpp_class.hpp
|
../include/cppast/cpp_class.hpp
|
||||||
../include/cppast/cpp_class_template.hpp
|
../include/cppast/cpp_class_template.hpp
|
||||||
|
../include/cppast/cpp_concept.hpp
|
||||||
../include/cppast/cpp_decltype_type.hpp
|
../include/cppast/cpp_decltype_type.hpp
|
||||||
../include/cppast/cpp_entity.hpp
|
../include/cppast/cpp_entity.hpp
|
||||||
../include/cppast/cpp_entity_container.hpp
|
../include/cppast/cpp_entity_container.hpp
|
||||||
|
|
@ -54,6 +55,7 @@ set(source
|
||||||
cpp_attribute.cpp
|
cpp_attribute.cpp
|
||||||
cpp_class.cpp
|
cpp_class.cpp
|
||||||
cpp_class_template.cpp
|
cpp_class_template.cpp
|
||||||
|
cpp_concept.cpp
|
||||||
cpp_entity.cpp
|
cpp_entity.cpp
|
||||||
cpp_entity_index.cpp
|
cpp_entity_index.cpp
|
||||||
cpp_entity_kind.cpp
|
cpp_entity_kind.cpp
|
||||||
|
|
@ -80,6 +82,7 @@ set(source
|
||||||
visitor.cpp)
|
visitor.cpp)
|
||||||
set(libclang_source
|
set(libclang_source
|
||||||
libclang/class_parser.cpp
|
libclang/class_parser.cpp
|
||||||
|
libclang/concept_parser.cpp
|
||||||
libclang/cxtokenizer.cpp
|
libclang/cxtokenizer.cpp
|
||||||
libclang/cxtokenizer.hpp
|
libclang/cxtokenizer.hpp
|
||||||
libclang/debug_helper.cpp
|
libclang/debug_helper.cpp
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <cppast/cpp_alias_template.hpp>
|
#include <cppast/cpp_alias_template.hpp>
|
||||||
#include <cppast/cpp_class.hpp>
|
#include <cppast/cpp_class.hpp>
|
||||||
#include <cppast/cpp_class_template.hpp>
|
#include <cppast/cpp_class_template.hpp>
|
||||||
|
#include <cppast/cpp_concept.hpp>
|
||||||
#include <cppast/cpp_entity_kind.hpp>
|
#include <cppast/cpp_entity_kind.hpp>
|
||||||
#include <cppast/cpp_enum.hpp>
|
#include <cppast/cpp_enum.hpp>
|
||||||
#include <cppast/cpp_file.hpp>
|
#include <cppast/cpp_file.hpp>
|
||||||
|
|
@ -879,6 +880,9 @@ bool generate_template_type_parameter(code_generator& generat
|
||||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access);
|
code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access);
|
||||||
if (output)
|
if (output)
|
||||||
{
|
{
|
||||||
|
if(param.keyword() == cpp_template_keyword::concept_contraint)
|
||||||
|
detail::write_token_string(output, param.concept_constraint().value());
|
||||||
|
else
|
||||||
output << keyword(to_string(param.keyword()));
|
output << keyword(to_string(param.keyword()));
|
||||||
if (param.is_variadic())
|
if (param.is_variadic())
|
||||||
output << operator_ws << punctuation("...");
|
output << operator_ws << punctuation("...");
|
||||||
|
|
@ -1036,6 +1040,23 @@ bool generate_class_template_specialization(code_generator&
|
||||||
return static_cast<bool>(output);
|
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,
|
bool generate_static_assert(code_generator& generator, const cpp_static_assert& assert,
|
||||||
cpp_access_specifier_kind cur_access)
|
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(function_template_specialization)
|
||||||
CPPAST_DETAIL_HANDLE(class_template)
|
CPPAST_DETAIL_HANDLE(class_template)
|
||||||
CPPAST_DETAIL_HANDLE(class_template_specialization)
|
CPPAST_DETAIL_HANDLE(class_template_specialization)
|
||||||
|
CPPAST_DETAIL_HANDLE(concept)
|
||||||
|
|
||||||
CPPAST_DETAIL_HANDLE(static_assert)
|
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";
|
return "class template";
|
||||||
case cpp_entity_kind::class_template_specialization_t:
|
case cpp_entity_kind::class_template_specialization_t:
|
||||||
return "class template specialization";
|
return "class template specialization";
|
||||||
|
case cpp_entity_kind::concept_t:
|
||||||
|
return "concept";
|
||||||
|
|
||||||
case cpp_entity_kind::static_assert_t:
|
case cpp_entity_kind::static_assert_t:
|
||||||
return "static_assert";
|
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::function_template_specialization_t:
|
||||||
case cpp_entity_kind::class_template_t:
|
case cpp_entity_kind::class_template_t:
|
||||||
case cpp_entity_kind::class_template_specialization_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::static_assert_t:
|
||||||
case cpp_entity_kind::unexposed_t:
|
case cpp_entity_kind::unexposed_t:
|
||||||
case cpp_entity_kind::count:
|
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::function_template_specialization_t:
|
||||||
case cpp_entity_kind::class_template_t:
|
case cpp_entity_kind::class_template_t:
|
||||||
case cpp_entity_kind::class_template_specialization_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::static_assert_t:
|
||||||
case cpp_entity_kind::unexposed_t:
|
case cpp_entity_kind::unexposed_t:
|
||||||
case cpp_entity_kind::count:
|
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::function_template_specialization_t:
|
||||||
case cpp_entity_kind::class_template_t:
|
case cpp_entity_kind::class_template_t:
|
||||||
case cpp_entity_kind::class_template_specialization_t:
|
case cpp_entity_kind::class_template_specialization_t:
|
||||||
|
case cpp_entity_kind::concept_t:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case cpp_entity_kind::file_t:
|
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::variable_template_t:
|
||||||
case cpp_entity_kind::function_template_t:
|
case cpp_entity_kind::function_template_t:
|
||||||
case cpp_entity_kind::class_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::static_assert_t:
|
||||||
case cpp_entity_kind::unexposed_t:
|
case cpp_entity_kind::unexposed_t:
|
||||||
case cpp_entity_kind::count:
|
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::template_type_parameter_t:
|
||||||
case cpp_entity_kind::non_type_template_parameter_t:
|
case cpp_entity_kind::non_type_template_parameter_t:
|
||||||
case cpp_entity_kind::template_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::alias_template_t:
|
||||||
case cpp_entity_kind::variable_template_t:
|
case cpp_entity_kind::variable_template_t:
|
||||||
case cpp_entity_kind::static_assert_t:
|
case cpp_entity_kind::static_assert_t:
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ const char* cppast::to_string(cpp_template_keyword kw) noexcept
|
||||||
return "class";
|
return "class";
|
||||||
case cpp_template_keyword::keyword_typename:
|
case cpp_template_keyword::keyword_typename:
|
||||||
return "typename";
|
return "typename";
|
||||||
|
case cpp_template_keyword::concept_contraint:
|
||||||
|
return "concept constraint data lost";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "should not get here";
|
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(
|
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,
|
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(
|
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));
|
idx.register_definition(std::move(id), type_safe::cref(*result));
|
||||||
return 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::function_template_specialization_t:
|
||||||
case cpp_entity_kind::class_template_t:
|
case cpp_entity_kind::class_template_t:
|
||||||
case cpp_entity_kind::class_template_specialization_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::static_assert_t:
|
||||||
case cpp_entity_kind::unexposed_t:
|
case cpp_entity_kind::unexposed_t:
|
||||||
case cpp_entity_kind::count:
|
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));
|
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
|
namespace
|
||||||
{
|
{
|
||||||
type_safe::optional<std::string> parse_attribute_using(detail::cxtoken_stream& stream)
|
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
|
// note: < might not work in the arguments of a template specialization
|
||||||
void skip_brackets(cxtoken_stream& stream);
|
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
|
// parses attributes
|
||||||
// if skip_anyway is true it will bump even if no attributes have been parsed
|
// 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);
|
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
|
// go through all the try_parse_XXX functions
|
||||||
if (auto entity = try_parse_cpp_language_linkage(context, cur))
|
if (auto entity = try_parse_cpp_language_linkage(context, cur))
|
||||||
return entity;
|
return entity;
|
||||||
|
if (auto entity = try_parse_cpp_concept(context, cur))
|
||||||
|
return entity;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CXCursor_MacroDefinition:
|
case CXCursor_MacroDefinition:
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,11 @@ namespace detail
|
||||||
// unexposed
|
// unexposed
|
||||||
std::unique_ptr<cpp_entity> try_parse_cpp_language_linkage(const parse_context& context,
|
std::unique_ptr<cpp_entity> try_parse_cpp_language_linkage(const parse_context& context,
|
||||||
const CXCursor& cur);
|
const CXCursor& cur);
|
||||||
|
|
||||||
|
//unexposed
|
||||||
|
std::unique_ptr<cpp_entity> try_parse_cpp_concept(const parse_context& context,
|
||||||
|
const CXCursor& cur);
|
||||||
|
|
||||||
// CXXMethod
|
// CXXMethod
|
||||||
std::unique_ptr<cpp_entity> try_parse_static_cpp_function(const parse_context& context,
|
std::unique_ptr<cpp_entity> try_parse_static_cpp_function(const parse_context& context,
|
||||||
const CXCursor& cur);
|
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())));
|
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,
|
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,
|
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter,
|
||||||
detail::assert_handler{});
|
detail::assert_handler{});
|
||||||
|
|
||||||
|
|
||||||
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 name = detail::get_cursor_name(cur);
|
auto name = detail::get_cursor_name(cur);
|
||||||
|
|
||||||
// syntax: typename/class [...] name [= ...]
|
// syntax: typename/class/constraint [...] name [= ...]
|
||||||
auto keyword = cpp_template_keyword::keyword_class;
|
auto keyword = cpp_template_keyword::keyword_class;
|
||||||
|
type_safe::optional<cpp_token_string> constraint = type_safe::nullopt;
|
||||||
|
|
||||||
if (detail::skip_if(stream, "typename"))
|
if (detail::skip_if(stream, "typename"))
|
||||||
keyword = cpp_template_keyword::keyword_typename;
|
keyword = cpp_template_keyword::keyword_typename;
|
||||||
else
|
else if (!detail::skip_if(stream, "class"))
|
||||||
detail::skip(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;
|
auto variadic = false;
|
||||||
if (detail::skip_if(stream, "..."))
|
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());
|
def = detail::parse_raw_type(context, stream, stream.end());
|
||||||
|
|
||||||
return cpp_template_type_parameter::build(*context.idx, detail::get_entity_id(cur),
|
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(
|
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) {
|
detail::visit_children(cur, [&](const CXCursor& child) {
|
||||||
auto kind = clang_getCursorKind(child);
|
auto kind = clang_getCursorKind(child);
|
||||||
if (kind == CXCursor_TemplateTypeParameter)
|
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)
|
else if (kind == CXCursor_NonTypeTemplateParameter)
|
||||||
builder.add_parameter(parse_non_type_parameter(context, child));
|
builder.add_parameter(parse_non_type_parameter(context, child));
|
||||||
else if (kind == CXCursor_TemplateTemplateParameter)
|
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) {
|
detail::visit_children(cur, [&](const CXCursor& child) {
|
||||||
auto kind = clang_getCursorKind(child);
|
auto kind = clang_getCursorKind(child);
|
||||||
if (kind == CXCursor_TemplateTypeParameter)
|
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)
|
else if (kind == CXCursor_NonTypeTemplateParameter)
|
||||||
builder.add_parameter(parse_non_type_parameter(context, child));
|
builder.add_parameter(parse_non_type_parameter(context, child));
|
||||||
else if (kind == CXCursor_TemplateTemplateParameter)
|
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),
|
return builder.finish(*context.idx, detail::get_entity_id(cur),
|
||||||
builder.get().class_().is_definition());
|
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::template_type_parameter_t:
|
||||||
case cpp_entity_kind::non_type_template_parameter_t:
|
case cpp_entity_kind::non_type_template_parameter_t:
|
||||||
case cpp_entity_kind::template_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::static_assert_t:
|
||||||
case cpp_entity_kind::unexposed_t:
|
case cpp_entity_kind::unexposed_t:
|
||||||
return cb(functor, e, {visitor_info::leaf_entity, cur_access, last_child});
|
return cb(functor, e, {visitor_info::leaf_entity, cur_access, last_child});
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ set(tests
|
||||||
cpp_attribute.cpp
|
cpp_attribute.cpp
|
||||||
cpp_class.cpp
|
cpp_class.cpp
|
||||||
cpp_class_template.cpp
|
cpp_class_template.cpp
|
||||||
|
cpp_concept.cpp
|
||||||
cpp_enum.cpp
|
cpp_enum.cpp
|
||||||
cpp_friend.cpp
|
cpp_friend.cpp
|
||||||
cpp_function.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