blob: 9c84fa773d000f6fe0dce0b51e317416a315b049 [file] [log] [blame]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "nullability/inference/infer_tu.h"
#include <utility>
#include <vector>
#include "nullability/inference/collect_evidence.h"
#include "nullability/inference/inference.proto.h"
#include "nullability/inference/merge.h"
#include "nullability/inference/slot_fingerprint.h"
#include "nullability/pragma.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclBase.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/raw_ostream.h"
namespace clang::tidy::nullability {
namespace {
class InferenceManager {
public:
InferenceManager(ASTContext& Ctx, unsigned Iterations,
llvm::function_ref<bool(const Decl&)> Filter,
const NullabilityPragmas& Pragmas)
: Ctx(Ctx), Iterations(Iterations), Filter(Filter), Pragmas(Pragmas) {}
std::vector<Inference> inferenceRound(
EvidenceSites Sites, USRCache USRCache,
PreviousInferences InferencesFromLastRound) const {
std::vector<Inference> AllInference;
std::vector<Evidence> AllEvidence;
// Collect all evidence.
auto Emitter = evidenceEmitter([&](auto& E) { AllEvidence.push_back(E); },
USRCache, Ctx);
for (const auto* Decl : Sites.Declarations) {
if (Filter && !Filter(*Decl)) continue;
collectEvidenceFromTargetDeclaration(*Decl, Emitter, Pragmas);
}
for (const auto* Impl : Sites.Definitions) {
if (Filter && !Filter(*Impl)) continue;
if (auto Err = collectEvidenceFromDefinition(
*Impl, Emitter, USRCache, Pragmas, InferencesFromLastRound)) {
llvm::errs() << "Error in evidence collection: "
<< toString(std::move(Err)) << "\n";
}
}
// Group by symbol.
llvm::sort(AllEvidence, [&](const Evidence& L, const Evidence& R) {
return L.symbol().usr() < R.symbol().usr();
});
// For each symbol, combine evidence into an inference.
llvm::ArrayRef<Evidence> RemainingEvidence = AllEvidence;
while (!RemainingEvidence.empty()) {
auto Batch = RemainingEvidence.take_while([&](const Evidence& E) {
return E.symbol().usr() == RemainingEvidence.front().symbol().usr();
});
RemainingEvidence = RemainingEvidence.drop_front(Batch.size());
AllInference.push_back(mergeEvidence(Batch));
}
return AllInference;
}
std::vector<Inference> iterativelyInfer() const {
if (!Ctx.getLangOpts().CPlusPlus) {
llvm::errs() << "Skipping non-C++ input file: "
<< Ctx.getSourceManager()
.getFileEntryRefForID(
Ctx.getSourceManager().getMainFileID())
->getName()
<< "\n";
return std::vector<Inference>();
}
auto Sites = EvidenceSites::discover(Ctx);
USRCache USRCache;
std::vector<Inference> AllInference = inferenceRound(Sites, USRCache, {});
for (unsigned Iteration = 1; Iteration < Iterations; ++Iteration) {
llvm::DenseSet<SlotFingerprint> NullableFromLastRound;
llvm::DenseSet<SlotFingerprint> NonnullFromLastRound;
for (const auto& Inference : AllInference) {
for (const auto& SlotInference : Inference.slot_inference()) {
if (SlotInference.trivial() || SlotInference.conflict()) continue;
switch (SlotInference.nullability()) {
case Nullability::NULLABLE:
NullableFromLastRound.insert(
fingerprint(Inference.symbol().usr(), SlotInference.slot()));
break;
case Nullability::NONNULL:
NonnullFromLastRound.insert(
fingerprint(Inference.symbol().usr(), SlotInference.slot()));
break;
default:
break;
}
}
}
AllInference = inferenceRound(
Sites, USRCache, {NullableFromLastRound, NonnullFromLastRound});
}
return AllInference;
}
private:
ASTContext& Ctx;
unsigned Iterations;
llvm::function_ref<bool(const Decl&)> Filter;
const NullabilityPragmas& Pragmas;
};
} // namespace
std::vector<Inference> inferTU(ASTContext& Ctx,
const NullabilityPragmas& Pragmas,
unsigned Iterations,
llvm::function_ref<bool(const Decl&)> Filter) {
return InferenceManager(Ctx, Iterations, Filter, Pragmas).iterativelyInfer();
}
} // namespace clang::tidy::nullability