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#if LLVM_VERSION_MAJOR < 21
193 print_policy.PrintCanonicalTypes = 0;
195 print_policy.PrintAsCanonical = 0;
200 result = type.getAsString(print_policy);
202 if (try_canonical && result.find(
'<') != std::string::npos) {
203 auto canonical_type_name =
204 type.getCanonicalType().getAsString(print_policy);
206 auto result_qualified_template_name =
207 result.substr(0, result.find(
'<'));
208 auto result_template_arguments = result.substr(result.find(
'<'));
210 auto canonical_qualified_template_name =
211 canonical_type_name.substr(0, canonical_type_name.find(
'<'));
214 if (result_qualified_template_name.size() <
215 canonical_qualified_template_name.size()) {
218 canonical_qualified_template_name + result_template_arguments;
225 result =
"(anonymous)";
228 const auto *declarationTag = type->getAsTagDecl();
229 if (declarationTag ==
nullptr) {
230 result =
"(unnamed undeclared)";
246 if (result.find(
"type-parameter-") != std::string::npos) {
249 [&result, &type](
auto *p) {
250 auto [unqualified_type, context] =
252 result = p->getDecl()->getNameAsString();
253 if (!context.empty()) {
254 std::vector<std::string> deduced_contexts;
256 for (
const auto &c : context) {
257 deduced_contexts.push_back(c.to_string());
260 result = fmt::format(
261 "{} {}", result, fmt::join(deduced_contexts,
" "));
270 const clang::ASTContext &ctx,
bool try_canonical)
272 return to_string(type.desugar(), ctx, try_canonical);
276 const clang::TemplateArgument &arg,
const clang::ASTContext *ctx)
278 switch (arg.getKind()) {
279 case clang::TemplateArgument::Expression:
281 case clang::TemplateArgument::Type:
282 return to_string(arg.getAsType(), *ctx,
false);
283 case clang::TemplateArgument::Null:
285 case clang::TemplateArgument::NullPtr:
287 case clang::TemplateArgument::Integral:
288 return std::to_string(arg.getAsIntegral().getExtValue());
289 case clang::TemplateArgument::Template:
291 case clang::TemplateArgument::TemplateExpansion:
292 return to_string(arg.getAsTemplateOrTemplatePattern());
300 if (templ.getAsTemplateDecl() !=
nullptr) {
301 return templ.getAsTemplateDecl()->getQualifiedNameAsString();
305 const clang::LangOptions lang_options;
306 llvm::raw_string_ostream ostream(result);
307 templ.print(ostream, clang::PrintingPolicy(lang_options));
314 const clang::LangOptions lang_options;
316 llvm::raw_string_ostream ostream(result);
317 expr->printPretty(ostream,
nullptr, clang::PrintingPolicy(lang_options));
324 return val->getQualifiedNameAsString();
329 const clang::LangOptions lang_options;
331 llvm::raw_string_ostream ostream(result);
332 stmt->printPretty(ostream,
nullptr, clang::PrintingPolicy(lang_options));
337std::string
to_string(
const clang::FunctionTemplateDecl *decl)
339 std::vector<std::string> template_parameters;
341 for (
const auto *parameter : *decl->getTemplateParameters()) {
342 if (clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter) !=
344 const auto *template_type_parameter =
345 clang::dyn_cast_or_null<clang::TemplateTypeParmDecl>(parameter);
347 std::string template_parameter{
348 template_type_parameter->getNameAsString()};
350 if (template_type_parameter->isParameterPack())
351 template_parameter +=
"...";
353 template_parameters.emplace_back(std::move(template_parameter));
359 return fmt::format(
"{}<{}>({})", decl->getQualifiedNameAsString(),
360 fmt::join(template_parameters,
","),
"");
368 const clang::PrintingPolicy print_policy(
369 tc->getNamedConcept()->getASTContext().getLangOpts());
371 std::string ostream_buf;
372 llvm::raw_string_ostream ostream{ostream_buf};
373 tc->print(ostream, print_policy);
375 return ostream.str();
379 clang::SourceRange range,
const clang::SourceManager &sm)
381 return clang::Lexer::getSourceText(
382 clang::CharSourceRange::getCharRange(range), sm, clang::LangOptions())
387 clang::SourceRange range,
const clang::SourceManager &sm)
389 const clang::LangOptions lo;
391 auto start_loc = sm.getSpellingLoc(range.getBegin());
392 auto last_token_loc = sm.getSpellingLoc(range.getEnd());
393 auto end_loc = clang::Lexer::getLocForEndOfToken(last_token_loc, 0, sm, lo);
394 auto printable_range = clang::SourceRange{start_loc, end_loc};
398std::tuple<unsigned int, unsigned int, std::string>
401 assert(type_parameter.find(
"type-parameter-") == 0);
403 auto type_parameter_and_suffix =
util::split(type_parameter,
" ");
406 type_parameter_and_suffix.front().substr(strlen(
"type-parameter-")),
409 std::string qualifier;
411 if (type_parameter_and_suffix.size() > 1) {
412 qualifier = type_parameter_and_suffix.at(1);
415 return {std::stoi(toks.at(0)), std::stoi(toks.at(1)), std::move(qualifier)};
423 fmt::format(
"{}", std::filesystem::current_path().root_name().
string());
425 auto root_name = std::string{
"/"};
428 std::string lambda_prefix{fmt::format(
"(lambda at {}", root_name)};
430 while (parameter_type.find(lambda_prefix) != std::string::npos) {
431 auto lambda_begin = parameter_type.find(lambda_prefix);
432 auto lambda_prefix_size = lambda_prefix.size();
435 lambda_prefix_size++;
437 auto absolute_lambda_path_end =
438 parameter_type.find(
':', lambda_begin + lambda_prefix_size);
439 auto absolute_lambda_path = parameter_type.substr(
440 lambda_begin + lambda_prefix_size - 1,
441 absolute_lambda_path_end - (lambda_begin + lambda_prefix_size - 1));
446 parameter_type = fmt::format(
"{}(lambda at {}{}",
447 parameter_type.substr(0, lambda_begin), relative_lambda_path,
448 parameter_type.substr(absolute_lambda_path_end));
452bool is_subexpr_of(
const clang::Stmt *parent_stmt,
const clang::Stmt *sub_stmt)
454 if (parent_stmt ==
nullptr || sub_stmt ==
nullptr)
457 if (parent_stmt == sub_stmt)
460 return std::any_of(parent_stmt->child_begin(), parent_stmt->child_end(),
461 [sub_stmt](
const auto *e) { return is_subexpr_of(e, sub_stmt); });
466 return static_cast<eid_t>(
467 static_cast<uint64_t
>(std::hash<std::string>{}(full_name)));
470eid_t to_id(
const clang::QualType &type,
const clang::ASTContext &ctx)
475template <>
eid_t to_id(
const clang::NamespaceDecl &declaration)
487 return to_id(fmt::format(
"__objc__category__{}", type.getNameAsString()));
492 return to_id(fmt::format(
"__objc__interface__{}", type.getNameAsString()));
497 return to_id(fmt::format(
"__objc__protocol__{}", type.getNameAsString()));
510template <>
eid_t to_id(
const clang::CXXRecordDecl &declaration)
517 return to_id(*t.getDecl());
522 return to_id(file.lexically_normal().string());
525template <>
eid_t to_id(
const clang::TemplateArgument &template_argument)
527 if (template_argument.getKind() == clang::TemplateArgument::Type) {
528 if (
const auto *enum_type =
529 template_argument.getAsType()->getAs<clang::EnumType>();
530 enum_type !=
nullptr)
531 return to_id(*enum_type->getAsTagDecl());
533 if (
const auto *record_type =
534 template_argument.getAsType()->getAs<clang::RecordType>();
535 record_type !=
nullptr)
536 return to_id(*record_type->getAsRecordDecl());
539 throw std::runtime_error(
"Cannot generate id for template argument");
542std::pair<common::model::namespace_, std::string>
split_ns(
543 const std::string &full_name)
545 assert(!full_name.empty());
550 auto name = ns.name();
556 const std::string ¶ms,
557 const std::function<std::string(
const std::string &)> &ns_resolve,
562 std::vector<template_parameter> res;
564 auto it = params.begin();
565 while (std::isspace(*it) != 0)
569 std::vector<template_parameter> nested_params;
570 bool complete_class_template_argument{
false};
572 while (it != params.end()) {
575 auto bracket_match_begin = it + 1;
576 auto bracket_match_end = bracket_match_begin;
577 while (bracket_match_end != params.end()) {
578 if (*bracket_match_end ==
'<') {
581 else if (*bracket_match_end ==
'>') {
582 if (nested_level > 0)
592 std::string nested_params_str(
593 bracket_match_begin, bracket_match_end);
596 nested_params_str, ns_resolve, depth + 1);
598 if (nested_params.empty()) {
602 nested_params.emplace_back(
603 template_parameter::make_unexposed_argument(
607 it = bracket_match_end - 1;
609 else if (*it ==
'>') {
610 complete_class_template_argument =
true;
615 else if (*it ==
',') {
616 complete_class_template_argument =
true;
621 if (complete_class_template_argument) {
622 auto t = template_parameter::make_unexposed_argument(
625 for (
auto &¶m : nested_params)
626 t.add_template_param(std::move(param));
628 res.emplace_back(std::move(t));
629 complete_class_template_argument =
false;
635 auto t = template_parameter::make_unexposed_argument(
638 for (
auto &¶m : nested_params)
639 t.add_template_param(std::move(param));
641 res.emplace_back(std::move(t));
649 return t.find(
"type-parameter-") == 0;
654 return q ==
"&" || q ==
"&&" || q ==
"const&";
659 return b ==
"(" || b ==
")" || b ==
"[" || b ==
"]";
664 return std::isalnum(c) != 0 || c ==
'_';
669 return std::all_of(t.begin(), t.end(),
670 [](
const char c) { return is_identifier_character(c); });
675 static std::vector<std::string> keywords{
"alignas",
"alignof",
"asm",
676 "auto",
"bool",
"break",
"case",
"catch",
"char",
"char16_t",
677 "char32_t",
"class",
"concept",
"const",
"constexpr",
"const_cast",
678 "continue",
"decltype",
"default",
"delete",
"do",
"double",
679 "dynamic_cast",
"else",
"enum",
"explicit",
"export",
"extern",
"false",
680 "float",
"for",
"friend",
"goto",
"if",
"inline",
"int",
"long",
681 "mutable",
"namespace",
"new",
"noexcept",
"nullptr",
"operator",
682 "private",
"protected",
"public",
"register",
"reinterpret_cast",
683 "return",
"requires",
"short",
"signed",
"sizeof",
"static",
684 "static_assert",
"static_cast",
"struct",
"switch",
"template",
"this",
685 "thread_local",
"throw",
"true",
"try",
"typedef",
"typeid",
"typename",
686 "union",
"unsigned",
"using",
"virtual",
"void",
"volatile",
"wchar_t",
694 return std::isalpha(t.at(0)) != 0 &&
695 std::all_of(t.begin(), t.end(), [](
const char c) {
696 return is_identifier_character(c) || c ==
':';
708 std::string result{condition_text};
710 if (result.size() < 2)
713 std::vector<std::string> text_lines =
util::split(result,
"\n",
true);
716 for (
auto &line : text_lines) {
722 if (result.at(0) ==
'(' && result.back() ==
')')
723 return result.substr(1, result.size() - 2);
730 auto condition_range =
731 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
738 auto condition_range =
739 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
745 clang::SourceManager &sm, clang::CXXForRangeStmt *stmt)
747 auto condition_range = stmt->getRangeStmt()->getSourceRange();
754 auto condition_range =
755 clang::SourceRange(stmt->getLParenLoc(), stmt->getRParenLoc());
762 auto condition_range = stmt->getCond()->getSourceRange();
768 clang::SourceManager &sm, clang::ConditionalOperator *stmt)
770 auto condition_range = stmt->getCond()->getSourceRange();
780 if (res->isReferenceType())
781 res = res.getNonReferenceType();
782 else if (res->isPointerType())
783 res = res->getPointeeType();
791std::pair<clang::QualType, std::deque<common::model::context>>
794 std::deque<common::model::context> res;
797 bool try_again{
false};
800 if (type.isConstQualified()) {
805 if (type.isVolatileQualified()) {
810 if (type->isPointerType() || type->isReferenceType()) {
811 if (type.isConstQualified() || type.isVolatileQualified()) {
819 if (type->isLValueReferenceType()) {
823 else if (type->isRValueReferenceType()) {
827 else if (type->isMemberFunctionPointerType() &&
828 type->getPointeeType()->getAs<clang::FunctionProtoType>() !=
830 const auto ref_qualifier =
831 type->getPointeeType()
832 ->getAs<clang::FunctionProtoType>()
835 if (ref_qualifier == clang::RefQualifierKind::RQ_RValue) {
839 else if (ref_qualifier == clang::RefQualifierKind::RQ_LValue) {
844 else if (type->isPointerType()) {
850 if (type->isPointerType()) {
851 if (type->getPointeeType().isConstQualified())
853 if (type->getPointeeType().isVolatileQualified())
856 type = type->getPointeeType().getUnqualifiedType();
858 else if (type->isReferenceType()) {
859 if (type.getNonReferenceType().isConstQualified())
861 if (type.getNonReferenceType().isVolatileQualified())
864 type = type.getNonReferenceType().getUnqualifiedType();
866 else if (type.isConstQualified() || type.isVolatileQualified()) {
867 ctx.
is_const = type.isConstQualified();
869 type = type.getUnqualifiedType();
874 if (type->isMemberFunctionPointerType())
875 return std::make_pair(type, res);
878 return std::make_pair(type, res);
883 const std::string &t)
885 std::vector<std::string> result;
889 for (
const auto &word : spaced_out) {
891 if (word !=
"class" && word !=
"templated" && word !=
"struct")
892 result.emplace_back(word);
898 for (
const char c : word) {
899 if (c ==
'(' || c ==
')' || c ==
'[' || c ==
']' || c ==
'<' ||
902 result.emplace_back(tok);
903 result.emplace_back(std::string{c});
907 if (!tok.empty() && tok !=
":") {
908 result.emplace_back(tok);
911 else if (tok ==
":") {
912 result.emplace_back(
"::");
921 result.emplace_back(tok);
923 result.emplace_back(
",");
928 result.emplace_back(tok);
930 result.emplace_back(
"*");
937 result.emplace_back(
"...");
940 else if (tok ==
".") {
943 else if (!tok.empty()) {
944 result.emplace_back(tok);
956 if (tok !=
"class" && tok !=
"typename" && word !=
"struct")
957 result.emplace_back(tok);
966 unsigned &line,
unsigned &column)
970 if (tokens.size() < 3)
973 if (tokens.size() == 4) {
975 decltype(tokens) tmp_tokens{};
976 tmp_tokens.emplace_back(
977 fmt::format(
"{}:{}", tokens.at(0), tokens.at(1)));
978 tmp_tokens.emplace_back(tokens.at(2));
979 tmp_tokens.emplace_back(tokens.at(3));
981 tokens = std::move(tmp_tokens);
986 line = std::stoi(tokens.at(1));
988 catch (std::invalid_argument &e) {
993 column = std::stoi(tokens.at(2));
995 catch (std::invalid_argument &e) {
1003 const clang::ASTContext &context,
const clang::Stmt *stmt)
1009 const clang::ASTContext &context,
const clang::Decl *decl)
1015 const clang::ASTContext &context,
const clang::SourceRange &source_range)
1017 auto expr_begin = sm.getExpansionLoc(source_range.getBegin());
1018 const auto expr_begin_line = sm.getExpansionLineNumber(expr_begin);
1020 auto file_id = sm.getFileID(expr_begin);
1022 const auto has_comments = !context.Comments.empty();
1023 const auto *comments = context.Comments.getCommentsInFile(file_id);
1025 if (has_comments && comments !=
nullptr) {
1026 for (
const auto [offset, raw_comment] :
1027 *context.Comments.getCommentsInFile(sm.getFileID(expr_begin))) {
1028 const auto comment_end_line = sm.getExpansionLineNumber(
1029 raw_comment->getSourceRange().getEnd());
1031 if (expr_begin_line == comment_end_line ||
1032 expr_begin_line == comment_end_line + 1)
1042 const auto *body = decl.getBody();
1043 return clang::isa_and_nonnull<clang::CoroutineBodyStmt>(body);
1048 if (decl ==
nullptr)
1051 if (
const auto *record = clang::dyn_cast<clang::CXXRecordDecl>(decl);
1053 return record->isStruct();
1056 if (
const auto *tag = clang::dyn_cast<clang::TagDecl>(decl); tag) {
1057 return tag->isStruct();
1063bool has_attr(
const clang::FunctionDecl *decl, clang::attr::Kind function_attr)
1065 return std::any_of(decl->attrs().begin(), decl->attrs().end(),
1067 auto &&attr) { return attr->getKind() == function_attr; });
1072 if (
const auto *constant_array =
1073 clang::dyn_cast<clang::ConstantArrayType>(&type);
1074 constant_array !=
nullptr) {
1075 return {constant_array->getSize().getZExtValue()};
1082 const clang::SourceLocation &location,
1084 std::filesystem::path tu_path, std::filesystem::path relative_to_path_)
1086 namespace fs = std::filesystem;
1092 if (location.isValid()) {
1094 source_manager.getFilename(source_manager.getSpellingLoc(location))
1096 line = source_manager.getSpellingLineNumber(location);
1097 column = source_manager.getSpellingColumnNumber(location);
1102 location.printToString(source_manager), file, line, column);
1107 location.printToString(source_manager), file, line, column);
1109 LOG_DBG(
"Failed to extract source location for element from {}",
1110 location.printToString(source_manager));
1116 fs::path file_path{file};
1117 if (!file_path.is_absolute()) {
1118 file_path = fs::absolute(file_path);
1121 file_path = weakly_canonical(file_path);
1123 file = file_path.string();
1129 fs::path{element.
file()}.lexically_relative(relative_to_path_)));
1143 const auto *type_source_info = decl->getTypeSourceInfo();
1145 if (type_source_info ==
nullptr)
1148 return type_source_info->getType().split().Ty;
1153 if (decl ==
nullptr)
1158 if (unqualified_type->getTypeClass() == clang::Type::Elaborated) {
1159 const auto *tag_decl =
1160 clang::cast<clang::ElaboratedType>(unqualified_type)
1164 if (tag_decl ==
nullptr)
1167 const auto *enum_decl = clang::dyn_cast<clang::EnumDecl>(tag_decl);
1168 if (enum_decl !=
nullptr && enum_decl->getIdentifier() ==
nullptr)
1177 const auto *call_expr = clang::dyn_cast<clang::CallExpr>(expr);
1179 if (call_expr ==
nullptr)
1182 if (clang::dyn_cast_or_null<clang::LambdaExpr>(call_expr->getCallee()) !=
1187 const auto *function_callee = call_expr->getDirectCallee();
1188 if (function_callee !=
nullptr) {
1190 const auto *method_decl =
1191 clang::dyn_cast<clang::CXXMethodDecl>(function_callee);
1192 if (method_decl ==
nullptr)
1195 if (method_decl->getParent()->isLambda() &&
1196 method_decl->getOverloadedOperator() == clang::OO_Call) {
1206 if (decl ==
nullptr)
1209 if (
const auto *method = clang::dyn_cast<clang::CXXMethodDecl>(decl);
1210 method !=
nullptr) {
1211 if (method->getParent() !=
nullptr && method->getParent()->isLambda())