Skip to content

feat: extract-friends option #942

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 4 commits into from
Jul 22, 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/.idea
/.vs
/.vscode
/.run
/CMakeUserPresets.json
/CMakeSettings.json
/build
Expand Down
6 changes: 0 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@
#-------------------------------------------------

cmake_minimum_required(VERSION 3.13)
if (GENERATOR_IS_MULTI_CONFIG AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Configurations" FORCE)
elseif(NOT GENERATOR_IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
endif()
project(
MrDocs
VERSION 0.0.3
Expand Down Expand Up @@ -256,7 +251,6 @@ if (WIN32)
-D_WIN32_WINNT=0x0601
-D_CRT_SECURE_NO_WARNINGS
-D_SILENCE_CXX20_CISO646_REMOVED_WARNING
-DFMT_HEADER_ONLY # because of _ITERATOR_DEBUG_LEVEL
)
if(MSVC)
get_target_property(LLVM_CONFIGURATION_TYPE LLVMCore IMPORTED_CONFIGURATIONS)
Expand Down
36 changes: 4 additions & 32 deletions bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,6 @@ class InstallOptions:
# Third-party dependencies
third_party_src_dir: str = "<mrdocs-src-dir>/../third-party"

# Fmt
fmt_src_dir: str = "<third-party-src-dir>/fmt"
fmt_build_type: str = "<mrdocs-build-type>"
fmt_build_dir: str = "<fmt-src-dir>/build/<fmt-build-type:lower>"
fmt_install_dir: str = "<fmt-src-dir>/install/<fmt-build-type:lower>"
fmt_repo: str = "https://github.com/fmtlib/fmt"
fmt_branch: str = "10.2.1"

# Duktape
duktape_src_dir: str = "<third-party-src-dir>/duktape"
duktape_url: str = "https://github.com/svaarala/duktape/releases/download/v2.7.0/duktape-2.7.0.tar.xz"
Expand Down Expand Up @@ -116,12 +108,6 @@ class InstallOptions:
"mrdocs_system_install": "Whether to install MrDocs to the system directories instead of a custom install directory.",
"mrdocs_run_tests": "Whether to run tests after building MrDocs.",
"third_party_src_dir": "Directory for all third-party source dependencies.",
"fmt_src_dir": "Directory for the fmt library source code.",
"fmt_build_type": "CMake build type for the fmt library. (Release, Debug, RelWithDebInfo, MilRelSize, DebWithOpt)",
"fmt_build_dir": "Directory where the fmt library will be built.",
"fmt_install_dir": "Directory where the fmt library will be installed.",
"fmt_repo": "URL of the fmt library repository to clone.",
"fmt_branch": "Branch or tag of the fmt library to use.",
"duktape_src_dir": "Directory for the Duktape source code.",
"duktape_url": "Download URL for the Duktape source archive.",
"duktape_build_type": "CMake build type for Duktape. (Release, Debug, RelWithDebInfo, MilRelSize, DebWithOpt)",
Expand Down Expand Up @@ -259,7 +245,7 @@ def repl(match):
key = match.group(1)
has_dir_key = has_dir_key or key.endswith("-dir")
key = key.replace("-", "_")
fmt = match.group(2)
transform_fn = match.group(2)
if key == 'os':
if self.is_windows():
val = "windows"
Expand All @@ -271,10 +257,10 @@ def repl(match):
raise ValueError("Unsupported operating system.")
else:
val = getattr(self.options, key, None)
if fmt:
if fmt == "lower":
if transform_fn:
if transform_fn == "lower":
val = val.lower()
elif fmt == "upper":
elif transform_fn == "upper":
val = val.upper()
# Add more formats as needed
return val
Expand Down Expand Up @@ -539,17 +525,6 @@ def setup_third_party_dir(self):
self.prompt_dependency_path_option("third_party_src_dir")
os.makedirs(self.options.third_party_src_dir, exist_ok=True)

def install_fmt(self):
self.prompt_dependency_path_option("fmt_src_dir")
if not os.path.exists(self.options.fmt_src_dir):
self.prompt_option("fmt_repo")
self.prompt_option("fmt_branch")
self.clone_repo(self.options.fmt_repo, self.options.fmt_src_dir, branch=self.options.fmt_branch, depth=1)
self.prompt_build_type_option("fmt_build_type")
self.prompt_dependency_path_option("fmt_build_dir")
self.prompt_dependency_path_option("fmt_install_dir")
self.cmake_workflow(self.options.fmt_src_dir, self.options.fmt_build_type, self.options.fmt_build_dir, self.options.fmt_install_dir, ["-D", "FMT_DOC=OFF", "-D", "FMT_TEST=OFF"])

def install_duktape(self):
self.prompt_dependency_path_option("duktape_src_dir")
if not os.path.exists(self.options.duktape_src_dir):
Expand Down Expand Up @@ -715,7 +690,6 @@ def create_cmake_presets(self):
"Clang_ROOT": self.options.llvm_install_dir,
"duktape_ROOT": self.options.duktape_install_dir,
"Duktape_ROOT": self.options.duktape_install_dir,
"fmt_ROOT": self.options.fmt_install_dir,
"libxml2_ROOT": self.options.libxml2_install_dir,
"LibXml2_ROOT": self.options.libxml2_install_dir,
"MRDOCS_BUILD_TESTS": self.options.mrdocs_build_tests,
Expand Down Expand Up @@ -797,7 +771,6 @@ def install_mrdocs(self):
"-D", f"Clang_ROOT={self.options.llvm_install_dir}",
"-D", f"duktape_ROOT={self.options.duktape_install_dir}",
"-D", f"Duktape_ROOT={self.options.duktape_install_dir}",
"-D", f"fmt_ROOT={self.options.fmt_install_dir}"
])
if self.options.mrdocs_build_tests:
extra_args.extend([
Expand Down Expand Up @@ -828,7 +801,6 @@ def install_all(self):
self.check_tools()
self.setup_mrdocs_dir()
self.setup_third_party_dir()
self.install_fmt()
self.install_duktape()
if self.options.mrdocs_build_tests:
self.install_libxml2()
Expand Down
10 changes: 10 additions & 0 deletions docs/mrdocs.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,16 @@
"title": "Extraction policy for empty namespaces",
"type": "boolean"
},
"extract-friends": {
"default": true,
"description": "Determine whether friend functions and classes should be extracted. When set to `true`, MrDocs extracts friend functions and classes. When set to `false`, friend functions and classes are not extracted.",
"enum": [
true,
false
],
"title": "Extraction policy for friend functions and classes",
"type": "boolean"
},
"extract-implicit-specializations": {
"default": true,
"description": "When set to `true`, MrDocs extracts implicit template specializations used as base classes as dependencies. This allows MrDocs to extract metadata that can later be used to determine the members of the derived class, as specified by the `inherit-base-members` option.",
Expand Down
4 changes: 3 additions & 1 deletion src/lib/AST/ASTVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,9 @@ populate(
}

// Iterate over the friends of the class
if (D->hasDefinition() && D->hasFriends())
if (config_->extractFriends &&
D->hasDefinition() &&
D->hasFriends())
{
for (FriendDecl const* FD : D->friends())
{
Expand Down
8 changes: 7 additions & 1 deletion src/lib/AST/ASTVisitor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,7 @@ class ASTVisitor
std::vector<Polymorphic<TArg>>& result,
Range&& args)
{
std::size_t i = 0;
for (TemplateArgument const& arg : args)
{
if (arg.getIsDefaulted())
Expand All @@ -699,8 +700,13 @@ class ASTVisitor
}
else
{
result.emplace_back(toTArg(arg));
if (i + 1 > result.size())
{
result.emplace_back();
}
result[i] = toTArg(arg);
}
++i;
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/lib/ConfigOptions.json
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,13 @@
"type": "bool",
"default": true
},
{
"name": "extract-friends",
"brief": "Extraction policy for friend functions and classes",
"details": "Determine whether friend functions and classes should be extracted. When set to `true`, MrDocs extracts friend functions and classes. When set to `false`, friend functions and classes are not extracted.",
"type": "bool",
"default": true
},
{
"name": "sort-members",
"brief": "Sort the members of a record or namespace",
Expand Down
105 changes: 105 additions & 0 deletions test-files/golden-tests/config/extract-friends/extract-friends.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
= Reference
:mrdocs:

[#index]
== Global namespace

=== Namespaces

[cols=1]
|===
| Name
| link:#std[`std`]
|===

=== Types

[cols=1]
|===
| Name
| link:#A[`A`]
|===

[#std]
== std

=== Types

[cols=1]
|===
| Name
| link:#std-hash-03[`hash`]
| link:#std-hash-08[`hash&lt;A&gt;`]
|===

[#std-hash-03]
== link:#std[std]::hash

=== Synopsis

Declared in `&lt;extract&hyphen;friends&period;cpp&gt;`

[source,cpp,subs="verbatim,replacements,macros,-callouts"]
----
template&lt;class T&gt;
class hash;
----

[#std-hash-08]
== link:#std[std]::link:#std-hash-03[hash]&lt;link:#A[A]&gt;

=== Synopsis

Declared in `&lt;extract&hyphen;friends&period;cpp&gt;`

[source,cpp,subs="verbatim,replacements,macros,-callouts"]
----
template&lt;&gt;
class link:#std-hash-03[hash]&lt;link:#A[A]&gt;;
----

=== Member Functions

[cols=1]
|===
| Name
| link:#std-hash-08-operator_call[`operator()`]
|===

[#std-hash-08-operator_call]
== link:#std[std]::link:#std-hash-08[hash&lt;A&gt;]::operator()

=== Synopsis

Declared in `&lt;extract&hyphen;friends&period;cpp&gt;`

[source,cpp,subs="verbatim,replacements,macros,-callouts"]
----
unsigned long long
operator()(link:#A[A] const& rhs) const noexcept;
----

[#A]
== A

=== Synopsis

Declared in `&lt;extract&hyphen;friends&period;cpp&gt;`

[source,cpp,subs="verbatim,replacements,macros,-callouts"]
----
class A;
----

=== Friends

[cols=2]
|===
| Name
| Description
| `link:#std-hash-08[std::hash&lt;A&gt;]`
|
|===


[.small]#Created with https://www.mrdocs.com[MrDocs]#
20 changes: 20 additions & 0 deletions test-files/golden-tests/config/extract-friends/extract-friends.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace std {
template <class T>
class hash;
}

class A {
friend std::hash<A>;
};

namespace std {
template <>
class hash<A> {
public:
unsigned long long
operator()(const A&) const noexcept
{
return 0;
}
};
}
Loading
Loading