================ @@ -0,0 +1,465 @@ +//===- ExamplePlugin.cpp - Example SSAF plugin ----------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// A loadable plugin that demonstrates the full SSAF analysis pipeline. +// +//===----------------------------------------------------------------------===// + +#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.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/AnalysisName.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisRegistry.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/AnalysisResult.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/DerivedAnalysis.h" +#include "clang/ScalableStaticAnalysisFramework/Core/WholeProgramAnalysis/SummaryAnalysis.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/Registry.h" +#include <algorithm> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +using namespace clang::ssaf; +using namespace llvm; + +namespace { + +//===----------------------------------------------------------------------===// +// TagsEntitySummary +// +// Per-entity data: a list of string tags. Stored in the LU data section +// under summary_name "TagsEntitySummary". Serialized as: +// { "tags": ["tag1", "tag2", ...] } +//===----------------------------------------------------------------------===// + +struct TagsEntitySummary final : EntitySummary { + static SummaryName summaryName() { return SummaryName("TagsEntitySummary"); } + + SummaryName getSummaryName() const override { + return SummaryName("TagsEntitySummary"); + } + + std::vector<std::string> Tags; +}; + +json::Object serializeTagsEntitySummary(const EntitySummary &ES, + JSONFormat::EntityIdToJSONFn) { + const auto &S = static_cast<const TagsEntitySummary &>(ES); + json::Array TagsArray; + for (const auto &Tag : S.Tags) { + TagsArray.push_back(Tag); + } + return json::Object{{"tags", std::move(TagsArray)}}; +} + +Expected<std::unique_ptr<EntitySummary>> +deserializeTagsEntitySummary(const json::Object &Obj, EntityIdTable &, + JSONFormat::EntityIdFromJSONFn) { + const json::Array *TagsArray = Obj.getArray("tags"); + if (!TagsArray) { + return createStringError(inconvertibleErrorCode(), + "missing or invalid field 'tags'"); + } + + auto S = std::make_unique<TagsEntitySummary>(); + for (const auto &[Index, Val] : llvm::enumerate(*TagsArray)) { + auto Str = Val.getAsString(); + if (!Str) { + return createStringError(inconvertibleErrorCode(), + "tags element at index %zu is not a string", + Index); + } + S->Tags.push_back(Str->str()); + } + return std::move(S); +} + +struct TagsEntitySummaryFormatInfo final : JSONFormat::FormatInfo { + TagsEntitySummaryFormatInfo() + : JSONFormat::FormatInfo(SummaryName("TagsEntitySummary"), + serializeTagsEntitySummary, + deserializeTagsEntitySummary) {} +}; + +llvm::Registry<JSONFormat::FormatInfo>::Add<TagsEntitySummaryFormatInfo> + RegisterTagsEntitySummaryForJSON("TagsEntitySummary", + "JSON format info for TagsEntitySummary"); + +//===----------------------------------------------------------------------===// +// 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"); + +//===----------------------------------------------------------------------===// +// TagsAnalysisResult +// +// Sorted, deduplicated list of all tags seen across entities. Serialized as: +// { "tags": ["tag1", "tag2", ...] } +//===----------------------------------------------------------------------===// + +struct TagsAnalysisResult final : AnalysisResult { + static AnalysisName analysisName() { + return AnalysisName("TagsAnalysisResult"); + } + + std::vector<std::string> Tags; +}; + +json::Object serializeTagsAnalysisResult(const TagsAnalysisResult &R, ---------------- aviralg wrote:
I split the ExamplePlugin into three files by analysis type. The two summary analyses have to be declared in a header file since they are used by the derived analysis. In fact this was mainly the reason I chose to put all of this in a single file; it felt conceptually simpler for these trivial analyses to all just be in one file. However, I don't have strong opinions here, so I made the split anyways. Regarding duplication, my plan is to transfer the JSONFormat unittests (the ones that check for format errors and round trips) to lit tests and remove these definitions from there. https://github.com/llvm/llvm-project/pull/187403 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
