std::move(New.begin(), New.end(), std::back_inserter(Original));
}
-// Compute the relative path that names the file path relative to the given
-// directory.
-static SmallString<128> computeRelativePath(StringRef FilePath,
- StringRef Directory) {
- StringRef Path = FilePath;
- while (!Path.empty()) {
- if (Directory == Path)
- return FilePath.substr(Path.size());
- Path = llvm::sys::path::parent_path(Path);
- }
-
- StringRef Dir = Directory;
- SmallString<128> Result;
- while (!Dir.empty()) {
- if (Dir == FilePath)
- break;
- Dir = llvm::sys::path::parent_path(Dir);
+// Compute the relative path from an Origin directory to a Destination directory
+static SmallString<128> computeRelativePath(StringRef Destination,
+ StringRef Origin) {
+ // If Origin is empty, the relative path to the Destination is its complete
+ // path.
+ if (Origin.empty())
+ return Destination;
+
+ // The relative path is an empty path if both directories are the same.
+ if (Destination == Origin)
+ return {};
+
+ // These iterators iterate through each of their parent directories
+ llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
+ llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
+ llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
+ llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
+ // Advance both iterators until the paths differ. Example:
+ // Destination = A/B/C/D
+ // Origin = A/B/E/F
+ // FileI will point to C and DirI to E. The directories behind them is the
+ // directory they share (A/B).
+ while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
+ ++FileI;
+ ++DirI;
+ }
+ SmallString<128> Result; // This will hold the resulting path.
+ // Result has to go up one directory for each of the remaining directories in
+ // Origin
+ while (DirI != DirE) {
llvm::sys::path::append(Result, "..");
+ ++DirI;
+ }
+ // Result has to append each of the remaining directories in Destination
+ while (FileI != FileE) {
+ llvm::sys::path::append(Result, *FileI);
+ ++FileI;
}
- llvm::sys::path::append(Result, FilePath.substr(Dir.size()));
return Result;
}
}
static std::unique_ptr<HTMLNode>
-genTypeReference(const Reference &Type, StringRef CurrentDirectory,
- llvm::Optional<StringRef> JumpToSection = None) {
+genReference(const Reference &Type, StringRef CurrentDirectory,
+ llvm::Optional<StringRef> JumpToSection = None) {
if (Type.Path.empty() && !Type.IsInGlobalNamespace) {
if (!JumpToSection)
return llvm::make_unique<TextNode>(Type.Name);
for (const auto &R : Refs) {
if (&R != Refs.begin())
Out.emplace_back(llvm::make_unique<TextNode>(", "));
- Out.emplace_back(genTypeReference(R, CurrentDirectory));
+ Out.emplace_back(genReference(R, CurrentDirectory));
}
return Out;
}
Access = Access + " ";
auto LIBody = llvm::make_unique<TagNode>(HTMLTag::TAG_LI);
LIBody->Children.emplace_back(llvm::make_unique<TextNode>(Access));
- LIBody->Children.emplace_back(genTypeReference(M.Type, ParentInfoDir));
+ LIBody->Children.emplace_back(genReference(M.Type, ParentInfoDir));
LIBody->Children.emplace_back(llvm::make_unique<TextNode>(" " + M.Name));
ULBody->Children.emplace_back(std::move(LIBody));
}
static std::vector<std::unique_ptr<TagNode>>
genReferencesBlock(const std::vector<Reference> &References,
- llvm::StringRef Title) {
+ llvm::StringRef Title, StringRef ParentPath) {
if (References.empty())
return {};
Out.back()->Attributes.try_emplace("id", Title);
Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_UL));
auto &ULBody = Out.back();
- for (const auto &R : References)
- ULBody->Children.emplace_back(
- llvm::make_unique<TagNode>(HTMLTag::TAG_LI, R.Name));
+ for (const auto &R : References) {
+ auto LiNode = llvm::make_unique<TagNode>(HTMLTag::TAG_LI);
+ LiNode->Children.emplace_back(genReference(R, ParentPath));
+ ULBody->Children.emplace_back(std::move(LiNode));
+ }
return Out;
}
Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_SPAN));
auto &SpanBody = Out.back();
if (!Index.JumpToSection)
- SpanBody->Children.emplace_back(genTypeReference(Index, InfoPath));
+ SpanBody->Children.emplace_back(genReference(Index, InfoPath));
else
- SpanBody->Children.emplace_back(genTypeReference(
+ SpanBody->Children.emplace_back(genReference(
Index, InfoPath, StringRef{Index.JumpToSection.getValue()}));
}
if (Index.Children.empty())
llvm::make_unique<TextNode>(Access + " "));
if (I.ReturnType.Type.Name != "") {
FunctionHeader->Children.emplace_back(
- genTypeReference(I.ReturnType.Type, ParentInfoDir));
+ genReference(I.ReturnType.Type, ParentInfoDir));
FunctionHeader->Children.emplace_back(llvm::make_unique<TextNode>(" "));
}
FunctionHeader->Children.emplace_back(
for (const auto &P : I.Params) {
if (&P != I.Params.begin())
FunctionHeader->Children.emplace_back(llvm::make_unique<TextNode>(", "));
- FunctionHeader->Children.emplace_back(
- genTypeReference(P.Type, ParentInfoDir));
+ FunctionHeader->Children.emplace_back(genReference(P.Type, ParentInfoDir));
FunctionHeader->Children.emplace_back(
llvm::make_unique<TextNode>(" " + P.Name));
}
Out.emplace_back(genHTML(I.Description));
std::vector<std::unique_ptr<TagNode>> ChildNamespaces =
- genReferencesBlock(I.ChildNamespaces, "Namespaces");
+ genReferencesBlock(I.ChildNamespaces, "Namespaces", I.Path);
AppendVector(std::move(ChildNamespaces), Out);
std::vector<std::unique_ptr<TagNode>> ChildRecords =
- genReferencesBlock(I.ChildRecords, "Records");
+ genReferencesBlock(I.ChildRecords, "Records", I.Path);
AppendVector(std::move(ChildRecords), Out);
std::vector<std::unique_ptr<TagNode>> ChildFunctions =
genRecordMembersBlock(I.Members, I.Path);
AppendVector(std::move(Members), Out);
std::vector<std::unique_ptr<TagNode>> ChildRecords =
- genReferencesBlock(I.ChildRecords, "Records");
+ genReferencesBlock(I.ChildRecords, "Records", I.Path);
AppendVector(std::move(ChildRecords), Out);
std::vector<std::unique_ptr<TagNode>> ChildFunctions =
return -1;
}
-// For References, we don't need to actually merge them, we just don't want
-// duplicates.
void reduceChildren(std::vector<Reference> &Children,
std::vector<Reference> &&ChildrenToMerge) {
for (auto &ChildToMerge : ChildrenToMerge) {
- if (getChildIndexIfExists(Children, ChildToMerge) == -1)
+ int mergeIdx = getChildIndexIfExists(Children, ChildToMerge);
+ if (mergeIdx == -1) {
Children.push_back(std::move(ChildToMerge));
+ continue;
+ }
+ Children[mergeIdx].merge(std::move(ChildToMerge));
}
}
}
}
+bool Reference::mergeable(const Reference &Other) {
+ return RefType == Other.RefType && USR == Other.USR;
+}
+
+void Reference::merge(Reference &&Other) {
+ assert(mergeable(Other));
+ if (Name.empty())
+ Name = Other.Name;
+ if (Path.empty())
+ Path = Other.Path;
+ if (!IsInGlobalNamespace)
+ IsInGlobalNamespace = Other.IsInGlobalNamespace;
+}
+
void Info::mergeBase(Info &&Other) {
assert(mergeable(Other));
if (USR == EmptySID)
std::tie(Other.USR, Other.Name, Other.RefType);
}
+ bool mergeable(const Reference &Other);
+ void merge(Reference &&I);
+
SymbolID USR = SymbolID(); // Unique identifer for referenced decl
SmallString<16> Name; // Name of type (possibly unresolved).
InfoType RefType = InfoType::IT_default; // Indicates the type of this
auto ParentI = llvm::make_unique<NamespaceInfo>();
ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR;
- ParentI->ChildNamespaces.emplace_back(I->USR, I->Name,
- InfoType::IT_namespace);
+ ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace,
+ getInfoRelativePath(I->Namespace));
if (I->Namespace.empty())
ParentI->Path = getInfoRelativePath(ParentI->Namespace);
return {std::unique_ptr<Info>{std::move(I)},
if (I->Namespace.empty()) {
auto ParentI = llvm::make_unique<NamespaceInfo>();
ParentI->USR = SymbolID();
- ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record);
+ ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
+ getInfoRelativePath(I->Namespace));
ParentI->Path = getInfoRelativePath(ParentI->Namespace);
return {std::unique_ptr<Info>{std::move(I)},
std::unique_ptr<Info>{std::move(ParentI)}};
case InfoType::IT_namespace: {
auto ParentI = llvm::make_unique<NamespaceInfo>();
ParentI->USR = I->Namespace[0].USR;
- ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record);
+ ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
+ getInfoRelativePath(I->Namespace));
return {std::unique_ptr<Info>{std::move(I)},
std::unique_ptr<Info>{std::move(ParentI)}};
}
case InfoType::IT_record: {
auto ParentI = llvm::make_unique<RecordInfo>();
ParentI->USR = I->Namespace[0].USR;
- ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record);
+ ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record,
+ getInfoRelativePath(I->Namespace));
return {std::unique_ptr<Info>{std::move(I)},
std::unique_ptr<Info>{std::move(ParentI)}};
}
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
- InfoType::IT_namespace);
- I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
+ InfoType::IT_namespace, "Namespace");
+ I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
+ "Namespace");
I.ChildFunctions.emplace_back();
I.ChildFunctions.back().Name = "OneFunction";
I.ChildEnums.emplace_back();
<h1>namespace Namespace</h1>
<h2 id="Namespaces">Namespaces</h2>
<ul>
- <li>ChildNamespace</li>
+ <li>
+ <a href="Namespace/ChildNamespace.html">ChildNamespace</a>
+ </li>
</ul>
<h2 id="Records">Records</h2>
<ul>
- <li>ChildStruct</li>
+ <li>
+ <a href="Namespace/ChildStruct.html">ChildStruct</a>
+ </li>
</ul>
<h2 id="Functions">Functions</h2>
<div>
I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, PathTo);
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
- I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
+ I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
+ "X/Y/Z/r");
I.ChildFunctions.emplace_back();
I.ChildFunctions.back().Name = "OneFunction";
I.ChildEnums.emplace_back();
</ul>
<h2 id="Records">Records</h2>
<ul>
- <li>ChildStruct</li>
+ <li>
+ <a href="r/ChildStruct.html">ChildStruct</a>
+ </li>
</ul>
<h2 id="Functions">Functions</h2>
<div>
One.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
One.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
- One.ChildRecords.emplace_back(NonEmptySID, "ChildStruct",
+ One.ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct",
InfoType::IT_record);
One.ChildFunctions.emplace_back();
One.ChildFunctions.back().Name = "OneFunction";
Two.TagType = TagTypeKind::TTK_Class;
- Two.ChildRecords.emplace_back(EmptySID, "OtherChildStruct",
- InfoType::IT_record);
+ Two.ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct",
+ InfoType::IT_record, "path");
Two.ChildFunctions.emplace_back();
Two.ChildFunctions.back().Name = "TwoFunction";
Two.ChildEnums.emplace_back();
Expected->Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
Expected->VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
- Expected->ChildRecords.emplace_back(NonEmptySID, "ChildStruct",
- InfoType::IT_record);
- Expected->ChildRecords.emplace_back(EmptySID, "OtherChildStruct",
- InfoType::IT_record);
+ Expected->ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct",
+ InfoType::IT_record, "path");
Expected->ChildFunctions.emplace_back();
Expected->ChildFunctions.back().Name = "OneFunction";
Expected->ChildFunctions.back().USR = NonEmptySID;
RecordInfo *ParentB = InfoAsRecord(Infos[3].get());
RecordInfo ExpectedParentB(EmptySID);
- ExpectedParentB.ChildRecords.emplace_back(EmptySID, "B", InfoType::IT_record);
+ ExpectedParentB.ChildRecords.emplace_back(EmptySID, "B", InfoType::IT_record,
+ "A");
CheckRecordInfo(&ExpectedParentB, ParentB);
NamespaceInfo *ParentC = InfoAsNamespace(Infos[7].get());
NamespaceInfo ExpectedParentC(EmptySID);
- ExpectedParentC.ChildRecords.emplace_back(EmptySID, "C", InfoType::IT_record);
+ ExpectedParentC.ChildRecords.emplace_back(EmptySID, "C", InfoType::IT_record,
+ "@nonymous_namespace");
CheckNamespaceInfo(&ExpectedParentC, ParentC);
}
NamespaceInfo *ParentB = InfoAsNamespace(Infos[3].get());
NamespaceInfo ExpectedParentB(EmptySID);
ExpectedParentB.ChildNamespaces.emplace_back(EmptySID, "B",
- InfoType::IT_namespace);
+ InfoType::IT_namespace, "A");
CheckNamespaceInfo(&ExpectedParentB, ParentB);
}
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
- InfoType::IT_namespace);
- I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
+ InfoType::IT_namespace, "path/to/A/Namespace");
+ I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
+ "path/to/A/Namespace");
I.ChildFunctions.emplace_back();
I.ChildFunctions.back().Name = "OneFunction";
I.ChildEnums.emplace_back();
ChildNamespaces:
- Type: Namespace
Name: 'ChildNamespace'
+ Path: 'path/to/A/Namespace'
ChildRecords:
- Type: Record
Name: 'ChildStruct'
+ Path: 'path/to/A/Namespace'
ChildFunctions:
- USR: '0000000000000000000000000000000000000000'
Name: 'OneFunction'
TEST(YAMLGeneratorTest, emitRecordYAML) {
RecordInfo I;
I.Name = "r";
- I.Path = "path/to/r";
+ I.Path = "path/to/A";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record,
"path/to/G");
- I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
+ I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
+ "path/to/A/r");
I.ChildFunctions.emplace_back();
I.ChildFunctions.back().Name = "OneFunction";
I.ChildEnums.emplace_back();
R"raw(---
USR: '0000000000000000000000000000000000000000'
Name: 'r'
-Path: 'path/to/r'
+Path: 'path/to/A'
Namespace:
- Type: Namespace
Name: 'A'
ChildRecords:
- Type: Record
Name: 'ChildStruct'
+ Path: 'path/to/A/r'
ChildFunctions:
- USR: '0000000000000000000000000000000000000000'
Name: 'OneFunction'