https://github.com/ilovepi updated 
https://github.com/llvm/llvm-project/pull/137732

>From 2d9fbe622785e052e848290f52e2cbbb9994d5fc Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.c...@mail.utoronto.ca>
Date: Thu, 3 Apr 2025 17:07:01 -0400
Subject: [PATCH] [clang-doc] Add start and end line numbers

This patch adds start and end line numbers to clang-doc. Currently
clang-doc only encodes the start line numbers of records, struct, etc.
This patch adds start and end line number to clang-doc bitcode which is
passed to the generator. This will be used by the mustache backend to
generate line ranges.

Co-author: Paul Kirth <paulki...@google.com>
---
 clang-tools-extra/clang-doc/BitcodeReader.cpp |  6 +-
 clang-tools-extra/clang-doc/BitcodeWriter.cpp | 12 ++--
 clang-tools-extra/clang-doc/HTMLGenerator.cpp | 11 +--
 clang-tools-extra/clang-doc/MDGenerator.cpp   |  6 +-
 clang-tools-extra/clang-doc/Mapper.cpp        | 17 ++++-
 clang-tools-extra/clang-doc/Mapper.h          |  3 +
 clang-tools-extra/clang-doc/Representation.h  | 24 +++----
 clang-tools-extra/clang-doc/Serialize.cpp     | 67 ++++++++-----------
 clang-tools-extra/clang-doc/Serialize.h       | 28 ++++----
 clang-tools-extra/clang-doc/YAMLGenerator.cpp |  2 +-
 .../unittests/clang-doc/BitcodeTest.cpp       | 26 +++----
 .../unittests/clang-doc/ClangDocTest.cpp      |  4 +-
 .../unittests/clang-doc/HTMLGeneratorTest.cpp | 26 ++++---
 .../unittests/clang-doc/MDGeneratorTest.cpp   | 17 ++---
 .../unittests/clang-doc/MergeTest.cpp         | 36 +++++-----
 .../unittests/clang-doc/SerializeTest.cpp     | 61 +++++++++--------
 .../unittests/clang-doc/YAMLGeneratorTest.cpp | 16 ++---
 17 files changed, 189 insertions(+), 173 deletions(-)

diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp 
b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 92674c5507e74..f8e338eb7c6ed 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -79,7 +79,8 @@ static llvm::Error decodeRecord(const Record &R, 
std::optional<Location> &Field,
   if (R[0] > INT_MAX)
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                    "integer too large to parse");
-  Field.emplace(static_cast<int>(R[0]), Blob, static_cast<bool>(R[1]));
+  Field.emplace(static_cast<int>(R[0]), static_cast<int>(R[1]), Blob,
+                static_cast<bool>(R[2]));
   return llvm::Error::success();
 }
 
@@ -130,7 +131,8 @@ static llvm::Error decodeRecord(const Record &R,
   if (R[0] > INT_MAX)
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                    "integer too large to parse");
-  Field.emplace_back(static_cast<int>(R[0]), Blob, static_cast<bool>(R[1]));
+  Field.emplace_back(static_cast<int>(R[0]), static_cast<int>(R[1]), Blob,
+                     static_cast<bool>(R[2]));
   return llvm::Error::success();
 }
 
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp 
b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 6545629f1c739..f0a445e606bff 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -78,13 +78,16 @@ static void 
genLocationAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
       {// 0. Fixed-size integer (line number)
        llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
                              BitCodeConstants::LineNumberSize),
-       // 1. Boolean (IsFileInRootDir)
+       // 1. Fixed-size integer (start line number)
+       llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
+                             BitCodeConstants::LineNumberSize),
+       // 2. Boolean (IsFileInRootDir)
        llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
                              BitCodeConstants::BoolSize),
-       // 2. Fixed-size integer (length of the following string (filename))
+       // 3. Fixed-size integer (length of the following string (filename))
        llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
                              BitCodeConstants::StringLengthSize),
-       // 3. The string blob
+       // 4. The string blob
        llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)});
 }
 
