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:
47 clang::ObjCIvarDecl::AccessControl access_specifier)
50 switch (access_specifier) {
51 case clang::ObjCIvarDecl::AccessControl::Public:
54 case clang::ObjCIvarDecl::AccessControl::Private:
57 case clang::ObjCIvarDecl::AccessControl::Protected:
71 const auto *parent{declaration.getParent()};
74 while ((parent !=
nullptr) && !parent->isNamespace()) {
75 parent = parent->getParent();
79 std::deque<std::string> namespace_tokens;
80 while ((parent !=
nullptr) && parent->isNamespace()) {
81 if (
const auto *ns_decl = clang::dyn_cast<clang::NamespaceDecl>(parent);
83 if (!ns_decl->isInline() && !ns_decl->isAnonymousNamespace())
84 namespace_tokens.push_front(ns_decl->getNameAsString());
87 parent = parent->getParent();
90 for (
const auto &ns_token : namespace_tokens) {
107 auto base_name = declaration.getNameAsString();
109 if (base_name.empty()) {
111 fmt::format(
"(anonymous_{})", std::to_string(declaration.getID()));
114 if ((declaration.getParent() !=
nullptr) &&
115 declaration.getParent()->isRecord()) {
119 std::deque<std::string> record_parent_names;
120 record_parent_names.push_front(base_name);
122 const auto *cls_parent{declaration.getParent()};
123 while (cls_parent->isRecord()) {
124 if (
const auto *record_decl =
125 clang::dyn_cast<clang::RecordDecl>(cls_parent);
126 record_decl !=
nullptr) {
127 record_parent_names.push_front(record_decl->getNameAsString());
129 cls_parent = cls_parent->getParent();
131 return fmt::format(
"{}", fmt::join(record_parent_names,
"##"));
137std::string
to_string(
const clang::ArrayType &array_type,
138 const clang::ASTContext &ctx,
bool try_canonical,
139 std::vector<std::string> &dimensions)
142 std::string array_size =
143 maybe_size.has_value() ? std::to_string(maybe_size.value()) :
"";
144 dimensions.emplace_back(std::move(array_size));
146 const auto underlying_type = array_type.getElementType();
148 if (underlying_type->isArrayType())
149 return to_string(*underlying_type->getAsArrayTypeUnsafe(), ctx,
150 try_canonical, dimensions);
152 std::string dimensions_str;
153 for (
const auto &d : dimensions) {
154 dimensions_str += fmt::format(
"[{}]", d);
157 "{}{}",
to_string(underlying_type, ctx, try_canonical), dimensions_str);
161 const clang::TemplateArgumentLoc &argLoc,
const clang::ASTContext &context)
164 llvm::raw_string_ostream stream(result);
166 clang::PrintingPolicy policy(context.getLangOpts());
168 const clang::TemplateArgument &arg = argLoc.getArgument();
170#if LLVM_VERSION_MAJOR > 18
171 arg.print(policy, stream,
false);
181std::string
to_string(
const clang::QualType &type,
const clang::ASTContext &ctx,
184 if (type->isArrayType()) {
185 std::vector<std::string> dimensions;
187 *type->getAsArrayTypeUnsafe(), ctx, try_canonical, dimensions);
190 clang::PrintingPolicy print_policy(ctx.getLangOpts());
191 print_policy.SuppressScope = 0;
192 print_policy.PrintCanonicalTypes = 0;
196 result = type.getAsString(print_policy);
198 if (try_canonical && result.find(
'<') != std::string::npos) {
199 auto canonical_type_name =
200 type.getCanonicalType().getAsString(print_policy);
202 auto result_qualified_template_name =
203 result.substr(0, result.find(
'<'));
204 auto result_template_arguments = result.substr(result.find(
'<'));
206 auto canonical_qualified_template_name =
207 canonical_type_name.substr(0, canonical_type_name.find(
'<'));
210 if (result_qualified_template_name.size() <
211 canonical_qualified_template_name.size()) {
214 canonical_qualified_template_name + result_template_arguments;
221 result =
"(anonymous)";
224 const auto *declarationTag = type->getAsTagDecl();
225 if (declarationTag ==
nullptr) {
226 result =
"(unnamed undeclared)";
242 if (result.find(
"type-parameter-") != std::string::npos) {
245 [&result, &type](
auto *p) {
246 auto [unqualified_type, context] =
248 result = p->getDecl()->getNameAsString();
249 if (!context.empty()) {
250 std::vector<std::string> deduced_contexts;
252 for (
const auto &c : context) {
253 deduced_contexts.push_back(c.to_string());
256 result = fmt::format(
257 "{} {}", result, fmt::join(deduced_contexts,
" "));
266 const clang::ASTContext &ctx,
bool try_canonical)
268 return to_string(type.desugar(), ctx, try_canonical);
272 const clang::TemplateArgument &arg,
const clang::ASTContext *ctx)
274 switch (arg.getKind()) {
275 case clang::TemplateArgument::Expression:
277 case clang::TemplateArgument::Type:
278 return to_string(arg.getAsType(), *ctx,
false);
279 case clang::TemplateArgument::Null:
281 case clang::TemplateArgument::NullPtr:
283 case clang::TemplateArgument::Integral:
284 return std::to_string(arg.getAsIntegral().getExtValue());
285 case clang::TemplateArgument::Template:
287 case clang::TemplateArgument::TemplateExpansion:
288 return to_string(arg.getAsTemplateOrTemplatePattern());
296 if (templ.getAsTemplateDecl() !=
nullptr) {
297 return templ.getAsTemplateDecl()->getQualifiedNameAsString();
301 const clang::LangOptions lang_options;
302 llvm::raw_string_ostream ostream(result);
303 templ.print(ostream, clang::PrintingPolicy(lang_options));
310 const clang::LangOptions lang_options;
312 llvm::raw_string_ostream ostream(result);
313 expr->printPretty(ostream,
nullptr, clang::PrintingPolicy(lang_options));
320 return val->getQualifiedNameAsString();
325 const clang::LangOptions lang_options;
327 llvm::raw_string_ostream ostream(result);
328 stmt->printPretty(ostream,
nullptr, clang::PrintingPolicy(lang_options));
333std::string
to_string(
const clang::FunctionTemplateDecl *decl)
335 std::vector<std::string> template_parameters;
337 for (
const auto *parameter : *decl->getTemplateParameters()) {
338 if (clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter) !=
340 const auto *template_type_parameter =
341 clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter);
343 std::string template_parameter{
344 template_type_parameter->getNameAsString()};
346 if (template_type_parameter->isParameterPack())
347 template_parameter +=
"...";
349 template_parameters.emplace_back(std::move(template_parameter));
355 return fmt::format(
"{}<{}>({})", decl->getQualifiedNameAsString(),
356 fmt::join(template_parameters,
","),
"");
364 const clang::PrintingPolicy print_policy(
365 tc->getNamedConcept()->getASTContext().getLangOpts());
367 std::string ostream_buf;
368 llvm::raw_string_ostream ostream{ostream_buf};
369 tc->print(ostream, print_policy);
371 return ostream.str();
375 clang::SourceRange range,
const clang::SourceManager &sm)
377 return clang::Lexer::getSourceText(
378 clang::CharSourceRange::getCharRange(range), sm, clang::LangOptions())
383 clang::SourceRange range,
const clang::SourceManager &sm)
385 const clang::LangOptions lo;
387 auto start_loc = sm.getSpellingLoc(range.getBegin());
388 auto last_token_loc = sm.getSpellingLoc(range.getEnd());
389 auto end_loc = clang::Lexer::getLocForEndOfToken(last_token_loc, 0, sm, lo);
390 auto printable_range = clang::SourceRange{start_loc, end_loc};
394std::tuple<unsigned int, unsigned int, std::string>
397 assert(type_parameter.find(
"type-parameter-") == 0);
399 auto type_parameter_and_suffix =
util::split(type_parameter,
" ");
402 type_parameter_and_suffix.front().substr(strlen(
"type-parameter-")),
405 std::string qualifier;
407 if (type_parameter_and_suffix.size() > 1) {
408 qualifier = type_parameter_and_suffix.at(1);
411 return {std::stoi(toks.at(0)), std::stoi(toks.at(1)), std::move(qualifier)};
419 fmt::format(
"{}", std::filesystem::current_path().root_name().
string());
421 auto root_name = std::string{
"/"};
424 std::string lambda_prefix{fmt::format(
"(lambda at {}", root_name)};
426 while (parameter_type.find(lambda_prefix) != std::string::npos) {
427 auto lambda_begin = parameter_type.find(lambda_prefix);
428 auto lambda_prefix_size = lambda_prefix.size();
431 lambda_prefix_size++;
433 auto absolute_lambda_path_end =
434 parameter_type.find(
':', lambda_begin + lambda_prefix_size);
435 auto absolute_lambda_path = parameter_type.substr(
436 lambda_begin + lambda_prefix_size - 1,
437 absolute_lambda_path_end - (lambda_begin + lambda_prefix_size - 1));
442 parameter_type = fmt::format(
"{}(lambda at {}{}",
443 parameter_type.substr(0, lambda_begin), relative_lambda_path,
444 parameter_type.substr(absolute_lambda_path_end));
448bool is_subexpr_of(
const clang::Stmt *parent_stmt,
const clang::Stmt *sub_stmt)
450 if (parent_stmt ==
nullptr || sub_stmt ==
nullptr)
453 if (parent_stmt == sub_stmt)
456 return std::any_of(parent_stmt->child_begin(), parent_stmt->child_end(),
457 [sub_stmt](
const auto *e) { return is_subexpr_of(e, sub_stmt); });
462 return static_cast<eid_t>(
463 static_cast<uint64_t
>(std::hash<std::string>{}(full_name)));
466eid_t to_id(
const clang::QualType &type,
const clang::ASTContext &ctx)
471template <>
eid_t to_id(
const clang::NamespaceDecl &declaration)
483 return to_id(fmt::format(
"__objc__category__{}", type.getNameAsString()));
488 return to_id(fmt::format(
"__objc__interface__{}", type.getNameAsString()));
493 return to_id(fmt::format(
"__objc__protocol__{}", type.getNameAsString()));
506template <>
eid_t to_id(
const clang::CXXRecordDecl &declaration)
513 return to_id(*t.getDecl());
518 return to_id(file.lexically_normal().string());
521template <>
eid_t to_id(
const clang::TemplateArgument &template_argument)
523 if (template_argument.getKind() == clang::TemplateArgument::Type) {
524 if (
const auto *enum_type =
525 template_argument.getAsType()->getAs<clang::EnumType>();
526 enum_type !=
nullptr)
527 return to_id(*enum_type->getAsTagDecl());
529 if (
const auto *record_type =
530 template_argument.getAsType()->getAs<clang::RecordType>();
531 record_type !=
nullptr)
532 return to_id(*record_type->getAsRecordDecl());
535 throw std::runtime_error(
"Cannot generate id for template argument");
538std::pair<common::model::namespace_, std::string>
split_ns(
539 const std::string &full_name)
541 assert(!full_name.empty());
546 auto name = ns.name();
552 const std::string ¶ms,
553 const std::function<std::string(
const std::string &)> &ns_resolve,
558 std::vector<template_parameter> res;
560 auto it = params.begin();
561 while (std::isspace(*it) != 0)
565 std::vector<template_parameter> nested_params;
566 bool complete_class_template_argument{
false};
568 while (it != params.end()) {
571 auto bracket_match_begin = it + 1;
572 auto bracket_match_end = bracket_match_begin;
573 while (bracket_match_end != params.end()) {
574 if (*bracket_match_end ==
'<') {
577 else if (*bracket_match_end ==
'>') {
578 if (nested_level > 0)
588 std::string nested_params_str(
589 bracket_match_begin, bracket_match_end);
592 nested_params_str, ns_resolve, depth + 1);
594 if (nested_params.empty()) {
598 nested_params.emplace_back(
599 template_parameter::make_unexposed_argument(
603 it = bracket_match_end - 1;
605 else if (*it ==
'>') {
606 complete_class_template_argument =
true;
611 else if (*it ==
',') {
612 complete_class_template_argument =
true;
617 if (complete_class_template_argument) {
618 auto t = template_parameter::make_unexposed_argument(
621 for (
auto &¶m : nested_params)
622 t.add_template_param(std::move(param));
624 res.emplace_back(std::move(t));
625 complete_class_template_argument =
false;
631 auto t = template_parameter::make_unexposed_argument(
634 for (
auto &¶m : nested_params)
635 t.add_template_param(std::move(param));
637 res.emplace_back(std::move(t));
645 return t.find(
"type-parameter-") == 0;
650 return q ==
"&" || q ==
"&&" || q ==
"const&";
655 return b ==
"(" || b ==
")" || b ==
"[" || b ==
"]";
660 return std::isalnum(c) != 0 || c ==
'_';
665 return std::all_of(t.begin(), t.end(),
666 [](
const char c) { return is_identifier_character(c); });
671 static std::vector<std::string> keywords{
"alignas",
"alignof",
"asm",
672 "auto",
"bool",
"break",
"case",
"catch",
"char",
"char16_t",
673 "char32_t",
"class",
"concept",
"const",
"constexpr",
"const_cast",
674 "continue",
"decltype",
"default",
"delete",
"do",
"double",
675 "dynamic_cast",
"else",
"enum",
"explicit",
"export",
"extern",
"false",
676 "float",
"for",
"friend",
"goto",
"if",
"inline",
"int",
"long",
677 "mutable",
"namespace",
"new",
"noexcept",
"nullptr",
"operator",
678 "private",
"protected",
"public",
"register",
"reinterpret_cast",
679 "return",
"requires",
"short",
"signed",
"sizeof",
"static",
680 "static_assert",
"static_cast",
"struct",
"switch",
"template",
"this",
681 "thread_local",
"throw",
"true",
"try",
"typedef",
"typeid",
"typename",
682 "union",
"unsigned",
"using",
"virtual",
"void",
"volatile",
"wchar_t",
690 return std::isalpha(t.at(0)) != 0 &&
691 std::all_of(t.begin(), t.end(), [](
const char c) {
692 return is_identifier_character(c) || c ==
':';
704 std::string result{condition_text};
706 if (result.size() < 2)
709 std::vector<std::string> text_lines =
util::split(result,
"\n",
true);
712 for (
auto &line : text_lines) {
718 if (result.at(0) ==
'(' && result.back() ==
')')
719 return result.substr(1, result.size() - 2);
726 auto condition_range =
727 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
734 auto condition_range =
735 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
741 clang::SourceManager &sm, clang::CXXForRangeStmt *stmt)
743 auto condition_range = stmt->getRangeStmt()->getSourceRange();
750 auto condition_range =
751 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
758 auto condition_range = stmt->getCond()->getSourceRange();
764 clang::SourceManager &sm, clang::ConditionalOperator *stmt)
766 auto condition_range = stmt->getCond()->getSourceRange();
776 if (res->isReferenceType())
777 res = res.getNonReferenceType();
778 else if (res->isPointerType())
779 res = res->getPointeeType();
787std::pair<clang::QualType, std::deque<common::model::context>>
790 std::deque<common::model::context> res;
793 bool try_again{
false};
796 if (type.isConstQualified()) {
801 if (type.isVolatileQualified()) {
806 if (type->isPointerType() || type->isReferenceType()) {
807 if (type.isConstQualified() || type.isVolatileQualified()) {
815 if (type->isLValueReferenceType()) {
819 else if (type->isRValueReferenceType()) {
823 else if (type->isMemberFunctionPointerType() &&
824 type->getPointeeType()->getAs<clang::FunctionProtoType>() !=
826 const auto ref_qualifier =
827 type->getPointeeType()
828 ->getAs<clang::FunctionProtoType>()
831 if (ref_qualifier == clang::RefQualifierKind::RQ_RValue) {
835 else if (ref_qualifier == clang::RefQualifierKind::RQ_LValue) {
840 else if (type->isPointerType()) {
846 if (type->isPointerType()) {
847 if (type->getPointeeType().isConstQualified())
849 if (type->getPointeeType().isVolatileQualified())
852 type = type->getPointeeType().getUnqualifiedType();
854 else if (type->isReferenceType()) {
855 if (type.getNonReferenceType().isConstQualified())
857 if (type.getNonReferenceType().isVolatileQualified())
860 type = type.getNonReferenceType().getUnqualifiedType();
862 else if (type.isConstQualified() || type.isVolatileQualified()) {
863 ctx.
is_const = type.isConstQualified();
865 type = type.getUnqualifiedType();
870 if (type->isMemberFunctionPointerType())
871 return std::make_pair(type, res);
874 return std::make_pair(type, res);
879 const std::string &t)
881 std::vector<std::string> result;
885 for (
const auto &word : spaced_out) {
887 if (word !=
"class" && word !=
"templated" && word !=
"struct")
888 result.emplace_back(word);
894 for (
const char c : word) {
895 if (c ==
'(' || c ==
')' || c ==
'[' || c ==
']' || c ==
'<' ||
898 result.emplace_back(tok);
899 result.emplace_back(std::string{c});
903 if (!tok.empty() && tok !=
":") {
904 result.emplace_back(tok);
907 else if (tok ==
":") {
908 result.emplace_back(
"::");
917 result.emplace_back(tok);
919 result.emplace_back(
",");
924 result.emplace_back(tok);
926 result.emplace_back(
"*");
933 result.emplace_back(
"...");
936 else if (tok ==
".") {
939 else if (!tok.empty()) {
940 result.emplace_back(tok);
952 if (tok !=
"class" && tok !=
"typename" && word !=
"struct")
953 result.emplace_back(tok);
962 unsigned &line,
unsigned &column)
966 if (tokens.size() < 3)
969 if (tokens.size() == 4) {
971 decltype(tokens) tmp_tokens{};
972 tmp_tokens.emplace_back(
973 fmt::format(
"{}:{}", tokens.at(0), tokens.at(1)));
974 tmp_tokens.emplace_back(tokens.at(2));
975 tmp_tokens.emplace_back(tokens.at(3));
977 tokens = std::move(tmp_tokens);
982 line = std::stoi(tokens.at(1));
984 catch (std::invalid_argument &e) {
989 column = std::stoi(tokens.at(2));
991 catch (std::invalid_argument &e) {
999 const clang::ASTContext &context,
const clang::Stmt *stmt)
1005 const clang::ASTContext &context,
const clang::Decl *decl)
1011 const clang::ASTContext &context,
const clang::SourceRange &source_range)
1013 auto expr_begin = source_range.getBegin();
1014 const auto expr_begin_line = sm.getSpellingLineNumber(expr_begin);
1016 std::string file_Path = sm.getFilename(expr_begin).str();
1018 auto file_id = sm.getFileID(expr_begin);
1020 if (!context.Comments.empty() &&
1021 context.Comments.getCommentsInFile(file_id) !=
nullptr) {
1022 for (
const auto [offset, raw_comment] :
1023 *context.Comments.getCommentsInFile(sm.getFileID(expr_begin))) {
1024 const auto comment_end_line = sm.getSpellingLineNumber(
1025 raw_comment->getSourceRange().getEnd());
1027 if (expr_begin_line == comment_end_line ||
1028 expr_begin_line == comment_end_line + 1)
1038 const auto *body = decl.getBody();
1039 return clang::isa_and_nonnull<clang::CoroutineBodyStmt>(body);
1044 if (decl ==
nullptr)
1047 if (
const auto *record = clang::dyn_cast<clang::CXXRecordDecl>(decl);
1049 return record->isStruct();
1052 if (
const auto *tag = clang::dyn_cast<clang::TagDecl>(decl); tag) {
1053 return tag->isStruct();
1059bool has_attr(
const clang::FunctionDecl *decl, clang::attr::Kind function_attr)
1061 return std::any_of(decl->attrs().begin(), decl->attrs().end(),
1063 auto &&attr) { return attr->getKind() == function_attr; });
1068 if (
const auto *constant_array =
1069 clang::dyn_cast<clang::ConstantArrayType>(&type);
1070 constant_array !=
nullptr) {
1071 return {constant_array->getSize().getZExtValue()};
1078 const clang::SourceLocation &location,
1080 std::filesystem::path tu_path, std::filesystem::path relative_to_path_)
1082 namespace fs = std::filesystem;
1088 if (location.isValid()) {
1090 source_manager.getFilename(source_manager.getSpellingLoc(location))
1092 line = source_manager.getSpellingLineNumber(location);
1093 column = source_manager.getSpellingColumnNumber(location);
1098 location.printToString(source_manager), file, line, column);
1103 location.printToString(source_manager), file, line, column);
1105 LOG_DBG(
"Failed to extract source location for element from {}",
1106 location.printToString(source_manager));
1112 fs::path file_path{file};
1113 if (!file_path.is_absolute()) {
1114 file_path = fs::absolute(file_path);
1117 file_path = file_path.lexically_normal();
1119 file = file_path.string();
1125 fs::path{element.
file()}.lexically_relative(relative_to_path_)));
1139 const auto *type_source_info = decl->getTypeSourceInfo();
1141 if (type_source_info ==
nullptr)
1144 return type_source_info->getType().split().Ty;
1149 if (decl ==
nullptr)
1154 if (unqualified_type->getTypeClass() == clang::Type::Elaborated) {
1155 const auto *tag_decl =
1156 clang::cast<clang::ElaboratedType>(unqualified_type)
1160 if (tag_decl ==
nullptr)
1163 const auto *enum_decl = clang::dyn_cast<clang::EnumDecl>(tag_decl);
1164 if (enum_decl !=
nullptr && enum_decl->getIdentifier() ==
nullptr)