21#include <clang/Lex/Preprocessor.h>
26 clang::AccessSpecifier access_specifier)
29 switch (access_specifier) {
30 case clang::AccessSpecifier::AS_public:
33 case clang::AccessSpecifier::AS_private:
36 case clang::AccessSpecifier::AS_protected:
50 const auto *parent{declaration.getParent()};
53 while ((parent !=
nullptr) && !parent->isNamespace()) {
54 parent = parent->getParent();
58 std::deque<std::string> namespace_tokens;
59 while ((parent !=
nullptr) && parent->isNamespace()) {
60 if (
const auto *ns_decl = clang::dyn_cast<clang::NamespaceDecl>(parent);
62 if (!ns_decl->isInline() && !ns_decl->isAnonymousNamespace())
63 namespace_tokens.push_front(ns_decl->getNameAsString());
66 parent = parent->getParent();
69 for (
const auto &ns_token : namespace_tokens) {
86 auto base_name = declaration.getNameAsString();
88 if (base_name.empty()) {
90 fmt::format(
"(anonymous_{})", std::to_string(declaration.getID()));
93 if ((declaration.getParent() !=
nullptr) &&
94 declaration.getParent()->isRecord()) {
98 std::deque<std::string> record_parent_names;
99 record_parent_names.push_front(base_name);
101 const auto *cls_parent{declaration.getParent()};
102 while (cls_parent->isRecord()) {
103 if (
const auto *record_decl =
104 clang::dyn_cast<clang::RecordDecl>(cls_parent);
105 record_decl !=
nullptr) {
106 record_parent_names.push_front(record_decl->getNameAsString());
108 cls_parent = cls_parent->getParent();
110 return fmt::format(
"{}", fmt::join(record_parent_names,
"##"));
116std::string
to_string(
const clang::ArrayType &array_type,
117 const clang::ASTContext &ctx,
bool try_canonical,
118 std::vector<std::string> &dimensions)
121 std::string array_size =
122 maybe_size.has_value() ? std::to_string(maybe_size.value()) :
"";
123 dimensions.emplace_back(std::move(array_size));
125 const auto underlying_type = array_type.getElementType();
127 if (underlying_type->isArrayType())
128 return to_string(*underlying_type->getAsArrayTypeUnsafe(), ctx,
129 try_canonical, dimensions);
131 std::string dimensions_str;
132 for (
const auto &d : dimensions) {
133 dimensions_str += fmt::format(
"[{}]", d);
136 "{}{}",
to_string(underlying_type, ctx, try_canonical), dimensions_str);
139std::string
to_string(
const clang::QualType &type,
const clang::ASTContext &ctx,
142 if (type->isArrayType()) {
143 std::vector<std::string> dimensions;
145 *type->getAsArrayTypeUnsafe(), ctx, try_canonical, dimensions);
148 clang::PrintingPolicy print_policy(ctx.getLangOpts());
149 print_policy.SuppressScope = 0;
150 print_policy.PrintCanonicalTypes = 0;
154 result = type.getAsString(print_policy);
156 if (try_canonical && result.find(
'<') != std::string::npos) {
157 auto canonical_type_name =
158 type.getCanonicalType().getAsString(print_policy);
160 auto result_qualified_template_name =
161 result.substr(0, result.find(
'<'));
162 auto result_template_arguments = result.substr(result.find(
'<'));
164 auto canonical_qualified_template_name =
165 canonical_type_name.substr(0, canonical_type_name.find(
'<'));
168 if (result_qualified_template_name.size() <
169 canonical_qualified_template_name.size()) {
172 canonical_qualified_template_name + result_template_arguments;
179 result =
"(anonymous)";
182 const auto *declarationTag = type->getAsTagDecl();
183 if (declarationTag ==
nullptr) {
184 result =
"(unnamed undeclared)";
200 if (result.find(
"type-parameter-") != std::string::npos) {
203 [&result, &type](
auto *p) {
204 auto [unqualified_type, context] =
206 result = p->getDecl()->getNameAsString();
207 if (!context.empty()) {
208 std::vector<std::string> deduced_contexts;
210 for (
const auto &c : context) {
211 deduced_contexts.push_back(c.to_string());
214 result = fmt::format(
215 "{} {}", result, fmt::join(deduced_contexts,
" "));
224 const clang::ASTContext &ctx,
bool try_canonical)
226 return to_string(type.desugar(), ctx, try_canonical);
230 const clang::TemplateArgument &arg,
const clang::ASTContext *ctx)
232 switch (arg.getKind()) {
233 case clang::TemplateArgument::Expression:
235 case clang::TemplateArgument::Type:
236 return to_string(arg.getAsType(), *ctx,
false);
237 case clang::TemplateArgument::Null:
239 case clang::TemplateArgument::NullPtr:
241 case clang::TemplateArgument::Integral:
242 return std::to_string(arg.getAsIntegral().getExtValue());
243 case clang::TemplateArgument::Template:
245 case clang::TemplateArgument::TemplateExpansion:
246 return to_string(arg.getAsTemplateOrTemplatePattern());
254 if (templ.getAsTemplateDecl() !=
nullptr) {
255 return templ.getAsTemplateDecl()->getQualifiedNameAsString();
259 const clang::LangOptions lang_options;
260 llvm::raw_string_ostream ostream(result);
261 templ.print(ostream, clang::PrintingPolicy(lang_options));
268 const clang::LangOptions lang_options;
270 llvm::raw_string_ostream ostream(result);
271 expr->printPretty(ostream,
nullptr, clang::PrintingPolicy(lang_options));
278 return val->getQualifiedNameAsString();
283 const clang::LangOptions lang_options;
285 llvm::raw_string_ostream ostream(result);
286 stmt->printPretty(ostream,
nullptr, clang::PrintingPolicy(lang_options));
291std::string
to_string(
const clang::FunctionTemplateDecl *decl)
293 std::vector<std::string> template_parameters;
295 for (
const auto *parameter : *decl->getTemplateParameters()) {
296 if (clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter) !=
298 const auto *template_type_parameter =
299 clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter);
301 std::string template_parameter{
302 template_type_parameter->getNameAsString()};
304 if (template_type_parameter->isParameterPack())
305 template_parameter +=
"...";
307 template_parameters.emplace_back(std::move(template_parameter));
313 return fmt::format(
"{}<{}>({})", decl->getQualifiedNameAsString(),
314 fmt::join(template_parameters,
","),
"");
322 const clang::PrintingPolicy print_policy(
323 tc->getNamedConcept()->getASTContext().getLangOpts());
325 std::string ostream_buf;
326 llvm::raw_string_ostream ostream{ostream_buf};
327 tc->print(ostream, print_policy);
329 return ostream.str();
333 clang::SourceRange range,
const clang::SourceManager &sm)
335 return clang::Lexer::getSourceText(
336 clang::CharSourceRange::getCharRange(range), sm, clang::LangOptions())
341 clang::SourceRange range,
const clang::SourceManager &sm)
343 const clang::LangOptions lo;
345 auto start_loc = sm.getSpellingLoc(range.getBegin());
346 auto last_token_loc = sm.getSpellingLoc(range.getEnd());
347 auto end_loc = clang::Lexer::getLocForEndOfToken(last_token_loc, 0, sm, lo);
348 auto printable_range = clang::SourceRange{start_loc, end_loc};
352std::tuple<unsigned int, unsigned int, std::string>
355 assert(type_parameter.find(
"type-parameter-") == 0);
357 auto type_parameter_and_suffix =
util::split(type_parameter,
" ");
360 type_parameter_and_suffix.front().substr(strlen(
"type-parameter-")),
363 std::string qualifier;
365 if (type_parameter_and_suffix.size() > 1) {
366 qualifier = type_parameter_and_suffix.at(1);
369 return {std::stoi(toks.at(0)), std::stoi(toks.at(1)), std::move(qualifier)};
377 fmt::format(
"{}", std::filesystem::current_path().root_name().
string());
379 auto root_name = std::string{
"/"};
382 std::string lambda_prefix{fmt::format(
"(lambda at {}", root_name)};
384 while (parameter_type.find(lambda_prefix) != std::string::npos) {
385 auto lambda_begin = parameter_type.find(lambda_prefix);
386 auto lambda_prefix_size = lambda_prefix.size();
389 lambda_prefix_size++;
391 auto absolute_lambda_path_end =
392 parameter_type.find(
':', lambda_begin + lambda_prefix_size);
393 auto absolute_lambda_path = parameter_type.substr(
394 lambda_begin + lambda_prefix_size - 1,
395 absolute_lambda_path_end - (lambda_begin + lambda_prefix_size - 1));
400 parameter_type = fmt::format(
"{}(lambda at {}{}",
401 parameter_type.substr(0, lambda_begin), relative_lambda_path,
402 parameter_type.substr(absolute_lambda_path_end));
406bool is_subexpr_of(
const clang::Stmt *parent_stmt,
const clang::Stmt *sub_stmt)
408 if (parent_stmt ==
nullptr || sub_stmt ==
nullptr)
411 if (parent_stmt == sub_stmt)
414 return std::any_of(parent_stmt->child_begin(), parent_stmt->child_end(),
415 [sub_stmt](
const auto *e) { return is_subexpr_of(e, sub_stmt); });
420 return static_cast<eid_t>(
421 static_cast<uint64_t
>(std::hash<std::string>{}(full_name)));
424eid_t to_id(
const clang::QualType &type,
const clang::ASTContext &ctx)
429template <>
eid_t to_id(
const clang::NamespaceDecl &declaration)
449template <>
eid_t to_id(
const clang::CXXRecordDecl &declaration)
456 return to_id(*t.getDecl());
461 return to_id(file.lexically_normal().string());
464template <>
eid_t to_id(
const clang::TemplateArgument &template_argument)
466 if (template_argument.getKind() == clang::TemplateArgument::Type) {
467 if (
const auto *enum_type =
468 template_argument.getAsType()->getAs<clang::EnumType>();
469 enum_type !=
nullptr)
470 return to_id(*enum_type->getAsTagDecl());
472 if (
const auto *record_type =
473 template_argument.getAsType()->getAs<clang::RecordType>();
474 record_type !=
nullptr)
475 return to_id(*record_type->getAsRecordDecl());
478 throw std::runtime_error(
"Cannot generate id for template argument");
481std::pair<common::model::namespace_, std::string>
split_ns(
482 const std::string &full_name)
484 assert(!full_name.empty());
489 auto name = ns.name();
495 const std::string ¶ms,
496 const std::function<std::string(
const std::string &)> &ns_resolve,
501 std::vector<template_parameter> res;
503 auto it = params.begin();
504 while (std::isspace(*it) != 0)
508 std::vector<template_parameter> nested_params;
509 bool complete_class_template_argument{
false};
511 while (it != params.end()) {
514 auto bracket_match_begin = it + 1;
515 auto bracket_match_end = bracket_match_begin;
516 while (bracket_match_end != params.end()) {
517 if (*bracket_match_end ==
'<') {
520 else if (*bracket_match_end ==
'>') {
521 if (nested_level > 0)
531 std::string nested_params_str(
532 bracket_match_begin, bracket_match_end);
535 nested_params_str, ns_resolve, depth + 1);
537 if (nested_params.empty()) {
541 nested_params.emplace_back(
542 template_parameter::make_unexposed_argument(
546 it = bracket_match_end - 1;
548 else if (*it ==
'>') {
549 complete_class_template_argument =
true;
554 else if (*it ==
',') {
555 complete_class_template_argument =
true;
560 if (complete_class_template_argument) {
561 auto t = template_parameter::make_unexposed_argument(
564 for (
auto &¶m : nested_params)
565 t.add_template_param(std::move(param));
567 res.emplace_back(std::move(t));
568 complete_class_template_argument =
false;
574 auto t = template_parameter::make_unexposed_argument(
577 for (
auto &¶m : nested_params)
578 t.add_template_param(std::move(param));
580 res.emplace_back(std::move(t));
588 return t.find(
"type-parameter-") == 0;
593 return q ==
"&" || q ==
"&&" || q ==
"const&";
598 return b ==
"(" || b ==
")" || b ==
"[" || b ==
"]";
603 return std::isalnum(c) != 0 || c ==
'_';
608 return std::all_of(t.begin(), t.end(),
609 [](
const char c) { return is_identifier_character(c); });
614 static std::vector<std::string> keywords{
"alignas",
"alignof",
"asm",
615 "auto",
"bool",
"break",
"case",
"catch",
"char",
"char16_t",
616 "char32_t",
"class",
"concept",
"const",
"constexpr",
"const_cast",
617 "continue",
"decltype",
"default",
"delete",
"do",
"double",
618 "dynamic_cast",
"else",
"enum",
"explicit",
"export",
"extern",
"false",
619 "float",
"for",
"friend",
"goto",
"if",
"inline",
"int",
"long",
620 "mutable",
"namespace",
"new",
"noexcept",
"nullptr",
"operator",
621 "private",
"protected",
"public",
"register",
"reinterpret_cast",
622 "return",
"requires",
"short",
"signed",
"sizeof",
"static",
623 "static_assert",
"static_cast",
"struct",
"switch",
"template",
"this",
624 "thread_local",
"throw",
"true",
"try",
"typedef",
"typeid",
"typename",
625 "union",
"unsigned",
"using",
"virtual",
"void",
"volatile",
"wchar_t",
633 return std::isalpha(t.at(0)) != 0 &&
634 std::all_of(t.begin(), t.end(), [](
const char c) {
635 return is_identifier_character(c) || c ==
':';
647 std::string result{condition_text};
649 if (result.size() < 2)
652 std::vector<std::string> text_lines =
util::split(result,
"\n",
true);
655 for (
auto &line : text_lines) {
661 if (result.at(0) ==
'(' && result.back() ==
')')
662 return result.substr(1, result.size() - 2);
669 auto condition_range =
670 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
677 auto condition_range =
678 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
684 clang::SourceManager &sm, clang::CXXForRangeStmt *stmt)
686 auto condition_range = stmt->getRangeStmt()->getSourceRange();
693 auto condition_range =
694 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
701 auto condition_range = stmt->getCond()->getSourceRange();
707 clang::SourceManager &sm, clang::ConditionalOperator *stmt)
709 auto condition_range = stmt->getCond()->getSourceRange();
719 if (res->isReferenceType())
720 res = res.getNonReferenceType();
721 else if (res->isPointerType())
722 res = res->getPointeeType();
730std::pair<clang::QualType, std::deque<common::model::context>>
733 std::deque<common::model::context> res;
736 bool try_again{
false};
739 if (type.isConstQualified()) {
744 if (type.isVolatileQualified()) {
749 if (type->isPointerType() || type->isReferenceType()) {
750 if (type.isConstQualified() || type.isVolatileQualified()) {
758 if (type->isLValueReferenceType()) {
762 else if (type->isRValueReferenceType()) {
766 else if (type->isMemberFunctionPointerType() &&
767 type->getPointeeType()->getAs<clang::FunctionProtoType>() !=
769 const auto ref_qualifier =
770 type->getPointeeType()
771 ->getAs<clang::FunctionProtoType>()
774 if (ref_qualifier == clang::RefQualifierKind::RQ_RValue) {
778 else if (ref_qualifier == clang::RefQualifierKind::RQ_LValue) {
783 else if (type->isPointerType()) {
789 if (type->isPointerType()) {
790 if (type->getPointeeType().isConstQualified())
792 if (type->getPointeeType().isVolatileQualified())
795 type = type->getPointeeType().getUnqualifiedType();
797 else if (type->isReferenceType()) {
798 if (type.getNonReferenceType().isConstQualified())
800 if (type.getNonReferenceType().isVolatileQualified())
803 type = type.getNonReferenceType().getUnqualifiedType();
805 else if (type.isConstQualified() || type.isVolatileQualified()) {
806 ctx.
is_const = type.isConstQualified();
808 type = type.getUnqualifiedType();
813 if (type->isMemberFunctionPointerType())
814 return std::make_pair(type, res);
817 return std::make_pair(type, res);
822 const std::string &t)
824 std::vector<std::string> result;
828 for (
const auto &word : spaced_out) {
830 if (word !=
"class" && word !=
"templated" && word !=
"struct")
831 result.emplace_back(word);
837 for (
const char c : word) {
838 if (c ==
'(' || c ==
')' || c ==
'[' || c ==
']' || c ==
'<' ||
841 result.emplace_back(tok);
842 result.emplace_back(std::string{c});
846 if (!tok.empty() && tok !=
":") {
847 result.emplace_back(tok);
850 else if (tok ==
":") {
851 result.emplace_back(
"::");
860 result.emplace_back(tok);
862 result.emplace_back(
",");
867 result.emplace_back(tok);
869 result.emplace_back(
"*");
876 result.emplace_back(
"...");
879 else if (tok ==
".") {
882 else if (!tok.empty()) {
883 result.emplace_back(tok);
895 if (tok !=
"class" && tok !=
"typename" && word !=
"struct")
896 result.emplace_back(tok);
905 unsigned &line,
unsigned &column)
909 if (tokens.size() < 3)
912 if (tokens.size() == 4) {
914 decltype(tokens) tmp_tokens{};
915 tmp_tokens.emplace_back(
916 fmt::format(
"{}:{}", tokens.at(0), tokens.at(1)));
917 tmp_tokens.emplace_back(tokens.at(2));
918 tmp_tokens.emplace_back(tokens.at(3));
920 tokens = std::move(tmp_tokens);
925 line = std::stoi(tokens.at(1));
927 catch (std::invalid_argument &e) {
932 column = std::stoi(tokens.at(2));
934 catch (std::invalid_argument &e) {
942 const clang::ASTContext &context,
const clang::Stmt *stmt)
948 const clang::ASTContext &context,
const clang::Decl *decl)
954 const clang::ASTContext &context,
const clang::SourceRange &source_range)
956 auto expr_begin = source_range.getBegin();
957 const auto expr_begin_line = sm.getSpellingLineNumber(expr_begin);
959 std::string file_Path = sm.getFilename(expr_begin).str();
961 auto file_id = sm.getFileID(expr_begin);
963 if (!context.Comments.empty() &&
964 context.Comments.getCommentsInFile(file_id) !=
nullptr) {
965 for (
const auto [offset, raw_comment] :
966 *context.Comments.getCommentsInFile(sm.getFileID(expr_begin))) {
967 const auto comment_end_line = sm.getSpellingLineNumber(
968 raw_comment->getSourceRange().getEnd());
970 if (expr_begin_line == comment_end_line ||
971 expr_begin_line == comment_end_line + 1)
981 const auto *body = decl.getBody();
982 return clang::isa_and_nonnull<clang::CoroutineBodyStmt>(body);
990 if (
const auto *record = clang::dyn_cast<clang::CXXRecordDecl>(decl);
992 return record->isStruct();
995 if (
const auto *tag = clang::dyn_cast<clang::TagDecl>(decl); tag) {
996 return tag->isStruct();
1002bool has_attr(
const clang::FunctionDecl *decl, clang::attr::Kind function_attr)
1004 return std::any_of(decl->attrs().begin(), decl->attrs().end(),
1006 auto &&attr) { return attr->getKind() == function_attr; });
1011 if (
const auto *constant_array =
1012 clang::dyn_cast<clang::ConstantArrayType>(&type);
1013 constant_array !=
nullptr) {
1014 return {constant_array->getSize().getZExtValue()};