Skip to content

Commit cf3c931

Browse files
committed
return errors, add unit tests
1 parent b90d226 commit cf3c931

File tree

3 files changed

+203
-6
lines changed

3 files changed

+203
-6
lines changed

clang-tools-extra/clang-doc/JSONGenerator.cpp

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
#include "Generators.h"
2+
#include "clang/Basic/Specifiers.h"
23
#include "llvm/Support/JSON.h"
34

45
using namespace llvm;
56
using namespace llvm::json;
67

7-
static llvm::ExitOnError ExitOnErr;
8-
98
namespace clang {
109
namespace doc {
1110

@@ -119,6 +118,18 @@ static void serializeReference(const Reference &Ref, Object &ReferenceObj,
119118
ReferenceObj["ID"] = toHex(toStringRef(Ref.USR));
120119
}
121120

121+
static void serializeReference(Object &Obj, SmallVector<Reference, 4> References, std::string Key) {
122+
json::Value ReferencesArray = Array();
123+
json::Array &ReferencesArrayRef = *ReferencesArray.getAsArray();
124+
for (const auto& Reference : References) {
125+
Object ReferenceObject = Object();
126+
auto BasePath = Reference.getRelativeFilePath("");
127+
serializeReference(Reference, ReferenceObject, BasePath);
128+
ReferencesArrayRef.push_back(std::move(ReferenceObject));
129+
}
130+
Obj[Key] = std::move(ReferencesArray);
131+
}
132+
122133
// Although namespaces and records both have ScopeChildren, they serialize them
123134
// differently. Only enums, records, and typedefs are handled here.
124135
static void serializeCommonChildren(const ScopeChildren &Children,
@@ -280,6 +291,26 @@ static void serializeInfo(const RecordInfo &I, json::Object &Obj,
280291
Obj["ProtectedMembers"] = std::move(ProtectedMembers);
281292
}
282293

294+
if (!I.Bases.empty()) {
295+
json::Value BasesArray = Array();
296+
json::Array &BasesArrayRef = *BasesArray.getAsArray();
297+
for (const auto &BaseInfo : I.Bases) {
298+
Object BaseInfoObj = Object();
299+
serializeInfo(BaseInfo, BaseInfoObj, RepositoryUrl);
300+
BaseInfoObj["IsVirtual"] = BaseInfo.IsVirtual;
301+
BaseInfoObj["Access"] = getAccessSpelling(BaseInfo.Access);
302+
BaseInfoObj["IsParent"] = BaseInfo.IsParent;
303+
BasesArrayRef.push_back(std::move(BaseInfoObj));
304+
}
305+
Obj["Bases"] = std::move(BasesArray);
306+
}
307+
308+
if (!I.Parents.empty())
309+
serializeReference(Obj, I.Parents, "Parents");
310+
311+
if (!I.VirtualParents.empty())
312+
serializeReference(Obj, I.VirtualParents, "VirtualParents");
313+
283314
serializeCommonChildren(I.Children, Obj, RepositoryUrl);
284315
}
285316

@@ -297,7 +328,7 @@ Error JSONGenerator::generateDocs(
297328
if (!CreatedDirs.contains(Path)) {
298329
if (std::error_code Err = sys::fs::create_directories(Path);
299330
Err != std::error_code())
300-
ExitOnErr(createFileError(Twine(Path), Err));
331+
return createFileError(Twine(Path), Err);
301332
CreatedDirs.insert(Path);
302333
}
303334

@@ -309,7 +340,7 @@ Error JSONGenerator::generateDocs(
309340
std::error_code FileErr;
310341
raw_fd_ostream InfoOS(Group.getKey(), FileErr, sys::fs::OF_Text);
311342
if (FileErr)
312-
ExitOnErr(createFileError("cannot open file " + Group.getKey(), FileErr));
343+
return createFileError("cannot open file " + Group.getKey(), FileErr);
313344

314345
for (const auto &Info : Group.getValue())
315346
if (Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
@@ -334,8 +365,7 @@ Error JSONGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
334365
case InfoType::IT_typedef:
335366
break;
336367
case InfoType::IT_default:
337-
ExitOnErr(
338-
createStringError(inconvertibleErrorCode(), "unexpected info type"));
368+
return createStringError(inconvertibleErrorCode(), "unexpected info type");
339369
}
340370
OS << llvm::formatv("{0:2}", llvm::json::Value(std::move(Obj)));
341371
return Error::success();

clang-tools-extra/unittests/clang-doc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ add_extra_unittest(ClangDocTests
3131
MergeTest.cpp
3232
SerializeTest.cpp
3333
YAMLGeneratorTest.cpp
34+
JSONGeneratorTest.cpp
3435
)
3536

3637
clang_target_link_libraries(ClangDocTests
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
#include "ClangDocTest.h"
2+
#include "Generators.h"
3+
#include "Representation.h"
4+
#include "gtest/gtest.h"
5+
6+
namespace clang {
7+
namespace doc {
8+
9+
static std::unique_ptr<Generator> getJSONGenerator() {
10+
auto G = doc::findGeneratorByName("json");
11+
if (!G)
12+
return nullptr;
13+
return std::move(G.get());
14+
}
15+
16+
TEST(JSONGeneratorTest, emitRecordJSON) {
17+
RecordInfo I;
18+
I.Name = "Foo";
19+
I.FullName = "Foo";
20+
I.IsTypeDef = false;
21+
I.Namespace.emplace_back(EmptySID, "GlobalNamespace", InfoType::IT_namespace);
22+
I.Path = "GlobalNamespace";
23+
I.DefLoc = Location(1, 1, "main.cpp");
24+
I.TagType = TagTypeKind::Class;
25+
26+
I.Children.Enums.emplace_back();
27+
I.Children.Enums.back().Name = "Color";
28+
I.Children.Enums.back().Scoped = false;
29+
I.Children.Enums.back().Members.emplace_back();
30+
I.Children.Enums.back().Members.back().Name = "RED";
31+
I.Children.Enums.back().Members.back().Value = "0";
32+
33+
I.Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_protected);
34+
35+
I.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
36+
AccessSpecifier::AS_public, true);
37+
I.Bases.back().Children.Functions.emplace_back();
38+
I.Bases.back().Children.Functions.back().Name = "InheritedFunctionOne";
39+
I.Bases.back().Members.emplace_back(TypeInfo("int"), "N",
40+
AccessSpecifier::AS_public);
41+
42+
// F is in the global namespace
43+
I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "");
44+
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record,
45+
"path::to::G::G", "path/to/G");
46+
47+
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
48+
"path::to::A::r::ChildStruct", "path/to/A/r");
49+
I.Children.Functions.emplace_back();
50+
I.Children.Functions.back().Name = "OneFunction";
51+
52+
auto G = getJSONGenerator();
53+
assert(G);
54+
std::string Buffer;
55+
llvm::raw_string_ostream Actual(Buffer);
56+
auto Err = G->generateDocForInfo(&I, Actual, ClangDocContext());
57+
assert(!Err);
58+
std::string Expected = R"raw({
59+
"Bases": [
60+
{
61+
"Access": "public",
62+
"FullName": "F",
63+
"IsParent": true,
64+
"IsTypedef": false,
65+
"IsVirtual": true,
66+
"Name": "F",
67+
"Path": "path/to/F",
68+
"PublicFunctions": [
69+
{
70+
"IsStatic": false,
71+
"Name": "InheritedFunctionOne",
72+
"ReturnType": {
73+
"ID": "0000000000000000000000000000000000000000",
74+
"IsBuiltIn": false,
75+
"IsTemplate": false,
76+
"Name": "",
77+
"QualName": ""
78+
},
79+
"USR": "0000000000000000000000000000000000000000"
80+
}
81+
],
82+
"PublicMembers": [
83+
{
84+
"Name": "N",
85+
"Type": "int"
86+
}
87+
],
88+
"TagType": "struct",
89+
"USR": "0000000000000000000000000000000000000000"
90+
}
91+
],
92+
"Enums": [
93+
{
94+
"Members": [
95+
{
96+
"Name": "RED",
97+
"Value": "0"
98+
}
99+
],
100+
"Name": "Color",
101+
"Scoped": false,
102+
"USR": "0000000000000000000000000000000000000000"
103+
}
104+
],
105+
"FullName": "Foo",
106+
"IsTypedef": false,
107+
"Location": {
108+
"Filename": "main.cpp",
109+
"LineNumber": 1
110+
},
111+
"Name": "Foo",
112+
"Namespace": [
113+
"GlobalNamespace"
114+
],
115+
"Parents": [
116+
{
117+
"ID": "0000000000000000000000000000000000000000",
118+
"Link": "F.json",
119+
"Name": "F",
120+
"QualName": ""
121+
}
122+
],
123+
"Path": "GlobalNamespace",
124+
"ProtectedMembers": [
125+
{
126+
"Name": "X",
127+
"Type": "int"
128+
}
129+
],
130+
"PublicFunctions": [
131+
{
132+
"IsStatic": false,
133+
"Name": "OneFunction",
134+
"ReturnType": {
135+
"ID": "0000000000000000000000000000000000000000",
136+
"IsBuiltIn": false,
137+
"IsTemplate": false,
138+
"Name": "",
139+
"QualName": ""
140+
},
141+
"USR": "0000000000000000000000000000000000000000"
142+
}
143+
],
144+
"Records": [
145+
{
146+
"ID": "0000000000000000000000000000000000000000",
147+
"Link": "ChildStruct.json",
148+
"Name": "ChildStruct",
149+
"QualName": "path::to::A::r::ChildStruct"
150+
}
151+
],
152+
"TagType": "class",
153+
"USR": "0000000000000000000000000000000000000000",
154+
"VirtualParents": [
155+
{
156+
"ID": "0000000000000000000000000000000000000000",
157+
"Link": "G.json",
158+
"Name": "G",
159+
"QualName": "path::to::G::G"
160+
}
161+
]
162+
})raw";
163+
EXPECT_EQ(Expected, Actual.str());
164+
}
165+
} // namespace doc
166+
} // namespace clang

0 commit comments

Comments
 (0)