@@ -357,7 +360,8 @@ void ClangDocBitcodeWriter::emitRecord(const Location &Loc, 
RecordId ID) {
   if (!prepRecordData(ID, true))
     return;
   // FIXME: Assert that the line number is of the appropriate size.
-  Record.push_back(Loc.LineNumber);
+  Record.push_back(Loc.StartLineNumber);
+  Record.push_back(Loc.EndLineNumber);
   assert(Loc.Filename.size() < (1U << BitCodeConstants::StringLengthSize));
   Record.push_back(Loc.IsFileInRootDir);
   Record.push_back(Loc.Filename.size());
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp 
b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index 6d061b33a67c1..4ad5ba29b28b8 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -455,7 +455,7 @@ static std::unique_ptr<TagNode> writeSourceFileRef(const 
ClangDocContext &CDCtx,
 
   if (!L.IsFileInRootDir && !CDCtx.RepositoryUrl)
     return std::make_unique<TagNode>(
-        HTMLTag::TAG_P, "Defined at line " + std::to_string(L.LineNumber) +
+        HTMLTag::TAG_P, "Defined at line " + std::to_string(L.StartLineNumber) 
+
                             " of file " + L.Filename);
 
   SmallString<128> FileURL(CDCtx.RepositoryUrl.value_or(""));
@@ -472,13 +472,14 @@ static std::unique_ptr<TagNode> writeSourceFileRef(const 
ClangDocContext &CDCtx,
           llvm::sys::path::Style::windows));
   auto Node = std::make_unique<TagNode>(HTMLTag::TAG_P);
   Node->Children.emplace_back(std::make_unique<TextNode>("Defined at line "));
-  auto LocNumberNode =
-      std::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.LineNumber));
+  auto LocNumberNode = std::make_unique<TagNode>(
+      HTMLTag::TAG_A, std::to_string(L.StartLineNumber));
   // The links to a specific line in the source code use the github /
   // googlesource notation so it won't work for all hosting pages.
   LocNumberNode->Attributes.emplace_back(
-      "href", formatv("{0}#{1}{2}", FileURL,
-                      CDCtx.RepositoryLinePrefix.value_or(""), L.LineNumber));
+      "href",
+      formatv("{0}#{1}{2}", FileURL, CDCtx.RepositoryLinePrefix.value_or(""),
+              L.StartLineNumber));
   Node->Children.emplace_back(std::move(LocNumberNode));
   Node->Children.emplace_back(std::make_unique<TextNode>(" of file "));
   auto LocFileNode = std::make_unique<TagNode>(
diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp 
b/clang-tools-extra/clang-doc/MDGenerator.cpp
index 43f1d7ba2f117..ccd6175c96cb8 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -56,12 +56,12 @@ static void writeSourceFileRef(const ClangDocContext 
&CDCtx, const Location &L,
                                raw_ostream &OS) {
 
   if (!CDCtx.RepositoryUrl) {
-    OS << "*Defined at " << L.Filename << "#" << std::to_string(L.LineNumber)
-       << "*";
+    OS << "*Defined at " << L.Filename << "#"
+       << std::to_string(L.StartLineNumber) << "*";
   } else {
 
     OS << formatv("*Defined at [#{0}{1}{2}](#{0}{1}{3})*",
-                  CDCtx.RepositoryLinePrefix.value_or(""), L.LineNumber,
+                  CDCtx.RepositoryLinePrefix.value_or(""), L.StartLineNumber,
                   L.Filename, *CDCtx.RepositoryUrl);
   }
   OS << "\n\n";
diff --git a/clang-tools-extra/clang-doc/Mapper.cpp 
b/clang-tools-extra/clang-doc/Mapper.cpp
index 3ccf026958c00..a17645c3f3a31 100644
--- a/clang-tools-extra/clang-doc/Mapper.cpp
+++ b/clang-tools-extra/clang-doc/Mapper.cpp
@@ -28,6 +28,17 @@ template <typename T> static bool isTypedefAnonRecord(const 
T *D) {
   return false;
 }
 
+Location MapASTVisitor::getDeclLocation(const NamedDecl *D) const {
+  bool IsFileInRootDir;
+  llvm::SmallString<128> File =
+      getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
+  SourceManager &SM = D->getASTContext().getSourceManager();
+  int Start = SM.getPresumedLoc(D->getBeginLoc()).getLine();
+  int End = SM.getPresumedLoc(D->getEndLoc()).getLine();
+
+  return Location(Start, End, File, IsFileInRootDir);
+}
+
 void MapASTVisitor::HandleTranslationUnit(ASTContext &Context) {
   TraverseDecl(Context.getTranslationUnitDecl());
 }
@@ -59,9 +70,9 @@ bool MapASTVisitor::mapDecl(const T *D, bool IsDefinition) {
   bool IsFileInRootDir;
   llvm::SmallString<128> File =
       getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
-  auto [Child, Parent] = serialize::emitInfo(
-      D, getComment(D, D->getASTContext()), getLine(D, D->getASTContext()),
-      File, IsFileInRootDir, CDCtx.PublicOnly);
+  auto [Child, Parent] =
+      serialize::emitInfo(D, getComment(D, D->getASTContext()),
+                          getDeclLocation(D), CDCtx.PublicOnly);
 
   // A null in place of a valid Info indicates that the serializer is skipping
   // this decl for some reason (e.g. we're only reporting public decls).
diff --git a/clang-tools-extra/clang-doc/Mapper.h 
b/clang-tools-extra/clang-doc/Mapper.h
index 75c8e947c8f90..36322ea2bfb77 100644
--- a/clang-tools-extra/clang-doc/Mapper.h
+++ b/clang-tools-extra/clang-doc/Mapper.h
@@ -46,6 +46,9 @@ class MapASTVisitor : public 
clang::RecursiveASTVisitor<MapASTVisitor>,
   template <typename T> bool mapDecl(const T *D, bool IsDefinition);
 
   int getLine(const NamedDecl *D, const ASTContext &Context) const;
+
+  Location getDeclLocation(const NamedDecl *D) const;
+
   llvm::SmallString<128> getFile(const NamedDecl *D, const ASTContext &Context,
                                  StringRef RootDir,
                                  bool &IsFileInRootDir) const;
diff --git a/clang-tools-extra/clang-doc/Representation.h 
b/clang-tools-extra/clang-doc/Representation.h
index d7f2453fe11b2..b0b34fc6f08dd 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -241,31 +241,29 @@ struct MemberTypeInfo : public FieldTypeInfo {
 };
 
 struct Location {
-  Location(int LineNumber = 0, StringRef Filename = StringRef(),
-           bool IsFileInRootDir = false)
-      : LineNumber(LineNumber), Filename(Filename),
-        IsFileInRootDir(IsFileInRootDir) {}
+  Location(int StartLineNumber = 0, int EndLineNumber = 0,
+           StringRef Filename = StringRef(), bool IsFileInRootDir = false)
+      : StartLineNumber(StartLineNumber), EndLineNumber(EndLineNumber),
+        Filename(Filename), IsFileInRootDir(IsFileInRootDir) {}
 
   bool operator==(const Location &Other) const {
-    return std::tie(LineNumber, Filename) ==
-           std::tie(Other.LineNumber, Other.Filename);
+    return std::tie(StartLineNumber, EndLineNumber, Filename) ==
+           std::tie(Other.StartLineNumber, Other.EndLineNumber, 
Other.Filename);
   }
 
-  bool operator!=(const Location &Other) const {
-    return std::tie(LineNumber, Filename) !=
-           std::tie(Other.LineNumber, Other.Filename);
-  }
+  bool operator!=(const Location &Other) const { return !(*this == Other); }
 
   // This operator is used to sort a vector of Locations.
   // No specific order (attributes more important than others) is required. Any
   // sort is enough, the order is only needed to call std::unique after sorting
   // the vector.
   bool operator<(const Location &Other) const {
-    return std::tie(LineNumber, Filename) <
-           std::tie(Other.LineNumber, Other.Filename);
+    return std::tie(StartLineNumber, EndLineNumber, Filename) <
+           std::tie(Other.StartLineNumber, Other.EndLineNumber, 
Other.Filename);
   }
 
-  int LineNumber = 0;           // Line number of this Location.
+  int StartLineNumber = 0; // Line number of this Location.
+  int EndLineNumber = 0;
   SmallString<32> Filename;     // File for this Location.
   bool IsFileInRootDir = false; // Indicates if file is inside root directory
 };
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp 
b/clang-tools-extra/clang-doc/Serialize.cpp
index 8ddc34b48dd8a..18db427b5239e 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -535,22 +535,18 @@ static void populateInfo(Info &I, const T *D, const 
FullComment *C,
 
 template <typename T>
 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
-                               int LineNumber, StringRef Filename,
-                               bool IsFileInRootDir,
-                               bool &IsInAnonymousNamespace) {
+                               Location Loc, bool &IsInAnonymousNamespace) {
   populateInfo(I, D, C, IsInAnonymousNamespace);
   if (D->isThisDeclarationADefinition())
-    I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
+    I.DefLoc = Loc;
   else
-    I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
+    I.Loc.emplace_back(Loc);
 }
 
 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
-                                 const FullComment *FC, int LineNumber,
-                                 StringRef Filename, bool IsFileInRootDir,
+                                 const FullComment *FC, Location Loc,
                                  bool &IsInAnonymousNamespace) {
-  populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
-                     IsInAnonymousNamespace);
+  populateSymbolInfo(I, D, FC, Loc, IsInAnonymousNamespace);
   auto &LO = D->getLangOpts();
   I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);
   parseParameters(I, D);
@@ -579,7 +575,7 @@ static void populateFunctionInfo(FunctionInfo &I, const 
FunctionDecl *D,
 static void populateMemberTypeInfo(MemberTypeInfo &I, const Decl *D) {
   assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
 
-  ASTContext& Context = D->getASTContext();
+  ASTContext &Context = D->getASTContext();
   // TODO investigate whether we can use ASTContext::getCommentForDecl instead
   // of this logic. See also similar code in Mapper.cpp.
   RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
@@ -643,8 +639,7 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool 
IsFileInRootDir,
             // reference, its value is not relevant in here so it's not used
             // anywhere besides the function call.
             bool IsInAnonymousNamespace;
-            populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},
-                                 /*FileName=*/{}, IsFileInRootDir,
+            populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*Location=*/{},
                                  IsInAnonymousNamespace);
             FI.Access =
                 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
@@ -662,8 +657,8 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool 
IsFileInRootDir,
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
-         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly) {
   auto NSI = std::make_unique<NamespaceInfo>();
   bool IsInAnonymousNamespace = false;
   populateInfo(*NSI, D, FC, IsInAnonymousNamespace);
@@ -683,12 +678,11 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, 
int LineNumber,
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
-         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly) {
   auto RI = std::make_unique<RecordInfo>();
   bool IsInAnonymousNamespace = false;
-  populateSymbolInfo(*RI, D, FC, LineNumber, File, IsFileInRootDir,
-                     IsInAnonymousNamespace);
+  populateSymbolInfo(*RI, D, FC, Loc, IsInAnonymousNamespace);
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
 
@@ -701,7 +695,7 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int 
LineNumber,
     }
     // TODO: remove first call to parseBases, that function should be deleted
     parseBases(*RI, C);
