Skip to content

Commit 99bf927

Browse files
committed
extract-empty-namespaces option
#feat
1 parent 11df305 commit 99bf927

File tree

290 files changed

+6129
-2327
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

290 files changed

+6129
-2327
lines changed

docs/mrdocs.schema.json

+11-1
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,24 @@
9393
},
9494
"extract-anonymous-namespaces": {
9595
"default": true,
96-
"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.",
96+
"description": "Determine whether symbols in anonymous namespaces should be extracted.",
9797
"enum": [
9898
true,
9999
false
100100
],
101101
"title": "Extraction policy for anonymous namespaces",
102102
"type": "boolean"
103103
},
104+
"extract-empty-namespaces": {
105+
"default": false,
106+
"description": "Determine whether empty namespaces without documentation should be extracted.",
107+
"enum": [
108+
true,
109+
false
110+
],
111+
"title": "Extraction policy for empty namespaces",
112+
"type": "boolean"
113+
},
104114
"extract-local-classes": {
105115
"default": true,
106116
"description": "Determine whether records only defined locally in source files should be extracted.",

mrdocs.rnc

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ grammar
228228
element aliased
229229
{
230230
attribute name { text },
231-
attribute id { text }
231+
attribute id { text } ?
232232
}
233233
}
234234

src/lib/AST/ParseRef.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,11 @@ class RefParser
385385
bool
386386
parseFunctionParameters()
387387
{
388+
// https://en.cppreference.com/w/cpp/language/function
389+
// parameter-list:
390+
// possibly empty, comma-separated list of the function parameters
391+
// (see below for details)
392+
388393
char const* start = ptr_;
389394
if (!parseLiteral('('))
390395
{
@@ -549,6 +554,21 @@ class RefParser
549554
bool
550555
parseMemberFunctionQualifiers()
551556
{
557+
// https://en.cppreference.com/w/cpp/language/function
558+
559+
// Parse cv:
560+
// const/volatile qualification, only allowed in non-static member
561+
// function declarations
562+
563+
// Parse ref:
564+
// ref-qualification, only allowed in non-static member function
565+
// declarations
566+
567+
// Parse except:
568+
// dynamic exception specification, dynamic exception specification
569+
// or noexcept specification, noexcept specification
570+
571+
552572
// Parse potential qualifiers at the end of a function
553573
// to identify the qualifiers to set
554574
// ReferenceKind Kind = ReferenceKind::None; and

src/lib/Lib/ConfigOptions.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,17 @@
223223
{
224224
"name": "extract-anonymous-namespaces",
225225
"brief": "Extraction policy for anonymous namespaces",
226-
"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.",
226+
"details": "Determine whether symbols in anonymous namespaces should be extracted.",
227227
"type": "bool",
228228
"default": true
229229
},
230+
{
231+
"name": "extract-empty-namespaces",
232+
"brief": "Extraction policy for empty namespaces",
233+
"details": "Determine whether empty namespaces without documentation should be extracted.",
234+
"type": "bool",
235+
"default": false
236+
},
230237
{
231238
"name": "inherit-base-members",
232239
"brief": "Determine how derived classes inherit base members",

src/lib/Lib/CorpusImpl.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "lib/Metadata/Finalizers/OverloadsFinalizer.hpp"
1818
#include "lib/Metadata/Finalizers/SortMembersFinalizer.hpp"
1919
#include "lib/Metadata/Finalizers/JavadocFinalizer.hpp"
20+
#include "lib/Metadata/Finalizers/NamespacesFinalizer.hpp"
2021
#include "lib/Support/Error.hpp"
2122
#include "lib/Support/Chrono.hpp"
2223
#include <mrdocs/Metadata.hpp>
@@ -592,6 +593,12 @@ qualifiedName(Info const& I, std::string& result) const
592593
void
593594
CorpusImpl::finalize()
594595
{
596+
{
597+
report::debug("Finalizing namespaces");
598+
NamespacesFinalizer finalizer(*this);
599+
finalizer.build();
600+
}
601+
595602
if (config->inheritBaseMembers != PublicSettings::BaseMemberInheritance::Never)
596603
{
597604
report::debug("Finalizing base members");

src/lib/Lib/CorpusImpl.hpp

+25
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class CorpusImpl final : public Corpus
6060
friend class OverloadsFinalizer;
6161
friend class SortMembersFinalizer;
6262
friend class JavadocFinalizer;
63+
friend class NamespacesFinalizer;
6364

6465
public:
6566
/** Constructor.
@@ -92,6 +93,30 @@ class CorpusImpl final : public Corpus
9293
Info const*
9394
find(SymbolID const& id) const noexcept override;
9495

96+
/** Return a range of Info objects for the specified Symbol IDs.
97+
*/
98+
template <range_of<SymbolID> R>
99+
auto
100+
find(R&& range)
101+
{
102+
return
103+
std::views::transform(
104+
range,
105+
[this](SymbolID const& id) -> Info*
106+
{
107+
return this->find(id);
108+
}) |
109+
std::views::filter([](Info const* info)
110+
{
111+
return info != nullptr;
112+
}) |
113+
std::views::transform([](Info* info) -> Info&
114+
{
115+
return *info;
116+
}) |
117+
std::views::common;
118+
}
119+
95120
Expected<std::reference_wrapper<Info const>>
96121
lookup(SymbolID const& context, std::string_view name) const override;
97122

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
//
2+
// Licensed under the Apache License v2.0 with LLVM Exceptions.
3+
// See https://llvm.org/LICENSE.txt for license information.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//
6+
// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com)
7+
//
8+
// Official repository: https://github.com/cppalliance/mrdocs
9+
//
10+
11+
#include "NamespacesFinalizer.hpp"
12+
13+
namespace clang::mrdocs {
14+
15+
NamespacesFinalizer::FinalizerResult
16+
NamespacesFinalizer::
17+
operator()(NamespaceInfo& I)
18+
{
19+
report::trace(
20+
"Finalizing namespace '{}'",
21+
corpus_.Corpus::qualifiedName(I));
22+
23+
// 1) Finalize sub-namespaces
24+
SmallVector<SymbolID, 64> removedNamespaces;
25+
for (auto subNamespaces = corpus_.find(I.Members.Namespaces);
26+
Info& info: subNamespaces)
27+
{
28+
MRDOCS_ASSERT(info.isNamespace());
29+
SymbolID memberID = info.id;
30+
auto& member = dynamic_cast<NamespaceInfo&>(info);
31+
if (auto const res = (*this)(member);
32+
res == FinalizerResult::Removed)
33+
{
34+
removedNamespaces.push_back(memberID);
35+
}
36+
}
37+
for (SymbolID const& memberID : removedNamespaces)
38+
{
39+
auto it = std::ranges::find(I.Members.Namespaces, memberID);
40+
MRDOCS_ASSERT(it != I.Members.Namespaces.end());
41+
I.Members.Namespaces.erase(it);
42+
}
43+
44+
// No more steps for the global namespace
45+
MRDOCS_CHECK_OR(I.id != SymbolID::global, FinalizerResult::None);
46+
47+
// No more steps for documented namespaces
48+
MRDOCS_CHECK_OR(!I.javadoc, FinalizerResult::None);
49+
50+
// 3) Remove empty undocumented namespaces
51+
auto memberIds = allMembers(I);
52+
if (std::ranges::empty(memberIds))
53+
{
54+
if (!corpus_.config->extractEmptyNamespaces)
55+
{
56+
auto const it = corpus_.info_.find(I.id);
57+
MRDOCS_CHECK_OR(it != corpus_.info_.end(), FinalizerResult::None);
58+
corpus_.info_.erase(it);
59+
return FinalizerResult::Removed;
60+
}
61+
return FinalizerResult::None;
62+
}
63+
64+
// 4) If the namespace is regular and undocumented,
65+
// it's extraction mode is updated according to
66+
// its members
67+
MRDOCS_CHECK_OR(I.Extraction == ExtractionMode::Regular, FinalizerResult::None);
68+
auto members = corpus_.find(memberIds);
69+
bool allDependencies = true;
70+
bool allImplementationDefined = true;
71+
bool anySeeBelow = false;
72+
for (Info const& member : members)
73+
{
74+
if (member.Extraction == ExtractionMode::Regular)
75+
{
76+
// It has a regular member, so it should remain
77+
// regular
78+
return FinalizerResult::None;
79+
}
80+
if (member.Extraction != ExtractionMode::Dependency)
81+
{
82+
allDependencies = false;
83+
}
84+
if (member.Extraction != ExtractionMode::ImplementationDefined)
85+
{
86+
allImplementationDefined = false;
87+
}
88+
if (member.Extraction == ExtractionMode::SeeBelow)
89+
{
90+
anySeeBelow = true;
91+
}
92+
}
93+
94+
if (allDependencies)
95+
{
96+
I.Extraction = ExtractionMode::Dependency;
97+
}
98+
else if (allImplementationDefined)
99+
{
100+
I.Extraction = ExtractionMode::ImplementationDefined;
101+
}
102+
else if (anySeeBelow)
103+
{
104+
I.Extraction = ExtractionMode::SeeBelow;
105+
}
106+
return FinalizerResult::Changed;
107+
}
108+
109+
} // clang::mrdocs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//
2+
// Licensed under the Apache License v2.0 with LLVM Exceptions.
3+
// See https://llvm.org/LICENSE.txt for license information.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//
6+
// Copyright (c) 2025 Alan de Freitas (alandefreitas@gmail.com)
7+
//
8+
// Official repository: https://github.com/cppalliance/mrdocs
9+
//
10+
11+
#ifndef MRDOCS_LIB_METADATA_FINALIZER_NAMESPACESFINALIZER_HPP
12+
#define MRDOCS_LIB_METADATA_FINALIZER_NAMESPACESFINALIZER_HPP
13+
14+
#include "lib/Lib/Info.hpp"
15+
#include "lib/Lib/CorpusImpl.hpp"
16+
17+
namespace clang::mrdocs {
18+
19+
/** Finalizes a set of Info.
20+
21+
This removes any references to SymbolIDs
22+
which do not exist.
23+
24+
References which should always be valid
25+
are not checked.
26+
*/
27+
class NamespacesFinalizer
28+
{
29+
CorpusImpl& corpus_;
30+
std::unordered_set<SymbolID> finalized_;
31+
32+
public:
33+
NamespacesFinalizer(
34+
CorpusImpl& corpus)
35+
: corpus_(corpus)
36+
{}
37+
38+
void
39+
build()
40+
{
41+
Info* info = corpus_.find(SymbolID::global);
42+
MRDOCS_CHECK_OR(info);
43+
operator()(*dynamic_cast<NamespaceInfo*>(info));
44+
}
45+
46+
enum class FinalizerResult {
47+
None,
48+
Removed,
49+
Changed
50+
};
51+
52+
FinalizerResult
53+
operator()(NamespaceInfo& I);
54+
};
55+
56+
} // clang::mrdocs
57+
58+
#endif

test-files/golden-tests/config/auto-brief/auto-brief.adoc

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
[cols=2]
1111
|===
12-
| Name | Description
12+
| Name
13+
| Description
1314

1415
| <<copyBriefFromCopyBrief,`copyBriefFromCopyBrief`>>
1516
| This is the explicit brief&period;

0 commit comments

Comments
 (0)