1//===- PairsAnalysis.cpp - Pairs analysis for ExamplePlugin ---------------===//
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 "AnalysisResults.h"
10#include "clang/ScalableStaticAnalysis/Core/Model/SummaryName.h"
11#include "clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h"
12#include "clang/ScalableStaticAnalysis/Core/TUSummary/EntitySummary.h"
13#include "clang/ScalableStaticAnalysis/Core/WholeProgramAnalysis/AnalysisRegistry.h"
14#include "clang/ScalableStaticAnalysis/Core/WholeProgramAnalysis/SummaryAnalysis.h"
15#include "llvm/Support/Error.h"
16#include "llvm/Support/JSON.h"
17#include "llvm/Support/Registry.h"
18#include <memory>
19#include <utility>
20#include <vector>
21
22using namespace clang::ssaf;
23using namespace llvm;
24using example_plugin::PairsAnalysisResult;
25
26namespace {
27
28//===----------------------------------------------------------------------===//
29// PairsEntitySummary
30//
31// Per-entity data: a list of (EntityId, EntityId) pairs. Stored in the LU
32// data section under summary_name "PairsEntitySummary". Serialized as:
33// { "pairs": [{"first": {...}, "second": {...}}, ...] }
34//===----------------------------------------------------------------------===//
35
36struct PairsEntitySummary final : EntitySummary {
37 static SummaryName summaryName() { return SummaryName("PairsEntitySummary"); }
38
39 SummaryName getSummaryName() const override {
40 return SummaryName("PairsEntitySummary");
41 }
42
43 std::vector<std::pair<EntityId, EntityId>> Pairs;
44};
45
46json::Object serializePairsEntitySummary(const EntitySummary &ES,
47 JSONFormat::EntityIdToJSONFn ToJSON) {
48 const auto &S = static_cast<const PairsEntitySummary &>(ES);
49 json::Array PairsArray;
50 for (const auto &[First, Second] : S.Pairs) {
51 PairsArray.push_back(E: json::Object{
52 {.K: "first", .V: ToJSON(First)},
53 {.K: "second", .V: ToJSON(Second)},
54 });
55 }
56 return json::Object{{.K: "pairs", .V: std::move(PairsArray)}};
57}
58
59Expected<std::unique_ptr<EntitySummary>>
60deserializePairsEntitySummary(const json::Object &Obj, EntityIdTable &,
61 JSONFormat::EntityIdFromJSONFn FromJSON) {
62 auto Result = std::make_unique<PairsEntitySummary>();
63 const json::Array *PairsArray = Obj.getArray(K: "pairs");
64 if (!PairsArray) {
65 return createStringError(EC: inconvertibleErrorCode(),
66 S: "missing or invalid field 'pairs'");
67 }
68 for (const auto &[Index, Value] : llvm::enumerate(First: *PairsArray)) {
69 const json::Object *Pair = Value.getAsObject();
70 if (!Pair) {
71 return createStringError(
72 EC: inconvertibleErrorCode(),
73 Fmt: "pairs element at index %zu is not a JSON object", Vals: Index);
74 }
75 const json::Object *FirstObj = Pair->getObject(K: "first");
76 if (!FirstObj) {
77 return createStringError(
78 EC: inconvertibleErrorCode(),
79 Fmt: "missing or invalid 'first' field at index '%zu'", Vals: Index);
80 }
81 const json::Object *SecondObj = Pair->getObject(K: "second");
82 if (!SecondObj) {
83 return createStringError(
84 EC: inconvertibleErrorCode(),
85 Fmt: "missing or invalid 'second' field at index '%zu'", Vals: Index);
86 }
87 auto ExpectedFirst = FromJSON(*FirstObj);
88 if (!ExpectedFirst) {
89 return createStringError(EC: inconvertibleErrorCode(),
90 Fmt: "invalid 'first' entity id at index '%zu': %s",
91 Vals: Index,
92 Vals: toString(E: ExpectedFirst.takeError()).c_str());
93 }
94 auto ExpectedSecond = FromJSON(*SecondObj);
95 if (!ExpectedSecond) {
96 return createStringError(EC: inconvertibleErrorCode(),
97 Fmt: "invalid 'second' entity id at index '%zu': %s",
98 Vals: Index,
99 Vals: toString(E: ExpectedSecond.takeError()).c_str());
100 }
101 Result->Pairs.emplace_back(args&: *ExpectedFirst, args&: *ExpectedSecond);
102 }
103 return std::move(Result);
104}
105
106struct PairsEntitySummaryFormatInfo final : JSONFormat::FormatInfo {
107 PairsEntitySummaryFormatInfo()
108 : JSONFormat::FormatInfo(SummaryName("PairsEntitySummary"),
109 serializePairsEntitySummary,
110 deserializePairsEntitySummary) {}
111};
112
113llvm::Registry<JSONFormat::FormatInfo>::Add<PairsEntitySummaryFormatInfo>
114 RegisterPairsEntitySummaryForJSON(
115 "PairsEntitySummary", "JSON format info for PairsEntitySummary");
116
117//===----------------------------------------------------------------------===//
118// PairsAnalysisResult serialization
119//
120// Per-entity pair count. Serialized as:
121// { "pair_counts": [{"entity_id": {...}, "count": N}, ...] }
122//===----------------------------------------------------------------------===//
123
124json::Object serializePairsAnalysisResult(const PairsAnalysisResult &R,
125 JSONFormat::EntityIdToJSONFn ToJSON) {
126 json::Array Arr;
127 for (const auto &[EI, Count] : R.PairCounts) {
128 Arr.push_back(E: json::Object{{.K: "entity_id", .V: ToJSON(EI)}, {.K: "count", .V: Count}});
129 }
130 return json::Object{{.K: "pair_counts", .V: std::move(Arr)}};
131}
132
133Expected<std::unique_ptr<AnalysisResult>>
134deserializePairsAnalysisResult(const json::Object &Obj,
135 JSONFormat::EntityIdFromJSONFn FromJSON) {
136 const json::Array *Arr = Obj.getArray(K: "pair_counts");
137 if (!Arr) {
138 return createStringError(EC: inconvertibleErrorCode(),
139 S: "missing or invalid field 'pair_counts'");
140 }
141
142 auto R = std::make_unique<PairsAnalysisResult>();
143 for (const auto &[Index, Val] : llvm::enumerate(First: *Arr)) {
144 const json::Object *Entry = Val.getAsObject();
145 if (!Entry) {
146 return createStringError(
147 EC: inconvertibleErrorCode(),
148 Fmt: "pair_counts element at index %zu is not an object", Vals: Index);
149 }
150 const json::Object *EIObj = Entry->getObject(K: "entity_id");
151 if (!EIObj) {
152 return createStringError(
153 EC: inconvertibleErrorCode(),
154 Fmt: "missing or invalid 'entity_id' field at index %zu", Vals: Index);
155 }
156 auto ExpectedEI = FromJSON(*EIObj);
157 if (!ExpectedEI) {
158 return ExpectedEI.takeError();
159 }
160
161 auto CountVal = Entry->getInteger(K: "count");
162 if (!CountVal) {
163 return createStringError(EC: inconvertibleErrorCode(),
164 Fmt: "missing or invalid 'count' field at index %zu",
165 Vals: Index);
166 }
167 R->PairCounts.emplace_back(args&: *ExpectedEI, args: static_cast<int>(*CountVal));
168 }
169 return std::move(R);
170}
171
172JSONFormat::AnalysisResultRegistry::Add<PairsAnalysisResult>
173 RegisterPairsResultForJSON(serializePairsAnalysisResult,
174 deserializePairsAnalysisResult);
175
176//===----------------------------------------------------------------------===//
177// PairsAnalysis
178//
179// SummaryAnalysis that reads per-entity PairsEntitySummary data and counts
180// the number of pairs per entity, producing (EntityId, count) pairs.
181//===----------------------------------------------------------------------===//
182
183class PairsAnalysis final
184 : public SummaryAnalysis<PairsAnalysisResult, PairsEntitySummary> {
185public:
186 using ResultType = PairsAnalysisResult;
187
188 llvm::Error add(EntityId Id, const PairsEntitySummary &S) override {
189 getResult().PairCounts.emplace_back(args&: Id, args: static_cast<int>(S.Pairs.size()));
190 return llvm::Error::success();
191 }
192};
193
194AnalysisRegistry::Add<PairsAnalysis>
195 RegisterPairsAnalysis("Counts pairs per entity");
196
197} // namespace
198