https://github.com/aviralg updated https://github.com/llvm/llvm-project/pull/205946
>From 33fa47112cc3115595579ca95e86239f681d7b61 Mon Sep 17 00:00:00 2001 From: Aviral Goel <[email protected]> Date: Thu, 25 Jun 2026 16:05:31 -0700 Subject: [PATCH 1/2] Add static library --- .../Core/EntityLinker/StaticLibrary.h | 75 +++++++ .../Core/EntityLinker/TUSummaryEncoding.h | 1 + .../Core/Model/BuildNamespace.h | 6 +- .../Core/Model/PrivateFieldNames.def | 3 + .../Core/Serialization/JSONFormat.h | 15 ++ .../Core/Serialization/SerializationFormat.h | 14 +- .../Core/CMakeLists.txt | 1 + .../Core/ModelStringConversions.h | 4 + .../Serialization/JSONFormat/Artifact.cpp | 19 +- .../Serialization/JSONFormat/JSONFormatImpl.h | 5 +- .../JSONFormat/StaticLibrary.cpp | 192 ++++++++++++++++++ .../JSONFormat/TUSummaryEncoding.cpp | 19 +- .../Inputs/rt-static-library-empty.json | 9 + .../ssaf-format/Artifact/round-trip.test | 9 + .../ssaf-format/Artifact/top-level.test | 2 +- .../Inputs/duplicate-member.json | 32 +++ .../StaticLibrary/Inputs/invalid-syntax.json | 1 + .../Inputs/member-inner-error.json | 17 ++ .../Inputs/member-mismatched-type.json | 18 ++ .../Inputs/member-missing-type.json | 20 ++ .../Inputs/member-not-object.json | 9 + .../Inputs/member-triple-mismatch.json | 21 ++ .../Inputs/members-not-array.json | 9 + .../StaticLibrary/Inputs/mismatched-type.json | 9 + .../StaticLibrary/Inputs/missing-members.json | 8 + .../Inputs/missing-namespace.json | 5 + .../Inputs/missing-target-triple.json | 8 + .../StaticLibrary/Inputs/missing-type.json | 8 + .../Inputs/namespace-invalid-kind.json | 9 + .../Inputs/namespace-missing-kind.json | 8 + .../Inputs/namespace-missing-name.json | 8 + .../Inputs/namespace-wrong-kind.json | 9 + .../Inputs/not-json-extension.txt | 1 + .../Inputs/not-normalized-target-triple.json | 9 + .../StaticLibrary/Inputs/not-object.json | 1 + .../StaticLibrary/Inputs/rt-empty.json | 9 + .../Inputs/rt-multiple-members.json | 43 ++++ .../Inputs/rt-single-member.json | 21 ++ .../Inputs/unsorted-members-input.json | 43 ++++ .../ssaf-format/StaticLibrary/io.test | 49 +++++ .../StaticLibrary/permissions.test | 40 ++++ .../ssaf-format/StaticLibrary/round-trip.test | 42 ++++ .../ssaf-format/StaticLibrary/top-level.test | 125 ++++++++++++ clang/tools/clang-ssaf-format/SSAFFormat.cpp | 11 +- .../BuildNamespaceTest.cpp | 8 + .../TUSummaryExtractorFrontendActionTest.cpp | 17 ++ .../ModelStringConversionsTest.cpp | 14 ++ .../Registries/MockSerializationFormat.cpp | 11 + .../Registries/MockSerializationFormat.h | 6 + .../ScalableStaticAnalysis/TestFixture.h | 1 + 50 files changed, 1009 insertions(+), 15 deletions(-) create mode 100644 clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h create mode 100644 clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/StaticLibrary.cpp create mode 100644 clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-static-library-empty.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/duplicate-member.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/invalid-syntax.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-inner-error.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-mismatched-type.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-missing-type.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-not-object.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-triple-mismatch.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/members-not-array.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/mismatched-type.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-members.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-namespace.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-target-triple.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-type.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-invalid-kind.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-kind.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-name.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-wrong-kind.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-json-extension.txt create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-normalized-target-triple.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-object.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-empty.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-multiple-members.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-single-member.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/unsorted-members-input.json create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/io.test create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/permissions.test create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/round-trip.test create mode 100644 clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/top-level.test diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h new file mode 100644 index 0000000000000..74b6fb4ea169f --- /dev/null +++ b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h @@ -0,0 +1,75 @@ +//===- StaticLibrary.h ------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the StaticLibrary class, which represents a static +// library of translation unit summary encodings (the SSAF analogue of an +// ar / libtool -static / lib.exe output). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SCALABLESTATICANALYSIS_CORE_ENTITYLINKER_STATICLIBRARY_H +#define LLVM_CLANG_SCALABLESTATICANALYSIS_CORE_ENTITYLINKER_STATICLIBRARY_H + +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" +#include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h" +#include "llvm/TargetParser/Triple.h" +#include <memory> +#include <set> + +namespace clang::ssaf { + +/// Represents a static library of translation unit summary encodings. +/// +/// A StaticLibrary bundles member translation units without performing +/// entity resolution, mirroring the role of ar / libtool -static / lib.exe +/// in native build pipelines. The final linker is responsible for +/// selective inclusion when a StaticLibrary appears on its command line. +/// +/// Static libraries are single-architecture: every member's target triple +/// must equal the library's. Multi-architecture static libraries are +/// expressed as a fat wrapper around per-architecture StaticLibrary +/// instances rather than as a single mixed-architecture library. +/// +/// Members are stored as encoded TUSummaryEncoding objects: the archiver +/// tool never decodes per-entity payloads, and the linker consumes them +/// as-is during its selective inclusion pass. +class StaticLibrary { + friend class SerializationFormat; + friend class TestFixture; + + /// Orders members by their TUNamespace. As a nested struct of + /// StaticLibrary, it inherits StaticLibrary's friend access to + /// TUSummaryEncoding's private fields. + struct MemberByNamespace { + bool operator()(const std::unique_ptr<TUSummaryEncoding> &A, + const std::unique_ptr<TUSummaryEncoding> &B) const { + return A->TUNamespace < B->TUNamespace; + } + }; + + // Target triple of the static library. All member TUs must share this + // triple. + llvm::Triple TargetTriple; + + // The namespace identifying this static library (kind=StaticLibrary). + BuildNamespace Namespace; + + // Member translation units, ordered by their TUNamespace. Membership is + // by namespace identity: inserting a TU whose TUNamespace already exists + // in the set is rejected during deserialization. + std::set<std::unique_ptr<TUSummaryEncoding>, MemberByNamespace> Members; + +public: + StaticLibrary(llvm::Triple TargetTriple, BuildNamespace Namespace) + : TargetTriple(std::move(TargetTriple)), Namespace(std::move(Namespace)) { + } +}; + +} // namespace clang::ssaf + +#endif // LLVM_CLANG_SCALABLESTATICANALYSIS_CORE_ENTITYLINKER_STATICLIBRARY_H diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h index 2c672a55e4873..2ece195ed1fd0 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h +++ b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h @@ -35,6 +35,7 @@ namespace clang::ssaf { class TUSummaryEncoding { friend class EntityLinker; friend class SerializationFormat; + friend class StaticLibrary; friend class TestFixture; // Target triple of the translation unit. diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h b/clang/include/clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h index 6da5a9e42c4da..1549b357ea50d 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h +++ b/clang/include/clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h @@ -27,7 +27,11 @@ namespace clang::ssaf { -enum class BuildNamespaceKind : unsigned short { CompilationUnit, LinkUnit }; +enum class BuildNamespaceKind : unsigned short { + CompilationUnit, + LinkUnit, + StaticLibrary +}; /// Represents a single namespace in the build process. /// diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def b/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def index 44931026ba428..b1c37823dc3fe 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def +++ b/clang/include/clang/ScalableStaticAnalysis/Core/Model/PrivateFieldNames.def @@ -36,6 +36,9 @@ FIELD(LUSummaryEncoding, IdTable) FIELD(LUSummaryEncoding, LinkageTable) FIELD(LUSummaryEncoding, LUNamespace) FIELD(NestedBuildNamespace, Namespaces) +FIELD(StaticLibrary, Members) +FIELD(StaticLibrary, Namespace) +FIELD(StaticLibrary, TargetTriple) FIELD(TUSummary, TargetTriple) FIELD(TUSummary, Data) FIELD(TUSummary, IdTable) diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h b/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h index 9d9b1ff0bbb51..d4b6cfc0e1690 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h +++ b/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h @@ -68,6 +68,12 @@ class JSONFormat final : public SerializationFormat { llvm::Error writeLUSummaryEncoding(const LUSummaryEncoding &SummaryEncoding, llvm::StringRef Path) override; + llvm::Expected<StaticLibrary> + readStaticLibrary(llvm::StringRef Path) override; + + llvm::Error writeStaticLibrary(const StaticLibrary &S, + llvm::StringRef Path) override; + llvm::Expected<WPASuite> readWPASuite(llvm::StringRef Path) override; llvm::Error writeWPASuite(const WPASuite &Suite, @@ -122,6 +128,15 @@ class JSONFormat final : public SerializationFormat { llvm::Expected<LUSummaryEncoding> readLUSummaryEncodingFromObject(const Object &Root); + /// Parses a StaticLibrary from an already-validated root JSON object. + /// See \c readTUSummaryFromObject for caller responsibilities. + llvm::Expected<StaticLibrary> readStaticLibraryFromObject(const Object &Root); + + /// Serializes a TUSummaryEncoding to a JSON object including its + /// self-describing \c type field. Used both by \c writeTUSummaryEncoding + /// and by the StaticLibrary writer to emit member entries. + Object tuSummaryEncodingToJSON(const TUSummaryEncoding &SE) const; + /// Parses a WPASuite from an already-validated root JSON object. See /// \c readTUSummaryFromObject for caller responsibilities. llvm::Expected<WPASuite> readWPASuiteFromObject(const Object &Root); diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormat.h b/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormat.h index ca3ee9ce3362c..29b662bc0e380 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormat.h +++ b/clang/include/clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormat.h @@ -16,6 +16,7 @@ #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummaryEncoding.h" +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" #include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h" #include "clang/ScalableStaticAnalysis/Core/Model/SummaryName.h" @@ -40,7 +41,12 @@ using Artifact = std::variant<TUSummary, LUSummary, WPASuite>; /// Lazily-deserialized counterpart of \c Artifact: the same on-disk /// artifacts but with their per-entity summary payloads left as opaque /// format-specific encodings rather than fully resolved analysis results. -using ArtifactEncoding = std::variant<TUSummaryEncoding, LUSummaryEncoding>; +/// +/// \c StaticLibrary appears only in this variant: the archiver tool and +/// the linker pass member payloads through without decoding them, so a +/// fully decoded static-library shape would have no consumer. +using ArtifactEncoding = + std::variant<TUSummaryEncoding, LUSummaryEncoding, StaticLibrary>; /// Abstract base class for serialization formats. class SerializationFormat { @@ -94,6 +100,12 @@ class SerializationFormat { writeLUSummaryEncoding(const LUSummaryEncoding &SummaryEncoding, llvm::StringRef Path) = 0; + virtual llvm::Expected<StaticLibrary> + readStaticLibrary(llvm::StringRef Path) = 0; + + virtual llvm::Error writeStaticLibrary(const StaticLibrary &S, + llvm::StringRef Path) = 0; + virtual llvm::Expected<WPASuite> readWPASuite(llvm::StringRef Path) = 0; virtual llvm::Error writeWPASuite(const WPASuite &Suite, diff --git a/clang/lib/ScalableStaticAnalysis/Core/CMakeLists.txt b/clang/lib/ScalableStaticAnalysis/Core/CMakeLists.txt index b2cde5f225445..0a0ce19d63732 100644 --- a/clang/lib/ScalableStaticAnalysis/Core/CMakeLists.txt +++ b/clang/lib/ScalableStaticAnalysis/Core/CMakeLists.txt @@ -17,6 +17,7 @@ add_clang_library(clangScalableStaticAnalysisCore Serialization/JSONFormat/JSONFormatImpl.cpp Serialization/JSONFormat/LUSummary.cpp Serialization/JSONFormat/LUSummaryEncoding.cpp + Serialization/JSONFormat/StaticLibrary.cpp Serialization/JSONFormat/TUSummary.cpp Serialization/JSONFormat/TUSummaryEncoding.cpp Serialization/JSONFormat/WPASuite.cpp diff --git a/clang/lib/ScalableStaticAnalysis/Core/ModelStringConversions.h b/clang/lib/ScalableStaticAnalysis/Core/ModelStringConversions.h index ee1603e50025f..8d3c932205740 100644 --- a/clang/lib/ScalableStaticAnalysis/Core/ModelStringConversions.h +++ b/clang/lib/ScalableStaticAnalysis/Core/ModelStringConversions.h @@ -36,6 +36,8 @@ inline llvm::StringRef buildNamespaceKindToString(BuildNamespaceKind BNK) { return "CompilationUnit"; case BuildNamespaceKind::LinkUnit: return "LinkUnit"; + case BuildNamespaceKind::StaticLibrary: + return "StaticLibrary"; } llvm_unreachable("Unhandled BuildNamespaceKind variant"); } @@ -48,6 +50,8 @@ buildNamespaceKindFromString(llvm::StringRef Str) { return BuildNamespaceKind::CompilationUnit; if (Str == "LinkUnit") return BuildNamespaceKind::LinkUnit; + if (Str == "StaticLibrary") + return BuildNamespaceKind::StaticLibrary; return std::nullopt; } diff --git a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/Artifact.cpp b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/Artifact.cpp index 424d7368cf33f..e5fc46a8ea952 100644 --- a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/Artifact.cpp +++ b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/Artifact.cpp @@ -145,10 +145,21 @@ JSONFormat::readArtifactEncoding(llvm::StringRef Path) { return ArtifactEncoding{std::move(*ExpectedLU)}; } + if (*ExpectedType == JSONTypeValueStaticLibrary) { + auto ExpectedStaticLibrary = readStaticLibraryFromObject(*RootObjectPtr); + if (!ExpectedStaticLibrary) { + return ErrorBuilder::wrap(ExpectedStaticLibrary.takeError()) + .context(ErrorMessages::ReadingFromFile, "ArtifactEncoding", Path) + .build(); + } + return ArtifactEncoding{std::move(*ExpectedStaticLibrary)}; + } + return ErrorBuilder::create(std::errc::invalid_argument, ErrorMessages::UnknownArtifactEncodingType, *ExpectedType, JSONTypeKey, - JSONTypeValueTUSummary, JSONTypeValueLUSummary) + JSONTypeValueTUSummary, JSONTypeValueLUSummary, + JSONTypeValueStaticLibrary) .context(ErrorMessages::ReadingFromFile, "ArtifactEncoding", Path) .build(); } @@ -160,11 +171,13 @@ llvm::Error JSONFormat::writeArtifactEncoding(const ArtifactEncoding &E, using T = std::decay_t<decltype(Enc)>; if constexpr (std::is_same_v<T, TUSummaryEncoding>) { return writeTUSummaryEncoding(Enc, Path); + } else if constexpr (std::is_same_v<T, LUSummaryEncoding>) { + return writeLUSummaryEncoding(Enc, Path); } else { static_assert( - std::is_same_v<T, LUSummaryEncoding>, + std::is_same_v<T, StaticLibrary>, "ArtifactEncoding visitor must cover all variant alternatives"); - return writeLUSummaryEncoding(Enc, Path); + return writeStaticLibrary(Enc, Path); } }, E); diff --git a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/JSONFormatImpl.h b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/JSONFormatImpl.h index 191be83ccb0e5..e83a8dabac113 100644 --- a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/JSONFormatImpl.h +++ b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/JSONFormatImpl.h @@ -85,7 +85,7 @@ inline constexpr const char *MismatchedSummaryType = inline constexpr const char *UnknownArtifactType = "unknown value '{0}' for field '{1}': expected '{2}', '{3}', or '{4}'"; inline constexpr const char *UnknownArtifactEncodingType = - "unknown value '{0}' for field '{1}': expected '{2}', or '{3}'"; + "unknown value '{0}' for field '{1}': expected '{2}', '{3}', or '{4}'"; inline constexpr const char *FailedToDeserializeEntitySummaryNoFormatInfo = "failed to deserialize EntitySummary: no FormatInfo registered for '{0}'"; @@ -149,6 +149,9 @@ inline constexpr const char *JSONTypeValueTUSummary = "TUSummary"; /// Value written to \c JSONTypeKey for serialized \c LUSummary files. inline constexpr const char *JSONTypeValueLUSummary = "LUSummary"; +/// Value written to \c JSONTypeKey for serialized \c StaticLibrary files. +inline constexpr const char *JSONTypeValueStaticLibrary = "StaticLibrary"; + /// Value written to \c JSONTypeKey for serialized \c WPASuite files. inline constexpr const char *JSONTypeValueWPASuite = "WPASuite"; diff --git a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/StaticLibrary.cpp b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/StaticLibrary.cpp new file mode 100644 index 0000000000000..b34c63b185b0e --- /dev/null +++ b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/StaticLibrary.cpp @@ -0,0 +1,192 @@ +//===- StaticLibrary.cpp --------------------------------------------------===// +// +// 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 "JSONFormatImpl.h" + +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h" +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" +#include "llvm/TargetParser/Triple.h" + +namespace clang::ssaf { + +//---------------------------------------------------------------------------- +// StaticLibrary +//---------------------------------------------------------------------------- + +llvm::Expected<StaticLibrary> +JSONFormat::readStaticLibrary(llvm::StringRef Path) { + auto ExpectedJSON = readJSON(Path); + if (!ExpectedJSON) { + return ErrorBuilder::wrap(ExpectedJSON.takeError()) + .context(ErrorMessages::ReadingFromFile, "StaticLibrary", Path) + .build(); + } + + Object *RootObjectPtr = ExpectedJSON->getAsObject(); + if (!RootObjectPtr) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObject, + "StaticLibrary", "object") + .context(ErrorMessages::ReadingFromFile, "StaticLibrary", Path) + .build(); + } + + if (auto Err = checkSummaryType(*RootObjectPtr, JSONTypeValueStaticLibrary)) { + return ErrorBuilder::wrap(std::move(Err)) + .context(ErrorMessages::ReadingFromFile, "StaticLibrary", Path) + .build(); + } + + auto ExpectedStaticLibrary = readStaticLibraryFromObject(*RootObjectPtr); + if (!ExpectedStaticLibrary) { + return ErrorBuilder::wrap(ExpectedStaticLibrary.takeError()) + .context(ErrorMessages::ReadingFromFile, "StaticLibrary", Path) + .build(); + } + + return std::move(*ExpectedStaticLibrary); +} + +llvm::Expected<StaticLibrary> +JSONFormat::readStaticLibraryFromObject(const Object &RootObject) { + auto OptTargetTriple = RootObject.getString("target_triple"); + if (!OptTargetTriple) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "TargetTriple", "target_triple", "string") + .build(); + } + + if (auto Err = validateNormalizedTargetTriple(*OptTargetTriple)) { + return ErrorBuilder::wrap(std::move(Err)) + .context(ErrorMessages::ReadingFromField, "TargetTriple", + "target_triple") + .build(); + } + + llvm::Triple T(*OptTargetTriple); + + const Object *NamespaceObject = RootObject.getObject("namespace"); + if (!NamespaceObject) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "BuildNamespace", "namespace", "object") + .build(); + } + + auto ExpectedNamespace = buildNamespaceFromJSON(*NamespaceObject); + if (!ExpectedNamespace) { + return ErrorBuilder::wrap(ExpectedNamespace.takeError()) + .context(ErrorMessages::ReadingFromField, "BuildNamespace", "namespace") + .build(); + } + + if (getKind(*ExpectedNamespace) != BuildNamespaceKind::StaticLibrary) { + return ErrorBuilder::create( + std::errc::invalid_argument, + ErrorMessages::MismatchedSummaryType, + buildNamespaceKindToJSON(BuildNamespaceKind::StaticLibrary), + "namespace.kind", + buildNamespaceKindToJSON(getKind(*ExpectedNamespace))) + .build(); + } + + StaticLibrary S(std::move(T), std::move(*ExpectedNamespace)); + + const Array *MembersArray = RootObject.getArray("members"); + if (!MembersArray) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtField, + "StaticLibrary members", "members", "array") + .build(); + } + + auto &Members = getMembers(S); + const auto &StaticLibraryTriple = getTargetTriple(S); + + for (const auto &[Index, MemberValue] : llvm::enumerate(*MembersArray)) { + const Object *MemberObject = MemberValue.getAsObject(); + if (!MemberObject) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedToReadObjectAtIndex, + "StaticLibrary member", Index, "object") + .build(); + } + + if (auto Err = checkSummaryType(*MemberObject, JSONTypeValueTUSummary)) { + return ErrorBuilder::wrap(std::move(Err)) + .context(ErrorMessages::ReadingFromIndex, "StaticLibrary member", + Index) + .build(); + } + + auto ExpectedMember = readTUSummaryEncodingFromObject(*MemberObject); + if (!ExpectedMember) { + return ErrorBuilder::wrap(ExpectedMember.takeError()) + .context(ErrorMessages::ReadingFromIndex, "StaticLibrary member", + Index) + .build(); + } + + if (ExpectedMember->getTargetTriple() != StaticLibraryTriple) { + return ErrorBuilder::create( + std::errc::invalid_argument, + ErrorMessages::MismatchedSummaryType, + llvm::Triple::normalize(StaticLibraryTriple.str()), + "target_triple", + llvm::Triple::normalize( + ExpectedMember->getTargetTriple().str())) + .context(ErrorMessages::ReadingFromIndex, "StaticLibrary member", + Index) + .build(); + } + + auto MemberNamespace = getTUNamespace(*ExpectedMember); + auto Owned = + std::make_unique<TUSummaryEncoding>(std::move(*ExpectedMember)); + auto [It, Inserted] = Members.insert(std::move(Owned)); + if (!Inserted) { + return ErrorBuilder::create(std::errc::invalid_argument, + ErrorMessages::FailedInsertionOnDuplication, + "StaticLibrary member", Index, + MemberNamespace) + .build(); + } + } + + return std::move(S); +} + +llvm::Error JSONFormat::writeStaticLibrary(const StaticLibrary &S, + llvm::StringRef Path) { + Object RootObject; + + RootObject[JSONTypeKey] = JSONTypeValueStaticLibrary; + + RootObject["target_triple"] = + llvm::Triple::normalize(getTargetTriple(S).str()); + + RootObject["namespace"] = buildNamespaceToJSON(getNamespace(S)); + + Array MembersArray; + MembersArray.reserve(getMembers(S).size()); + for (const auto &Member : getMembers(S)) { + MembersArray.push_back(tuSummaryEncodingToJSON(*Member)); + } + RootObject["members"] = std::move(MembersArray); + + if (auto Error = writeJSON(std::move(RootObject), Path)) { + return ErrorBuilder::wrap(std::move(Error)) + .context(ErrorMessages::WritingToFile, "StaticLibrary", Path) + .build(); + } + + return llvm::Error::success(); +} + +} // namespace clang::ssaf diff --git a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/TUSummaryEncoding.cpp b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/TUSummaryEncoding.cpp index 5a5ab98731c47..3ee4f059bd458 100644 --- a/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/TUSummaryEncoding.cpp +++ b/clang/lib/ScalableStaticAnalysis/Core/Serialization/JSONFormat/TUSummaryEncoding.cpp @@ -164,6 +164,17 @@ JSONFormat::readTUSummaryEncodingFromObject(const Object &RootObject) { llvm::Error JSONFormat::writeTUSummaryEncoding(const TUSummaryEncoding &SummaryEncoding, llvm::StringRef Path) { + if (auto Error = writeJSON(tuSummaryEncodingToJSON(SummaryEncoding), Path)) { + return ErrorBuilder::wrap(std::move(Error)) + .context(ErrorMessages::WritingToFile, "TUSummary", Path) + .build(); + } + + return llvm::Error::success(); +} + +Object JSONFormat::tuSummaryEncodingToJSON( + const TUSummaryEncoding &SummaryEncoding) const { Object RootObject; RootObject[JSONTypeKey] = JSONTypeValueTUSummary; @@ -181,13 +192,7 @@ JSONFormat::writeTUSummaryEncoding(const TUSummaryEncoding &SummaryEncoding, RootObject["data"] = encodingSummaryDataMapToJSON(getData(SummaryEncoding)); - if (auto Error = writeJSON(std::move(RootObject), Path)) { - return ErrorBuilder::wrap(std::move(Error)) - .context(ErrorMessages::WritingToFile, "TUSummary", Path) - .build(); - } - - return llvm::Error::success(); + return RootObject; } } // namespace clang::ssaf diff --git a/clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-static-library-empty.json b/clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-static-library-empty.json new file mode 100644 index 0000000000000..3059d007a2e9e --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/Artifact/Inputs/rt-static-library-empty.json @@ -0,0 +1,9 @@ +{ + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libempty" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/Artifact/round-trip.test b/clang/test/Analysis/Scalable/ssaf-format/Artifact/round-trip.test index 4ddb233940afe..d6d67826e4847 100644 --- a/clang/test/Analysis/Scalable/ssaf-format/Artifact/round-trip.test +++ b/clang/test/Analysis/Scalable/ssaf-format/Artifact/round-trip.test @@ -38,3 +38,12 @@ // RUN: diff %S/Inputs/rt-lu-empty.json %t/rt-lu-empty-enc.json // RUN: clang-ssaf-format --type auto --encoding %S/Inputs/rt-lu-empty.json -o %t/rt-lu-empty-enc-explicit.json // RUN: diff %S/Inputs/rt-lu-empty.json %t/rt-lu-empty-enc-explicit.json + +// StaticLibrary appears only in the ArtifactEncoding variant (the archiver +// tool and the linker pass member payloads through encoded), so it +// round-trips only via --encoding here. + +// RUN: clang-ssaf-format --encoding %S/Inputs/rt-static-library-empty.json -o %t/rt-static-library-empty-enc.json +// RUN: diff %S/Inputs/rt-static-library-empty.json %t/rt-static-library-empty-enc.json +// RUN: clang-ssaf-format --type auto --encoding %S/Inputs/rt-static-library-empty.json -o %t/rt-static-library-empty-enc-explicit.json +// RUN: diff %S/Inputs/rt-static-library-empty.json %t/rt-static-library-empty-enc-explicit.json diff --git a/clang/test/Analysis/Scalable/ssaf-format/Artifact/top-level.test b/clang/test/Analysis/Scalable/ssaf-format/Artifact/top-level.test index 904b1dffa7d7b..7838f3a90534c 100644 --- a/clang/test/Analysis/Scalable/ssaf-format/Artifact/top-level.test +++ b/clang/test/Analysis/Scalable/ssaf-format/Artifact/top-level.test @@ -40,4 +40,4 @@ // RUN: not clang-ssaf-format --type auto --encoding %S/Inputs/unknown-type.json 2>&1 \ // RUN: | FileCheck %s --match-full-lines --check-prefix=ENCODING-UNKNOWN-TYPE // ENCODING-UNKNOWN-TYPE: clang-ssaf-format: error: reading ArtifactEncoding from file '{{.*}}unknown-type.json' -// ENCODING-UNKNOWN-TYPE-NEXT: unknown value 'Mystery' for field 'type': expected 'TUSummary', or 'LUSummary' +// ENCODING-UNKNOWN-TYPE-NEXT: unknown value 'Mystery' for field 'type': expected 'TUSummary', 'LUSummary', or 'StaticLibrary' diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/duplicate-member.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/duplicate-member.json new file mode 100644 index 0000000000000..e3ea75ba7024c --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/duplicate-member.json @@ -0,0 +1,32 @@ +{ + "type": "StaticLibrary", + "target_triple": "arm64-apple-macosx", + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "members": [ + { + "type": "TUSummary", + "target_triple": "arm64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "a.cpp" + }, + "id_table": [], + "linkage_table": [], + "data": [] + }, + { + "type": "TUSummary", + "target_triple": "arm64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "a.cpp" + }, + "id_table": [], + "linkage_table": [], + "data": [] + } + ] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/invalid-syntax.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/invalid-syntax.json new file mode 100644 index 0000000000000..b0e13f61aa06e --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/invalid-syntax.json @@ -0,0 +1 @@ +{ invalid json } diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-inner-error.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-inner-error.json new file mode 100644 index 0000000000000..5134814e4166a --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-inner-error.json @@ -0,0 +1,17 @@ +{ + "members": [ + { + "type": "TUSummary", + "target_triple": "arm64-apple-macosx", + "id_table": [], + "linkage_table": [], + "data": [] + } + ], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-mismatched-type.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-mismatched-type.json new file mode 100644 index 0000000000000..cf85ec21d0d4e --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-mismatched-type.json @@ -0,0 +1,18 @@ +{ + "type": "StaticLibrary", + "target_triple": "arm64-apple-macosx", + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "members": [ + { + "type": "LUSummary", + "target_triple": "arm64-apple-macosx", + "lu_namespace": [], + "id_table": [], + "linkage_table": [], + "data": [] + } + ] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-missing-type.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-missing-type.json new file mode 100644 index 0000000000000..3ba536ee869f6 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-missing-type.json @@ -0,0 +1,20 @@ +{ + "members": [ + { + "target_triple": "arm64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "a.cpp" + }, + "id_table": [], + "linkage_table": [], + "data": [] + } + ], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-not-object.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-not-object.json new file mode 100644 index 0000000000000..d62af500c4b04 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-not-object.json @@ -0,0 +1,9 @@ +{ + "type": "StaticLibrary", + "target_triple": "arm64-apple-macosx", + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "members": ["not an object"] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-triple-mismatch.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-triple-mismatch.json new file mode 100644 index 0000000000000..39a8c23970d1d --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/member-triple-mismatch.json @@ -0,0 +1,21 @@ +{ + "type": "StaticLibrary", + "target_triple": "arm64-apple-macosx", + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "members": [ + { + "type": "TUSummary", + "target_triple": "x86_64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "a.cpp" + }, + "id_table": [], + "linkage_table": [], + "data": [] + } + ] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/members-not-array.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/members-not-array.json new file mode 100644 index 0000000000000..1686e7ac517b4 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/members-not-array.json @@ -0,0 +1,9 @@ +{ + "type": "StaticLibrary", + "target_triple": "arm64-apple-macosx", + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "members": {} +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/mismatched-type.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/mismatched-type.json new file mode 100644 index 0000000000000..5c754996b18ce --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/mismatched-type.json @@ -0,0 +1,9 @@ +{ + "type": "TUSummary", + "target_triple": "arm64-apple-macosx", + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "members": [] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-members.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-members.json new file mode 100644 index 0000000000000..6f5059edaac85 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-members.json @@ -0,0 +1,8 @@ +{ + "type": "StaticLibrary", + "target_triple": "arm64-apple-macosx", + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + } +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-namespace.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-namespace.json new file mode 100644 index 0000000000000..59aa554be123c --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-namespace.json @@ -0,0 +1,5 @@ +{ + "type": "StaticLibrary", + "target_triple": "arm64-apple-macosx", + "members": [] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-target-triple.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-target-triple.json new file mode 100644 index 0000000000000..6ab76fc69aa0c --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-target-triple.json @@ -0,0 +1,8 @@ +{ + "type": "StaticLibrary", + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "members": [] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-type.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-type.json new file mode 100644 index 0000000000000..9e470063d7865 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/missing-type.json @@ -0,0 +1,8 @@ +{ + "target_triple": "arm64-apple-macosx", + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "members": [] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-invalid-kind.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-invalid-kind.json new file mode 100644 index 0000000000000..5e96399f34387 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-invalid-kind.json @@ -0,0 +1,9 @@ +{ + "type": "StaticLibrary", + "target_triple": "arm64-apple-macosx", + "namespace": { + "kind": "invalid_kind", + "name": "libfoo" + }, + "members": [] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-kind.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-kind.json new file mode 100644 index 0000000000000..7aa0a00cdc4a2 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-kind.json @@ -0,0 +1,8 @@ +{ + "type": "StaticLibrary", + "target_triple": "arm64-apple-macosx", + "namespace": { + "name": "libfoo" + }, + "members": [] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-name.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-name.json new file mode 100644 index 0000000000000..750e310763ee9 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-missing-name.json @@ -0,0 +1,8 @@ +{ + "type": "StaticLibrary", + "target_triple": "arm64-apple-macosx", + "namespace": { + "kind": "StaticLibrary" + }, + "members": [] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-wrong-kind.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-wrong-kind.json new file mode 100644 index 0000000000000..073cd4a505d2f --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/namespace-wrong-kind.json @@ -0,0 +1,9 @@ +{ + "type": "StaticLibrary", + "target_triple": "arm64-apple-macosx", + "namespace": { + "kind": "CompilationUnit", + "name": "libfoo" + }, + "members": [] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-json-extension.txt b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-json-extension.txt new file mode 100644 index 0000000000000..0967ef424bce6 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-json-extension.txt @@ -0,0 +1 @@ +{} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-normalized-target-triple.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-normalized-target-triple.json new file mode 100644 index 0000000000000..f9b0c3351c0ee --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-normalized-target-triple.json @@ -0,0 +1,9 @@ +{ + "type": "StaticLibrary", + "target_triple": "arm64-macosx-apple", + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "members": [] +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-object.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-object.json new file mode 100644 index 0000000000000..fe51488c7066f --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/not-object.json @@ -0,0 +1 @@ +[] diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-empty.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-empty.json new file mode 100644 index 0000000000000..3059d007a2e9e --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-empty.json @@ -0,0 +1,9 @@ +{ + "members": [], + "namespace": { + "kind": "StaticLibrary", + "name": "libempty" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-multiple-members.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-multiple-members.json new file mode 100644 index 0000000000000..7e1636d2740c8 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-multiple-members.json @@ -0,0 +1,43 @@ +{ + "members": [ + { + "data": [], + "id_table": [], + "linkage_table": [], + "target_triple": "arm64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "a.cpp" + }, + "type": "TUSummary" + }, + { + "data": [], + "id_table": [], + "linkage_table": [], + "target_triple": "arm64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "b.cpp" + }, + "type": "TUSummary" + }, + { + "data": [], + "id_table": [], + "linkage_table": [], + "target_triple": "arm64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "c.cpp" + }, + "type": "TUSummary" + } + ], + "namespace": { + "kind": "StaticLibrary", + "name": "libmulti" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-single-member.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-single-member.json new file mode 100644 index 0000000000000..7ad6021fbc447 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/rt-single-member.json @@ -0,0 +1,21 @@ +{ + "members": [ + { + "data": [], + "id_table": [], + "linkage_table": [], + "target_triple": "arm64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "a.cpp" + }, + "type": "TUSummary" + } + ], + "namespace": { + "kind": "StaticLibrary", + "name": "libfoo" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/unsorted-members-input.json b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/unsorted-members-input.json new file mode 100644 index 0000000000000..fb6820ca7a07d --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/Inputs/unsorted-members-input.json @@ -0,0 +1,43 @@ +{ + "members": [ + { + "data": [], + "id_table": [], + "linkage_table": [], + "target_triple": "arm64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "c.cpp" + }, + "type": "TUSummary" + }, + { + "data": [], + "id_table": [], + "linkage_table": [], + "target_triple": "arm64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "a.cpp" + }, + "type": "TUSummary" + }, + { + "data": [], + "id_table": [], + "linkage_table": [], + "target_triple": "arm64-apple-macosx", + "tu_namespace": { + "kind": "CompilationUnit", + "name": "b.cpp" + }, + "type": "TUSummary" + } + ], + "namespace": { + "kind": "StaticLibrary", + "name": "libmulti" + }, + "target_triple": "arm64-apple-macosx", + "type": "StaticLibrary" +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/io.test b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/io.test new file mode 100644 index 0000000000000..22a94dd600ec7 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/io.test @@ -0,0 +1,49 @@ +// File-I/O error tests: readJSON() input validation and writeJSON() output +// validation for clang-ssaf-format --type static-library. + +// RUN: rm -rf %t +// RUN: mkdir -p %t + +// ============================================================================ +// readJSON() errors +// ============================================================================ + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/nonexistent.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=NONEXISTENT-FILE +// NONEXISTENT-FILE: clang-ssaf-format: error: failed to validate path '{{.*}}nonexistent.json': Path does not exist + +// RUN: mkdir -p %t/test-directory.json +// RUN: not clang-ssaf-format --type static-library %t/test-directory.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=PATH-IS-DIRECTORY +// PATH-IS-DIRECTORY: clang-ssaf-format: error: failed to validate path '{{.*}}test-directory.json': Path is not a file + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/not-json-extension.txt 2>&1 \ +// RUN: | FileCheck %s --check-prefix=NOT-JSON-EXTENSION +// NOT-JSON-EXTENSION: clang-ssaf-format: error: failed to validate path '{{.*}}not-json-extension.txt': No format registered for extension 'txt' + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/invalid-syntax.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=INVALID-SYNTAX +// INVALID-SYNTAX: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}invalid-syntax.json' +// INVALID-SYNTAX-NEXT: {{.*}}Expected object key + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/not-object.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=NOT-OBJECT +// NOT-OBJECT: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}not-object.json' +// NOT-OBJECT-NEXT: failed to read StaticLibrary: expected JSON object + +// ============================================================================ +// writeJSON() errors +// ============================================================================ + +// RUN: echo '{}' > %t/existing.json +// RUN: not clang-ssaf-format --type static-library %S/Inputs/rt-empty.json -o %t/existing.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=WRITE-FILE-ALREADY-EXISTS +// WRITE-FILE-ALREADY-EXISTS: clang-ssaf-format: error: failed to validate path '{{.*}}existing.json': File already exists + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/rt-empty.json -o %t/nonexistent-dir/test.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=WRITE-PARENT-DIRECTORY-NOT-FOUND +// WRITE-PARENT-DIRECTORY-NOT-FOUND: clang-ssaf-format: error: failed to validate path '{{.*}}nonexistent-dir{{.}}test.json': Parent directory does not exist + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/rt-empty.json -o %t/test.txt 2>&1 \ +// RUN: | FileCheck %s --check-prefix=WRITE-NOT-JSON-EXTENSION +// WRITE-NOT-JSON-EXTENSION: clang-ssaf-format: error: failed to validate path '{{.*}}test.txt': No format registered for extension 'txt' diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/permissions.test b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/permissions.test new file mode 100644 index 0000000000000..a9455a050c55c --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/permissions.test @@ -0,0 +1,40 @@ +// Tests for clang-ssaf-format --type static-library that require filesystem +// permission support (symlinks, chmod). + +// UNSUPPORTED: system-windows +// REQUIRES: non-root-user + +// RUN: rm -rf %t +// RUN: mkdir -p %t + +// ============================================================================ +// Broken symlink +// ============================================================================ + +// RUN: ln -sf %t/nonexistent-target.json %t/broken-symlink.json +// RUN: not clang-ssaf-format --type static-library %t/broken-symlink.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=BROKEN-SYMLINK +// BROKEN-SYMLINK: clang-ssaf-format: error: failed to validate path '{{.*}}broken-symlink.json': Path does not exist + +// ============================================================================ +// No read permission +// ============================================================================ + +// RUN: cp %S/Inputs/rt-empty.json %t/no-read-permission.json +// RUN: chmod -r %t/no-read-permission.json +// RUN: not clang-ssaf-format --type static-library %t/no-read-permission.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=NO-READ-PERMISSION +// RUN: chmod +r %t/no-read-permission.json +// NO-READ-PERMISSION: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}no-read-permission.json' +// NO-READ-PERMISSION-NEXT: failed to read file '{{.*}}no-read-permission.json': {{.*}} + +// ============================================================================ +// Write to directory without write permission +// ============================================================================ + +// RUN: mkdir -p %t/write-protected-dir +// RUN: chmod -w %t/write-protected-dir +// RUN: not clang-ssaf-format --type static-library %S/Inputs/rt-empty.json -o %t/write-protected-dir/test.json 2>&1 \ +// RUN: | FileCheck %s --check-prefix=WRITE-STREAM-OPEN-FAILURE +// RUN: chmod +w %t/write-protected-dir +// WRITE-STREAM-OPEN-FAILURE: clang-ssaf-format: error: failed to validate path '{{.*}}write-protected-dir{{.}}test.json': Parent directory is not writable diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/round-trip.test b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/round-trip.test new file mode 100644 index 0000000000000..b63dd08a3975b --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/round-trip.test @@ -0,0 +1,42 @@ +// Round-trip tests: read a StaticLibrary JSON input, write it back out, +// and diff the result against the original. +// +// StaticLibrary has only an encoding representation, so --encoding is a +// no-op for --type static-library and both invocations must produce the +// same output. + +// RUN: rm -rf %t +// RUN: mkdir -p %t + +// ============================================================================ +// Round-Trip Tests +// ============================================================================ + +// RUN: clang-ssaf-format --type static-library %S/Inputs/rt-empty.json -o %t/rt-empty.json +// RUN: diff %S/Inputs/rt-empty.json %t/rt-empty.json +// RUN: clang-ssaf-format --type static-library --encoding %S/Inputs/rt-empty.json -o %t/rt-empty-enc.json +// RUN: diff %S/Inputs/rt-empty.json %t/rt-empty-enc.json + +// RUN: clang-ssaf-format --type static-library %S/Inputs/rt-single-member.json -o %t/rt-single-member.json +// RUN: diff %S/Inputs/rt-single-member.json %t/rt-single-member.json +// RUN: clang-ssaf-format --type static-library --encoding %S/Inputs/rt-single-member.json -o %t/rt-single-member-enc.json +// RUN: diff %S/Inputs/rt-single-member.json %t/rt-single-member-enc.json + +// RUN: clang-ssaf-format --type static-library %S/Inputs/rt-multiple-members.json -o %t/rt-multiple-members.json +// RUN: diff %S/Inputs/rt-multiple-members.json %t/rt-multiple-members.json +// RUN: clang-ssaf-format --type static-library --encoding %S/Inputs/rt-multiple-members.json -o %t/rt-multiple-members-enc.json +// RUN: diff %S/Inputs/rt-multiple-members.json %t/rt-multiple-members-enc.json + +// ============================================================================ +// Member sort order +// ============================================================================ +// +// Members are stored in a std::set ordered by TUNamespace, so the writer +// emits them sorted regardless of input order. unsorted-members-input.json +// has the same members as rt-multiple-members.json but listed c, a, b; +// the writer must produce the canonical (a, b, c) ordering. + +// RUN: clang-ssaf-format --type static-library %S/Inputs/unsorted-members-input.json -o %t/unsorted-members-output.json +// RUN: diff %S/Inputs/rt-multiple-members.json %t/unsorted-members-output.json +// RUN: clang-ssaf-format --type static-library --encoding %S/Inputs/unsorted-members-input.json -o %t/unsorted-members-output-enc.json +// RUN: diff %S/Inputs/rt-multiple-members.json %t/unsorted-members-output-enc.json diff --git a/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/top-level.test b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/top-level.test new file mode 100644 index 0000000000000..a27496f2681e6 --- /dev/null +++ b/clang/test/Analysis/Scalable/ssaf-format/StaticLibrary/top-level.test @@ -0,0 +1,125 @@ +// Top-level StaticLibrary structure tests: target_triple, namespace +// (StaticLibrary kind), members array, and per-member validation. Mirrors +// the LUSummary top-level test pattern but covers the StaticLibrary-specific +// shape. + +// ============================================================================ +// readStaticLibrary() / writeStaticLibrary() type-field errors +// ============================================================================ + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/missing-type.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MISSING-TYPE +// MISSING-TYPE: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}missing-type.json' +// MISSING-TYPE-NEXT: failed to read summary type from field 'type': expected JSON string + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/mismatched-type.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MISMATCHED-TYPE +// MISMATCHED-TYPE: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}mismatched-type.json' +// MISMATCHED-TYPE-NEXT: expected 'StaticLibrary' for field 'type' but got 'TUSummary' + +// ============================================================================ +// target_triple errors +// ============================================================================ + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/missing-target-triple.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MISSING-TARGET-TRIPLE +// MISSING-TARGET-TRIPLE: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}missing-target-triple.json' +// MISSING-TARGET-TRIPLE-NEXT: failed to read TargetTriple from field 'target_triple': expected JSON string + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/not-normalized-target-triple.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=NOT-NORMALIZED-TARGET-TRIPLE +// NOT-NORMALIZED-TARGET-TRIPLE: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}not-normalized-target-triple.json' +// NOT-NORMALIZED-TARGET-TRIPLE-NEXT: reading TargetTriple from field 'target_triple' +// NOT-NORMALIZED-TARGET-TRIPLE-NEXT: target triple 'arm64-macosx-apple' is not in normalized form (expected 'arm64-apple-macosx') + +// ============================================================================ +// namespace errors +// ============================================================================ + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/missing-namespace.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MISSING-NAMESPACE +// MISSING-NAMESPACE: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}missing-namespace.json' +// MISSING-NAMESPACE-NEXT: failed to read BuildNamespace from field 'namespace': expected JSON object + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/namespace-missing-kind.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-MISSING-KIND +// NAMESPACE-MISSING-KIND: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}namespace-missing-kind.json' +// NAMESPACE-MISSING-KIND-NEXT: reading BuildNamespace from field 'namespace' +// NAMESPACE-MISSING-KIND-NEXT: failed to read BuildNamespaceKind from field 'kind': expected JSON string + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/namespace-invalid-kind.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-INVALID-KIND +// NAMESPACE-INVALID-KIND: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}namespace-invalid-kind.json' +// NAMESPACE-INVALID-KIND-NEXT: reading BuildNamespace from field 'namespace' +// NAMESPACE-INVALID-KIND-NEXT: reading BuildNamespaceKind from field 'kind' +// NAMESPACE-INVALID-KIND-NEXT: invalid BuildNamespaceKind value 'invalid_kind' for field 'kind' + +// StaticLibrary's namespace field must specifically have kind=StaticLibrary. +// Any other well-formed kind (CompilationUnit, LinkUnit) is reported as a +// mismatch. +// RUN: not clang-ssaf-format --type static-library %S/Inputs/namespace-wrong-kind.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-WRONG-KIND +// NAMESPACE-WRONG-KIND: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}namespace-wrong-kind.json' +// NAMESPACE-WRONG-KIND-NEXT: expected 'StaticLibrary' for field 'namespace.kind' but got 'CompilationUnit' + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/namespace-missing-name.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-MISSING-NAME +// NAMESPACE-MISSING-NAME: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}namespace-missing-name.json' +// NAMESPACE-MISSING-NAME-NEXT: reading BuildNamespace from field 'namespace' +// NAMESPACE-MISSING-NAME-NEXT: failed to read BuildNamespaceName from field 'name': expected JSON string + +// ============================================================================ +// members array errors +// ============================================================================ + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/missing-members.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MISSING-MEMBERS +// MISSING-MEMBERS: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}missing-members.json' +// MISSING-MEMBERS-NEXT: failed to read StaticLibrary members from field 'members': expected JSON array + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/members-not-array.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MEMBERS-NOT-ARRAY +// MEMBERS-NOT-ARRAY: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}members-not-array.json' +// MEMBERS-NOT-ARRAY-NEXT: failed to read StaticLibrary members from field 'members': expected JSON array + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/member-not-object.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MEMBER-NOT-OBJECT +// MEMBER-NOT-OBJECT: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}member-not-object.json' +// MEMBER-NOT-OBJECT-NEXT: failed to read StaticLibrary member from index '0': expected JSON object + +// ============================================================================ +// per-member errors +// ============================================================================ + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/member-mismatched-type.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MEMBER-MISMATCHED-TYPE +// MEMBER-MISMATCHED-TYPE: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}member-mismatched-type.json' +// MEMBER-MISMATCHED-TYPE-NEXT: reading StaticLibrary member from index '0' +// MEMBER-MISMATCHED-TYPE-NEXT: expected 'TUSummary' for field 'type' but got 'LUSummary' + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/member-missing-type.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MEMBER-MISSING-TYPE +// MEMBER-MISSING-TYPE: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}member-missing-type.json' +// MEMBER-MISSING-TYPE-NEXT: reading StaticLibrary member from index '0' +// MEMBER-MISSING-TYPE-NEXT: failed to read summary type from field 'type': expected JSON string + +// Verifies that errors raised inside readTUSummaryEncodingFromObject (here, +// a missing tu_namespace on the member) are properly wrapped with the +// "reading StaticLibrary member from index 'N'" context, then with the +// outer "reading StaticLibrary from file '...'" context. +// RUN: not clang-ssaf-format --type static-library %S/Inputs/member-inner-error.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MEMBER-INNER-ERROR +// MEMBER-INNER-ERROR: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}member-inner-error.json' +// MEMBER-INNER-ERROR-NEXT: reading StaticLibrary member from index '0' +// MEMBER-INNER-ERROR-NEXT: failed to read BuildNamespace from field 'tu_namespace': expected JSON object + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/member-triple-mismatch.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=MEMBER-TRIPLE-MISMATCH +// MEMBER-TRIPLE-MISMATCH: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}member-triple-mismatch.json' +// MEMBER-TRIPLE-MISMATCH-NEXT: reading StaticLibrary member from index '0' +// MEMBER-TRIPLE-MISMATCH-NEXT: expected 'arm64-apple-macosx' for field 'target_triple' but got 'x86_64-apple-macosx' + +// RUN: not clang-ssaf-format --type static-library %S/Inputs/duplicate-member.json 2>&1 \ +// RUN: | FileCheck %s --match-full-lines --check-prefix=DUPLICATE-MEMBER +// DUPLICATE-MEMBER: clang-ssaf-format: error: reading StaticLibrary from file '{{.*}}duplicate-member.json' +// DUPLICATE-MEMBER-NEXT: failed to insert StaticLibrary member at index '1': encountered duplicate 'BuildNamespace(CompilationUnit, a.cpp)' diff --git a/clang/tools/clang-ssaf-format/SSAFFormat.cpp b/clang/tools/clang-ssaf-format/SSAFFormat.cpp index 9416b45c974b5..5b94da384cb7d 100644 --- a/clang/tools/clang-ssaf-format/SSAFFormat.cpp +++ b/clang/tools/clang-ssaf-format/SSAFFormat.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummaryEncoding.h" +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" #include "clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h" #include "clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormatRegistry.h" @@ -39,7 +40,7 @@ namespace { // Summary Type //===----------------------------------------------------------------------===// -enum class SummaryType { Auto, TU, LU, WPA }; +enum class SummaryType { Auto, TU, LU, StaticLibrary, WPA }; //===----------------------------------------------------------------------===// // Command-Line Options @@ -63,6 +64,8 @@ cl::opt<SummaryType> Type( "Detect type from the file's 'type' field"), clEnumValN(SummaryType::TU, "tu", "Translation unit summary"), clEnumValN(SummaryType::LU, "lu", "Link unit summary"), + clEnumValN(SummaryType::StaticLibrary, "static-library", + "Static library of translation unit summaries"), clEnumValN(SummaryType::WPA, "wpa", "Whole-program analysis suite")), cl::init(SummaryType::Auto), cl::cat(SsafFormatCategory)); @@ -295,6 +298,12 @@ void convert(const FormatInput &FI) { &SerializationFormat::writeLUSummary); } return; + case SummaryType::StaticLibrary: + // StaticLibrary has only an encoded representation, so --encoding is a + // no-op here: both paths route to readStaticLibrary / writeStaticLibrary. + run(FI, &SerializationFormat::readStaticLibrary, + &SerializationFormat::writeStaticLibrary); + return; case SummaryType::WPA: run(FI, &SerializationFormat::readWPASuite, &SerializationFormat::writeWPASuite); diff --git a/clang/unittests/ScalableStaticAnalysis/BuildNamespaceTest.cpp b/clang/unittests/ScalableStaticAnalysis/BuildNamespaceTest.cpp index e98efd45ea6b0..5089796a2b0b8 100644 --- a/clang/unittests/ScalableStaticAnalysis/BuildNamespaceTest.cpp +++ b/clang/unittests/ScalableStaticAnalysis/BuildNamespaceTest.cpp @@ -83,6 +83,8 @@ TEST(BuildNamespaceKindTest, FormatProvider) { "CompilationUnit"); EXPECT_EQ(llvm::formatv("{0}", BuildNamespaceKind::LinkUnit).str(), "LinkUnit"); + EXPECT_EQ(llvm::formatv("{0}", BuildNamespaceKind::StaticLibrary).str(), + "StaticLibrary"); } TEST(BuildNamespaceKindTest, StreamOutputCompilationUnit) { @@ -97,6 +99,12 @@ TEST(BuildNamespaceKindTest, StreamOutputLinkUnit) { EXPECT_EQ(S, "LinkUnit"); } +TEST(BuildNamespaceKindTest, StreamOutputStaticLibrary) { + std::string S; + llvm::raw_string_ostream(S) << BuildNamespaceKind::StaticLibrary; + EXPECT_EQ(S, "StaticLibrary"); +} + TEST(BuildNamespaceTest, FormatProvider) { EXPECT_EQ( llvm::formatv("{0}", BuildNamespace(BuildNamespaceKind::CompilationUnit, diff --git a/clang/unittests/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendActionTest.cpp b/clang/unittests/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendActionTest.cpp index 2d65fb675eec3..4baf593f57eef 100644 --- a/clang/unittests/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendActionTest.cpp +++ b/clang/unittests/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendActionTest.cpp @@ -98,6 +98,16 @@ class FailingSerializationFormat final : public SerializationFormat { return failing("writeLUSummaryEncoding"); } + llvm::Expected<StaticLibrary> + readStaticLibrary(llvm::StringRef Path) override { + return failing("readStaticLibrary"); + } + + llvm::Error writeStaticLibrary(const StaticLibrary &S, + llvm::StringRef Path) override { + return failing("writeStaticLibrary"); + } + llvm::Expected<WPASuite> readWPASuite(llvm::StringRef Path) override { return failing("readWPASuite"); } @@ -179,6 +189,13 @@ class CapturingSerializationFormat final : public SerializationFormat { llvm::StringRef) override { return llvm::Error::success(); } + llvm::Expected<StaticLibrary> readStaticLibrary(llvm::StringRef) override { + return llvm::createStringError("not implemented"); + } + llvm::Error writeStaticLibrary(const StaticLibrary &, + llvm::StringRef) override { + return llvm::Error::success(); + } llvm::Expected<WPASuite> readWPASuite(llvm::StringRef) override { return llvm::createStringError("not implemented"); } diff --git a/clang/unittests/ScalableStaticAnalysis/ModelStringConversionsTest.cpp b/clang/unittests/ScalableStaticAnalysis/ModelStringConversionsTest.cpp index ff24a8358612d..b0c741d83feaf 100644 --- a/clang/unittests/ScalableStaticAnalysis/ModelStringConversionsTest.cpp +++ b/clang/unittests/ScalableStaticAnalysis/ModelStringConversionsTest.cpp @@ -27,6 +27,11 @@ TEST(BuildNamespaceKindStringTest, ToStringLinkUnit) { "LinkUnit"); } +TEST(BuildNamespaceKindStringTest, ToStringStaticLibrary) { + EXPECT_EQ(buildNamespaceKindToString(BuildNamespaceKind::StaticLibrary), + "StaticLibrary"); +} + TEST(BuildNamespaceKindStringTest, FromStringCompilationUnit) { EXPECT_EQ(buildNamespaceKindFromString("CompilationUnit"), BuildNamespaceKind::CompilationUnit); @@ -37,9 +42,15 @@ TEST(BuildNamespaceKindStringTest, FromStringLinkUnit) { BuildNamespaceKind::LinkUnit); } +TEST(BuildNamespaceKindStringTest, FromStringStaticLibrary) { + EXPECT_EQ(buildNamespaceKindFromString("StaticLibrary"), + BuildNamespaceKind::StaticLibrary); +} + TEST(BuildNamespaceKindStringTest, FromStringUnknown) { EXPECT_EQ(buildNamespaceKindFromString("compilation_unit"), std::nullopt); EXPECT_EQ(buildNamespaceKindFromString("link_unit"), std::nullopt); + EXPECT_EQ(buildNamespaceKindFromString("static_library"), std::nullopt); EXPECT_EQ(buildNamespaceKindFromString(""), std::nullopt); EXPECT_EQ(buildNamespaceKindFromString("unknown"), std::nullopt); } @@ -51,6 +62,9 @@ TEST(BuildNamespaceKindStringTest, RoundTrip) { EXPECT_EQ(buildNamespaceKindFromString( buildNamespaceKindToString(BuildNamespaceKind::LinkUnit)), BuildNamespaceKind::LinkUnit); + EXPECT_EQ(buildNamespaceKindFromString( + buildNamespaceKindToString(BuildNamespaceKind::StaticLibrary)), + BuildNamespaceKind::StaticLibrary); } //===----------------------------------------------------------------------===// diff --git a/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.cpp b/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.cpp index 915fe15af832c..4a01248b53ac7 100644 --- a/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.cpp +++ b/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.cpp @@ -9,6 +9,7 @@ #include "Registries/MockSerializationFormat.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummaryEncoding.h" +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" #include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h" #include "clang/ScalableStaticAnalysis/Core/Model/EntityName.h" @@ -198,6 +199,16 @@ llvm::Error MockSerializationFormat::writeLUSummaryEncoding( "MockSerializationFormat does not support LUSummaryEncoding"); } +llvm::Expected<StaticLibrary> +MockSerializationFormat::readStaticLibrary(llvm::StringRef Path) { + llvm_unreachable("MockSerializationFormat does not support StaticLibrary"); +} + +llvm::Error MockSerializationFormat::writeStaticLibrary(const StaticLibrary &S, + llvm::StringRef Path) { + llvm_unreachable("MockSerializationFormat does not support StaticLibrary"); +} + llvm::Expected<WPASuite> MockSerializationFormat::readWPASuite(llvm::StringRef Path) { llvm_unreachable("MockSerializationFormat does not support WPASuite"); diff --git a/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.h b/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.h index 2ecaab9fb0132..0cadd0b98fe3a 100644 --- a/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.h +++ b/clang/unittests/ScalableStaticAnalysis/Registries/MockSerializationFormat.h @@ -44,6 +44,12 @@ class MockSerializationFormat final : public SerializationFormat { llvm::Error writeLUSummaryEncoding(const LUSummaryEncoding &SummaryEncoding, llvm::StringRef Path) override; + llvm::Expected<StaticLibrary> + readStaticLibrary(llvm::StringRef Path) override; + + llvm::Error writeStaticLibrary(const StaticLibrary &S, + llvm::StringRef Path) override; + llvm::Expected<Artifact> readArtifact(llvm::StringRef Path) override; llvm::Error writeArtifact(const Artifact &A, llvm::StringRef Path) override; diff --git a/clang/unittests/ScalableStaticAnalysis/TestFixture.h b/clang/unittests/ScalableStaticAnalysis/TestFixture.h index 22fbff754fb37..e7eed940938ce 100644 --- a/clang/unittests/ScalableStaticAnalysis/TestFixture.h +++ b/clang/unittests/ScalableStaticAnalysis/TestFixture.h @@ -11,6 +11,7 @@ #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummaryEncoding.h" +#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h" #include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h" #include "clang/ScalableStaticAnalysis/Core/Model/BuildNamespace.h" #include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h" >From 71cf72cc46052e2e47fffc6ac314e8c413cba508 Mon Sep 17 00:00:00 2001 From: Aviral Goel <[email protected]> Date: Mon, 29 Jun 2026 17:59:17 -0700 Subject: [PATCH 2/2] Fix documentation --- .../ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h index 74b6fb4ea169f..ae8cfa4c7bacb 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h +++ b/clang/include/clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h @@ -27,8 +27,8 @@ namespace clang::ssaf { /// /// A StaticLibrary bundles member translation units without performing /// entity resolution, mirroring the role of ar / libtool -static / lib.exe -/// in native build pipelines. The final linker is responsible for -/// selective inclusion when a StaticLibrary appears on its command line. +/// in native build pipelines. It is consumed by the EntityLinker for +/// selective inclusion when passed as a command line argument. /// /// Static libraries are single-architecture: every member's target triple /// must equal the library's. Multi-architecture static libraries are _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
