Skip to content

doxygen-like extract_* options #858

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 60 additions & 30 deletions docs/mrdocs.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,6 @@
"title": "Path to the Addons directory",
"type": "string"
},
"anonymous-namespaces": {
"default": true,
"description": "Determine whether symbols in anonymous namespaces should be extracted. When set to `always`, symbols in anonymous namespaces are always extracted. When set to `dependency`, symbols in anonymous namespaces are extracted only if they are referenced by the source code. When set to `never`, symbols in anonymous namespaces are never extracted.",
"enum": [
true,
false
],
"title": "Extraction policy for anonymous namespaces",
"type": "boolean"
},
"auto-brief": {
"default": true,
"description": "When set to `true`, MrDocs uses the first line (until the first dot, question mark, or exclamation mark) of the comment as the brief of the symbol. When set to `false`, a explicit @brief command is required.",
Expand Down Expand Up @@ -101,6 +91,66 @@
"title": "Extract all symbols",
"type": "boolean"
},
"extract-anonymous-namespaces": {
"default": true,
"description": "Determine whether symbols in anonymous namespaces should be extracted. When set to `always`, symbols in anonymous namespaces are always extracted. When set to `dependency`, symbols in anonymous namespaces are extracted only if they are referenced by the source code. When set to `never`, symbols in anonymous namespaces are never extracted.",
"enum": [
true,
false
],
"title": "Extraction policy for anonymous namespaces",
"type": "boolean"
},
"extract-local-classes": {
"default": true,
"description": "Determine whether records only defined locally in source files should be extracted.",
"enum": [
true,
false
],
"title": "Extraction policy for records defined locally in source files",
"type": "boolean"
},
"extract-private": {
"default": false,
"description": "Determine whether private class members should be extracted",
"enum": [
true,
false
],
"title": "Extraction policy for private class members",
"type": "boolean"
},
"extract-private-bases": {
"default": false,
"description": "Determine whether private base classes should be extracted",
"enum": [
true,
false
],
"title": "Extraction policy for private base classes",
"type": "boolean"
},
"extract-private-virtual": {
"default": false,
"description": "Determine whether private virtual methods of a class should be extracted",
"enum": [
true,
false
],
"title": "Extraction policy for private virtual methods of a class",
"type": "boolean"
},
"extract-static": {
"default": false,
"description": "Determine whether static members of a file should be extracted. This option does not refer to static members of a class.",
"enum": [
true,
false
],
"title": "Extraction policy for static members of a file",
"type": "boolean"
},
"file-patterns": {
"default": [
"*.hpp",
Expand Down Expand Up @@ -260,26 +310,6 @@
"title": "Detect and group function overloads",
"type": "boolean"
},
"private-bases": {
"default": true,
"description": "Determine whether private base classes should be extracted",
"enum": [
true,
false
],
"title": "Extraction policy for private base classes",
"type": "boolean"
},
"private-members": {
"default": false,
"description": "Determine whether private class members should be extracted",
"enum": [
true,
false
],
"title": "Extraction policy for private class members",
"type": "boolean"
},
"recursive": {
"default": true,
"description": "Recursively include files. When set to true, MrDocs includes files in subdirectories of the input directories. When set to false, MrDocs includes only the files in the input directories.",
Expand Down
71 changes: 43 additions & 28 deletions src/lib/AST/ASTVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ populate(
{
AccessSpecifier const access = B.getAccessSpecifier();

if (!config_->privateBases &&
if (!config_->extractPrivateBases &&
access == AS_private)
{
continue;
Expand Down Expand Up @@ -2701,51 +2701,54 @@ checkFilters(

bool
ASTVisitor::
checkTypeFilters(Decl const* D, AccessSpecifier access)
checkTypeFilters(Decl const* D, AccessSpecifier const access)
{
if (!config_->privateMembers)
if (access == AS_private)
{
// KRYSTIAN FIXME: this doesn't handle direct
// dependencies on inaccessible declarations
MRDOCS_CHECK_OR(
access != AccessSpecifier::AS_private, false);
// Don't extract private members
if (isVirtualMember(D))
{
// Don't extract private virtual members
return config_->extractPrivateVirtual || config_->extractPrivate;
}
return config_->extractPrivate;
}
if (!config_->extractAnonymousNamespaces)
{
// Don't extract anonymous namespaces
MRDOCS_CHECK_OR(!isAnonymousNamespace(D), false);
}
if (!config_->extractStatic)
{
MRDOCS_CHECK_OR(!isStaticFileLevelMember(D), false);
}
if (!config_->extractLocalClasses && isa<RecordDecl>(D))
{
if (auto const* FI = findFileInfo(D);
FI->full_path.ends_with(".cpp") ||
FI->full_path.ends_with(".cc") ||
FI->full_path.ends_with(".cxx") ||
FI->full_path.ends_with(".c"))
{
return false;
}
}

// Don't extract anonymous unions
auto const* RD = dyn_cast<RecordDecl>(D);
MRDOCS_CHECK_OR(!RD || !RD->isAnonymousStructOrUnion(), false);

// Don't extract implicitly generated declarations
// (except for IndirectFieldDecls)
MRDOCS_CHECK_OR(!D->isImplicit() || isa<IndirectFieldDecl>(D), false);

// Don't extract anonymous namespaces unless configured to do so
// and the current mode is normal
if (auto const* ND = dyn_cast<NamespaceDecl>(D);
ND &&
ND->isAnonymousNamespace() &&
config_->anonymousNamespaces)
{
// Otherwise, skip extraction if this isn't a dependency
// KRYSTIAN FIXME: is this correct? a namespace should not
// be extracted as a dependency (until namespace aliases and
// using directives are supported)
MRDOCS_CHECK_OR(mode_ == TraversalMode::Regular, false);
}

return true;
}

bool
ASTVisitor::
checkFileFilters(Decl const* D)
{
clang::SourceLocation Loc = D->getBeginLoc();
if (Loc.isInvalid())
{
Loc = D->getLocation();
}
FileInfo* fileInfo = findFileInfo(Loc);
FileInfo* fileInfo = findFileInfo(D);
MRDOCS_CHECK_OR(fileInfo, false);

// Check pre-processed file filters
Expand Down Expand Up @@ -3149,6 +3152,18 @@ findFileInfo(clang::SourceLocation const loc)
return std::addressof(it->second);
}

ASTVisitor::FileInfo*
ASTVisitor::
findFileInfo(Decl const* D)
{
clang::SourceLocation Loc = D->getBeginLoc();
if (Loc.isInvalid())
{
Loc = D->getLocation();
}
return findFileInfo(Loc);
}

std::optional<ASTVisitor::FileInfo>
ASTVisitor::
buildFileInfo(FileEntry const* entry)
Expand Down
3 changes: 3 additions & 0 deletions src/lib/AST/ASTVisitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,9 @@ class ASTVisitor
FileInfo*
findFileInfo(clang::SourceLocation loc);

FileInfo*
findFileInfo(Decl const* D);

/* Build a FileInfo for a FileEntry

This function will build a FileInfo object for a
Expand Down
33 changes: 33 additions & 0 deletions src/lib/AST/ClangHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,37 @@ isAllImplicit(Decl const* D)
return isAllImplicit(P);
}

bool
isVirtualMember(Decl const* D)
{
if (auto const* MD = dyn_cast<CXXMethodDecl>(D))
{
return MD->isVirtual();
}
return false;
}

bool
isAnonymousNamespace(const Decl *D)
{
if (auto const* ND = dyn_cast<NamespaceDecl>(D))
{
return ND->isAnonymousNamespace();
}
return false;
}

bool
isStaticFileLevelMember(const Decl *D)
{
if (const auto *VD = dyn_cast<VarDecl>(D)) {
return VD->getStorageClass() == SC_Static && VD->getDeclContext()->isFileContext();
}
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
return FD->getStorageClass() == SC_Static && FD->getDeclContext()->isFileContext();
}
return false;
}


} // clang::mrdocs
12 changes: 12 additions & 0 deletions src/lib/AST/ClangHelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,18 @@ MRDOCS_DECL
bool
isAllImplicit(Decl const* D);

MRDOCS_DECL
bool
isVirtualMember(Decl const* D);

MRDOCS_DECL
bool
isAnonymousNamespace(Decl const* D);

MRDOCS_DECL
bool
isStaticFileLevelMember(Decl const *D);

#ifdef NDEBUG
#define MRDOCS_SYMBOL_TRACE(D, C)
#else
Expand Down
39 changes: 30 additions & 9 deletions src/lib/Lib/ConfigOptions.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,17 +186,45 @@
"default": true
},
{
"name": "private-members",
"name": "extract-private",
"brief": "Extraction policy for private class members",
"details": "Determine whether private class members should be extracted",
"type": "bool",
"default": false
},
{
"name": "private-bases",
"name": "extract-private-virtual",
"brief": "Extraction policy for private virtual methods of a class",
"details": "Determine whether private virtual methods of a class should be extracted",
"type": "bool",
"default": false
},
{
"name": "extract-private-bases",
"brief": "Extraction policy for private base classes",
"details": "Determine whether private base classes should be extracted",
"type": "bool",
"default": false
},
{
"name": "extract-static",
"brief": "Extraction policy for static members of a file",
"details": "Determine whether static members of a file should be extracted. This option does not refer to static members of a class.",
"type": "bool",
"default": false
},
{
"name": "extract-local-classes",
"brief": "Extraction policy for records defined locally in source files",
"details": "Determine whether records only defined locally in source files should be extracted.",
"type": "bool",
"default": true
},
{
"name": "extract-anonymous-namespaces",
"brief": "Extraction policy for anonymous namespaces",
"details": "Determine whether symbols in anonymous namespaces should be extracted. When set to `always`, symbols in anonymous namespaces are always extracted. When set to `dependency`, symbols in anonymous namespaces are extracted only if they are referenced by the source code. When set to `never`, symbols in anonymous namespaces are never extracted.",
"type": "bool",
"default": true
},
{
Expand All @@ -212,13 +240,6 @@
],
"default": "copy-dependencies"
},
{
"name": "anonymous-namespaces",
"brief": "Extraction policy for anonymous namespaces",
"details": "Determine whether symbols in anonymous namespaces should be extracted. When set to `always`, symbols in anonymous namespaces are always extracted. When set to `dependency`, symbols in anonymous namespaces are extracted only if they are referenced by the source code. When set to `never`, symbols in anonymous namespaces are never extracted.",
"type": "bool",
"default": true
},
{
"name": "sort-members",
"brief": "Sort the members of a record or namespace",
Expand Down
2 changes: 1 addition & 1 deletion src/lib/Metadata/Finalizers/BaseMembersFinalizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ inheritBaseMembers(
inheritBaseMembers(derivedId, derived.Protected, base.Public);
inheritBaseMembers(derivedId, derived.Protected, base.Protected);
}
else if (A == AccessKind::Private && corpus_.config->privateMembers)
else if (A == AccessKind::Private && corpus_.config->extractPrivate)
{
// When a class uses private member access specifier to derive from a
// base, all public and protected members of the base class are
Expand Down
Loading
Loading