1//===- TUSummaryExtractorFrontendAction.cpp -------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.h"
10#include "clang/AST/ASTConsumer.h"
11#include "clang/Basic/DiagnosticFrontend.h"
12#include "clang/Frontend/MultiplexConsumer.h"
13#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/SerializationFormatRegistry.h"
14#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/ExtractorRegistry.h"
15#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummary.h"
16#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryBuilder.h"
17#include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/ADT/StringRef.h"
20#include "llvm/Support/IOSandbox.h"
21#include "llvm/Support/Path.h"
22#include <memory>
23#include <string>
24#include <vector>
25
26using namespace clang;
27using namespace ssaf;
28
29static std::optional<std::pair<llvm::StringRef, llvm::StringRef>>
30parseOutputFileFormatAndPathOrReportError(DiagnosticsEngine &Diags,
31 StringRef SSAFTUSummaryFile) {
32
33 StringRef Ext = llvm::sys::path::extension(path: SSAFTUSummaryFile);
34 StringRef FilePath = SSAFTUSummaryFile.drop_back(N: Ext.size());
35
36 if (!Ext.consume_front(Prefix: ".") || FilePath.empty()) {
37 Diags.Report(DiagID: diag::warn_ssaf_extract_tu_summary_file_unknown_format)
38 << SSAFTUSummaryFile;
39 return std::nullopt;
40 }
41
42 if (!isFormatRegistered(FormatName: Ext)) {
43 Diags.Report(DiagID: diag::warn_ssaf_extract_tu_summary_file_unknown_output_format)
44 << Ext << SSAFTUSummaryFile;
45 return std::nullopt;
46 }
47
48 return std::pair{Ext, FilePath};
49}
50
51/// Return \c true if reported unrecognized extractors.
52static bool
53reportUnrecognizedExtractorNames(DiagnosticsEngine &Diags,
54 ArrayRef<std::string> SSAFExtractSummaries) {
55 if (SSAFExtractSummaries.empty()) {
56 Diags.Report(DiagID: diag::warn_ssaf_must_enable_summary_extractors);
57 return true;
58 }
59
60 std::vector<StringRef> UnrecognizedExtractorNames;
61 for (StringRef Name : SSAFExtractSummaries)
62 if (!isTUSummaryExtractorRegistered(SummaryName: Name))
63 UnrecognizedExtractorNames.push_back(x: Name);
64
65 if (!UnrecognizedExtractorNames.empty()) {
66 Diags.Report(DiagID: diag::warn_ssaf_extract_summary_unknown_extractor_name)
67 << UnrecognizedExtractorNames.size()
68 << llvm::join(R&: UnrecognizedExtractorNames, Separator: ", ");
69 return true;
70 }
71
72 return false;
73}
74
75static std::vector<std::unique_ptr<ASTConsumer>>
76makeTUSummaryExtractors(TUSummaryBuilder &Builder,
77 ArrayRef<std::string> SSAFExtractSummaries) {
78 std::vector<std::unique_ptr<ASTConsumer>> Extractors;
79 Extractors.reserve(n: SSAFExtractSummaries.size());
80 for (StringRef Name : SSAFExtractSummaries) {
81 assert(isTUSummaryExtractorRegistered(Name));
82 Extractors.push_back(x: makeTUSummaryExtractor(SummaryName: Name, Builder));
83 }
84 return Extractors;
85}
86
87namespace {
88
89/// Drives all extractor \c ASTConsumers and serializes the completed
90/// \c TUSummary.
91///
92/// Derives from \c MultiplexConsumer so every \c ASTConsumer virtual method is
93/// automatically forwarded to each extractor.
94class TUSummaryRunner final : public MultiplexConsumer {
95public:
96 static std::unique_ptr<TUSummaryRunner> create(CompilerInstance &CI,
97 StringRef InFile);
98
99private:
100 TUSummaryRunner(StringRef InFile, std::unique_ptr<SerializationFormat> Format,
101 const FrontendOptions &Opts);
102
103 void HandleTranslationUnit(ASTContext &Ctx) override;
104
105 TUSummary Summary;
106 TUSummaryBuilder Builder = TUSummaryBuilder(Summary);
107 std::unique_ptr<SerializationFormat> Format;
108 const FrontendOptions &Opts;
109};
110} // namespace
111
112std::unique_ptr<TUSummaryRunner> TUSummaryRunner::create(CompilerInstance &CI,
113 StringRef InFile) {
114 const FrontendOptions &Opts = CI.getFrontendOpts();
115 DiagnosticsEngine &Diags = CI.getDiagnostics();
116
117 auto MaybePair =
118 parseOutputFileFormatAndPathOrReportError(Diags, SSAFTUSummaryFile: Opts.SSAFTUSummaryFile);
119 if (!MaybePair.has_value())
120 return nullptr;
121 auto [FormatName, OutputPath] = MaybePair.value();
122
123 if (reportUnrecognizedExtractorNames(Diags, SSAFExtractSummaries: Opts.SSAFExtractSummaries))
124 return nullptr;
125
126 return std::unique_ptr<TUSummaryRunner>{
127 new TUSummaryRunner{InFile, makeFormat(FormatName), Opts}};
128}
129
130TUSummaryRunner::TUSummaryRunner(StringRef InFile,
131 std::unique_ptr<SerializationFormat> Format,
132 const FrontendOptions &Opts)
133 : MultiplexConsumer(std::vector<std::unique_ptr<ASTConsumer>>{}),
134 Summary(BuildNamespace(BuildNamespaceKind::CompilationUnit, InFile)),
135 Format(std::move(Format)), Opts(Opts) {
136 assert(this->Format);
137
138 // Now the Summary and the builders are constructed, we can also construct the
139 // extractors.
140 auto Extractors = makeTUSummaryExtractors(Builder, SSAFExtractSummaries: Opts.SSAFExtractSummaries);
141 assert(!Extractors.empty());
142
143 // We must initialize the Consumers here because our extractors need a
144 // Builder that holds a reference to the TUSummary, which would be only
145 // initialized after the MultiplexConsumer ctor. This is the only way we can
146 // avoid the use of the TUSummary before it starts its lifetime.
147 MultiplexConsumer::Consumers = std::move(Extractors);
148}
149
150void TUSummaryRunner::HandleTranslationUnit(ASTContext &Ctx) {
151 // First, invoke the Summary Extractors.
152 MultiplexConsumer::HandleTranslationUnit(Ctx);
153
154 // FIXME(sandboxing): Remove this by adopting `llvm::vfs::OutputBackend`.
155 llvm::sys::sandbox::ScopedSetting Guard = llvm::sys::sandbox::scopedDisable();
156
157 // Then serialize the result.
158 if (auto Err = Format->writeTUSummary(Summary, Path: Opts.SSAFTUSummaryFile)) {
159 Ctx.getDiagnostics().Report(DiagID: diag::warn_ssaf_write_tu_summary_failed)
160 << Opts.SSAFTUSummaryFile << llvm::toString(E: std::move(Err));
161 }
162}
163
164TUSummaryExtractorFrontendAction::~TUSummaryExtractorFrontendAction() = default;
165
166TUSummaryExtractorFrontendAction::TUSummaryExtractorFrontendAction(
167 std::unique_ptr<FrontendAction> WrappedAction)
168 : WrapperFrontendAction(std::move(WrappedAction)) {}
169
170std::unique_ptr<ASTConsumer>
171TUSummaryExtractorFrontendAction::CreateASTConsumer(CompilerInstance &CI,
172 StringRef InFile) {
173 auto WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
174 if (!WrappedConsumer)
175 return nullptr;
176
177 if (auto Runner = TUSummaryRunner::create(CI, InFile)) {
178 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
179 Consumers.reserve(n: 2);
180 Consumers.push_back(x: std::move(WrappedConsumer));
181 Consumers.push_back(x: std::move(Runner));
182 return std::make_unique<MultiplexConsumer>(args: std::move(Consumers));
183 }
184 return WrappedConsumer;
185}
186