diff --git a/matchers/class_matcher.cpp b/matchers/class_matcher.cpp index 196d1f7..1808e8d 100644 --- a/matchers/class_matcher.cpp +++ b/matchers/class_matcher.cpp @@ -73,7 +73,7 @@ class FindStaticMembers : public RecursiveASTVisitor { auto type = hyde::StandardDeclInfo(context, d); auto name = type["qualified_name"].get(); type["static"] = true; - type["type"] = hyde::to_string(context, d->getType()); + type["type"] = hyde::to_string(d, d->getType()); static_members[name] = type; } return true; @@ -151,7 +151,7 @@ void ClassInfo::run(const MatchFinder::MatchResult& Result) { if (!AccessCheck(_access_filter, field->getAccess())) continue; json fieldInfo = StandardDeclInfo(Result.Context, field); - fieldInfo["type"] = hyde::to_string(Result.Context, field->getType()); + fieldInfo["type"] = hyde::to_string(field, field->getType()); info["fields"][static_cast(fieldInfo["qualified_name"])] = fieldInfo; // can't move this into place for some reason. } @@ -174,7 +174,7 @@ void ClassInfo::run(const MatchFinder::MatchResult& Result) { if (!AccessCheck(_access_filter, type_def->getAccess())) continue; json typedefInfo = StandardDeclInfo(Result.Context, type_def); - typedefInfo["type"] = hyde::to_string(Result.Context, type_def->getUnderlyingType()); + typedefInfo["type"] = hyde::to_string(type_def, type_def->getUnderlyingType()); info["typedefs"].push_back(std::move(typedefInfo)); } @@ -188,7 +188,7 @@ void ClassInfo::run(const MatchFinder::MatchResult& Result) { if (!AccessCheck(_access_filter, type_alias->getAccess())) continue; json typealiasInfo = StandardDeclInfo(Result.Context, type_alias); - typealiasInfo["type"] = hyde::to_string(Result.Context, type_alias->getUnderlyingType()); + typealiasInfo["type"] = hyde::to_string(type_alias, type_alias->getUnderlyingType()); if (auto template_decl = type_alias->getDescribedAliasTemplate()) { typealiasInfo["template_parameters"] = GetTemplateParameters(Result.Context, template_decl); diff --git a/matchers/enum_matcher.cpp b/matchers/enum_matcher.cpp index 5dd904e..015d5ec 100644 --- a/matchers/enum_matcher.cpp +++ b/matchers/enum_matcher.cpp @@ -43,7 +43,7 @@ void EnumInfo::run(const MatchFinder::MatchResult& Result) { json info = StandardDeclInfo(Result.Context, enumeration); //info["scoped"] = enumeration->isScoped(); //info["fixed"] = enumeration->isFixed(); - info["type"] = hyde::to_string(Result.Context, enumeration->getIntegerType()); + info["type"] = hyde::to_string(enumeration, enumeration->getIntegerType()); info["values"] = json::array(); for (const auto& p : enumeration->enumerators()) { diff --git a/matchers/matcher_fwd.hpp b/matchers/matcher_fwd.hpp index 4ee46ec..4d70c8b 100644 --- a/matchers/matcher_fwd.hpp +++ b/matchers/matcher_fwd.hpp @@ -18,9 +18,9 @@ namespace hyde { /**************************************************************************************************/ enum ToolAccessFilter { - ToolAccessFilterPublic, + ToolAccessFilterPrivate, ToolAccessFilterProtected, - ToolAccessFilterPrivate + ToolAccessFilterPublic, }; /**************************************************************************************************/ diff --git a/matchers/typealias_matcher.cpp b/matchers/typealias_matcher.cpp index 231f182..f1beb8a 100644 --- a/matchers/typealias_matcher.cpp +++ b/matchers/typealias_matcher.cpp @@ -45,7 +45,7 @@ void TypeAliasInfo::run(const MatchFinder::MatchResult& Result) { // do not process class type aliases here. if (!info["parents"].empty()) return; - info["type"] = hyde::to_string(Result.Context, node->getUnderlyingType()); + info["type"] = hyde::to_string(node, node->getUnderlyingType()); if (auto template_decl = node->getDescribedAliasTemplate()) { info["template_parameters"] = GetTemplateParameters(Result.Context, template_decl); diff --git a/matchers/typedef_matcher.cpp b/matchers/typedef_matcher.cpp index 2d01554..e2f0157 100644 --- a/matchers/typedef_matcher.cpp +++ b/matchers/typedef_matcher.cpp @@ -45,7 +45,7 @@ void TypedefInfo::run(const MatchFinder::MatchResult& Result) { // do not process class type aliases here. if (!info["parents"].empty()) return; - info["type"] = hyde::to_string(Result.Context, node->getUnderlyingType()); + info["type"] = hyde::to_string(node, node->getUnderlyingType()); _j["typedefs"].push_back(std::move(info)); } diff --git a/matchers/utilities.cpp b/matchers/utilities.cpp index 3f5b97f..cd0c959 100644 --- a/matchers/utilities.cpp +++ b/matchers/utilities.cpp @@ -69,13 +69,12 @@ std::string to_string(const ASTContext* n, const clang::TemplateDecl* template_d std::string result = "template <"; for (const auto& parameter_decl : *template_decl->getTemplateParameters()) { if (count++) result += ", "; - if (const auto& template_type = dyn_cast(parameter_decl)) { + if (auto* template_type = dyn_cast(parameter_decl)) { result += template_type->wasDeclaredWithTypename() ? "typename" : "class"; if (template_type->isParameterPack()) result += "..."; result += " " + template_type->getNameAsString(); - } else if (const auto& non_template_type = - dyn_cast(parameter_decl)) { - result += hyde::to_string(n, non_template_type->getType()); + } else if (auto* non_template_type = dyn_cast(parameter_decl)) { + result += hyde::to_string(non_template_type, non_template_type->getType()); if (non_template_type->isParameterPack()) result += "..."; result += " " + non_template_type->getNameAsString(); } @@ -87,7 +86,7 @@ std::string to_string(const ASTContext* n, const clang::TemplateDecl* template_d /**************************************************************************************************/ -template +template void ForEachParent(const Decl* d, F f) { if (!d) return; @@ -102,7 +101,7 @@ void ForEachParent(const Decl* d, F f) { auto* parent = parents.begin(); - if (auto* node = parent->get()) { + if (auto* node = parent->get()) { f(node); } @@ -112,75 +111,6 @@ void ForEachParent(const Decl* d, F f) { /**************************************************************************************************/ -std::string post_process_name(const FunctionDecl* function, std::string name) { - // If our name has `type-parameter-N-M` in it, run up the parent tree in an attempt - // to resolve them. - static const std::string needle("type-parameter-"); - - auto pos = name.find(needle); - - // Do this early to avoid the following rigamaroll for nearly all use cases. - if (pos == std::string::npos) return name; - - auto& context = function->getASTContext(); - std::vector> parent_template_types; - - ForEachParent(function, [&](const Decl* parent) { - // REVISIT (fbrereto) : Gotta do this for FunctionTemplateDecl, and ClassTemplateDecl - if (auto* ctpsd = dyn_cast_or_null(parent)) { - for (const auto& parameter_decl : *ctpsd->getTemplateParameters()) { - if (auto* template_type = dyn_cast(parameter_decl)) { - auto depth = template_type->getDepth(); - auto index = template_type->getIndex(); - auto qualType = context.getTemplateTypeParmType( - depth, index, template_type->isParameterPack(), template_type); - - std::string old_type = - needle + std::to_string(depth) + "-" + std::to_string(index); - std::string new_type = hyde::to_string(&context, qualType); - parent_template_types.emplace_back( - std::make_pair(std::move(old_type), std::move(new_type))); - } - } - } - }); - - while (true) { - auto end_pos = pos + needle.size(); - - // This loop is faster than std::string::find_first_not_of - while (true) { - auto c = name[end_pos]; - if (!std::isdigit(c) && c != '-') break; - ++end_pos; - } - - auto length = end_pos - pos; - std::string old_type = name.substr(pos, length); - - // sort and lower_bound this? - auto found = std::find_if(parent_template_types.begin(), parent_template_types.end(), - [&](const auto& cur_pair) { return cur_pair.first == old_type; }); - - if (found != parent_template_types.end()) { - const auto& new_type = found->second; - name.replace(pos, length, new_type); - pos += new_type.size(); - } else { - // Not resolved. Skip to avoid infinite loop. - pos += old_type.size(); - } - - pos = name.find(needle, pos); - - if (pos == std::string::npos) break; - } - - return name; -} - -/**************************************************************************************************/ - enum class signature_options : std::uint8_t { none = 0, fully_qualified = 1 << 0, @@ -240,7 +170,7 @@ std::string GetSignature(const ASTContext* n, if (isTrailing) { signature << "auto "; } else { - signature << hyde::to_string(n, function->getReturnType()) << " "; + signature << hyde::to_string(function, function->getReturnType()) << " "; } } @@ -263,16 +193,18 @@ std::string GetSignature(const ASTContext* n, } if (auto conversionDecl = llvm::dyn_cast_or_null(function)) { - signature << "operator " << hyde::to_string(n, conversionDecl->getConversionType()); + signature << "operator " + << hyde::to_string(conversionDecl, conversionDecl->getConversionType()); } else { - signature << post_process_name(function, function->getNameInfo().getAsString()); + signature << hyde::PostProcessType(function, function->getNameInfo().getAsString()); } signature << "("; for (int i = 0, paramsCount = function->getNumParams(); i < paramsCount; ++i) { if (i) signature << ", "; - signature << hyde::to_string(n, function->getParamDecl(i)->getType()); + auto* paramDecl = function->getParamDecl(i); + signature << hyde::to_string(paramDecl, paramDecl->getType()); if (named_args) { auto arg_name = function->getParamDecl(i)->getNameAsString(); if (!arg_name.empty()) { @@ -293,7 +225,7 @@ std::string GetSignature(const ASTContext* n, if (functionT->isRestrict()) signature << " restrict"; } - signature << " -> " << hyde::to_string(n, function->getReturnType()); + signature << " -> " << hyde::to_string(function, function->getReturnType()); } if (!canHaveCV) { @@ -330,11 +262,11 @@ std::string GetShortName(const clang::ASTContext* n, const clang::FunctionDecl* // the return type and the open paren. It's used e.g., for the file names // being output. - std::string result = post_process_name(function, function->getNameInfo().getAsString()); + std::string result = hyde::PostProcessType(function, function->getNameInfo().getAsString()); if (!isa(function) && !isa(function) && !isa(function)) { - std::string return_type = hyde::to_string(n, function->getReturnType()); + std::string return_type = hyde::to_string(function, function->getReturnType()); auto return_pos = result.find(return_type); if (return_pos != std::string::npos) { result = result.substr(return_pos + return_type.size(), std::string::npos); @@ -364,11 +296,12 @@ hyde::json GetParents(const ASTContext* n, const Decl* d) { if (node) { std::string name = node->getNameAsString(); if (auto specialization = dyn_cast_or_null(node)) { - name = hyde::to_string(n, specialization->getTypeAsWritten()->getType()); + name = + hyde::to_string(specialization, specialization->getTypeAsWritten()->getType()); } else if (auto cxxrecord = dyn_cast_or_null(node)) { if (auto template_decl = cxxrecord->getDescribedClassTemplate()) { name += - hyde::GetArgumentList(n, template_decl->getTemplateParameters()->asArray()); + hyde::GetArgumentList(template_decl->getTemplateParameters()->asArray()); } } @@ -410,11 +343,11 @@ json DetailCXXRecordDecl(const ASTContext* n, const clang::CXXRecordDecl* cxx) { // overrides for various fields if the record is of a specific sub-type. if (auto s = llvm::dyn_cast_or_null(cxx)) { - info["name"] = hyde::to_string(n, s->getTypeAsWritten()->getType()); + info["name"] = hyde::to_string(s, s->getTypeAsWritten()->getType()); info["qualified_name"] = s->getQualifiedNameAsString(); } else if (auto template_decl = cxx->getDescribedClassTemplate()) { std::string arguments = - GetArgumentList(n, template_decl->getTemplateParameters()->asArray()); + GetArgumentList(template_decl->getTemplateParameters()->asArray()); info["name"] = static_cast(info["name"]) + arguments; info["qualified_name"] = template_decl->getQualifiedNameAsString(); } @@ -437,7 +370,8 @@ json GetTemplateParameters(const ASTContext* n, const clang::TemplateDecl* d) { parameter_info["name"] = template_type->getNameAsString(); } else if (const auto& non_template_type = dyn_cast(parameter_decl)) { - parameter_info["type"] = hyde::to_string(n, non_template_type->getType()); + parameter_info["type"] = + hyde::to_string(non_template_type, non_template_type->getType()); if (non_template_type->isParameterPack()) parameter_info["parameter_pack"] = "true"; parameter_info["name"] = non_template_type->getNameAsString(); } else if (const auto& template_template_type = @@ -461,7 +395,7 @@ json GetTemplateParameters(const ASTContext* n, const clang::TemplateDecl* d) { json DetailFunctionDecl(const ASTContext* n, const FunctionDecl* f) { json info = StandardDeclInfo(n, f); - info["return_type"] = hyde::to_string(n, f->getReturnType()); + info["return_type"] = hyde::to_string(f, f->getReturnType()); info["arguments"] = json::array(); info["signature"] = GetSignature(n, f); info["signature_with_names"] = GetSignature(n, f, signature_options::named_args); @@ -515,7 +449,7 @@ json DetailFunctionDecl(const ASTContext* n, const FunctionDecl* f) { for (const auto& p : f->parameters()) { json argument = json::object(); - argument["type"] = to_string(n, p->getOriginalType()); + argument["type"] = to_string(p, p->getOriginalType()); argument["name"] = p->getNameAsString(); info["arguments"].push_back(std::move(argument)); @@ -525,47 +459,7 @@ json DetailFunctionDecl(const ASTContext* n, const FunctionDecl* f) { /**************************************************************************************************/ -std::string GetArgumentList(const ASTContext* n, - const llvm::ArrayRef args) { - bool first = true; - std::stringstream str; - str << "<"; - - // There is a better approach coming - // https://en.cppreference.com/w/cpp/experimental/ostream_joiner/make_ostream_joiner - - for (const auto& arg : args) { - if (first) { - first = false; - } else { - str << ", "; - } - - switch (arg.getKind()) { - case TemplateArgument::ArgKind::Type: { - str << to_string(n, arg.getAsType()); - } break; - case TemplateArgument::ArgKind::Integral: { - str << arg.getAsIntegral().toString(10); - } break; - case TemplateArgument::ArgKind::Template: { - str << ::to_string(n, arg.getAsTemplate().getAsTemplateDecl()); - } break; - case TemplateArgument::ArgKind::Expression: { - str << ::to_string(n, arg.getAsExpr()->getSourceRange(), true); - } break; - default: { str << "XXXXXX"; } break; - } - } - - str << ">"; - - return str.str(); -} - -/**************************************************************************************************/ - -std::string GetArgumentList(const ASTContext*, const llvm::ArrayRef args) { +std::string GetArgumentList(const llvm::ArrayRef args) { std::size_t count{0}; std::string result("<"); @@ -621,6 +515,76 @@ bool AccessCheck(ToolAccessFilter hyde_filter, clang::AccessSpecifier clang_acce /**************************************************************************************************/ +std::string PostProcessType(const clang::Decl* decl, std::string type) { + // If our type contains one or more `type-parameter-N-M`s, run up the parent + // tree in an attempt to resolve them. + static const std::string needle("type-parameter-"); + + auto pos = type.find(needle); + + // Do this early to avoid the following rigamaroll for nearly all use cases. + if (pos == std::string::npos) return type; + + auto& context = decl->getASTContext(); + std::vector> parent_template_types; + + ForEachParent(decl, [&](const Decl* parent) { + // REVISIT (fbrereto) : Gotta do this for FunctionTemplateDecl, and ClassTemplateDecl + if (auto* ctpsd = dyn_cast_or_null(parent)) { + for (const auto& parameter_decl : *ctpsd->getTemplateParameters()) { + if (auto* template_type = dyn_cast(parameter_decl)) { + auto depth = template_type->getDepth(); + auto index = template_type->getIndex(); + auto qualType = context.getTemplateTypeParmType( + depth, index, template_type->isParameterPack(), template_type); + + std::string old_type = + needle + std::to_string(depth) + "-" + std::to_string(index); + std::string new_type = hyde::to_string(template_type, qualType); + parent_template_types.emplace_back( + std::make_pair(std::move(old_type), std::move(new_type))); + } + } + } + }); + + while (true) { + auto end_pos = pos + needle.size(); + + // This loop is faster than std::string::find_first_not_of + while (true) { + auto c = type[end_pos]; + if (!std::isdigit(c) && c != '-') break; + ++end_pos; + } + + auto length = end_pos - pos; + std::string old_type = type.substr(pos, length); + + // sort and lower_bound this? parent_template_types.size() is usually + // small (< 5 or so), so it might not be worth the effort. + auto found = std::find_if(parent_template_types.begin(), parent_template_types.end(), + [&](const auto& cur_pair) { return cur_pair.first == old_type; }); + + if (found != parent_template_types.end()) { + const auto& new_type = found->second; + type.replace(pos, length, new_type); + pos += new_type.size(); + } else { + // Not resolved. Skip to avoid infinite loop. + pos += old_type.size(); + } + + pos = type.find(needle, pos); + + if (pos == std::string::npos) break; + } + + return type; +} + +/**************************************************************************************************/ + } // namespace hyde /**************************************************************************************************/ diff --git a/matchers/utilities.hpp b/matchers/utilities.hpp index b6f1c12..dfd1951 100644 --- a/matchers/utilities.hpp +++ b/matchers/utilities.hpp @@ -6,7 +6,7 @@ NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms of the Adobe license agreement accompanying it. If you have received this file from a source other than Adobe, then your use, modification, or distribution of it requires the prior -written permission of Adobe. +written permission of Adobe. */ #pragma once @@ -15,11 +15,11 @@ written permission of Adobe. #include "boost/filesystem.hpp" // clang -#include "llvm/ADT/ArrayRef.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" +#include "llvm/ADT/ArrayRef.h" // application #include "json.hpp" @@ -45,25 +45,31 @@ bool PathCheck(const std::vector& paths, const clang::Decl* d, clan bool AccessCheck(ToolAccessFilter hyde_filter, clang::AccessSpecifier clang_access); -std::string GetArgumentList(const clang::ASTContext* n, const llvm::ArrayRef args); -std::string GetArgumentList(const clang::ASTContext* n, const llvm::ArrayRef args); +std::string GetArgumentList(const llvm::ArrayRef args); + +// type-parameter-N-M filtering. +std::string PostProcessType(const clang::Decl* decl, std::string type); /**************************************************************************************************/ inline std::string to_string(clang::AccessSpecifier access) { switch (access) { - case clang::AccessSpecifier::AS_public: return "public"; - case clang::AccessSpecifier::AS_protected: return "protected"; - case clang::AccessSpecifier::AS_private: return "private"; - case clang::AccessSpecifier::AS_none: return "none"; + case clang::AccessSpecifier::AS_public: + return "public"; + case clang::AccessSpecifier::AS_protected: + return "protected"; + case clang::AccessSpecifier::AS_private: + return "private"; + case clang::AccessSpecifier::AS_none: + return "none"; } } /**************************************************************************************************/ -inline std::string to_string(const clang::ASTContext* n, clang::QualType type) { - static const clang::PrintingPolicy policy(n->getLangOpts()); - std::string result = type.getAsString(policy); +inline std::string to_string(const clang::Decl* decl, clang::QualType type) { + static const clang::PrintingPolicy policy(decl->getASTContext().getLangOpts()); + std::string result = PostProcessType(decl, type.getAsString(policy)); bool is_lambda = result.find("(lambda at ") == 0; return is_lambda ? "__lambda" : result; } @@ -80,15 +86,15 @@ json StandardDeclInfo(const clang::ASTContext* n, const DeclarationType* d) { info["qualified_name"] = d->getQualifiedNameAsString(); std::string access(to_string(d->getAccess())); - if (access != "none" ) info["access"] = std::move(access); - info["defined-in-file"] = [&]{ + if (access != "none") info["access"] = std::move(access); + info["defined-in-file"] = [&] { auto beginLoc = d->getBeginLoc(); auto location = beginLoc.printToString(n->getSourceManager()); return location.substr(0, location.find(':')); }(); info["deprecated"] = false; - + if (auto attr = d->template getAttr()) { info["deprecated"] = true; auto message = attr->getMessage();