[clang-tools-extra] [clang-doc] Add Start and End Line Numbers (PR #135081)

via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 9 14:06:28 PDT 2025


https://github.com/PeterChou1 created https://github.com/llvm/llvm-project/pull/135081

Split from https://github.com/llvm/llvm-project/pull/133161

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 is will be used by the mustache backend to generate line ranges

>From 8fb896f16396c2e01427ac7baa8791461ff500a3 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 3 Apr 2025 17:07:01 -0400
Subject: [PATCH 1/2] [clang-doc] add start and end line number

---
 clang-tools-extra/clang-doc/BitcodeReader.cpp |  2 +-
 clang-tools-extra/clang-doc/BitcodeWriter.cpp |  9 +-
 clang-tools-extra/clang-doc/Mapper.cpp        | 17 +++-
 clang-tools-extra/clang-doc/Mapper.h          |  3 +
 clang-tools-extra/clang-doc/Representation.h  | 22 ++---
 clang-tools-extra/clang-doc/Serialize.cpp     | 85 ++++++++++---------
 6 files changed, 82 insertions(+), 56 deletions(-)

diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 1f2fb0a8b2b85..c9f588303fd08 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -85,7 +85,7 @@ 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((int)R[0], Blob, (bool)R[1]);
+  Field.emplace((int)R[0], (int) R[1], Blob, (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 06f30f76e33d8..8230097c0726d 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -77,13 +77,16 @@ static void LocationAbbrev(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)});
 }
 
diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp
index 6c90db03424c6..bdcf0c842b98f 100644
--- a/clang-tools-extra/clang-doc/Mapper.cpp
+++ b/clang-tools-extra/clang-doc/Mapper.cpp
@@ -28,6 +28,21 @@ template <typename T> 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);
+  ASTContext &Context = D->getASTContext();
+  int Start = Context.getSourceManager()
+                  .getPresumedLoc(D->getBeginLoc())
+                  .getLine();
+  int End = Context.getSourceManager()
+                .getPresumedLoc(D->getEndLoc())
+                .getLine();
+  
+  return Location(Start, End, File, IsFileInRootDir);
+}
+
 void MapASTVisitor::HandleTranslationUnit(ASTContext &Context) {
   TraverseDecl(Context.getTranslationUnitDecl());
 }
@@ -60,7 +75,7 @@ bool MapASTVisitor::mapDecl(const T *D, bool IsDefinition) {
   llvm::SmallString<128> File =
       getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
   auto I = serialize::emitInfo(D, getComment(D, D->getASTContext()),
-                               getLine(D, D->getASTContext()), File,
+                               getDeclLocation(D), File,
                                IsFileInRootDir, CDCtx.PublicOnly);
 
   // A null in place of I indicates that the serializer is skipping this decl
diff --git a/clang-tools-extra/clang-doc/Mapper.h b/clang-tools-extra/clang-doc/Mapper.h
index 75c8e947c8f90..3c3ebdb683911 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 2153b62864ee3..53a62edf3dbcf 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -238,19 +238,20 @@ struct MemberTypeInfo : public FieldTypeInfo {
 };
 
 struct Location {
-  Location(int LineNumber = 0, StringRef Filename = StringRef(),
+  Location(int StartLineNumber = 0, 
+           int EndLineNumber = 0, StringRef Filename = StringRef(),
            bool IsFileInRootDir = false)
-      : LineNumber(LineNumber), Filename(Filename),
-        IsFileInRootDir(IsFileInRootDir) {}
+      : 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);
+    return std::tie(StartLineNumber, Filename) !=
+           std::tie(Other.StartLineNumber, Other.Filename);
   }
 
   // This operator is used to sort a vector of Locations.
@@ -258,11 +259,12 @@ struct Location {
   // 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, Filename) <
+           std::tie(Other.StartLineNumber, 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 f737fc75135a1..aa3dff1d210b5 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -527,22 +527,20 @@ 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,
+                               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);
@@ -623,8 +621,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());
@@ -642,8 +639,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 I = std::make_unique<NamespaceInfo>();
   bool IsInAnonymousNamespace = false;
   populateInfo(*I, D, FC, IsInAnonymousNamespace);
@@ -663,12 +660,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 I = std::make_unique<RecordInfo>();
   bool IsInAnonymousNamespace = false;
-  populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
-                     IsInAnonymousNamespace);
+  populateSymbolInfo(*I, D, FC, Loc, IsInAnonymousNamespace);
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
 
@@ -681,7 +677,7 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
     }
     // TODO: remove first call to parseBases, that function should be deleted
     parseBases(*I, C);
-    parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
+    parseBases(*I, C, true, PublicOnly, true);
   }
   I->Path = getInfoRelativePath(I->Namespace);
 
@@ -730,30 +726,28 @@ 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 {};
-
+  
   // Info is wrapped in its parent scope so is returned in the second position.
   return {nullptr, MakeAndInsertIntoParent<FunctionInfo &&>(std::move(Func))};
 }
 
 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 {};
-
+  
   Func.IsMethod = true;
 
   const NamedDecl *Parent = nullptr;
@@ -774,18 +768,21 @@ 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;
-
+  ASTContext& Context = D->getASTContext();
   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()) {
     // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
     // The record serializer explicitly checks for this syntax and constructs
@@ -793,7 +790,13 @@ emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
     return {};
   }
   Info.IsUsing = false;
-
+  if (RawComment *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) {
+    Comment->setAttached();
+    if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
+      Info.Description.emplace_back();
+      parseFullComment(Fc, Info.Description.back());
+    }
+  }
   // Info is wrapped in its parent scope so is returned in the second position.
   return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
 }
@@ -801,8 +804,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;
@@ -810,7 +813,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;
@@ -820,15 +823,15 @@ 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 {};
-
+  
   Enum.Scoped = D->isScoped();
   if (D->isFixed()) {
     auto Name = D->getIntegerType().getAsString();

>From 7dd8ebe47b2a7669fdb9dbd2a5369b487831389f Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 7 Apr 2025 15:41:29 -0400
Subject: [PATCH 2/2] add number

---
 clang-tools-extra/clang-doc/Mapper.cpp        |  3 +-
 clang-tools-extra/clang-doc/Serialize.h       | 28 +++++++++----------
 clang-tools-extra/clang-doc/YAMLGenerator.cpp |  2 +-
 3 files changed, 16 insertions(+), 17 deletions(-)

diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp
index bdcf0c842b98f..4dadff41e96e8 100644
--- a/clang-tools-extra/clang-doc/Mapper.cpp
+++ b/clang-tools-extra/clang-doc/Mapper.cpp
@@ -75,8 +75,7 @@ bool MapASTVisitor::mapDecl(const T *D, bool IsDefinition) {
   llvm::SmallString<128> File =
       getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
   auto I = serialize::emitInfo(D, getComment(D, D->getASTContext()),
-                               getDeclLocation(D), File,
-                               IsFileInRootDir, CDCtx.PublicOnly);
+                               getDeclLocation(D), CDCtx.PublicOnly);
 
   // A null in place of I 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/Serialize.h b/clang-tools-extra/clang-doc/Serialize.h
index 4e203ca7891ac..8874299e9af9e 100644
--- a/clang-tools-extra/clang-doc/Serialize.h
+++ b/clang-tools-extra/clang-doc/Serialize.h
@@ -37,32 +37,32 @@ 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 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 ffabd2fd82229..d990dd4b647e8 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>());
   }
 };



More information about the cfe-commits mailing list