[clang-doc] Serialize record files with mangled name (#148021)
This patch changes JSON file serialization. Now, files are serialized to a single directory instead of nesting them based on namespaces. The global namespace retains the "index.json" name. This solves the problem of class template specializations being serialized to the same file as its base template. This is also planned as part of future integration with the Mustache generator which will consume the JSON files.
This commit is contained in:
parent
136558bab2
commit
94bb9e12ec
@ -180,6 +180,8 @@ static llvm::Error parseRecord(const Record &R, unsigned ID,
|
||||
return decodeRecord(R, I->TagType, Blob);
|
||||
case RECORD_IS_TYPE_DEF:
|
||||
return decodeRecord(R, I->IsTypeDef, Blob);
|
||||
case RECORD_MANGLED_NAME:
|
||||
return decodeRecord(R, I->MangledName, Blob);
|
||||
default:
|
||||
return llvm::createStringError(llvm::inconvertibleErrorCode(),
|
||||
"invalid field for RecordInfo");
|
||||
|
@ -189,6 +189,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
|
||||
{RECORD_LOCATION, {"Location", &genLocationAbbrev}},
|
||||
{RECORD_TAG_TYPE, {"TagType", &genIntAbbrev}},
|
||||
{RECORD_IS_TYPE_DEF, {"IsTypeDef", &genBoolAbbrev}},
|
||||
{RECORD_MANGLED_NAME, {"MangledName", &genStringAbbrev}},
|
||||
{BASE_RECORD_USR, {"USR", &genSymbolIdAbbrev}},
|
||||
{BASE_RECORD_NAME, {"Name", &genStringAbbrev}},
|
||||
{BASE_RECORD_PATH, {"Path", &genStringAbbrev}},
|
||||
@ -271,7 +272,8 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
|
||||
// Record Block
|
||||
{BI_RECORD_BLOCK_ID,
|
||||
{RECORD_USR, RECORD_NAME, RECORD_PATH, RECORD_DEFLOCATION,
|
||||
RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF}},
|
||||
RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF,
|
||||
RECORD_MANGLED_NAME}},
|
||||
// BaseRecord Block
|
||||
{BI_BASE_RECORD_BLOCK_ID,
|
||||
{BASE_RECORD_USR, BASE_RECORD_NAME, BASE_RECORD_PATH,
|
||||
@ -616,6 +618,7 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
|
||||
emitRecord(I.USR, RECORD_USR);
|
||||
emitRecord(I.Name, RECORD_NAME);
|
||||
emitRecord(I.Path, RECORD_PATH);
|
||||
emitRecord(I.MangledName, RECORD_MANGLED_NAME);
|
||||
for (const auto &N : I.Namespace)
|
||||
emitBlock(N, FieldId::F_namespace);
|
||||
for (const auto &CI : I.Description)
|
||||
|
@ -126,6 +126,7 @@ enum RecordId {
|
||||
RECORD_LOCATION,
|
||||
RECORD_TAG_TYPE,
|
||||
RECORD_IS_TYPE_DEF,
|
||||
RECORD_MANGLED_NAME,
|
||||
BASE_RECORD_USR,
|
||||
BASE_RECORD_NAME,
|
||||
BASE_RECORD_PATH,
|
||||
|
@ -386,6 +386,7 @@ static void serializeInfo(const RecordInfo &I, json::Object &Obj,
|
||||
Obj["FullName"] = I.FullName;
|
||||
Obj["TagType"] = getTagType(I.TagType);
|
||||
Obj["IsTypedef"] = I.IsTypeDef;
|
||||
Obj["MangledName"] = I.MangledName;
|
||||
|
||||
if (!I.Children.Functions.empty()) {
|
||||
json::Value PubFunctionsArray = Array();
|
||||
@ -491,6 +492,23 @@ static void serializeInfo(const NamespaceInfo &I, json::Object &Obj,
|
||||
serializeCommonChildren(I.Children, Obj, RepositoryUrl);
|
||||
}
|
||||
|
||||
static SmallString<16> determineFileName(Info *I, SmallString<128> &Path) {
|
||||
SmallString<16> FileName;
|
||||
if (I->IT == InfoType::IT_record) {
|
||||
auto *RecordSymbolInfo = static_cast<SymbolInfo *>(I);
|
||||
if (RecordSymbolInfo->MangledName.size() < 255)
|
||||
FileName = RecordSymbolInfo->MangledName;
|
||||
else
|
||||
FileName = toStringRef(toHex(RecordSymbolInfo->USR));
|
||||
} else if (I->IT == InfoType::IT_namespace && I->Name != "")
|
||||
// Serialize the global namespace as index.json
|
||||
FileName = I->Name;
|
||||
else
|
||||
FileName = I->getFileBaseName();
|
||||
sys::path::append(Path, FileName + ".json");
|
||||
return FileName;
|
||||
}
|
||||
|
||||
Error JSONGenerator::generateDocs(
|
||||
StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
|
||||
const ClangDocContext &CDCtx) {
|
||||
@ -501,7 +519,6 @@ Error JSONGenerator::generateDocs(
|
||||
|
||||
SmallString<128> Path;
|
||||
sys::path::native(RootDir, Path);
|
||||
sys::path::append(Path, Info->getRelativeFilePath(""));
|
||||
if (!CreatedDirs.contains(Path)) {
|
||||
if (std::error_code Err = sys::fs::create_directories(Path);
|
||||
Err != std::error_code())
|
||||
@ -509,7 +526,7 @@ Error JSONGenerator::generateDocs(
|
||||
CreatedDirs.insert(Path);
|
||||
}
|
||||
|
||||
sys::path::append(Path, Info->getFileBaseName() + ".json");
|
||||
SmallString<16> FileName = determineFileName(Info, Path);
|
||||
FileToInfos[Path].push_back(Info);
|
||||
}
|
||||
|
||||
|
@ -290,6 +290,8 @@ void SymbolInfo::merge(SymbolInfo &&Other) {
|
||||
auto *Last = llvm::unique(Loc);
|
||||
Loc.erase(Last, Loc.end());
|
||||
mergeBase(std::move(Other));
|
||||
if (MangledName.empty())
|
||||
MangledName = std::move(Other.MangledName);
|
||||
}
|
||||
|
||||
NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path)
|
||||
|
@ -377,6 +377,7 @@ struct SymbolInfo : public Info {
|
||||
|
||||
std::optional<Location> DefLoc; // Location where this decl is defined.
|
||||
llvm::SmallVector<Location, 2> Loc; // Locations where this decl is declared.
|
||||
SmallString<16> MangledName;
|
||||
bool IsStatic = false;
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "clang/AST/Attr.h"
|
||||
#include "clang/AST/Comment.h"
|
||||
#include "clang/AST/DeclFriend.h"
|
||||
#include "clang/AST/Mangle.h"
|
||||
#include "clang/Index/USRGeneration.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
@ -767,6 +768,17 @@ static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
|
||||
I.DefLoc = Loc;
|
||||
else
|
||||
I.Loc.emplace_back(Loc);
|
||||
|
||||
auto *Mangler = ItaniumMangleContext::create(
|
||||
D->getASTContext(), D->getASTContext().getDiagnostics());
|
||||
std::string MangledName;
|
||||
llvm::raw_string_ostream MangledStream(MangledName);
|
||||
if (auto *CXXD = dyn_cast<CXXRecordDecl>(D))
|
||||
Mangler->mangleCXXVTable(CXXD, MangledStream);
|
||||
else
|
||||
MangledStream << D->getNameAsString();
|
||||
I.MangledName = MangledName;
|
||||
delete Mangler;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1,6 +1,6 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: clang-doc --extra-arg -std=c++20 --output=%t --format=json --executor=standalone %s
|
||||
// RUN: FileCheck %s < %t/GlobalNamespace/MyClass.json
|
||||
// RUN: FileCheck %s < %t/_ZTV7MyClass.json
|
||||
|
||||
template<typename T>
|
||||
concept Addable = requires(T a, T b) {
|
||||
|
@ -0,0 +1,37 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: clang-doc --output=%t --format=json --executor=standalone %s
|
||||
// RUN: FileCheck %s < %t/_ZTV7MyClass.json --check-prefix=BASE
|
||||
// RUN: FileCheck %s < %t/_ZTV7MyClassIiE.json --check-prefix=SPECIALIZATION
|
||||
|
||||
template<typename T> struct MyClass {};
|
||||
|
||||
template<> struct MyClass<int> {};
|
||||
|
||||
// BASE: "MangledName": "_ZTV7MyClass",
|
||||
// BASE-NEXT: "Name": "MyClass",
|
||||
// BASE-NEXT: "Namespace": [
|
||||
// BASE-NEXT: "GlobalNamespace"
|
||||
// BASE-NEXT: ],
|
||||
// BASE-NEXT: "Path": "GlobalNamespace",
|
||||
// BASE-NEXT: "TagType": "struct",
|
||||
// BASE-NEXT: "Template": {
|
||||
// BASE-NEXT: "Parameters": [
|
||||
// BASE-NEXT: "typename T"
|
||||
// BASE-NEXT: ]
|
||||
// BASE-NEXT: },
|
||||
|
||||
// SPECIALIZATION: "MangledName": "_ZTV7MyClassIiE",
|
||||
// SPECIALIZATION-NEXT: "Name": "MyClass",
|
||||
// SPECIALIZATION-NEXT: "Namespace": [
|
||||
// SPECIALIZATION-NEXT: "GlobalNamespace"
|
||||
// SPECIALIZATION-NEXT: ],
|
||||
// SPECIALIZATION-NEXT: "Path": "GlobalNamespace",
|
||||
// SPECIALIZATION-NEXT: "TagType": "struct",
|
||||
// SPECIALIZATION-NEXT: "Template": {
|
||||
// SPECIALIZATION-NEXT: "Specialization": {
|
||||
// SPECIALIZATION-NEXT: "Parameters": [
|
||||
// SPECIALIZATION-NEXT: "int"
|
||||
// SPECIALIZATION-NEXT: ],
|
||||
// SPECIALIZATION-NEXT: "SpecializationOf": "{{[0-9A-F]*}}"
|
||||
// SPECIALIZATION-NEXT: }
|
||||
// SPECIALIZATION-NEXT: },
|
@ -1,6 +1,6 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: clang-doc --output=%t --format=json --executor=standalone %s
|
||||
// RUN: FileCheck %s < %t/GlobalNamespace/MyClass.json
|
||||
// RUN: FileCheck %s < %t/_ZTV7MyClass.json
|
||||
|
||||
template<typename T> struct MyClass {
|
||||
T MemberTemplate;
|
||||
|
@ -1,6 +1,6 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: clang-doc --output=%t --format=json --executor=standalone %s
|
||||
// RUN: FileCheck %s < %t/GlobalNamespace/MyClass.json
|
||||
// RUN: FileCheck %s < %t/_ZTV7MyClass.json
|
||||
|
||||
struct Foo;
|
||||
|
||||
@ -134,6 +134,7 @@ protected:
|
||||
// CHECK-NEXT: "Filename": "{{.*}}class.cpp",
|
||||
// CHECK-NEXT: "LineNumber": 10
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "MangledName": "_ZTV7MyClass",
|
||||
// CHECK-NEXT: "Name": "MyClass",
|
||||
// CHECK-NEXT: "Namespace": [
|
||||
// CHECK-NEXT: "GlobalNamespace"
|
||||
|
@ -1,6 +1,6 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: clang-doc --extra-arg -std=c++20 --output=%t --format=json --executor=standalone %s
|
||||
// RUN: FileCheck %s < %t/GlobalNamespace/index.json
|
||||
// RUN: FileCheck %s < %t/index.json
|
||||
|
||||
template<typename T> concept Incrementable = requires (T a) {
|
||||
a++;
|
||||
|
@ -1,6 +1,6 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: clang-doc --extra-arg -std=c++20 --output=%t --format=json --executor=standalone %s
|
||||
// RUN: FileCheck %s < %t/GlobalNamespace/index.json
|
||||
// RUN: FileCheck %s < %t/index.json
|
||||
|
||||
// Requires that T suports post and pre-incrementing.
|
||||
template<typename T>
|
||||
|
@ -1,6 +1,6 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: clang-doc --extra-arg -std=c++20 --output=%t --format=json --executor=standalone %s
|
||||
// RUN: FileCheck %s < %t/GlobalNamespace/index.json
|
||||
// RUN: FileCheck %s < %t/index.json
|
||||
|
||||
template<typename T>
|
||||
concept Incrementable = requires(T x) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: clang-doc --output=%t --format=json --executor=standalone %s
|
||||
// RUN: FileCheck %s < %t/GlobalNamespace/index.json
|
||||
// RUN: FileCheck %s < %t/index.json
|
||||
|
||||
static void myFunction() {}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: clang-doc --output=%t --format=json --executor=standalone %s
|
||||
// RUN: FileCheck %s < %t/GlobalNamespace/MyClass.json
|
||||
// RUN: FileCheck %s < %t/_ZTV7MyClass.json
|
||||
|
||||
struct MyClass {
|
||||
template<class T> T methodTemplate(T param) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: clang-doc --output=%t --format=json --executor=standalone %s
|
||||
// RUN: FileCheck %s < %t/GlobalNamespace/index.json
|
||||
// RUN: FileCheck %s < %t/index.json
|
||||
|
||||
class MyClass {};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
// RUN: rm -rf %t && mkdir -p %t
|
||||
// RUN: clang-doc --output=%t --format=json --executor=standalone %s
|
||||
// RUN: FileCheck %s < %t/nested/index.json --check-prefix=NESTED
|
||||
// RUN: FileCheck %s < %t/nested/inner/index.json --check-prefix=INNER
|
||||
// RUN: FileCheck %s < %t/nested.json --check-prefix=NESTED
|
||||
// RUN: FileCheck %s < %t/inner.json --check-prefix=INNER
|
||||
|
||||
namespace nested {
|
||||
int Global;
|
||||
|
@ -67,6 +67,7 @@ TEST(JSONGeneratorTest, emitRecordJSON) {
|
||||
"IsParent": true,
|
||||
"IsTypedef": false,
|
||||
"IsVirtual": true,
|
||||
"MangledName": "",
|
||||
"Name": "F",
|
||||
"Path": "path/to/F",
|
||||
"PublicFunctions": [
|
||||
@ -112,6 +113,7 @@ TEST(JSONGeneratorTest, emitRecordJSON) {
|
||||
"Filename": "main.cpp",
|
||||
"LineNumber": 1
|
||||
},
|
||||
"MangledName": "",
|
||||
"Name": "Foo",
|
||||
"Namespace": [
|
||||
"GlobalNamespace"
|
||||
|
Loading…
x
Reference in New Issue
Block a user