From b5573d91a7c968fd10f416c93e2d9a147ddd708f Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Tue, 17 Dec 2024 11:32:26 -0300 Subject: [PATCH 1/5] InfoTypeFor trait #refactor --- src/lib/AST/ASTVisitor.cpp | 22 +++++----- src/lib/AST/ASTVisitor.hpp | 2 +- src/lib/AST/ClangHelpers.hpp | 78 +++++++++++++++++++++++++----------- 3 files changed, 66 insertions(+), 36 deletions(-) diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index 28f7da0e91..013abd970d 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -348,15 +348,7 @@ defaultTraverseImpl(DeclTy* D, TemplateDeclTy* TD) // If D is also a template declaration, we extract // the template information from the declaration itself - if constexpr (!PopulateFromTD && std::derived_from) - { - defaultTraverseImpl(D, D); - return; - } - - using InfoT = MrDocsType_t; - static constexpr bool HasMrDocsInfoType = !std::same_as; - if constexpr (!HasMrDocsInfoType) + if constexpr (!HasInfoTypeFor) { // Traverse the members of the declaration even if // the declaration does not have a corresponding Info type @@ -364,6 +356,12 @@ defaultTraverseImpl(DeclTy* D, TemplateDeclTy* TD) } else { + if constexpr (!PopulateFromTD && std::derived_from) + { + defaultTraverseImpl(D, D); + return; + } + // If the declaration has a corresponding Info type, // we build the Info object and populate it with the // necessary information. @@ -2805,7 +2803,7 @@ upsert(SymbolID const& id) } template DeclType> -Expected>> +Expected>> ASTVisitor:: upsert(DeclType* D) { @@ -2817,10 +2815,10 @@ upsert(DeclType* D) SymbolID const id = generateID(D); MRDOCS_CHECK_MSG(id, "Failed to extract symbol ID"); - auto [I, isNew] = upsert>(id); + auto [I, isNew] = upsert>(id); I.Access = convertToAccessKind(access); - using R = upsertResult>; + using R = upsertResult>; return R{std::ref(I), isNew}; } diff --git a/src/lib/AST/ASTVisitor.hpp b/src/lib/AST/ASTVisitor.hpp index bce3575d7d..3246a73ca4 100644 --- a/src/lib/AST/ASTVisitor.hpp +++ b/src/lib/AST/ASTVisitor.hpp @@ -879,7 +879,7 @@ class ASTVisitor @param D The declaration to extract */ template DeclType> - Expected>> + Expected>> upsert(DeclType* D); /* Get or construct an empty Info for a dependency declaration. diff --git a/src/lib/AST/ClangHelpers.hpp b/src/lib/AST/ClangHelpers.hpp index 76fc6219e0..16683eac80 100644 --- a/src/lib/AST/ClangHelpers.hpp +++ b/src/lib/AST/ClangHelpers.hpp @@ -63,53 +63,85 @@ SubstituteConstraintExpressionWithoutSatisfaction( When there's no direct correspondence, this trait returns the base Info type. - */ -template -struct MrDocsType : std::type_identity {}; +template +struct InfoTypeFor {}; -template DeclType> -struct MrDocsType : std::type_identity {}; +// Extract NamespaceInfo from NamespaceDecl +template <> +struct InfoTypeFor + : std::type_identity {}; -template VarTy> -struct MrDocsType : std::type_identity {}; +// Extract RecordInfo from anything derived from CXXRecordDecl +template DeclType> +struct InfoTypeFor + : std::type_identity {}; +// Extract FunctionInfo from anything derived from FunctionDecl template FunctionTy> -struct MrDocsType : std::type_identity {}; - -template TypedefNameTy> -struct MrDocsType : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; +// Extract EnumInfo from EnumDecl template <> -struct MrDocsType : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; +// Extract EnumConstantInfo from EnumConstantDecl template <> -struct MrDocsType : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; -template <> -struct MrDocsType : std::type_identity {}; +// Extract TypedefInfo from anything derived from TypedefNameDecl +template TypedefNameTy> +struct InfoTypeFor + : std::type_identity {}; +// Extract VariableInfo from anything derived from VarDecl +template VarTy> +struct InfoTypeFor + : std::type_identity {}; + +// Extract FieldInfo from FieldDecl template <> -struct MrDocsType : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; +// Extract FriendInfo from FriendDecl template <> -struct MrDocsType : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; +// Extract GuideInfo from CXXDeductionGuideDecl template <> -struct MrDocsType : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; +// Extract NamespaceAliasInfo from NamespaceAliasDecl template <> -struct MrDocsType : std::type_identity {}; +struct InfoTypeFor + : std::type_identity {}; +// Extract UsingInfo from UsingDecl template <> -struct MrDocsType : std::type_identity{}; +struct InfoTypeFor + : std::type_identity {}; +// Extract ConceptInfo from ConceptDecl template <> -struct MrDocsType : std::type_identity{}; +struct InfoTypeFor + : std::type_identity {}; + +/// Determine if there's a MrDocs Info type for a Clang DeclType +template +concept HasInfoTypeFor = requires +{ + typename InfoTypeFor::type; +}; -/// @copydoc MrDocsType +/// @copydoc InfoTypeFor template -using MrDocsType_t = typename MrDocsType::type; +using InfoTypeFor_t = typename InfoTypeFor::type; /** Convert a Clang AccessSpecifier into a MrDocs AccessKind */ From 51c0266f01c4e2beb5ff358ab8517cb520303d85 Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Tue, 17 Dec 2024 12:48:39 -0300 Subject: [PATCH 2/5] Support file inputs #test --- src/test/TestRunner.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/test/TestRunner.cpp b/src/test/TestRunner.cpp index de4d86c9c5..bc9ab3edd2 100644 --- a/src/test/TestRunner.cpp +++ b/src/test/TestRunner.cpp @@ -292,28 +292,28 @@ checkPath( namespace fs = llvm::sys::fs; namespace path = llvm::sys::path; + // See if inputPath references a file or directory inputPath = files::normalizePath(inputPath); + auto fileType = files::getFileType(inputPath); + if (!fileType) + { + return report::error("{}: \"{}\"", fileType.error(), inputPath); + } // Set the reference directories for the test - dirs_.configDir = inputPath; + std::string const inputDir = fileType == files::FileType::directory + ? inputPath + : files::getParentDir(inputPath); + dirs_.configDir = inputDir; dirs_.cwd = dirs_.configDir; - // See if inputPath references a file or directory - auto fileType = files::getFileType(inputPath); - if(! fileType) - return report::error("{}: \"{}\"", - fileType.error(), inputPath); - // Check for a directory-wide config Config::Settings dirSettings; testArgs.apply(dirSettings, dirs_, argv); dirSettings.multipage = false; dirSettings.sourceRoot = files::appendPath(inputPath, "."); - dirSettings.stdlibIncludes.insert( - dirSettings.stdlibIncludes.end(), - testArgs.stdlibIncludes.begin(), - testArgs.stdlibIncludes.end()); - std::string const& configPath = files::appendPath(inputPath, "mrdocs.yml"); + + std::string const& configPath = files::appendPath(inputDir, "mrdocs.yml"); if (files::exists(configPath)) { Config::Settings::load_file(dirSettings, configPath, dirs_).value(); if (auto exp = dirSettings.normalize(dirs_); !exp) { From 6a48fb1b1f1766d1de87ad17557f63efc1fd9592 Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Tue, 17 Dec 2024 12:49:09 -0300 Subject: [PATCH 3/5] Map translation units to global namespaces #fix --- src/lib/AST/ASTVisitor.cpp | 35 +++++++++++++++++++++++++++-------- src/lib/AST/ASTVisitor.hpp | 21 ++++++++++++++++++++- src/lib/AST/ClangHelpers.hpp | 6 +++++- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index 013abd970d..c3b6ea689d 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -212,9 +212,7 @@ build() // declarations which satisfy all filter conditions. // dependencies will be tracked, but not extracted traverseAny(context_.getTranslationUnitDecl()); - - // Ensure the global namespace is always present - upsert(SymbolID::global); + MRDOCS_ASSERT(find(SymbolID::global)); // Dependency extraction is disabled: we are done if(config_->referencedDeclarations == @@ -449,11 +447,7 @@ void ASTVisitor:: traverse(UsingDirectiveDecl* D) { - // A using directive such as `using namespace std;` - if (!shouldExtract(D, getAccess(D))) - { - return; - } + MRDOCS_CHECK_OR(shouldExtract(D)); Decl* PD = getParentDecl(D); bool const isNamespaceScope = cast(PD)->isFileContext(); @@ -713,6 +707,21 @@ populate( populateNamespaces(I, D); } +void +ASTVisitor:: +populate( + NamespaceInfo& I, + bool const isNew, + TranslationUnitDecl*) +{ + // Only extract Namespace data once + MRDOCS_CHECK_OR(isNew); + I.id = SymbolID::global; + I.IsAnonymous = false; + I.Name.clear(); + I.IsInline = false; +} + void ASTVisitor:: populate( @@ -2624,6 +2633,12 @@ inExtractedFile( } FileInfo const* file = findFileInfo(D->getBeginLoc()); + if (!file && isa(D)) + { + // TranslationUnitDecl is a special case + // that doesn't have a valid SourceLocation + return true; + } // KRYSTIAN NOTE: I'm unsure under what conditions // this assert would fire. MRDOCS_ASSERT(file); @@ -2755,6 +2770,10 @@ ASTVisitor::FileInfo* ASTVisitor:: findFileInfo(clang::SourceLocation const loc) { + if (loc.isInvalid()) + { + return nullptr; + } // KRYSTIAN FIXME: we really should not be // calling getPresumedLoc this often, // it's quite expensive diff --git a/src/lib/AST/ASTVisitor.hpp b/src/lib/AST/ASTVisitor.hpp index 3246a73ca4..9f3974f57c 100644 --- a/src/lib/AST/ASTVisitor.hpp +++ b/src/lib/AST/ASTVisitor.hpp @@ -481,6 +481,10 @@ class ASTVisitor void populate(NamespaceInfo& I, bool isNew, NamespaceDecl* D); + static + void + populate(NamespaceInfo& I, bool isNew, TranslationUnitDecl* D); + void populate(RecordInfo& I, bool isNew, CXXRecordDecl* D); @@ -753,7 +757,22 @@ class ASTVisitor and false otherwise. */ bool - shouldExtract(const Decl* D, AccessSpecifier access); + shouldExtract(Decl const* D, AccessSpecifier access); + + static + bool + shouldExtract(TranslationUnitDecl const*, AccessSpecifier) + { + return true; + } + + template DeclTy> + bool + shouldExtract(DeclTy const* D) + { + return shouldExtract(D, getAccess(D)); + } + // Determine if a declaration passes the symbol filter bool diff --git a/src/lib/AST/ClangHelpers.hpp b/src/lib/AST/ClangHelpers.hpp index 16683eac80..4f9768a7a6 100644 --- a/src/lib/AST/ClangHelpers.hpp +++ b/src/lib/AST/ClangHelpers.hpp @@ -67,11 +67,15 @@ SubstituteConstraintExpressionWithoutSatisfaction( template struct InfoTypeFor {}; -// Extract NamespaceInfo from NamespaceDecl +// Extract NamespaceInfo from NamespaceDecl or TranslationUnitDecl template <> struct InfoTypeFor : std::type_identity {}; +template <> +struct InfoTypeFor + : std::type_identity {}; + // Extract RecordInfo from anything derived from CXXRecordDecl template DeclType> struct InfoTypeFor From 7333663cbd1bd4227fec1441a0b752bf98f9892c Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Tue, 17 Dec 2024 16:23:26 -0300 Subject: [PATCH 4/5] Info parent is not always namespace #refactor --- docs/modules/ROOT/pages/generators.adoc | 6 +- include/mrdocs/Corpus.hpp | 27 ++-- include/mrdocs/Metadata/Info.hpp | 8 +- include/mrdocs/Metadata/Overloads.hpp | 5 - .../common/partials/symbol/qualified-name.hbs | 4 +- src/lib/AST/ASTVisitor.cpp | 118 +++++++++--------- src/lib/AST/ASTVisitor.hpp | 20 ++- src/lib/AST/ClangHelpers.cpp | 2 +- src/lib/AST/ClangHelpers.hpp | 6 +- src/lib/Lib/Corpus.cpp | 57 +++++++-- src/lib/Lib/Lookup.cpp | 23 ++-- src/lib/Metadata/DomCorpus.cpp | 4 +- src/lib/Metadata/Finalize.cpp | 118 +++++++++++++----- src/lib/Metadata/Finalize.hpp | 4 +- src/lib/Metadata/Info.cpp | 19 ++- src/lib/Metadata/Interface.cpp | 5 +- src/lib/Metadata/Overloads.cpp | 23 ++-- src/lib/Metadata/Reduce.cpp | 25 ++-- src/lib/Metadata/Symbols.cpp | 9 +- src/lib/Support/Debug.cpp | 20 +-- src/lib/Support/LegibleNames.cpp | 19 +-- 21 files changed, 344 insertions(+), 178 deletions(-) diff --git a/docs/modules/ROOT/pages/generators.adoc b/docs/modules/ROOT/pages/generators.adoc index 52cf001933..055392e1b4 100644 --- a/docs/modules/ROOT/pages/generators.adoc +++ b/docs/modules/ROOT/pages/generators.adoc @@ -161,13 +161,13 @@ The `Symbol` object represents a symbol extracted from the source code.The symbo | `string` | Whether the symbol was implicitly extracted as a dependency. -| `namespace` +| `parents` | `<>` -| The namespaces of the symbol. +| The parent contexts (namespaces or records) of the symbol. | `parent` | `<>` -| The parent namespace of the symbol. +| The parent context (namespace or record) of the symbol. | `doc` | `Any` diff --git a/include/mrdocs/Corpus.hpp b/include/mrdocs/Corpus.hpp index e48befbe43..772a3d13b9 100644 --- a/include/mrdocs/Corpus.hpp +++ b/include/mrdocs/Corpus.hpp @@ -247,9 +247,9 @@ traverseOverloads( for(const SymbolID& id : S.Members) { const Info& member = get(id); - const auto& lookup = S.Lookups.at(member.Name); + const auto& members = S.Lookups.at(member.Name); auto first_func = std::ranges::find_if( - lookup, [this](const SymbolID& elem) + members, [this](const SymbolID& elem) { #if 0 const Info& I = get(elem); @@ -258,19 +258,20 @@ traverseOverloads( return get(elem).isFunction(); #endif }); - bool const nonOverloadedFunction = lookup.size() == 1; - bool const notFunction = first_func == lookup.end(); - if(nonOverloadedFunction || - notFunction) + bool const nonOverloadedFunction = members.size() == 1; + bool const notFunction = first_func == members.end(); + if (nonOverloadedFunction || + notFunction) { visit(member, std::forward(f), std::forward(args)...); } - else if(*first_func == id) + else if (*first_func == id) { - OverloadSet overloads(member.Name, - member.Namespace.front(), - member.Namespace, lookup); + OverloadSet overloads( + member.Name, + member.Parent, + members); visit(overloads, std::forward(f), std::forward(args)...); } @@ -344,6 +345,12 @@ class Corpus::iterator } }; +/** Return a list of the parent symbols of the specified Info. + */ +MRDOCS_DECL +std::vector +getParents(Corpus const& C, Info const& I); + } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Info.hpp b/include/mrdocs/Metadata/Info.hpp index a9c99b6ee7..069c133ef0 100644 --- a/include/mrdocs/Metadata/Info.hpp +++ b/include/mrdocs/Metadata/Info.hpp @@ -100,9 +100,12 @@ struct MRDOCS_VISIBLE */ bool Implicit = false; - /** Ordered list of parent namespaces. + /** The parent symbol, if any. + + This is the parent namespace or record + where the symbol is defined. */ - std::vector Namespace; + SymbolID Parent; /** The extracted javadoc for this declaration. */ @@ -225,7 +228,6 @@ tag_invoke( Info const& I, DomCorpus const* domCorpus); - } // mrdocs } // clang diff --git a/include/mrdocs/Metadata/Overloads.hpp b/include/mrdocs/Metadata/Overloads.hpp index 42b7a30379..553d7344d0 100644 --- a/include/mrdocs/Metadata/Overloads.hpp +++ b/include/mrdocs/Metadata/Overloads.hpp @@ -45,9 +45,6 @@ struct OverloadSet /// The parent symbol ID. SymbolID Parent; - /// The namespace of the overload set. - std::span Namespace; - /// The members of the overload set. std::span Members; @@ -62,11 +59,9 @@ struct OverloadSet OverloadSet( std::string_view name, const SymbolID& parent, - std::span ns, std::span members) : Name(name) , Parent(parent) - , Namespace(ns) , Members(members) { } diff --git a/share/mrdocs/addons/generator/common/partials/symbol/qualified-name.hbs b/share/mrdocs/addons/generator/common/partials/symbol/qualified-name.hbs index 2d38e824e1..ffbeb54c0e 100644 --- a/share/mrdocs/addons/generator/common/partials/symbol/qualified-name.hbs +++ b/share/mrdocs/addons/generator/common/partials/symbol/qualified-name.hbs @@ -20,8 +20,8 @@ {{! We remove whitespace between all tag so the result is in a single line ~}} {{~#if (ne kind "friend")~}} {{~#if name~}} - {{! General case: linked namespaces followed by the symbol name ~}} - {{#each (reverse namespace)~}} + {{! General case: linked parent symbols followed by the symbol name ~}} + {{#each parents~}} {{#if name~}} {{>symbol/name . link=. nolink=../nolink}}:: {{~/if}} diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index c3b6ea689d..8ae3e7a3ec 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -449,7 +449,7 @@ traverse(UsingDirectiveDecl* D) { MRDOCS_CHECK_OR(shouldExtract(D)); - Decl* PD = getParentDecl(D); + Decl* PD = getParent(D); bool const isNamespaceScope = cast(PD)->isFileContext(); MRDOCS_CHECK_OR(isNamespaceScope); @@ -704,7 +704,7 @@ populate( I.Name = extractName(D); } I.IsInline = D->isInline(); - populateNamespaces(I, D); + linkParent(I, D); } void @@ -789,7 +789,7 @@ populate( } } - populateNamespaces(I, D); + linkParent(I, D); } template DeclTy> @@ -933,7 +933,7 @@ populate( if(auto* TRC = D->getTrailingRequiresClause()) populate(I.Requires, TRC); - populateNamespaces(I, D); + linkParent(I, D); } void @@ -961,7 +961,7 @@ populate( I.UnderlyingType = toTypeInfo(D->getIntegerType()); } - populateNamespaces(I, D); + linkParent(I, D); } void @@ -986,7 +986,7 @@ populate( D->getInitExpr(), D->getInitVal()); - populateNamespaces(I, D); + linkParent(I, D); } template TypedefNameDeclTy> @@ -1021,7 +1021,7 @@ populate( D->getUnderlyingType(), currentMode()); - populateNamespaces(I, D); + linkParent(I, D); } void @@ -1072,7 +1072,7 @@ populate( I.Type = toTypeInfo(D->getType()); - populateNamespaces(I, D); + linkParent(I, D); } void @@ -1115,7 +1115,7 @@ populate( I.IsDeprecated = D->hasAttr(); I.IsMaybeUnused = D->hasAttr(); - populateNamespaces(I, D); + linkParent(I, D); } void @@ -1137,7 +1137,7 @@ populate( generateID(PD, I.Primary); I.Name = extractName(PD); - populateNamespaces(I, D); + linkParent(I, D); } void @@ -1183,7 +1183,7 @@ populate( I.FriendType = toTypeInfo(TSI->getType()); } - populateNamespaces(I, D); + linkParent(I, D); } void @@ -1217,7 +1217,7 @@ populate( populate(I.Explicit, D->getExplicitSpecifier()); - populateNamespaces(I, D); + linkParent(I, D); } void @@ -1247,7 +1247,7 @@ populate( Underlying->Prefix = toNameInfo(NNS); } - populateNamespaces(I, D); + linkParent(I, D); } void @@ -1275,7 +1275,7 @@ populate( UDS->getTargetDecl(), I.UsingSymbols.emplace_back()); } - populateNamespaces(I, D); + linkParent(I, D); } void @@ -1295,7 +1295,7 @@ populate( I.Name = extractName(D); populate(I.Constraint, D->getConstraintExpr()); - populateNamespaces(I, D); + linkParent(I, D); } void @@ -1690,28 +1690,43 @@ extractName(DeclarationName const N) void ASTVisitor:: -populateNamespaces( +linkParent( Info& I, Decl* D) { - Decl* PD = getParentDecl(D); - SymbolID ParentID = generateID(PD); - switch(PD->getKind()) + // Find the parent DeclContext + Decl* PD = getParent(D); + auto const ParentID = upsertParent(PD, I); + MRDOCS_ASSERT(find(ParentID)); + I.Parent = ParentID; +} + +SymbolID +ASTVisitor:: +upsertParent(Decl* PD, Info& I) +{ + // AFREITAS: this function should eventually + // be replaced with a simple call to upsert(Decl*) + // and another call to addMember(ScopeInfo&, Info&) + + SymbolID const ParentID = generateID(PD); + + // Ensure the Info object for the parent exists + // and ensure D is added to the parent's members + switch (PD->getKind()) { // The TranslationUnit DeclContext is the global namespace; // it uses SymbolID::global and should *always* exist case Decl::TranslationUnit: { MRDOCS_ASSERT(ParentID == SymbolID::global); - auto [P, isNew] = upsert< - NamespaceInfo>(ParentID); + auto [P, isNew] = upsert(ParentID); addMember(P, I); break; } case Decl::Namespace: { - auto [P, isNew] = upsert< - NamespaceInfo>(ParentID); + auto [P, isNew] = upsert(ParentID); populate(P, isNew, cast(PD)); addMember(P, I); break; @@ -1720,40 +1735,37 @@ populateNamespaces( // a member of an implicit instantiation. case Decl::ClassTemplateSpecialization: case Decl::ClassTemplatePartialSpecialization: - if(auto* S = dyn_cast(PD); - S && S->getSpecializationKind() == TSK_ImplicitInstantiation) - { - // KRYSTIAN FIXME: i'm pretty sure DeclContext::getDeclKind() - // will never be Decl::ClassTemplatePartialSpecialization for - // implicit instantiations; instead, the ClassTemplatePartialSpecializationDecl - // is accessible through S->getSpecializedTemplateOrPartial - // if the implicit instantiation used a partially specialized template, - MRDOCS_ASSERT(PD->getKind() != - Decl::ClassTemplatePartialSpecialization); - - auto [P, isNew] = upsert< - SpecializationInfo>(ParentID); - populate(P, isNew, S); - addMember(P, I); - break; - } - // non-implicit instantiations should be - // treated like normal CXXRecordDecls - [[fallthrough]]; + if (auto* S = dyn_cast(PD); + S && S->getSpecializationKind() == TSK_ImplicitInstantiation) + { + // KRYSTIAN FIXME: i'm pretty sure DeclContext::getDeclKind() + // will never be Decl::ClassTemplatePartialSpecialization for + // implicit instantiations; instead, the + // ClassTemplatePartialSpecializationDecl is accessible through + // S->getSpecializedTemplateOrPartial if the implicit instantiation + // used a partially specialized template, + MRDOCS_ASSERT( + PD->getKind() != Decl::ClassTemplatePartialSpecialization); + auto [P, isNew] = upsert(ParentID); + populate(P, isNew, S); + addMember(P, I); + break; + } + // non-implicit instantiations should be + // treated like normal CXXRecordDecls + [[fallthrough]]; // we should never encounter a Record // that is not a CXXRecord case Decl::CXXRecord: { - auto [P, isNew] = upsert< - RecordInfo>(ParentID); + auto [P, isNew] = upsert(ParentID); populate(P, isNew, cast(PD)); addMember(P, I); break; } case Decl::Enum: { - auto [P, isNew] = upsert< - EnumInfo>(ParentID); + auto [P, isNew] = upsert(ParentID); populate(P, isNew, cast(PD)); addMember(P, I); break; @@ -1762,16 +1774,10 @@ populateNamespaces( MRDOCS_UNREACHABLE(); } - Info* P = find(ParentID); - MRDOCS_ASSERT(P); - - I.Namespace.emplace_back(ParentID); - I.Namespace.insert( - I.Namespace.end(), - P->Namespace.begin(), - P->Namespace.end()); + return ParentID; } + void ASTVisitor:: addMember( @@ -2606,7 +2612,7 @@ inExtractedFile( // collect all parent classes/enums/namespaces llvm::SmallVector parents; const Decl* P = ND; - while((P = getParentDecl(P))) + while((P = getParent(P))) { if (isa(P)) { diff --git a/src/lib/AST/ASTVisitor.hpp b/src/lib/AST/ASTVisitor.hpp index 9f3974f57c..51d6c09edf 100644 --- a/src/lib/AST/ASTVisitor.hpp +++ b/src/lib/AST/ASTVisitor.hpp @@ -619,15 +619,23 @@ class ASTVisitor std::string extractName(DeclarationName N); - /* Populate the Info with its parent namespaces + /* Populate the Info.Parent of a declaration - Given a Decl `D`, this function will populate - the `Info` `I` with the SymbolID of each parent namespace - of `D`. The SymbolID of the global namespace is always - included as the first element of `I.Namespace`. + This function will find the parent context `P` of + `D` and then: + + @li It ensures the Info object for the parent context `P` + exists, and that `D` is included as a member of `P`. + @li It ensures the SymbolID of `P` is set as the parent + of `D`. */ void - populateNamespaces(Info& I, Decl* D); + linkParent(Info& I, Decl* D); + + /* Ensure parent exists and has child has member + */ + SymbolID + upsertParent(Decl* Parent, Info& Child); /* Emplace a member Info into a ScopeInfo diff --git a/src/lib/AST/ClangHelpers.cpp b/src/lib/AST/ClangHelpers.cpp index 3b4a7f8335..4df4cf0217 100644 --- a/src/lib/AST/ClangHelpers.cpp +++ b/src/lib/AST/ClangHelpers.cpp @@ -213,7 +213,7 @@ getNTTPFromExpr(const Expr* E, unsigned const Depth) } Decl* -getParentDecl(Decl* D) +getParent(Decl* D) { while((D = cast_if_present< Decl>(D->getDeclContext()))) diff --git a/src/lib/AST/ClangHelpers.hpp b/src/lib/AST/ClangHelpers.hpp index 4f9768a7a6..13ef637f22 100644 --- a/src/lib/AST/ClangHelpers.hpp +++ b/src/lib/AST/ClangHelpers.hpp @@ -718,13 +718,13 @@ getNTTPFromExpr(const Expr* E, unsigned Depth); // Get the parent declaration of a declaration MRDOCS_DECL Decl* -getParentDecl(Decl* D); +getParent(Decl* D); // Get the parent declaration of a declaration inline Decl const* -getParentDecl(Decl const* D) { - return getParentDecl(const_cast(D)); +getParent(Decl const* D) { + return getParent(const_cast(D)); } diff --git a/src/lib/Lib/Corpus.cpp b/src/lib/Lib/Corpus.cpp index be6c22cebd..789a30d865 100644 --- a/src/lib/Lib/Corpus.cpp +++ b/src/lib/Lib/Corpus.cpp @@ -61,27 +61,62 @@ getFullyQualifiedName( if(! I.id || I.id == SymbolID::global) return temp; - MRDOCS_ASSERT(! I.Namespace.empty()); - MRDOCS_ASSERT(I.Namespace.back() == SymbolID::global); - for(auto const& ns_id : I.Namespace | - std::views::reverse | - std::views::drop(1)) + MRDOCS_ASSERT(I.Parent); + for(auto const& pID : getParents(*this, I)) { - if(const Info* ns = find(ns_id)) - temp.append(ns->Name.data(), ns->Name.size()); + if (!pID || pID == SymbolID::global) + { + continue; + } + if (Info const* P = find(pID)) + { + if (!P->Name.empty()) + { + temp.append(P->Name); + } + else + { + fmt::format_to( + std::back_inserter(temp), + "", + toString(P->Kind)); + } + } else + { temp.append(""); - + } temp.append("::"); } - if(I.Name.empty()) - fmt::format_to(std::back_inserter(temp), - "", toString(I.Kind)); + if (I.Name.empty()) + { + fmt::format_to( + std::back_inserter(temp), + "", + toString(I.Kind)); + } else + { temp.append(I.Name); + } return temp; } +std::vector +getParents(Corpus const& C, const Info& I) +{ + // AFREITAS: This function could eventually + // return a view. + std::vector parents; + auto curParent = I.Parent; + while (curParent) + { + parents.push_back(curParent); + curParent = C.get(curParent).Parent; + } + std::ranges::reverse(parents); + return parents; +} } // mrdocs } // clang diff --git a/src/lib/Lib/Lookup.cpp b/src/lib/Lib/Lookup.cpp index 58f23473d1..31e00b2f91 100644 --- a/src/lib/Lib/Lookup.cpp +++ b/src/lib/Lib/Lookup.cpp @@ -97,14 +97,13 @@ SymbolLookup(const Corpus& corpus) const Info* SymbolLookup:: adjustLookupContext( - const Info* context) + Info const* context) { // find the innermost enclosing context that supports name lookup while(! supportsLookup(context)) { - MRDOCS_ASSERT(! context->Namespace.empty()); - const SymbolID& parent = context->Namespace.front(); - context = corpus_.find(parent); + MRDOCS_ASSERT(context->Parent); + context = corpus_.find(context->Parent); } return context; } @@ -187,18 +186,22 @@ lookupUnqualifiedImpl( bool for_nns, LookupCallback& callback) { - if(! context) + if (!context) + { return nullptr; + } context = adjustLookupContext(context); while(context) { - if(auto result = lookupInContext( - context, name, for_nns, callback)) + if (auto result = lookupInContext(context, name, for_nns, callback)) + { return result; - if(context->Namespace.empty()) + } + if (!context->Parent) + { return nullptr; - const SymbolID& parent = context->Namespace.front(); - context = corpus_.find(parent); + } + context = corpus_.find(context->Parent); } MRDOCS_UNREACHABLE(); } diff --git a/src/lib/Metadata/DomCorpus.cpp b/src/lib/Metadata/DomCorpus.cpp index 9cb7c0f0b5..ad80ccd716 100644 --- a/src/lib/Metadata/DomCorpus.cpp +++ b/src/lib/Metadata/DomCorpus.cpp @@ -116,8 +116,10 @@ dom::Value DomCorpus:: get(SymbolID const& id) const { - if(! id) + if (!id) + { return nullptr; + } return impl_->get(id); } diff --git a/src/lib/Metadata/Finalize.cpp b/src/lib/Metadata/Finalize.cpp index c536726cc8..92b507fc8a 100644 --- a/src/lib/Metadata/Finalize.cpp +++ b/src/lib/Metadata/Finalize.cpp @@ -254,12 +254,14 @@ class Finalizer // ---------------------------------------------------------------- - void check(const SymbolID& id) + void + check(const SymbolID& id) { MRDOCS_ASSERT(info_.contains(id)); } - void check(const std::vector& ids) + void + check(const std::vector& ids) { MRDOCS_ASSERT(std::all_of(ids.begin(), ids.end(), [this](const SymbolID& id) @@ -284,18 +286,26 @@ class Finalizer visit(I, *this); } - void operator()(NamespaceInfo& I) + void + operator()(NamespaceInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } check(I.Members); finalize(I.javadoc); finalize(I.UsingDirectives); // finalize(I.Specializations); } - void operator()(RecordInfo& I) + void + operator()(RecordInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } check(I.Members); finalize(I.javadoc); // finalize(I.Specializations); @@ -303,96 +313,144 @@ class Finalizer finalize(I.Bases); } - void operator()(SpecializationInfo& I) + void + operator()(SpecializationInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } check(I.Members); finalize(I.javadoc); finalize(I.Primary); finalize(I.Args); } - void operator()(FunctionInfo& I) + void + operator()(FunctionInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } finalize(I.javadoc); finalize(I.Template); finalize(I.ReturnType); finalize(I.Params); } - void operator()(TypedefInfo& I) + void + operator()(TypedefInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } finalize(I.javadoc); finalize(I.Template); finalize(I.Type); } - void operator()(EnumInfo& I) + void + operator()(EnumInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } check(I.Members); finalize(I.javadoc); finalize(I.UnderlyingType); } - void operator()(FieldInfo& I) + void + operator()(FieldInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } finalize(I.javadoc); finalize(I.Type); } - void operator()(VariableInfo& I) + void + operator()(VariableInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } finalize(I.javadoc); finalize(I.Template); finalize(I.Type); } - void operator()(FriendInfo& I) + void + operator()(FriendInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } finalize(I.javadoc); finalize(I.FriendSymbol); finalize(I.FriendType); } - void operator()(NamespaceAliasInfo& I) + void + operator()(NamespaceAliasInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } finalize(I.javadoc); finalize(I.AliasedSymbol); } - void operator()(UsingInfo& I) + void + operator()(UsingInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } finalize(I.javadoc); finalize(I.Qualifier); finalize(I.UsingSymbols); } - void operator()(EnumConstantInfo& I) + void + operator()(EnumConstantInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } finalize(I.javadoc); } - void operator()(GuideInfo& I) + void + operator()(GuideInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } finalize(I.javadoc); finalize(I.Template); finalize(I.Deduced); finalize(I.Params); } - void operator()(ConceptInfo& I) + void + operator()(ConceptInfo& I) { - check(I.Namespace); + if (I.Parent) + { + check(I.Parent); + } finalize(I.javadoc); finalize(I.Template); } diff --git a/src/lib/Metadata/Finalize.hpp b/src/lib/Metadata/Finalize.hpp index 3b97d1d45f..a90b562310 100644 --- a/src/lib/Metadata/Finalize.hpp +++ b/src/lib/Metadata/Finalize.hpp @@ -17,7 +17,9 @@ namespace clang { namespace mrdocs { -void finalize(InfoSet& Info, SymbolLookup& Lookup); +MRDOCS_DECL +void +finalize(InfoSet& Info, SymbolLookup& Lookup); } // mrdocs } // clang diff --git a/src/lib/Metadata/Info.cpp b/src/lib/Metadata/Info.cpp index 9616cae506..30e7de6979 100644 --- a/src/lib/Metadata/Info.cpp +++ b/src/lib/Metadata/Info.cpp @@ -72,10 +72,23 @@ tag_invoke( io.map("kind", I.Kind); io.map("access", I.Access); io.map("implicit", I.Implicit); - io.map("namespace", dom::LazyArray(I.Namespace, domCorpus)); - if (!I.Namespace.empty()) + if (I.Parent) { - io.map("parent", I.Namespace.front()); + io.map("parent", I.Parent); + io.defer("parents", [&] + { + // A convenient list to iterate over the parents + // with resorting to partial template recursion + Corpus const& corpus = domCorpus->getCorpus(); + auto pIds = getParents(corpus, I); + dom::Array res; + for (auto const& id : pIds) + { + Info const& PI = corpus.get(id); + res.push_back(domCorpus->construct(PI)); + } + return res; + }); } if (I.javadoc) { diff --git a/src/lib/Metadata/Interface.cpp b/src/lib/Metadata/Interface.cpp index 12339d801d..2752a0f2a4 100644 --- a/src/lib/Metadata/Interface.cpp +++ b/src/lib/Metadata/Interface.cpp @@ -113,8 +113,9 @@ class TrancheBuilder bool isFromParent(const Info& I) { - return ! I.Namespace.empty() && - I.Namespace.front() == parent_.id; + return + I.Parent && + I.Parent == parent_.id; } const Info* diff --git a/src/lib/Metadata/Overloads.cpp b/src/lib/Metadata/Overloads.cpp index 318a99fd44..45e2abe689 100644 --- a/src/lib/Metadata/Overloads.cpp +++ b/src/lib/Metadata/Overloads.cpp @@ -18,8 +18,7 @@ #include #include "lib/Support/Radix.hpp" -namespace clang { -namespace mrdocs { +namespace clang::mrdocs { void tag_invoke( @@ -39,12 +38,23 @@ tag_invoke( res.set("kind", "overloads"); res.set("name", overloads.Name); res.set("members", dom::LazyArray(overloads.Members, domCorpus)); - res.set("namespace", dom::LazyArray(overloads.Namespace, domCorpus)); - res.set("parent", domCorpus->get(overloads.Parent)); + + // Copy redundant fields from the first member + if (overloads.Members.size() > 0) + { + dom::Value const member = domCorpus->get(overloads.Members[0]); + if (member.isObject()) + { + for (std::string_view const key: {"parent", "parents"}) + { + res.set(key, member.get(key)); + } + } + } // Copy relevant values from the first member with documentation // that contains it. - for (std::string_view key: {"doc", "loc", "dcl"}) + for (std::string_view const key: {"doc", "loc", "dcl"}) { for (std::size_t i = 0; i < overloads.Members.size(); ++i) { @@ -59,5 +69,4 @@ tag_invoke( v = res; } -} // mrdocs -} // clang +} // clang::mrdocs diff --git a/src/lib/Metadata/Reduce.cpp b/src/lib/Metadata/Reduce.cpp index 2e4d7fe5b5..4cc0065fcb 100644 --- a/src/lib/Metadata/Reduce.cpp +++ b/src/lib/Metadata/Reduce.cpp @@ -103,18 +103,29 @@ void mergeInfo(Info& I, Info&& Other) { MRDOCS_ASSERT(canMerge(I, Other)); MRDOCS_ASSERT(I.id); - if(I.Name == "") + if (I.Name == "") + { I.Name = Other.Name; - if(I.Namespace.empty()) - I.Namespace = std::move(Other.Namespace); - if(I.Access == AccessKind::None) + } + if (I.Parent) + { + I.Parent = std::move(Other.Parent); + } + if (I.Access == AccessKind::None) + { I.Access = Other.Access; + } I.Implicit &= Other.Implicit; - // append javadocs - if(! I.javadoc) + + // Append javadocs + if (!I.javadoc) + { I.javadoc = std::move(Other.javadoc); - else if(Other.javadoc) + } + else if (Other.javadoc) + { merge(*I.javadoc, std::move(*Other.javadoc)); + } } void mergeScopeInfo(ScopeInfo& I, ScopeInfo&& Other) diff --git a/src/lib/Metadata/Symbols.cpp b/src/lib/Metadata/Symbols.cpp index 5e157b970a..a969d158c0 100644 --- a/src/lib/Metadata/Symbols.cpp +++ b/src/lib/Metadata/Symbols.cpp @@ -96,7 +96,14 @@ tag_invoke( dom::Value& v, SymbolID const& id) { - v = toBase16(id); + if (id != SymbolID::invalid) + { + v = toBase16(id); + } + else + { + v = {}; + } } void diff --git a/src/lib/Support/Debug.cpp b/src/lib/Support/Debug.cpp index a222229096..a81543f952 100644 --- a/src/lib/Support/Debug.cpp +++ b/src/lib/Support/Debug.cpp @@ -66,22 +66,28 @@ format( fmt::format_context::iterator fmt::formatter:: format( - const clang::mrdocs::Info& i, + clang::mrdocs::Info const& i, fmt::format_context& ctx) const { std::string str = fmt::format("Info: kind = {}", i.Kind); - if(! i.Name.empty()) + if (!i.Name.empty()) + { str += fmt::format(", name = '{}'", i.Name); + } str += fmt::format(", ID = {}", i.id); - if(! i.Namespace.empty()) + clang::mrdocs::SymbolID curParent = i.Parent; + std::string namespaces; + while (curParent) { - std::string namespaces; - namespaces += fmt::format("{}", i.Namespace[0]); - for(std::size_t idx = 1; idx < i.Namespace.size(); ++idx) + namespaces += fmt::format("{}", curParent); + curParent = i.Parent; + if (curParent) { namespaces += "::"; - namespaces += fmt::format("{}", i.Namespace[idx]); } + } + if (!namespaces.empty()) + { str += fmt::format(", namespace = {}", namespaces); } return fmt::formatter::format(std::move(str), ctx); diff --git a/src/lib/Support/LegibleNames.cpp b/src/lib/Support/LegibleNames.cpp index 07c03f476e..de8a08408d 100644 --- a/src/lib/Support/LegibleNames.cpp +++ b/src/lib/Support/LegibleNames.cpp @@ -374,19 +374,18 @@ class LegibleNames::Impl getLegibleQualified( std::string& result, const SymbolID& id, - char delim) + char const delim) { MRDOCS_ASSERT(corpus_.exists(id)); - auto const& parents = corpus_.get(id).Namespace; - if(parents.size() > 1) + auto const& I = corpus_.get(id); + for(auto const& parent : getParents(corpus_, I)) { - for(auto const& parent : parents | - std::views::reverse | - std::views::drop(1)) + if (!parent || parent == SymbolID::global) { - getLegibleUnqualified(result, parent); - result.push_back(delim); + continue; } + getLegibleUnqualified(result, parent); + result.push_back(delim); } getLegibleUnqualified(result, id); } @@ -435,8 +434,10 @@ getQualified( SymbolID const& id, char delim) const { - if(! impl_) + if (!impl_) + { return toBase16(id); + } std::string result; impl_->getLegibleQualified(result, id, delim); return result; From 6efafc0469e99b64d910732750aa77ada2978510 Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Tue, 17 Dec 2024 16:44:35 -0300 Subject: [PATCH 5/5] Corpus qualified name returns void #refactor --- include/mrdocs/Corpus.hpp | 12 ++++++------ src/lib/Gen/xml/XMLWriter.cpp | 10 +++++++--- src/lib/Lib/Corpus.cpp | 25 +++++++++++++++++-------- src/lib/Lib/TagfileWriter.cpp | 6 +++--- 4 files changed, 33 insertions(+), 20 deletions(-) diff --git a/include/mrdocs/Corpus.hpp b/include/mrdocs/Corpus.hpp index 772a3d13b9..b4bf5a605c 100644 --- a/include/mrdocs/Corpus.hpp +++ b/include/mrdocs/Corpus.hpp @@ -197,18 +197,18 @@ class MRDOCS_VISIBLE @return A reference to the string `temp`. */ - // KRYSTIAN NOTE: temporary MRDOCS_DECL - std::string& - getFullyQualifiedName( - const Info& I, + void + qualifiedName( + Info const& I, std::string& temp) const; std::string - getFullyQualifiedName(const Info& I) const + qualifiedName(const Info& I) const { std::string temp; - return getFullyQualifiedName(I, temp); + qualifiedName(I, temp); + return temp; } }; diff --git a/src/lib/Gen/xml/XMLWriter.cpp b/src/lib/Gen/xml/XMLWriter.cpp index 702749a8b4..bdc783e686 100644 --- a/src/lib/Gen/xml/XMLWriter.cpp +++ b/src/lib/Gen/xml/XMLWriter.cpp @@ -161,13 +161,14 @@ writeIndex() tags_.open("symbols"); if(options_.legible_names) { - LegibleNames names(corpus_, true); + LegibleNames const names(corpus_, true); for(auto& I : corpus_) { + corpus_.qualifiedName(I, temp); auto legible_name = names.getUnqualified(I.id); tags_.write("symbol", {}, { { "legible", legible_name }, - { "name", corpus_.getFullyQualifiedName(I, temp) }, + { "name", temp }, { "tag", toString(I.Kind) }, { I.id } }); } @@ -175,10 +176,13 @@ writeIndex() else { for(auto& I : corpus_) + { + corpus_.qualifiedName(I, temp); tags_.write("symbol", {}, { - { "name", corpus_.getFullyQualifiedName(I, temp) }, + { "name", temp }, { "tag", toString(I.Kind) }, { I.id } }); + } } tags_.close("symbols"); } diff --git a/src/lib/Lib/Corpus.cpp b/src/lib/Lib/Corpus.cpp index 789a30d865..46f985be2b 100644 --- a/src/lib/Lib/Corpus.cpp +++ b/src/lib/Lib/Corpus.cpp @@ -51,15 +51,17 @@ globalNamespace() const noexcept //------------------------------------------------ // KRYSTIAN NOTE: temporary -std::string& +void Corpus:: -getFullyQualifiedName( +qualifiedName( const Info& I, std::string& temp) const { temp.clear(); if(! I.id || I.id == SymbolID::global) - return temp; + { + return; + } MRDOCS_ASSERT(I.Parent); for(auto const& pID : getParents(*this, I)) @@ -99,22 +101,29 @@ getFullyQualifiedName( { temp.append(I.Name); } - return temp; } std::vector getParents(Corpus const& C, const Info& I) { - // AFREITAS: This function could eventually - // return a view. std::vector parents; + std::size_t n = 0; auto curParent = I.Parent; while (curParent) { - parents.push_back(curParent); + ++n; + MRDOCS_ASSERT(C.find(curParent)); + curParent = C.get(curParent).Parent; + } + parents.reserve(n); + parents.resize(n); + curParent = I.Parent; + while (curParent) + { + parents[--n] = curParent; + MRDOCS_ASSERT(C.find(curParent)); curParent = C.get(curParent).Parent; } - std::ranges::reverse(parents); return parents; } diff --git a/src/lib/Lib/TagfileWriter.cpp b/src/lib/Lib/TagfileWriter.cpp index 5ef5fe017c..f1ec7be081 100644 --- a/src/lib/Lib/TagfileWriter.cpp +++ b/src/lib/Lib/TagfileWriter.cpp @@ -134,7 +134,7 @@ writeNamespace( { "kind", "namespace" } }); - tags_.write("name", corpus_->getFullyQualifiedName(I)); + tags_.write("name", corpus_->qualifiedName(I)); tags_.write("filename", generateFilename(I)); // Write the class-like members of this namespace @@ -144,7 +144,7 @@ writeNamespace( { tags_.write( "class", - corpus_->getFullyQualifiedName(J), + corpus_->qualifiedName(J), {{"kind", "class"}}); } }); @@ -178,7 +178,7 @@ writeClassLike( tags_.open("compound", { { "kind", "class" } }); - tags_.write("name", corpus_->getFullyQualifiedName(I)); + tags_.write("name", corpus_->qualifiedName(I)); tags_.write("filename", generateFilename(I)); if constexpr (T::isRecord()) {