[clang-tools-extra] [clang-doc] Add a breadcrumb navigation bar (PR #173297)
Erick Velez via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 7 13:21:48 PST 2026
https://github.com/evelez7 updated https://github.com/llvm/llvm-project/pull/173297
>From aaed9901b7418e153c18e5428da65fdfaa6e3535 Mon Sep 17 00:00:00 2001
From: Erick Velez <erickvelez7 at gmail.com>
Date: Tue, 9 Dec 2025 10:10:38 -0800
Subject: [PATCH] final feedback
---
clang-tools-extra/clang-doc/BitcodeReader.cpp | 4 +
clang-tools-extra/clang-doc/BitcodeWriter.cpp | 8 +-
clang-tools-extra/clang-doc/BitcodeWriter.h | 2 +
clang-tools-extra/clang-doc/JSONGenerator.cpp | 91 ++++++++++++++++++-
.../clang-doc/Representation.cpp | 4 +
clang-tools-extra/clang-doc/Representation.h | 18 ++++
clang-tools-extra/clang-doc/Serialize.cpp | 36 ++++++++
.../clang-doc/assets/clang-doc-mustache.css | 42 +++++++--
.../clang-doc/assets/navbar-template.mustache | 7 ++
.../clang-doc/basic-project.mustache.test | 12 +++
.../test/clang-doc/json/class.cpp | 11 +++
.../test/clang-doc/json/nested-namespace.cpp | 6 +-
.../test/clang-doc/namespace.cpp | 32 ++++++-
13 files changed, 258 insertions(+), 15 deletions(-)
diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 76b5c6cefca6b..8c2f0041b340b 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -159,6 +159,8 @@ static llvm::Error parseRecord(const Record &R, unsigned ID,
return decodeRecord(R, I->Name, Blob);
case NAMESPACE_PATH:
return decodeRecord(R, I->Path, Blob);
+ case NAMESPACE_PARENT_USR:
+ return decodeRecord(R, I->ParentUSR, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for NamespaceInfo");
@@ -184,6 +186,8 @@ static llvm::Error parseRecord(const Record &R, unsigned ID,
return decodeRecord(R, I->IsTypeDef, Blob);
case RECORD_MANGLED_NAME:
return decodeRecord(R, I->MangledName, Blob);
+ case RECORD_PARENT_USR:
+ return decodeRecord(R, I->ParentUSR, Blob);
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"invalid field for RecordInfo");
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 8a7efd82bc75d..710ae5760162b 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -174,6 +174,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
{NAMESPACE_USR, {"USR", &genSymbolIdAbbrev}},
{NAMESPACE_NAME, {"Name", &genStringAbbrev}},
{NAMESPACE_PATH, {"Path", &genStringAbbrev}},
+ {NAMESPACE_PARENT_USR, {"ParentUSR", &genSymbolIdAbbrev}},
{ENUM_USR, {"USR", &genSymbolIdAbbrev}},
{ENUM_NAME, {"Name", &genStringAbbrev}},
{ENUM_DEFLOCATION, {"DefLocation", &genLocationAbbrev}},
@@ -190,6 +191,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
{RECORD_TAG_TYPE, {"TagType", &genIntAbbrev}},
{RECORD_IS_TYPE_DEF, {"IsTypeDef", &genBoolAbbrev}},
{RECORD_MANGLED_NAME, {"MangledName", &genStringAbbrev}},
+ {RECORD_PARENT_USR, {"ParentUSR", &genSymbolIdAbbrev}},
{BASE_RECORD_USR, {"USR", &genSymbolIdAbbrev}},
{BASE_RECORD_NAME, {"Name", &genStringAbbrev}},
{BASE_RECORD_PATH, {"Path", &genStringAbbrev}},
@@ -270,12 +272,12 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
{TYPEDEF_USR, TYPEDEF_NAME, TYPEDEF_DEFLOCATION, TYPEDEF_IS_USING}},
// Namespace Block
{BI_NAMESPACE_BLOCK_ID,
- {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH}},
+ {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH, NAMESPACE_PARENT_USR}},
// Record Block
{BI_RECORD_BLOCK_ID,
{RECORD_USR, RECORD_NAME, RECORD_PATH, RECORD_DEFLOCATION,
RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF,
- RECORD_MANGLED_NAME}},
+ RECORD_MANGLED_NAME, RECORD_PARENT_USR}},
// BaseRecord Block
{BI_BASE_RECORD_BLOCK_ID,
{BASE_RECORD_USR, BASE_RECORD_NAME, BASE_RECORD_PATH,
@@ -570,6 +572,7 @@ void ClangDocBitcodeWriter::emitBlock(const NamespaceInfo &I) {
emitRecord(I.USR, NAMESPACE_USR);
emitRecord(I.Name, NAMESPACE_NAME);
emitRecord(I.Path, NAMESPACE_PATH);
+ emitRecord(I.ParentUSR, NAMESPACE_PARENT_USR);
for (const auto &N : I.Namespace)
emitBlock(N, FieldId::F_namespace);
for (const auto &CI : I.Description)
@@ -624,6 +627,7 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
emitRecord(I.Name, RECORD_NAME);
emitRecord(I.Path, RECORD_PATH);
emitRecord(I.MangledName, RECORD_MANGLED_NAME);
+ emitRecord(I.ParentUSR, RECORD_PARENT_USR);
for (const auto &N : I.Namespace)
emitBlock(N, FieldId::F_namespace);
for (const auto &CI : I.Description)
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h
index 89c556fc310cb..30106ceb3c206 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.h
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -108,6 +108,7 @@ enum RecordId {
NAMESPACE_USR,
NAMESPACE_NAME,
NAMESPACE_PATH,
+ NAMESPACE_PARENT_USR,
ENUM_USR,
ENUM_NAME,
ENUM_DEFLOCATION,
@@ -124,6 +125,7 @@ enum RecordId {
RECORD_TAG_TYPE,
RECORD_IS_TYPE_DEF,
RECORD_MANGLED_NAME,
+ RECORD_PARENT_USR,
BASE_RECORD_USR,
BASE_RECORD_NAME,
BASE_RECORD_PATH,
diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index 86b8687c05cd8..2dbc4186a32cc 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -278,6 +278,64 @@ static Object serializeComment(const CommentInfo &I, Object &Description) {
llvm_unreachable("Unknown comment kind encountered.");
}
+/// Creates Contexts for namespaces and records to allow for navigation.
+static void generateContext(const Info &I, Object &Obj) {
+ json::Value ContextArray = json::Array();
+ auto &ContextArrayRef = *ContextArray.getAsArray();
+ ContextArrayRef.reserve(I.Contexts.size());
+
+ std::string CurrentRelativePath;
+ bool PreviousRecord = false;
+ for (const auto &Current : I.Contexts) {
+ json::Value ContextVal = Object();
+ Object &Context = *ContextVal.getAsObject();
+ serializeReference(Current, Context);
+
+ if (ContextArrayRef.empty() && I.IT == InfoType::IT_record) {
+ if (Current.DocumentationFileName == "index") {
+ // If the record's immediate context is a namespace, then the
+ // "index.html" is in the same directory.
+ PreviousRecord = false;
+ Context["RelativePath"] = "./";
+ } else {
+ // If the immediate context is a record, then the file is one level
+ // above
+ PreviousRecord = true;
+ CurrentRelativePath += "../";
+ Context["RelativePath"] = CurrentRelativePath;
+ }
+ ContextArrayRef.push_back(ContextVal);
+ continue;
+ }
+
+ if (PreviousRecord && (Current.DocumentationFileName == "index")) {
+ // If the previous Context was a record then we already went up a level,
+ // so the current namespace index is in the same directory.
+ PreviousRecord = false;
+ } else if (Current.DocumentationFileName != "index") {
+ // If the current Context is a record but the previous wasn't a record,
+ // then the namespace index is located one level above.
+ PreviousRecord = true;
+ CurrentRelativePath += "../";
+ } else {
+ // The current Context is a namespace and so was the previous Context.
+ PreviousRecord = false;
+ CurrentRelativePath += "../";
+ // If this namespace is the global namespace, then its documentation
+ // name needs to be changed to link correctly.
+ if (Current.QualName == "GlobalNamespace" && Current.RelativePath != "./")
+ Context["DocumentationFileName"] =
+ SmallString<16>("GlobalNamespace/index");
+ }
+ Context["RelativePath"] = CurrentRelativePath;
+ ContextArrayRef.insert(ContextArrayRef.begin(), ContextVal);
+ }
+
+ ContextArrayRef.back().getAsObject()->insert({"End", true});
+ Obj["Contexts"] = ContextArray;
+ Obj["HasContexts"] = true;
+}
+
static void
serializeCommonAttributes(const Info &I, json::Object &Obj,
const std::optional<StringRef> RepositoryUrl) {
@@ -323,6 +381,9 @@ serializeCommonAttributes(const Info &I, json::Object &Obj,
Obj["Location"] =
serializeLocation(Symbol->DefLoc.value(), RepositoryUrl);
}
+
+ if (!I.Contexts.empty())
+ generateContext(I, Obj);
}
static void serializeReference(const Reference &Ref, Object &ReferenceObj) {
@@ -335,7 +396,7 @@ static void serializeReference(const Reference &Ref, Object &ReferenceObj) {
// If the reference is a nested class it will be put into a folder named
// after the parent class. We can get that name from the path's stem.
- if (Ref.Path != "GlobalNamespace")
+ if (Ref.Path != "GlobalNamespace" && !Ref.Path.empty())
ReferenceObj["PathStem"] = sys::path::stem(Ref.Path);
}
}
@@ -730,6 +791,29 @@ static Error serializeIndex(const ClangDocContext &CDCtx, StringRef RootDir) {
return Error::success();
}
+static void serializeContexts(Info *I,
+ StringMap<std::unique_ptr<Info>> &Infos) {
+ if (I->USR == GlobalNamespaceID)
+ return;
+ auto ParentUSR = I->ParentUSR;
+
+ while (true) {
+ auto &ParentInfo = Infos.at(llvm::toHex(ParentUSR));
+
+ if (ParentInfo && ParentInfo->USR == GlobalNamespaceID) {
+ Context GlobalRef(ParentInfo->USR, "Global Namespace",
+ InfoType::IT_namespace, "GlobalNamespace", "",
+ SmallString<16>("index"));
+ I->Contexts.push_back(GlobalRef);
+ return;
+ }
+
+ Context ParentRef(*ParentInfo);
+ I->Contexts.push_back(ParentRef);
+ ParentUSR = ParentInfo->ParentUSR;
+ }
+}
+
Error JSONGenerator::generateDocumentation(
StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
const ClangDocContext &CDCtx, std::string DirName) {
@@ -763,9 +847,12 @@ Error JSONGenerator::generateDocumentation(
if (FileErr)
return createFileError("cannot open file " + Group.getKey(), FileErr);
- for (const auto &Info : Group.getValue())
+ for (const auto &Info : Group.getValue()) {
+ if (Info->IT == InfoType::IT_record || Info->IT == InfoType::IT_namespace)
+ serializeContexts(Info, Infos);
if (Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
return Err;
+ }
}
return serializeIndex(CDCtx, RootDir);
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index a0487c69b7378..a69129041516f 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -273,6 +273,10 @@ void Info::mergeBase(Info &&Other) {
llvm::sort(Description);
auto Last = llvm::unique(Description);
Description.erase(Last, Description.end());
+ if (ParentUSR == EmptySID)
+ ParentUSR = Other.ParentUSR;
+ if (DocumentationFileName.empty())
+ DocumentationFileName = Other.DocumentationFileName;
}
bool Info::mergeable(const Info &Other) {
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 7685d17bb033d..297e564ea7866 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -165,6 +165,16 @@ struct Reference {
SmallString<16> DocumentationFileName;
};
+// A Context is a reference that holds a relative path from a certain Info's
+// location.
+struct Context : public Reference {
+ Context(SymbolID USR, StringRef Name, InfoType IT, StringRef QualName,
+ StringRef Path, SmallString<16> DocumentationFileName)
+ : Reference(USR, Name, IT, QualName, Path, DocumentationFileName) {}
+ explicit Context(const Info &I);
+ SmallString<128> RelativePath;
+};
+
// Holds the children of a record or namespace.
struct ScopeChildren {
// Namespaces and Records are references because they will be properly
@@ -356,13 +366,21 @@ struct Info {
// Unique identifier for the decl described by this Info.
SymbolID USR = SymbolID();
+ // Currently only used for namespaces and records.
+ SymbolID ParentUSR = SymbolID();
+
// InfoType of this particular Info.
InfoType IT = InfoType::IT_default;
// Comment description of this decl.
std::vector<CommentInfo> Description;
+
+ SmallVector<Context, 4> Contexts;
};
+inline Context::Context(const Info &I)
+ : Reference(I.USR, I.Name, I.IT, I.Name, I.Path, I.DocumentationFileName) {}
+
// Info for namespaces.
struct NamespaceInfo : public Info {
NamespaceInfo(SymbolID USR = SymbolID(), StringRef Name = StringRef(),
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 776399d2b5a60..39c0cb378b0f5 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -697,10 +697,46 @@ static TemplateParamInfo convertTemplateArgToInfo(const clang::Decl *D,
return TemplateParamInfo(Str);
}
+// Check if the DeclKind is one for which we support contextual relationships.
+// There might be other ContextDecls, like blocks, that we currently don't
+// handle at all.
+static bool isSupportedContext(Decl::Kind DeclKind) {
+ switch (DeclKind) {
+ case Decl::Kind::Record:
+ case Decl::Kind::CXXRecord:
+ case Decl::Kind::ClassTemplateSpecialization:
+ case Decl::Kind::ClassTemplatePartialSpecialization:
+ case Decl::Kind::Namespace:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void findParent(Info &I, const Decl *D) {
+ assert(D && "Invalid Decl");
+
+ // Only walk up contexts if D is a record or namespace.
+ if (!isSupportedContext(D->getKind()))
+ return;
+
+ const DeclContext *ParentCtx = dyn_cast<DeclContext>(D)->getLexicalParent();
+ while (ParentCtx) {
+ if (isSupportedContext(ParentCtx->getDeclKind())) {
+ // Break when we reach the first record or namespace.
+ I.ParentUSR = getUSRForDecl(dyn_cast<Decl>(ParentCtx));
+ break;
+ }
+ ParentCtx = ParentCtx->getParent();
+ }
+}
+
template <typename T>
static void populateInfo(Info &I, const T *D, const FullComment *C,
bool &IsInAnonymousNamespace) {
I.USR = getUSRForDecl(D);
+ findParent(I, D);
+
if (auto ConversionDecl = dyn_cast_or_null<CXXConversionDecl>(D);
ConversionDecl && ConversionDecl->getConversionType()
.getTypePtr()
diff --git a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
index 3218031ce5c5a..1474a27a7d8a7 100644
--- a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -112,7 +112,6 @@ body, html {
width: 100%;
top: 0;
left: 0;
- height: 60px; /* Adjust as needed */
color: white;
display: flex;
align-items: center;
@@ -255,6 +254,38 @@ body, html {
color:var(--text1)
}
+.navbar-breadcrumb-container {
+ position: absolute;
+ top: 60px;
+ left: 0;
+ width: 100%;
+ background: var(--surface2);
+ padding: 0.5rem 1rem;
+ display: flex;
+ gap: 0.5rem;
+ border-bottom: 1px solid var(--text2);
+ box-sizing: border-box;
+ border-top: 1px solid var(--text2);
+ border-bottom: 1px solid var(--text2);
+}
+
+.navbar-breadcrumb-item {
+ padding: 0.25rem 0.75rem;
+ background: var(--surface1);
+ border: 1px solid var(--text2);
+ border-radius: 4px;
+ color: var(--text1);
+ font-size: 0.9rem;
+ white-space: nowrap;
+}
+
+.navbar-breadcrumb-item:hover {
+ background: var(--brand);
+ color: var(--text1-inverse);
+ border-color: var(--brand);
+ cursor: pointer;
+}
+
.hero__container {
margin-top:1rem;
display:flex;
@@ -317,9 +348,7 @@ body, html {
max-width: 2048px;
margin-left:auto;
margin-right:auto;
- margin-top:0;
margin-bottom: 1rem;
- padding:1rem 2rem
}
@media(max-width:768px) {
@@ -404,9 +433,9 @@ body, html {
.sidebar {
width: 250px;
- top: 60px;
left: 0;
- height: 100%;
+ top: 60px;
+ bottom: 0;
position: fixed;
background-color: var(--surface1);
display: flex;
@@ -414,6 +443,7 @@ body, html {
flex-direction: column;
overflow-y: auto;
scrollbar-width: thin;
+ flex-shrink: 0;
}
.sidebar h2 {
@@ -445,8 +475,8 @@ body, html {
/* Content */
.content {
+ top: 60px;
background-color: var(--text1-inverse);
- padding: 20px;
left: 250px;
position: relative;
width: calc(100% - 250px);
diff --git a/clang-tools-extra/clang-doc/assets/navbar-template.mustache b/clang-tools-extra/clang-doc/assets/navbar-template.mustache
index 2767d5af86668..8acbd55d705bd 100644
--- a/clang-tools-extra/clang-doc/assets/navbar-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/navbar-template.mustache
@@ -12,5 +12,12 @@
</li>
</ul>
</div>
+ {{#HasContexts}}
+ <div class="navbar-breadcrumb-container">
+ {{#Contexts}}
+ <a href="{{RelativePath}}{{DocumentationFileName}}.html"><div class="navbar-breadcrumb-item">{{Name}}</div></a>{{^End}}::{{/End}}
+ {{/Contexts}}
+ </div>
+ {{/HasContexts}}
</div>
</nav>
diff --git a/clang-tools-extra/test/clang-doc/basic-project.mustache.test b/clang-tools-extra/test/clang-doc/basic-project.mustache.test
index 2f4bcb4c0e12b..d1f3e910bb31f 100644
--- a/clang-tools-extra/test/clang-doc/basic-project.mustache.test
+++ b/clang-tools-extra/test/clang-doc/basic-project.mustache.test
@@ -29,6 +29,9 @@ HTML-SHAPE: <a href="../index.html" class="navbar__link">Hom
HTML-SHAPE: </li>
HTML-SHAPE: </ul>
HTML-SHAPE: </div>
+HTML-SHAPE: <div class="navbar-breadcrumb-container">
+HTML-SHAPE: <a href="./index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
+HTML-SHAPE: </div>
HTML-SHAPE: </div>
HTML-SHAPE: </nav>
HTML-SHAPE: <main>
@@ -136,6 +139,9 @@ HTML-CALC: <a href="../index.html" class="navbar__link">Home
HTML-CALC: </li>
HTML-CALC: </ul>
HTML-CALC: </div>
+HTML-CALC: <div class="navbar-breadcrumb-container">
+HTML-CALC: <a href="./index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
+HTML-CALC: </div>
HTML-CALC: </div>
HTML-CALC: </nav>
HTML-CALC: <main>
@@ -347,6 +353,9 @@ HTML-RECTANGLE: <a href="../index.html" class="navbar__link"
HTML-RECTANGLE: </li>
HTML-RECTANGLE: </ul>
HTML-RECTANGLE: </div>
+HTML-RECTANGLE: <div class="navbar-breadcrumb-container">
+HTML-RECTANGLE: <a href="./index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
+HTML-RECTANGLE: </div>
HTML-RECTANGLE: </div>
HTML-RECTANGLE: </nav>
HTML-RECTANGLE: <main>
@@ -458,6 +467,9 @@ HTML-CIRCLE: <a href="../index.html" class="navbar__link">Ho
HTML-CIRCLE: </li>
HTML-CIRCLE: </ul>
HTML-CIRCLE: </div>
+HTML-CIRCLE: <div class="navbar-breadcrumb-container">
+HTML-CIRCLE: <a href="./index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
+HTML-CIRCLE: </div>
HTML-CIRCLE: </div>
HTML-CIRCLE: </nav>
HTML-CIRCLE: <main>
diff --git a/clang-tools-extra/test/clang-doc/json/class.cpp b/clang-tools-extra/test/clang-doc/json/class.cpp
index e678b05fceb03..9b45f86ff3292 100644
--- a/clang-tools-extra/test/clang-doc/json/class.cpp
+++ b/clang-tools-extra/test/clang-doc/json/class.cpp
@@ -38,6 +38,16 @@ struct MyClass {
};
// CHECK: {
+// CHECK-NEXT: "Contexts": [
+// CHECK-NEXT: {
+// CHECK-NEXT: "DocumentationFileName": "index",
+// CHECK-NEXT: "End": true,
+// CHECK-NEXT: "Name": "Global Namespace",
+// CHECK-NEXT: "QualName": "GlobalNamespace",
+// CHECK-NEXT: "RelativePath": "./",
+// CHECK-NEXT: "USR": "0000000000000000000000000000000000000000"
+// CHECK-NEXT: }
+// CHECK-NEXT: ],
// CHECK-NEXT: "Description": {
// CHECK-NEXT: "BriefComments": [
// CHECK-NEXT: [
@@ -156,6 +166,7 @@ struct MyClass {
// CHECK-NEXT: "USR": "0000000000000000000000000000000000000000"
// CHECK-NEXT: }
// CHECK-NEXT: ],
+// CHECK-NEXT: "HasContexts": true,
// CHECK-NEXT: "HasEnums": true,
// CHECK-NEXT: "HasFriends": true,
// CHECK-NEXT: "HasPrivateMembers": true,
diff --git a/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp b/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp
index 5baca7f39b783..3ca63503a650c 100644
--- a/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp
+++ b/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp
@@ -1,5 +1,6 @@
// RUN: rm -rf %t && mkdir -p %t
// RUN: clang-doc --output=%t --format=json --executor=standalone %s
+// RUN: clang-doc --output=%t --format=html --executor=standalone %s
// RUN: FileCheck %s < %t/json/nested/index.json --check-prefix=NESTED
// RUN: FileCheck %s < %t/json/nested/inner/index.json --check-prefix=INNER
@@ -7,6 +8,7 @@ namespace nested {
int Global;
namespace inner {
int InnerGlobal;
+ namespace inner_inner {}
} // namespace inner
} // namespace nested
@@ -17,7 +19,7 @@ namespace nested {
// NESTED-NEXT: "IsStatic": false,
// NESTED-NEXT: "Location": {
// NESTED-NEXT: "Filename": "{{.*}}nested-namespace.cpp",
-// NESTED-NEXT: "LineNumber": 7
+// NESTED-NEXT: "LineNumber": 8
// NESTED-NEXT: },
// NESTED-NEXT: "Name": "Global",
// NESTED-NEXT: "Namespace": [
@@ -31,7 +33,7 @@ namespace nested {
// INNER-NEXT: "IsStatic": false,
// INNER-NEXT: "Location": {
// INNER-NEXT: "Filename": "{{.*}}nested-namespace.cpp",
-// INNER-NEXT: "LineNumber": 9
+// INNER-NEXT: "LineNumber": 10
// INNER-NEXT: },
// INNER-NEXT: "Name": "InnerGlobal",
// INNER-NEXT: "Namespace": [
diff --git a/clang-tools-extra/test/clang-doc/namespace.cpp b/clang-tools-extra/test/clang-doc/namespace.cpp
index d3808bd39cc70..a7c025fd7254e 100644
--- a/clang-tools-extra/test/clang-doc/namespace.cpp
+++ b/clang-tools-extra/test/clang-doc/namespace.cpp
@@ -63,6 +63,9 @@ class AnonClass {};
// MD-ANON-INDEX: ### anonFunction
// MD-ANON-INDEX: *void anonFunction()*
+// HTML-ANON-INDEX: <div class="navbar-breadcrumb-container">
+// HTML-ANON-INDEX: <a href="../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
+// HTML-ANON-INDEX: </div>
// HTML-ANON-INDEX: <h2>@nonymous_namespace</h2>
// HTML-ANON-INDEX: <h2>Inner Classes</h2>
// HTML-ANON-INDEX: <ul class="class-container">
@@ -90,6 +93,10 @@ class ClassInPrimaryNamespace {};
// MD-PRIMARY-CLASS: # class ClassInPrimaryNamespace
// MD-PRIMARY-CLASS: Class in PrimaryNamespace
+// HTML-PRIMARY-CLASS: <div class="navbar-breadcrumb-container">
+// HTML-PRIMARY-CLASS: <a href="../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>::
+// HTML-PRIMARY-CLASS: <a href="./index.html"><div class="navbar-breadcrumb-item">PrimaryNamespace</div></a>
+// HTML-PRIMARY-CLASS: </div>
// HTML-PRIMARY-CLASS: <h1 class="hero__title-large">class ClassInPrimaryNamespace</h1>
// Nested namespace
@@ -107,6 +114,11 @@ class ClassInNestedNamespace {};
// MD-NESTED-CLASS: # class ClassInNestedNamespace
// MD-NESTED-CLASS: Class in NestedNamespace
+// HTML-NESTED-CLASS: <div class="navbar-breadcrumb-container">
+// HTML-NESTED-CLASS: <a href="../../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>::
+// HTML-NESTED-CLASS: <a href="../index.html"><div class="navbar-breadcrumb-item">PrimaryNamespace</div></a>::
+// HTML-NESTED-CLASS: <a href="./index.html"><div class="navbar-breadcrumb-item">NestedNamespace</div></a>
+// HTML-NESTED-CLASS: </div>
// HTML-NESTED-CLASS: <h1 class="hero__title-large">class ClassInNestedNamespace</h1>
} // namespace NestedNamespace
@@ -119,6 +131,10 @@ class ClassInNestedNamespace {};
// MD-NESTED-INDEX: *void functionInNestedNamespace()*
// MD-NESTED-INDEX: Function in NestedNamespace
+// HTML-NESTED-INDEX: <div class="navbar-breadcrumb-container">
+// HTML-NESTED-INDEX: <a href="../../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>::
+// HTML-NESTED-INDEX: <a href="../index.html"><div class="navbar-breadcrumb-item">PrimaryNamespace</div></a>
+// HTML-NESTED-INDEX: </div>
// HTML-NESTED-INDEX: <h2>NestedNamespace</h2>
// HTML-NESTED-INDEX: <h2>Inner Classes</h2>
// HTML-NESTED-INDEX: <ul class="class-container">
@@ -134,7 +150,7 @@ class ClassInNestedNamespace {};
// HTML-NESTED-INDEX: <p> Function in NestedNamespace</p>
// HTML-NESTED-INDEX: </div>
// HTML-NESTED-INDEX: </div>
-// HTML-NESTED-INDEX: <p>Defined at line 98 of file {{.*}}namespace.cpp</p>
+// HTML-NESTED-INDEX: <p>Defined at line 105 of file {{.*}}namespace.cpp</p>
// HTML-NESTED-INDEX: </div>
} // namespace PrimaryNamespace
@@ -149,6 +165,9 @@ class ClassInNestedNamespace {};
// MD-PRIMARY-INDEX: *void functionInPrimaryNamespace()*
// MD-PRIMARY-INDEX: Function in PrimaryNamespace
+// HTML-PRIMARY-INDEX: <div class="navbar-breadcrumb-container">
+// HTML-PRIMARY-INDEX: <a href="../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
+// HTML-PRIMARY-INDEX: </div>
// HTML-PRIMARY-INDEX: <h2>PrimaryNamespace</h2>
// HTML-PRIMARY-INDEX-NOT: <h2 id="Namespaces">Namespaces</h2>
// HTML-PRIMARY-INDEX-NOT: <a href="NestedNamespace{{[\/]}}index.html">NestedNamespace</a>
@@ -166,7 +185,7 @@ class ClassInNestedNamespace {};
// HTML-PRIMARY-INDEX: <p> Function in PrimaryNamespace</p>
// HTML-PRIMARY-INDEX: </div>
// HTML-PRIMARY-INDEX: </div>
-// HTML-PRIMARY-INDEX: <p>Defined at line 81 of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
+// HTML-PRIMARY-INDEX: <p>Defined at line 84 of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
// HTML-PRIMARY-INDEX: </div>
// AnotherNamespace
namespace AnotherNamespace {
@@ -183,6 +202,10 @@ class ClassInAnotherNamespace {};
// MD-ANOTHER-CLASS: # class ClassInAnotherNamespace
// MD-ANOTHER-CLASS: Class in AnotherNamespace
+// HTML-ANOTHER-CLASS: <div class="navbar-breadcrumb-container">
+// HTML-ANOTHER-CLASS: <a href="../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>::
+// HTML-ANOTHER-CLASS: <a href="./index.html"><div class="navbar-breadcrumb-item">AnotherNamespace</div></a>
+// HTML-ANOTHER-CLASS: </div>
// HTML-ANOTHER-CLASS: <h1 class="hero__title-large">class ClassInAnotherNamespace</h1>
} // namespace AnotherNamespace
@@ -196,6 +219,9 @@ class ClassInAnotherNamespace {};
// MD-ANOTHER-INDEX: *void functionInAnotherNamespace()*
// MD-ANOTHER-INDEX: Function in AnotherNamespace
+// HTML-ANOTHER-INDEX: <div class="navbar-breadcrumb-container">
+// HTML-ANOTHER-INDEX: <a href="../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>
+// HTML-ANOTHER-INDEX: </div>
// HTML-ANOTHER-INDEX: <h2>AnotherNamespace</h2>
// HTML-ANOTHER-INDEX: <h2>Inner Classes</h2>
// HTML-ANOTHER-INDEX: <ul class="class-container">
@@ -211,7 +237,7 @@ class ClassInAnotherNamespace {};
// HTML-ANOTHER-INDEX: <p> Function in AnotherNamespace</p>
// HTML-ANOTHER-INDEX: </div>
// HTML-ANOTHER-INDEX: </div>
-// HTML-ANOTHER-INDEX: <p>Defined at line 174 of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
+// HTML-ANOTHER-INDEX: <p>Defined at line 193 of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
// HTML-ANOTHER-INDEX: </div>
// HTML-ANOTHER-INDEX: </div>
More information about the cfe-commits
mailing list