diff --git a/include/mrdox/Config.hpp b/include/mrdox/Config.hpp index 6ae2d3b19..12a9aa09f 100644 --- a/include/mrdox/Config.hpp +++ b/include/mrdox/Config.hpp @@ -42,8 +42,37 @@ class MRDOX_DECL Config() noexcept; public: + struct ExtractOptions + { + enum class Policy + { + Always, + Dependency, + Never + }; + + /** Extraction policy for references to external declarations. + + This determines how declarations which are referenced by + explicitly extracted declarations are extracted. + + Given a function parameter of type `std::string`, `std::string` + would be extracted if this option is set to `Policy::Always`. + */ + Policy referencedDeclarations = Policy::Dependency; + + Policy anonymousNamespaces = Policy::Always; + + Policy inaccessibleMembers = Policy::Always; + + Policy inaccessibleBases = Policy::Always; + }; + struct Settings { + + ExtractOptions extractOptions; + /** `true` if anonymous namespace members should be extracted and displayed. In some cases anonymous namespace members will diff --git a/include/mrdox/Metadata/Type.hpp b/include/mrdox/Metadata/Type.hpp index a6328e999..c08606632 100644 --- a/include/mrdox/Metadata/Type.hpp +++ b/include/mrdox/Metadata/Type.hpp @@ -45,6 +45,7 @@ enum class TypeKind MemberPointer, Array, Function, + // KRYSTIAN FIXME: get rid of PackTypeInfo Pack }; diff --git a/share/mrdox/addons/generator/asciidoc/partials/declarator-before.adoc.hbs b/share/mrdox/addons/generator/asciidoc/partials/declarator-before.adoc.hbs index 5b42e5581..92d829566 100644 --- a/share/mrdox/addons/generator/asciidoc/partials/declarator-before.adoc.hbs +++ b/share/mrdox/addons/generator/asciidoc/partials/declarator-before.adoc.hbs @@ -1,16 +1,11 @@ {{#if pointee-type~}} {{~>declarator-before pointee-type~}} {{#if (or (eq pointee-type.kind "array") (eq pointee-type.kind "function"))}}({{/if~}} -{{else if element-type~}} - {{~>declarator-before element-type~}} -{{else if return-type~}} - {{~>declarator-before return-type~}} -{{else if pattern-type~}} - {{~>declarator-before pattern-type}}... {{~/if~}} -{{#if parent-type~}} - {{#if parent-type}}{{>declarator parent-type}}::{{/if~}} -{{/if~}} +{{#if element-type~}}{{~>declarator-before element-type~}}{{/if~}} +{{#if return-type~}}{{~>declarator-before return-type~}}{{/if~}} +{{#if pattern-type~}}{{~>declarator-before pattern-type}}...{{/if~}} +{{#if parent-type~}}{{>declarator parent-type}}::{{/if~}} {{#if (eq kind "lvalue-reference")}}&{{/if~}} {{#if (eq kind "rvalue-reference")}}&&{{/if~}} {{#if (eq kind "pointer")}}*{{/if~}} diff --git a/share/mrdox/addons/generator/asciidoc/partials/declarator.adoc.hbs b/share/mrdox/addons/generator/asciidoc/partials/declarator.adoc.hbs index 39d77e805..7d75e1ca1 100644 --- a/share/mrdox/addons/generator/asciidoc/partials/declarator.adoc.hbs +++ b/share/mrdox/addons/generator/asciidoc/partials/declarator.adoc.hbs @@ -1,5 +1,5 @@ {{>declarator-before .~}} {{~#if decl-name}} {{decl-name~}} -{{~#if decl-name-targs}}{{>template-args args=decl-name-targs}}{{/if~}} +{{~#if decl-name-targs~}}{{>template-args args=decl-name-targs}}{{~/if~}} {{~/if~}} {{~>declarator-after}} \ No newline at end of file diff --git a/share/mrdox/addons/generator/asciidoc/partials/function-sig.adoc.hbs b/share/mrdox/addons/generator/asciidoc/partials/function-sig.adoc.hbs index 29ed194e4..eb9509cfa 100644 --- a/share/mrdox/addons/generator/asciidoc/partials/function-sig.adoc.hbs +++ b/share/mrdox/addons/generator/asciidoc/partials/function-sig.adoc.hbs @@ -12,9 +12,11 @@ {{#if (eq class "normal")~}} {{>declarator-before return}} -{{name~}} {{#if (eq template.kind "explicit")~}} + {{#if template.primary.id~}}{{>xref template.primary}}[{{name}}]{{else}}{{name}}{{/if~}} {{~>template-args args=template.args~}} +{{else~}} + {{name~}} {{/if~}} {{else if (neq class "conversion")~}} {{name~}} diff --git a/share/mrdox/addons/generator/asciidoc/partials/record.adoc.hbs b/share/mrdox/addons/generator/asciidoc/partials/record.adoc.hbs index 96d1fc01a..80671e767 100644 --- a/share/mrdox/addons/generator/asciidoc/partials/record.adoc.hbs +++ b/share/mrdox/addons/generator/asciidoc/partials/record.adoc.hbs @@ -9,9 +9,11 @@ ---- {{#if template}}{{>template-head template}} {{/if~}} -{{symbol.tag}} {{symbol.name~}} -{{#if (or (eq symbol.template.kind "explicit") (eq symbol.template.kind "partial"))~}} - {{~>template-args args=symbol.template.args}} +{{#if (or (eq template.kind "explicit") (eq template.kind "partial"))~}} + {{tag}} {{#if template.primary.id~}}{{>xref template.primary}}[{{name}}]{{else}}{{name}}{{/if~}} + {{~>template-args args=template.args~}} +{{else~}} + {{tag}} {{name~}} {{/if~}} {{#unless symbol.bases}} ; diff --git a/share/mrdox/addons/generator/asciidoc/partials/variable.adoc.hbs b/share/mrdox/addons/generator/asciidoc/partials/variable.adoc.hbs index ab7c44a1a..2a7cff604 100644 --- a/share/mrdox/addons/generator/asciidoc/partials/variable.adoc.hbs +++ b/share/mrdox/addons/generator/asciidoc/partials/variable.adoc.hbs @@ -15,9 +15,11 @@ {{/if~}} {{#if symbol.isThreadLocal}}thread_local {{/if~}} -{{>declarator-before symbol.type}} {{symbol.name~}} -{{#if (or (eq symbol.template.kind "explicit") (eq symbol.template.kind "partial"))~}} - {{~>template-args args=symbol.template.args}} +{{#if (or (eq template.kind "explicit") (eq template.kind "partial"))~}} + {{>declarator-before symbol.type}} {{#if template.primary.id~}}{{>xref template.primary}}[{{name}}]{{else}}{{name}}{{/if~}} + {{~>template-args args=template.args~}} +{{else~}} + {{>declarator-before symbol.type}} {{name~}} {{/if~}} {{>declarator-after symbol.type~}} ; diff --git a/src/lib/AST/ASTVisitor.cpp b/src/lib/AST/ASTVisitor.cpp index 268f2f16c..ba78727d7 100644 --- a/src/lib/AST/ASTVisitor.cpp +++ b/src/lib/AST/ASTVisitor.cpp @@ -27,9 +27,13 @@ #include #include #include +#include +#include #include +#include #include #include +#include #include #include #include @@ -142,11 +146,54 @@ class ASTVisitor llvm::SmallString<128> usr_; - // KRYSTIAN FIXME: this is terrible - bool forceExtract_ = false; - SymbolFilter symbolFilter_; + enum class ExtractMode + { + // extraction of declarations which pass all filters + Normal, + // extraction of declarations as direct dependencies + DirectDependency, + // extraction of declarations as indirect dependencies + IndirectDependency, + }; + + ExtractMode mode = ExtractMode::Normal; + + struct [[nodiscard]] ExtractionScope + { + ASTVisitor& visitor_; + ExtractMode previous_; + + public: + ExtractionScope( + ASTVisitor& visitor, + ExtractMode mode) noexcept + : visitor_(visitor) + , previous_(visitor.mode) + { + visitor_.mode = mode; + } + + ~ExtractionScope() + { + visitor_.mode = previous_; + } + }; + + ExtractionScope + enterMode( + ExtractMode new_mode) noexcept + { + return ExtractionScope(*this, new_mode); + } + + ExtractMode + currentMode() const noexcept + { + return mode; + } + ASTVisitor( const ConfigImpl& config, Diagnostics& diags, @@ -201,25 +248,59 @@ class ASTVisitor created = true; } MRDOX_ASSERT(info->Kind == InfoTy::kind_id); - info->Implicit &= forceExtract_; + info->Implicit &= currentMode() != ExtractMode::Normal; return {static_cast(*info), created}; } - Info& + Info* getOrBuildInfo(Decl* D) { SymbolID id = extractSymbolID(D); - if(Info* info = getInfo(id)) - return *info; + Info* info = getInfo(id); + + if(config_->extractOptions.referencedDeclarations == + Config::ExtractOptions::Policy::Never) + return info; + + if(config_->extractOptions.referencedDeclarations == + Config::ExtractOptions::Policy::Dependency) + { + // if(currentMode() == ExtractMode::Normal) + if(currentMode() != ExtractMode::DirectDependency) + return info; + } + + // KRYSTIAN FIXME: this terrible hack ensures that + // the underlying type of a typedef is extracted + // in cases where the TypedefInfo was extracted earlier + // without extracting the underlying type. fixing this will + // require deferred dependency extraction, which requires us + // to store Info references as Info* instead of SymbolID. + auto* TND = dyn_cast(D); + if(auto* TATD = dyn_cast(D)) + TND = TATD->getTemplatedDecl(); + if(TND) + { + auto TI = buildTypeInfo( + TND->getUnderlyingType(), + ExtractMode::DirectDependency); + if(info && info->isTypedef()) + { + static_cast(info)->Type = + std::move(TI); + } + } + + if(info) + return info; + + // make sure we restore the current mode upon return + ExtractionScope mode = + enterMode(currentMode()); - // KRYSTIAN FIXME: this is terrible - bool force = forceExtract_; - forceExtract_ = true; traverseDecl(D); - forceExtract_ = force; - MRDOX_ASSERT(getInfo(id)); - return *getInfo(id); + return getInfo(id); } //------------------------------------------------ @@ -342,19 +423,6 @@ class ASTVisitor return T.getAsString(context_.getPrintingPolicy()); } - template - std::unique_ptr - makeTypeInfo( - const IdentifierInfo* II, - unsigned quals) - { - auto I = std::make_unique(); - I->CVQualifiers = convertToQualifierKind(quals); - if(II) - I->Name = II->getName(); - return I; - } - NamedDecl* lookupTypedefInPrimary( TypedefNameDecl* TD) @@ -380,22 +448,31 @@ class ASTVisitor template std::unique_ptr makeTypeInfo( - NamedDecl* N, + const IdentifierInfo* II, unsigned quals) { auto I = std::make_unique(); I->CVQualifiers = convertToQualifierKind(quals); - if(! N) - return I; - if(const auto* II = N->getIdentifier()) + if(II) I->Name = II->getName(); + return I; + } + + template + std::unique_ptr + makeTypeInfo( + NamedDecl* N, + unsigned quals) + { + auto I = makeTypeInfo( + N->getIdentifier(), quals); + + N = cast(getInstantiatedFrom(N)); // do not generate references to implicit declarations, // template template parameters, or builtin templates - if(! N->isImplicit() && - ! isa(N) && + if(! isa(N) && ! isa(N)) { - N = cast(getInstantiatedFrom(N)); if(auto* TD = dyn_cast(N)) { if(auto* PTD = lookupTypedefInPrimary(TD)) @@ -408,365 +485,434 @@ class ASTVisitor auto* TD = ATD->getTemplatedDecl(); if(auto* R = dyn_cast(TD->getDeclContext())) { - // KRYSTIAN FIXME: this appears to not work - if(auto* PATD = lookupTypedefInPrimary(TD)) - N = PATD; + // KRYSTIAN FIXME: this appears to not work + if(auto* PATD = lookupTypedefInPrimary(TD)) + N = PATD; } } - extractSymbolID(N, I->id); - getOrBuildInfo(N); + // extractSymbolID(N, I->id); + // getOrBuildInfo(N); + + if(! N->isImplicit()) + { + if(Info* NI = getOrBuildInfo(N)) + I->id = NI->id; + } } return I; } - std::unique_ptr - buildTypeInfo( - const NestedNameSpecifier* N) + template< + std::same_as TypeInfoTy, + typename DeclOrName, + typename TArgsRange> + std::unique_ptr + makeTypeInfo( + DeclOrName&& N, + unsigned quals, + TArgsRange&& targs) + { + auto I = makeTypeInfo( + std::forward(N), quals); + buildTemplateArgs(I->TemplateArgs, + std::forward(targs)); + return I; + } + + void + buildParentTypeInfo( + std::unique_ptr& Parent, + const NestedNameSpecifier* NNS, + ExtractMode extract_mode) { - if(N) + if(! NNS) + return; + // extraction for parents of a terminal TypeInfo + // node use the same mode as that node + if(const auto* T = NNS->getAsType()) { - if(const auto* T = N->getAsType()) - return buildTypeInfo(QualType(T, 0)); - if(const auto* I = N->getAsIdentifier()) - { - auto R = std::make_unique(); - R->ParentType = buildTypeInfo(N->getPrefix()); - R->Name = I->getName(); - return R; - } + Parent = buildTypeInfo( + QualType(T, 0), + extract_mode); + } + else if(const auto* II = NNS->getAsIdentifier()) + { + auto R = std::make_unique(); + buildParentTypeInfo( + R->ParentType, + NNS->getPrefix(), + extract_mode); + R->Name = II->getName(); + Parent = std::move(R); } - return nullptr; } - // KRYSTIAN FIXME: something is broken here w.r.t - // qualified names, but i'm not sure what exactly std::unique_ptr buildTypeInfo( QualType qt, - unsigned quals = 0) - { - qt.addFastQualifiers(quals); - // should never be called for a QualType - // that has no Type pointer - MRDOX_ASSERT(! qt.isNull()); - const Type* type = qt.getTypePtr(); - quals = qt.getLocalFastQualifiers(); - switch(qt->getTypeClass()) - { - // parenthesized types - case Type::Paren: - { - auto* T = cast(type); - return buildTypeInfo( - T->getInnerType(), quals); - } - case Type::MacroQualified: - { - auto* T = cast(type); - return buildTypeInfo( - T->getUnderlyingType(), quals); - } - // type with __atribute__ - case Type::Attributed: - { - auto* T = cast(type); - return buildTypeInfo( - T->getModifiedType(), quals); - } - // adjusted and decayed types - case Type::Decayed: - case Type::Adjusted: - { - auto* T = cast(type); - return buildTypeInfo( - T->getOriginalType(), quals); - } - // using declarations - case Type::Using: - { - auto* T = cast(type); - // look through the using declaration and - // use the the type from the referenced declaration - return buildTypeInfo( - T->getUnderlyingType(), quals); - } - // pointers - case Type::Pointer: - { - auto* T = cast(type); - auto I = std::make_unique(); - I->PointeeType = buildTypeInfo( - T->getPointeeType()); - I->CVQualifiers = convertToQualifierKind(quals); - return I; - } - // references - case Type::LValueReference: - { - auto* T = cast(type); - auto I = std::make_unique(); - I->PointeeType = buildTypeInfo( - T->getPointeeType()); - return I; - } - case Type::RValueReference: - { - auto* T = cast(type); - auto I = std::make_unique(); - I->PointeeType = buildTypeInfo( - T->getPointeeType()); - return I; - } - // pointer to members - case Type::MemberPointer: - { - auto* T = cast(type); - auto I = std::make_unique(); - I->PointeeType = buildTypeInfo( - T->getPointeeType()); - I->ParentType = buildTypeInfo( - QualType(T->getClass(), 0)); - I->CVQualifiers = convertToQualifierKind(quals); - return I; - } - // pack expansion - case Type::PackExpansion: - { - auto* T = cast(type); - auto I = std::make_unique(); - I->PatternType = buildTypeInfo(T->getPattern()); - return I; - } - // KRYSTIAN NOTE: we don't handle FunctionNoProto here, - // and it's unclear if we should. we should not encounter - // such types in c++ (but it might be possible?) - // functions - case Type::FunctionProto: - { - auto* T = cast(type); - auto I = std::make_unique(); - I->ReturnType = buildTypeInfo( - T->getReturnType()); - for(QualType PT : T->getParamTypes()) - I->ParamTypes.emplace_back( - buildTypeInfo(PT)); - I->RefQualifier = convertToReferenceKind( - T->getRefQualifier()); - I->CVQualifiers = convertToQualifierKind( - T->getMethodQuals().getFastQualifiers()); - I->ExceptionSpec = convertToNoexceptKind( - T->getExceptionSpecType()); - return I; - } - // KRYSTIAN FIXME: do we handle variables arrays? - // they can only be created within function scope - // arrays - case Type::IncompleteArray: - { - auto* T = cast(type); - auto I = std::make_unique(); - I->ElementType = buildTypeInfo( - T->getElementType()); - return I; - } - case Type::ConstantArray: - { - auto* T = cast(type); - auto I = std::make_unique(); - I->ElementType = buildTypeInfo( - T->getElementType()); - // KRYSTIAN FIXME: this is broken; cannonical - // constant array types never have a size expression - buildExprInfo(I->Bounds, - T->getSizeExpr(), T->getSize()); - return I; - } - case Type::DependentSizedArray: - { - auto* T = cast(type); - auto I = std::make_unique(); - I->ElementType = buildTypeInfo( - T->getElementType()); - buildExprInfo(I->Bounds, - T->getSizeExpr()); - return I; - } - case Type::Auto: - { - auto* T = cast(type); - QualType deduced = T->getDeducedType(); - // KRYSTIAN NOTE: we don't use isDeduced because it will - // return true if the type is dependent - // if the type has been deduced, use the deduced type - if(! deduced.isNull()) - return buildTypeInfo(deduced); - auto I = std::make_unique(); - I->Name = getTypeAsString( - qt.withoutLocalFastQualifiers()); - I->CVQualifiers = convertToQualifierKind(quals); - return I; - } - case Type::DeducedTemplateSpecialization: - { - auto* T = cast(type); - if(T->isDeduced()) - return buildTypeInfo(T->getDeducedType()); - auto I = makeTypeInfo( - T->getTemplateName().getAsTemplateDecl(), quals); - return I; - } - // elaborated type specifier or - // type with nested name specifier - case Type::Elaborated: + ExtractMode extract_mode = ExtractMode::IndirectDependency) + { + // extract_mode is only used during the extraction + // the terminal type & its parents; the extraction of + // function parameters, template arguments, and the parent class + // of member pointers is done in ExtractMode::IndirectDependency + ExtractionScope scope = enterMode(extract_mode); + + std::unique_ptr result; + std::unique_ptr* inner = &result; + + // nested name specifier used for the terminal type node + NestedNameSpecifier* NNS = nullptr; + + while(true) { - auto* T = cast(type); - auto I = buildTypeInfo( - T->getNamedType(), quals); - // ignore elaborated-type-specifiers - if(auto kw = T->getKeyword(); - kw != ElaboratedTypeKeyword::ETK_Typename && - kw != ElaboratedTypeKeyword::ETK_None) - return I; - switch(I->Kind) + // should never be called for a null QualType + MRDOX_ASSERT(! qt.isNull()); + const Type* type = qt.getTypePtr(); + unsigned quals = qt.getLocalFastQualifiers(); + + switch(qt->getTypeClass()) + { + // parenthesized types + case Type::Paren: + { + auto* T = cast(type); + qt = T->getInnerType().withFastQualifiers(quals); + continue; + } + case Type::MacroQualified: + { + auto* T = cast(type); + qt = T->getUnderlyingType().withFastQualifiers(quals); + continue; + } + // type with __atribute__ + case Type::Attributed: + { + auto* T = cast(type); + qt = T->getModifiedType().withFastQualifiers(quals); + continue; + } + // adjusted and decayed types + case Type::Decayed: + case Type::Adjusted: + { + auto* T = cast(type); + qt = T->getOriginalType().withFastQualifiers(quals); + continue; + } + // using declarations + case Type::Using: + { + auto* T = cast(type); + // look through the using declaration and + // use the the type from the referenced declaration + qt = T->getUnderlyingType().withFastQualifiers(quals); + continue; + } + case Type::SubstTemplateTypeParm: { - case TypeKind::Tag: - static_cast(*I).ParentType = - buildTypeInfo(T->getQualifier()); + auto* T = cast(type); + qt = T->getReplacementType().withFastQualifiers(quals); + continue; + } + // pointers + case Type::Pointer: + { + auto* T = cast(type); + auto I = std::make_unique(); + I->CVQualifiers = convertToQualifierKind(quals); + *std::exchange(inner, &I->PointeeType) = std::move(I); + qt = T->getPointeeType(); + continue; + } + // references + case Type::LValueReference: + { + auto* T = cast(type); + auto I = std::make_unique(); + *std::exchange(inner, &I->PointeeType) = std::move(I); + qt = T->getPointeeType(); + continue; + } + case Type::RValueReference: + { + auto* T = cast(type); + auto I = std::make_unique(); + *std::exchange(inner, &I->PointeeType) = std::move(I); + qt = T->getPointeeType(); + continue; + } + // pointer to members + case Type::MemberPointer: + { + auto* T = cast(type); + auto I = std::make_unique(); + I->CVQualifiers = convertToQualifierKind(quals); + // do not set NNS because the parent type is *not* + // a nested-name-specifier which qualifies the pointee type + I->ParentType = buildTypeInfo(QualType(T->getClass(), 0)); + *std::exchange(inner, &I->PointeeType) = std::move(I); + qt = T->getPointeeType(); + continue; + } + // pack expansion + case Type::PackExpansion: + { + auto* T = cast(type); + auto I = std::make_unique(); + *std::exchange(inner, &I->PatternType) = std::move(I); + qt = T->getPattern(); + continue; + } + // KRYSTIAN NOTE: we don't handle FunctionNoProto here, + // and it's unclear if we should. we should not encounter + // such types in c++ (but it might be possible?) + // functions + case Type::FunctionProto: + { + auto* T = cast(type); + auto I = std::make_unique(); + for(QualType PT : T->getParamTypes()) + I->ParamTypes.emplace_back(buildTypeInfo(PT)); + I->RefQualifier = convertToReferenceKind( + T->getRefQualifier()); + I->CVQualifiers = convertToQualifierKind( + T->getMethodQuals().getFastQualifiers()); + I->ExceptionSpec = convertToNoexceptKind( + T->getExceptionSpecType()); + *std::exchange(inner, &I->ReturnType) = std::move(I); + qt = T->getReturnType(); + continue; + } + // KRYSTIAN FIXME: do we handle variables arrays? + // they can only be created within function scope + // arrays + case Type::IncompleteArray: + { + auto* T = cast(type); + auto I = std::make_unique(); + *std::exchange(inner, &I->ElementType) = std::move(I); + qt = T->getElementType(); + continue; + } + case Type::ConstantArray: + { + auto* T = cast(type); + auto I = std::make_unique(); + // KRYSTIAN FIXME: this is broken; cannonical + // constant array types never have a size expression + buildExprInfo(I->Bounds, T->getSizeExpr(), T->getSize()); + *std::exchange(inner, &I->ElementType) = std::move(I); + qt = T->getElementType(); + continue; + } + case Type::DependentSizedArray: + { + auto* T = cast(type); + auto I = std::make_unique(); + buildExprInfo(I->Bounds, T->getSizeExpr()); + *std::exchange(inner, &I->ElementType) = std::move(I); + qt = T->getElementType(); + continue; + } + + // ------------------------------------------------ + // terminal TypeInfo nodes + + case Type::Auto: + { + auto* T = cast(type); + QualType deduced = T->getDeducedType(); + // KRYSTIAN NOTE: we don't use isDeduced because it will + // return true if the type is dependent + // if the type has been deduced, use the deduced type + if(! deduced.isNull()) + { + qt = deduced; + continue; + } + // otherwise, use the placeholder type specifier + auto I = std::make_unique(); + I->Name = getTypeAsString( + qt.withoutLocalFastQualifiers()); + I->CVQualifiers = convertToQualifierKind(quals); + *inner = std::move(I); break; - case TypeKind::Specialization: - static_cast(*I).ParentType = - buildTypeInfo(T->getQualifier()); + } + case Type::DeducedTemplateSpecialization: + { + auto* T = cast(type); + QualType deduced = T->getDeducedType(); + // if(! T->isDeduced()) + if(! deduced.isNull()) + { + qt = deduced; + continue; + } + *inner = makeTypeInfo( + T->getTemplateName().getAsTemplateDecl(), quals); break; - case TypeKind::Builtin: - // KRYSTIAN FIXME: is this correct? + } + // elaborated type specifier or + // type with nested name specifier + case Type::Elaborated: + { + auto* T = cast(type); + // there should only ever be one + // nested-name-specifier for the terminal type + MRDOX_ASSERT(! NNS || ! T->getQualifier()); + NNS = T->getQualifier(); + qt = T->getNamedType().withFastQualifiers(quals); + continue; + } + // qualified dependent name with template keyword + case Type::DependentTemplateSpecialization: + { + auto* T = cast(type); + auto I = makeTypeInfo( + T->getIdentifier(), quals, T->template_arguments()); + // there should only ever be one + // nested-name-specifier for the terminal type + MRDOX_ASSERT(! NNS || ! T->getQualifier()); + NNS = T->getQualifier(); + *inner = std::move(I); break; - default: - MRDOX_UNREACHABLE(); - }; - return I; - } - // qualified dependent name with template keyword - case Type::DependentTemplateSpecialization: - { - auto* T = cast(type); - auto I = makeTypeInfo( - T->getIdentifier(), quals); - I->ParentType = buildTypeInfo( - T->getQualifier()); - buildTemplateArgs(I->TemplateArgs, T->template_arguments()); - return I; - } - // specialization of a class/alias template or - // template template parameter - case Type::TemplateSpecialization: - { - auto* T = cast(type); - auto name = T->getTemplateName(); - MRDOX_ASSERT(! name.isNull()); - NamedDecl* ND = name.getAsTemplateDecl(); - // if this is a specialization of a alias template, - // the canonical type will be the named type. in such cases, - // we will use the template name. otherwise, we use the - // canonical type whenever possible. - if(! T->isTypeAlias()) + } + // dependent typename-specifier + case Type::DependentName: + { + auto* T = cast(type); + auto I = makeTypeInfo( + T->getIdentifier(), quals); + // there should only ever be one + // nested-name-specifier for the terminal type + MRDOX_ASSERT(! NNS || ! T->getQualifier()); + NNS = T->getQualifier(); + *inner = std::move(I); + break; + } + // specialization of a class/alias template or + // template template parameter + case Type::TemplateSpecialization: { - auto* CT = qt.getCanonicalType().getTypePtrOrNull(); - if(auto* ICT = dyn_cast_or_null< - InjectedClassNameType>(CT)) + auto* T = cast(type); + auto name = T->getTemplateName(); + MRDOX_ASSERT(! name.isNull()); + NamedDecl* ND = name.getAsTemplateDecl(); + // if this is a specialization of a alias template, + // the canonical type will be the named type. in such cases, + // we will use the template name. otherwise, we use the + // canonical type whenever possible. + if(! T->isTypeAlias()) { - ND = ICT->getDecl(); + auto* CT = qt.getCanonicalType().getTypePtrOrNull(); + if(auto* ICT = dyn_cast_or_null(CT)) + ND = ICT->getDecl(); + else if(auto* RT = dyn_cast_or_null(CT)) + ND = RT->getDecl(); } - if(auto* RT = dyn_cast_or_null< - RecordType>(CT)) + *inner = makeTypeInfo( + ND, quals, T->template_arguments()); + break; + } + case Type::Record: + { + auto* T = cast(type); + RecordDecl* RD = T->getDecl(); + // if this is an instantiation of a class template, + // create a SpecializationTypeInfo & extract the template arguments + if(auto* CTSD = dyn_cast(RD)) + *inner = makeTypeInfo( + CTSD, quals, CTSD->getTemplateArgs().asArray()); + else + *inner = makeTypeInfo(RD, quals); + break; + } + // enum types, as well as injected class names + // within a class template (or specializations thereof) + case Type::InjectedClassName: + case Type::Enum: + { + *inner = makeTypeInfo( + type->getAsTagDecl(), quals); + break; + } + // typedef/alias type + case Type::Typedef: + { + auto* T = cast(type); + *inner = makeTypeInfo( + T->getDecl(), quals); + break; + } + case Type::SubstTemplateTypeParmPack: + { + auto* T = cast(type); + auto I = std::make_unique(); + I->PatternType = makeTypeInfo( + T->getIdentifier(), quals); + *inner = std::move(I); + break; + } + case Type::TemplateTypeParm: + { + auto* T = cast(type); + auto I = std::make_unique(); + I->CVQualifiers = convertToQualifierKind(quals); + if(auto* D = T->getDecl()) { - ND = RT->getDecl(); + // special case for implicit template parameters + // resulting from abbreviated function templates + if(D->isImplicit()) + I->Name = "auto"; + else if(auto* II = D->getIdentifier()) + I->Name = II->getName(); } + *inner = std::move(I); + break; } - auto I = makeTypeInfo(ND, quals); - buildTemplateArgs(I->TemplateArgs, T->template_arguments()); - return I; - } - // dependent typename-specifier - case Type::DependentName: - { - auto* T = cast(type); - auto I = makeTypeInfo( - T->getIdentifier(), quals); - I->ParentType = buildTypeInfo( - T->getQualifier()); - return I; - } - case Type::Record: - { - auto* T = cast(type); - RecordDecl* RD = T->getDecl(); - // if this is an instantiation of a class template, - // create a SpecializationTypeInfo & extract the template arguments - if(auto* CTSD = dyn_cast(RD)) + // builtin/unhandled type + default: { - auto I = makeTypeInfo(CTSD, quals); - buildTemplateArgs(I->TemplateArgs, - CTSD->getTemplateArgs().asArray()); - return I; + auto I = std::make_unique(); + I->CVQualifiers = convertToQualifierKind(quals); + I->Name = getTypeAsString( + qt.withoutLocalFastQualifiers()); + *inner = std::move(I); + break; } - return makeTypeInfo(RD, quals); - } - // enum types, as well as injected class names - // within a class template (or specializations thereof) - case Type::InjectedClassName: - case Type::Enum: - { - return makeTypeInfo( - type->getAsTagDecl(), quals); - } - // typedef/alias type - case Type::Typedef: - { - auto* T = cast(type); - return makeTypeInfo( - T->getDecl(), quals); - } - case Type::TemplateTypeParm: - { - auto* T = cast(type); - auto I = std::make_unique(); - if(auto* D = T->getDecl()) - { - // special case for implicit template parameters - // resulting from abbreviated function templates - if(D->isImplicit()) - I->Name = "auto"; - else if(auto* II = D->getIdentifier()) - I->Name = II->getName(); } - I->CVQualifiers = convertToQualifierKind(quals); - return I; - } - case Type::SubstTemplateTypeParm: - { - auto* T = cast(type); - return buildTypeInfo( - T->getReplacementType(), quals); - } - case Type::SubstTemplateTypeParmPack: - { - auto* T = cast(type); - auto I = std::make_unique(); - I->PatternType = makeTypeInfo( - T->getIdentifier(), quals); - return I; - } - // builtin/unhandled type - default: - { - auto I = std::make_unique(); - I->Name = getTypeAsString( - qt.withoutLocalFastQualifiers()); - I->CVQualifiers = convertToQualifierKind(quals); - return I; - } + // the terminal type must be BuiltinTypeInfo, + // TagTypeInfo, or SpecializationTypeInfo + MRDOX_ASSERT( + (*inner)->isBuiltin() || + (*inner)->isTag() || + (*inner)->isSpecialization()); + + // if there is no nested-name-specifier for + // the terminal type, then we are done + if(! NNS) + return result; + + // KRYSTIAN FIXME: nested-name-specifier on builtin type? + visit(**inner, [&](T& t) + { + if constexpr(requires { t.ParentType; }) + { + // build the TypeInfo for the nested-name-specifier + // using the same mode used for this TypeInfo + buildParentTypeInfo( + t.ParentType, + NNS, + extract_mode); + } + }); + + return result; } + } /** Get the user-written `Decl` for a `Decl` @@ -821,34 +967,29 @@ class ASTVisitor { FunctionDecl* FD = DT; - for(auto* R : FD->redecls()) + const FunctionDecl* DD = nullptr; + if(FD->isDefined(DD, false)) + FD = const_cast(DD); + + if(MemberSpecializationInfo* MSI = + FD->getMemberSpecializationInfo()) { - if(MemberSpecializationInfo* MSI = - R->getMemberSpecializationInfo()) - { - if(MSI->isExplicitSpecialization()) - FD = R; - else - FD = cast( - MSI->getInstantiatedFrom()); - break; - } - else if(R->getTemplateSpecializationKind() == - TSK_ImplicitInstantiation) + if(! MSI->isExplicitSpecialization()) + FD = cast( + MSI->getInstantiatedFrom()); + } + else if(FD->getTemplateSpecializationKind() != + TSK_ExplicitSpecialization) + { + FD = FD->getFirstDecl(); + if(auto* FTD = FD->getPrimaryTemplate()) { - if(auto* FTD = FD->getPrimaryTemplate()) - { - FTD = cast( - getInstantiatedFrom(FTD)); - FD = FTD->getTemplatedDecl(); - } - break; + FTD = cast( + getInstantiatedFrom(FTD)); + FD = FTD->getTemplatedDecl(); } } - if(FunctionDecl* DD = FD->getDefinition()) - FD = DD; - return FD; } @@ -1054,8 +1195,7 @@ class ASTVisitor else if constexpr(kind == Decl::NonTypeTemplateParm) { auto R = std::make_unique(); - R->Type = buildTypeInfo( - P->getType()); + R->Type = buildTypeInfo(P->getType()); if(P->hasDefaultArgument()) { R->Default = buildTemplateArg( @@ -1160,8 +1300,8 @@ class ASTVisitor ! isa(TD)) { Decl* D = getInstantiatedFrom(TD); - extractSymbolID(D, R->Template); - getOrBuildInfo(D); + if(Info* I = getOrBuildInfo(D)) + R->Template = I->id; } } else @@ -1277,6 +1417,25 @@ class ASTVisitor buildTemplateArgs(I.Args, args->asArray()); } + void + parseTemplateArgs( + TemplateInfo& I, + const DependentFunctionTemplateSpecializationInfo* spec) + { + // set the ID of the primary template if there is one candidate + if(spec->getNumTemplates() == 1) + { + if(Decl* primary = getInstantiatedFrom(spec->getTemplate(0))) + extractSymbolID(primary, I.Primary.emplace()); + } + + buildTemplateArgs(I.Args, std::views::transform( + spec->arguments(), [](auto& x) -> auto& + { + return x.getArgument(); + })); + } + void parseTemplateArgs( TemplateInfo& I, @@ -1300,17 +1459,14 @@ class ASTVisitor void parseTemplateParams( TemplateInfo& I, - const Decl* D) + const TemplateParameterList* TPL) { - if(const TemplateParameterList* ParamList = - D->getDescribedTemplateParams()) + for(const NamedDecl* ND : *TPL) { - for(const NamedDecl* ND : *ParamList) - { - I.Params.emplace_back( - buildTemplateParam(ND)); - } + I.Params.emplace_back( + buildTemplateParam(ND)); } + } void @@ -1364,7 +1520,8 @@ class ASTVisitor bool checkSymbolFilter(const NamedDecl* ND) { - if(forceExtract_ || symbolFilter_.detached) + if(currentMode() != ExtractMode::Normal || + symbolFilter_.detached) return true; std::string name = extractName(ND); @@ -1404,9 +1561,10 @@ class ASTVisitor return true; } + // #define CHECK_IN_FILE_FILTER // This also sets IsFileInRootDir bool - shouldExtract( + inExtractedFile( const Decl* D) { namespace path = llvm::sys::path; @@ -1453,27 +1611,43 @@ class ASTVisitor return false; } - // skip system header - if(! forceExtract_ && source_.isInSystemHeader(D->getLocation())) - return false; - const PresumedLoc loc = source_.getPresumedLoc(D->getBeginLoc()); - auto [it, inserted] = fileFilter_.emplace( - loc.getIncludeLoc().getRawEncoding(), - FileFilter()); + MRDOX_ASSERT(loc.isValid()); - FileFilter& ff = it->second; file_ = files::makePosixStyle(loc.getFilename()); - // file has not been previously visited + #ifdef CHECK_IN_FILE_FILTER + // skip system header + if(currentMode() == ExtractMode::Normal && + source_.isInSystemHeader(D->getLocation())) + #else + + // skip system header + if(source_.isInSystemHeader(D->getLocation())) + #endif + return false; + + + auto [it, inserted] = fileFilter_.emplace( + loc.getIncludeLoc().getRawEncoding(), + FileFilter()); + + FileFilter& ff = it->second; + + // file has not been previously visited if(inserted) ff.include = config_.shouldExtractFromFile(file_, ff.prefix); // don't extract if the declaration is in a file // that should not be visited - if(! forceExtract_ && ! ff.include) + #ifdef CHECK_IN_FILE_FILTER + if(currentMode() == ExtractMode::Normal && + ! ff.include) + #else + if(! ff.include) + #endif return false; // VFALCO we could assert that the prefix // matches and just lop off the @@ -1486,6 +1660,27 @@ class ASTVisitor return true; } + bool + shouldExtract( + const Decl* D) + { + #ifdef CHECK_IN_FILE_FILTER + return inExtractedFile(D); + #elif 0 + return inExtractedFile(D) || + currentMode() != ExtractMode::Normal; + #elif 1 + bool extract = inExtractedFile(D); + // if we're extracting a declaration as a dependency, + // override the current extraction mode if + // it would be extracted anyway + if(extract) + mode = ExtractMode::Normal; + + return extract || currentMode() != ExtractMode::Normal; + #endif + } + std::string extractName( const NamedDecl* D) @@ -1743,7 +1938,11 @@ class ASTVisitor for(const CXXBaseSpecifier& B : D->bases()) { I.Bases.emplace_back( - buildTypeInfo(B.getType()), + // the extraction of the base type is + // performed in direct dependency mode + buildTypeInfo( + B.getType(), + ExtractMode::DirectDependency), convertToAccessKind( B.getAccessSpecifier()), B.isVirtual()); @@ -2004,6 +2203,9 @@ class ASTVisitor I.Name = extractName(D); + I.Class = convertToFunctionClass( + D->getDeclKind()); + for(const ParmVarDecl* P : D->parameters()) { I.Params.emplace_back( @@ -2012,15 +2214,17 @@ class ASTVisitor getSourceCode(P->getDefaultArgRange())); } - I.ReturnType = buildTypeInfo( - D->getReturnType()); - - if(auto* ftsi = D->getTemplateSpecializationInfo()) + QualType RT = D->getReturnType(); + ExtractMode next_mode = ExtractMode::IndirectDependency; + if(auto* AT = RT->getContainedAutoType(); + AT && AT->hasUnnamedOrLocalType()) { - if(! I.Template) - I.Template = std::make_unique(); - parseTemplateArgs(*I.Template, ftsi); + next_mode = ExtractMode::DirectDependency; } + // extract the return type in direct dependency mode + // if it contains a placeholder type which is + // deduceded as a local class type + I.ReturnType = buildTypeInfo(RT, next_mode); getParentNamespaces(I, D); } @@ -2053,7 +2257,7 @@ class ASTVisitor if(Info* parent = getInfo(parent_id)) { MRDOX_ASSERT(parent->isRecord()); - parent->Implicit &= forceExtract_; + parent->Implicit &= currentMode() != ExtractMode::Normal; static_cast(parent)->Friends.emplace_back(I.id); } @@ -2088,32 +2292,33 @@ class ASTVisitor //------------------------------------------------ + // namespaces bool traverse(NamespaceDecl*); - bool traverse(CXXRecordDecl*, AccessSpecifier, std::unique_ptr&&); - bool traverse(EnumDecl*, AccessSpecifier); - bool traverse(TypedefDecl*, AccessSpecifier); - bool traverse(TypeAliasDecl*, AccessSpecifier, std::unique_ptr&&); - bool traverse(VarDecl*, AccessSpecifier, std::unique_ptr&&); - bool traverse(FieldDecl*, AccessSpecifier); - bool traverse(FunctionDecl*, AccessSpecifier, std::unique_ptr&&); - bool traverse(CXXMethodDecl*, AccessSpecifier, std::unique_ptr&&); - bool traverse(CXXConstructorDecl*, AccessSpecifier, std::unique_ptr&&); - bool traverse(CXXConversionDecl*, AccessSpecifier, std::unique_ptr&&); - bool traverse(CXXDeductionGuideDecl*, AccessSpecifier, std::unique_ptr&&); - // destructors cannot be templates - bool traverse(CXXDestructorDecl*, AccessSpecifier); + + // enums + bool traverse(EnumDecl*); // KRYSTIAN TODO: friends are a can of worms // we do not wish to open just yet bool traverse(FriendDecl*); - bool traverse(ClassTemplateDecl*, AccessSpecifier); - bool traverse(ClassTemplateSpecializationDecl*); - bool traverse(VarTemplateDecl*, AccessSpecifier); - bool traverse(VarTemplateSpecializationDecl*); - bool traverse(FunctionTemplateDecl*, AccessSpecifier); + // non-static data members + bool traverse(FieldDecl*); + + // class scope function template explicit specializations bool traverse(ClassScopeFunctionSpecializationDecl*); - bool traverse(TypeAliasTemplateDecl*, AccessSpecifier); + + template CXXRecordTy> + bool traverse(CXXRecordTy*, ClassTemplateDecl* = nullptr); + + template VarTy> + bool traverse(VarTy*, VarTemplateDecl* = nullptr); + + template FunctionTy> + bool traverse(FunctionTy*, FunctionTemplateDecl* = nullptr); + + template TypedefNameTy> + bool traverse(TypedefNameTy*, TypeAliasTemplateDecl* = nullptr); #if 0 // includes both linkage-specification forms in [dcl.link]: @@ -2127,7 +2332,7 @@ class ASTVisitor // catch-all function so overload resolution does not // cause a hard error in the Traverse function for Decl template - auto traverse(Args&&...); + auto traverse(Decl* D, Args&&...); template bool traverseDecl(Decl* D, Args&&... args); @@ -2135,6 +2340,7 @@ class ASTVisitor }; //------------------------------------------------ +// NamespaceDecl bool ASTVisitor:: @@ -2155,238 +2361,210 @@ traverse(NamespaceDecl* D) return traverseContext(D); } -bool -ASTVisitor:: -traverse(CXXRecordDecl* D, - AccessSpecifier A, - std::unique_ptr&& Template = nullptr) -{ - if(! shouldExtract(D)) - return true; - - MRDOX_ASSERT(! D->getDeclContext()->isRecord() || - A != AccessSpecifier::AS_none); - - SymbolID id; - if(! extractSymbolID(D, id)) - return false; - auto [I, created] = getOrCreateInfo(id); - I.Access = convertToAccessKind(A); - I.Template = std::move(Template); - - buildRecord(I, created, D); - return traverseContext(D); -} +//------------------------------------------------ +// EnumDecl bool ASTVisitor:: -traverse(EnumDecl* D, - AccessSpecifier A) +traverse(EnumDecl* D) { if(! shouldExtract(D)) return true; - MRDOX_ASSERT(! D->getDeclContext()->isRecord() || - A != AccessSpecifier::AS_none); + // MRDOX_ASSERT(! D->getDeclContext()->isRecord() || + // A != AccessSpecifier::AS_none); SymbolID id; if(! extractSymbolID(D, id)) return false; auto [I, created] = getOrCreateInfo(id); - I.Access = convertToAccessKind(A); + I.Access = convertToAccessKind(D->getAccessUnsafe()); buildEnum(I, created, D); return true; } +//------------------------------------------------ +// FieldDecl + bool ASTVisitor:: -traverse(TypedefDecl* D, - AccessSpecifier A) +traverse(FieldDecl* D) { if(! shouldExtract(D)) return true; - MRDOX_ASSERT(! D->getDeclContext()->isRecord() || - A != AccessSpecifier::AS_none); + // MRDOX_ASSERT(D->getDeclContext()->isRecord() && + // A != AccessSpecifier::AS_none); SymbolID id; if(! extractSymbolID(D, id)) return false; - auto [I, created] = getOrCreateInfo(id); - I.Access = convertToAccessKind(A); + auto [I, created] = getOrCreateInfo(id); + I.Access = convertToAccessKind(D->getAccessUnsafe()); - buildTypedef(I, created, D); + buildField(I, created, D); return true; } +//------------------------------------------------ +// FriendDecl + bool ASTVisitor:: -traverse(TypeAliasDecl* D, - AccessSpecifier A, - std::unique_ptr&& Template = nullptr) +traverse(FriendDecl* D) { - if(! shouldExtract(D)) - return true; - - MRDOX_ASSERT(! D->getDeclContext()->isRecord() || - A != AccessSpecifier::AS_none); - - SymbolID id; - if(! extractSymbolID(D, id)) - return false; - auto [I, created] = getOrCreateInfo(id); - I.Access = convertToAccessKind(A); - I.IsUsing = true; - I.Template = std::move(Template); - - buildTypedef(I, created, D); + buildFriend(D); return true; } +//------------------------------------------------ + +template CXXRecordTy> bool ASTVisitor:: -traverse(VarDecl* D, - AccessSpecifier A, - std::unique_ptr&& Template = nullptr) +traverse(CXXRecordTy* D, + ClassTemplateDecl* CTD) { if(! shouldExtract(D)) return true; - MRDOX_ASSERT(! D->getDeclContext()->isRecord() || - A != AccessSpecifier::AS_none); - SymbolID id; if(! extractSymbolID(D, id)) return false; - auto [I, created] = getOrCreateInfo(id); - I.Access = convertToAccessKind(A); - I.Template = std::move(Template); - buildVariable(I, created, D); - return true; -} + auto [I, created] = getOrCreateInfo(id); -bool -ASTVisitor:: -traverse(FieldDecl* D, - AccessSpecifier A) -{ - if(! shouldExtract(D)) - return true; + AccessSpecifier access = D->getAccessUnsafe(); + // CTD is the specialized template if D is a partial or + // explicit specialization, and the described template otherwise + if(CTD) + { + // use the access of the described/specialized template + access = CTD->getAccessUnsafe(); - MRDOX_ASSERT(D->getDeclContext()->isRecord()); - MRDOX_ASSERT(A != AccessSpecifier::AS_none); + I.Template = std::make_unique(); + // if D is a partial/explicit specialization, extract the template arguments + if(auto* CTSD = dyn_cast(D)) + { + parseTemplateArgs(*I.Template, CTSD); + // extract the template parameters if this is a partial specialization + if(auto* CTPSD = dyn_cast(D)) + parseTemplateParams(*I.Template, + CTPSD->getTemplateParameters()); + } + else + { + // otherwise, extract the template parameter list from CTD + parseTemplateParams(*I.Template, + CTD->getTemplateParameters()); + } + } - SymbolID id; - if(! extractSymbolID(D, id)) - return false; - auto [I, created] = getOrCreateInfo(id); - I.Access = convertToAccessKind(A); + I.Access = convertToAccessKind(access); - buildField(I, created, D); - return true; + buildRecord(I, created, D); + return traverseContext(D); } +template VarTy> bool ASTVisitor:: -traverse(FunctionDecl* D, - AccessSpecifier A, - std::unique_ptr&& Template = nullptr) +traverse(VarTy* D, + VarTemplateDecl* VTD) { if(! shouldExtract(D)) return true; - MRDOX_ASSERT(! D->getDeclContext()->isRecord()); - MRDOX_ASSERT(A == AccessSpecifier::AS_none); - SymbolID id; if(! extractSymbolID(D, id)) return false; - auto [I, created] = getOrCreateInfo(id); - I.Access = convertToAccessKind(A); - I.Template = std::move(Template); - buildFunction(I, created, D); - return true; -} + auto [I, created] = getOrCreateInfo(id); -bool -ASTVisitor:: -traverse(CXXMethodDecl* D, - AccessSpecifier A, - std::unique_ptr&& Template = nullptr) -{ - if(! shouldExtract(D)) - return true; + AccessSpecifier access = D->getAccessUnsafe(); + // VTD is the specialized template if D is a partial or + // explicit specialization, and the described template otherwise + if(VTD) + { + // use the access of the described/specialized template + access = VTD->getAccessUnsafe(); - MRDOX_ASSERT(D->getDeclContext()->isRecord()); - MRDOX_ASSERT(A != AccessSpecifier::AS_none); + I.Template = std::make_unique(); + // if D is a partial/explicit specialization, extract the template arguments + if(auto* VTSD = dyn_cast(D)) + { + parseTemplateArgs(*I.Template, VTSD); + // extract the template parameters if this is a partial specialization + if(auto* VTPSD = dyn_cast(D)) + parseTemplateParams(*I.Template, + VTPSD->getTemplateParameters()); + } + else + { + // otherwise, extract the template parameter list from VTD + parseTemplateParams(*I.Template, + VTD->getTemplateParameters()); + } + } - SymbolID id; - if(! extractSymbolID(D, id)) - return false; - auto [I, created] = getOrCreateInfo(id); - I.Access = convertToAccessKind(A); - I.Template = std::move(Template); + I.Access = convertToAccessKind(access); - buildFunction(I, created, D); + buildVariable(I, created, D); return true; } + +template FunctionTy> bool ASTVisitor:: -traverse(CXXConstructorDecl* D, - AccessSpecifier A, - std::unique_ptr&& Template = nullptr) +traverse(FunctionTy* D, + FunctionTemplateDecl* FTD) { if(! shouldExtract(D)) return true; - MRDOX_ASSERT(D->getDeclContext()->isRecord()); - MRDOX_ASSERT(A != AccessSpecifier::AS_none); - SymbolID id; if(! extractSymbolID(D, id)) return false; + auto [I, created] = getOrCreateInfo(id); - I.Access = convertToAccessKind(A); - I.Template = std::move(Template); - I.Class = FunctionClass::Constructor; - buildFunction(I, created, D); - return true; -} + AccessSpecifier access = D->getAccessUnsafe(); -bool -ASTVisitor:: -traverse(CXXConversionDecl* D, - AccessSpecifier A, - std::unique_ptr&& Template = nullptr) -{ - if(! shouldExtract(D)) - return true; + auto* FTSI = D->getTemplateSpecializationInfo(); + auto* DFTSI = D->getDependentSpecializationInfo(); + if(FTD || FTSI || DFTSI) + { + I.Template = std::make_unique(); - MRDOX_ASSERT(D->getDeclContext()->isRecord()); - MRDOX_ASSERT(A != AccessSpecifier::AS_none); + if(FTD) + { + access = FTD->getAccessUnsafe(); + parseTemplateParams(*I.Template, + FTD->getTemplateParameters()); + } + else if(FTSI) + { + parseTemplateArgs(*I.Template, FTSI); + } + else if(DFTSI) + { + parseTemplateArgs(*I.Template, DFTSI); + } + } - SymbolID id; - if(! extractSymbolID(D, id)) - return false; - auto [I, created] = getOrCreateInfo(id); - I.Access = convertToAccessKind(A); - I.Template = std::move(Template); - I.Class = FunctionClass::Conversion; + I.Access = convertToAccessKind(access); buildFunction(I, created, D); return true; } +template TypedefNameTy> bool ASTVisitor:: -traverse(CXXDeductionGuideDecl* D, - AccessSpecifier A, - std::unique_ptr&& Template = nullptr) +traverse(TypedefNameTy* D, + TypeAliasTemplateDecl* ATD) { if(! shouldExtract(D)) return true; @@ -2394,132 +2572,31 @@ traverse(CXXDeductionGuideDecl* D, SymbolID id; if(! extractSymbolID(D, id)) return false; - auto [I, created] = getOrCreateInfo(id); - I.Access = convertToAccessKind(A); - I.Template = std::move(Template); - I.Class = FunctionClass::Deduction; - buildFunction(I, created, D); - return true; -} + auto [I, created] = getOrCreateInfo(id); -bool -ASTVisitor:: -traverse(CXXDestructorDecl* D, - AccessSpecifier A) -{ - if(! shouldExtract(D)) - return true; + if(isa(D)) + I.IsUsing = true; - MRDOX_ASSERT(D->getDeclContext()->isRecord()); - MRDOX_ASSERT(A != AccessSpecifier::AS_none); + AccessSpecifier access = D->getAccessUnsafe(); - SymbolID id; - if(! extractSymbolID(D, id)) - return false; - auto [I, created] = getOrCreateInfo(id); - I.Access = convertToAccessKind(A); - I.Class = FunctionClass::Destructor; + if(ATD) + { + access = ATD->getAccessUnsafe(); - buildFunction(I, created, D); - return true; -} + I.Template = std::make_unique(); + parseTemplateParams(*I.Template, + ATD->getTemplateParameters()); + } -bool -ASTVisitor:: -traverse(FriendDecl* D) -{ - buildFriend(D); + I.Access = convertToAccessKind(access); + + buildTypedef(I, created, D); return true; } //------------------------------------------------ -bool -ASTVisitor:: -traverse(ClassTemplateDecl* D, - AccessSpecifier A) -{ - CXXRecordDecl* RD = D->getTemplatedDecl(); - - if(! shouldExtract(RD)) - return true; - - auto Template = std::make_unique(); - parseTemplateParams(*Template, RD); - - return traverse(RD, A, std::move(Template)); -} - -bool -ASTVisitor:: -traverse(ClassTemplateSpecializationDecl* D) -{ - CXXRecordDecl* RD = D; - if(! shouldExtract(RD)) - return true; - - auto Template = std::make_unique(); - parseTemplateParams(*Template, RD); - parseTemplateArgs(*Template, D); - - // determine the access from the primary template - return traverse(RD, - D->getSpecializedTemplate()->getAccessUnsafe(), - std::move(Template)); -} - -bool -ASTVisitor:: -traverse(VarTemplateDecl* D, - AccessSpecifier A) -{ - VarDecl* VD = D->getTemplatedDecl(); - if(! shouldExtract(VD)) - return true; - - auto Template = std::make_unique(); - parseTemplateParams(*Template, VD); - - return traverse(VD, A, std::move(Template)); -} - -bool -ASTVisitor:: -traverse(VarTemplateSpecializationDecl* D) -{ - VarDecl* VD = D; - if(! shouldExtract(VD)) - return true; - - auto Template = std::make_unique(); - parseTemplateParams(*Template, VD); - parseTemplateArgs(*Template, D); - - return traverse(VD, - D->getSpecializedTemplate()->getAccessUnsafe(), - std::move(Template)); -} - -bool -ASTVisitor:: -traverse(FunctionTemplateDecl* D, - AccessSpecifier A) -{ - FunctionDecl* FD = D->getTemplatedDecl(); - - // check whether to extract using the templated declaration. - // this is done because the template-head may be implicit - // (e.g. for an abbreviated function template with no template-head) - if(! shouldExtract(FD)) - return true; - auto Template = std::make_unique(); - parseTemplateParams(*Template, FD); - - // traverse the templated declaration according to its kind - return traverseDecl(FD, std::move(Template)); -} - bool ASTVisitor:: traverse(ClassScopeFunctionSpecializationDecl* D) @@ -2530,45 +2607,69 @@ traverse(ClassScopeFunctionSpecializationDecl* D) /* For class scope explicit specializations of member function templates which are members of class templates, it is impossible to know what the primary template is until the enclosing class template is instantiated. - while such declarations are valid C++ (see CWG 727 and [temp.expl.spec] p3), + while such declarations are valid C++ (see CWG 727, N4090, and [temp.expl.spec] p3), GCC does not consider them to be valid. Consequently, we do not extract the SymbolID of the primary template. In the future, we could take a best-effort approach to find the primary template, but this is only possible when none of the candidates are dependent upon a template parameter of the enclosing class template. */ - auto Template = std::make_unique(); - parseTemplateArgs(*Template, D); + DeclContext* DC = D->getDeclContext(); CXXMethodDecl* MD = D->getSpecialization(); - // since the templated CXXMethodDecl may be a constructor - // or conversion function, call TraverseDecl to ensure that - // we call traverse for the dynamic type of the CXXMethodDecl - return traverseDecl(MD, std::move(Template)); -} + // create a set of all function templates declared in + // the enclosing class template which share the same name + // as this specialization. this will not include MD as it + // has not been added to the DeclContext yet + // UnresolvedSet<8> Candidates; + llvm::SmallPtrSet Found; + auto lookup_result = DC->lookup(MD->getDeclName()); + for(auto first = lookup_result.begin(), + last = lookup_result.end(); + first != last; ++first) + { + NamedDecl* ND = *first; + if(! isa(ND)) + continue; + Found.insert(ND); + } + // in theory we could check whether the declarations are + // lexically before the explicit specialization by comparing + // source locations, but i'm uncertain whether this would work. + for(Decl* next = D; (next = next->getNextDeclInContext());) + { + if(auto* N = dyn_cast(next)) + Found.erase(N); + } -bool -ASTVisitor:: -traverse(TypeAliasTemplateDecl* D, - AccessSpecifier A) -{ - TypeAliasDecl* AD = D->getTemplatedDecl(); - if(! shouldExtract(AD)) - return true; + UnresolvedSet<8> Candidates; + for(auto* N : Found) + Candidates.addDecl(N); + + TemplateArgumentListInfo args; + if(auto* args_written = D->getTemplateArgsAsWritten()) + { + args.setLAngleLoc(args_written->getLAngleLoc()); + args.setRAngleLoc(args_written->getRAngleLoc()); + for(const auto& arg_loc : args_written->arguments()) + args.addArgument(arg_loc); + } - auto Template = std::make_unique(); - parseTemplateParams(*Template, AD); + MD->setDependentTemplateSpecialization( + MD->getASTContext(), Candidates, args); - return traverse(AD, A, std::move(Template)); + return traverseDecl(MD); } +//------------------------------------------------ + template auto ASTVisitor:: -traverse(Args&&...) +traverse(Decl* D, Args&&...) { - // no matching traverse overload found - MRDOX_UNREACHABLE(); + if(auto* DC = dyn_cast(D)) + traverseContext(DC); } //------------------------------------------------ @@ -2581,146 +2682,39 @@ traverseDecl( Args&&... args) { MRDOX_ASSERT(D); + if(D->isInvalidDecl() || D->isImplicit()) return true; SymbolFilter::FilterScope scope(symbolFilter_); - AccessSpecifier access = - D->getAccessUnsafe(); - - switch(D->getKind()) - { - case Decl::Namespace: - traverse(static_cast< - NamespaceDecl*>(D), - std::forward(args)...); - break; - case Decl::CXXRecord: - traverse(static_cast< - CXXRecordDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::CXXMethod: - traverse(static_cast< - CXXMethodDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::CXXConstructor: - traverse(static_cast< - CXXConstructorDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::CXXConversion: - traverse(static_cast< - CXXConversionDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::CXXDestructor: - traverse(static_cast< - CXXDestructorDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::CXXDeductionGuide: - traverse(static_cast< - CXXDeductionGuideDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::Function: - traverse(static_cast< - FunctionDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::Friend: - traverse(static_cast< - FriendDecl*>(D), - std::forward(args)...); - break; - case Decl::TypeAlias: - traverse(static_cast< - TypeAliasDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::Typedef: - traverse(static_cast< - TypedefDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::Enum: - traverse(static_cast< - EnumDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::Field: - traverse(static_cast< - FieldDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::Var: - traverse(static_cast< - VarDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::ClassTemplate: - traverse(static_cast< - ClassTemplateDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::ClassTemplatePartialSpecialization: - case Decl::ClassTemplateSpecialization: - traverse(static_cast< - ClassTemplateSpecializationDecl*>(D), - std::forward(args)...); - break; - case Decl::VarTemplate: - traverse(static_cast< - VarTemplateDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::VarTemplatePartialSpecialization: - case Decl::VarTemplateSpecialization: - traverse(static_cast< - VarTemplateSpecializationDecl*>(D), - std::forward(args)...); - break; - case Decl::FunctionTemplate: - traverse(static_cast< - FunctionTemplateDecl*>(D), - access, - std::forward(args)...); - break; - case Decl::ClassScopeFunctionSpecialization: - traverse(static_cast< - ClassScopeFunctionSpecializationDecl*>(D), - std::forward(args)...); - break; - case Decl::TypeAliasTemplate: - traverse(static_cast< - TypeAliasTemplateDecl*>(D), - access, - std::forward(args)...); - break; - default: - // for declarations we don't explicitly handle, traverse the children - // if it has any (e.g. LinkageSpecDecl, ExportDecl, ExternCContextDecl). - if(auto* DC = dyn_cast(D)) - traverseContext(DC); - break; - } + visit(D, [&](DeclTy* DT) + { + // only ClassTemplateDecl, FunctionTemplateDecl, VarTemplateDecl, + // and TypeAliasDecl are derived from RedeclarableTemplateDecl. + // note that this doesn't include ConceptDecl. + if constexpr(std::derived_from) + { + // call traverseDecl so traverse is called with + // a pointer to the most derived type of the templated Decl + traverseDecl(DT->getTemplatedDecl(), DT); + } + else if constexpr(std::derived_from) + { + traverse(DT, DT->getSpecializedTemplate()); + } + else if constexpr(std::derived_from) + { + traverse(DT, DT->getSpecializedTemplate()); + } + else + { + traverse(DT, std::forward(args)...); + } + }); return true; } @@ -2737,6 +2731,64 @@ traverseContext( return true; } +#if 0 +class ASTInstantiationCallbacks + : public TemplateInstantiationCallback +{ + void initialize(const Sema&) override { } + void finalize(const Sema&) override { } + + void + atTemplateBegin( + const Sema& S, + const Sema::CodeSynthesisContext& Context) override + { + } + + void + atTemplateEnd( + const Sema& S, + const Sema::CodeSynthesisContext& Context) override + { + if(Context.Kind != Sema::CodeSynthesisContext::TemplateInstantiation) + return; + + Decl* D = Context.Entity; + if(! D) + return; + + bool skip = visit(D, [&](DeclTy* DT) -> bool + { + constexpr Decl::Kind kind = DeclToKind(); + + if constexpr(std::derived_from) + { + CXXMethodDecl* MD = DT; + return MD->isFunctionTemplateSpecialization(); + } + + if constexpr(kind == Decl::TypeAliasTemplate) + { + TypeAliasTemplateDecl* TAD = DT; + + if(DeclContext* DC = TAD->getDeclContext()) + { + Decl* DCD = cast(DC); + return ! DCD->isImplicit(); + } + } + + return false; + }); + + if(skip) + return; + + D->setImplicit(); + } +}; +#endif + //------------------------------------------------ // // ASTVisitorConsumer @@ -2758,6 +2810,11 @@ class ASTVisitorConsumer // Sema should not have been initialized yet MRDOX_ASSERT(! sema_); sema_ = &S; + +#if 0 + S.TemplateInstCallbacks.emplace_back( + std::make_unique()); +#endif } void diff --git a/src/lib/AST/ASTVisitorHelpers.hpp b/src/lib/AST/ASTVisitorHelpers.hpp index e1c20c62b..4a068dbd8 100644 --- a/src/lib/AST/ASTVisitorHelpers.hpp +++ b/src/lib/AST/ASTVisitorHelpers.hpp @@ -236,7 +236,26 @@ convertToQualifierKind( if(quals & Qualifiers::Volatile) result |= QualifierKind::Volatile; return static_cast(result); - + +} + +FunctionClass +convertToFunctionClass( + Decl::Kind kind) +{ + using OldKind = Decl::Kind; + using NewKind = FunctionClass; + switch(kind) + { + case OldKind::Function: return NewKind::Normal; + case OldKind::CXXMethod: return NewKind::Normal; + case OldKind::CXXConstructor: return NewKind::Constructor; + case OldKind::CXXConversion: return NewKind::Conversion; + case OldKind::CXXDestructor: return NewKind::Destructor; + case OldKind::CXXDeductionGuide: return NewKind::Deduction; + default: + MRDOX_UNREACHABLE(); + } } // ---------------------------------------------------------------- @@ -248,19 +267,19 @@ template< requires std::derived_from decltype(auto) visit( - DeclTy* d, + DeclTy* D, Visitor&& visitor, Args&&... args) { - switch(d->getKind()) + switch(D->getKind()) { - #define ABSTRACT_DECL(DECL) + #define ABSTRACT_DECL(TYPE) #define DECL(DERIVED, BASE) \ case Decl::DERIVED: \ if constexpr(std::derived_from) \ return std::forward(visitor)( \ static_cast*>(d), \ + DERIVED##Decl>*>(D), \ std::forward(args)...); \ else \ MRDOX_UNREACHABLE(); @@ -273,11 +292,11 @@ visit( } template -consteval -Decl::Kind +consteval +Decl::Kind DeclToKindImpl() = delete; -#define ABSTRACT_DECL(DECL) +#define ABSTRACT_DECL(TYPE) #define DECL(DERIVED, BASE) \ template<> \ consteval \ @@ -287,8 +306,8 @@ DeclToKindImpl() = delete; #include template -consteval -Decl::Kind +consteval +Decl::Kind DeclToKind() { return DeclToKindImpl< @@ -304,19 +323,19 @@ template< requires std::derived_from decltype(auto) visit( - TypeTy* t, + TypeTy* T, Visitor&& visitor, Args&&... args) { - switch(t->getTypeClass()) - { #define ABSTRACT_TYPE(DERIVED, BASE) + switch(T->getTypeClass()) + { #define TYPE(DERIVED, BASE) \ case Type::DERIVED: \ if constexpr(std::derived_from) \ return std::forward(visitor)( \ static_cast*>(t), \ + DERIVED##Type>*>(T), \ std::forward(args)...); \ else \ MRDOX_UNREACHABLE(); @@ -329,7 +348,7 @@ visit( } template -consteval +consteval Type::TypeClass TypeToKindImpl() = delete; @@ -343,7 +362,7 @@ TypeToKindImpl() = delete; #include template -consteval +consteval Type::TypeClass TypeToKind() { @@ -360,11 +379,11 @@ template< requires std::derived_from decltype(auto) visit( - TypeLocTy* t, + TypeLocTy* T, Visitor&& visitor, Args&&... args) { - switch(t->getTypeLocClass()) + switch(T->getTypeLocClass()) { #define ABSTRACT_TYPELOC(DERIVED, BASE) #define TYPELOC(DERIVED, BASE) \ @@ -372,20 +391,20 @@ visit( if constexpr(std::derived_from) \ return std::forward(visitor)( \ static_cast*>(t), \ + DERIVED##TypeLoc>*>(T), \ std::forward(args)...); \ else \ MRDOX_UNREACHABLE(); #include - + default: MRDOX_UNREACHABLE(); } } template -consteval +consteval TypeLoc::TypeLocClass TypeLocToKindImpl() = delete; @@ -399,7 +418,7 @@ TypeLocToKindImpl() = delete; #include template -consteval +consteval TypeLoc::TypeLocClass TypeLocToKind() { diff --git a/src/lib/Lib/ConfigImpl.cpp b/src/lib/Lib/ConfigImpl.cpp index 917eb5173..c44d44809 100644 --- a/src/lib/Lib/ConfigImpl.cpp +++ b/src/lib/Lib/ConfigImpl.cpp @@ -32,13 +32,42 @@ template<> struct llvm::yaml::MappingTraits< clang::mrdox::ConfigImpl::SettingsImpl::FileFilter> { - static void mapping(IO &io, + static void mapping(IO& io, clang::mrdox::ConfigImpl::SettingsImpl::FileFilter& f) { io.mapOptional("include", f.include); } }; +template<> +struct llvm::yaml::ScalarEnumerationTraits< + clang::mrdox::Config::ExtractOptions::Policy> +{ + using Policy = clang::mrdox::Config::ExtractOptions::Policy; + + static void enumeration(IO& io, + Policy& value) + { + io.enumCase(value, "always", Policy::Always); + io.enumCase(value, "dependency", Policy::Dependency); + io.enumCase(value, "never", Policy::Never); + } +}; + +template<> +struct llvm::yaml::MappingTraits< + clang::mrdox::Config::ExtractOptions> +{ + static void mapping(IO &io, + clang::mrdox::Config::ExtractOptions& opts) + { + io.mapOptional("referenced-declarations", opts.referencedDeclarations); + io.mapOptional("anonymous-namespaces", opts.anonymousNamespaces); + io.mapOptional("inaccessible-members", opts.inaccessibleMembers); + io.mapOptional("inaccessible-bases", opts.inaccessibleBases); + } +}; + template<> struct llvm::yaml::MappingTraits< clang::mrdox::ConfigImpl::SettingsImpl::Filters::Category> @@ -71,6 +100,9 @@ struct llvm::yaml::MappingTraits< { io.mapOptional("defines", cfg.defines); io.mapOptional("ignore-failures", cfg.ignoreFailures); + + io.mapOptional("extract-options", cfg.extractOptions); + io.mapOptional("include-anonymous", cfg.includeAnonymous); io.mapOptional("include-private", cfg.includePrivate); io.mapOptional("multipage", cfg.multiPage); diff --git a/src/lib/clang.natvis b/src/lib/clang.natvis index de23942c0..3d04d37ad 100644 --- a/src/lib/clang.natvis +++ b/src/lib/clang.natvis @@ -229,8 +229,8 @@ For later versions of Visual Studio, no setup is required--> {Name,s} - invalid {ID} + invalid @@ -686,6 +686,7 @@ For later versions of Visual Studio, no setup is required--> const volatile {get_type_ptr(),na} volatile restrict {get_type_ptr(),na} const volatile restrict {get_type_ptr(),na} + <null> @@ -1416,6 +1417,25 @@ For later versions of Visual Studio, no setup is required--> (FunctionTemplateSpecializationInfo*)TemplateOrSpecialization.get_ptr() + + (FunctionDeclBits.IsDeleted && ! FunctionDeclBits.IsDefaulted) || + ((! FunctionDeclBits.HasDefaultedFunctionInfo && Body.Ptr) || FunctionDeclBits.IsLateTemplateParsed) || + FunctionDeclBits.HasSkippedBody || + FunctionDeclBits.WillHaveBody + + + size = {DeclType.get_type_ptr()->FunctionTypeBits.NumParams} size = 0 diff --git a/src/test/TestRunner.cpp b/src/test/TestRunner.cpp index 1102bc80a..45386995d 100644 --- a/src/test/TestRunner.cpp +++ b/src/test/TestRunner.cpp @@ -124,7 +124,7 @@ handleFile( // Get expected XML if it exists std::unique_ptr expectedXml; { - auto fileResult = llvm::MemoryBuffer::getFile(expectedPath, false); + auto fileResult = llvm::MemoryBuffer::getFile(expectedPath, false, true, true); if(fileResult) expectedXml = std::move(fileResult.get()); else if(fileResult.getError() != std::errc::no_such_file_or_directory) diff --git a/test-files/old-tests/temp/ct_mft_expl_inline.xml b/test-files/old-tests/temp/ct_mft_expl_inline.xml index 2d6ac4cc3..29d307c91 100644 --- a/test-files/old-tests/temp/ct_mft_expl_inline.xml +++ b/test-files/old-tests/temp/ct_mft_expl_inline.xml @@ -12,7 +12,7 @@ -