diff --git a/nullability/inference/collect_evidence.cc b/nullability/inference/collect_evidence.cc index 44275f252..96a2893ac 100644 --- a/nullability/inference/collect_evidence.cc +++ b/nullability/inference/collect_evidence.cc @@ -3266,7 +3266,7 @@ static void saveCFGScopeVirtualMethodIndex( CFGVirtualMethodSummary); } -llvm::Expected summarizeDefinition( +llvm::Expected> summarizeDefinition( const Decl& Definition, USRCache& USRCache, const NullabilityPragmas& Pragmas, const VirtualMethodIndex& VirtualMethodsInTU, @@ -3290,7 +3290,7 @@ llvm::Expected summarizeDefinition( // functions. if (!TargetFunc && !isInferenceTarget(Definition) && !hasAnyInferenceTargets(ReferencedDecls)) - return Summary; + return std::nullopt; ASTContext &Ctx = Definition.getASTContext(); llvm::Expected ACFG = @@ -3340,10 +3340,10 @@ llvm::Expected summarizeDefinition( .moveInto(Results)) return Error; - // When `Results` are empty, the post-analysis callbacks should not have been - // called, so `Summary` should be empty. But, to be sure, explicitly return a - // fresh empty summary. - if (Results.empty()) return CFGSummary(); + // When `Results` are empty, there's nothing to summarize and the + // post-analysis callbacks should not have been called, so `Summary` should be + // empty. Instead of returning an empty summary, don't return one. + if (Results.empty()) return std::nullopt; if (std::optional> &ExitBlockResult = Results[ACFG->getCFG().getExit().getBlockID()]) { @@ -3374,6 +3374,7 @@ llvm::Expected summarizeDefinition( "SAT solver reached iteration limit"); } + if (Summary.behavior_summaries_size() <= 0) return std::nullopt; return Summary; } diff --git a/nullability/inference/collect_evidence.h b/nullability/inference/collect_evidence.h index 85a0a33b2..6ba530c92 100644 --- a/nullability/inference/collect_evidence.h +++ b/nullability/inference/collect_evidence.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -193,9 +194,9 @@ llvm::Error collectEvidenceFromDefinition( const SolverFactory &MakeSolver = makeDefaultSolverForInference); // Summarizes Nullability-relevant behaviors in and context for `Definition`. -// If the resulting summary has no `behavior_summaries`, the analysis succeeded, -// but there's no relevant content. -llvm::Expected summarizeDefinition( +// If std::nullopt is returned, the analysis succeeded, but there's no relevant +// content. +llvm::Expected> summarizeDefinition( const Decl& Definition, USRCache& USRCache, const NullabilityPragmas& Pragmas, const VirtualMethodIndex& VirtualMethodsInTU, diff --git a/nullability/inference/collect_evidence_test.cc b/nullability/inference/collect_evidence_test.cc index 6b1c159a5..ce7dd7e84 100644 --- a/nullability/inference/collect_evidence_test.cc +++ b/nullability/inference/collect_evidence_test.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -299,18 +300,21 @@ TEST(SummarizeDefinitionTest, Deref) { definition_location: "input.cc:2:10" )pb"; - llvm::Expected Summary = summarizeTargetFuncDefinition(Src); - ASSERT_THAT_EXPECTED(Summary, llvm::Succeeded()); + llvm::Expected> SummaryResult = + summarizeTargetFuncDefinition(Src); + ASSERT_THAT_EXPECTED(SummaryResult, llvm::Succeeded()); + ASSERT_TRUE(SummaryResult->has_value()); + CFGSummary& Summary = **SummaryResult; // Given our reliance on particular atoms, we verify that the atom maps are // not empty. It is difficult to meaningfully connect the input code to more // detailed aspects of these maps. - EXPECT_THAT(Summary->logical_context().atom_defs(), Not(IsEmpty())); - Summary->mutable_logical_context()->clear_atom_defs(); + EXPECT_THAT(Summary.logical_context().atom_defs(), Not(IsEmpty())); + Summary.mutable_logical_context()->clear_atom_defs(); - EXPECT_THAT(Summary->logical_context().atom_deps(), Not(IsEmpty())); - Summary->mutable_logical_context()->clear_atom_deps(); + EXPECT_THAT(Summary.logical_context().atom_deps(), Not(IsEmpty())); + Summary.mutable_logical_context()->clear_atom_deps(); - EXPECT_THAT(*Summary, EqualsProto(Proto)); + EXPECT_THAT(Summary, EqualsProto(Proto)); } TEST(SummarizeDefinitionTest, NullableArgPassed) { @@ -340,18 +344,21 @@ TEST(SummarizeDefinitionTest, NullableArgPassed) { definition_location: "input.cc:3:10" )pb"; - llvm::Expected Summary = summarizeTargetFuncDefinition(Src); - ASSERT_THAT_EXPECTED(Summary, llvm::Succeeded()); + llvm::Expected> SummaryResult = + summarizeTargetFuncDefinition(Src); + ASSERT_THAT_EXPECTED(SummaryResult, llvm::Succeeded()); + ASSERT_TRUE(SummaryResult->has_value()); + CFGSummary& Summary = **SummaryResult; // Given our reliance on particular atoms, we verify that the atom maps are // not empty. It is difficult to meaningfully connect the input code to more // detailed aspects of these maps. - EXPECT_THAT(Summary->logical_context().atom_defs(), Not(IsEmpty())); - Summary->mutable_logical_context()->clear_atom_defs(); + EXPECT_THAT(Summary.logical_context().atom_defs(), Not(IsEmpty())); + Summary.mutable_logical_context()->clear_atom_defs(); - EXPECT_THAT(Summary->logical_context().atom_deps(), Not(IsEmpty())); - Summary->mutable_logical_context()->clear_atom_deps(); + EXPECT_THAT(Summary.logical_context().atom_deps(), Not(IsEmpty())); + Summary.mutable_logical_context()->clear_atom_deps(); - EXPECT_THAT(*Summary, EqualsProto(Proto)); + EXPECT_THAT(Summary, EqualsProto(Proto)); } class CollectEvidenceFromDefinitionTest diff --git a/nullability/inference/collect_evidence_test_utilities.cc b/nullability/inference/collect_evidence_test_utilities.cc index 23affe0d2..82223ff85 100644 --- a/nullability/inference/collect_evidence_test_utilities.cc +++ b/nullability/inference/collect_evidence_test_utilities.cc @@ -5,6 +5,7 @@ #include "nullability/inference/collect_evidence_test_utilities.h" #include +#include #include #include #include @@ -73,7 +74,7 @@ std::string printToString(DefinitionCollectionMode Mode) { llvm_unreachable("Unknown CollectionMode"); } -static llvm::Expected summarizeDefinitionNamed( +static llvm::Expected> summarizeDefinitionNamed( llvm::StringRef TargetName, llvm::StringRef Source) { USRCache UsrCache; NullabilityPragmas Pragmas; @@ -84,7 +85,7 @@ static llvm::Expected summarizeDefinitionNamed( getVirtualMethodIndex(AST.context(), UsrCache)); } -llvm::Expected summarizeTargetFuncDefinition( +llvm::Expected> summarizeTargetFuncDefinition( llvm::StringRef Source) { return summarizeDefinitionNamed("target", Source); } @@ -98,17 +99,20 @@ collectFromDefinitionViaSummaryWithErrors( USRCache UsrCache; std::vector Results; VirtualMethodIndex TUScopeVMI = getVirtualMethodIndex(ASTCtx, UsrCache); - llvm::Expected Summary = summarizeDefinition( + llvm::Expected> SummaryResult = summarizeDefinition( Definition, UsrCache, Pragmas, TUScopeVMI, MakeSolver); - if (!Summary) return {Summary.takeError(), Results}; + if (!SummaryResult) return {SummaryResult.takeError(), Results}; + if (!SummaryResult->has_value()) + return {llvm::Error::success(), std::vector{}}; + CFGSummary& Summary = **SummaryResult; auto VMIForPropagation = std::make_shared( - loadVirtualMethodsIndex(Summary->virtual_method_index())); + loadVirtualMethodsIndex(Summary.virtual_method_index())); // All overrides from anywhere in the TU are relevant for propagating // evidence, so we use the entire TU-scoped collection for this direction. VMIForPropagation->Overrides = std::move(TUScopeVMI.Overrides); return {collectEvidenceFromSummary( - *Summary, + Summary, evidenceEmitterWithPropagation( [&Results](const Evidence& E) { Results.push_back(E); }, VMIForPropagation), diff --git a/nullability/inference/collect_evidence_test_utilities.h b/nullability/inference/collect_evidence_test_utilities.h index 3076b6859..db00f8182 100644 --- a/nullability/inference/collect_evidence_test_utilities.h +++ b/nullability/inference/collect_evidence_test_utilities.h @@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_CRUBIT_NULLABILITY_INFERENCE_COLLECT_EVIDENCE_TEST_UTILITIES_H_ #define THIRD_PARTY_CRUBIT_NULLABILITY_INFERENCE_COLLECT_EVIDENCE_TEST_UTILITIES_H_ +#include #include #include #include @@ -105,7 +106,7 @@ std::string printToString(DefinitionCollectionMode Mode); /// Summarizes the definition in `Source` with the name "target" (a generic name /// using the LLVM-style capitalization for function names). -llvm::Expected summarizeTargetFuncDefinition( +llvm::Expected> summarizeTargetFuncDefinition( llvm::StringRef Source); //// Returns both an error and a vector to represent partial computations -- diff --git a/nullability/inference/infer_tu.cc b/nullability/inference/infer_tu.cc index 2418e1a1e..b9e85b3f3 100644 --- a/nullability/inference/infer_tu.cc +++ b/nullability/inference/infer_tu.cc @@ -5,6 +5,7 @@ #include "nullability/inference/infer_tu.h" #include +#include #include #include @@ -114,9 +115,11 @@ class InferenceManager { for (const auto* Impl : Sites.Definitions) { if (Filter && !Filter(*Impl)) continue; - if (llvm::Expected Summary = + if (llvm::Expected> Summary = summarizeDefinition(*Impl, USRCache, Pragmas, *VMI)) { - Result.Summaries.push_back(*std::move(Summary)); + if (Summary->has_value()) { + Result.Summaries.push_back(**std::move(Summary)); + } } else { llvm::errs() << "Error summarizing definition: " << Summary.takeError() << "\n";