================ @@ -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, ---------------- steakhal wrote:
I was looking this and wondered - have I seen this one already? Sort of. This has the same implementation as the `TagsEntitySummary`. I'm fine with the duplication by semantically we could chunk this file into separe ones dealing with the implementations of the functions we then register. Probably having each file implement one registry entry is granular enough - but I;m open for a different strategy for chunking. This would also make the example more realistic because we don't expect people to define all of their stuff in a single file. https://github.com/llvm/llvm-project/pull/187403 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
