blob: 34923f4a15e6ea641d9ca9f1bd3597c5478b6171 [file]
//===- PairsAnalysis.cpp - Pairs analysis for ExamplePlugin ---------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "AnalysisResults.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Model/SummaryName.h"
#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h"
#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/EntitySummary.h"
#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.h"
#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/SummaryAnalysis.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/Registry.h"
#include <memory>
#include <utility>
#include <vector>
using namespace clang::ssaf;
using namespace llvm;
using example_plugin::PairsAnalysisResult;
namespace {
//===----------------------------------------------------------------------===//
// PairsEntitySummary
//
// Per-entity data: a list of (EntityId, EntityId) pairs. Stored in the LU
// data section under summary_name "PairsEntitySummary". Serialized as:
// { "pairs": [{"first": {...}, "second": {...}}, ...] }
//===----------------------------------------------------------------------===//
struct PairsEntitySummary final : EntitySummary {
static SummaryName summaryName() { return SummaryName("PairsEntitySummary"); }
SummaryName getSummaryName() const override {
return SummaryName("PairsEntitySummary");
}
std::vector<std::pair<EntityId, EntityId>> Pairs;
};
json::Object serializePairsEntitySummary(const EntitySummary &ES,
JSONFormat::EntityIdToJSONFn ToJSON) {
const auto &S = static_cast<const PairsEntitySummary &>(ES);
json::Array PairsArray;
for (const auto &[First, Second] : S.Pairs) {
PairsArray.push_back(json::Object{
{"first", ToJSON(First)},
{"second", ToJSON(Second)},
});
}
return json::Object{{"pairs", std::move(PairsArray)}};
}
Expected<std::unique_ptr<EntitySummary>>
deserializePairsEntitySummary(const json::Object &Obj, EntityIdTable &,
JSONFormat::EntityIdFromJSONFn FromJSON) {
auto Result = std::make_unique<PairsEntitySummary>();
const json::Array *PairsArray = Obj.getArray("pairs");
if (!PairsArray) {
return createStringError(inconvertibleErrorCode(),
"missing or invalid field 'pairs'");
}
for (const auto &[Index, Value] : llvm::enumerate(*PairsArray)) {
const json::Object *Pair = Value.getAsObject();
if (!Pair) {
return createStringError(
inconvertibleErrorCode(),
"pairs element at index %zu is not a JSON object", Index);
}
const json::Object *FirstObj = Pair->getObject("first");
if (!FirstObj) {
return createStringError(
inconvertibleErrorCode(),
"missing or invalid 'first' field at index '%zu'", Index);
}
const json::Object *SecondObj = Pair->getObject("second");
if (!SecondObj) {
return createStringError(
inconvertibleErrorCode(),
"missing or invalid 'second' field at index '%zu'", Index);
}
auto ExpectedFirst = FromJSON(*FirstObj);
if (!ExpectedFirst) {
return createStringError(inconvertibleErrorCode(),
"invalid 'first' entity id at index '%zu': %s",
Index,
toString(ExpectedFirst.takeError()).c_str());
}
auto ExpectedSecond = FromJSON(*SecondObj);
if (!ExpectedSecond) {
return createStringError(inconvertibleErrorCode(),
"invalid 'second' entity id at index '%zu': %s",
Index,
toString(ExpectedSecond.takeError()).c_str());
}
Result->Pairs.emplace_back(*ExpectedFirst, *ExpectedSecond);
}
return std::move(Result);
}
struct PairsEntitySummaryFormatInfo final : JSONFormat::FormatInfo {
PairsEntitySummaryFormatInfo()
: JSONFormat::FormatInfo(SummaryName("PairsEntitySummary"),
serializePairsEntitySummary,
deserializePairsEntitySummary) {}
};
llvm::Registry<JSONFormat::FormatInfo>::Add<PairsEntitySummaryFormatInfo>
RegisterPairsEntitySummaryForJSON(
"PairsEntitySummary", "JSON format info for PairsEntitySummary");
//===----------------------------------------------------------------------===//
// PairsAnalysisResult serialization
//
// Per-entity pair count. Serialized as:
// { "pair_counts": [{"entity_id": {...}, "count": N}, ...] }
//===----------------------------------------------------------------------===//
json::Object serializePairsAnalysisResult(const PairsAnalysisResult &R,
JSONFormat::EntityIdToJSONFn ToJSON) {
json::Array Arr;
for (const auto &[EI, Count] : R.PairCounts) {
Arr.push_back(json::Object{{"entity_id", ToJSON(EI)}, {"count", Count}});
}
return json::Object{{"pair_counts", std::move(Arr)}};
}
Expected<std::unique_ptr<AnalysisResult>>
deserializePairsAnalysisResult(const json::Object &Obj,
JSONFormat::EntityIdFromJSONFn FromJSON) {
const json::Array *Arr = Obj.getArray("pair_counts");
if (!Arr) {
return createStringError(inconvertibleErrorCode(),
"missing or invalid field 'pair_counts'");
}
auto R = std::make_unique<PairsAnalysisResult>();
for (const auto &[Index, Val] : llvm::enumerate(*Arr)) {
const json::Object *Entry = Val.getAsObject();
if (!Entry) {
return createStringError(
inconvertibleErrorCode(),
"pair_counts element at index %zu is not an object", Index);
}
const json::Object *EIObj = Entry->getObject("entity_id");
if (!EIObj) {
return createStringError(
inconvertibleErrorCode(),
"missing or invalid 'entity_id' field at index %zu", Index);
}
auto ExpectedEI = FromJSON(*EIObj);
if (!ExpectedEI) {
return ExpectedEI.takeError();
}
auto CountVal = Entry->getInteger("count");
if (!CountVal) {
return createStringError(inconvertibleErrorCode(),
"missing or invalid 'count' field at index %zu",
Index);
}
R->PairCounts.emplace_back(*ExpectedEI, static_cast<int>(*CountVal));
}
return std::move(R);
}
JSONFormat::AnalysisResultRegistry::Add<PairsAnalysisResult>
RegisterPairsResultForJSON(serializePairsAnalysisResult,
deserializePairsAnalysisResult);
//===----------------------------------------------------------------------===//
// PairsAnalysis
//
// SummaryAnalysis that reads per-entity PairsEntitySummary data and counts
// the number of pairs per entity, producing (EntityId, count) pairs.
//===----------------------------------------------------------------------===//
class PairsAnalysis final
: public SummaryAnalysis<PairsAnalysisResult, PairsEntitySummary> {
public:
using ResultType = PairsAnalysisResult;
llvm::Error add(EntityId Id, const PairsEntitySummary &S) override {
getResult().PairCounts.emplace_back(Id, static_cast<int>(S.Pairs.size()));
return llvm::Error::success();
}
};
AnalysisRegistry::Add<PairsAnalysis>
RegisterPairsAnalysis("Counts pairs per entity");
} // namespace