clang 23.0.0git
JSONFormatImpl.cpp
Go to the documentation of this file.
1//===- JSONFormatImpl.cpp -----------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "JSONFormatImpl.h"
10
12#include "llvm/Support/Registry.h"
13
15
16namespace clang::ssaf {
17
18//----------------------------------------------------------------------------
19// JSON Reader and Writer
20//----------------------------------------------------------------------------
21
22llvm::Expected<Value> readJSON(llvm::StringRef Path) {
23 if (!llvm::sys::fs::exists(Path)) {
24 return ErrorBuilder::create(std::errc::no_such_file_or_directory,
27 .build();
28 }
29
30 if (llvm::sys::fs::is_directory(Path)) {
31 return ErrorBuilder::create(std::errc::is_a_directory,
34 .build();
35 }
36
37 if (!Path.ends_with_insensitive(JSONFormatFileExtension)) {
38 return ErrorBuilder::create(std::errc::invalid_argument,
40 llvm::formatv(ErrorMessages::FileIsNotJSON,
42 .build();
43 }
44
45 auto BufferOrError = llvm::MemoryBuffer::getFile(Path);
46 if (!BufferOrError) {
47 const std::error_code EC = BufferOrError.getError();
49 EC.message())
50 .build();
51 }
52
53 return llvm::json::parse(BufferOrError.get()->getBuffer());
54}
55
56llvm::Error writeJSON(Value &&Value, llvm::StringRef Path) {
57 if (llvm::sys::fs::exists(Path)) {
58 return ErrorBuilder::create(std::errc::file_exists,
61 .build();
62 }
63
64 llvm::StringRef Dir = llvm::sys::path::parent_path(Path);
65 if (!Dir.empty() && !llvm::sys::fs::is_directory(Dir)) {
66 return ErrorBuilder::create(std::errc::no_such_file_or_directory,
69 .build();
70 }
71
72 if (!Path.ends_with_insensitive(JSONFormatFileExtension)) {
73 return ErrorBuilder::create(std::errc::invalid_argument,
75 llvm::formatv(ErrorMessages::FileIsNotJSON,
77 .build();
78 }
79
80 std::error_code EC;
81 llvm::raw_fd_ostream OutStream(Path, EC, llvm::sys::fs::OF_Text);
82
83 if (EC) {
85 EC.message())
86 .build();
87 }
88
89 OutStream << llvm::formatv("{0:2}\n", Value);
90 OutStream.flush();
91
92 // This path handles post-write stream errors (e.g. ENOSPC after buffered
93 // writes). It is difficult to exercise in unit tests so it is intentionally
94 // left without test coverage.
95 if (OutStream.has_error()) {
96 return ErrorBuilder::create(OutStream.error(),
98 OutStream.error().message())
99 .build();
100 }
101
102 return llvm::Error::success();
103}
104
105//----------------------------------------------------------------------------
106// JSONFormat Static Methods
107//----------------------------------------------------------------------------
108
109std::map<SummaryName, JSONFormat::FormatInfo> JSONFormat::initFormatInfos() {
110 std::map<SummaryName, FormatInfo> FormatInfos;
111 for (const auto &FormatInfoEntry : llvm::Registry<FormatInfo>::entries()) {
112 std::unique_ptr<FormatInfo> Info = FormatInfoEntry.instantiate();
113 bool Inserted = FormatInfos.try_emplace(Info->ForSummary, *Info).second;
114 if (!Inserted) {
115 llvm::report_fatal_error(
116 "FormatInfo is already registered for summary: " +
117 Info->ForSummary.str());
118 }
119 }
120 return FormatInfos;
121}
122
123//----------------------------------------------------------------------------
124// SummaryName
125//----------------------------------------------------------------------------
126
127SummaryName summaryNameFromJSON(llvm::StringRef SummaryNameStr) {
128 return SummaryName(SummaryNameStr.str());
129}
130
131llvm::StringRef summaryNameToJSON(const SummaryName &SN) { return SN.str(); }
132
133//----------------------------------------------------------------------------
134// EntityId
135//----------------------------------------------------------------------------
136
137EntityId JSONFormat::entityIdFromJSON(const uint64_t EntityIdIndex) const {
138 return makeEntityId(static_cast<size_t>(EntityIdIndex));
139}
140
141uint64_t JSONFormat::entityIdToJSON(EntityId EI) const {
142 return static_cast<uint64_t>(getIndex(EI));
143}
144
145//----------------------------------------------------------------------------
146// BuildNamespaceKind
147//----------------------------------------------------------------------------
148
149llvm::Expected<BuildNamespaceKind>
150buildNamespaceKindFromJSON(llvm::StringRef BuildNamespaceKindStr) {
151 auto OptBuildNamespaceKind =
152 buildNamespaceKindFromString(BuildNamespaceKindStr);
153 if (!OptBuildNamespaceKind) {
154 return ErrorBuilder::create(std::errc::invalid_argument,
156 BuildNamespaceKindStr)
157 .build();
158 }
159 return *OptBuildNamespaceKind;
160}
161
162// Provided for consistency with respect to rest of the codebase.
164 return buildNamespaceKindToString(BNK);
165}
166
167//----------------------------------------------------------------------------
168// BuildNamespace
169//----------------------------------------------------------------------------
170
172JSONFormat::buildNamespaceFromJSON(const Object &BuildNamespaceObject) const {
173 auto OptBuildNamespaceKindStr = BuildNamespaceObject.getString("kind");
174 if (!OptBuildNamespaceKindStr) {
175 return ErrorBuilder::create(std::errc::invalid_argument,
177 "BuildNamespaceKind", "kind", "string")
178 .build();
179 }
180
181 auto ExpectedKind = buildNamespaceKindFromJSON(*OptBuildNamespaceKindStr);
182 if (!ExpectedKind) {
183 return ErrorBuilder::wrap(ExpectedKind.takeError())
184 .context(ErrorMessages::ReadingFromField, "BuildNamespaceKind", "kind")
185 .build();
186 }
187
188 auto OptNameStr = BuildNamespaceObject.getString("name");
189 if (!OptNameStr) {
190 return ErrorBuilder::create(std::errc::invalid_argument,
192 "BuildNamespaceName", "name", "string")
193 .build();
194 }
195
196 return {BuildNamespace(*ExpectedKind, *OptNameStr)};
197}
198
199Object JSONFormat::buildNamespaceToJSON(const BuildNamespace &BN) const {
200 Object Result;
202 Result["name"] = getName(BN);
203 return Result;
204}
205
206//----------------------------------------------------------------------------
207// NestedBuildNamespace
208//----------------------------------------------------------------------------
209
210llvm::Expected<NestedBuildNamespace> JSONFormat::nestedBuildNamespaceFromJSON(
211 const Array &NestedBuildNamespaceArray) const {
212 std::vector<BuildNamespace> Namespaces;
213
214 size_t NamespaceCount = NestedBuildNamespaceArray.size();
215 Namespaces.reserve(NamespaceCount);
216
217 for (const auto &[Index, BuildNamespaceValue] :
218 llvm::enumerate(NestedBuildNamespaceArray)) {
219
220 const Object *BuildNamespaceObject = BuildNamespaceValue.getAsObject();
221 if (!BuildNamespaceObject) {
222 return ErrorBuilder::create(std::errc::invalid_argument,
224 "BuildNamespace", Index, "object")
225 .build();
226 }
227
228 auto ExpectedBuildNamespace = buildNamespaceFromJSON(*BuildNamespaceObject);
229 if (!ExpectedBuildNamespace) {
230 return ErrorBuilder::wrap(ExpectedBuildNamespace.takeError())
231 .context(ErrorMessages::ReadingFromIndex, "BuildNamespace", Index)
232 .build();
233 }
234
235 Namespaces.push_back(std::move(*ExpectedBuildNamespace));
236 }
237
238 return NestedBuildNamespace(std::move(Namespaces));
239}
240
241Array JSONFormat::nestedBuildNamespaceToJSON(
242 const NestedBuildNamespace &NBN) const {
243 Array Result;
244 const auto &Namespaces = getNamespaces(NBN);
245 Result.reserve(Namespaces.size());
246
247 for (const auto &BN : Namespaces) {
248 Result.push_back(buildNamespaceToJSON(BN));
249 }
250
251 return Result;
252}
253
254//----------------------------------------------------------------------------
255// EntityName
256//----------------------------------------------------------------------------
257
258llvm::Expected<EntityName>
259JSONFormat::entityNameFromJSON(const Object &EntityNameObject) const {
260 const auto OptUSR = EntityNameObject.getString("usr");
261 if (!OptUSR) {
262 return ErrorBuilder::create(std::errc::invalid_argument,
264 "usr", "string")
265 .build();
266 }
267
268 const auto OptSuffix = EntityNameObject.getString("suffix");
269 if (!OptSuffix) {
270 return ErrorBuilder::create(std::errc::invalid_argument,
272 "Suffix", "suffix", "string")
273 .build();
274 }
275
276 const Array *OptNamespaceArray = EntityNameObject.getArray("namespace");
277 if (!OptNamespaceArray) {
278 return ErrorBuilder::create(std::errc::invalid_argument,
280 "NestedBuildNamespace", "namespace", "array")
281 .build();
282 }
283
284 auto ExpectedNamespace = nestedBuildNamespaceFromJSON(*OptNamespaceArray);
285 if (!ExpectedNamespace) {
286 return ErrorBuilder::wrap(ExpectedNamespace.takeError())
287 .context(ErrorMessages::ReadingFromField, "NestedBuildNamespace",
288 "namespace")
289 .build();
290 }
291
292 return EntityName{*OptUSR, *OptSuffix, std::move(*ExpectedNamespace)};
293}
294
295Object JSONFormat::entityNameToJSON(const EntityName &EN) const {
296 Object Result;
297 Result["usr"] = getUSR(EN);
298 Result["suffix"] = getSuffix(EN);
299 Result["namespace"] = nestedBuildNamespaceToJSON(getNamespace(EN));
300 return Result;
301}
302
303//----------------------------------------------------------------------------
304// EntityLinkageType
305//----------------------------------------------------------------------------
306
307llvm::Expected<EntityLinkageType>
308entityLinkageTypeFromJSON(llvm::StringRef EntityLinkageTypeStr) {
309 auto OptEntityLinkageType = entityLinkageTypeFromString(EntityLinkageTypeStr);
310 if (!OptEntityLinkageType) {
311 return ErrorBuilder::create(std::errc::invalid_argument,
313 EntityLinkageTypeStr)
314 .build();
315 }
316 return *OptEntityLinkageType;
317}
318
319// Provided for consistency with respect to rest of the codebase.
321 return entityLinkageTypeToString(LT);
322}
323
324//----------------------------------------------------------------------------
325// EntityLinkage
326//----------------------------------------------------------------------------
327
329JSONFormat::entityLinkageFromJSON(const Object &EntityLinkageObject) const {
330 auto OptLinkageStr = EntityLinkageObject.getString("type");
331 if (!OptLinkageStr) {
332 return ErrorBuilder::create(std::errc::invalid_argument,
334 "EntityLinkageType", "type", "string")
335 .build();
336 }
337
338 auto ExpectedLinkageType = entityLinkageTypeFromJSON(*OptLinkageStr);
339 if (!ExpectedLinkageType) {
340 return ErrorBuilder::wrap(ExpectedLinkageType.takeError())
341 .context(ErrorMessages::ReadingFromField, "EntityLinkageType", "type")
342 .build();
343 }
344
345 return EntityLinkage(*ExpectedLinkageType);
346}
347
348Object JSONFormat::entityLinkageToJSON(const EntityLinkage &EL) const {
349 Object Result;
350 Result["type"] = entityLinkageTypeToJSON(getLinkage(EL));
351 return Result;
352}
353
354//----------------------------------------------------------------------------
355// EntityIdTableEntry
356//----------------------------------------------------------------------------
357
358llvm::Expected<std::pair<EntityName, EntityId>>
359JSONFormat::entityIdTableEntryFromJSON(
360 const Object &EntityIdTableEntryObject) const {
361
362 const Object *OptEntityNameObject =
363 EntityIdTableEntryObject.getObject("name");
364 if (!OptEntityNameObject) {
365 return ErrorBuilder::create(std::errc::invalid_argument,
367 "EntityName", "name", "object")
368 .build();
369 }
370
371 auto ExpectedEntityName = entityNameFromJSON(*OptEntityNameObject);
372 if (!ExpectedEntityName) {
373 return ErrorBuilder::wrap(ExpectedEntityName.takeError())
374 .context(ErrorMessages::ReadingFromField, "EntityName", "name")
375 .build();
376 }
377
378 const Value *EntityIdIntValue = EntityIdTableEntryObject.get("id");
379 if (!EntityIdIntValue) {
380 return ErrorBuilder::create(std::errc::invalid_argument,
382 "EntityId", "id",
383 "number (unsigned 64-bit integer)")
384 .build();
385 }
386
387 const std::optional<uint64_t> OptEntityIdInt =
388 EntityIdIntValue->getAsUINT64();
389 if (!OptEntityIdInt) {
390 return ErrorBuilder::create(std::errc::invalid_argument,
392 "EntityId", "id",
393 "number (unsigned 64-bit integer)")
394 .build();
395 }
396
397 EntityId EI = entityIdFromJSON(*OptEntityIdInt);
398
399 return std::make_pair(std::move(*ExpectedEntityName), std::move(EI));
400}
401
402Object JSONFormat::entityIdTableEntryToJSON(const EntityName &EN,
403 EntityId EI) const {
404 Object Entry;
405 Entry["id"] = entityIdToJSON(EI);
406 Entry["name"] = entityNameToJSON(EN);
407 return Entry;
408}
409
410//----------------------------------------------------------------------------
411// EntityIdTable
412//----------------------------------------------------------------------------
413
414llvm::Expected<EntityIdTable>
415JSONFormat::entityIdTableFromJSON(const Array &EntityIdTableArray) const {
416 EntityIdTable IdTable;
417 std::map<EntityName, EntityId> &Entities = getEntities(IdTable);
418
419 for (const auto &[Index, EntityIdTableEntryValue] :
420 llvm::enumerate(EntityIdTableArray)) {
421
422 const Object *OptEntityIdTableEntryObject =
423 EntityIdTableEntryValue.getAsObject();
424 if (!OptEntityIdTableEntryObject) {
425 return ErrorBuilder::create(std::errc::invalid_argument,
427 "EntityIdTable entry", Index, "object")
428 .build();
429 }
430
431 auto ExpectedEntityIdTableEntry =
432 entityIdTableEntryFromJSON(*OptEntityIdTableEntryObject);
433 if (!ExpectedEntityIdTableEntry)
434 return ErrorBuilder::wrap(ExpectedEntityIdTableEntry.takeError())
435 .context(ErrorMessages::ReadingFromIndex, "EntityIdTable entry",
436 Index)
437 .build();
438
439 auto [EntityIt, EntityInserted] =
440 Entities.emplace(std::move(*ExpectedEntityIdTableEntry));
441 if (!EntityInserted) {
442 return ErrorBuilder::create(std::errc::invalid_argument,
444 "EntityIdTable entry", Index,
445 EntityIt->second)
446 .build();
447 }
448 }
449
450 return IdTable;
451}
452
453Array JSONFormat::entityIdTableToJSON(const EntityIdTable &IdTable) const {
454 Array EntityIdTableArray;
455 const auto &Entities = getEntities(IdTable);
456 EntityIdTableArray.reserve(Entities.size());
457
458 for (const auto &[EntityName, EntityId] : Entities) {
459 EntityIdTableArray.push_back(
460 entityIdTableEntryToJSON(EntityName, EntityId));
461 }
462
463 return EntityIdTableArray;
464}
465
466//----------------------------------------------------------------------------
467// LinkageTableEntry
468//----------------------------------------------------------------------------
469
470llvm::Expected<std::pair<EntityId, EntityLinkage>>
471JSONFormat::linkageTableEntryFromJSON(
472 const Object &LinkageTableEntryObject) const {
473 const Value *EntityIdIntValue = LinkageTableEntryObject.get("id");
474 if (!EntityIdIntValue) {
475 return ErrorBuilder::create(std::errc::invalid_argument,
477 "EntityId", "id",
478 "number (unsigned 64-bit integer)")
479 .build();
480 }
481
482 const std::optional<uint64_t> OptEntityIdInt =
483 EntityIdIntValue->getAsUINT64();
484 if (!OptEntityIdInt) {
485 return ErrorBuilder::create(std::errc::invalid_argument,
487 "EntityId", "id",
488 "number (unsigned 64-bit integer)")
489 .build();
490 }
491
492 EntityId EI = entityIdFromJSON(*OptEntityIdInt);
493
494 const Object *OptEntityLinkageObject =
495 LinkageTableEntryObject.getObject("linkage");
496 if (!OptEntityLinkageObject) {
497 return ErrorBuilder::create(std::errc::invalid_argument,
499 "EntityLinkage", "linkage", "object")
500 .build();
501 }
502
503 auto ExpectedEntityLinkage = entityLinkageFromJSON(*OptEntityLinkageObject);
504 if (!ExpectedEntityLinkage) {
505 return ErrorBuilder::wrap(ExpectedEntityLinkage.takeError())
506 .context(ErrorMessages::ReadingFromField, "EntityLinkage", "linkage")
507 .build();
508 }
509
510 return std::make_pair(std::move(EI), std::move(*ExpectedEntityLinkage));
511}
512
513Object JSONFormat::linkageTableEntryToJSON(EntityId EI,
514 const EntityLinkage &EL) const {
515 Object Entry;
516 Entry["id"] = entityIdToJSON(EI);
517 Entry["linkage"] = entityLinkageToJSON(EL);
518 return Entry;
519}
520
521//----------------------------------------------------------------------------
522// LinkageTable
523//----------------------------------------------------------------------------
524
525// ExpectedIds is the set of EntityIds from the IdTable that must appear in the
526// linkage table—no more, no fewer. It is taken by value because it is consumed
527// during parsing: each successfully matched id is erased from the set, and any
528// ids remaining at the end are reported as missing.
529llvm::Expected<std::map<EntityId, EntityLinkage>>
530JSONFormat::linkageTableFromJSON(const Array &LinkageTableArray,
531 std::set<EntityId> ExpectedIds) const {
532 std::map<EntityId, EntityLinkage> LinkageTable;
533
534 for (const auto &[Index, LinkageTableEntryValue] :
535 llvm::enumerate(LinkageTableArray)) {
536 const Object *OptLinkageTableEntryObject =
537 LinkageTableEntryValue.getAsObject();
538 if (!OptLinkageTableEntryObject) {
539 return ErrorBuilder::create(std::errc::invalid_argument,
541 "LinkageTable entry", Index, "object")
542 .build();
543 }
544
545 auto ExpectedLinkageTableEntry =
546 linkageTableEntryFromJSON(*OptLinkageTableEntryObject);
547 if (!ExpectedLinkageTableEntry) {
548 return ErrorBuilder::wrap(ExpectedLinkageTableEntry.takeError())
549 .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index)
550 .build();
551 }
552
553 const EntityId EI = ExpectedLinkageTableEntry->first;
554
555 auto [It, Inserted] =
556 LinkageTable.insert(std::move(*ExpectedLinkageTableEntry));
557 if (!Inserted) {
558 return ErrorBuilder::create(std::errc::invalid_argument,
560 "LinkageTable entry", Index, It->first)
561 .build();
562 }
563
564 if (ExpectedIds.erase(EI) == 0) {
566 std::errc::invalid_argument,
568 .context(ErrorMessages::ReadingFromIndex, "LinkageTable entry", Index)
569 .build();
570 }
571 }
572
573 if (!ExpectedIds.empty()) {
575 std::errc::invalid_argument,
577 *ExpectedIds.begin())
578 .build();
579 }
580
581 return LinkageTable;
582}
583
584Array JSONFormat::linkageTableToJSON(
585 const std::map<EntityId, EntityLinkage> &LinkageTable) const {
586 Array Result;
587 Result.reserve(LinkageTable.size());
588
589 for (const auto &[EI, EL] : LinkageTable) {
590 Result.push_back(linkageTableEntryToJSON(EI, EL));
591 }
592
593 return Result;
594}
595
596} // namespace clang::ssaf
static std::optional< NonLoc > getIndex(ProgramStateRef State, const ElementRegion *ER, CharKind CK)
static Decl::Kind getKind(const Decl *D)
static std::string getUSR(const Decl *D)
Represents a single namespace in the build process.
Manages entity name interning and provides efficient EntityId handles.
Lightweight opaque handle representing an entity in an EntityIdTable.
Definition EntityId.h:31
Represents the linkage properties of an entity in the program model.
Uniquely identifies an entity in a program.
Definition EntityName.h:28
static ErrorBuilder create(std::error_code EC, const char *Fmt, Args &&...ArgVals)
Create an ErrorBuilder with an error code and formatted message.
ErrorBuilder & context(const char *Msg)
Add context information as a plain string.
llvm::Error build() const
Build and return the final error.
static ErrorBuilder wrap(llvm::Error E)
Wrap an existing error and optionally add context.
Represents a hierarchical sequence of build namespaces.
EntityId makeEntityId(const size_t Index) const
Uniquely identifies an analysis summary.
Definition SummaryName.h:22
llvm::StringRef str() const
Explicit conversion to the underlying string representation.
Definition SummaryName.h:31
StringRef getName(const HeaderType T)
Definition HeaderFile.h:38
constexpr const char * InvalidBuildNamespaceKind
constexpr const char * ReadingFromField
constexpr const char * FailedToReadObjectAtIndex
constexpr const char * InvalidEntityLinkageType
constexpr const char * FileIsDirectory
constexpr const char * FileNotFound
constexpr const char * FailedToReadObjectAtField
constexpr const char * FileExists
constexpr const char * ReadingFromIndex
constexpr const char * FailedInsertionOnDuplication
constexpr const char * FailedToDeserializeLinkageTableExtraId
constexpr const char * FailedToWriteFile
constexpr const char * FailedToDeserializeLinkageTableMissingId
constexpr const char * FileIsNotJSON
constexpr const char * FailedToReadFile
constexpr const char * ParentDirectoryNotFound
SummaryName summaryNameFromJSON(llvm::StringRef SummaryNameStr)
llvm::StringRef summaryNameToJSON(const SummaryName &SN)
std::optional< EntityLinkageType > entityLinkageTypeFromString(llvm::StringRef Str)
Parses a string produced by entityLinkageTypeToString().
llvm::json::Array Array
std::optional< BuildNamespaceKind > buildNamespaceKindFromString(llvm::StringRef Str)
Parses a string produced by buildNamespaceKindToString().
llvm::Expected< EntityLinkageType > entityLinkageTypeFromJSON(llvm::StringRef EntityLinkageTypeStr)
llvm::Error writeJSON(Value &&Value, llvm::StringRef Path)
llvm::json::Value Value
llvm::Expected< BuildNamespaceKind > buildNamespaceKindFromJSON(llvm::StringRef BuildNamespaceKindStr)
llvm::json::Object Object
llvm::Expected< Value > readJSON(llvm::StringRef Path)
constexpr const char * JSONFormatFileExtension
llvm::StringRef buildNamespaceKindToJSON(BuildNamespaceKind BNK)
llvm::StringRef entityLinkageTypeToJSON(EntityLinkageType LT)
llvm::StringRef buildNamespaceKindToString(BuildNamespaceKind BNK)
Returns the canonical string representation of BNK used for serialization and display (e....
llvm::StringRef entityLinkageTypeToString(EntityLinkageType LT)
Returns the canonical string representation of LT used for serialization and display (e....
@ Result
The result type of a method or function.
Definition TypeBase.h:905
template class CLANG_TEMPLATE_ABI Registry< clang::ssaf::JSONFormat::FormatInfo >