-    parseBases(*RI, C, IsFileInRootDir, PublicOnly, true);
+    parseBases(*RI, C, /*IsFileInRootDir=*/true, PublicOnly, 
/*IsParent=*/true);
   }
   RI->Path = getInfoRelativePath(RI->Namespace);
 
@@ -750,12 +744,11 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int 
LineNumber,
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
-         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const FunctionDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly) {
   FunctionInfo Func;
   bool IsInAnonymousNamespace = false;
-  populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
-                       IsInAnonymousNamespace);
+  populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
   Func.Access = clang::AccessSpecifier::AS_none;
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
@@ -765,12 +758,11 @@ emitInfo(const FunctionDecl *D, const FullComment *FC, 
int LineNumber,
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
-         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly) {
   FunctionInfo Func;
   bool IsInAnonymousNamespace = false;
-  populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
-                       IsInAnonymousNamespace);
+  populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
 
@@ -795,16 +787,15 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, 
int LineNumber,
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly) {
   TypedefInfo Info;
-
   bool IsInAnonymousNamespace = false;
   populateInfo(Info, D, FC, IsInAnonymousNamespace);
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
 
-  Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
+  Info.DefLoc = Loc;
   auto &LO = D->getLangOpts();
   Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
   if (Info.Underlying.Type.Name.empty()) {
@@ -822,8 +813,8 @@ emitInfo(const TypedefDecl *D, const FullComment *FC, int 
LineNumber,
 // A type alias is a C++ "using" declaration for a type. It gets mapped to a
 // TypedefInfo with the IsUsing flag set.
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const TypeAliasDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly) {
   TypedefInfo Info;
 
   bool IsInAnonymousNamespace = false;
@@ -831,7 +822,7 @@ emitInfo(const TypeAliasDecl *D, const FullComment *FC, int 
LineNumber,
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
 
-  Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
+  Info.DefLoc = Loc;
   auto &LO = D->getLangOpts();
   Info.Underlying = getTypeInfoForType(D->getUnderlyingType(), LO);
   Info.IsUsing = true;
@@ -841,12 +832,12 @@ emitInfo(const TypeAliasDecl *D, const FullComment *FC, 
int LineNumber,
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
-         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly) {
   EnumInfo Enum;
   bool IsInAnonymousNamespace = false;
-  populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
-                     IsInAnonymousNamespace);
+  populateSymbolInfo(Enum, D, FC, Loc, IsInAnonymousNamespace);
+
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
 
diff --git a/clang-tools-extra/clang-doc/Serialize.h 
b/clang-tools-extra/clang-doc/Serialize.h
index 41946796f39f6..7e6cbb70721ec 100644
--- a/clang-tools-extra/clang-doc/Serialize.h
+++ b/clang-tools-extra/clang-doc/Serialize.h
@@ -37,36 +37,36 @@ namespace serialize {
 // its parent scope. For NamespaceDecl and RecordDecl both elements are not
 // nullptr.
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const FunctionDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
 emitInfo(const VarDecl *D, const FullComment *FC, int LineNumber,
          StringRef File, bool IsFileInRootDir, bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const TypeAliasDecl *D, const FullComment *FC, Location Loc,
+         bool PublicOnly);
 
 // Function to hash a given USR value for storage.
 // As USRs (Unified Symbol Resolution) could be large, especially for functions
diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp 
b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
index 1b741f52ea5e3..8c110b34e8e20 100644
--- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
@@ -169,7 +169,7 @@ static void commentInfoMapping(IO &IO, CommentInfo &I) {
 
 template <> struct MappingTraits<Location> {
   static void mapping(IO &IO, Location &Loc) {
-    IO.mapOptional("LineNumber", Loc.LineNumber, 0);
+    IO.mapOptional("LineNumber", Loc.StartLineNumber, 0);
     IO.mapOptional("Filename", Loc.Filename, SmallString<32>());
   }
 };
diff --git a/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp 
b/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp
index 5b9745af7810a..4f2466af9a6bd 100644
--- a/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/BitcodeTest.cpp
@@ -25,7 +25,7 @@ template <typename T> static std::string writeInfo(T &I) {
   return Buffer.str().str();
 }
 
-std::string writeInfo(Info *I) {
+static std::string writeInfo(Info *I) {
   switch (I->IT) {
   case InfoType::IT_namespace:
     return writeInfo(*static_cast<NamespaceInfo *>(I));
@@ -42,8 +42,8 @@ std::string writeInfo(Info *I) {
   }
 }
 
-std::vector<std::unique_ptr<Info>> readInfo(StringRef Bitcode,
-                                            size_t NumInfos) {
+static std::vector<std::unique_ptr<Info>> readInfo(StringRef Bitcode,
+                                                   size_t NumInfos) {
   llvm::BitstreamCursor Stream(Bitcode);
   doc::ClangDocBitcodeReader Reader(Stream);
   auto Infos = Reader.readBitcode();
@@ -77,8 +77,8 @@ TEST(BitcodeTest, emitRecordInfoBitcode) {
   I.Name = "r";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp");
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   I.Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_private);
   I.TagType = TagTypeKind::Class;
@@ -119,8 +119,8 @@ TEST(BitcodeTest, emitFunctionInfoBitcode) {
   I.Name = "f";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp");
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   I.ReturnType = TypeInfo("void");
   I.Params.emplace_back(TypeInfo("int"), "P");
@@ -139,8 +139,8 @@ TEST(BitcodeTest, emitMethodInfoBitcode) {
   I.Name = "f";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp");
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   I.ReturnType = TypeInfo("void");
   I.Params.emplace_back(TypeInfo("int"), "P");
@@ -161,8 +161,8 @@ TEST(BitcodeTest, emitEnumInfoBitcode) {
   I.Name = "e";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp");
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   I.Members.emplace_back("X");
   I.Scoped = true;
@@ -179,7 +179,7 @@ TEST(BitcodeTest, emitTypedefInfoBitcode) {
   I.Name = "MyInt";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp");
   I.Underlying = TypeInfo("unsigned");
   I.IsUsing = true;
 
@@ -216,7 +216,7 @@ TEST(SerializeTest, emitInfoWithCommentBitcode) {
   FunctionInfo F;
   F.Name = "F";
   F.ReturnType = TypeInfo("void");
-  F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  F.DefLoc = Location(0, 0, "test.cpp");
   F.Params.emplace_back(TypeInfo("int"), "I");
 
   CommentInfo Top;
diff --git a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp 
b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
index 6e28aff09f9d9..f408c91077f6b 100644
--- a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
@@ -118,7 +118,9 @@ void CheckSymbolInfo(SymbolInfo *Expected, SymbolInfo 
*Actual) {
   CheckBaseInfo(Expected, Actual);
   EXPECT_EQ(Expected->DefLoc.has_value(), Actual->DefLoc.has_value());
   if (Expected->DefLoc && Actual->DefLoc.has_value()) {
-    EXPECT_EQ(Expected->DefLoc->LineNumber, Actual->DefLoc->LineNumber);
+    EXPECT_EQ(Expected->DefLoc->StartLineNumber,
+              Actual->DefLoc->StartLineNumber);
+    EXPECT_EQ(Expected->DefLoc->EndLineNumber, Actual->DefLoc->EndLineNumber);
     EXPECT_EQ(Expected->DefLoc->Filename, Actual->DefLoc->Filename);
   }
   ASSERT_EQ(Expected->Loc.size(), Actual->Loc.size());
diff --git a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp 
b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
index e440e11c07fdb..edf502475b511 100644
--- a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
@@ -9,7 +9,6 @@
 #include "ClangDocTest.h"
 #include "Generators.h"
 #include "Representation.h"
-#include "Serialize.h"
 #include "clang/Basic/Version.h"
 #include "gtest/gtest.h"
 
@@ -19,14 +18,14 @@ namespace doc {
 static const std::string ClangDocVersion =
     clang::getClangToolFullVersion("clang-doc");
 
-std::unique_ptr<Generator> getHTMLGenerator() {
+static std::unique_ptr<Generator> getHTMLGenerator() {
   auto G = doc::findGeneratorByName("html");
   if (!G)
     return nullptr;
   return std::move(G.get());
 }
 
-ClangDocContext
+static ClangDocContext
 getClangDocContext(std::vector<std::string> UserStylesheets = {},
                    StringRef RepositoryUrl = "",
                    StringRef RepositoryLinePrefix = "", StringRef Base = "") {
@@ -157,8 +156,8 @@ TEST(HTMLGeneratorTest, emitRecordHTML) {
   I.Path = "X/Y/Z";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, true);
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "dir/test.cpp", true);
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   SmallString<16> PathTo;
   llvm::sys::path::native("path/to", PathTo);
@@ -284,8 +283,8 @@ TEST(HTMLGeneratorTest, emitFunctionHTML) {
   I.Name = "f";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"dir/test.cpp"}, false);
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "dir/test.cpp", true);
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   I.Access = AccessSpecifier::AS_none;
 
@@ -344,8 +343,8 @@ TEST(HTMLGeneratorTest, emitEnumHTML) {
   I.Name = "e";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}, true);
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp", true);
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   I.Members.emplace_back("X");
   I.Scoped = true;
@@ -401,7 +400,7 @@ TEST(HTMLGeneratorTest, emitEnumHTML) {
 TEST(HTMLGeneratorTest, emitCommentHTML) {
   FunctionInfo I;
   I.Name = "f";
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp", true);
   I.ReturnType = TypeInfo("void");
   I.Params.emplace_back(TypeInfo("int"), "I");
   I.Params.emplace_back(TypeInfo("int"), "J");
@@ -464,7 +463,12 @@ TEST(HTMLGeneratorTest, emitCommentHTML) {
   <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
     <h3 id="0000000000000000000000000000000000000000">f</h3>
     <p>void f(int I, int J)</p>
-    <p>Defined at line 10 of file test.cpp</p>
+    <p>
+      Defined at line 
+      <a href="test.cpp#10">10</a>
+       of file 
+      <a href="test.cpp">test.cpp</a>
+    </p>
     <div>
       <div>
         <p> Brief description.</p>
diff --git a/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp 
b/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
index d3567efd82283..1795ef5a46c3a 100644
--- a/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
@@ -14,7 +14,7 @@
 namespace clang {
 namespace doc {
 
-std::unique_ptr<Generator> getMDGenerator() {
+static std::unique_ptr<Generator> getMDGenerator() {
   auto G = doc::findGeneratorByName("md");
   if (!G)
     return nullptr;
@@ -82,8 +82,8 @@ TEST(MDGeneratorTest, emitRecordMD) {
   I.Name = "r";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp");
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   I.Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_private);
   I.TagType = TagTypeKind::Class;
@@ -149,8 +149,8 @@ TEST(MDGeneratorTest, emitFunctionMD) {
   I.Name = "f";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp");
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   I.Access = AccessSpecifier::AS_none;
 
@@ -181,8 +181,8 @@ TEST(MDGeneratorTest, emitEnumMD) {
   I.Name = "e";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp");
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   I.Members.emplace_back("X");
   I.Scoped = true;
@@ -210,7 +210,8 @@ TEST(MDGeneratorTest, emitEnumMD) {
 TEST(MDGeneratorTest, emitCommentMD) {
   FunctionInfo I;
   I.Name = "f";
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+
+  I.DefLoc = Location(10, 10, "test.cpp");
   I.ReturnType = TypeInfo("void");
   I.Params.emplace_back(TypeInfo("int"), "I");
   I.Params.emplace_back(TypeInfo("int"), "J");
diff --git a/clang-tools-extra/unittests/clang-doc/MergeTest.cpp 
b/clang-tools-extra/unittests/clang-doc/MergeTest.cpp
index 9aa76524194b3..f99748fdf347a 100644
--- a/clang-tools-extra/unittests/clang-doc/MergeTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/MergeTest.cpp
@@ -81,7 +81,7 @@ TEST(MergeTest, mergeRecordInfos) {
   One.IsTypeDef = true;
   One.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  One.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+  One.DefLoc = Location(10, 10, "test.cpp");
 
   One.Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_private);
   One.TagType = TagTypeKind::Class;
@@ -103,7 +103,7 @@ TEST(MergeTest, mergeRecordInfos) {
   Two.Name = "r";
   Two.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  Two.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  Two.Loc.emplace_back(12, 12, "test.cpp");
 
   Two.TagType = TagTypeKind::Class;
 
@@ -123,8 +123,8 @@ TEST(MergeTest, mergeRecordInfos) {
   Expected->IsTypeDef = true;
   Expected->Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  Expected->DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  Expected->Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  Expected->DefLoc = Location(10, 10, "test.cpp");
+  Expected->Loc.emplace_back(12, 12, "test.cpp");
 
   Expected->Members.emplace_back(TypeInfo("int"), "X",
                                  AccessSpecifier::AS_private);
@@ -158,14 +158,14 @@ TEST(MergeTest, mergeFunctionInfos) {
   One.Name = "f";
   One.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  One.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  One.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  One.DefLoc = Location(10, 10, "test.cpp");
+  One.Loc.emplace_back(12, 12, "test.cpp");
 
   One.IsMethod = true;
   One.Parent = Reference(EmptySID, "Parent", InfoType::IT_namespace);
 
   One.Description.emplace_back();
-  auto OneFullComment = &One.Description.back();
+  auto *OneFullComment = &One.Description.back();
   OneFullComment->Kind = "FullComment";
   auto OneParagraphComment = std::make_unique<CommentInfo>();
   OneParagraphComment->Kind = "ParagraphComment";
@@ -179,13 +179,13 @@ TEST(MergeTest, mergeFunctionInfos) {
   Two.Name = "f";
   Two.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  Two.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  Two.Loc.emplace_back(12, 12, "test.cpp");
 
   Two.ReturnType = TypeInfo("void");
   Two.Params.emplace_back(TypeInfo("int"), "P");
 
   Two.Description.emplace_back();
-  auto TwoFullComment = &Two.Description.back();
+  auto *TwoFullComment = &Two.Description.back();
   TwoFullComment->Kind = "FullComment";
   auto TwoParagraphComment = std::make_unique<CommentInfo>();
   TwoParagraphComment->Kind = "ParagraphComment";
@@ -203,8 +203,8 @@ TEST(MergeTest, mergeFunctionInfos) {
   Expected->Name = "f";
   Expected->Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  Expected->DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  Expected->Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  Expected->DefLoc = Location(10, 10, "test.cpp");
+  Expected->Loc.emplace_back(12, 12, "test.cpp");
 
   Expected->ReturnType = TypeInfo("void");
   Expected->Params.emplace_back(TypeInfo("int"), "P");
@@ -212,7 +212,7 @@ TEST(MergeTest, mergeFunctionInfos) {
   Expected->Parent = Reference(EmptySID, "Parent", InfoType::IT_namespace);
 
   Expected->Description.emplace_back();
-  auto ExpectedFullComment = &Expected->Description.back();
+  auto *ExpectedFullComment = &Expected->Description.back();
   ExpectedFullComment->Kind = "FullComment";
   auto ExpectedParagraphComment = std::make_unique<CommentInfo>();
   ExpectedParagraphComment->Kind = "ParagraphComment";
@@ -233,8 +233,8 @@ TEST(MergeTest, mergeEnumInfos) {
   One.Name = "e";
   One.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  One.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  One.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  One.DefLoc = Location(10, 10, "test.cpp");
+  One.Loc.emplace_back(12, 12, "test.cpp");
 
   One.Scoped = true;
 
@@ -242,7 +242,7 @@ TEST(MergeTest, mergeEnumInfos) {
   Two.Name = "e";
   Two.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  Two.Loc.emplace_back(20, llvm::SmallString<16>{"test.cpp"});
+  Two.Loc.emplace_back(20, 20, "test.cpp");
 
   Two.Members.emplace_back("X");
   Two.Members.emplace_back("Y");
@@ -255,9 +255,9 @@ TEST(MergeTest, mergeEnumInfos) {
   Expected->Name = "e";
   Expected->Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  Expected->DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  Expected->Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
-  Expected->Loc.emplace_back(20, llvm::SmallString<16>{"test.cpp"});
+  Expected->DefLoc = Location(10, 10, "test.cpp");
+  Expected->Loc.emplace_back(12, 12, "test.cpp");
+  Expected->Loc.emplace_back(20, 20, "test.cpp");
 
   Expected->Members.emplace_back("X");
   Expected->Members.emplace_back("Y");
diff --git a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp 
b/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
index e6168418b58fa..6d5e7da689c5c 100644
--- a/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/SerializeTest.cpp
@@ -36,12 +36,12 @@ class ClangDocSerializeTestVisitor
       : EmittedInfos(EmittedInfos), Public(Public) {}
 
   template <typename T> bool mapDecl(const T *D) {
-    auto I = serialize::emitInfo(D, getComment(D), /*Line=*/0,
-                                 /*File=*/"test.cpp", true, Public);
-    if (I.first)
-      EmittedInfos.emplace_back(std::move(I.first));
-    if (I.second)
-      EmittedInfos.emplace_back(std::move(I.second));
+    Location Loc(0, 0, "test.cpp");
+    auto [Child, Parent] = serialize::emitInfo(D, getComment(D), Loc, Public);
+    if (Child)
+      EmittedInfos.emplace_back(std::move(Child));
+    if (Parent)
+      EmittedInfos.emplace_back(std::move(Parent));
     return true;
   }
 
@@ -124,7 +124,7 @@ TEST(SerializeTest, emitNamespaceInfo) {
   FunctionInfo F;
   F.Name = "f";
   F.ReturnType = TypeInfo("void");
-  F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  F.DefLoc = Location(0, 0, "test.cpp");
   F.Namespace.emplace_back(EmptySID, "B", InfoType::IT_namespace);
   F.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
   F.Access = AccessSpecifier::AS_none;
@@ -168,7 +168,7 @@ typedef struct {} G;)raw",
   ExpectedE.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                    InfoType::IT_namespace);
   ExpectedE.TagType = TagTypeKind::Class;
-  ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedE.DefLoc = Location(0, 0, "test.cpp");
   ExpectedE.Members.emplace_back(TypeInfo("int"), "value",
                                  AccessSpecifier::AS_public);
   // TODO the data member should have the docstring on it:
@@ -181,7 +181,7 @@ typedef struct {} G;)raw",
   EConstructor.Name = "E";
   EConstructor.Parent = Reference(EmptySID, "E", InfoType::IT_record);
   EConstructor.ReturnType = TypeInfo("void");
-  EConstructor.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  EConstructor.DefLoc = Location(0, 0, "test.cpp");
   EConstructor.Namespace.emplace_back(EmptySID, "E", InfoType::IT_record);
   EConstructor.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                       InfoType::IT_namespace);
@@ -197,7 +197,7 @@ typedef struct {} G;)raw",
   Method.Name = "ProtectedMethod";
   Method.Parent = Reference(EmptySID, "E", InfoType::IT_record);
   Method.ReturnType = TypeInfo("void");
-  Method.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"});
+  Method.Loc.emplace_back(0, 0, "test.cpp");
   Method.Namespace.emplace_back(EmptySID, "E", InfoType::IT_record);
   Method.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                 InfoType::IT_namespace);
@@ -211,7 +211,7 @@ typedef struct {} G;)raw",
   ExpectedF.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                    InfoType::IT_namespace);
   ExpectedF.TagType = TagTypeKind::Struct;
-  ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedF.DefLoc = Location(0, 0, "test.cpp");
   CheckRecordInfo(&ExpectedF, F);
 
   RecordInfo *RecordWithTemplateMethod = InfoAsRecord(Infos[6].get());
@@ -220,7 +220,7 @@ typedef struct {} G;)raw",
   TemplateMethod.Name = "TemplateMethod";
   TemplateMethod.Parent = Reference(EmptySID, "F", InfoType::IT_record);
   TemplateMethod.ReturnType = TypeInfo("void");
-  TemplateMethod.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"});
+  TemplateMethod.Loc.emplace_back(0, 0, "test.cpp");
   TemplateMethod.Namespace.emplace_back(EmptySID, "F", InfoType::IT_record);
   TemplateMethod.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                         InfoType::IT_namespace);
@@ -237,8 +237,7 @@ typedef struct {} G;)raw",
   SpecializedTemplateMethod.Parent =
       Reference(EmptySID, "F", InfoType::IT_record);
   SpecializedTemplateMethod.ReturnType = TypeInfo("void");
-  SpecializedTemplateMethod.Loc.emplace_back(0,
-                                             
llvm::SmallString<16>{"test.cpp"});
+  SpecializedTemplateMethod.Loc.emplace_back(0, 0, "test.cpp");
   SpecializedTemplateMethod.Namespace.emplace_back(EmptySID, "F",
                                                    InfoType::IT_record);
   SpecializedTemplateMethod.Namespace.emplace_back(EmptySID, "GlobalNamespace",
@@ -254,7 +253,7 @@ typedef struct {} G;)raw",
   ExpectedG.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                    InfoType::IT_namespace);
   ExpectedG.TagType = TagTypeKind::Struct;
-  ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedG.DefLoc = Location(0, 0, "test.cpp");
   ExpectedG.IsTypeDef = true;
   CheckRecordInfo(&ExpectedG, G);
 }
@@ -269,7 +268,7 @@ TEST(SerializeTest, emitEnumInfo) {
   NamespaceInfo ExpectedNamespaceWithEnum(EmptySID);
   EnumInfo E;
   E.Name = "E";
-  E.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  E.DefLoc = Location(0, 0, "test.cpp");
   E.Members.emplace_back("X", "0");
   E.Members.emplace_back("Y", "1");
   ExpectedNamespaceWithEnum.Children.Enums.emplace_back(std::move(E));
@@ -280,7 +279,7 @@ TEST(SerializeTest, emitEnumInfo) {
   EnumInfo G;
   G.Name = "G";
   G.Scoped = true;
-  G.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  G.DefLoc = Location(0, 0, "test.cpp");
   G.Members.emplace_back("A", "0");
   G.Members.emplace_back("B", "1");
   ExpectedNamespaceWithScopedEnum.Children.Enums.emplace_back(std::move(G));
@@ -296,7 +295,7 @@ TEST(SerializeTest, emitUndefinedRecordInfo) {
   ExpectedE.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                    InfoType::IT_namespace);
   ExpectedE.TagType = TagTypeKind::Class;
-  ExpectedE.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedE.Loc.emplace_back(0, 0, "test.cpp");
   CheckRecordInfo(&ExpectedE, E);
 }
 
@@ -309,7 +308,7 @@ TEST(SerializeTest, emitRecordMemberInfo) {
   ExpectedE.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                    InfoType::IT_namespace);
   ExpectedE.TagType = TagTypeKind::Struct;
-  ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedE.DefLoc = Location(0, 0, "test.cpp");
   ExpectedE.Members.emplace_back(TypeInfo("int"), "I",
                                  AccessSpecifier::AS_public);
   CheckRecordInfo(&ExpectedE, E);
@@ -323,7 +322,7 @@ TEST(SerializeTest, emitInternalRecordInfo) {
   RecordInfo ExpectedE(EmptySID, /*Name=*/"E", /*Path=*/"GlobalNamespace");
   ExpectedE.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                    InfoType::IT_namespace);
-  ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedE.DefLoc = Location(0, 0, "test.cpp");
   ExpectedE.TagType = TagTypeKind::Class;
   CheckRecordInfo(&ExpectedE, E);
 
@@ -331,7 +330,7 @@ TEST(SerializeTest, emitInternalRecordInfo) {
   llvm::SmallString<128> ExpectedGPath("GlobalNamespace/E");
   llvm::sys::path::native(ExpectedGPath);
   RecordInfo ExpectedG(EmptySID, /*Name=*/"G", /*Path=*/ExpectedGPath);
-  ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedG.DefLoc = Location(0, 0, "test.cpp");
   ExpectedG.TagType = TagTypeKind::Class;
   ExpectedG.Namespace.emplace_back(EmptySID, "E", InfoType::IT_record);
   ExpectedG.Namespace.emplace_back(EmptySID, "GlobalNamespace",
@@ -354,7 +353,7 @@ TEST(SerializeTest, emitPublicFunctionInternalInfo) {
   FunctionInfo F;
   F.Name = "F";
   F.ReturnType = TypeInfo("int");
-  F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  F.DefLoc = Location(0, 0, "test.cpp");
   F.Access = AccessSpecifier::AS_none;
   ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F));
   CheckNamespaceInfo(&ExpectedBWithFunction, BWithFunction);
@@ -369,7 +368,7 @@ TEST(SerializeTest, emitInlinedFunctionInfo) {
   FunctionInfo F;
   F.Name = "F";
   F.ReturnType = TypeInfo("void");
-  F.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  F.DefLoc = Location(0, 0, "test.cpp");
   F.Params.emplace_back(TypeInfo("int"), "I");
   F.Access = AccessSpecifier::AS_none;
   ExpectedBWithFunction.Children.Functions.emplace_back(std::move(F));
@@ -392,7 +391,7 @@ class J : public I<int> {} ;)raw",
   ExpectedF.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                    InfoType::IT_namespace, "");
   ExpectedF.TagType = TagTypeKind::Class;
-  ExpectedF.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedF.DefLoc = Location(0, 0, "test.cpp");
   CheckRecordInfo(&ExpectedF, F);
 
   RecordInfo *G = InfoAsRecord(Infos[3].get());
@@ -400,7 +399,7 @@ class J : public I<int> {} ;)raw",
   ExpectedG.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                    InfoType::IT_namespace);
   ExpectedG.TagType = TagTypeKind::Class;
-  ExpectedG.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedG.DefLoc = Location(0, 0, "test.cpp");
   ExpectedG.Members.emplace_back(TypeInfo("int"), "I",
                                  AccessSpecifier::AS_protected);
   CheckRecordInfo(&ExpectedG, G);
@@ -445,7 +444,7 @@ class J : public I<int> {} ;)raw",
       std::move(FunctionGet));
   ExpectedE.Bases.back().Members.emplace_back(TypeInfo("int"), "I",
                                               AccessSpecifier::AS_private);
-  ExpectedE.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedE.DefLoc = Location(0, 0, "test.cpp");
   ExpectedE.TagType = TagTypeKind::Class;
   CheckRecordInfo(&ExpectedE, E);
 
@@ -454,7 +453,7 @@ class J : public I<int> {} ;)raw",
   ExpectedH.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                    InfoType::IT_namespace);
   ExpectedH.TagType = TagTypeKind::Class;
-  ExpectedH.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedH.DefLoc = Location(0, 0, "test.cpp");
   ExpectedH.Parents.emplace_back(EmptySID, /*Name=*/"E", InfoType::IT_record,
                                  /*QualName=*/"E", /*Path=*/"GlobalNamespace");
   ExpectedH.VirtualParents.emplace_back(EmptySID, /*Name=*/"G",
@@ -501,7 +500,7 @@ class J : public I<int> {} ;)raw",
   ExpectedI.Namespace.emplace_back(EmptySID, "GlobalNamespace",
                                    InfoType::IT_namespace);
   ExpectedI.TagType = TagTypeKind::Class;
-  ExpectedI.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedI.DefLoc = Location(0, 0, "test.cpp");
   CheckRecordInfo(&ExpectedI, I);
 
   RecordInfo *J = InfoAsRecord(Infos[12].get());
@@ -513,7 +512,7 @@ class J : public I<int> {} ;)raw",
   ExpectedJ.Bases.emplace_back(EmptySID, /*Name=*/"I<int>",
                                /*Path=*/"GlobalNamespace", false,
                                AccessSpecifier::AS_public, true);
-  ExpectedJ.DefLoc = Location(0, llvm::SmallString<16>{"test.cpp"});
+  ExpectedJ.DefLoc = Location(0, 0, "test.cpp");
   ExpectedJ.TagType = TagTypeKind::Class;
   CheckRecordInfo(&ExpectedJ, J);
 }
@@ -533,7 +532,7 @@ export double exportedModuleFunction(double y);)raw",
   FunctionInfo F;
   F.Name = "moduleFunction";
   F.ReturnType = TypeInfo("int");
-  F.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"});
+  F.Loc.emplace_back(0, 0, "test.cpp");
   F.Params.emplace_back(TypeInfo("int"), "x");
   F.Params.emplace_back(TypeInfo("double"), "d");
   F.Params.back().DefaultValue = "3.2 - 1.0";
@@ -547,7 +546,7 @@ export double exportedModuleFunction(double y);)raw",
   ExportedF.Name = "exportedModuleFunction";
   ExportedF.ReturnType =
       TypeInfo(Reference(EmptySID, "double", InfoType::IT_default));
-  ExportedF.Loc.emplace_back(0, llvm::SmallString<16>{"test.cpp"});
+  ExportedF.Loc.emplace_back(0, 0, "test.cpp");
   ExportedF.Params.emplace_back(TypeInfo("double"), "y");
   ExportedF.Access = AccessSpecifier::AS_none;
   ExpectedBWithExportedFunction.Children.Functions.emplace_back(
diff --git a/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp 
b/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
index 9b1d87dfa0135..407a503e2d43a 100644
--- a/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp
@@ -15,7 +15,7 @@
 namespace clang {
 namespace doc {
 
-std::unique_ptr<Generator> getYAMLGenerator() {
+static std::unique_ptr<Generator> getYAMLGenerator() {
   auto G = doc::findGeneratorByName("yaml");
   if (!G)
     return nullptr;
@@ -84,8 +84,8 @@ TEST(YAMLGeneratorTest, emitRecordYAML) {
   I.IsTypeDef = true;
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp");
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   I.Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_private);
 
@@ -207,8 +207,8 @@ TEST(YAMLGeneratorTest, emitFunctionYAML) {
   I.Name = "f";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp");
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   I.Access = AccessSpecifier::AS_none;
 
@@ -272,8 +272,8 @@ TEST(YAMLGeneratorTest, emitSimpleEnumYAML) {
   I.Name = "e";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
-  I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp");
+  I.Loc.emplace_back(12, 12, "test.cpp");
 
   I.Members.emplace_back("X");
   I.Scoped = false;
@@ -368,7 +368,7 @@ IsUsing:         true
 TEST(YAMLGeneratorTest, emitCommentYAML) {
   FunctionInfo I;
   I.Name = "f";
-  I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+  I.DefLoc = Location(10, 10, "test.cpp");
   I.ReturnType = TypeInfo("void");
   I.Params.emplace_back(TypeInfo("int"), "I");
   I.Params.emplace_back(TypeInfo("int"), "J");

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to