[PATCH] D132147: [clang][dataflow] Refactor `TestingSupport.h`

2022-08-29 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456305.
wyt marked 5 inline comments as done.
wyt added a comment.

Address comments: add const qualifiers where applicable.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132147/new/

https://reviews.llvm.org/D132147

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -1240,43 +1240,46 @@
 /*IgnoreSmartPointerDereference=*/true};
 std::vector Diagnostics;
 llvm::Error Error = checkDataflow(
-SourceCode, FuncMatcher,
-[Options](ASTContext &Ctx, Environment &) {
-  return UncheckedOptionalAccessModel(Ctx, Options);
+/*AI:AnalysisInputs=*/{
+.Code = SourceCode,
+.TargetFuncMatcher = FuncMatcher,
+.MakeAnalysis =
+[Options](ASTContext &Ctx, Environment &) {
+  return UncheckedOptionalAccessModel(Ctx, Options);
+},
+.PostVisitCFG =
+[&Diagnostics,
+ Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
+ASTContext &Ctx, const CFGElement &Elt,
+const TypeErasedDataflowAnalysisState &State) mutable {
+  auto Stmt = Elt.getAs();
+  if (!Stmt) {
+return;
+  }
+  auto StmtDiagnostics =
+  Diagnoser.diagnose(Ctx, Stmt->getStmt(), State.Env);
+  llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
+},
+.ASTBuildArgs = {"-fsyntax-only", "-std=c++17",
+ "-Wno-undefined-inline"},
+.ASTBuildVirtualMappedFiles = FileContents,
 },
-[&Diagnostics, Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
-ASTContext &Ctx, const CFGStmt &Stmt,
-const TypeErasedDataflowAnalysisState &State) mutable {
-  auto StmtDiagnostics =
-  Diagnoser.diagnose(Ctx, Stmt.getStmt(), State.Env);
-  llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
-},
-[&Diagnostics](AnalysisData AnalysisData) {
-  auto &SrcMgr = AnalysisData.ASTCtx.getSourceManager();
-
+/*VerifyResults=*/[&Diagnostics](
+  const llvm::DenseMap
+  &Annotations,
+  const AnalysisOutputs &AO) {
   llvm::DenseSet AnnotationLines;
-  for (const auto &Pair : AnalysisData.Annotations) {
-auto *Stmt = Pair.getFirst();
-AnnotationLines.insert(
-SrcMgr.getPresumedLineNumber(Stmt->getBeginLoc()));
-// We add both the begin and end locations, so that if the
-// statement spans multiple lines then the test will fail.
-//
-// FIXME: Going forward, we should change this to instead just
-// get the single line number from the annotation itself, rather
-// than looking at the statement it's attached to.
-AnnotationLines.insert(
-SrcMgr.getPresumedLineNumber(Stmt->getEndLoc()));
+  for (const auto &[Line, _] : Annotations) {
+AnnotationLines.insert(Line);
   }
-
+  auto &SrcMgr = AO.ASTCtx.getSourceManager();
   llvm::DenseSet DiagnosticLines;
   for (SourceLocation &Loc : Diagnostics) {
 DiagnosticLines.insert(SrcMgr.getPresumedLineNumber(Loc));
   }
 
   EXPECT_THAT(DiagnosticLines, ContainerEq(AnnotationLines));
-},
-{"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents);
+});
 if (Error)
   FAIL() << llvm::toString(std::move(Error));
   }
Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -56,47 +56,134 @@
 
 namespace test {
 
-// Returns assertions based on annotations that are present after statements in
-// `AnnotatedCode`.
-llvm::Expected>
-buildStatementToAnnotationMapping(const FunctionDecl *Func,
-  llvm::Annotations AnnotatedCode);
-
-struct AnalysisData {
+/// Contains data structures required and produced by a dataflow analysis run.
+struct AnalysisOutputs {
+  /// Input code that is analyzed. Points within the code may be mar

[PATCH] D132377: [clang][dataflow] Add `SetupTest` parameter for `AnalysisInputs`.

2022-08-29 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456307.
wyt marked an inline comment as done.
wyt added a comment.

Propagate change from parent patch.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132377/new/

https://reviews.llvm.org/D132377

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -94,6 +94,11 @@
   /// takes as argument the AST generated from the code being analyzed and the
   /// initial state from which the analysis starts with.
   std::function MakeAnalysis;
+  /// Optional. If provided, this function is executed immediately before
+  /// running the dataflow analysis to allow for additional setup. All fields in
+  /// the `AnalysisOutputs` argument will be initialized except for the
+  /// `BlockStates` field which is only computed later during the analysis.
+  std::function SetupTest = nullptr;
   /// Optional. If provided, this function is applied on each CFG element after
   /// the analysis has been run.
   std::function
-getAnnotationLinesAndContent(const AnalysisOutputs &AO);
-
-// FIXME: Return a string map instead of a vector of pairs.
-//
-/// Returns the analysis states at each annotated statement in `AO.Code`.
-template 
-llvm::Expected>>>
-getAnnotationStates(const AnalysisOutputs &AO) {
-  using StateT = DataflowAnalysisState;
-  // FIXME: Extend to annotations on non-statement constructs.
-  // Get annotated statements.
-  llvm::Expected>
-  MaybeStmtToAnnotations =
-  buildStatementToAnnotationMapping(AO.Target, AO.Code);
-  if (!MaybeStmtToAnnotations)
-return MaybeStmtToAnnotations.takeError();
-  auto &StmtToAnnotations = *MaybeStmtToAnnotations;
-
-  // Compute a map from statement annotations to the state computed
-  // for the program point immediately after the annotated statement.
-  std::vector> Results;
-  for (const CFGBlock *Block : AO.CFCtx.getCFG()) {
-// Skip blocks that were not evaluated.
-if (!AO.BlockStates[Block->getBlockID()])
-  continue;
-
-transferBlock(
-AO.CFCtx, AO.BlockStates, *Block, AO.InitEnv, AO.Analysis,
-[&Results,
- &StmtToAnnotations](const clang::CFGElement &Element,
- const TypeErasedDataflowAnalysisState &State) {
-  auto Stmt = Element.getAs();
-  if (!Stmt)
-return;
-  auto It = StmtToAnnotations.find(Stmt->getStmt());
-  if (It == StmtToAnnotations.end())
-return;
-  auto *Lattice =
-  llvm::any_cast(&State.Lattice.Value);
-  Results.emplace_back(It->second, StateT{*Lattice, State.Env});
-});
-  }
-
-  return Results;
-}
+buildLineToAnnotationMapping(SourceManager &SM,
+ llvm::Annotations AnnotatedCode);
 
 /// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on the
 /// body of the function that matches `AI.TargetFuncMatcher` in `AI.Code`.
@@ -174,9 +135,9 @@
 ///
 ///   `VerifyResults` must be provided.
 template 
-llvm::Error checkDataflow(
-AnalysisInputs AI,
-std::function VerifyResults) {
+llvm::Error
+checkDataflow(AnalysisInputs AI,
+  std::function VerifyResults) {
   // Build AST context from code.
   llvm::Annotations AnnotatedCode(AI.Code);
   auto Unit = tooling::buildASTFromCodeWithArgs(
@@ -210,7 +171,7 @@
 return MaybeCFCtx.takeError();
   auto &CFCtx = *MaybeCFCtx;
 
-  // Initialize states and run dataflow analysis.
+  // Initialize states for running dataflow analysis.
   DataflowAnalysisContext DACtx(std::make_unique());
   Environment InitEnv(DACtx, *Target);
   auto Analysis = AI.MakeAnalysis(Context, InitEnv);
@@ -225,19 +186,28 @@
 };
   }
 
-  // If successful, the run returns a mapping from block IDs to the
-  // post-analysis states for the CFG blocks that have been evaluated.
+  // Additional test setup.
+  AnalysisOutputs AO{AnnotatedCode, Context, Target, CFCtx,
+ Analysis,  InitEnv, {}};
+  if (AI.SetupTest) {
+auto Error = AI.SetupTest(AO);
+if (Error) {
+  return Error;
+}
+  }
+
+  // If successful, the dataflow analysis returns a mapping from block IDs to
+  // the post-analysis states for the CFG blocks that have been evaluated.
   llvm::Expected>>
   MaybeBlockStates = runTypeErasedDataflowAnalysis(CFCtx, Analysis, InitEnv,
PostVisitCFGClosure);
   if (!MaybeBlockStates)
 return MaybeBlockStates.takeError();
-  auto &BlockStates = *MaybeBlockStates;
+  AO.BlockStates = *MaybeBlockStates;
 
   // Verify dataflow analysis outputs.
-  AnalysisOutputs AO{AnnotatedCode, Context, Target,

[PATCH] D132377: [clang][dataflow] Add `SetupTest` parameter for `AnalysisInputs`.

2022-08-29 Thread weiyi via Phabricator via cfe-commits
wyt added inline comments.



Comment at: clang/unittests/Analysis/FlowSensitive/TestingSupport.h:88
+/// Arguments for building the dataflow analysis.
+template  struct AnalysisInputs {
+  /// Input code that is analyzed.

sgatev wrote:
> Why move this? It makes it hard to tell if there are other changes. If there 
> are other changes, let's keep it where it is to have a clean diff and move it 
> in a separate commit.
It was moved as the new parameter declared uses `AnalysisOutputs`, hence 
`AnalysisOutputs` needs to come before `AnalysisInputs`. The move is now fixed 
by reordering these two struct declarations in the parent patch where they were 
first introduced.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132377/new/

https://reviews.llvm.org/D132377

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D132763: [clang][dataflow] Use `StringMap` for storing analysis states at annotated points instead of `vector>`.

2022-08-29 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456308.
wyt marked 6 inline comments as done.
wyt added a comment.

Address comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132763/new/

https://reviews.llvm.org/D132763

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -428,12 +428,12 @@
std::pair>>
Results,
ASTContext &ASTCtx) {
-ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
- Pair("p2", _), Pair("p1", _)));
-const Environment &Env1 = Results[3].second.Env;
-const Environment &Env2 = Results[2].second.Env;
-const Environment &Env3 = Results[1].second.Env;
-const Environment &Env4 = Results[0].second.Env;
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _),
+ Pair("p3", _), Pair("p4", _)));
+const Environment &Env1 = Results[0].second.Env;
+const Environment &Env2 = Results[1].second.Env;
+const Environment &Env3 = Results[2].second.Env;
+const Environment &Env4 = Results[3].second.Env;
 
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
@@ -581,10 +581,10 @@
  Results,
  ASTContext &ASTCtx) {
 ASSERT_THAT(Results,
-ElementsAre(Pair("p3", _), Pair("p2", _), Pair("p1", _)));
-const Environment &Env1 = Results[2].second.Env;
+ElementsAre(Pair("p1", _), Pair("p2", _), Pair("p3", _)));
+const Environment &Env1 = Results[0].second.Env;
 const Environment &Env2 = Results[1].second.Env;
-const Environment &Env3 = Results[0].second.Env;
+const Environment &Env3 = Results[2].second.Env;
 
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
@@ -624,12 +624,12 @@
  std::pair>>
  Results,
  ASTContext &ASTCtx) {
-ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
- Pair("p2", _), Pair("p1", _)));
-const Environment &Env1 = Results[3].second.Env;
-const Environment &Env2 = Results[2].second.Env;
-const Environment &Env3 = Results[1].second.Env;
-const Environment &Env4 = Results[0].second.Env;
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _),
+ Pair("p3", _), Pair("p4", _)));
+const Environment &Env1 = Results[0].second.Env;
+const Environment &Env2 = Results[1].second.Env;
+const Environment &Env3 = Results[2].second.Env;
+const Environment &Env4 = Results[3].second.Env;
 
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
@@ -753,14 +753,14 @@
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
 
-ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _)));
 
-const Environment &Env1 = Results[1].second.Env;
+const Environment &Env1 = Results[0].second.Env;
 auto *FooVal1 =
 cast(Env1.getValue(*FooDecl, SkipPast::None));
 EXPECT_TRUE(Env1.flowConditionImplies(*FooVal1));
 
-const Environment &Env2 = Results[0].second.Env;
+const Environment &Env2 = Results[1].second.Env;
 auto *FooVal2 =
 cast(Env2.getValue(*FooDecl, SkipPast::None));
 EXPECT_FALSE(Env2.flowConditionImplies(*FooVal2));
@@ -787,14 +787,14 @@
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
 
-ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _)));
 
-const Environment &Env1 = Results[1].second.Env;
+const Environment &Env1 = Results[0].second.Env;
 auto *FooVal1 =
 

[PATCH] D132763: [clang][dataflow] Use `StringMap` for storing analysis states at annotated points instead of `vector>`.

2022-08-29 Thread weiyi via Phabricator via cfe-commits
wyt added inline comments.



Comment at: clang/unittests/Analysis/FlowSensitive/TestingSupport.h:363-365
+std::sort(AnnotationStatesAsVector.begin(),
+  AnnotationStatesAsVector.end(),
+  [](auto a, auto b) { return a.first < b.first; });

gribozavr2 wrote:
> Please use llvm::sort. You also don't need to specify the comparator, the 
> default comparator for strings is exactly the same.
> 
> 
Used llvm::sort.

The default comparator for pairs also tries to compare the second field which 
is undefined for DataflowAnalysisState. And considering that annotations are 
unique here, it should be sufficient to just compare the strings here - hence 
the specified comparator that just checks the first of the pair.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132763/new/

https://reviews.llvm.org/D132763

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D132745: [clang] Fix ambiguous use of `report_fatal_error`.

2022-08-29 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456311.
wyt marked an inline comment as done.
wyt added a comment.

Address comment.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132745/new/

https://reviews.llvm.org/D132745

Files:
  clang/lib/Basic/SanitizerSpecialCaseList.cpp


Index: clang/lib/Basic/SanitizerSpecialCaseList.cpp
===
--- clang/lib/Basic/SanitizerSpecialCaseList.cpp
+++ clang/lib/Basic/SanitizerSpecialCaseList.cpp
@@ -33,7 +33,7 @@
   std::string Error;
   if (auto SSCL = create(Paths, VFS, Error))
 return SSCL;
-  llvm::report_fatal_error(Error);
+  llvm::report_fatal_error(StringRef(Error));
 }
 
 void SanitizerSpecialCaseList::createSanitizerSections() {


Index: clang/lib/Basic/SanitizerSpecialCaseList.cpp
===
--- clang/lib/Basic/SanitizerSpecialCaseList.cpp
+++ clang/lib/Basic/SanitizerSpecialCaseList.cpp
@@ -33,7 +33,7 @@
   std::string Error;
   if (auto SSCL = create(Paths, VFS, Error))
 return SSCL;
-  llvm::report_fatal_error(Error);
+  llvm::report_fatal_error(StringRef(Error));
 }
 
 void SanitizerSpecialCaseList::createSanitizerSections() {
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D132756: [clang][dataflow] Refactor `TypeErasedDataflowAnalysisTest` - replace usage of the deprecated overload of `checkDataflow`.

2022-08-29 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456317.
wyt marked 2 inline comments as done.
wyt added a comment.

Address comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132756/new/

https://reviews.llvm.org/D132756

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -24,8 +24,10 @@
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Testing/ADT/StringMapEntry.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -42,12 +44,10 @@
 using namespace dataflow;
 using namespace test;
 using namespace ast_matchers;
-using ::testing::_;
-using ::testing::ElementsAre;
+using llvm::IsStringMapEntry;
+using ::testing::DescribeMatcher;
 using ::testing::IsEmpty;
-using ::testing::IsNull;
 using ::testing::NotNull;
-using ::testing::Pair;
 using ::testing::Test;
 using ::testing::UnorderedElementsAre;
 
@@ -130,7 +130,8 @@
 }
 
 struct FunctionCallLattice {
-  llvm::SmallSet CalledFunctions;
+  using FunctionSet = llvm::SmallSet;
+  FunctionSet CalledFunctions;
 
   bool operator==(const FunctionCallLattice &Other) const {
 return CalledFunctions == Other.CalledFunctions;
@@ -197,16 +198,24 @@
 
 ASSERT_THAT_ERROR(
 test::checkDataflow(
-Code, "target",
-[](ASTContext &C, Environment &) {
-  return FunctionCallAnalysis(C);
+/*AI:AnalysisInputs=*/
+{
+.Code = Code,
+.TargetFuncMatcher = ast_matchers::hasName("target"),
+.MakeAnalysis =
+[](ASTContext &C, Environment &) {
+  return FunctionCallAnalysis(C);
+},
+.ASTBuildArgs = {"-fsyntax-only", "-std=c++17"},
+.ASTBuildVirtualMappedFiles = FilesContents,
 },
+/*VerifyResults=*/
 [&Expectations](
-llvm::ArrayRef>>
-Results,
-ASTContext &) { EXPECT_THAT(Results, Expectations); },
-{"-fsyntax-only", "-std=c++17"}, FilesContents),
+const llvm::StringMap<
+DataflowAnalysisState> &Results,
+const AnalysisOutputs &) {
+  EXPECT_THAT(Results, Expectations);
+}),
 llvm::Succeeded());
   }
 };
@@ -214,12 +223,16 @@
 MATCHER_P(HoldsFunctionCallLattice, m,
   ((negation ? "doesn't hold" : "holds") +
llvm::StringRef(" a lattice element that ") +
-   ::testing::DescribeMatcher(m, negation))
+   DescribeMatcher(m))
   .str()) {
   return ExplainMatchResult(m, arg.Lattice, result_listener);
 }
 
-MATCHER_P(HasCalledFunctions, m, "") {
+MATCHER_P(HasCalledFunctions, m,
+  ((negation ? "doesn't hold" : "holds") +
+   llvm::StringRef(" a set of called functions that ") +
+   DescribeMatcher(m))
+  .str()) {
   return ExplainMatchResult(m, arg.CalledFunctions, result_listener);
 }
 
@@ -233,9 +246,9 @@
   // [[p]]
 }
   )";
-  runDataflow(Code, UnorderedElementsAre(
-Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
-  UnorderedElementsAre("foo", "bar"));
+  runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
+"p", HoldsFunctionCallLattice(HasCalledFunctions(
+ UnorderedElementsAre("foo", "bar"));
 }
 
 TEST_F(NoreturnDestructorTest, ConditionalOperatorLeftBranchReturns) {
@@ -248,9 +261,9 @@
   // [[p]]
 }
   )";
-  runDataflow(Code, UnorderedElementsAre(
-Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
-  UnorderedElementsAre("foo"));
+  runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
+"p", HoldsFunctionCallLattice(HasCalledFunctions(
+ UnorderedElementsAre("foo"));
 }
 
 TEST_F(NoreturnDestructorTest, ConditionalOperatorRightBranchReturns) {
@@ -263,9 +276,9 @@
   // [[p]]
 }
   )";
-  runDataflow(Code, UnorderedElementsAre(
-Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
-  UnorderedElementsAre("foo"));
+  runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
+"p", HoldsFunctionCallLat

[PATCH] D132763: [clang][dataflow] Use `StringMap` for storing analysis states at annotated points instead of `vector>`.

2022-08-29 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456323.
wyt marked an inline comment as done.
wyt added a comment.

Address comment.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132763/new/

https://reviews.llvm.org/D132763

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -428,12 +428,12 @@
std::pair>>
Results,
ASTContext &ASTCtx) {
-ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
- Pair("p2", _), Pair("p1", _)));
-const Environment &Env1 = Results[3].second.Env;
-const Environment &Env2 = Results[2].second.Env;
-const Environment &Env3 = Results[1].second.Env;
-const Environment &Env4 = Results[0].second.Env;
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _),
+ Pair("p3", _), Pair("p4", _)));
+const Environment &Env1 = Results[0].second.Env;
+const Environment &Env2 = Results[1].second.Env;
+const Environment &Env3 = Results[2].second.Env;
+const Environment &Env4 = Results[3].second.Env;
 
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
@@ -581,10 +581,10 @@
  Results,
  ASTContext &ASTCtx) {
 ASSERT_THAT(Results,
-ElementsAre(Pair("p3", _), Pair("p2", _), Pair("p1", _)));
-const Environment &Env1 = Results[2].second.Env;
+ElementsAre(Pair("p1", _), Pair("p2", _), Pair("p3", _)));
+const Environment &Env1 = Results[0].second.Env;
 const Environment &Env2 = Results[1].second.Env;
-const Environment &Env3 = Results[0].second.Env;
+const Environment &Env3 = Results[2].second.Env;
 
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
@@ -624,12 +624,12 @@
  std::pair>>
  Results,
  ASTContext &ASTCtx) {
-ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
- Pair("p2", _), Pair("p1", _)));
-const Environment &Env1 = Results[3].second.Env;
-const Environment &Env2 = Results[2].second.Env;
-const Environment &Env3 = Results[1].second.Env;
-const Environment &Env4 = Results[0].second.Env;
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _),
+ Pair("p3", _), Pair("p4", _)));
+const Environment &Env1 = Results[0].second.Env;
+const Environment &Env2 = Results[1].second.Env;
+const Environment &Env3 = Results[2].second.Env;
+const Environment &Env4 = Results[3].second.Env;
 
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
@@ -753,14 +753,14 @@
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
 
-ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _)));
 
-const Environment &Env1 = Results[1].second.Env;
+const Environment &Env1 = Results[0].second.Env;
 auto *FooVal1 =
 cast(Env1.getValue(*FooDecl, SkipPast::None));
 EXPECT_TRUE(Env1.flowConditionImplies(*FooVal1));
 
-const Environment &Env2 = Results[0].second.Env;
+const Environment &Env2 = Results[1].second.Env;
 auto *FooVal2 =
 cast(Env2.getValue(*FooDecl, SkipPast::None));
 EXPECT_FALSE(Env2.flowConditionImplies(*FooVal2));
@@ -787,14 +787,14 @@
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
 
-ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _)));
 
-const Environment &Env1 = Results[1].second.Env;
+const Environment &Env1 = Results[0].second.Env;
 auto *FooVal1 =
  

[PATCH] D132745: [clang] Fix ambiguous use of `report_fatal_error`.

2022-08-29 Thread weiyi via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGd346eb7bf08c: [clang] Fix ambiguous use of 
`report_fatal_error`. (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132745/new/

https://reviews.llvm.org/D132745

Files:
  clang/lib/Basic/SanitizerSpecialCaseList.cpp


Index: clang/lib/Basic/SanitizerSpecialCaseList.cpp
===
--- clang/lib/Basic/SanitizerSpecialCaseList.cpp
+++ clang/lib/Basic/SanitizerSpecialCaseList.cpp
@@ -33,7 +33,7 @@
   std::string Error;
   if (auto SSCL = create(Paths, VFS, Error))
 return SSCL;
-  llvm::report_fatal_error(Error);
+  llvm::report_fatal_error(StringRef(Error));
 }
 
 void SanitizerSpecialCaseList::createSanitizerSections() {


Index: clang/lib/Basic/SanitizerSpecialCaseList.cpp
===
--- clang/lib/Basic/SanitizerSpecialCaseList.cpp
+++ clang/lib/Basic/SanitizerSpecialCaseList.cpp
@@ -33,7 +33,7 @@
   std::string Error;
   if (auto SSCL = create(Paths, VFS, Error))
 return SSCL;
-  llvm::report_fatal_error(Error);
+  llvm::report_fatal_error(StringRef(Error));
 }
 
 void SanitizerSpecialCaseList::createSanitizerSections() {
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131614: [clang][dataflow] Extend transfer functions for other `CFGElement`s

2022-08-30 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456624.
wyt added a comment.

Replace use of virtual functions with sfinea and CRTP to call transfer 
functions defined by the user.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131614/new/

https://reviews.llvm.org/D131614

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -71,14 +71,25 @@
   std::vector> &BlockStates;
 };
 
+// FIXME: Rename to `checkDataflow` after usages of the overload that applies to
+// `CFGStmt` have been replaced.
+//
+/// Runs dataflow analysis (specified from `MakeAnalysis`) and the
+/// `PostVisitCFG` function (if provided) on the body of the function that
+/// matches `TargetFuncMatcher` in code snippet `Code`. `VerifyResults` checks
+/// that the results from the analysis are correct.
+///
+/// Requirements:
+///
+///  `AnalysisT` contains a type `Lattice`.
 template 
-llvm::Error checkDataflow(
+llvm::Error checkDataflowOnCFG(
 llvm::StringRef Code,
 ast_matchers::internal::Matcher TargetFuncMatcher,
 std::function MakeAnalysis,
-std::function
-PostVisitStmt,
+PostVisitCFG,
 std::function VerifyResults, ArrayRef Args,
 const tooling::FileContentMappings &VirtualMappedFiles = {}) {
   llvm::Annotations AnnotatedCode(Code);
@@ -112,13 +123,14 @@
   Environment Env(DACtx, *F);
   auto Analysis = MakeAnalysis(Context, Env);
 
-  std::function
-  PostVisitStmtClosure = nullptr;
-  if (PostVisitStmt != nullptr) {
-PostVisitStmtClosure = [&PostVisitStmt, &Context](
-   const CFGStmt &Stmt,
-   const TypeErasedDataflowAnalysisState &State) {
-  PostVisitStmt(Context, Stmt, State);
+  std::function
+  PostVisitCFGClosure = nullptr;
+  if (PostVisitCFG) {
+PostVisitCFGClosure = [&PostVisitCFG, &Context](
+  const CFGElement &Element,
+  const TypeErasedDataflowAnalysisState &State) {
+  PostVisitCFG(Context, Element, State);
 };
   }
 
@@ -130,7 +142,7 @@
 
   llvm::Expected>>
   MaybeBlockStates = runTypeErasedDataflowAnalysis(*CFCtx, Analysis, Env,
-   PostVisitStmtClosure);
+   PostVisitCFGClosure);
   if (!MaybeBlockStates)
 return MaybeBlockStates.takeError();
   auto &BlockStates = *MaybeBlockStates;
@@ -141,6 +153,32 @@
   return llvm::Error::success();
 }
 
+template 
+llvm::Error checkDataflow(
+llvm::StringRef Code,
+ast_matchers::internal::Matcher TargetFuncMatcher,
+std::function MakeAnalysis,
+std::function
+PostVisitStmt,
+std::function VerifyResults, ArrayRef Args,
+const tooling::FileContentMappings &VirtualMappedFiles = {}) {
+  std::function
+  PostVisitCFG = nullptr;
+  if (PostVisitStmt) {
+PostVisitCFG =
+[&PostVisitStmt](ASTContext &Context, const CFGElement &Element,
+ const TypeErasedDataflowAnalysisState &State) {
+  if (auto Stmt = Element.getAs()) {
+PostVisitStmt(Context, *Stmt, State);
+  }
+};
+  }
+  return checkDataflowOnCFG(Code, TargetFuncMatcher, MakeAnalysis, PostVisitCFG,
+VerifyResults, Args, VirtualMappedFiles);
+}
+
 // Runs dataflow on the body of the function that matches `TargetFuncMatcher` in
 // code snippet `Code`. Requires: `AnalysisT` contains a type `Lattice`.
 template 
@@ -157,9 +195,9 @@
 const tooling::FileContentMappings &VirtualMappedFiles = {}) {
   using StateT = DataflowAnalysisState;
 
-  return checkDataflow(
+  return checkDataflowOnCFG(
   Code, std::move(TargetFuncMatcher), std::move(MakeAnalysis),
-  /*PostVisitStmt=*/nullptr,
+  /*PostVisitCFG=*/nullptr,
   [&VerifyResults](AnalysisData AnalysisData) {
 if (AnalysisData.BlockStates.empty()) {
   VerifyResults({}, AnalysisData.ASTCtx);
@@ -180,9 +218,13 @@
   AnalysisData.CFCtx, AnalysisData.BlockStates, *Block,
   AnalysisData.Env, AnalysisData.Analysis,
   [&Results,
-   &Annotations](const clang::CFGStmt &Stmt,
+   &Annotations](const clang::CFGElement &Element,
  const TypeErasedDataflowAnalysisState &State) {
-auto It = Annotations.find(Stmt.getStmt());
+// FIXME: Extend

[PATCH] D132377: [clang][dataflow] Add `SetupTest` parameter for `AnalysisInputs`.

2022-08-30 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456699.
wyt marked an inline comment as done.
wyt added a comment.

Address comment.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132377/new/

https://reviews.llvm.org/D132377

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -94,6 +94,11 @@
   /// takes as argument the AST generated from the code being analyzed and the
   /// initial state from which the analysis starts with.
   std::function MakeAnalysis;
+  /// Optional. If provided, this function is executed immediately before
+  /// running the dataflow analysis to allow for additional setup. All fields in
+  /// the `AnalysisOutputs` argument will be initialized except for the
+  /// `BlockStates` field which is only computed later during the analysis.
+  std::function SetupTest = nullptr;
   /// Optional. If provided, this function is applied on each CFG element after
   /// the analysis has been run.
   std::function
-getAnnotationLinesAndContent(const AnalysisOutputs &AO);
-
-// FIXME: Return a string map instead of a vector of pairs.
-//
-/// Returns the analysis states at each annotated statement in `AO.Code`.
-template 
-llvm::Expected>>>
-getAnnotationStates(const AnalysisOutputs &AO) {
-  using StateT = DataflowAnalysisState;
-  // FIXME: Extend to annotations on non-statement constructs.
-  // Get annotated statements.
-  llvm::Expected>
-  MaybeStmtToAnnotations =
-  buildStatementToAnnotationMapping(AO.Target, AO.Code);
-  if (!MaybeStmtToAnnotations)
-return MaybeStmtToAnnotations.takeError();
-  auto &StmtToAnnotations = *MaybeStmtToAnnotations;
-
-  // Compute a map from statement annotations to the state computed
-  // for the program point immediately after the annotated statement.
-  std::vector> Results;
-  for (const CFGBlock *Block : AO.CFCtx.getCFG()) {
-// Skip blocks that were not evaluated.
-if (!AO.BlockStates[Block->getBlockID()])
-  continue;
-
-transferBlock(
-AO.CFCtx, AO.BlockStates, *Block, AO.InitEnv, AO.Analysis,
-[&Results,
- &StmtToAnnotations](const clang::CFGElement &Element,
- const TypeErasedDataflowAnalysisState &State) {
-  auto Stmt = Element.getAs();
-  if (!Stmt)
-return;
-  auto It = StmtToAnnotations.find(Stmt->getStmt());
-  if (It == StmtToAnnotations.end())
-return;
-  auto *Lattice =
-  llvm::any_cast(&State.Lattice.Value);
-  Results.emplace_back(It->second, StateT{*Lattice, State.Env});
-});
-  }
-
-  return Results;
-}
+buildLineToAnnotationMapping(SourceManager &SM,
+ llvm::Annotations AnnotatedCode);
 
 /// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on the
 /// body of the function that matches `AI.TargetFuncMatcher` in `AI.Code`.
@@ -174,9 +135,9 @@
 ///
 ///   `VerifyResults` must be provided.
 template 
-llvm::Error checkDataflow(
-AnalysisInputs AI,
-std::function VerifyResults) {
+llvm::Error
+checkDataflow(AnalysisInputs AI,
+  std::function VerifyResults) {
   // Build AST context from code.
   llvm::Annotations AnnotatedCode(AI.Code);
   auto Unit = tooling::buildASTFromCodeWithArgs(
@@ -210,7 +171,7 @@
 return MaybeCFCtx.takeError();
   auto &CFCtx = *MaybeCFCtx;
 
-  // Initialize states and run dataflow analysis.
+  // Initialize states for running dataflow analysis.
   DataflowAnalysisContext DACtx(std::make_unique());
   Environment InitEnv(DACtx, *Target);
   auto Analysis = AI.MakeAnalysis(Context, InitEnv);
@@ -225,19 +186,26 @@
 };
   }
 
-  // If successful, the run returns a mapping from block IDs to the
-  // post-analysis states for the CFG blocks that have been evaluated.
+  // Additional test setup.
+  AnalysisOutputs AO{AnnotatedCode, Context, Target, CFCtx,
+ Analysis,  InitEnv, {}};
+  if (AI.SetupTest) {
+if (auto Error = AI.SetupTest(AO))
+  return Error;
+  }
+
+  // If successful, the dataflow analysis returns a mapping from block IDs to
+  // the post-analysis states for the CFG blocks that have been evaluated.
   llvm::Expected>>
   MaybeBlockStates = runTypeErasedDataflowAnalysis(CFCtx, Analysis, InitEnv,
PostVisitCFGClosure);
   if (!MaybeBlockStates)
 return MaybeBlockStates.takeError();
-  auto &BlockStates = *MaybeBlockStates;
+  AO.BlockStates = *MaybeBlockStates;
 
   // Verify dataflow analysis outputs.
-  AnalysisOutputs AO{AnnotatedCode, Context, Target, CFCtx,
- Analysis, 

[PATCH] D132377: [clang][dataflow] Add `SetupTest` parameter for `AnalysisInputs`.

2022-08-30 Thread weiyi via Phabricator via cfe-commits
wyt added inline comments.



Comment at: clang/unittests/Analysis/FlowSensitive/TestingSupport.h:191
+  AnalysisOutputs AO{AnnotatedCode, Context, Target, CFCtx,
+ Analysis,  InitEnv, {}};
+  if (AI.SetupTest) {

ymandel wrote:
> formatting seems funny here?
this is returned from clang-format, seems to align the arguments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132377/new/

https://reviews.llvm.org/D132377

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D132763: [clang][dataflow] Use `StringMap` for storing analysis states at annotated points instead of `vector>`.

2022-08-30 Thread weiyi via Phabricator via cfe-commits
wyt added inline comments.



Comment at: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp:652
ASTContext &ASTCtx) {
-ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
- Pair("p2", _), Pair("p1", _)));
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _),
+ Pair("p3", _), Pair("p4", _)));

ymandel wrote:
> Given that its now a map, should we use `UnorderedElementsAre`? Or, does 
> `StringMap` guarantee ordering?
`StringMap` is not yet exposed to the tests in this patch.

Some ordering is currently required to correctly retrieve the states in the 
tests. Hence, the conversion from `StringMap` to `vector>` 
(in deprecated `checkDataflow`) orders the elements alphabetically on the 
annotations.

Future refactoring will expose `StringMap` in the tests, where we will then not 
need to maintain an ordering since we can retrieve elements by keys, we can 
then use `UnorderedElementsAre`.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132763/new/

https://reviews.llvm.org/D132763

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131614: [clang][dataflow] Extend transfer functions for other `CFGElement`s

2022-08-31 Thread weiyi via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG9e842dd4bd55: [clang][dataflow] Extend transfer functions 
for other `CFGElement`s (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131614/new/

https://reviews.llvm.org/D131614

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -71,14 +71,25 @@
   std::vector> &BlockStates;
 };
 
+// FIXME: Rename to `checkDataflow` after usages of the overload that applies to
+// `CFGStmt` have been replaced.
+//
+/// Runs dataflow analysis (specified from `MakeAnalysis`) and the
+/// `PostVisitCFG` function (if provided) on the body of the function that
+/// matches `TargetFuncMatcher` in code snippet `Code`. `VerifyResults` checks
+/// that the results from the analysis are correct.
+///
+/// Requirements:
+///
+///  `AnalysisT` contains a type `Lattice`.
 template 
-llvm::Error checkDataflow(
+llvm::Error checkDataflowOnCFG(
 llvm::StringRef Code,
 ast_matchers::internal::Matcher TargetFuncMatcher,
 std::function MakeAnalysis,
-std::function
-PostVisitStmt,
+PostVisitCFG,
 std::function VerifyResults, ArrayRef Args,
 const tooling::FileContentMappings &VirtualMappedFiles = {}) {
   llvm::Annotations AnnotatedCode(Code);
@@ -112,13 +123,14 @@
   Environment Env(DACtx, *F);
   auto Analysis = MakeAnalysis(Context, Env);
 
-  std::function
-  PostVisitStmtClosure = nullptr;
-  if (PostVisitStmt != nullptr) {
-PostVisitStmtClosure = [&PostVisitStmt, &Context](
-   const CFGStmt &Stmt,
-   const TypeErasedDataflowAnalysisState &State) {
-  PostVisitStmt(Context, Stmt, State);
+  std::function
+  PostVisitCFGClosure = nullptr;
+  if (PostVisitCFG) {
+PostVisitCFGClosure = [&PostVisitCFG, &Context](
+  const CFGElement &Element,
+  const TypeErasedDataflowAnalysisState &State) {
+  PostVisitCFG(Context, Element, State);
 };
   }
 
@@ -130,7 +142,7 @@
 
   llvm::Expected>>
   MaybeBlockStates = runTypeErasedDataflowAnalysis(*CFCtx, Analysis, Env,
-   PostVisitStmtClosure);
+   PostVisitCFGClosure);
   if (!MaybeBlockStates)
 return MaybeBlockStates.takeError();
   auto &BlockStates = *MaybeBlockStates;
@@ -141,6 +153,32 @@
   return llvm::Error::success();
 }
 
+template 
+llvm::Error checkDataflow(
+llvm::StringRef Code,
+ast_matchers::internal::Matcher TargetFuncMatcher,
+std::function MakeAnalysis,
+std::function
+PostVisitStmt,
+std::function VerifyResults, ArrayRef Args,
+const tooling::FileContentMappings &VirtualMappedFiles = {}) {
+  std::function
+  PostVisitCFG = nullptr;
+  if (PostVisitStmt) {
+PostVisitCFG =
+[&PostVisitStmt](ASTContext &Context, const CFGElement &Element,
+ const TypeErasedDataflowAnalysisState &State) {
+  if (auto Stmt = Element.getAs()) {
+PostVisitStmt(Context, *Stmt, State);
+  }
+};
+  }
+  return checkDataflowOnCFG(Code, TargetFuncMatcher, MakeAnalysis, PostVisitCFG,
+VerifyResults, Args, VirtualMappedFiles);
+}
+
 // Runs dataflow on the body of the function that matches `TargetFuncMatcher` in
 // code snippet `Code`. Requires: `AnalysisT` contains a type `Lattice`.
 template 
@@ -157,9 +195,9 @@
 const tooling::FileContentMappings &VirtualMappedFiles = {}) {
   using StateT = DataflowAnalysisState;
 
-  return checkDataflow(
+  return checkDataflowOnCFG(
   Code, std::move(TargetFuncMatcher), std::move(MakeAnalysis),
-  /*PostVisitStmt=*/nullptr,
+  /*PostVisitCFG=*/nullptr,
   [&VerifyResults](AnalysisData AnalysisData) {
 if (AnalysisData.BlockStates.empty()) {
   VerifyResults({}, AnalysisData.ASTCtx);
@@ -180,9 +218,13 @@
   AnalysisData.CFCtx, AnalysisData.BlockStates, *Block,
   AnalysisData.Env, AnalysisData.Analysis,
   [&Results,
-   &Annotations](const clang::CFGStmt &Stmt,
+   &Annotations](const clang::CFGElement &Element,
  const TypeErasedDataflowAnalysisState &State) {
-auto It = Annotations.find(Stmt.getStmt());
+ 

[PATCH] D132147: [clang][dataflow] Refactor `TestingSupport.h`

2022-08-31 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456978.
wyt added a comment.
Herald added subscribers: llvm-commits, mgrang, mgorny.
Herald added a project: LLVM.

Remove use of designated initializers which are only allowed on C++20. Instead, 
introduce a constructor that sets required fields, and `withFieldName` methods 
that sets optional fields when constructing an `AnalysisInputs` struct.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132147/new/

https://reviews.llvm.org/D132147

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
  llvm/include/llvm/ADT/StringMapEntry.h
  llvm/include/llvm/Testing/ADT/StringMap.h
  llvm/include/llvm/Testing/ADT/StringMapEntry.h
  llvm/unittests/Testing/ADT/CMakeLists.txt
  llvm/unittests/Testing/ADT/StringMapEntryTest.cpp
  llvm/unittests/Testing/ADT/StringMapTest.cpp
  llvm/unittests/Testing/CMakeLists.txt
  utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
  utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel

Index: utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel
===
--- utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel
+++ utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel
@@ -644,6 +644,23 @@
 ],
 )
 
+cc_test(
+name = "testing_adt_tests",
+size = "small",
+srcs = glob(
+[
+"Testing/ADT/*.cpp",
+],
+allow_empty = False,
+),
+deps = [
+"//llvm:Support",
+"//llvm:TestingADT",
+"//llvm:gtest",
+"//llvm:gtest_main",
+],
+)
+
 cc_test(
 name = "transforms_tests",
 size = "small",
Index: utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
===
--- utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
+++ utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
@@ -4181,6 +4181,19 @@
 srcs = ["utils/lit/lit.py"] + glob(["utils/lit/lit/**/*.py"]),
 )
 
+cc_library(
+name = "TestingADT",
+testonly = True,
+hdrs = glob([
+"include/llvm/Testing/ADT/*.h",
+]),
+copts = llvm_copts,
+deps = [
+":Support",
+":gmock",
+],
+)
+
 cc_library(
 name = "TestingSupport",
 testonly = True,
Index: llvm/unittests/Testing/CMakeLists.txt
===
--- llvm/unittests/Testing/CMakeLists.txt
+++ llvm/unittests/Testing/CMakeLists.txt
@@ -1 +1,2 @@
+add_subdirectory(ADT)
 add_subdirectory(Support)
Index: llvm/unittests/Testing/ADT/StringMapTest.cpp
===
--- /dev/null
+++ llvm/unittests/Testing/ADT/StringMapTest.cpp
@@ -0,0 +1,55 @@
+//===- StringMapTest.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 "llvm/Testing/ADT/StringMap.h"
+#include "llvm/ADT/StringMap.h"
+
+#include "gtest/gtest.h"
+#include 
+
+namespace llvm {
+namespace {
+
+TEST(StringMapTest, StringMapStream) {
+  std::ostringstream OS;
+  StringMap Map;
+  Map["A"] = 42;
+  Map["Z"] = 35;
+  Map["B"] = 7;
+  OS << Map;
+
+  EXPECT_EQ(OS.str(), R"({
+{"A": 42},
+{"B": 7},
+{"Z": 35},
+})");
+}
+
+TEST(StringMapTest, NestedStringMapStream) {
+  std::ostringstream OS;
+  StringMap> Map;
+  Map["Z"];
+  Map["A"]["Apple"] = 5;
+  Map["B"]["Bee"] = 3;
+  Map["A"]["Axe"] = 3;
+  OS << Map;
+
+  EXPECT_EQ(OS.str(), R"({
+{"A": {
+{"Apple": 5},
+{"Axe": 3},
+}},
+{"B": {
+{"Bee": 3},
+}},
+{"Z": { }},
+})");
+}
+
+} // namespace
+} // namespace llvm
Index: llvm/unittests/Testing/ADT/StringMapEntryTest.cpp
===
--- /dev/null
+++ llvm/unittests/Testing/ADT/StringMapEntryTest.cpp
@@ -0,0 +1,88 @@
+//===- StringMapEntryTest.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 "llvm/Testing/ADT/StringMapEntry.h"
+#include "llvm/ADT/StringMap.h"
+
+#include "gtest/gtest.h"
+#include 
+
+namespace llvm {
+namespace {
+
+using testing::Gt;
+using testing::Matcher;
+using testing::StrCaseEq;
+using testing::StringMatchResultListener;
+using testing::UnorderedElementsAre;
+
+template  std::string Desc

[PATCH] D132147: [clang][dataflow] Refactor `TestingSupport.h`

2022-08-31 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456980.
wyt added a comment.

This is a re-update of a previous update which uploaded the wrong diff.
Remove use of designated initializers which are only allowed on C++20. Instead, 
introduce a constructor that sets required fields, and withFieldName methods 
that sets optional fields when constructing an AnalysisInputs struct.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132147/new/

https://reviews.llvm.org/D132147

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -1234,49 +1234,48 @@
   template 
   T Make();
 )");
-const tooling::FileContentMappings FileContents(Headers.begin(),
-Headers.end());
 UncheckedOptionalAccessModelOptions Options{
 /*IgnoreSmartPointerDereference=*/true};
 std::vector Diagnostics;
 llvm::Error Error = checkDataflow(
-SourceCode, FuncMatcher,
-[Options](ASTContext &Ctx, Environment &) {
-  return UncheckedOptionalAccessModel(Ctx, Options);
-},
-[&Diagnostics, Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
-ASTContext &Ctx, const CFGStmt &Stmt,
-const TypeErasedDataflowAnalysisState &State) mutable {
-  auto StmtDiagnostics =
-  Diagnoser.diagnose(Ctx, Stmt.getStmt(), State.Env);
-  llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
-},
-[&Diagnostics](AnalysisData AnalysisData) {
-  auto &SrcMgr = AnalysisData.ASTCtx.getSourceManager();
-
+AnalysisInputs(
+SourceCode, std::move(FuncMatcher),
+[Options](ASTContext &Ctx, Environment &) {
+  return UncheckedOptionalAccessModel(Ctx, Options);
+})
+.withPostVisitCFG(
+[&Diagnostics,
+ Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
+ASTContext &Ctx, const CFGElement &Elt,
+const TypeErasedDataflowAnalysisState &State) mutable {
+  auto Stmt = Elt.getAs();
+  if (!Stmt) {
+return;
+  }
+  auto StmtDiagnostics =
+  Diagnoser.diagnose(Ctx, Stmt->getStmt(), State.Env);
+  llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
+})
+.withASTBuildArgs(
+{"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"})
+.withASTBuildVirtualMappedFiles(
+tooling::FileContentMappings(Headers.begin(), Headers.end())),
+/*VerifyResults=*/[&Diagnostics](
+  const llvm::DenseMap
+  &Annotations,
+  const AnalysisOutputs &AO) {
   llvm::DenseSet AnnotationLines;
-  for (const auto &Pair : AnalysisData.Annotations) {
-auto *Stmt = Pair.getFirst();
-AnnotationLines.insert(
-SrcMgr.getPresumedLineNumber(Stmt->getBeginLoc()));
-// We add both the begin and end locations, so that if the
-// statement spans multiple lines then the test will fail.
-//
-// FIXME: Going forward, we should change this to instead just
-// get the single line number from the annotation itself, rather
-// than looking at the statement it's attached to.
-AnnotationLines.insert(
-SrcMgr.getPresumedLineNumber(Stmt->getEndLoc()));
+  for (const auto &[Line, _] : Annotations) {
+AnnotationLines.insert(Line);
   }
-
+  auto &SrcMgr = AO.ASTCtx.getSourceManager();
   llvm::DenseSet DiagnosticLines;
   for (SourceLocation &Loc : Diagnostics) {
 DiagnosticLines.insert(SrcMgr.getPresumedLineNumber(Loc));
   }
 
   EXPECT_THAT(DiagnosticLines, ContainerEq(AnnotationLines));
-},
-{"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents);
+});
 if (Error)
   FAIL() << llvm::toString(std::move(Error));
   }
Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -56,47 +56,160 @@
 
 namespace test {
 
-// Returns assertions based

[PATCH] D132377: [clang][dataflow] Add `SetupTest` parameter for `AnalysisInputs`.

2022-08-31 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456982.
wyt added a comment.

Update according to change in parent patch to replace designated initialisers 
when constructing `AnalysisInputs`.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132377/new/

https://reviews.llvm.org/D132377

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -96,6 +96,11 @@
 
   /// Optional fields can be set with methods of the form `withFieldName(...)`.
   AnalysisInputs &&
+  withSetupTest(std::function Arg) && {
+SetupTest = std::move(Arg);
+return std::move(*this);
+  }
+  AnalysisInputs &&
   withPostVisitCFG(std::function
Arg) && {
@@ -120,6 +125,11 @@
   /// takes as argument the AST generated from the code being analyzed and the
   /// initial state from which the analysis starts with.
   std::function MakeAnalysis;
+  /// Optional. If provided, this function is executed immediately before
+  /// running the dataflow analysis to allow for additional setup. All fields in
+  /// the `AnalysisOutputs` argument will be initialized except for the
+  /// `BlockStates` field which is only computed later during the analysis.
+  std::function SetupTest = nullptr;
   /// Optional. If provided, this function is applied on each CFG element after
   /// the analysis has been run.
   std::function
-getAnnotationLinesAndContent(const AnalysisOutputs &AO);
-
-// FIXME: Return a string map instead of a vector of pairs.
-//
-/// Returns the analysis states at each annotated statement in `AO.Code`.
-template 
-llvm::Expected>>>
-getAnnotationStates(const AnalysisOutputs &AO) {
-  using StateT = DataflowAnalysisState;
-  // FIXME: Extend to annotations on non-statement constructs.
-  // Get annotated statements.
-  llvm::Expected>
-  MaybeStmtToAnnotations =
-  buildStatementToAnnotationMapping(AO.Target, AO.Code);
-  if (!MaybeStmtToAnnotations)
-return MaybeStmtToAnnotations.takeError();
-  auto &StmtToAnnotations = *MaybeStmtToAnnotations;
-
-  // Compute a map from statement annotations to the state computed
-  // for the program point immediately after the annotated statement.
-  std::vector> Results;
-  for (const CFGBlock *Block : AO.CFCtx.getCFG()) {
-// Skip blocks that were not evaluated.
-if (!AO.BlockStates[Block->getBlockID()])
-  continue;
-
-transferBlock(
-AO.CFCtx, AO.BlockStates, *Block, AO.InitEnv, AO.Analysis,
-[&Results,
- &StmtToAnnotations](const clang::CFGElement &Element,
- const TypeErasedDataflowAnalysisState &State) {
-  auto Stmt = Element.getAs();
-  if (!Stmt)
-return;
-  auto It = StmtToAnnotations.find(Stmt->getStmt());
-  if (It == StmtToAnnotations.end())
-return;
-  auto *Lattice =
-  llvm::any_cast(&State.Lattice.Value);
-  Results.emplace_back(It->second, StateT{*Lattice, State.Env});
-});
-  }
-
-  return Results;
-}
+buildLineToAnnotationMapping(SourceManager &SM,
+ llvm::Annotations AnnotatedCode);
 
 /// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on the
 /// body of the function that matches `AI.TargetFuncMatcher` in `AI.Code`.
@@ -200,9 +166,9 @@
 ///
 ///   `VerifyResults` must be provided.
 template 
-llvm::Error checkDataflow(
-AnalysisInputs AI,
-std::function VerifyResults) {
+llvm::Error
+checkDataflow(AnalysisInputs AI,
+  std::function VerifyResults) {
   // Build AST context from code.
   llvm::Annotations AnnotatedCode(AI.Code);
   auto Unit = tooling::buildASTFromCodeWithArgs(
@@ -236,7 +202,7 @@
 return MaybeCFCtx.takeError();
   auto &CFCtx = *MaybeCFCtx;
 
-  // Initialize states and run dataflow analysis.
+  // Initialize states for running dataflow analysis.
   DataflowAnalysisContext DACtx(std::make_unique());
   Environment InitEnv(DACtx, *Target);
   auto Analysis = AI.MakeAnalysis(Context, InitEnv);
@@ -251,19 +217,26 @@
 };
   }
 
-  // If successful, the run returns a mapping from block IDs to the
-  // post-analysis states for the CFG blocks that have been evaluated.
+  // Additional test setup.
+  AnalysisOutputs AO{AnnotatedCode, Context, Target, CFCtx,
+ Analysis,  InitEnv, {}};
+  if (AI.SetupTest) {
+if (auto Error = AI.SetupTest(AO))
+  return Error;
+  }
+
+  // If successful, the dataflow analysis returns a mapping from block IDs to
+  // the post-analysis states for the CFG blocks that have been evaluated.
   llvm::Expected>>
   MaybeBlockStates = runTypeErasedDataflowAnalysis(CFCtx, An

[PATCH] D132763: [clang][dataflow] Use `StringMap` for storing analysis states at annotated points instead of `vector>`.

2022-08-31 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456983.
wyt added a comment.

Propagate change from parent patch.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132763/new/

https://reviews.llvm.org/D132763

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -426,12 +426,12 @@
std::pair>>
Results,
ASTContext &ASTCtx) {
-ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
- Pair("p2", _), Pair("p1", _)));
-const Environment &Env1 = Results[3].second.Env;
-const Environment &Env2 = Results[2].second.Env;
-const Environment &Env3 = Results[1].second.Env;
-const Environment &Env4 = Results[0].second.Env;
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _),
+ Pair("p3", _), Pair("p4", _)));
+const Environment &Env1 = Results[0].second.Env;
+const Environment &Env2 = Results[1].second.Env;
+const Environment &Env3 = Results[2].second.Env;
+const Environment &Env4 = Results[3].second.Env;
 
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
@@ -579,10 +579,10 @@
  Results,
  ASTContext &ASTCtx) {
 ASSERT_THAT(Results,
-ElementsAre(Pair("p3", _), Pair("p2", _), Pair("p1", _)));
-const Environment &Env1 = Results[2].second.Env;
+ElementsAre(Pair("p1", _), Pair("p2", _), Pair("p3", _)));
+const Environment &Env1 = Results[0].second.Env;
 const Environment &Env2 = Results[1].second.Env;
-const Environment &Env3 = Results[0].second.Env;
+const Environment &Env3 = Results[2].second.Env;
 
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
@@ -622,12 +622,12 @@
  std::pair>>
  Results,
  ASTContext &ASTCtx) {
-ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
- Pair("p2", _), Pair("p1", _)));
-const Environment &Env1 = Results[3].second.Env;
-const Environment &Env2 = Results[2].second.Env;
-const Environment &Env3 = Results[1].second.Env;
-const Environment &Env4 = Results[0].second.Env;
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _),
+ Pair("p3", _), Pair("p4", _)));
+const Environment &Env1 = Results[0].second.Env;
+const Environment &Env2 = Results[1].second.Env;
+const Environment &Env3 = Results[2].second.Env;
+const Environment &Env4 = Results[3].second.Env;
 
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
@@ -751,14 +751,14 @@
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
 
-ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _)));
 
-const Environment &Env1 = Results[1].second.Env;
+const Environment &Env1 = Results[0].second.Env;
 auto *FooVal1 =
 cast(Env1.getValue(*FooDecl, SkipPast::None));
 EXPECT_TRUE(Env1.flowConditionImplies(*FooVal1));
 
-const Environment &Env2 = Results[0].second.Env;
+const Environment &Env2 = Results[1].second.Env;
 auto *FooVal2 =
 cast(Env2.getValue(*FooDecl, SkipPast::None));
 EXPECT_FALSE(Env2.flowConditionImplies(*FooVal2));
@@ -785,14 +785,14 @@
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
 
-ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _)));
 
-const Environment &Env1 = Results[1].second.Env;
+const Environment &Env1 = Results[0].second.Env;
 auto *FooVal1 =
 cast(Env

[PATCH] D132756: [clang][dataflow] Refactor `TypeErasedDataflowAnalysisTest` - replace usage of the deprecated overload of `checkDataflow`.

2022-08-31 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 456986.
wyt added a comment.

Update according to change in preceding patches to replace use of designated 
initialisers when constructing `AnalysisInputs`.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132756/new/

https://reviews.llvm.org/D132756

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -24,8 +24,10 @@
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Testing/ADT/StringMapEntry.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -42,12 +44,10 @@
 using namespace dataflow;
 using namespace test;
 using namespace ast_matchers;
-using ::testing::_;
-using ::testing::ElementsAre;
+using llvm::IsStringMapEntry;
+using ::testing::DescribeMatcher;
 using ::testing::IsEmpty;
-using ::testing::IsNull;
 using ::testing::NotNull;
-using ::testing::Pair;
 using ::testing::Test;
 using ::testing::UnorderedElementsAre;
 
@@ -129,7 +129,8 @@
 }
 
 struct FunctionCallLattice {
-  llvm::SmallSet CalledFunctions;
+  using FunctionSet = llvm::SmallSet;
+  FunctionSet CalledFunctions;
 
   bool operator==(const FunctionCallLattice &Other) const {
 return CalledFunctions == Other.CalledFunctions;
@@ -195,16 +196,20 @@
 
 ASSERT_THAT_ERROR(
 test::checkDataflow(
-Code, "target",
-[](ASTContext &C, Environment &) {
-  return FunctionCallAnalysis(C);
-},
+AnalysisInputs(
+Code, ast_matchers::hasName("target"),
+[](ASTContext &C, Environment &) {
+  return FunctionCallAnalysis(C);
+})
+.withASTBuildArgs({"-fsyntax-only", "-std=c++17"})
+.withASTBuildVirtualMappedFiles(std::move(FilesContents)),
+/*VerifyResults=*/
 [&Expectations](
-llvm::ArrayRef>>
-Results,
-ASTContext &) { EXPECT_THAT(Results, Expectations); },
-{"-fsyntax-only", "-std=c++17"}, FilesContents),
+const llvm::StringMap<
+DataflowAnalysisState> &Results,
+const AnalysisOutputs &) {
+  EXPECT_THAT(Results, Expectations);
+}),
 llvm::Succeeded());
   }
 };
@@ -212,12 +217,16 @@
 MATCHER_P(HoldsFunctionCallLattice, m,
   ((negation ? "doesn't hold" : "holds") +
llvm::StringRef(" a lattice element that ") +
-   ::testing::DescribeMatcher(m, negation))
+   DescribeMatcher(m))
   .str()) {
   return ExplainMatchResult(m, arg.Lattice, result_listener);
 }
 
-MATCHER_P(HasCalledFunctions, m, "") {
+MATCHER_P(HasCalledFunctions, m,
+  ((negation ? "doesn't hold" : "holds") +
+   llvm::StringRef(" a set of called functions that ") +
+   DescribeMatcher(m))
+  .str()) {
   return ExplainMatchResult(m, arg.CalledFunctions, result_listener);
 }
 
@@ -231,9 +240,9 @@
   // [[p]]
 }
   )";
-  runDataflow(Code, UnorderedElementsAre(
-Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
-  UnorderedElementsAre("foo", "bar"));
+  runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
+"p", HoldsFunctionCallLattice(HasCalledFunctions(
+ UnorderedElementsAre("foo", "bar"));
 }
 
 TEST_F(NoreturnDestructorTest, ConditionalOperatorLeftBranchReturns) {
@@ -246,9 +255,9 @@
   // [[p]]
 }
   )";
-  runDataflow(Code, UnorderedElementsAre(
-Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
-  UnorderedElementsAre("foo"));
+  runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
+"p", HoldsFunctionCallLattice(HasCalledFunctions(
+ UnorderedElementsAre("foo"));
 }
 
 TEST_F(NoreturnDestructorTest, ConditionalOperatorRightBranchReturns) {
@@ -261,9 +270,9 @@
   // [[p]]
 }
   )";
-  runDataflow(Code, UnorderedElementsAre(
-Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
-  UnorderedElementsAre("foo"));
+  runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
+"p", HoldsFunctionCallLattice(HasCalledFunctions(
+

[PATCH] D131616: [clang][dataflow] Generalise match switch utility to other AST types and add a `CFGMatchSwitch` which currently handles `CFGStmt` and `CFGInitializer`.

2022-08-31 Thread weiyi via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGc9033eeb2e59: [clang][dataflow] Generalise match switch 
utility to other AST types and add a… (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131616/new/

https://reviews.llvm.org/D131616

Files:
  clang/include/clang/Analysis/FlowSensitive/CFGMatchSwitch.h
  clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
  clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
@@ -5,12 +5,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===--===//
-//
-//  This file defines a simplistic version of Constant Propagation as an example
-//  of a forward, monotonic dataflow analysis. The analysis tracks all
-//  variables in the scope, but lacks escape analysis.
-//
-//===--===//
 
 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
 #include "TestingSupport.h"
Index: clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
===
--- clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -4,6 +4,7 @@
   )
 
 add_clang_unittest(ClangAnalysisFlowSensitiveTests
+  CFGMatchSwitchTest.cpp
   ChromiumCheckModelTest.cpp
   DataflowAnalysisContextTest.cpp
   DataflowEnvironmentTest.cpp
Index: clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
@@ -0,0 +1,124 @@
+//===- unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.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 "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace dataflow;
+using namespace ast_matchers;
+
+namespace {
+// State for tracking the number of matches on each kind of CFGElement by the
+// CFGMatchSwitch. Currently only tracks CFGStmt and CFGInitializer.
+struct CFGElementMatches {
+  unsigned StmtMatches = 0;
+  unsigned InitializerMatches = 0;
+};
+
+// Returns a match switch that counts the number of local variables
+// (singly-declared) and fields initialized to the integer literal 42.
+auto buildCFGMatchSwitch() {
+  return CFGMatchSwitchBuilder()
+  .CaseOfCFGStmt(
+  declStmt(hasSingleDecl(
+  varDecl(hasInitializer(integerLiteral(equals(42)),
+  [](const DeclStmt *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.StmtMatches++; })
+  .CaseOfCFGInit(
+  cxxCtorInitializer(withInitializer(integerLiteral(equals(42,
+  [](const CXXCtorInitializer *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.InitializerMatches++; })
+  .Build();
+}
+
+// Runs the match switch `MS` on the control flow graph generated from `Code`,
+// tracking information in state `S`. For simplicity, this test utility is
+// restricted to CFGs with a single control flow block (excluding entry and
+// exit blocks) - generated by `Code` with sequential flow (i.e. no branching).
+//
+// Requirements:
+//
+//  `Code` must contain a function named `f`, the body of this function will be
+//  used to generate the CFG.
+template 
+void applySwitchToCode(CFGMatchSwitch &MS, State &S,
+   llvm::StringRef Code) {
+  auto Unit = tooling::buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
+  auto &Ctx = Unit->getASTContext();
+  const auto *F = selectFirst(
+  "f", match(functionDecl(isDefinition(), hasName("f")).bind("f"), Ctx));
+
+  CFG::BuildOptions BO;
+  BO.AddInitializers = true;
+
+  auto CFG = CFG::buildCFG(F, F->getBody(), &Ctx, BO);
+  auto CFGBlock = *CFG->getEntry().succ_begin();
+  for (auto &Elt : CFGBlock->Elements) {
+MS(Elt, Ctx, S);
+  }
+}
+
+TEST(CFGMatchSwitchTest, NoInitializationTo42) {
+  CFGMatchSwitch Switch =

[PATCH] D131616: [clang][dataflow] Generalise match switch utility to other AST types and add a `CFGMatchSwitch` which currently handles `CFGStmt` and `CFGInitializer`.

2022-08-31 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 457052.
wyt added a comment.

Use `u` suffix to declare constants as unsigned in `CFGMatchSwitchTest.cpp`.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131616/new/

https://reviews.llvm.org/D131616

Files:
  clang/include/clang/Analysis/FlowSensitive/CFGMatchSwitch.h
  clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
  clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
@@ -5,12 +5,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===--===//
-//
-//  This file defines a simplistic version of Constant Propagation as an example
-//  of a forward, monotonic dataflow analysis. The analysis tracks all
-//  variables in the scope, but lacks escape analysis.
-//
-//===--===//
 
 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
 #include "TestingSupport.h"
Index: clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
===
--- clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -4,6 +4,7 @@
   )
 
 add_clang_unittest(ClangAnalysisFlowSensitiveTests
+  CFGMatchSwitchTest.cpp
   ChromiumCheckModelTest.cpp
   DataflowAnalysisContextTest.cpp
   DataflowEnvironmentTest.cpp
Index: clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
@@ -0,0 +1,124 @@
+//===- unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.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 "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace dataflow;
+using namespace ast_matchers;
+
+namespace {
+// State for tracking the number of matches on each kind of CFGElement by the
+// CFGMatchSwitch. Currently only tracks CFGStmt and CFGInitializer.
+struct CFGElementMatches {
+  unsigned StmtMatches = 0;
+  unsigned InitializerMatches = 0;
+};
+
+// Returns a match switch that counts the number of local variables
+// (singly-declared) and fields initialized to the integer literal 42.
+auto buildCFGMatchSwitch() {
+  return CFGMatchSwitchBuilder()
+  .CaseOfCFGStmt(
+  declStmt(hasSingleDecl(
+  varDecl(hasInitializer(integerLiteral(equals(42)),
+  [](const DeclStmt *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.StmtMatches++; })
+  .CaseOfCFGInit(
+  cxxCtorInitializer(withInitializer(integerLiteral(equals(42,
+  [](const CXXCtorInitializer *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.InitializerMatches++; })
+  .Build();
+}
+
+// Runs the match switch `MS` on the control flow graph generated from `Code`,
+// tracking information in state `S`. For simplicity, this test utility is
+// restricted to CFGs with a single control flow block (excluding entry and
+// exit blocks) - generated by `Code` with sequential flow (i.e. no branching).
+//
+// Requirements:
+//
+//  `Code` must contain a function named `f`, the body of this function will be
+//  used to generate the CFG.
+template 
+void applySwitchToCode(CFGMatchSwitch &MS, State &S,
+   llvm::StringRef Code) {
+  auto Unit = tooling::buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
+  auto &Ctx = Unit->getASTContext();
+  const auto *F = selectFirst(
+  "f", match(functionDecl(isDefinition(), hasName("f")).bind("f"), Ctx));
+
+  CFG::BuildOptions BO;
+  BO.AddInitializers = true;
+
+  auto CFG = CFG::buildCFG(F, F->getBody(), &Ctx, BO);
+  auto CFGBlock = *CFG->getEntry().succ_begin();
+  for (auto &Elt : CFGBlock->Elements) {
+MS(Elt, Ctx, S);
+  }
+}
+
+TEST(CFGMatchSwitchTest, NoInitializationTo42) {
+  CFGMatchSwitch Switch = buildCFGMatchSwitch();
+  CFGElementMatches Counter;
+  applySwi

[PATCH] D131616: [clang][dataflow] Generalise match switch utility to other AST types and add a `CFGMatchSwitch` which currently handles `CFGStmt` and `CFGInitializer`.

2022-09-01 Thread weiyi via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGd931ac9e27ca: [clang][dataflow] Generalise match switch 
utility to other AST types and add a… (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131616/new/

https://reviews.llvm.org/D131616

Files:
  clang/include/clang/Analysis/FlowSensitive/CFGMatchSwitch.h
  clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
  clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
@@ -5,12 +5,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===--===//
-//
-//  This file defines a simplistic version of Constant Propagation as an example
-//  of a forward, monotonic dataflow analysis. The analysis tracks all
-//  variables in the scope, but lacks escape analysis.
-//
-//===--===//
 
 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
 #include "TestingSupport.h"
Index: clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
===
--- clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -4,6 +4,7 @@
   )
 
 add_clang_unittest(ClangAnalysisFlowSensitiveTests
+  CFGMatchSwitchTest.cpp
   ChromiumCheckModelTest.cpp
   DataflowAnalysisContextTest.cpp
   DataflowEnvironmentTest.cpp
Index: clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
@@ -0,0 +1,124 @@
+//===- unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.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 "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace dataflow;
+using namespace ast_matchers;
+
+namespace {
+// State for tracking the number of matches on each kind of CFGElement by the
+// CFGMatchSwitch. Currently only tracks CFGStmt and CFGInitializer.
+struct CFGElementMatches {
+  unsigned StmtMatches = 0;
+  unsigned InitializerMatches = 0;
+};
+
+// Returns a match switch that counts the number of local variables
+// (singly-declared) and fields initialized to the integer literal 42.
+auto buildCFGMatchSwitch() {
+  return CFGMatchSwitchBuilder()
+  .CaseOfCFGStmt(
+  declStmt(hasSingleDecl(
+  varDecl(hasInitializer(integerLiteral(equals(42)),
+  [](const DeclStmt *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.StmtMatches++; })
+  .CaseOfCFGInit(
+  cxxCtorInitializer(withInitializer(integerLiteral(equals(42,
+  [](const CXXCtorInitializer *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.InitializerMatches++; })
+  .Build();
+}
+
+// Runs the match switch `MS` on the control flow graph generated from `Code`,
+// tracking information in state `S`. For simplicity, this test utility is
+// restricted to CFGs with a single control flow block (excluding entry and
+// exit blocks) - generated by `Code` with sequential flow (i.e. no branching).
+//
+// Requirements:
+//
+//  `Code` must contain a function named `f`, the body of this function will be
+//  used to generate the CFG.
+template 
+void applySwitchToCode(CFGMatchSwitch &MS, State &S,
+   llvm::StringRef Code) {
+  auto Unit = tooling::buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
+  auto &Ctx = Unit->getASTContext();
+  const auto *F = selectFirst(
+  "f", match(functionDecl(isDefinition(), hasName("f")).bind("f"), Ctx));
+
+  CFG::BuildOptions BO;
+  BO.AddInitializers = true;
+
+  auto CFG = CFG::buildCFG(F, F->getBody(), &Ctx, BO);
+  auto CFGBlock = *CFG->getEntry().succ_begin();
+  for (auto &Elt : CFGBlock->Elements) {
+MS(Elt, Ctx, S);
+  }
+}
+
+TEST(CFGMatchSwitchTest, NoInitializationTo42) {
+  CFGMatchSwitch Switch =

[PATCH] D132756: [clang][dataflow] Refactor `TypeErasedDataflowAnalysisTest` - replace usage of the deprecated overload of `checkDataflow`.

2022-09-01 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 457231.
wyt added a comment.

Update build target dependency.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132756/new/

https://reviews.llvm.org/D132756

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
  utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel

Index: utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel
===
--- utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel
+++ utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel
@@ -134,6 +134,7 @@
 "//clang:serialization",
 "//clang:tooling",
 "//llvm:Support",
+"//llvm:TestingADT",
 "//llvm:TestingSupport",
 "//llvm:gmock",
 "//llvm:gtest",
Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -24,8 +24,10 @@
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Testing/ADT/StringMapEntry.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -42,12 +44,10 @@
 using namespace dataflow;
 using namespace test;
 using namespace ast_matchers;
-using ::testing::_;
-using ::testing::ElementsAre;
+using llvm::IsStringMapEntry;
+using ::testing::DescribeMatcher;
 using ::testing::IsEmpty;
-using ::testing::IsNull;
 using ::testing::NotNull;
-using ::testing::Pair;
 using ::testing::Test;
 using ::testing::UnorderedElementsAre;
 
@@ -129,7 +129,8 @@
 }
 
 struct FunctionCallLattice {
-  llvm::SmallSet CalledFunctions;
+  using FunctionSet = llvm::SmallSet;
+  FunctionSet CalledFunctions;
 
   bool operator==(const FunctionCallLattice &Other) const {
 return CalledFunctions == Other.CalledFunctions;
@@ -195,16 +196,20 @@
 
 ASSERT_THAT_ERROR(
 test::checkDataflow(
-Code, "target",
-[](ASTContext &C, Environment &) {
-  return FunctionCallAnalysis(C);
-},
+AnalysisInputs(
+Code, ast_matchers::hasName("target"),
+[](ASTContext &C, Environment &) {
+  return FunctionCallAnalysis(C);
+})
+.withASTBuildArgs({"-fsyntax-only", "-std=c++17"})
+.withASTBuildVirtualMappedFiles(std::move(FilesContents)),
+/*VerifyResults=*/
 [&Expectations](
-llvm::ArrayRef>>
-Results,
-ASTContext &) { EXPECT_THAT(Results, Expectations); },
-{"-fsyntax-only", "-std=c++17"}, FilesContents),
+const llvm::StringMap<
+DataflowAnalysisState> &Results,
+const AnalysisOutputs &) {
+  EXPECT_THAT(Results, Expectations);
+}),
 llvm::Succeeded());
   }
 };
@@ -212,12 +217,16 @@
 MATCHER_P(HoldsFunctionCallLattice, m,
   ((negation ? "doesn't hold" : "holds") +
llvm::StringRef(" a lattice element that ") +
-   ::testing::DescribeMatcher(m, negation))
+   DescribeMatcher(m))
   .str()) {
   return ExplainMatchResult(m, arg.Lattice, result_listener);
 }
 
-MATCHER_P(HasCalledFunctions, m, "") {
+MATCHER_P(HasCalledFunctions, m,
+  ((negation ? "doesn't hold" : "holds") +
+   llvm::StringRef(" a set of called functions that ") +
+   DescribeMatcher(m))
+  .str()) {
   return ExplainMatchResult(m, arg.CalledFunctions, result_listener);
 }
 
@@ -231,9 +240,9 @@
   // [[p]]
 }
   )";
-  runDataflow(Code, UnorderedElementsAre(
-Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
-  UnorderedElementsAre("foo", "bar"));
+  runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
+"p", HoldsFunctionCallLattice(HasCalledFunctions(
+ UnorderedElementsAre("foo", "bar"));
 }
 
 TEST_F(NoreturnDestructorTest, ConditionalOperatorLeftBranchReturns) {
@@ -246,9 +255,9 @@
   // [[p]]
 }
   )";
-  runDataflow(Code, UnorderedElementsAre(
-Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
-  UnorderedElementsAre("foo"));
+  runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
+"p", HoldsFunctionCallLattice(HasCalledFunctions(
+ UnorderedElementsAre("foo"));
 }

[PATCH] D132147: [clang][dataflow] Refactor `TestingSupport.h`

2022-09-01 Thread weiyi via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rGdb898d43b08e: [clang][dataflow] Refactor `TestingSupport.h` 
(authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132147/new/

https://reviews.llvm.org/D132147

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -1234,49 +1234,48 @@
   template 
   T Make();
 )");
-const tooling::FileContentMappings FileContents(Headers.begin(),
-Headers.end());
 UncheckedOptionalAccessModelOptions Options{
 /*IgnoreSmartPointerDereference=*/true};
 std::vector Diagnostics;
 llvm::Error Error = checkDataflow(
-SourceCode, FuncMatcher,
-[Options](ASTContext &Ctx, Environment &) {
-  return UncheckedOptionalAccessModel(Ctx, Options);
-},
-[&Diagnostics, Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
-ASTContext &Ctx, const CFGStmt &Stmt,
-const TypeErasedDataflowAnalysisState &State) mutable {
-  auto StmtDiagnostics =
-  Diagnoser.diagnose(Ctx, Stmt.getStmt(), State.Env);
-  llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
-},
-[&Diagnostics](AnalysisData AnalysisData) {
-  auto &SrcMgr = AnalysisData.ASTCtx.getSourceManager();
-
+AnalysisInputs(
+SourceCode, std::move(FuncMatcher),
+[Options](ASTContext &Ctx, Environment &) {
+  return UncheckedOptionalAccessModel(Ctx, Options);
+})
+.withPostVisitCFG(
+[&Diagnostics,
+ Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
+ASTContext &Ctx, const CFGElement &Elt,
+const TypeErasedDataflowAnalysisState &State) mutable {
+  auto Stmt = Elt.getAs();
+  if (!Stmt) {
+return;
+  }
+  auto StmtDiagnostics =
+  Diagnoser.diagnose(Ctx, Stmt->getStmt(), State.Env);
+  llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
+})
+.withASTBuildArgs(
+{"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"})
+.withASTBuildVirtualMappedFiles(
+tooling::FileContentMappings(Headers.begin(), Headers.end())),
+/*VerifyResults=*/[&Diagnostics](
+  const llvm::DenseMap
+  &Annotations,
+  const AnalysisOutputs &AO) {
   llvm::DenseSet AnnotationLines;
-  for (const auto &Pair : AnalysisData.Annotations) {
-auto *Stmt = Pair.getFirst();
-AnnotationLines.insert(
-SrcMgr.getPresumedLineNumber(Stmt->getBeginLoc()));
-// We add both the begin and end locations, so that if the
-// statement spans multiple lines then the test will fail.
-//
-// FIXME: Going forward, we should change this to instead just
-// get the single line number from the annotation itself, rather
-// than looking at the statement it's attached to.
-AnnotationLines.insert(
-SrcMgr.getPresumedLineNumber(Stmt->getEndLoc()));
+  for (const auto &[Line, _] : Annotations) {
+AnnotationLines.insert(Line);
   }
-
+  auto &SrcMgr = AO.ASTCtx.getSourceManager();
   llvm::DenseSet DiagnosticLines;
   for (SourceLocation &Loc : Diagnostics) {
 DiagnosticLines.insert(SrcMgr.getPresumedLineNumber(Loc));
   }
 
   EXPECT_THAT(DiagnosticLines, ContainerEq(AnnotationLines));
-},
-{"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents);
+});
 if (Error)
   FAIL() << llvm::toString(std::move(Error));
   }
Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -56,47 +56,160 @@
 
 namespace test {
 
-// Returns assertions based on annotations that are present after statements in
-// `AnnotatedCode`.
-llvm::Expected>
-buildStatementToAnnotationMapping(const FunctionDecl *Func,
-  llvm::

[PATCH] D132377: [clang][dataflow] Add `SetupTest` parameter for `AnalysisInputs`.

2022-09-01 Thread weiyi via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG5a4aece76de7: [clang][dataflow] Add `SetupTest` parameter 
for `AnalysisInputs`. (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132377/new/

https://reviews.llvm.org/D132377

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -96,6 +96,11 @@
 
   /// Optional fields can be set with methods of the form `withFieldName(...)`.
   AnalysisInputs &&
+  withSetupTest(std::function Arg) && {
+SetupTest = std::move(Arg);
+return std::move(*this);
+  }
+  AnalysisInputs &&
   withPostVisitCFG(std::function
Arg) && {
@@ -120,6 +125,11 @@
   /// takes as argument the AST generated from the code being analyzed and the
   /// initial state from which the analysis starts with.
   std::function MakeAnalysis;
+  /// Optional. If provided, this function is executed immediately before
+  /// running the dataflow analysis to allow for additional setup. All fields in
+  /// the `AnalysisOutputs` argument will be initialized except for the
+  /// `BlockStates` field which is only computed later during the analysis.
+  std::function SetupTest = nullptr;
   /// Optional. If provided, this function is applied on each CFG element after
   /// the analysis has been run.
   std::function
-getAnnotationLinesAndContent(const AnalysisOutputs &AO);
-
-// FIXME: Return a string map instead of a vector of pairs.
-//
-/// Returns the analysis states at each annotated statement in `AO.Code`.
-template 
-llvm::Expected>>>
-getAnnotationStates(const AnalysisOutputs &AO) {
-  using StateT = DataflowAnalysisState;
-  // FIXME: Extend to annotations on non-statement constructs.
-  // Get annotated statements.
-  llvm::Expected>
-  MaybeStmtToAnnotations =
-  buildStatementToAnnotationMapping(AO.Target, AO.Code);
-  if (!MaybeStmtToAnnotations)
-return MaybeStmtToAnnotations.takeError();
-  auto &StmtToAnnotations = *MaybeStmtToAnnotations;
-
-  // Compute a map from statement annotations to the state computed
-  // for the program point immediately after the annotated statement.
-  std::vector> Results;
-  for (const CFGBlock *Block : AO.CFCtx.getCFG()) {
-// Skip blocks that were not evaluated.
-if (!AO.BlockStates[Block->getBlockID()])
-  continue;
-
-transferBlock(
-AO.CFCtx, AO.BlockStates, *Block, AO.InitEnv, AO.Analysis,
-[&Results,
- &StmtToAnnotations](const clang::CFGElement &Element,
- const TypeErasedDataflowAnalysisState &State) {
-  auto Stmt = Element.getAs();
-  if (!Stmt)
-return;
-  auto It = StmtToAnnotations.find(Stmt->getStmt());
-  if (It == StmtToAnnotations.end())
-return;
-  auto *Lattice =
-  llvm::any_cast(&State.Lattice.Value);
-  Results.emplace_back(It->second, StateT{*Lattice, State.Env});
-});
-  }
-
-  return Results;
-}
+buildLineToAnnotationMapping(SourceManager &SM,
+ llvm::Annotations AnnotatedCode);
 
 /// Runs dataflow specified from `AI.MakeAnalysis` and `AI.PostVisitCFG` on the
 /// body of the function that matches `AI.TargetFuncMatcher` in `AI.Code`.
@@ -200,9 +166,9 @@
 ///
 ///   `VerifyResults` must be provided.
 template 
-llvm::Error checkDataflow(
-AnalysisInputs AI,
-std::function VerifyResults) {
+llvm::Error
+checkDataflow(AnalysisInputs AI,
+  std::function VerifyResults) {
   // Build AST context from code.
   llvm::Annotations AnnotatedCode(AI.Code);
   auto Unit = tooling::buildASTFromCodeWithArgs(
@@ -236,7 +202,7 @@
 return MaybeCFCtx.takeError();
   auto &CFCtx = *MaybeCFCtx;
 
-  // Initialize states and run dataflow analysis.
+  // Initialize states for running dataflow analysis.
   DataflowAnalysisContext DACtx(std::make_unique());
   Environment InitEnv(DACtx, *Target);
   auto Analysis = AI.MakeAnalysis(Context, InitEnv);
@@ -251,19 +217,26 @@
 };
   }
 
-  // If successful, the run returns a mapping from block IDs to the
-  // post-analysis states for the CFG blocks that have been evaluated.
+  // Additional test setup.
+  AnalysisOutputs AO{AnnotatedCode, Context, Target, CFCtx,
+ Analysis,  InitEnv, {}};
+  if (AI.SetupTest) {
+if (auto Error = AI.SetupTest(AO))
+  return Error;
+  }
+
+  // If successful, the dataflow analysis returns a mapping from block IDs to
+  // the post-analysis states for the CFG blocks that have been evaluated.
   llvm::Expected>>
   MaybeBlockStates = runTypeErasedDataflowAnaly

[PATCH] D132763: [clang][dataflow] Use `StringMap` for storing analysis states at annotated points instead of `vector>`.

2022-09-01 Thread weiyi via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG8dd14c427ae5: [clang][dataflow] Use `StringMap` for storing 
analysis states at annotated… (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132763/new/

https://reviews.llvm.org/D132763

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -426,12 +426,12 @@
std::pair>>
Results,
ASTContext &ASTCtx) {
-ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
- Pair("p2", _), Pair("p1", _)));
-const Environment &Env1 = Results[3].second.Env;
-const Environment &Env2 = Results[2].second.Env;
-const Environment &Env3 = Results[1].second.Env;
-const Environment &Env4 = Results[0].second.Env;
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _),
+ Pair("p3", _), Pair("p4", _)));
+const Environment &Env1 = Results[0].second.Env;
+const Environment &Env2 = Results[1].second.Env;
+const Environment &Env3 = Results[2].second.Env;
+const Environment &Env4 = Results[3].second.Env;
 
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
@@ -579,10 +579,10 @@
  Results,
  ASTContext &ASTCtx) {
 ASSERT_THAT(Results,
-ElementsAre(Pair("p3", _), Pair("p2", _), Pair("p1", _)));
-const Environment &Env1 = Results[2].second.Env;
+ElementsAre(Pair("p1", _), Pair("p2", _), Pair("p3", _)));
+const Environment &Env1 = Results[0].second.Env;
 const Environment &Env2 = Results[1].second.Env;
-const Environment &Env3 = Results[0].second.Env;
+const Environment &Env3 = Results[2].second.Env;
 
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
@@ -622,12 +622,12 @@
  std::pair>>
  Results,
  ASTContext &ASTCtx) {
-ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
- Pair("p2", _), Pair("p1", _)));
-const Environment &Env1 = Results[3].second.Env;
-const Environment &Env2 = Results[2].second.Env;
-const Environment &Env3 = Results[1].second.Env;
-const Environment &Env4 = Results[0].second.Env;
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _),
+ Pair("p3", _), Pair("p4", _)));
+const Environment &Env1 = Results[0].second.Env;
+const Environment &Env2 = Results[1].second.Env;
+const Environment &Env3 = Results[2].second.Env;
+const Environment &Env4 = Results[3].second.Env;
 
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
@@ -751,14 +751,14 @@
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
 
-ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _)));
 
-const Environment &Env1 = Results[1].second.Env;
+const Environment &Env1 = Results[0].second.Env;
 auto *FooVal1 =
 cast(Env1.getValue(*FooDecl, SkipPast::None));
 EXPECT_TRUE(Env1.flowConditionImplies(*FooVal1));
 
-const Environment &Env2 = Results[0].second.Env;
+const Environment &Env2 = Results[1].second.Env;
 auto *FooVal2 =
 cast(Env2.getValue(*FooDecl, SkipPast::None));
 EXPECT_FALSE(Env2.flowConditionImplies(*FooVal2));
@@ -785,14 +785,14 @@
 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
 ASSERT_THAT(FooDecl, NotNull());
 
-ASSERT_THAT(Results, ElementsAre(Pair("p2", _), Pair("p1", _)));
+ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _)));
 
-const Environment &Env1 = Results[1].second.Env;
+const Env

[PATCH] D132756: [clang][dataflow] Refactor `TypeErasedDataflowAnalysisTest` - replace usage of the deprecated overload of `checkDataflow`.

2022-09-01 Thread weiyi via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG14757d5b845b: [clang][dataflow] Refactor 
`TypeErasedDataflowAnalysisTest` - replace usage of… (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132756/new/

https://reviews.llvm.org/D132756

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
  utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel

Index: utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel
===
--- utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel
+++ utils/bazel/llvm-project-overlay/clang/unittests/BUILD.bazel
@@ -134,6 +134,7 @@
 "//clang:serialization",
 "//clang:tooling",
 "//llvm:Support",
+"//llvm:TestingADT",
 "//llvm:TestingSupport",
 "//llvm:gmock",
 "//llvm:gtest",
Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -24,8 +24,10 @@
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Testing/ADT/StringMapEntry.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -42,12 +44,10 @@
 using namespace dataflow;
 using namespace test;
 using namespace ast_matchers;
-using ::testing::_;
-using ::testing::ElementsAre;
+using llvm::IsStringMapEntry;
+using ::testing::DescribeMatcher;
 using ::testing::IsEmpty;
-using ::testing::IsNull;
 using ::testing::NotNull;
-using ::testing::Pair;
 using ::testing::Test;
 using ::testing::UnorderedElementsAre;
 
@@ -129,7 +129,8 @@
 }
 
 struct FunctionCallLattice {
-  llvm::SmallSet CalledFunctions;
+  using FunctionSet = llvm::SmallSet;
+  FunctionSet CalledFunctions;
 
   bool operator==(const FunctionCallLattice &Other) const {
 return CalledFunctions == Other.CalledFunctions;
@@ -195,16 +196,20 @@
 
 ASSERT_THAT_ERROR(
 test::checkDataflow(
-Code, "target",
-[](ASTContext &C, Environment &) {
-  return FunctionCallAnalysis(C);
-},
+AnalysisInputs(
+Code, ast_matchers::hasName("target"),
+[](ASTContext &C, Environment &) {
+  return FunctionCallAnalysis(C);
+})
+.withASTBuildArgs({"-fsyntax-only", "-std=c++17"})
+.withASTBuildVirtualMappedFiles(std::move(FilesContents)),
+/*VerifyResults=*/
 [&Expectations](
-llvm::ArrayRef>>
-Results,
-ASTContext &) { EXPECT_THAT(Results, Expectations); },
-{"-fsyntax-only", "-std=c++17"}, FilesContents),
+const llvm::StringMap<
+DataflowAnalysisState> &Results,
+const AnalysisOutputs &) {
+  EXPECT_THAT(Results, Expectations);
+}),
 llvm::Succeeded());
   }
 };
@@ -212,12 +217,16 @@
 MATCHER_P(HoldsFunctionCallLattice, m,
   ((negation ? "doesn't hold" : "holds") +
llvm::StringRef(" a lattice element that ") +
-   ::testing::DescribeMatcher(m, negation))
+   DescribeMatcher(m))
   .str()) {
   return ExplainMatchResult(m, arg.Lattice, result_listener);
 }
 
-MATCHER_P(HasCalledFunctions, m, "") {
+MATCHER_P(HasCalledFunctions, m,
+  ((negation ? "doesn't hold" : "holds") +
+   llvm::StringRef(" a set of called functions that ") +
+   DescribeMatcher(m))
+  .str()) {
   return ExplainMatchResult(m, arg.CalledFunctions, result_listener);
 }
 
@@ -231,9 +240,9 @@
   // [[p]]
 }
   )";
-  runDataflow(Code, UnorderedElementsAre(
-Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
-  UnorderedElementsAre("foo", "bar"));
+  runDataflow(Code, UnorderedElementsAre(IsStringMapEntry(
+"p", HoldsFunctionCallLattice(HasCalledFunctions(
+ UnorderedElementsAre("foo", "bar"));
 }
 
 TEST_F(NoreturnDestructorTest, ConditionalOperatorLeftBranchReturns) {
@@ -246,9 +255,9 @@
   // [[p]]
 }
   )";
-  runDataflow(Code, UnorderedElementsAre(
-Pair("p", HoldsFunctionCallLattice(HasCalledFunctions(
-  UnorderedElementsAre("foo"));
+  runDataflow(Code, UnorderedElementsAr

[PATCH] D133930: [clang][dataflow] Replace `transfer(const Stmt *, ...)` with `transfer(const CFGElement *, ...)` in `Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel`.

2022-09-15 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D133930

Files:
  
clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h
  clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
  clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -14,10 +14,8 @@
 #include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Tooling/Tooling.h"
-#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -1248,13 +1246,9 @@
  Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
 ASTContext &Ctx, const CFGElement &Elt,
 const TypeErasedDataflowAnalysisState &State) mutable {
-  auto Stmt = Elt.getAs();
-  if (!Stmt) {
-return;
-  }
-  auto StmtDiagnostics =
-  Diagnoser.diagnose(Ctx, Stmt->getStmt(), State.Env);
-  llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
+  auto EltDiagnostics =
+  Diagnoser.diagnose(Ctx, &Elt, State.Env);
+  llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));
 })
 .withASTBuildArgs(
 {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"})
Index: clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
===
--- clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -18,8 +18,9 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/Stmt.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
-#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
 #include "clang/Analysis/FlowSensitive/NoopLattice.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "clang/Basic/SourceLocation.h"
@@ -559,41 +560,42 @@
   // lot of duplicated work (e.g. string comparisons), consider providing APIs
   // that avoid it through memoization.
   auto IgnorableOptional = ignorableOptional(Options);
-  return MatchSwitchBuilder()
+  return CFGMatchSwitchBuilder()
   // Attach a symbolic "has_value" state to optional values that we see for
   // the first time.
-  .CaseOf(
+  .CaseOfCFGStmt(
   expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
   initializeOptionalReference)
 
   // make_optional
-  .CaseOf(isMakeOptionalCall(), transferMakeOptionalCall)
+  .CaseOfCFGStmt(isMakeOptionalCall(), transferMakeOptionalCall)
 
   // optional::optional
-  .CaseOf(
+  .CaseOfCFGStmt(
   isOptionalInPlaceConstructor(),
   [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
  LatticeTransferState &State) {
 assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true));
   })
-  .CaseOf(
+  .CaseOfCFGStmt(
   isOptionalNulloptConstructor(),
   [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
  LatticeTransferState &State) {
 assignOptionalValue(*E, State,
 State.Env.getBoolLiteralValue(false));
   })
-  .CaseOf(isOptionalValueOrConversionConstructor(),
-transferValueOrConversionConstructor)
+  .CaseOfCFGStmt(isOptionalValueOrConversionConstructor(),
+   transferValueOrConversionConstructor)
 
   // optional::operator=
-  .CaseOf(isOptionalValueOrConversionAssignment(),
-   transferValueOrConversionAssignment)
-  .CaseOf(isOptionalNulloptAssignment(),
-   transferNulloptAssignment)
+  .CaseOfCFGStmt(
+  isOptionalValueOrConversionAssignment(),
+  transferValueOrConversionAssignment)
+  .CaseOfCFGStmt(isOptionalNulloptAssignment(),
+

[PATCH] D133931: [clang][dataflow] Replace `transfer(const Stmt *, ...)` with `transfer(const CFGElement *, ...)` in `clang/Analysis/FlowSensitive`.

2022-09-15 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D133931

Files:
  clang/include/clang/Analysis/FlowSensitive/NoopAnalysis.h
  clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
  clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp
  clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
  clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -111,7 +111,7 @@
 
   static NonConvergingLattice initialElement() { return {0}; }
 
-  void transfer(const Stmt *S, NonConvergingLattice &E, Environment &Env) {
+  void transfer(const CFGElement *, NonConvergingLattice &E, Environment &) {
 ++E.State;
   }
 };
@@ -162,7 +162,11 @@
 
   static FunctionCallLattice initialElement() { return {}; }
 
-  void transfer(const Stmt *S, FunctionCallLattice &E, Environment &Env) {
+  void transfer(const CFGElement *Elt, FunctionCallLattice &E, Environment &) {
+auto CS = Elt->getAs();
+if (!CS)
+  return;
+auto S = CS->getStmt();
 if (auto *C = dyn_cast(S)) {
   if (auto *F = dyn_cast(C->getCalleeDecl())) {
 E.CalledFunctions.insert(F->getNameInfo().getAsString());
@@ -314,7 +318,11 @@
 
   static NoopLattice initialElement() { return {}; }
 
-  void transfer(const Stmt *S, NoopLattice &, Environment &Env) {
+  void transfer(const CFGElement *Elt, NoopLattice &, Environment &Env) {
+auto CS = Elt->getAs();
+if (!CS)
+  return;
+auto S = CS->getStmt();
 auto SpecialBoolRecordDecl = recordDecl(hasName("SpecialBool"));
 auto HasSpecialBoolType = hasType(SpecialBoolRecordDecl);
 
@@ -466,7 +474,11 @@
 
   static NoopLattice initialElement() { return {}; }
 
-  void transfer(const Stmt *S, NoopLattice &, Environment &Env) {
+  void transfer(const CFGElement *Elt, NoopLattice &, Environment &Env) {
+auto CS = Elt->getAs();
+if (!CS)
+  return;
+auto S = CS->getStmt();
 auto OptionalIntRecordDecl = recordDecl(hasName("OptionalInt"));
 auto HasOptionalIntType = hasType(OptionalIntRecordDecl);
 
Index: clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp
@@ -19,16 +19,15 @@
 #include "clang/AST/Stmt.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
-#include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/None.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/Error.h"
-#include "llvm/Testing/Support/Annotations.h"
 #include "llvm/Testing/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -122,8 +121,12 @@
 return ConstantPropagationLattice::bottom();
   }
 
-  void transfer(const Stmt *S, ConstantPropagationLattice &Element,
+  void transfer(const CFGElement *E, ConstantPropagationLattice &Element,
 Environment &Env) {
+auto CS = E->getAs();
+if (!CS)
+  return;
+auto S = CS->getStmt();
 auto matcher = stmt(
 anyOf(declStmt(hasSingleDecl(varDecl(hasType(isInteger()),
  hasInitializer(expr().bind(kInit)))
Index: clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp
@@ -19,17 +19,16 @@
 #include "clang/AST/Stmt.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
 #include "clang/Analysis/FlowSensitive/MapLattice.h"
-#include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/None.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringRef.h"
 #includ

[PATCH] D133933: [clang][dataflow] Modify `transfer` in `DataflowModel` to take `CFGElement` as input instead of `Stmt`.

2022-09-15 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

To keep API of transfer functions consistent.

The single use of this transfer function in `ChromiumCheckModel` is also 
updated.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D133933

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
  clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
  clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
@@ -120,9 +120,7 @@
   static NoopLattice initialElement() { return NoopLattice(); }
 
   void transfer(const CFGElement *E, NoopLattice &, Environment &Env) {
-if (auto S = E->getAs()) {
-  M.transfer(S->getStmt(), Env);
-}
+M.transfer(E, Env);
   }
 
 private:
Index: clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
===
--- clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
+++ clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
@@ -50,7 +50,11 @@
   return CheckDecls.contains(&D);
 }
 
-bool ChromiumCheckModel::transfer(const Stmt *Stmt, Environment &Env) {
+bool ChromiumCheckModel::transfer(const CFGElement *Elt, Environment &Env) {
+  auto CS = Elt->getAs();
+  if (!CS)
+return false;
+  auto Stmt = CS->getStmt();
   if (const auto *Call = dyn_cast(Stmt)) {
 if (const auto *M = dyn_cast(Call->getDirectCallee())) {
   if (isCheckLikeMethod(CheckDecls, *M)) {
Index: clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
===
--- clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
+++ clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
@@ -26,7 +26,7 @@
 class ChromiumCheckModel : public DataflowModel {
 public:
   ChromiumCheckModel() = default;
-  bool transfer(const Stmt *Stmt, Environment &Env) override;
+  bool transfer(const CFGElement *Elt, Environment &Env) override;
 
 private:
   /// Declarations for `::logging::CheckError::.*Check`, lazily initialized.
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
@@ -231,8 +231,8 @@
 /// example, a model may capture a type and its related functions.
 class DataflowModel : public Environment::ValueModel {
 public:
-  /// Return value indicates whether the model processed the `Stmt`.
-  virtual bool transfer(const Stmt *Stmt, Environment &Env) = 0;
+  /// Return value indicates whether the model processed the `Element`.
+  virtual bool transfer(const CFGElement *Elt, Environment &Env) = 0;
 };
 
 } // namespace dataflow


Index: clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
@@ -120,9 +120,7 @@
   static NoopLattice initialElement() { return NoopLattice(); }
 
   void transfer(const CFGElement *E, NoopLattice &, Environment &Env) {
-if (auto S = E->getAs()) {
-  M.transfer(S->getStmt(), Env);
-}
+M.transfer(E, Env);
   }
 
 private:
Index: clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
===
--- clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
+++ clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
@@ -50,7 +50,11 @@
   return CheckDecls.contains(&D);
 }
 
-bool ChromiumCheckModel::transfer(const Stmt *Stmt, Environment &Env) {
+bool ChromiumCheckModel::transfer(const CFGElement *Elt, Environment &Env) {
+  auto CS = Elt->getAs();
+  if (!CS)
+return false;
+  auto Stmt = CS->getStmt();
   if (const auto *Call = dyn_cast(Stmt)) {
 if (const auto *M = dyn_cast(Call->getDirectCallee())) {
   if (isCheckLikeMethod(CheckDecls, *M)) {
Index: clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
===
--- clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
+++ clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
@@ -26,7 +26,7 @@
 class ChromiumCheckModel

[PATCH] D133935: [clang][dataflow] Refactor `clang/Analysis/FlowSensitive/MatchSwitchTest.cpp`.

2022-09-15 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

- Remove use of `runDataflowAnalysis` to keep test isolated.
- Add test for `ASTMatchSwitch`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D133935

Files:
  clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
@@ -7,26 +7,15 @@
 //===--===//
 
 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
-#include "TestingSupport.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/Stmt.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
-#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
-#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
-#include "clang/Analysis/FlowSensitive/MapLattice.h"
 #include "clang/Tooling/Tooling.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Testing/Support/Annotations.h"
-#include "llvm/Testing/Support/Error.h"
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include 
 #include 
@@ -38,162 +27,112 @@
 using namespace dataflow;
 
 namespace {
-using ::testing::Pair;
-using ::testing::UnorderedElementsAre;
 
-class BooleanLattice {
-public:
-  BooleanLattice() : Value(false) {}
-  explicit BooleanLattice(bool B) : Value(B) {}
-
-  static BooleanLattice bottom() { return BooleanLattice(false); }
-
-  static BooleanLattice top() { return BooleanLattice(true); }
-
-  LatticeJoinEffect join(BooleanLattice Other) {
-auto Prev = Value;
-Value = Value || Other.Value;
-return Prev == Value ? LatticeJoinEffect::Unchanged
- : LatticeJoinEffect::Changed;
-  }
-
-  friend bool operator==(BooleanLattice LHS, BooleanLattice RHS) {
-return LHS.Value == RHS.Value;
-  }
-
-  friend std::ostream &operator<<(std::ostream &Os, const BooleanLattice &B) {
-Os << B.Value;
-return Os;
-  }
-
-  bool value() const { return Value; }
-
-private:
-  bool Value;
-};
-} // namespace
-
-MATCHER_P(Holds, m,
-  ((negation ? "doesn't hold" : "holds") +
-   llvm::StringRef(" a lattice element that ") +
-   ::testing::DescribeMatcher(m, negation))
-  .str()) {
-  return ExplainMatchResult(m, arg.Lattice, result_listener);
-}
-
-void TransferSetTrue(const DeclRefExpr *,
- const ast_matchers::MatchFinder::MatchResult &,
- TransferState &State) {
-  State.Lattice = BooleanLattice(true);
-}
-
-void TransferSetFalse(const Stmt *,
-  const ast_matchers::MatchFinder::MatchResult &,
-  TransferState &State) {
-  State.Lattice = BooleanLattice(false);
-}
-
-class TestAnalysis : public DataflowAnalysis {
-  MatchSwitch> TransferSwitch;
-
-public:
-  explicit TestAnalysis(ASTContext &Context)
-  : DataflowAnalysis(Context) {
-using namespace ast_matchers;
-TransferSwitch =
-MatchSwitchBuilder>()
-.CaseOf(declRefExpr(to(varDecl(hasName("X",
- TransferSetTrue)
-.CaseOf(callExpr(callee(functionDecl(hasName("Foo",
-  TransferSetFalse)
-.Build();
-  }
-
-  static BooleanLattice initialElement() { return BooleanLattice::bottom(); }
-
-  void transfer(const Stmt *S, BooleanLattice &L, Environment &Env) {
-TransferState State(L, Env);
-TransferSwitch(*S, getASTContext(), State);
-  }
-};
-
-template 
-void RunDataflow(llvm::StringRef Code, Matcher Expectations) {
-  ASSERT_THAT_ERROR(
-  test::checkDataflow(
-  Code, "fun",
-  [](ASTContext &C, Environment &) { return TestAnalysis(C); },
-  [&Expectations](
-  llvm::ArrayRef>>
-  Results,
-  ASTContext &) { EXPECT_THAT(Results, Expectations); },
-  {"-fsyntax-only", "-std=c++17"}),
-  llvm::Succeeded());
-}
-
-TEST(MatchSwitchTest, JustX) {
-  std::string Code = R"(
-void fun() {
-  int X = 1;
-  (void)X;
-  // [[p]]
-}
-  )";
-  RunDataflow(Code,
-  UnorderedElementsAre(Pair("p", Holds(BooleanLattice(true);
-}
-
-TEST(MatchSwitchTest, JustFoo) {
-  std::string Code = R"(
-void Foo();
-void fun() {
-  Foo();
-  // [[p]]
-}
-  )";
-  RunDataflow(Code,
-  Unor

[PATCH] D133930: [clang][dataflow] Replace `transfer(const Stmt *, ...)` with `transfer(const CFGElement *, ...)` in `Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel`.

2022-09-15 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 460366.
wyt marked an inline comment as done.
wyt added a comment.

Address comment.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D133930/new/

https://reviews.llvm.org/D133930

Files:
  
clang/include/clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h
  clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
  clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -14,10 +14,8 @@
 #include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Tooling/Tooling.h"
-#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Error.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -1248,13 +1246,9 @@
  Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
 ASTContext &Ctx, const CFGElement &Elt,
 const TypeErasedDataflowAnalysisState &State) mutable {
-  auto Stmt = Elt.getAs();
-  if (!Stmt) {
-return;
-  }
-  auto StmtDiagnostics =
-  Diagnoser.diagnose(Ctx, Stmt->getStmt(), State.Env);
-  llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
+  auto EltDiagnostics =
+  Diagnoser.diagnose(Ctx, &Elt, State.Env);
+  llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));
 })
 .withASTBuildArgs(
 {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"})
Index: clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
===
--- clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -18,8 +18,9 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/Stmt.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
-#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
 #include "clang/Analysis/FlowSensitive/NoopLattice.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "clang/Basic/SourceLocation.h"
@@ -559,41 +560,42 @@
   // lot of duplicated work (e.g. string comparisons), consider providing APIs
   // that avoid it through memoization.
   auto IgnorableOptional = ignorableOptional(Options);
-  return MatchSwitchBuilder()
+  return CFGMatchSwitchBuilder()
   // Attach a symbolic "has_value" state to optional values that we see for
   // the first time.
-  .CaseOf(
+  .CaseOfCFGStmt(
   expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
   initializeOptionalReference)
 
   // make_optional
-  .CaseOf(isMakeOptionalCall(), transferMakeOptionalCall)
+  .CaseOfCFGStmt(isMakeOptionalCall(), transferMakeOptionalCall)
 
   // optional::optional
-  .CaseOf(
+  .CaseOfCFGStmt(
   isOptionalInPlaceConstructor(),
   [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
  LatticeTransferState &State) {
 assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true));
   })
-  .CaseOf(
+  .CaseOfCFGStmt(
   isOptionalNulloptConstructor(),
   [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
  LatticeTransferState &State) {
 assignOptionalValue(*E, State,
 State.Env.getBoolLiteralValue(false));
   })
-  .CaseOf(isOptionalValueOrConversionConstructor(),
-transferValueOrConversionConstructor)
+  .CaseOfCFGStmt(isOptionalValueOrConversionConstructor(),
+   transferValueOrConversionConstructor)
 
   // optional::operator=
-  .CaseOf(isOptionalValueOrConversionAssignment(),
-   transferValueOrConversionAssignment)
-  .CaseOf(isOptionalNulloptAssignment(),
-   transferNulloptAssignment)
+  .CaseOfCFGStmt(
+  isOptionalValueOrConversionAssignment(),
+  transferValueOrConversionAssignment)
+  .CaseOfCFGStmt(isOptionalNulloptAssignment(),
+  transferNulloptAssignment)
 
   // option

[PATCH] D133933: [clang][dataflow] Modify `transfer` in `DataflowModel` to take `CFGElement` as input instead of `Stmt`.

2022-09-15 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 460368.
wyt added a comment.

Address comment.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D133933/new/

https://reviews.llvm.org/D133933

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
  clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
  clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
@@ -120,9 +120,7 @@
   static NoopLattice initialElement() { return NoopLattice(); }
 
   void transfer(const CFGElement *E, NoopLattice &, Environment &Env) {
-if (auto S = E->getAs()) {
-  M.transfer(S->getStmt(), Env);
-}
+M.transfer(E, Env);
   }
 
 private:
Index: clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
===
--- clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
+++ clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
@@ -50,7 +50,11 @@
   return CheckDecls.contains(&D);
 }
 
-bool ChromiumCheckModel::transfer(const Stmt *Stmt, Environment &Env) {
+bool ChromiumCheckModel::transfer(const CFGElement *Element, Environment &Env) 
{
+  auto CS = Element->getAs();
+  if (!CS)
+return false;
+  auto Stmt = CS->getStmt();
   if (const auto *Call = dyn_cast(Stmt)) {
 if (const auto *M = dyn_cast(Call->getDirectCallee())) {
   if (isCheckLikeMethod(CheckDecls, *M)) {
Index: clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
===
--- clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
+++ clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
@@ -26,7 +26,7 @@
 class ChromiumCheckModel : public DataflowModel {
 public:
   ChromiumCheckModel() = default;
-  bool transfer(const Stmt *Stmt, Environment &Env) override;
+  bool transfer(const CFGElement *Element, Environment &Env) override;
 
 private:
   /// Declarations for `::logging::CheckError::.*Check`, lazily initialized.
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
@@ -231,8 +231,8 @@
 /// example, a model may capture a type and its related functions.
 class DataflowModel : public Environment::ValueModel {
 public:
-  /// Return value indicates whether the model processed the `Stmt`.
-  virtual bool transfer(const Stmt *Stmt, Environment &Env) = 0;
+  /// Return value indicates whether the model processed the `Element`.
+  virtual bool transfer(const CFGElement *Element, Environment &Env) = 0;
 };
 
 } // namespace dataflow


Index: clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp
@@ -120,9 +120,7 @@
   static NoopLattice initialElement() { return NoopLattice(); }
 
   void transfer(const CFGElement *E, NoopLattice &, Environment &Env) {
-if (auto S = E->getAs()) {
-  M.transfer(S->getStmt(), Env);
-}
+M.transfer(E, Env);
   }
 
 private:
Index: clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
===
--- clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
+++ clang/lib/Analysis/FlowSensitive/Models/ChromiumCheckModel.cpp
@@ -50,7 +50,11 @@
   return CheckDecls.contains(&D);
 }
 
-bool ChromiumCheckModel::transfer(const Stmt *Stmt, Environment &Env) {
+bool ChromiumCheckModel::transfer(const CFGElement *Element, Environment &Env) {
+  auto CS = Element->getAs();
+  if (!CS)
+return false;
+  auto Stmt = CS->getStmt();
   if (const auto *Call = dyn_cast(Stmt)) {
 if (const auto *M = dyn_cast(Call->getDirectCallee())) {
   if (isCheckLikeMethod(CheckDecls, *M)) {
Index: clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
===
--- clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
+++ clang/include/clang/Analysis/FlowSensitive/Models/ChromiumCheckModel.h
@@ -26,7 +26,7 @@
 class ChromiumCheckModel : public DataflowModel {
 public:
   ChromiumCheckModel() = default;
-  bool transfer(const Stmt *Stmt, Environment &Env) override;
+  bool transfer(const CFGElement *Element, Environment &Env) overr

[PATCH] D133935: [clang][dataflow] Refactor `clang/Analysis/FlowSensitive/MatchSwitchTest.cpp`.

2022-09-15 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 460371.
wyt added a comment.

Address comment.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D133935/new/

https://reviews.llvm.org/D133935

Files:
  clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
@@ -7,26 +7,15 @@
 //===--===//
 
 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
-#include "TestingSupport.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/Stmt.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
-#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
-#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
-#include "clang/Analysis/FlowSensitive/MapLattice.h"
 #include "clang/Tooling/Tooling.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Testing/Support/Annotations.h"
-#include "llvm/Testing/Support/Error.h"
-#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 #include 
 #include 
@@ -36,169 +25,114 @@
 
 using namespace clang;
 using namespace dataflow;
+using namespace ast_matchers;
 
 namespace {
-using ::testing::Pair;
-using ::testing::UnorderedElementsAre;
 
-class BooleanLattice {
-public:
-  BooleanLattice() : Value(false) {}
-  explicit BooleanLattice(bool B) : Value(B) {}
-
-  static BooleanLattice bottom() { return BooleanLattice(false); }
-
-  static BooleanLattice top() { return BooleanLattice(true); }
-
-  LatticeJoinEffect join(BooleanLattice Other) {
-auto Prev = Value;
-Value = Value || Other.Value;
-return Prev == Value ? LatticeJoinEffect::Unchanged
- : LatticeJoinEffect::Changed;
-  }
-
-  friend bool operator==(BooleanLattice LHS, BooleanLattice RHS) {
-return LHS.Value == RHS.Value;
-  }
-
-  friend std::ostream &operator<<(std::ostream &Os, const BooleanLattice &B) {
-Os << B.Value;
-return Os;
-  }
-
-  bool value() const { return Value; }
-
-private:
-  bool Value;
-};
-} // namespace
-
-MATCHER_P(Holds, m,
-  ((negation ? "doesn't hold" : "holds") +
-   llvm::StringRef(" a lattice element that ") +
-   ::testing::DescribeMatcher(m, negation))
-  .str()) {
-  return ExplainMatchResult(m, arg.Lattice, result_listener);
-}
-
-void TransferSetTrue(const DeclRefExpr *,
- const ast_matchers::MatchFinder::MatchResult &,
- TransferState &State) {
-  State.Lattice = BooleanLattice(true);
-}
-
-void TransferSetFalse(const Stmt *,
-  const ast_matchers::MatchFinder::MatchResult &,
-  TransferState &State) {
-  State.Lattice = BooleanLattice(false);
-}
-
-class TestAnalysis : public DataflowAnalysis {
-  MatchSwitch> TransferSwitch;
-
-public:
-  explicit TestAnalysis(ASTContext &Context)
-  : DataflowAnalysis(Context) {
-using namespace ast_matchers;
-TransferSwitch =
-MatchSwitchBuilder>()
-.CaseOf(declRefExpr(to(varDecl(hasName("X",
- TransferSetTrue)
-.CaseOf(callExpr(callee(functionDecl(hasName("Foo",
-  TransferSetFalse)
-.Build();
-  }
-
-  static BooleanLattice initialElement() { return BooleanLattice::bottom(); }
-
-  void transfer(const Stmt *S, BooleanLattice &L, Environment &Env) {
-TransferState State(L, Env);
-TransferSwitch(*S, getASTContext(), State);
-  }
-};
-
-template 
-void RunDataflow(llvm::StringRef Code, Matcher Expectations) {
-  ASSERT_THAT_ERROR(
-  test::checkDataflow(
-  Code, "fun",
-  [](ASTContext &C, Environment &) { return TestAnalysis(C); },
-  [&Expectations](
-  llvm::ArrayRef>>
-  Results,
-  ASTContext &) { EXPECT_THAT(Results, Expectations); },
-  {"-fsyntax-only", "-std=c++17"}),
-  llvm::Succeeded());
-}
-
-TEST(MatchSwitchTest, JustX) {
-  std::string Code = R"(
-void fun() {
-  int X = 1;
-  (void)X;
-  // [[p]]
-}
-  )";
-  RunDataflow(Code,
-  UnorderedElementsAre(Pair("p", Holds(BooleanLattice(true);
-}
-
-TEST(MatchSwitchTest, JustFoo) {
-  std::string Code = R"(
-void Foo();
-void fun() {
-  Foo();
-  // [[p]]
-}
-  )";
-  RunDataflow(Code,
-  UnorderedElementsAre(Pair("p", Holds(BooleanLattice(false);
-}
-
-TEST(MatchSwitchTest, XThenFoo) {
+T

[PATCH] D128363: [clang][dataflow] Implement functionality for flow condition variable substitution.

2022-06-27 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 440119.
wyt marked an inline comment as done.
wyt added a comment.

Address comments - add example to `buildAndSubstituteFlowCondition` doc 
comment, add tests for atomic and negated flow condition.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D128363/new/

https://reviews.llvm.org/D128363

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -276,4 +276,172 @@
   Context.getOrCreateConjunction(X, Context.getOrCreateConjunction(Y, Z;
 }
 
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsAtomicFC) {
+  auto &X = Context.createAtomicBoolValue();
+  auto &True = Context.getBoolLiteralValue(true);
+  auto &False = Context.getBoolLiteralValue(false);
+
+  // FC = X
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, X);
+
+  // If X is true in FC, FC = X must be true
+  auto &FCWithXTrue =
+  Context.buildAndSubstituteFlowCondition(FC, {{&X, &True}});
+  EXPECT_TRUE(Context.equivalentBoolValues(FCWithXTrue, True));
+
+  // If X is false in FC, FC = X must be false
+  auto &FC1WithXFalse =
+  Context.buildAndSubstituteFlowCondition(FC, {{&X, &False}});
+  EXPECT_TRUE(Context.equivalentBoolValues(FC1WithXFalse, False));
+}
+
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsNegatedFC) {
+  auto &X = Context.createAtomicBoolValue();
+  auto &True = Context.getBoolLiteralValue(true);
+  auto &False = Context.getBoolLiteralValue(false);
+
+  // FC = !X
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, Context.getOrCreateNegation(X));
+
+  // If X is true in FC, FC = !X must be false
+  auto &FCWithXTrue =
+  Context.buildAndSubstituteFlowCondition(FC, {{&X, &True}});
+  EXPECT_TRUE(Context.equivalentBoolValues(FCWithXTrue, False));
+
+  // If X is false in FC, FC = !X must be true
+  auto &FC1WithXFalse =
+  Context.buildAndSubstituteFlowCondition(FC, {{&X, &False}});
+  EXPECT_TRUE(Context.equivalentBoolValues(FC1WithXFalse, True));
+}
+
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsDisjunctiveFC) {
+  auto &X = Context.createAtomicBoolValue();
+  auto &Y = Context.createAtomicBoolValue();
+  auto &True = Context.getBoolLiteralValue(true);
+  auto &False = Context.getBoolLiteralValue(false);
+
+  // FC = X || Y
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, Context.getOrCreateDisjunction(X, Y));
+
+  // If X is true in FC, FC = X || Y must be true
+  auto &FCWithXTrue =
+  Context.buildAndSubstituteFlowCondition(FC, {{&X, &True}});
+  EXPECT_TRUE(Context.equivalentBoolValues(FCWithXTrue, True));
+
+  // If X is false in FC, FC = X || Y is equivalent to evaluating Y
+  auto &FC1WithXFalse =
+  Context.buildAndSubstituteFlowCondition(FC, {{&X, &False}});
+  EXPECT_TRUE(Context.equivalentBoolValues(FC1WithXFalse, Y));
+}
+
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsConjunctiveFC) {
+  auto &X = Context.createAtomicBoolValue();
+  auto &Y = Context.createAtomicBoolValue();
+  auto &True = Context.getBoolLiteralValue(true);
+  auto &False = Context.getBoolLiteralValue(false);
+
+  // FC = X && Y
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, Context.getOrCreateConjunction(X, Y));
+
+  // If X is true in FC, FC = X && Y is equivalent to evaluating Y
+  auto &FCWithXTrue =
+  Context.buildAndSubstituteFlowCondition(FC, {{&X, &True}});
+  EXPECT_TRUE(Context.equivalentBoolValues(FCWithXTrue, Y));
+
+  // If X is false in FC, FC = X && Y must be false
+  auto &FCWithXFalse =
+  Context.buildAndSubstituteFlowCondition(FC, {{&X, &False}});
+  EXPECT_TRUE(Context.equivalentBoolValues(FCWithXFalse, False));
+}
+
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsForkedFC) {
+  auto &X = Context.createAtomicBoolValue();
+  auto &Y = Context.createAtomicBoolValue();
+  auto &Z = Context.createAtomicBoolValue();
+  auto &True = Context.getBoolLiteralValue(true);
+  auto &False = Context.getBoolLiteralValue(false);
+
+  // FC = X && Y
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, Context.getOrCreateConjunction(X, Y));
+  // ForkedFC = FC && Z = X && Y && Z
+  auto &ForkedFC = Context.forkFlowCondition(FC);
+  Context.addFlowConditionConstraint(ForkedFC, Z);
+
+  // If any of X,Y,Z is true in ForkedFC, ForkedFC = X && Y && Z is equivalent
+  // to evaluating the conjunction of the remaining 

[PATCH] D128359: [clang][dataflow] Move logic for `createStorageLocation` from `DataflowEnvironment` to `DataflowAnalysisContext`.

2022-06-27 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 440123.
wyt marked an inline comment as done.
wyt added a comment.

Fix comment.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D128359/new/

https://reviews.llvm.org/D128359

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -152,29 +152,6 @@
   }
 }
 
-// FIXME: Does not precisely handle non-virtual diamond inheritance. A single
-// field decl will be modeled for all instances of the inherited field.
-static void
-getFieldsFromClassHierarchy(QualType Type,
-llvm::DenseSet &Fields) {
-  if (Type->isIncompleteType() || Type->isDependentType() ||
-  !Type->isRecordType())
-return;
-
-  for (const FieldDecl *Field : Type->getAsRecordDecl()->fields())
-Fields.insert(Field);
-  if (auto *CXXRecord = Type->getAsCXXRecordDecl())
-for (const CXXBaseSpecifier &Base : CXXRecord->bases())
-  getFieldsFromClassHierarchy(Base.getType(), Fields);
-}
-
-/// Gets the set of all fields in the type.
-static llvm::DenseSet getObjectFields(QualType Type) {
-  llvm::DenseSet Fields;
-  getFieldsFromClassHierarchy(Type, Fields);
-  return Fields;
-}
-
 Environment::Environment(DataflowAnalysisContext &DACtx)
 : DACtx(&DACtx), FlowConditionToken(&DACtx.makeFlowConditionToken()) {}
 
@@ -310,39 +287,21 @@
 }
 
 StorageLocation &Environment::createStorageLocation(QualType Type) {
-  assert(!Type.isNull());
-  if (Type->isStructureOrClassType() || Type->isUnionType()) {
-// FIXME: Explore options to avoid eager initialization of fields as some of
-// them might not be needed for a particular analysis.
-llvm::DenseMap FieldLocs;
-for (const FieldDecl *Field : getObjectFields(Type))
-  FieldLocs.insert({Field, &createStorageLocation(Field->getType())});
-return takeOwnership(
-std::make_unique(Type, std::move(FieldLocs)));
-  }
-  return takeOwnership(std::make_unique(Type));
+  return DACtx->getStableStorageLocation(Type);
 }
 
 StorageLocation &Environment::createStorageLocation(const VarDecl &D) {
   // Evaluated declarations are always assigned the same storage locations to
   // ensure that the environment stabilizes across loop iterations. Storage
   // locations for evaluated declarations are stored in the analysis context.
-  if (auto *Loc = DACtx->getStorageLocation(D))
-return *Loc;
-  auto &Loc = createStorageLocation(D.getType());
-  DACtx->setStorageLocation(D, Loc);
-  return Loc;
+  return DACtx->getStableStorageLocation(D);
 }
 
 StorageLocation &Environment::createStorageLocation(const Expr &E) {
   // Evaluated expressions are always assigned the same storage locations to
   // ensure that the environment stabilizes across loop iterations. Storage
   // locations for evaluated expressions are stored in the analysis context.
-  if (auto *Loc = DACtx->getStorageLocation(E))
-return *Loc;
-  auto &Loc = createStorageLocation(E.getType());
-  DACtx->setStorageLocation(E, Loc);
-  return Loc;
+  return DACtx->getStableStorageLocation(E);
 }
 
 void Environment::setStorageLocation(const ValueDecl &D, StorageLocation &Loc) {
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -22,6 +22,39 @@
 namespace clang {
 namespace dataflow {
 
+StorageLocation &
+DataflowAnalysisContext::getStableStorageLocation(QualType Type) {
+  assert(!Type.isNull());
+  if (Type->isStructureOrClassType() || Type->isUnionType()) {
+// FIXME: Explore options to avoid eager initialization of fields as some of
+// them might not be needed for a particular analysis.
+llvm::DenseMap FieldLocs;
+for (const FieldDecl *Field : getObjectFields(Type))
+  FieldLocs.insert({Field, &getStableStorageLocation(Field->getType())});
+return takeOwnership(
+std::make_unique(Type, std::move(FieldLocs)));
+  }
+  return takeOwnership(std::make_unique(Type));
+}
+
+StorageLocation &
+DataflowAnalysisContext::getStableStorageLocation(const VarDecl &D) {
+  if (auto *Loc = getStorageLocation(D))
+return *Loc;
+  auto &Loc = getStableStorageLocation(D.getType());
+  setStorageLocation(D, Loc);
+  return Loc;
+}
+
+StorageLocation &
+DataflowAnalysisContext::getStableStorageLocation(const Expr &E) {
+  if (auto *Loc = getStorageLocation(E))
+return *Loc;
+  auto &Loc = getStableStorageLocation(E.getType());
+  setStorageLocation(E, Loc);
+  return 

[PATCH] D128056: [clang][dataflow] Singleton pointer values for null pointers.

2022-06-27 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 440162.
wyt marked 2 inline comments as done.
wyt added a comment.

Address comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D128056/new/

https://reviews.llvm.org/D128056

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.cpp
  clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -2214,6 +2214,93 @@
   });
 }
 
+TEST_F(TransferTest, NullToPointerCast) {
+  std::string Code = R"(
+struct Baz {};
+void target() {
+  int *FooX = nullptr;
+  int *FooY = nullptr;
+  bool **Bar = nullptr;
+  Baz *Baz = nullptr;
+  // [[p]]
+}
+  )";
+  runDataflow(Code,
+  [](llvm::ArrayRef<
+ std::pair>>
+ Results,
+ ASTContext &ASTCtx) {
+ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+const Environment &Env = Results[0].second.Env;
+
+const ValueDecl *FooXDecl = findValueDecl(ASTCtx, "FooX");
+ASSERT_THAT(FooXDecl, NotNull());
+
+const ValueDecl *FooYDecl = findValueDecl(ASTCtx, "FooY");
+ASSERT_THAT(FooYDecl, NotNull());
+
+const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ASSERT_THAT(BarDecl, NotNull());
+
+const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
+ASSERT_THAT(BazDecl, NotNull());
+
+const auto *FooXVal =
+cast(Env.getValue(*FooXDecl, SkipPast::None));
+const auto *FooYVal =
+cast(Env.getValue(*FooYDecl, SkipPast::None));
+const auto *BarVal =
+cast(Env.getValue(*BarDecl, SkipPast::None));
+const auto *BazVal =
+cast(Env.getValue(*BazDecl, SkipPast::None));
+
+EXPECT_EQ(FooXVal, FooYVal);
+EXPECT_NE(FooXVal, BarVal);
+EXPECT_NE(FooXVal, BazVal);
+EXPECT_NE(BarVal, BazVal);
+
+const StorageLocation &FooPointeeLoc = FooXVal->getPointeeLoc();
+EXPECT_TRUE(isa(FooPointeeLoc));
+EXPECT_THAT(Env.getValue(FooPointeeLoc), IsNull());
+
+const StorageLocation &BarPointeeLoc = BarVal->getPointeeLoc();
+EXPECT_TRUE(isa(BarPointeeLoc));
+EXPECT_THAT(Env.getValue(BarPointeeLoc), IsNull());
+
+const StorageLocation &BazPointeeLoc = BazVal->getPointeeLoc();
+EXPECT_TRUE(isa(BazPointeeLoc));
+EXPECT_THAT(Env.getValue(BazPointeeLoc), IsNull());
+  });
+}
+
+TEST_F(TransferTest, NullToMemberPointerCast) {
+  std::string Code = R"(
+struct Foo {};
+void target(Foo *Foo) {
+  int Foo::*MemberPointer = nullptr;
+  // [[p]]
+}
+  )";
+  runDataflow(
+  Code, [](llvm::ArrayRef<
+   std::pair>>
+   Results,
+   ASTContext &ASTCtx) {
+ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+const Environment &Env = Results[0].second.Env;
+
+const ValueDecl *MemberPointerDecl =
+findValueDecl(ASTCtx, "MemberPointer");
+ASSERT_THAT(MemberPointerDecl, NotNull());
+
+const auto *MemberPointerVal = cast(
+Env.getValue(*MemberPointerDecl, SkipPast::None));
+
+const StorageLocation &MemberLoc = MemberPointerVal->getPointeeLoc();
+EXPECT_THAT(Env.getValue(MemberLoc), IsNull());
+  });
+}
+
 TEST_F(TransferTest, AddrOfValue) {
   std::string Code = R"(
 void target() {
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -251,6 +251,16 @@
   Env.setStorageLocation(*S, *SubExprLoc);
   break;
 }
+case CK_NullToPointer:
+case CK_NullToMemberPointer: {
+  auto &Loc = Env.createStorageLocation(S->getType());
+  Env.setStorageLocation(*S, Loc);
+
+  auto &NullPointerVal =
+  Env.getOrCreateNullPointerValue(S->getType()->getPointeeType());
+  Env.setValue(Loc, NullPointerVal);
+  break;
+}
 default:
   break;
 }
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===

[PATCH] D128659: [clang][dataflow] Add `buildAndSubstituteFlowCondition` to `DataflowEnvironment`

2022-06-27 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, tschuett, xazax.hun.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Depends On D128658 


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D128659

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h


Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -304,6 +304,17 @@
   /// Returns the token that identifies the flow condition of the environment.
   AtomicBoolValue &getFlowConditionToken() const { return *FlowConditionToken; 
}
 
+  /// Builds and returns the logical formula defining the flow condition
+  /// identified by `Token`. If a value in the formula is present as a key in
+  /// `Substitutions`, and it is not a True/False boolean literal, it will be
+  /// substituted with the value it maps to.
+  BoolValue &buildAndSubstituteFlowCondition(
+  AtomicBoolValue &Token,
+  llvm::DenseMap Substitutions) {
+return DACtx->buildAndSubstituteFlowCondition(Token,
+  std::move(Substitutions));
+  }
+
   /// Adds `Val` to the set of clauses that constitute the flow condition.
   void addToFlowCondition(BoolValue &Val);
 


Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -304,6 +304,17 @@
   /// Returns the token that identifies the flow condition of the environment.
   AtomicBoolValue &getFlowConditionToken() const { return *FlowConditionToken; }
 
+  /// Builds and returns the logical formula defining the flow condition
+  /// identified by `Token`. If a value in the formula is present as a key in
+  /// `Substitutions`, and it is not a True/False boolean literal, it will be
+  /// substituted with the value it maps to.
+  BoolValue &buildAndSubstituteFlowCondition(
+  AtomicBoolValue &Token,
+  llvm::DenseMap Substitutions) {
+return DACtx->buildAndSubstituteFlowCondition(Token,
+  std::move(Substitutions));
+  }
+
   /// Adds `Val` to the set of clauses that constitute the flow condition.
   void addToFlowCondition(BoolValue &Val);
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D128658: [clang][dataflow] Ensure atomic boolean values representing true and false are not replaced in `buildAndSubstituteFlowCondition`

2022-06-27 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, tschuett, xazax.hun.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D128658

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -276,6 +276,38 @@
   Context.getOrCreateConjunction(X, Context.getOrCreateConjunction(Y, 
Z;
 }
 
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsTrueUnchanged) {
+  auto &True = Context.getBoolLiteralValue(true);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = True
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, True);
+
+  // `True` should never be substituted
+  auto &FCNoSubstitution = Context.buildAndSubstituteFlowCondition(FC, {{}});
+  auto &FCTrySubstituteTrue =
+  Context.buildAndSubstituteFlowCondition(FC, {{&True, &Other}});
+  EXPECT_TRUE(
+  Context.equivalentBoolValues(FCNoSubstitution, FCTrySubstituteTrue));
+}
+
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsFalseUnchanged) {
+  auto &False = Context.getBoolLiteralValue(false);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = False
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, False);
+
+  // `False` should never be substituted
+  auto &FCNoSubstitution = Context.buildAndSubstituteFlowCondition(FC, {{}});
+  auto &FCTrySubstituteFalse =
+  Context.buildAndSubstituteFlowCondition(FC, {{&False, &Other}});
+  EXPECT_TRUE(
+  Context.equivalentBoolValues(FCNoSubstitution, FCTrySubstituteFalse));
+}
+
 TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsAtomicFC) {
   auto &X = Context.createAtomicBoolValue();
   auto &True = Context.getBoolLiteralValue(true);
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -172,10 +172,20 @@
 BoolValue &DataflowAnalysisContext::substituteBoolValue(
 BoolValue &Val,
 llvm::DenseMap &SubstitutionsCache) {
+  if (&Val == &getBoolLiteralValue(true) ||
+  &Val == &getBoolLiteralValue(false)) {
+// Don't substitute boolean values representing true / false.
+return Val;
+  }
+
   auto IT = SubstitutionsCache.find(&Val);
   if (IT != SubstitutionsCache.end()) {
+// Return memoized result of substituting this boolean value.
 return *IT->second;
   }
+
+  // Handle substitution on the boolean value (and its subvalues), saving the
+  // result into `SubstitutionsCache`.
   BoolValue *Result;
   switch (Val.getKind()) {
   case Value::Kind::AtomicBool: {
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -205,13 +205,14 @@
   //
   /// Builds and returns the logical formula defining the flow condition
   /// identified by `Token`. If a value in the formula is present as a key in
-  /// `Substitutions`, it will be substituted with the value it maps to.
+  /// `Substitutions`, and it is not a True/False boolean literal, it will be
+  /// substituted with the value it maps to.
   /// As an example, say we have flow condition tokens FC1, FC2, FC3 and
   /// FlowConditionConstraints: { FC1: C1,
-  /// FC2: C2,
+  /// FC2: C2 ^ True,
   /// FC3: (FC1 v FC2) ^ C3 }
-  /// buildAndSubstituteFlowCondition(FC3, {{C1 -> C1'}}) will return a value
-  /// corresponding to (C1' v C2) ^ C3.
+  /// buildAndSubstituteFlowCondition(FC3, {{C1 -> C1', True -> _ }}) will
+  /// return a value corresponding to (C1' v (C2 ^ True)) ^ C3.
   BoolValue &buildAndSubstituteFlowCondition(
   AtomicBoolValue &Token,
   llvm::DenseMap Substitutions);


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -276,6 +276,38 @@
   Context.getOrCreateConjunction(X, Context.getO

[PATCH] D128658: [clang][dataflow] Ensure atomic boolean values representing true and false are not replaced in `buildAndSubstituteFlowCondition`

2022-06-27 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 440304.
wyt added a comment.

Assert that user does not try to substitute true/false booleans.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D128658/new/

https://reviews.llvm.org/D128658

Files:
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -16,6 +16,7 @@
 
 using namespace clang;
 using namespace dataflow;
+using testing::_;
 
 class DataflowAnalysisContextTest : public ::testing::Test {
 protected:
@@ -276,6 +277,32 @@
   Context.getOrCreateConjunction(X, Context.getOrCreateConjunction(Y, 
Z;
 }
 
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsTrueUnchanged) {
+  auto &True = Context.getBoolLiteralValue(true);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = True
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, True);
+
+  // `True` should never be substituted
+  EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&True, &Other}}),
+   _);
+}
+
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsFalseUnchanged) {
+  auto &False = Context.getBoolLiteralValue(false);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = False
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, False);
+
+  // `False` should never be substituted
+  EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&False, &Other}}),
+   _);
+}
+
 TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsAtomicFC) {
   auto &X = Context.createAtomicBoolValue();
   auto &True = Context.getBoolLiteralValue(true);
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -174,8 +174,12 @@
 llvm::DenseMap &SubstitutionsCache) {
   auto IT = SubstitutionsCache.find(&Val);
   if (IT != SubstitutionsCache.end()) {
+// Return memoized result of substituting this boolean value.
 return *IT->second;
   }
+
+  // Handle substitution on the boolean value (and its subvalues), saving the
+  // result into `SubstitutionsCache`.
   BoolValue *Result;
   switch (Val.getKind()) {
   case Value::Kind::AtomicBool: {
@@ -216,6 +220,10 @@
 BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowCondition(
 AtomicBoolValue &Token,
 llvm::DenseMap Substitutions) {
+  // Do not substitute true/false boolean literals.
+  assert(
+  Substitutions.find(&getBoolLiteralValue(true)) == Substitutions.end() &&
+  Substitutions.find(&getBoolLiteralValue(false)) == Substitutions.end());
   llvm::DenseMap SubstitutionsCache(
   Substitutions.begin(), Substitutions.end());
   return buildAndSubstituteFlowConditionWithCache(Token, SubstitutionsCache);


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -16,6 +16,7 @@
 
 using namespace clang;
 using namespace dataflow;
+using testing::_;
 
 class DataflowAnalysisContextTest : public ::testing::Test {
 protected:
@@ -276,6 +277,32 @@
   Context.getOrCreateConjunction(X, Context.getOrCreateConjunction(Y, Z;
 }
 
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsTrueUnchanged) {
+  auto &True = Context.getBoolLiteralValue(true);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = True
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, True);
+
+  // `True` should never be substituted
+  EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&True, &Other}}),
+   _);
+}
+
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsFalseUnchanged) {
+  auto &False = Context.getBoolLiteralValue(false);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = False
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, False);
+
+  // `False` should never be substituted
+  EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&False, &Other}}),
+   _);
+}
+
 TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsAtomicFC) {
   auto &X = Context.createAtomicBoolValue();
   auto &True = Context.getBoolLiteralValue(true);
Index: clang/lib/Analysis/FlowSensitive/DataflowAn

[PATCH] D128659: [clang][dataflow] Add `buildAndSubstituteFlowCondition` to `DataflowEnvironment`

2022-06-27 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 440305.
wyt added a comment.

Fix comment.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D128659/new/

https://reviews.llvm.org/D128659

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -16,7 +16,6 @@
 
 using namespace clang;
 using namespace dataflow;
-using testing::_;
 
 class DataflowAnalysisContextTest : public ::testing::Test {
 protected:
@@ -287,7 +286,7 @@
 
   // `True` should never be substituted
   EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&True, &Other}}),
-   _);
+   testing::_);
 }
 
 TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsFalseUnchanged) {
@@ -300,7 +299,7 @@
 
   // `False` should never be substituted
   EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&False, &Other}}),
-   _);
+   testing::_);
 }
 
 TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsAtomicFC) {
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -220,7 +220,7 @@
 BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowCondition(
 AtomicBoolValue &Token,
 llvm::DenseMap Substitutions) {
-  // Do not substitute true/false boolean literals.
+  // Do not try and substitute true/false boolean literals
   assert(
   Substitutions.find(&getBoolLiteralValue(true)) == Substitutions.end() &&
   Substitutions.find(&getBoolLiteralValue(false)) == Substitutions.end());
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -304,6 +304,16 @@
   /// Returns the token that identifies the flow condition of the environment.
   AtomicBoolValue &getFlowConditionToken() const { return *FlowConditionToken; 
}
 
+  /// Builds and returns the logical formula defining the flow condition
+  /// identified by `Token`. If a value in the formula is present as a key in
+  /// `Substitutions`, it will be substituted with the value it maps to.
+  BoolValue &buildAndSubstituteFlowCondition(
+  AtomicBoolValue &Token,
+  llvm::DenseMap Substitutions) {
+return DACtx->buildAndSubstituteFlowCondition(Token,
+  std::move(Substitutions));
+  }
+
   /// Adds `Val` to the set of clauses that constitute the flow condition.
   void addToFlowCondition(BoolValue &Val);
 


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -16,7 +16,6 @@
 
 using namespace clang;
 using namespace dataflow;
-using testing::_;
 
 class DataflowAnalysisContextTest : public ::testing::Test {
 protected:
@@ -287,7 +286,7 @@
 
   // `True` should never be substituted
   EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&True, &Other}}),
-   _);
+   testing::_);
 }
 
 TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsFalseUnchanged) {
@@ -300,7 +299,7 @@
 
   // `False` should never be substituted
   EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&False, &Other}}),
-   _);
+   testing::_);
 }
 
 TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsAtomicFC) {
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -220,7 +220,7 @@
 BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowCondition(
 AtomicBoolValue &Token,
 llvm::DenseMap Substitutions) {
-  // Do not substitute true/false boolean literals.
+  // Do not try and substitute true/false boolean literals
   assert(
   Substitutions.find(&getBoolLiteralValue(true)) == Substitutions.end() &&
   Substitutions.find(&getBoolLiteralValue(false)) == Substitutions.end());
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnv

[PATCH] D128658: [clang][dataflow] Do not allow substitution of true/false boolean literals in `buildAndSubstituteFlowCondition`

2022-06-27 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 440333.
wyt added a comment.

Add macro for only testing asserts in debug mode.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D128658/new/

https://reviews.llvm.org/D128658

Files:
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -16,6 +16,7 @@
 
 using namespace clang;
 using namespace dataflow;
+using testing::_;
 
 class DataflowAnalysisContextTest : public ::testing::Test {
 protected:
@@ -276,6 +277,34 @@
   Context.getOrCreateConjunction(X, Context.getOrCreateConjunction(Y, 
Z;
 }
 
+#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsTrueUnchanged) {
+  auto &True = Context.getBoolLiteralValue(true);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = True
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, True);
+
+  // `True` should never be substituted
+  EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&True, &Other}}),
+   _);
+}
+
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsFalseUnchanged) {
+  auto &False = Context.getBoolLiteralValue(false);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = False
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, False);
+
+  // `False` should never be substituted
+  EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&False, &Other}}),
+   _);
+}
+#endif
+
 TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsAtomicFC) {
   auto &X = Context.createAtomicBoolValue();
   auto &True = Context.getBoolLiteralValue(true);
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -174,8 +174,12 @@
 llvm::DenseMap &SubstitutionsCache) {
   auto IT = SubstitutionsCache.find(&Val);
   if (IT != SubstitutionsCache.end()) {
+// Return memoized result of substituting this boolean value.
 return *IT->second;
   }
+
+  // Handle substitution on the boolean value (and its subvalues), saving the
+  // result into `SubstitutionsCache`.
   BoolValue *Result;
   switch (Val.getKind()) {
   case Value::Kind::AtomicBool: {
@@ -216,6 +220,10 @@
 BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowCondition(
 AtomicBoolValue &Token,
 llvm::DenseMap Substitutions) {
+  // Do not substitute true/false boolean literals.
+  assert(
+  Substitutions.find(&getBoolLiteralValue(true)) == Substitutions.end() &&
+  Substitutions.find(&getBoolLiteralValue(false)) == Substitutions.end());
   llvm::DenseMap SubstitutionsCache(
   Substitutions.begin(), Substitutions.end());
   return buildAndSubstituteFlowConditionWithCache(Token, SubstitutionsCache);


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -16,6 +16,7 @@
 
 using namespace clang;
 using namespace dataflow;
+using testing::_;
 
 class DataflowAnalysisContextTest : public ::testing::Test {
 protected:
@@ -276,6 +277,34 @@
   Context.getOrCreateConjunction(X, Context.getOrCreateConjunction(Y, Z;
 }
 
+#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsTrueUnchanged) {
+  auto &True = Context.getBoolLiteralValue(true);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = True
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, True);
+
+  // `True` should never be substituted
+  EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&True, &Other}}),
+   _);
+}
+
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsFalseUnchanged) {
+  auto &False = Context.getBoolLiteralValue(false);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = False
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, False);
+
+  // `False` should never be substituted
+  EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&False, &Other}}),
+   _);
+}
+#endif
+
 TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsAtomicFC) {
   auto &X = Context.createAtomicBoolValue();
   auto 

[PATCH] D128659: [clang][dataflow] Add `buildAndSubstituteFlowCondition` to `DataflowEnvironment`

2022-06-27 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 440336.
wyt added a comment.

Propagate change from parent patch.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D128659/new/

https://reviews.llvm.org/D128659

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h


Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -304,6 +304,16 @@
   /// Returns the token that identifies the flow condition of the environment.
   AtomicBoolValue &getFlowConditionToken() const { return *FlowConditionToken; 
}
 
+  /// Builds and returns the logical formula defining the flow condition
+  /// identified by `Token`. If a value in the formula is present as a key in
+  /// `Substitutions`, it will be substituted with the value it maps to.
+  BoolValue &buildAndSubstituteFlowCondition(
+  AtomicBoolValue &Token,
+  llvm::DenseMap Substitutions) {
+return DACtx->buildAndSubstituteFlowCondition(Token,
+  std::move(Substitutions));
+  }
+
   /// Adds `Val` to the set of clauses that constitute the flow condition.
   void addToFlowCondition(BoolValue &Val);
 


Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -304,6 +304,16 @@
   /// Returns the token that identifies the flow condition of the environment.
   AtomicBoolValue &getFlowConditionToken() const { return *FlowConditionToken; }
 
+  /// Builds and returns the logical formula defining the flow condition
+  /// identified by `Token`. If a value in the formula is present as a key in
+  /// `Substitutions`, it will be substituted with the value it maps to.
+  BoolValue &buildAndSubstituteFlowCondition(
+  AtomicBoolValue &Token,
+  llvm::DenseMap Substitutions) {
+return DACtx->buildAndSubstituteFlowCondition(Token,
+  std::move(Substitutions));
+  }
+
   /// Adds `Val` to the set of clauses that constitute the flow condition.
   void addToFlowCondition(BoolValue &Val);
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D128658: [clang][dataflow] Do not allow substitution of true/false boolean literals in `buildAndSubstituteFlowCondition`

2022-06-27 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 440341.
wyt added a comment.

Add assert message.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D128658/new/

https://reviews.llvm.org/D128658

Files:
  clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
  clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -276,6 +276,34 @@
   Context.getOrCreateConjunction(X, Context.getOrCreateConjunction(Y, 
Z;
 }
 
+#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsTrueUnchanged) {
+  auto &True = Context.getBoolLiteralValue(true);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = True
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, True);
+
+  // `True` should never be substituted
+  EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&True, &Other}}),
+   "Do not substitute true/false boolean literals");
+}
+
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsFalseUnchanged) {
+  auto &False = Context.getBoolLiteralValue(false);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = False
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, False);
+
+  // `False` should never be substituted
+  EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&False, &Other}}),
+   "Do not substitute true/false boolean literals");
+}
+#endif
+
 TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsAtomicFC) {
   auto &X = Context.createAtomicBoolValue();
   auto &True = Context.getBoolLiteralValue(true);
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
@@ -174,8 +174,12 @@
 llvm::DenseMap &SubstitutionsCache) {
   auto IT = SubstitutionsCache.find(&Val);
   if (IT != SubstitutionsCache.end()) {
+// Return memoized result of substituting this boolean value.
 return *IT->second;
   }
+
+  // Handle substitution on the boolean value (and its subvalues), saving the
+  // result into `SubstitutionsCache`.
   BoolValue *Result;
   switch (Val.getKind()) {
   case Value::Kind::AtomicBool: {
@@ -216,6 +220,10 @@
 BoolValue &DataflowAnalysisContext::buildAndSubstituteFlowCondition(
 AtomicBoolValue &Token,
 llvm::DenseMap Substitutions) {
+  assert(
+  Substitutions.find(&getBoolLiteralValue(true)) == Substitutions.end() &&
+  Substitutions.find(&getBoolLiteralValue(false)) == Substitutions.end() &&
+  "Do not substitute true/false boolean literals");
   llvm::DenseMap SubstitutionsCache(
   Substitutions.begin(), Substitutions.end());
   return buildAndSubstituteFlowConditionWithCache(Token, SubstitutionsCache);


Index: clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp
@@ -276,6 +276,34 @@
   Context.getOrCreateConjunction(X, Context.getOrCreateConjunction(Y, Z;
 }
 
+#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsTrueUnchanged) {
+  auto &True = Context.getBoolLiteralValue(true);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = True
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, True);
+
+  // `True` should never be substituted
+  EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&True, &Other}}),
+   "Do not substitute true/false boolean literals");
+}
+
+TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsFalseUnchanged) {
+  auto &False = Context.getBoolLiteralValue(false);
+  auto &Other = Context.createAtomicBoolValue();
+
+  // FC = False
+  auto &FC = Context.makeFlowConditionToken();
+  Context.addFlowConditionConstraint(FC, False);
+
+  // `False` should never be substituted
+  EXPECT_DEATH(Context.buildAndSubstituteFlowCondition(FC, {{&False, &Other}}),
+   "Do not substitute true/false boolean literals");
+}
+#endif
+
 TEST_F(DataflowAnalysisContextTest, SubstituteFlowConditionsAtomicFC) {
   auto &X = Context.createAtomicBoolValue();
   auto &True = Context.getBoolLiteralValue(true);
Index: clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
==

[PATCH] D129180: [clang][dataflow] Return a solution from the solver when `Constraints` are `Satisfiable`.

2022-07-06 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, tschuett, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D129180

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/Solver.h
  clang/lib/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -20,6 +20,12 @@
 using namespace clang;
 using namespace dataflow;
 
+using testing::_;
+using testing::AnyOf;
+using testing::Optional;
+using testing::Pair;
+using testing::UnorderedElementsAre;
+
 class SolverTest : public ::testing::Test {
 protected:
   // Checks if the conjunction of `Vals` is satisfiable and returns the
@@ -64,6 +70,17 @@
 return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
   }
 
+  void expectUnsatisfiable(Solver::Result Result) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Unsatisfiable);
+EXPECT_FALSE(Result.getSolution().has_value());
+  }
+
+  template 
+  void expectSatisfiable(Solver::Result Result, Matcher Solution) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Satisfiable);
+EXPECT_THAT(Result.getSolution(), Optional(Solution));
+  }
+
 private:
   std::vector> Vals;
 };
@@ -72,7 +89,9 @@
   auto X = atom();
 
   // X
-  EXPECT_EQ(solve({X}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, NegatedVar) {
@@ -80,7 +99,9 @@
   auto NotX = neg(X);
 
   // !X
-  EXPECT_EQ(solve({NotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotX}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, UnitConflict) {
@@ -88,7 +109,7 @@
   auto NotX = neg(X);
 
   // X ^ !X
-  EXPECT_EQ(solve({X, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({X, NotX}));
 }
 
 TEST_F(SolverTest, DistinctVars) {
@@ -97,7 +118,10 @@
   auto NotY = neg(Y);
 
   // X ^ !Y
-  EXPECT_EQ(solve({X, NotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X, NotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue),
+   Pair(Y, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, DoubleNegation) {
@@ -106,7 +130,7 @@
   auto NotNotX = neg(NotX);
 
   // !!X ^ !X
-  EXPECT_EQ(solve({NotNotX, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotNotX, NotX}));
 }
 
 TEST_F(SolverTest, NegatedDisjunction) {
@@ -116,7 +140,7 @@
   auto NotXOrY = neg(XOrY);
 
   // !(X v Y) ^ (X v Y)
-  EXPECT_EQ(solve({NotXOrY, XOrY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXOrY, XOrY}));
 }
 
 TEST_F(SolverTest, NegatedConjunction) {
@@ -126,7 +150,7 @@
   auto NotXAndY = neg(XAndY);
 
   // !(X ^ Y) ^ (X ^ Y)
-  EXPECT_EQ(solve({NotXAndY, XAndY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXAndY, XAndY}));
 }
 
 TEST_F(SolverTest, DisjunctionSameVars) {
@@ -135,7 +159,7 @@
   auto XOrNotX = disj(X, NotX);
 
   // X v !X
-  EXPECT_EQ(solve({XOrNotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(solve({XOrNotX}), _);
 }
 
 TEST_F(SolverTest, ConjunctionSameVarsConflict) {
@@ -144,7 +168,7 @@
   auto XAndNotX = conj(X, NotX);
 
   // X ^ !X
-  EXPECT_EQ(solve({XAndNotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XAndNotX}));
 }
 
 TEST_F(SolverTest, PureVar) {
@@ -156,7 +180,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, _)));
 }
 
 TEST_F(SolverTest, MustAssumeVarIsFalse) {
@@ -169,7 +196,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({XOrY, NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, DeepConflict) {
@@ -183,8 +213,7 @@
   auto XOrNotY = disj(X, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y) ^ (X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY, XOrNotY}),
-Solver::Resu

[PATCH] D129180: [clang][dataflow] Return a solution from the solver when `Constraints` are `Satisfiable`.

2022-07-07 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 442824.
wyt added a comment.

Address comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129180/new/

https://reviews.llvm.org/D129180

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/Solver.h
  clang/lib/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -20,6 +20,12 @@
 using namespace clang;
 using namespace dataflow;
 
+using testing::_;
+using testing::AnyOf;
+using testing::Optional;
+using testing::Pair;
+using testing::UnorderedElementsAre;
+
 class SolverTest : public ::testing::Test {
 protected:
   // Checks if the conjunction of `Vals` is satisfiable and returns the
@@ -64,6 +70,17 @@
 return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
   }
 
+  void expectUnsatisfiable(Solver::Result Result) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Unsatisfiable);
+EXPECT_FALSE(Result.getSolution().has_value());
+  }
+
+  template 
+  void expectSatisfiable(Solver::Result Result, Matcher Solution) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Satisfiable);
+EXPECT_THAT(Result.getSolution(), Optional(Solution));
+  }
+
 private:
   std::vector> Vals;
 };
@@ -72,7 +89,9 @@
   auto X = atom();
 
   // X
-  EXPECT_EQ(solve({X}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, NegatedVar) {
@@ -80,7 +99,9 @@
   auto NotX = neg(X);
 
   // !X
-  EXPECT_EQ(solve({NotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotX}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, UnitConflict) {
@@ -88,7 +109,7 @@
   auto NotX = neg(X);
 
   // X ^ !X
-  EXPECT_EQ(solve({X, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({X, NotX}));
 }
 
 TEST_F(SolverTest, DistinctVars) {
@@ -97,7 +118,10 @@
   auto NotY = neg(Y);
 
   // X ^ !Y
-  EXPECT_EQ(solve({X, NotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X, NotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue),
+   Pair(Y, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, DoubleNegation) {
@@ -106,7 +130,7 @@
   auto NotNotX = neg(NotX);
 
   // !!X ^ !X
-  EXPECT_EQ(solve({NotNotX, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotNotX, NotX}));
 }
 
 TEST_F(SolverTest, NegatedDisjunction) {
@@ -116,7 +140,7 @@
   auto NotXOrY = neg(XOrY);
 
   // !(X v Y) ^ (X v Y)
-  EXPECT_EQ(solve({NotXOrY, XOrY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXOrY, XOrY}));
 }
 
 TEST_F(SolverTest, NegatedConjunction) {
@@ -126,7 +150,7 @@
   auto NotXAndY = neg(XAndY);
 
   // !(X ^ Y) ^ (X ^ Y)
-  EXPECT_EQ(solve({NotXAndY, XAndY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXAndY, XAndY}));
 }
 
 TEST_F(SolverTest, DisjunctionSameVars) {
@@ -135,7 +159,7 @@
   auto XOrNotX = disj(X, NotX);
 
   // X v !X
-  EXPECT_EQ(solve({XOrNotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(solve({XOrNotX}), _);
 }
 
 TEST_F(SolverTest, ConjunctionSameVarsConflict) {
@@ -144,7 +168,7 @@
   auto XAndNotX = conj(X, NotX);
 
   // X ^ !X
-  EXPECT_EQ(solve({XAndNotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XAndNotX}));
 }
 
 TEST_F(SolverTest, PureVar) {
@@ -156,7 +180,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, _)));
 }
 
 TEST_F(SolverTest, MustAssumeVarIsFalse) {
@@ -169,7 +196,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({XOrY, NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, DeepConflict) {
@@ -183,8 +213,7 @@
   auto XOrNotY = disj(X, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y) ^ (X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY, XOrNotY}),
-Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XOrY, NotXOrY, NotXOrNotY, XOrNotY}));
 }
 
 TEST_F(

[PATCH] D129180: [clang][dataflow] Return a solution from the solver when `Constraints` are `Satisfiable`.

2022-07-07 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 442825.
wyt marked 3 inline comments as done.
wyt added a comment.

Typo fix.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129180/new/

https://reviews.llvm.org/D129180

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/Solver.h
  clang/lib/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -20,6 +20,12 @@
 using namespace clang;
 using namespace dataflow;
 
+using testing::_;
+using testing::AnyOf;
+using testing::Optional;
+using testing::Pair;
+using testing::UnorderedElementsAre;
+
 class SolverTest : public ::testing::Test {
 protected:
   // Checks if the conjunction of `Vals` is satisfiable and returns the
@@ -64,6 +70,17 @@
 return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
   }
 
+  void expectUnsatisfiable(Solver::Result Result) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Unsatisfiable);
+EXPECT_FALSE(Result.getSolution().has_value());
+  }
+
+  template 
+  void expectSatisfiable(Solver::Result Result, Matcher Solution) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Satisfiable);
+EXPECT_THAT(Result.getSolution(), Optional(Solution));
+  }
+
 private:
   std::vector> Vals;
 };
@@ -72,7 +89,9 @@
   auto X = atom();
 
   // X
-  EXPECT_EQ(solve({X}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, NegatedVar) {
@@ -80,7 +99,9 @@
   auto NotX = neg(X);
 
   // !X
-  EXPECT_EQ(solve({NotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotX}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, UnitConflict) {
@@ -88,7 +109,7 @@
   auto NotX = neg(X);
 
   // X ^ !X
-  EXPECT_EQ(solve({X, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({X, NotX}));
 }
 
 TEST_F(SolverTest, DistinctVars) {
@@ -97,7 +118,10 @@
   auto NotY = neg(Y);
 
   // X ^ !Y
-  EXPECT_EQ(solve({X, NotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X, NotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue),
+   Pair(Y, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, DoubleNegation) {
@@ -106,7 +130,7 @@
   auto NotNotX = neg(NotX);
 
   // !!X ^ !X
-  EXPECT_EQ(solve({NotNotX, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotNotX, NotX}));
 }
 
 TEST_F(SolverTest, NegatedDisjunction) {
@@ -116,7 +140,7 @@
   auto NotXOrY = neg(XOrY);
 
   // !(X v Y) ^ (X v Y)
-  EXPECT_EQ(solve({NotXOrY, XOrY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXOrY, XOrY}));
 }
 
 TEST_F(SolverTest, NegatedConjunction) {
@@ -126,7 +150,7 @@
   auto NotXAndY = neg(XAndY);
 
   // !(X ^ Y) ^ (X ^ Y)
-  EXPECT_EQ(solve({NotXAndY, XAndY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXAndY, XAndY}));
 }
 
 TEST_F(SolverTest, DisjunctionSameVars) {
@@ -135,7 +159,7 @@
   auto XOrNotX = disj(X, NotX);
 
   // X v !X
-  EXPECT_EQ(solve({XOrNotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(solve({XOrNotX}), _);
 }
 
 TEST_F(SolverTest, ConjunctionSameVarsConflict) {
@@ -144,7 +168,7 @@
   auto XAndNotX = conj(X, NotX);
 
   // X ^ !X
-  EXPECT_EQ(solve({XAndNotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XAndNotX}));
 }
 
 TEST_F(SolverTest, PureVar) {
@@ -156,7 +180,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, _)));
 }
 
 TEST_F(SolverTest, MustAssumeVarIsFalse) {
@@ -169,7 +196,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({XOrY, NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, DeepConflict) {
@@ -183,8 +213,7 @@
   auto XOrNotY = disj(X, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y) ^ (X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY, XOrNotY}),
-Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XOrY, NotXOrY, NotXOrN

[PATCH] D129180: [clang][dataflow] Return a solution from the solver when `Constraints` are `Satisfiable`.

2022-07-07 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 442828.
wyt added a comment.

Make scope resolution consistent (Solver:: instead of WatchedLiteralsSolver::).


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129180/new/

https://reviews.llvm.org/D129180

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/Solver.h
  clang/lib/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -20,6 +20,12 @@
 using namespace clang;
 using namespace dataflow;
 
+using testing::_;
+using testing::AnyOf;
+using testing::Optional;
+using testing::Pair;
+using testing::UnorderedElementsAre;
+
 class SolverTest : public ::testing::Test {
 protected:
   // Checks if the conjunction of `Vals` is satisfiable and returns the
@@ -64,6 +70,17 @@
 return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
   }
 
+  void expectUnsatisfiable(Solver::Result Result) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Unsatisfiable);
+EXPECT_FALSE(Result.getSolution().has_value());
+  }
+
+  template 
+  void expectSatisfiable(Solver::Result Result, Matcher Solution) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Satisfiable);
+EXPECT_THAT(Result.getSolution(), Optional(Solution));
+  }
+
 private:
   std::vector> Vals;
 };
@@ -72,7 +89,9 @@
   auto X = atom();
 
   // X
-  EXPECT_EQ(solve({X}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, NegatedVar) {
@@ -80,7 +99,9 @@
   auto NotX = neg(X);
 
   // !X
-  EXPECT_EQ(solve({NotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotX}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, UnitConflict) {
@@ -88,7 +109,7 @@
   auto NotX = neg(X);
 
   // X ^ !X
-  EXPECT_EQ(solve({X, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({X, NotX}));
 }
 
 TEST_F(SolverTest, DistinctVars) {
@@ -97,7 +118,10 @@
   auto NotY = neg(Y);
 
   // X ^ !Y
-  EXPECT_EQ(solve({X, NotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X, NotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue),
+   Pair(Y, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, DoubleNegation) {
@@ -106,7 +130,7 @@
   auto NotNotX = neg(NotX);
 
   // !!X ^ !X
-  EXPECT_EQ(solve({NotNotX, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotNotX, NotX}));
 }
 
 TEST_F(SolverTest, NegatedDisjunction) {
@@ -116,7 +140,7 @@
   auto NotXOrY = neg(XOrY);
 
   // !(X v Y) ^ (X v Y)
-  EXPECT_EQ(solve({NotXOrY, XOrY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXOrY, XOrY}));
 }
 
 TEST_F(SolverTest, NegatedConjunction) {
@@ -126,7 +150,7 @@
   auto NotXAndY = neg(XAndY);
 
   // !(X ^ Y) ^ (X ^ Y)
-  EXPECT_EQ(solve({NotXAndY, XAndY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXAndY, XAndY}));
 }
 
 TEST_F(SolverTest, DisjunctionSameVars) {
@@ -135,7 +159,7 @@
   auto XOrNotX = disj(X, NotX);
 
   // X v !X
-  EXPECT_EQ(solve({XOrNotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(solve({XOrNotX}), _);
 }
 
 TEST_F(SolverTest, ConjunctionSameVarsConflict) {
@@ -144,7 +168,7 @@
   auto XAndNotX = conj(X, NotX);
 
   // X ^ !X
-  EXPECT_EQ(solve({XAndNotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XAndNotX}));
 }
 
 TEST_F(SolverTest, PureVar) {
@@ -156,7 +180,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, _)));
 }
 
 TEST_F(SolverTest, MustAssumeVarIsFalse) {
@@ -169,7 +196,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({XOrY, NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, DeepConflict) {
@@ -183,8 +213,7 @@
   auto XOrNotY = disj(X, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y) ^ (X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY, XOrNotY}),
-Solver::Result::Unsatisfiable);
+  expectUnsatisfiab

[PATCH] D129180: [clang][dataflow] Return a solution from the solver when `Constraints` are `Satisfiable`.

2022-07-07 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 442840.
wyt marked 3 inline comments as done.
wyt added a comment.

Address comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129180/new/

https://reviews.llvm.org/D129180

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/Solver.h
  clang/lib/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -20,6 +20,12 @@
 using namespace clang;
 using namespace dataflow;
 
+using testing::_;
+using testing::AnyOf;
+using testing::Optional;
+using testing::Pair;
+using testing::UnorderedElementsAre;
+
 class SolverTest : public ::testing::Test {
 protected:
   // Checks if the conjunction of `Vals` is satisfiable and returns the
@@ -64,6 +70,17 @@
 return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
   }
 
+  void expectUnsatisfiable(Solver::Result Result) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Unsatisfiable);
+EXPECT_FALSE(Result.getSolution().has_value());
+  }
+
+  template 
+  void expectSatisfiable(Solver::Result Result, Matcher Solution) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Satisfiable);
+EXPECT_THAT(Result.getSolution(), Optional(Solution));
+  }
+
 private:
   std::vector> Vals;
 };
@@ -72,7 +89,9 @@
   auto X = atom();
 
   // X
-  EXPECT_EQ(solve({X}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, NegatedVar) {
@@ -80,7 +99,9 @@
   auto NotX = neg(X);
 
   // !X
-  EXPECT_EQ(solve({NotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotX}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, UnitConflict) {
@@ -88,7 +109,7 @@
   auto NotX = neg(X);
 
   // X ^ !X
-  EXPECT_EQ(solve({X, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({X, NotX}));
 }
 
 TEST_F(SolverTest, DistinctVars) {
@@ -97,7 +118,10 @@
   auto NotY = neg(Y);
 
   // X ^ !Y
-  EXPECT_EQ(solve({X, NotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X, NotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue),
+   Pair(Y, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, DoubleNegation) {
@@ -106,7 +130,7 @@
   auto NotNotX = neg(NotX);
 
   // !!X ^ !X
-  EXPECT_EQ(solve({NotNotX, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotNotX, NotX}));
 }
 
 TEST_F(SolverTest, NegatedDisjunction) {
@@ -116,7 +140,7 @@
   auto NotXOrY = neg(XOrY);
 
   // !(X v Y) ^ (X v Y)
-  EXPECT_EQ(solve({NotXOrY, XOrY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXOrY, XOrY}));
 }
 
 TEST_F(SolverTest, NegatedConjunction) {
@@ -126,7 +150,7 @@
   auto NotXAndY = neg(XAndY);
 
   // !(X ^ Y) ^ (X ^ Y)
-  EXPECT_EQ(solve({NotXAndY, XAndY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXAndY, XAndY}));
 }
 
 TEST_F(SolverTest, DisjunctionSameVars) {
@@ -135,7 +159,7 @@
   auto XOrNotX = disj(X, NotX);
 
   // X v !X
-  EXPECT_EQ(solve({XOrNotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(solve({XOrNotX}), _);
 }
 
 TEST_F(SolverTest, ConjunctionSameVarsConflict) {
@@ -144,7 +168,7 @@
   auto XAndNotX = conj(X, NotX);
 
   // X ^ !X
-  EXPECT_EQ(solve({XAndNotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XAndNotX}));
 }
 
 TEST_F(SolverTest, PureVar) {
@@ -156,7 +180,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, _)));
 }
 
 TEST_F(SolverTest, MustAssumeVarIsFalse) {
@@ -169,7 +196,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({XOrY, NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, DeepConflict) {
@@ -183,8 +213,7 @@
   auto XOrNotY = disj(X, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y) ^ (X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY, XOrNotY}),
-Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XOrY, NotXOrY,

[PATCH] D129180: [clang][dataflow] Return a solution from the solver when `Constraints` are `Satisfiable`.

2022-07-07 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 442845.
wyt added a comment.

Fix comment.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129180/new/

https://reviews.llvm.org/D129180

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/Solver.h
  clang/lib/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -20,6 +20,12 @@
 using namespace clang;
 using namespace dataflow;
 
+using testing::_;
+using testing::AnyOf;
+using testing::Optional;
+using testing::Pair;
+using testing::UnorderedElementsAre;
+
 class SolverTest : public ::testing::Test {
 protected:
   // Checks if the conjunction of `Vals` is satisfiable and returns the
@@ -64,6 +70,17 @@
 return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
   }
 
+  void expectUnsatisfiable(Solver::Result Result) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Unsatisfiable);
+EXPECT_FALSE(Result.getSolution().has_value());
+  }
+
+  template 
+  void expectSatisfiable(Solver::Result Result, Matcher Solution) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Satisfiable);
+EXPECT_THAT(Result.getSolution(), Optional(Solution));
+  }
+
 private:
   std::vector> Vals;
 };
@@ -72,7 +89,9 @@
   auto X = atom();
 
   // X
-  EXPECT_EQ(solve({X}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, NegatedVar) {
@@ -80,7 +99,9 @@
   auto NotX = neg(X);
 
   // !X
-  EXPECT_EQ(solve({NotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotX}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, UnitConflict) {
@@ -88,7 +109,7 @@
   auto NotX = neg(X);
 
   // X ^ !X
-  EXPECT_EQ(solve({X, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({X, NotX}));
 }
 
 TEST_F(SolverTest, DistinctVars) {
@@ -97,7 +118,10 @@
   auto NotY = neg(Y);
 
   // X ^ !Y
-  EXPECT_EQ(solve({X, NotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X, NotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue),
+   Pair(Y, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, DoubleNegation) {
@@ -106,7 +130,7 @@
   auto NotNotX = neg(NotX);
 
   // !!X ^ !X
-  EXPECT_EQ(solve({NotNotX, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotNotX, NotX}));
 }
 
 TEST_F(SolverTest, NegatedDisjunction) {
@@ -116,7 +140,7 @@
   auto NotXOrY = neg(XOrY);
 
   // !(X v Y) ^ (X v Y)
-  EXPECT_EQ(solve({NotXOrY, XOrY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXOrY, XOrY}));
 }
 
 TEST_F(SolverTest, NegatedConjunction) {
@@ -126,7 +150,7 @@
   auto NotXAndY = neg(XAndY);
 
   // !(X ^ Y) ^ (X ^ Y)
-  EXPECT_EQ(solve({NotXAndY, XAndY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXAndY, XAndY}));
 }
 
 TEST_F(SolverTest, DisjunctionSameVars) {
@@ -135,7 +159,7 @@
   auto XOrNotX = disj(X, NotX);
 
   // X v !X
-  EXPECT_EQ(solve({XOrNotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(solve({XOrNotX}), _);
 }
 
 TEST_F(SolverTest, ConjunctionSameVarsConflict) {
@@ -144,7 +168,7 @@
   auto XAndNotX = conj(X, NotX);
 
   // X ^ !X
-  EXPECT_EQ(solve({XAndNotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XAndNotX}));
 }
 
 TEST_F(SolverTest, PureVar) {
@@ -156,7 +180,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, _)));
 }
 
 TEST_F(SolverTest, MustAssumeVarIsFalse) {
@@ -169,7 +196,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({XOrY, NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, DeepConflict) {
@@ -183,8 +213,7 @@
   auto XOrNotY = disj(X, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y) ^ (X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY, XOrNotY}),
-Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XOrY, NotXOrY, NotXOrNotY, XOrNotY}));
 }
 
 TEST_F(Solve

[PATCH] D129180: [clang][dataflow] Return a solution from the solver when `Constraints` are `Satisfiable`.

2022-07-07 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 442848.
wyt added a comment.

Replace std::optional with llvm::Optional


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129180/new/

https://reviews.llvm.org/D129180

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/Solver.h
  clang/lib/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -20,6 +20,12 @@
 using namespace clang;
 using namespace dataflow;
 
+using testing::_;
+using testing::AnyOf;
+using testing::Optional;
+using testing::Pair;
+using testing::UnorderedElementsAre;
+
 class SolverTest : public ::testing::Test {
 protected:
   // Checks if the conjunction of `Vals` is satisfiable and returns the
@@ -64,6 +70,17 @@
 return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
   }
 
+  void expectUnsatisfiable(Solver::Result Result) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Unsatisfiable);
+EXPECT_FALSE(Result.getSolution().has_value());
+  }
+
+  template 
+  void expectSatisfiable(Solver::Result Result, Matcher Solution) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Satisfiable);
+EXPECT_THAT(Result.getSolution(), Optional(Solution));
+  }
+
 private:
   std::vector> Vals;
 };
@@ -72,7 +89,9 @@
   auto X = atom();
 
   // X
-  EXPECT_EQ(solve({X}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, NegatedVar) {
@@ -80,7 +99,9 @@
   auto NotX = neg(X);
 
   // !X
-  EXPECT_EQ(solve({NotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotX}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, UnitConflict) {
@@ -88,7 +109,7 @@
   auto NotX = neg(X);
 
   // X ^ !X
-  EXPECT_EQ(solve({X, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({X, NotX}));
 }
 
 TEST_F(SolverTest, DistinctVars) {
@@ -97,7 +118,10 @@
   auto NotY = neg(Y);
 
   // X ^ !Y
-  EXPECT_EQ(solve({X, NotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X, NotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue),
+   Pair(Y, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, DoubleNegation) {
@@ -106,7 +130,7 @@
   auto NotNotX = neg(NotX);
 
   // !!X ^ !X
-  EXPECT_EQ(solve({NotNotX, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotNotX, NotX}));
 }
 
 TEST_F(SolverTest, NegatedDisjunction) {
@@ -116,7 +140,7 @@
   auto NotXOrY = neg(XOrY);
 
   // !(X v Y) ^ (X v Y)
-  EXPECT_EQ(solve({NotXOrY, XOrY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXOrY, XOrY}));
 }
 
 TEST_F(SolverTest, NegatedConjunction) {
@@ -126,7 +150,7 @@
   auto NotXAndY = neg(XAndY);
 
   // !(X ^ Y) ^ (X ^ Y)
-  EXPECT_EQ(solve({NotXAndY, XAndY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXAndY, XAndY}));
 }
 
 TEST_F(SolverTest, DisjunctionSameVars) {
@@ -135,7 +159,7 @@
   auto XOrNotX = disj(X, NotX);
 
   // X v !X
-  EXPECT_EQ(solve({XOrNotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(solve({XOrNotX}), _);
 }
 
 TEST_F(SolverTest, ConjunctionSameVarsConflict) {
@@ -144,7 +168,7 @@
   auto XAndNotX = conj(X, NotX);
 
   // X ^ !X
-  EXPECT_EQ(solve({XAndNotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XAndNotX}));
 }
 
 TEST_F(SolverTest, PureVar) {
@@ -156,7 +180,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, _)));
 }
 
 TEST_F(SolverTest, MustAssumeVarIsFalse) {
@@ -169,7 +196,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({XOrY, NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, DeepConflict) {
@@ -183,8 +213,7 @@
   auto XOrNotY = disj(X, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y) ^ (X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY, XOrNotY}),
-Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XOrY, NotXOrY, NotXOrNotY, X

[PATCH] D129180: [clang][dataflow] Return a solution from the solver when `Constraints` are `Satisfiable`.

2022-07-07 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 442874.
wyt added a comment.

Remove decomposing declarations which are not available in llvm.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129180/new/

https://reviews.llvm.org/D129180

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
  clang/include/clang/Analysis/FlowSensitive/Solver.h
  clang/lib/Analysis/FlowSensitive/WatchedLiteralsSolver.cpp
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -20,6 +20,12 @@
 using namespace clang;
 using namespace dataflow;
 
+using testing::_;
+using testing::AnyOf;
+using testing::Optional;
+using testing::Pair;
+using testing::UnorderedElementsAre;
+
 class SolverTest : public ::testing::Test {
 protected:
   // Checks if the conjunction of `Vals` is satisfiable and returns the
@@ -64,6 +70,17 @@
 return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
   }
 
+  void expectUnsatisfiable(Solver::Result Result) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Unsatisfiable);
+EXPECT_FALSE(Result.getSolution().has_value());
+  }
+
+  template 
+  void expectSatisfiable(Solver::Result Result, Matcher Solution) {
+EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Satisfiable);
+EXPECT_THAT(Result.getSolution(), Optional(Solution));
+  }
+
 private:
   std::vector> Vals;
 };
@@ -72,7 +89,9 @@
   auto X = atom();
 
   // X
-  EXPECT_EQ(solve({X}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, NegatedVar) {
@@ -80,7 +99,9 @@
   auto NotX = neg(X);
 
   // !X
-  EXPECT_EQ(solve({NotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotX}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, UnitConflict) {
@@ -88,7 +109,7 @@
   auto NotX = neg(X);
 
   // X ^ !X
-  EXPECT_EQ(solve({X, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({X, NotX}));
 }
 
 TEST_F(SolverTest, DistinctVars) {
@@ -97,7 +118,10 @@
   auto NotY = neg(Y);
 
   // X ^ !Y
-  EXPECT_EQ(solve({X, NotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({X, NotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedTrue),
+   Pair(Y, Solver::Result::Assignment::AssignedFalse)));
 }
 
 TEST_F(SolverTest, DoubleNegation) {
@@ -106,7 +130,7 @@
   auto NotNotX = neg(NotX);
 
   // !!X ^ !X
-  EXPECT_EQ(solve({NotNotX, NotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotNotX, NotX}));
 }
 
 TEST_F(SolverTest, NegatedDisjunction) {
@@ -116,7 +140,7 @@
   auto NotXOrY = neg(XOrY);
 
   // !(X v Y) ^ (X v Y)
-  EXPECT_EQ(solve({NotXOrY, XOrY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXOrY, XOrY}));
 }
 
 TEST_F(SolverTest, NegatedConjunction) {
@@ -126,7 +150,7 @@
   auto NotXAndY = neg(XAndY);
 
   // !(X ^ Y) ^ (X ^ Y)
-  EXPECT_EQ(solve({NotXAndY, XAndY}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({NotXAndY, XAndY}));
 }
 
 TEST_F(SolverTest, DisjunctionSameVars) {
@@ -135,7 +159,7 @@
   auto XOrNotX = disj(X, NotX);
 
   // X v !X
-  EXPECT_EQ(solve({XOrNotX}), Solver::Result::Satisfiable);
+  expectSatisfiable(solve({XOrNotX}), _);
 }
 
 TEST_F(SolverTest, ConjunctionSameVarsConflict) {
@@ -144,7 +168,7 @@
   auto XAndNotX = conj(X, NotX);
 
   // X ^ !X
-  EXPECT_EQ(solve({XAndNotX}), Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XAndNotX}));
 }
 
 TEST_F(SolverTest, PureVar) {
@@ -156,7 +180,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, _)));
 }
 
 TEST_F(SolverTest, MustAssumeVarIsFalse) {
@@ -169,7 +196,10 @@
   auto NotXOrNotY = disj(NotX, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY}), Solver::Result::Satisfiable);
+  expectSatisfiable(
+  solve({XOrY, NotXOrY, NotXOrNotY}),
+  UnorderedElementsAre(Pair(X, Solver::Result::Assignment::AssignedFalse),
+   Pair(Y, Solver::Result::Assignment::AssignedTrue)));
 }
 
 TEST_F(SolverTest, DeepConflict) {
@@ -183,8 +213,7 @@
   auto XOrNotY = disj(X, NotY);
 
   // (X v Y) ^ (!X v Y) ^ (!X v !Y) ^ (X v !Y)
-  EXPECT_EQ(solve({XOrY, NotXOrY, NotXOrNotY, XOrNotY}),
-Solver::Result::Unsatisfiable);
+  expectUnsatisfiable(solve({XOrY,

[PATCH] D131065: [clang][dataflow] Store DeclContext of block being analysed in Environment if available.

2022-08-03 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, tschuett, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131065

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp


Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -154,7 +154,7 @@
 : DACtx(&DACtx), FlowConditionToken(&DACtx.makeFlowConditionToken()) {}
 
 Environment::Environment(const Environment &Other)
-: DACtx(Other.DACtx), DeclToLoc(Other.DeclToLoc),
+: DACtx(Other.DACtx), DeclCtx(Other.DeclCtx), DeclToLoc(Other.DeclToLoc),
   ExprToLoc(Other.ExprToLoc), LocToVal(Other.LocToVal),
   MemberLocToStruct(Other.MemberLocToStruct),
   FlowConditionToken(&DACtx->forkFlowCondition(*Other.FlowConditionToken)) 
{
@@ -167,9 +167,11 @@
 }
 
 Environment::Environment(DataflowAnalysisContext &DACtx,
- const DeclContext &DeclCtx)
+ const DeclContext &DeclCtxArg)
 : Environment(DACtx) {
-  if (const auto *FuncDecl = dyn_cast(&DeclCtx)) {
+  DeclCtx = &DeclCtxArg;
+
+  if (const auto *FuncDecl = dyn_cast(DeclCtx)) {
 assert(FuncDecl->getBody() != nullptr);
 initGlobalVars(*FuncDecl->getBody(), *this);
 for (const auto *ParamDecl : FuncDecl->parameters()) {
@@ -181,7 +183,7 @@
 }
   }
 
-  if (const auto *MethodDecl = dyn_cast(&DeclCtx)) {
+  if (const auto *MethodDecl = dyn_cast(DeclCtx)) {
 auto *Parent = MethodDecl->getParent();
 assert(Parent != nullptr);
 if (Parent->isLambda())
@@ -268,12 +270,14 @@
 
 LatticeJoinEffect Environment::join(const Environment &Other,
 Environment::ValueModel &Model) {
-  assert(DACtx == Other.DACtx);
+  assert(DACtx == Other.DACtx && DeclCtx == Other.DeclCtx);
 
   auto Effect = LatticeJoinEffect::Unchanged;
 
   Environment JoinedEnv(*DACtx);
 
+  JoinedEnv.DeclCtx = DeclCtx;
+
   JoinedEnv.DeclToLoc = intersectDenseMaps(DeclToLoc, Other.DeclToLoc);
   if (DeclToLoc.size() != JoinedEnv.DeclToLoc.size())
 Effect = LatticeJoinEffect::Changed;
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -167,6 +167,10 @@
   LatticeJoinEffect join(const Environment &Other,
  Environment::ValueModel &Model);
 
+  /// Returns the `DeclCtx` of the block being analysed if provided, otherwise
+  /// returns nullptr.
+  const DeclContext *getDeclCtx() { return DeclCtx; }
+
   // FIXME: Rename `createOrGetStorageLocation` to 
`getOrCreateStorageLocation`,
   // `getStableStorageLocation`, or something more appropriate.
 
@@ -363,6 +367,9 @@
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
+  // `DeclCtx` of the block being analysed if provided.
+  const DeclContext *DeclCtx;
+
   // Maps from program declarations and statements to storage locations that 
are
   // assigned to them. Unlike the maps in `DataflowAnalysisContext`, these
   // include only storage locations that are in scope for a particular basic


Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -154,7 +154,7 @@
 : DACtx(&DACtx), FlowConditionToken(&DACtx.makeFlowConditionToken()) {}
 
 Environment::Environment(const Environment &Other)
-: DACtx(Other.DACtx), DeclToLoc(Other.DeclToLoc),
+: DACtx(Other.DACtx), DeclCtx(Other.DeclCtx), DeclToLoc(Other.DeclToLoc),
   ExprToLoc(Other.ExprToLoc), LocToVal(Other.LocToVal),
   MemberLocToStruct(Other.MemberLocToStruct),
   FlowConditionToken(&DACtx->forkFlowCondition(*Other.FlowConditionToken)) {
@@ -167,9 +167,11 @@
 }
 
 Environment::Environment(DataflowAnalysisContext &DACtx,
- const DeclContext &DeclCtx)
+ const DeclContext &DeclCtxArg)
 : Environment(DACtx) {
-  if (const auto *FuncDecl = dyn_cast(&DeclCtx)) {
+  DeclCtx = &DeclCtxArg;
+
+  if (const auto *FuncDecl = dyn_cast(DeclCtx)) {
 assert(FuncDecl->getBody() != nullptr);
 initGlobalVars(*FuncDecl->getBody(), *this);
 for (const auto *ParamDecl : FuncDecl->parameters()) {
@@ -181,7 +183,7 @@
 }
   }
 
-  if (const auto *MethodDecl = dyn_cast(&DeclCtx)) {
+  if (con

[PATCH] D131065: [clang][dataflow] Store DeclContext of block being analysed in Environment if available.

2022-08-03 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 449664.
wyt added a comment.

Update DeclCtx when pushing/popping calls.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131065/new/

https://reviews.llvm.org/D131065

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.cpp

Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -534,7 +534,9 @@
   auto ExitState = (*BlockToOutputState)[ExitBlock];
   assert(ExitState);
 
-  Env = ExitState->Env;
+  auto *Caller = Env.getDeclCtx();
+  Env = Environment(ExitState->Env);
+  Env.setDeclCtx(Caller);
 }
   }
 
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -154,7 +154,7 @@
 : DACtx(&DACtx), FlowConditionToken(&DACtx.makeFlowConditionToken()) {}
 
 Environment::Environment(const Environment &Other)
-: DACtx(Other.DACtx), DeclToLoc(Other.DeclToLoc),
+: DACtx(Other.DACtx), DeclCtx(Other.DeclCtx), DeclToLoc(Other.DeclToLoc),
   ExprToLoc(Other.ExprToLoc), LocToVal(Other.LocToVal),
   MemberLocToStruct(Other.MemberLocToStruct),
   FlowConditionToken(&DACtx->forkFlowCondition(*Other.FlowConditionToken)) {
@@ -167,9 +167,11 @@
 }
 
 Environment::Environment(DataflowAnalysisContext &DACtx,
- const DeclContext &DeclCtx)
+ const DeclContext &DeclCtxArg)
 : Environment(DACtx) {
-  if (const auto *FuncDecl = dyn_cast(&DeclCtx)) {
+  setDeclCtx(&DeclCtxArg);
+
+  if (const auto *FuncDecl = dyn_cast(DeclCtx)) {
 assert(FuncDecl->getBody() != nullptr);
 initGlobalVars(*FuncDecl->getBody(), *this);
 for (const auto *ParamDecl : FuncDecl->parameters()) {
@@ -181,7 +183,7 @@
 }
   }
 
-  if (const auto *MethodDecl = dyn_cast(&DeclCtx)) {
+  if (const auto *MethodDecl = dyn_cast(DeclCtx)) {
 auto *Parent = MethodDecl->getParent();
 assert(Parent != nullptr);
 if (Parent->isLambda())
@@ -214,6 +216,8 @@
   const auto *FuncDecl = Call->getDirectCallee();
   assert(FuncDecl != nullptr);
   assert(FuncDecl->getBody() != nullptr);
+
+  Env.setDeclCtx(FuncDecl);
   // FIXME: In order to allow the callee to reference globals, we probably need
   // to call `initGlobalVars` here in some way.
 
@@ -268,12 +272,14 @@
 
 LatticeJoinEffect Environment::join(const Environment &Other,
 Environment::ValueModel &Model) {
-  assert(DACtx == Other.DACtx);
+  assert(DACtx == Other.DACtx && DeclCtx == Other.DeclCtx);
 
   auto Effect = LatticeJoinEffect::Unchanged;
 
   Environment JoinedEnv(*DACtx);
 
+  JoinedEnv.setDeclCtx(DeclCtx);
+
   JoinedEnv.DeclToLoc = intersectDenseMaps(DeclToLoc, Other.DeclToLoc);
   if (DeclToLoc.size() != JoinedEnv.DeclToLoc.size())
 Effect = LatticeJoinEffect::Changed;
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -167,6 +167,13 @@
   LatticeJoinEffect join(const Environment &Other,
  Environment::ValueModel &Model);
 
+  /// Returns the `DeclCtx` of the block being analysed if provided, otherwise
+  /// returns nullptr.
+  const DeclContext *getDeclCtx() { return DeclCtx; }
+
+  /// Sets the `DeclCtx` of the block being analysed.
+  void setDeclCtx(const DeclContext *Ctx) { DeclCtx = Ctx; }
+
   // FIXME: Rename `createOrGetStorageLocation` to `getOrCreateStorageLocation`,
   // `getStableStorageLocation`, or something more appropriate.
 
@@ -363,6 +370,9 @@
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
+  // `DeclCtx` of the block being analysed if provided.
+  const DeclContext *DeclCtx;
+
   // Maps from program declarations and statements to storage locations that are
   // assigned to them. Unlike the maps in `DataflowAnalysisContext`, these
   // include only storage locations that are in scope for a particular basic
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131065: [clang][dataflow] Store DeclContext of block being analysed in Environment if available.

2022-08-03 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 449666.
wyt marked 3 inline comments as done.
wyt added a comment.

Formatting fixes.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131065/new/

https://reviews.llvm.org/D131065

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
  clang/lib/Analysis/FlowSensitive/Transfer.cpp

Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -534,7 +534,9 @@
   auto ExitState = (*BlockToOutputState)[ExitBlock];
   assert(ExitState);
 
-  Env = ExitState->Env;
+  auto *Caller = Env.getDeclCtx();
+  Env = Environment(ExitState->Env);
+  Env.setDeclCtx(Caller);
 }
   }
 
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -154,7 +154,7 @@
 : DACtx(&DACtx), FlowConditionToken(&DACtx.makeFlowConditionToken()) {}
 
 Environment::Environment(const Environment &Other)
-: DACtx(Other.DACtx), DeclToLoc(Other.DeclToLoc),
+: DACtx(Other.DACtx), DeclCtx(Other.DeclCtx), DeclToLoc(Other.DeclToLoc),
   ExprToLoc(Other.ExprToLoc), LocToVal(Other.LocToVal),
   MemberLocToStruct(Other.MemberLocToStruct),
   FlowConditionToken(&DACtx->forkFlowCondition(*Other.FlowConditionToken)) {
@@ -167,9 +167,11 @@
 }
 
 Environment::Environment(DataflowAnalysisContext &DACtx,
- const DeclContext &DeclCtx)
+ const DeclContext &DeclCtxArg)
 : Environment(DACtx) {
-  if (const auto *FuncDecl = dyn_cast(&DeclCtx)) {
+  setDeclCtx(&DeclCtxArg);
+
+  if (const auto *FuncDecl = dyn_cast(DeclCtx)) {
 assert(FuncDecl->getBody() != nullptr);
 initGlobalVars(*FuncDecl->getBody(), *this);
 for (const auto *ParamDecl : FuncDecl->parameters()) {
@@ -181,7 +183,7 @@
 }
   }
 
-  if (const auto *MethodDecl = dyn_cast(&DeclCtx)) {
+  if (const auto *MethodDecl = dyn_cast(DeclCtx)) {
 auto *Parent = MethodDecl->getParent();
 assert(Parent != nullptr);
 if (Parent->isLambda())
@@ -214,6 +216,8 @@
   const auto *FuncDecl = Call->getDirectCallee();
   assert(FuncDecl != nullptr);
   assert(FuncDecl->getBody() != nullptr);
+
+  Env.setDeclCtx(FuncDecl);
   // FIXME: In order to allow the callee to reference globals, we probably need
   // to call `initGlobalVars` here in some way.
 
@@ -268,12 +272,14 @@
 
 LatticeJoinEffect Environment::join(const Environment &Other,
 Environment::ValueModel &Model) {
-  assert(DACtx == Other.DACtx);
+  assert(DACtx == Other.DACtx && DeclCtx == Other.DeclCtx);
 
   auto Effect = LatticeJoinEffect::Unchanged;
 
   Environment JoinedEnv(*DACtx);
 
+  JoinedEnv.setDeclCtx(DeclCtx);
+
   JoinedEnv.DeclToLoc = intersectDenseMaps(DeclToLoc, Other.DeclToLoc);
   if (DeclToLoc.size() != JoinedEnv.DeclToLoc.size())
 Effect = LatticeJoinEffect::Changed;
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -167,6 +167,13 @@
   LatticeJoinEffect join(const Environment &Other,
  Environment::ValueModel &Model);
 
+  /// Returns the `DeclContext` of the block being analysed if provided,
+  /// otherwise returns nullptr.
+  const DeclContext *getDeclCtx() { return DeclCtx; }
+
+  /// Sets the `DeclContext` of the block being analysed.
+  void setDeclCtx(const DeclContext *Ctx) { DeclCtx = Ctx; }
+
   // FIXME: Rename `createOrGetStorageLocation` to `getOrCreateStorageLocation`,
   // `getStableStorageLocation`, or something more appropriate.
 
@@ -363,6 +370,9 @@
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
+  // `DeclContext` of the block being analysed if provided.
+  const DeclContext *DeclCtx;
+
   // Maps from program declarations and statements to storage locations that are
   // assigned to them. Unlike the maps in `DataflowAnalysisContext`, these
   // include only storage locations that are in scope for a particular basic
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131065: [clang][dataflow] Store DeclContext of block being analysed in Environment if available.

2022-08-03 Thread weiyi via Phabricator via cfe-commits
wyt added a reviewer: samestep.
wyt removed a subscriber: samestep.
wyt added inline comments.



Comment at: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp:170-172
+ const DeclContext &DeclCtxArg)
 : Environment(DACtx) {
+  DeclCtx = &DeclCtxArg;

sgatev wrote:
> 
Unfortunately doesn't work, I get a warning about "An initializer for a 
delegating constructor must appear alone".



Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131065/new/

https://reviews.llvm.org/D131065

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131065: [clang][dataflow] Store DeclContext of block being analysed in Environment if available.

2022-08-05 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 450276.
wyt marked an inline comment as done.
wyt added a comment.

Rebase to head.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131065/new/

https://reviews.llvm.org/D131065

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -154,7 +154,7 @@
 : DACtx(&DACtx), FlowConditionToken(&DACtx.makeFlowConditionToken()) {}
 
 Environment::Environment(const Environment &Other)
-: DACtx(Other.DACtx), ReturnLoc(Other.ReturnLoc),
+: DACtx(Other.DACtx), DeclCtx(Other.DeclCtx), ReturnLoc(Other.ReturnLoc),
   ThisPointeeLoc(Other.ThisPointeeLoc), DeclToLoc(Other.DeclToLoc),
   ExprToLoc(Other.ExprToLoc), LocToVal(Other.LocToVal),
   MemberLocToStruct(Other.MemberLocToStruct),
@@ -168,9 +168,11 @@
 }
 
 Environment::Environment(DataflowAnalysisContext &DACtx,
- const DeclContext &DeclCtx)
+ const DeclContext &DeclCtxArg)
 : Environment(DACtx) {
-  if (const auto *FuncDecl = dyn_cast(&DeclCtx)) {
+  setDeclCtx(&DeclCtxArg);
+
+  if (const auto *FuncDecl = dyn_cast(DeclCtx)) {
 assert(FuncDecl->getBody() != nullptr);
 initGlobalVars(*FuncDecl->getBody(), *this);
 for (const auto *ParamDecl : FuncDecl->parameters()) {
@@ -185,7 +187,7 @@
 ReturnLoc = &createStorageLocation(ReturnType);
   }
 
-  if (const auto *MethodDecl = dyn_cast(&DeclCtx)) {
+  if (const auto *MethodDecl = dyn_cast(DeclCtx)) {
 auto *Parent = MethodDecl->getParent();
 assert(Parent != nullptr);
 if (Parent->isLambda())
@@ -210,6 +212,9 @@
 
   const auto *FuncDecl = Call->getDirectCallee();
   assert(FuncDecl != nullptr);
+
+  Env.setDeclCtx(FuncDecl);
+
   // FIXME: In order to allow the callee to reference globals, we probably need
   // to call `initGlobalVars` here in some way.
 
@@ -252,12 +257,12 @@
 
 void Environment::popCall(const Environment &CalleeEnv) {
   // We ignore `DACtx` because it's already the same in both. We don't want the
-  // callee's `ReturnLoc` or `ThisPointeeLoc`. We don't bring back `DeclToLoc`
-  // and `ExprToLoc` because we want to be able to later analyze the same callee
-  // in a different context, and `setStorageLocation` requires there to not
-  // already be a storage location assigned. Conceptually, these maps capture
-  // information from the local scope, so when popping that scope, we do not
-  // propagate the maps.
+  // callee's `DeclCtx`, `ReturnLoc` or `ThisPointeeLoc`. We don't bring back
+  // `DeclToLoc` and `ExprToLoc` because we want to be able to later analyze the
+  // same callee in a different context, and `setStorageLocation` requires there
+  // to not already be a storage location assigned. Conceptually, these maps
+  // capture information from the local scope, so when popping that scope, we do
+  // not propagate the maps.
   this->LocToVal = std::move(CalleeEnv.LocToVal);
   this->MemberLocToStruct = std::move(CalleeEnv.MemberLocToStruct);
   this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);
@@ -304,11 +309,13 @@
   assert(DACtx == Other.DACtx);
   assert(ReturnLoc == Other.ReturnLoc);
   assert(ThisPointeeLoc == Other.ThisPointeeLoc);
+  assert(DeclCtx == Other.DeclCtx);
 
   auto Effect = LatticeJoinEffect::Unchanged;
 
   Environment JoinedEnv(*DACtx);
 
+  JoinedEnv.setDeclCtx(DeclCtx);
   JoinedEnv.ReturnLoc = ReturnLoc;
   JoinedEnv.ThisPointeeLoc = ThisPointeeLoc;
 
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -172,6 +172,13 @@
   LatticeJoinEffect join(const Environment &Other,
  Environment::ValueModel &Model);
 
+  /// Returns the `DeclContext` of the block being analysed if provided,
+  /// otherwise returns nullptr.
+  const DeclContext *getDeclCtx() { return DeclCtx; }
+
+  /// Sets the `DeclContext` of the block being analysed.
+  void setDeclCtx(const DeclContext *Ctx) { DeclCtx = Ctx; }
+
   // FIXME: Rename `createOrGetStorageLocation` to `getOrCreateStorageLocation`,
   // `getStableStorageLocation`, or something more appropriate.
 
@@ -377,6 +384,9 @@
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
+  // `DeclContext` of the block being analysed if provided.
+  const DeclContext *DeclCtx;
+
   // In a properly initialized `Environment`, `ReturnLoc` should only be null if
   // its `DeclContext` could not be cast to a `FunctionDecl`.
   Stor

[PATCH] D131065: [clang][dataflow] Store DeclContext of block being analysed in Environment if available.

2022-08-10 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 451411.
wyt added a comment.

Relocate function and fix comment for consistency.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131065/new/

https://reviews.llvm.org/D131065

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -154,7 +154,7 @@
 : DACtx(&DACtx), FlowConditionToken(&DACtx.makeFlowConditionToken()) {}
 
 Environment::Environment(const Environment &Other)
-: DACtx(Other.DACtx), ReturnLoc(Other.ReturnLoc),
+: DACtx(Other.DACtx), DeclCtx(Other.DeclCtx), ReturnLoc(Other.ReturnLoc),
   ThisPointeeLoc(Other.ThisPointeeLoc), DeclToLoc(Other.DeclToLoc),
   ExprToLoc(Other.ExprToLoc), LocToVal(Other.LocToVal),
   MemberLocToStruct(Other.MemberLocToStruct),
@@ -168,9 +168,11 @@
 }
 
 Environment::Environment(DataflowAnalysisContext &DACtx,
- const DeclContext &DeclCtx)
+ const DeclContext &DeclCtxArg)
 : Environment(DACtx) {
-  if (const auto *FuncDecl = dyn_cast(&DeclCtx)) {
+  setDeclCtx(&DeclCtxArg);
+
+  if (const auto *FuncDecl = dyn_cast(DeclCtx)) {
 assert(FuncDecl->getBody() != nullptr);
 initGlobalVars(*FuncDecl->getBody(), *this);
 for (const auto *ParamDecl : FuncDecl->parameters()) {
@@ -185,7 +187,7 @@
 ReturnLoc = &createStorageLocation(ReturnType);
   }
 
-  if (const auto *MethodDecl = dyn_cast(&DeclCtx)) {
+  if (const auto *MethodDecl = dyn_cast(DeclCtx)) {
 auto *Parent = MethodDecl->getParent();
 assert(Parent != nullptr);
 if (Parent->isLambda())
@@ -210,6 +212,9 @@
 
   const auto *FuncDecl = Call->getDirectCallee();
   assert(FuncDecl != nullptr);
+
+  Env.setDeclCtx(FuncDecl);
+
   // FIXME: In order to allow the callee to reference globals, we probably need
   // to call `initGlobalVars` here in some way.
 
@@ -252,12 +257,12 @@
 
 void Environment::popCall(const Environment &CalleeEnv) {
   // We ignore `DACtx` because it's already the same in both. We don't want the
-  // callee's `ReturnLoc` or `ThisPointeeLoc`. We don't bring back `DeclToLoc`
-  // and `ExprToLoc` because we want to be able to later analyze the same callee
-  // in a different context, and `setStorageLocation` requires there to not
-  // already be a storage location assigned. Conceptually, these maps capture
-  // information from the local scope, so when popping that scope, we do not
-  // propagate the maps.
+  // callee's `DeclCtx`, `ReturnLoc` or `ThisPointeeLoc`. We don't bring back
+  // `DeclToLoc` and `ExprToLoc` because we want to be able to later analyze the
+  // same callee in a different context, and `setStorageLocation` requires there
+  // to not already be a storage location assigned. Conceptually, these maps
+  // capture information from the local scope, so when popping that scope, we do
+  // not propagate the maps.
   this->LocToVal = std::move(CalleeEnv.LocToVal);
   this->MemberLocToStruct = std::move(CalleeEnv.MemberLocToStruct);
   this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);
@@ -304,11 +309,13 @@
   assert(DACtx == Other.DACtx);
   assert(ReturnLoc == Other.ReturnLoc);
   assert(ThisPointeeLoc == Other.ThisPointeeLoc);
+  assert(DeclCtx == Other.DeclCtx);
 
   auto Effect = LatticeJoinEffect::Unchanged;
 
   Environment JoinedEnv(*DACtx);
 
+  JoinedEnv.setDeclCtx(DeclCtx);
   JoinedEnv.ReturnLoc = ReturnLoc;
   JoinedEnv.ThisPointeeLoc = ThisPointeeLoc;
 
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -347,6 +347,13 @@
   /// imply that `Val` is true.
   bool flowConditionImplies(BoolValue &Val) const;
 
+  /// Returns the `DeclContext` of the block being analysed, if any. Otherwise,
+  /// returns null.
+  const DeclContext *getDeclCtx() { return DeclCtx; }
+
+  /// Sets the `DeclContext` of the block being analysed.
+  void setDeclCtx(const DeclContext *Ctx) { DeclCtx = Ctx; }
+
   /// Returns the `ControlFlowContext` registered for `F`, if any. Otherwise,
   /// returns null.
   const ControlFlowContext *getControlFlowContext(const FunctionDecl *F) {
@@ -377,6 +384,9 @@
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
+  // `DeclContext` of the block being analysed if provided.
+  const DeclContext *DeclCtx;
+
   // In a properly initialized `Environment`, `ReturnLoc` should only be null if
   // its `DeclContext` could not be cast to a `FunctionDecl`.
   StorageLocat

[PATCH] D131065: [clang][dataflow] Store DeclContext of block being analysed in Environment if available.

2022-08-10 Thread weiyi via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG8a4c40bfe8e6: [clang][dataflow] Store DeclContext of block 
being analysed in Environment if… (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131065/new/

https://reviews.llvm.org/D131065

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -154,7 +154,7 @@
 : DACtx(&DACtx), FlowConditionToken(&DACtx.makeFlowConditionToken()) {}
 
 Environment::Environment(const Environment &Other)
-: DACtx(Other.DACtx), ReturnLoc(Other.ReturnLoc),
+: DACtx(Other.DACtx), DeclCtx(Other.DeclCtx), ReturnLoc(Other.ReturnLoc),
   ThisPointeeLoc(Other.ThisPointeeLoc), DeclToLoc(Other.DeclToLoc),
   ExprToLoc(Other.ExprToLoc), LocToVal(Other.LocToVal),
   MemberLocToStruct(Other.MemberLocToStruct),
@@ -168,9 +168,11 @@
 }
 
 Environment::Environment(DataflowAnalysisContext &DACtx,
- const DeclContext &DeclCtx)
+ const DeclContext &DeclCtxArg)
 : Environment(DACtx) {
-  if (const auto *FuncDecl = dyn_cast(&DeclCtx)) {
+  setDeclCtx(&DeclCtxArg);
+
+  if (const auto *FuncDecl = dyn_cast(DeclCtx)) {
 assert(FuncDecl->getBody() != nullptr);
 initGlobalVars(*FuncDecl->getBody(), *this);
 for (const auto *ParamDecl : FuncDecl->parameters()) {
@@ -185,7 +187,7 @@
 ReturnLoc = &createStorageLocation(ReturnType);
   }
 
-  if (const auto *MethodDecl = dyn_cast(&DeclCtx)) {
+  if (const auto *MethodDecl = dyn_cast(DeclCtx)) {
 auto *Parent = MethodDecl->getParent();
 assert(Parent != nullptr);
 if (Parent->isLambda())
@@ -210,6 +212,9 @@
 
   const auto *FuncDecl = Call->getDirectCallee();
   assert(FuncDecl != nullptr);
+
+  Env.setDeclCtx(FuncDecl);
+
   // FIXME: In order to allow the callee to reference globals, we probably need
   // to call `initGlobalVars` here in some way.
 
@@ -252,12 +257,12 @@
 
 void Environment::popCall(const Environment &CalleeEnv) {
   // We ignore `DACtx` because it's already the same in both. We don't want the
-  // callee's `ReturnLoc` or `ThisPointeeLoc`. We don't bring back `DeclToLoc`
-  // and `ExprToLoc` because we want to be able to later analyze the same callee
-  // in a different context, and `setStorageLocation` requires there to not
-  // already be a storage location assigned. Conceptually, these maps capture
-  // information from the local scope, so when popping that scope, we do not
-  // propagate the maps.
+  // callee's `DeclCtx`, `ReturnLoc` or `ThisPointeeLoc`. We don't bring back
+  // `DeclToLoc` and `ExprToLoc` because we want to be able to later analyze the
+  // same callee in a different context, and `setStorageLocation` requires there
+  // to not already be a storage location assigned. Conceptually, these maps
+  // capture information from the local scope, so when popping that scope, we do
+  // not propagate the maps.
   this->LocToVal = std::move(CalleeEnv.LocToVal);
   this->MemberLocToStruct = std::move(CalleeEnv.MemberLocToStruct);
   this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);
@@ -304,11 +309,13 @@
   assert(DACtx == Other.DACtx);
   assert(ReturnLoc == Other.ReturnLoc);
   assert(ThisPointeeLoc == Other.ThisPointeeLoc);
+  assert(DeclCtx == Other.DeclCtx);
 
   auto Effect = LatticeJoinEffect::Unchanged;
 
   Environment JoinedEnv(*DACtx);
 
+  JoinedEnv.setDeclCtx(DeclCtx);
   JoinedEnv.ReturnLoc = ReturnLoc;
   JoinedEnv.ThisPointeeLoc = ThisPointeeLoc;
 
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -347,6 +347,13 @@
   /// imply that `Val` is true.
   bool flowConditionImplies(BoolValue &Val) const;
 
+  /// Returns the `DeclContext` of the block being analysed, if any. Otherwise,
+  /// returns null.
+  const DeclContext *getDeclCtx() { return DeclCtx; }
+
+  /// Sets the `DeclContext` of the block being analysed.
+  void setDeclCtx(const DeclContext *Ctx) { DeclCtx = Ctx; }
+
   /// Returns the `ControlFlowContext` registered for `F`, if any. Otherwise,
   /// returns null.
   const ControlFlowContext *getControlFlowContext(const FunctionDecl *F) {
@@ -377,6 +384,9 @@
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
+  // `DeclContext` of the block being analysed if provided.
+  const DeclContext *DeclCtx;
+
   // In a pro

[PATCH] D131614: [clang][dataflow] Extend transfer functions for other `CFGElement`s

2022-08-10 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131614

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -71,14 +71,22 @@
   std::vector> &BlockStates;
 };
 
+// TODO:  add example of annotated test code snippet
+//
+// Runs dataflow analysis (specified from `MakeAnalysis`) and the `PostVisit`
+// function (if provided) on the body of the function that matches
+// `TargetFuncMatcher` in code snippet `Code`. `VerifyResults` checks that the
+// results from the analysis matches the expectations (which can be specified by
+// annotations in the `Code`).
+// Requires: `AnalysisT` contains a type `Lattice`.
 template 
-llvm::Error checkDataflow(
+llvm::Error checkDataflowOnCFG(
 llvm::StringRef Code,
 ast_matchers::internal::Matcher TargetFuncMatcher,
 std::function MakeAnalysis,
-std::function
-PostVisitStmt,
+PostVisit,
 std::function VerifyResults, ArrayRef Args,
 const tooling::FileContentMappings &VirtualMappedFiles = {}) {
   llvm::Annotations AnnotatedCode(Code);
@@ -112,14 +120,15 @@
   Environment Env(DACtx, *F);
   auto Analysis = MakeAnalysis(Context, Env);
 
-  std::function
-  PostVisitStmtClosure = nullptr;
-  if (PostVisitStmt != nullptr) {
-PostVisitStmtClosure = [&PostVisitStmt, &Context](
-   const CFGStmt &Stmt,
+  std::function
+  PostVisitClosure = nullptr;
+  if (PostVisit != nullptr) {
+PostVisitClosure =
+[&PostVisit, &Context](const CFGElement &Element,
const TypeErasedDataflowAnalysisState &State) {
-  PostVisitStmt(Context, Stmt, State);
-};
+  PostVisit(Context, Element, State);
+};
   }
 
   llvm::Expected>
@@ -130,7 +139,7 @@
 
   llvm::Expected>>
   MaybeBlockStates = runTypeErasedDataflowAnalysis(*CFCtx, Analysis, Env,
-   PostVisitStmtClosure);
+   PostVisitClosure);
   if (!MaybeBlockStates)
 return MaybeBlockStates.takeError();
   auto &BlockStates = *MaybeBlockStates;
@@ -141,6 +150,32 @@
   return llvm::Error::success();
 }
 
+template 
+llvm::Error checkDataflow(
+llvm::StringRef Code,
+ast_matchers::internal::Matcher TargetFuncMatcher,
+std::function MakeAnalysis,
+std::function
+PostVisitStmt,
+std::function VerifyResults, ArrayRef Args,
+const tooling::FileContentMappings &VirtualMappedFiles = {}) {
+
+  std::function
+  PostVisit = nullptr;
+  if (PostVisitStmt != nullptr) {
+PostVisit = [&PostVisitStmt](ASTContext &Context, const CFGElement &Element,
+ const TypeErasedDataflowAnalysisState &State) {
+  if (Element.getKind() == CFGElement::Statement) {
+PostVisitStmt(Context, *Element.getAs(), State);
+  }
+};
+  }
+  return checkDataflowOnCFG(Code, TargetFuncMatcher, MakeAnalysis, PostVisit,
+VerifyResults, Args, VirtualMappedFiles);
+}
+
 // Runs dataflow on the body of the function that matches `TargetFuncMatcher` in
 // code snippet `Code`. Requires: `AnalysisT` contains a type `Lattice`.
 template 
@@ -157,9 +192,9 @@
 const tooling::FileContentMappings &VirtualMappedFiles = {}) {
   using StateT = DataflowAnalysisState;
 
-  return checkDataflow(
+  return checkDataflowOnCFG(
   Code, std::move(TargetFuncMatcher), std::move(MakeAnalysis),
-  /*PostVisitStmt=*/nullptr,
+  /*PostVisit=*/nullptr,
   [&VerifyResults](AnalysisData AnalysisData) {
 if (AnalysisData.BlockStates.empty()) {
   VerifyResults({}, AnalysisData.ASTCtx);
@@ -180,9 +215,12 @@
   AnalysisData.CFCtx, AnalysisData.BlockStates, *Block,
   AnalysisData.Env, AnalysisData.Analysis,
   [&Results,
-   &Annotations](const clang::CFGStmt &Stmt,
+   &Annotations](const clang::CFGElement &Element,
  const TypeErasedDataflowAnalysisState &State) {
-auto It = Annotations.find(Stmt.getStmt());
+// FIXME: extend testing annotations to non statement constructs
+if (Element.getKind() != clang::CFGElement::Statement)
+  return;
+

[PATCH] D131616: [clang][dataflow] Generalise match switch utility to other AST types and add a `CFGMatchSwitch` which currently handles `CFGStmt` and `CFGInitializer`.

2022-08-10 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Depends On D131614 


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131616

Files:
  clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h

Index: clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
===
--- clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
+++ clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
@@ -24,6 +24,7 @@
 #include "clang/AST/Stmt.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
 #include "llvm/ADT/StringRef.h"
 #include 
@@ -31,6 +32,8 @@
 #include 
 #include 
 
+// TODO: add tests and documentation
+
 namespace clang {
 namespace dataflow {
 
@@ -44,59 +47,45 @@
   Environment &Env;
 };
 
-/// Matches against `Stmt` and, based on its structure, dispatches to an
-/// appropriate handler.
+template  using MatcherT = ast_matchers::internal::Matcher;
+
+template 
+using ActionT = std::function;
+
+template 
+using ASTMatchSwitch =
+std::function;
+
 template 
-using MatchSwitch = std::function;
-
-/// Collects cases of a "match switch": a collection of matchers paired with
-/// callbacks, which together define a switch that can be applied to a
-/// `Stmt`. This structure can simplify the definition of `transfer` functions
-/// that rely on pattern-matching.
-///
-/// For example, consider an analysis that handles particular function calls. It
-/// can define the `MatchSwitch` once, in the constructor of the analysis, and
-/// then reuse it each time that `transfer` is called, with a fresh state value.
-///
-/// \code
-/// MatchSwitch BuildSwitch() {
-///   return MatchSwitchBuilder>()
-/// .CaseOf(callExpr(callee(functionDecl(hasName("foo", TransferFooCall)
-/// .CaseOf(callExpr(argumentCountIs(2),
-///  callee(functionDecl(hasName("bar",
-/// TransferBarCall)
-/// .Build();
-/// }
-/// \endcode
-template  class MatchSwitchBuilder {
+using CFGMatchSwitch =
+std::function;
+
+// Alias for the previous implementation under the name of `MatchSwitch`.
+template 
+using MatchSwitch = ASTMatchSwitch;
+
+template 
+class ASTMatchSwitchBuilder {
 public:
-  /// Registers an action that will be triggered by the match of a pattern
-  /// against the input statement.
-  ///
-  /// Requirements:
-  ///
-  ///  `Node` should be a subclass of `Stmt`.
-  template 
-  MatchSwitchBuilder &&
-  CaseOf(ast_matchers::internal::Matcher M,
- std::function
- A) && {
-Matchers.push_back(std::move(M));
-Actions.push_back(
-[A = std::move(A)](const Stmt *Stmt,
-   const ast_matchers::MatchFinder::MatchResult &R,
-   State &S) { return A(cast(Stmt), R, S); });
+  template 
+  ASTMatchSwitchBuilder CaseOf(MatcherT Matcher,
+   ActionT Action) {
+Matchers.push_back(std::move(Matcher));
+Actions.push_back([A = std::move(Action)](
+  const BaseT *Node,
+  const ast_matchers::MatchFinder::MatchResult &R,
+  State &S) { return A(cast(Node), R, S); });
 return std::move(*this);
   }
 
-  MatchSwitch Build() && {
+  ASTMatchSwitch Build() {
 return [Matcher = BuildMatcher(), Actions = std::move(Actions)](
-   const Stmt &Stmt, ASTContext &Context, State &S) -> Result {
-  auto Results = ast_matchers::matchDynamic(Matcher, Stmt, Context);
-  if (Results.empty())
+   const BaseT &Node, ASTContext &Context, State &S) -> Result {
+  auto Results = ast_matchers::matchDynamic(Matcher, Node, Context);
+  if (Results.empty()) {
 return Result();
+  }
   // Look through the map for the first binding of the form "TagN..." use
   // that to select the action.
   for (const auto &Element : Results[0].getMap()) {
@@ -105,7 +94,7 @@
 if (ID.consume_front("Tag") && !ID.getAsInteger(10, Index) &&
 Index < Actions.size()) {
   return Actions[Index](
-  &Stmt,
+  &Node,
   ast_matchers::MatchFinder::MatchResult(Results[0], &Context), S);
 }
   }
@@ -137,15 +126,59 @@
 // The matcher type on the cases ensures that `Expr` kind is compatible with
 // all of the matchers.
 return DynTypedMatcher::constructVariadic(
-DynTypedMatcher::VO_AnyOf, ASTNodeKind::getFromNodeKind(),
+DynTypedMatcher::VO_AnyOf, ASTNodeKind::getFromNodeKind(),
 std::move(Matchers));
   }
 
+public:
   std::vector Matchers;
-  std

[PATCH] D129546: [clang][dataflow] Refactor boolean creation as a test utility.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, tschuett, xazax.hun.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D129546

Files:
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -220,6 +220,49 @@
 ///  `Name` must be unique in `ASTCtx`.
 const ValueDecl *findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name);
 
+/// Utility class for creating boolean values.
+class BoolValueManager {
+public:
+  // Creates an atomic boolean value.
+  BoolValue *atom() {
+Vals.push_back(std::make_unique());
+return Vals.back().get();
+  }
+
+  // Creates a boolean conjunction value.
+  BoolValue *conj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+Vals.push_back(
+std::make_unique(*LeftSubVal, *RightSubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean disjunction value.
+  BoolValue *disj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+Vals.push_back(
+std::make_unique(*LeftSubVal, *RightSubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean negation value.
+  BoolValue *neg(BoolValue *SubVal) {
+Vals.push_back(std::make_unique(*SubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean implication value.
+  BoolValue *impl(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+return disj(neg(LeftSubVal), RightSubVal);
+  }
+
+  // Creates a boolean biconditional value.
+  BoolValue *iff(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
+  }
+
+private:
+  std::vector> Vals;
+};
+
 } // namespace test
 } // namespace dataflow
 } // namespace clang
Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -7,6 +7,7 @@
 //===--===//
 
 #include "clang/Analysis/FlowSensitive/Solver.h"
+#include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
@@ -34,42 +35,6 @@
 return WatchedLiteralsSolver().solve(std::move(Vals));
   }
 
-  // Creates an atomic boolean value.
-  BoolValue *atom() {
-Vals.push_back(std::make_unique());
-return Vals.back().get();
-  }
-
-  // Creates a boolean conjunction value.
-  BoolValue *conj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-Vals.push_back(
-std::make_unique(*LeftSubVal, *RightSubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean disjunction value.
-  BoolValue *disj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-Vals.push_back(
-std::make_unique(*LeftSubVal, *RightSubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean negation value.
-  BoolValue *neg(BoolValue *SubVal) {
-Vals.push_back(std::make_unique(*SubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean implication value.
-  BoolValue *impl(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-return disj(neg(LeftSubVal), RightSubVal);
-  }
-
-  // Creates a boolean biconditional value.
-  BoolValue *iff(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
-  }
-
   void expectUnsatisfiable(Solver::Result Result) {
 EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Unsatisfiable);
 EXPECT_FALSE(Result.getSolution().has_value());
@@ -81,12 +46,11 @@
 EXPECT_THAT(Result.getSolution(), Optional(Solution));
   }
 
-private:
-  std::vector> Vals;
+  test::BoolValueManager Bools;
 };
 
 TEST_F(SolverTest, Var) {
-  auto X = atom();
+  auto X = Bools.atom();
 
   // X
   expectSatisfiable(
@@ -95,8 +59,8 @@
 }
 
 TEST_F(SolverTest, NegatedVar) {
-  auto X = atom();
-  auto NotX = neg(X);
+  auto X = Bools.atom();
+  auto NotX = Bools.neg(X);
 
   // !X
   expectSatisfiable(
@@ -105,17 +69,17 @@
 }
 
 TEST_F(SolverTest, UnitConflict) {
-  auto X = atom();
-  auto NotX = neg(X);
+  auto X = Bools.atom();
+  auto NotX = Bools.neg(X);
 
   // X ^ !X
   expectUnsatisfiable(solve({X, NotX}));
 }
 
 TEST_F(SolverTest, DistinctVars) {
-  auto X = atom();
-  auto Y = atom();
-  auto NotY = neg(Y);
+  auto X = Bools.atom();
+  auto Y = Bools.atom();
+  auto NotY = Bools.neg(Y);
 
   // X ^ !Y
   expectSatisfiable(
@@ -125,59 +89,59 @@
 }
 
 TEST_F(Solve

[PATCH] D129547: [clang][dataflow] Generate readable form of boolean values.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, tschuett, xazax.hun, mgorny.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added projects: clang, LLVM.
Herald added subscribers: llvm-commits, cfe-commits.

Depends On D129546 


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D129547

Files:
  clang/docs/tools/clang-formatted-files.txt
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/CMakeLists.txt
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
  llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn

Index: llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
===
--- llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
+++ llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
@@ -12,5 +12,6 @@
 "Transfer.cpp",
 "TypeErasedDataflowAnalysis.cpp",
 "WatchedLiteralsSolver.cpp",
+"DebugSupport.cpp",
   ]
 }
Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -0,0 +1,188 @@
+//===- unittests/Analysis/FlowSensitive/DebugSupportTest.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 "clang/Analysis/FlowSensitive/DebugSupport.h"
+#include "TestingSupport.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace clang;
+using namespace dataflow;
+using testing::StrEq;
+
+class BoolValueDebugStringTest : public ::testing::Test {
+protected:
+  test::BoolValueManager Bools;
+};
+
+TEST_F(BoolValueDebugStringTest, AtomicBoolean) {
+  // B0
+  auto B = Bools.atom();
+
+  auto Expected = R"((B0))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Negation) {
+  // !B0
+  auto B = Bools.neg(Bools.atom());
+
+  auto Expected = R"((not
+(B0)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Conjunction) {
+  // B0 ^ B1
+  auto B = Bools.conj(Bools.atom(), Bools.atom());
+
+  auto Expected = R"((and
+(B0)
+(B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Disjunction) {
+  // B0 v B1
+  auto B = Bools.disj(Bools.atom(), Bools.atom());
+
+  auto Expected = R"((or
+(B0)
+(B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Implication) {
+  // B0 => B1, implemented as !B0 v B1
+  auto B = Bools.disj(Bools.neg(Bools.atom()), Bools.atom());
+
+  auto Expected = R"((or
+(not
+(B0))
+(B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Iff) {
+  // B0 <=> B1, implemented as (!B0 v B1) ^ (B0 v !B1)
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto B =
+  Bools.conj(Bools.disj(Bools.neg(B0), B1), Bools.disj(B0, Bools.neg(B1)));
+
+  auto Expected = R"((and
+(or
+(not
+(B0))
+(B1))
+(or
+(B0)
+(not
+(B1)";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Xor) {
+  // (B0 ^ !B1) V (!B0 ^ B1)
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto B =
+  Bools.disj(Bools.conj(B0, Bools.neg(B1)), Bools.conj(Bools.neg(B0), B1));
+
+  auto Expected = R"((or
+(and
+(B0)
+(not
+(B1)))
+(and
+(not
+(B0))
+(B1";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, NestedBoolean) {
+  // B0 ^ (B1 v (B2 ^ (B3 v B4)))
+  auto B = Bools.conj(
+  Bools.atom(),
+  Bools.disj(
+  Bools.atom(),
+  Bools.conj(Bools.atom(), Bools.disj(Bools.atom(), Bools.atom();
+
+  auto Expected = R"((and
+(B0)
+(or
+(B1)
+(and
+(B2)
+(or
+(B3)
+(B4))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, AtomicBooleanWithName) {
+  // True
+  llvm::StringMap NamedBools;
+  auto True = cast(Bools.atom());
+  auto B = True;
+
+  auto Expected = R"((True))";
+  EXPECT_THAT(debugString(*B, {{True, "True"}}), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, ComplexBooleanWithNames) {
+  // (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  auto Cond = cast(Bools.

[PATCH] D129548: [clang][dataflow] Generate readable form of input and output of satisfiability checking.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, tschuett, mgrang, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Depends On D129547 


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D129548

Files:
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -9,6 +9,7 @@
 #include "clang/Analysis/FlowSensitive/DebugSupport.h"
 #include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -185,4 +186,267 @@
   StrEq(Expected));
 }
 
+class SATCheckDebugStringTest : public ::testing::Test {
+protected:
+  Solver::Result CheckSAT(llvm::DenseSet Constraints) {
+return WatchedLiteralsSolver().solve(std::move(Constraints));
+  }
+  test::BoolValueManager Bools;
+};
+
+TEST_F(SATCheckDebugStringTest, AtomicBoolean) {
+  // B0
+  llvm::DenseSet Constraints({Bools.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+(B0)
+
+Satisfiable.
++--+---+
+| Atom | Value |
++--+---+
+| B0   | True  |
++--+---+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST_F(SATCheckDebugStringTest, AtomicBooleanAndNegation) {
+  // B0, !B0
+  auto B0 = Bools.atom();
+  llvm::DenseSet Constraints({B0, Bools.neg(B0)});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+(B0)
+
+(not
+(B0))
+
+Unsatisfiable.
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST_F(SATCheckDebugStringTest, MultipleAtomicBooleans) {
+  // B0, B1
+  llvm::DenseSet Constraints({Bools.atom(), Bools.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+(B0)
+
+(B1)
+
+Satisfiable.
++--+---+
+| Atom | Value |
++--+---+
+| B0   | True  |
++--+---+
+| B1   | True  |
++--+---+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST_F(SATCheckDebugStringTest, Implication) {
+  // B0, B0 => B1
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto Impl = Bools.disj(Bools.neg(B0), B1);
+  llvm::DenseSet Constraints({B0, Impl});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+(B0)
+
+(or
+(not
+(B0))
+(B1))
+
+Satisfiable.
++--+---+
+| Atom | Value |
++--+---+
+| B0   | True  |
++--+---+
+| B1   | True  |
++--+---+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST_F(SATCheckDebugStringTest, Iff) {
+  // B0, B0 <=> B1
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto Iff =
+  Bools.conj(Bools.disj(Bools.neg(B0), B1), Bools.disj(B0, Bools.neg(B1)));
+  llvm::DenseSet Constraints({B0, Iff});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+(B0)
+
+(and
+(or
+(not
+(B0))
+(B1))
+(or
+(B0)
+(not
+(B1
+
+Satisfiable.
++--+---+
+| Atom | Value |
++--+---+
+| B0   | True  |
++--+---+
+| B1   | True  |
++--+---+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST_F(SATCheckDebugStringTest, Xor) {
+  // B0, XOR(B0, B1)
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto XOR =
+  Bools.disj(Bools.conj(B0, Bools.neg(B1)), Bools.conj(Bools.neg(B0), B1));
+  llvm::DenseSet Constraints({B0, XOR});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+(B0)
+
+(or
+(and
+(B0)
+(not
+(B1)))
+(and
+(not
+(B0))
+(B1)))
+
+Satisfiable.
++--+---+
+| Atom | Value |
++--+---+
+| B0   | True  |
++--+---+
+| B1   | False |
++--+---+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST_F(SATCheckDebugStringTest, ComplexBooleanWithNames) {
+  // Cond, (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  auto Cond = cast(Bools.atom());
+  auto Then = cast(Bools.atom());
+  auto Else = cast(Bools.atom());
+  auto B = Bools.disj(
+  Bools.conj(Cond, Bools.conj(Then, Bools.neg(Else))),
+  Bools.conj(Bools.neg(Cond), Bools.conj(Bools

[PATCH] D129547: [clang][dataflow] Generate readable form of boolean values.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 443868.
wyt added a comment.

Update CMakeList for DebugSupportTest.cpp


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129547/new/

https://reviews.llvm.org/D129547

Files:
  clang/docs/tools/clang-formatted-files.txt
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/CMakeLists.txt
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
  llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn

Index: llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
===
--- llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
+++ llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
@@ -12,5 +12,6 @@
 "Transfer.cpp",
 "TypeErasedDataflowAnalysis.cpp",
 "WatchedLiteralsSolver.cpp",
+"DebugSupport.cpp",
   ]
 }
Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -0,0 +1,188 @@
+//===- unittests/Analysis/FlowSensitive/DebugSupportTest.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 "clang/Analysis/FlowSensitive/DebugSupport.h"
+#include "TestingSupport.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace clang;
+using namespace dataflow;
+using testing::StrEq;
+
+class BoolValueDebugStringTest : public ::testing::Test {
+protected:
+  test::BoolValueManager Bools;
+};
+
+TEST_F(BoolValueDebugStringTest, AtomicBoolean) {
+  // B0
+  auto B = Bools.atom();
+
+  auto Expected = R"((B0))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Negation) {
+  // !B0
+  auto B = Bools.neg(Bools.atom());
+
+  auto Expected = R"((not
+(B0)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Conjunction) {
+  // B0 ^ B1
+  auto B = Bools.conj(Bools.atom(), Bools.atom());
+
+  auto Expected = R"((and
+(B0)
+(B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Disjunction) {
+  // B0 v B1
+  auto B = Bools.disj(Bools.atom(), Bools.atom());
+
+  auto Expected = R"((or
+(B0)
+(B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Implication) {
+  // B0 => B1, implemented as !B0 v B1
+  auto B = Bools.disj(Bools.neg(Bools.atom()), Bools.atom());
+
+  auto Expected = R"((or
+(not
+(B0))
+(B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Iff) {
+  // B0 <=> B1, implemented as (!B0 v B1) ^ (B0 v !B1)
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto B =
+  Bools.conj(Bools.disj(Bools.neg(B0), B1), Bools.disj(B0, Bools.neg(B1)));
+
+  auto Expected = R"((and
+(or
+(not
+(B0))
+(B1))
+(or
+(B0)
+(not
+(B1)";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Xor) {
+  // (B0 ^ !B1) V (!B0 ^ B1)
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto B =
+  Bools.disj(Bools.conj(B0, Bools.neg(B1)), Bools.conj(Bools.neg(B0), B1));
+
+  auto Expected = R"((or
+(and
+(B0)
+(not
+(B1)))
+(and
+(not
+(B0))
+(B1";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, NestedBoolean) {
+  // B0 ^ (B1 v (B2 ^ (B3 v B4)))
+  auto B = Bools.conj(
+  Bools.atom(),
+  Bools.disj(
+  Bools.atom(),
+  Bools.conj(Bools.atom(), Bools.disj(Bools.atom(), Bools.atom();
+
+  auto Expected = R"((and
+(B0)
+(or
+(B1)
+(and
+(B2)
+(or
+(B3)
+(B4))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, AtomicBooleanWithName) {
+  // True
+  llvm::StringMap NamedBools;
+  auto True = cast(Bools.atom());
+  auto B = True;
+
+  auto Expected = R"((True))";
+  EXPECT_THAT(debugString(*B, {{True, "True"}}), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, ComplexBooleanWithNames) {
+  // (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  auto Cond = cast(Bools.atom());
+  auto Then = cast(Bools.atom());
+  auto Else = cast(Bools.atom());
+  auto B = Bools.disj(
+

[PATCH] D129547: [clang][dataflow] Generate readable form of boolean values for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 443905.
wyt added a comment.

Extract recursion into boolean subvalues into separate statements to enforce 
order of evaluation.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129547/new/

https://reviews.llvm.org/D129547

Files:
  clang/docs/tools/clang-formatted-files.txt
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/CMakeLists.txt
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
  llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn

Index: llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
===
--- llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
+++ llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
@@ -12,5 +12,6 @@
 "Transfer.cpp",
 "TypeErasedDataflowAnalysis.cpp",
 "WatchedLiteralsSolver.cpp",
+"DebugSupport.cpp",
   ]
 }
Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -0,0 +1,188 @@
+//===- unittests/Analysis/FlowSensitive/DebugSupportTest.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 "clang/Analysis/FlowSensitive/DebugSupport.h"
+#include "TestingSupport.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace clang;
+using namespace dataflow;
+using testing::StrEq;
+
+class BoolValueDebugStringTest : public ::testing::Test {
+protected:
+  test::BoolValueManager Bools;
+};
+
+TEST_F(BoolValueDebugStringTest, AtomicBoolean) {
+  // B0
+  auto B = Bools.atom();
+
+  auto Expected = R"((B0))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Negation) {
+  // !B0
+  auto B = Bools.neg(Bools.atom());
+
+  auto Expected = R"((not
+(B0)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Conjunction) {
+  // B0 ^ B1
+  auto B = Bools.conj(Bools.atom(), Bools.atom());
+
+  auto Expected = R"((and
+(B0)
+(B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Disjunction) {
+  // B0 v B1
+  auto B = Bools.disj(Bools.atom(), Bools.atom());
+
+  auto Expected = R"((or
+(B0)
+(B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Implication) {
+  // B0 => B1, implemented as !B0 v B1
+  auto B = Bools.disj(Bools.neg(Bools.atom()), Bools.atom());
+
+  auto Expected = R"((or
+(not
+(B0))
+(B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Iff) {
+  // B0 <=> B1, implemented as (!B0 v B1) ^ (B0 v !B1)
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto B =
+  Bools.conj(Bools.disj(Bools.neg(B0), B1), Bools.disj(B0, Bools.neg(B1)));
+
+  auto Expected = R"((and
+(or
+(not
+(B0))
+(B1))
+(or
+(B0)
+(not
+(B1)";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Xor) {
+  // (B0 ^ !B1) V (!B0 ^ B1)
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto B =
+  Bools.disj(Bools.conj(B0, Bools.neg(B1)), Bools.conj(Bools.neg(B0), B1));
+
+  auto Expected = R"((or
+(and
+(B0)
+(not
+(B1)))
+(and
+(not
+(B0))
+(B1";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, NestedBoolean) {
+  // B0 ^ (B1 v (B2 ^ (B3 v B4)))
+  auto B = Bools.conj(
+  Bools.atom(),
+  Bools.disj(
+  Bools.atom(),
+  Bools.conj(Bools.atom(), Bools.disj(Bools.atom(), Bools.atom();
+
+  auto Expected = R"((and
+(B0)
+(or
+(B1)
+(and
+(B2)
+(or
+(B3)
+(B4))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, AtomicBooleanWithName) {
+  // True
+  llvm::StringMap NamedBools;
+  auto True = cast(Bools.atom());
+  auto B = True;
+
+  auto Expected = R"((True))";
+  EXPECT_THAT(debugString(*B, {{True, "True"}}), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, ComplexBooleanWithNames) {
+  // (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  auto Cond = cast(Bools.atom());
+  auto Then = cast(Bools.atom());
+  

[PATCH] D129547: [clang][dataflow] Generate readable form of boolean values for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 443914.
wyt added a comment.

Minor fixes.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129547/new/

https://reviews.llvm.org/D129547

Files:
  clang/docs/tools/clang-formatted-files.txt
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/CMakeLists.txt
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
  llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn

Index: llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
===
--- llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
+++ llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
@@ -12,5 +12,6 @@
 "Transfer.cpp",
 "TypeErasedDataflowAnalysis.cpp",
 "WatchedLiteralsSolver.cpp",
+"DebugSupport.cpp",
   ]
 }
Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -0,0 +1,188 @@
+//===- unittests/Analysis/FlowSensitive/DebugSupportTest.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 "clang/Analysis/FlowSensitive/DebugSupport.h"
+#include "TestingSupport.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace clang;
+using namespace dataflow;
+using testing::StrEq;
+
+class BoolValueDebugStringTest : public ::testing::Test {
+protected:
+  test::BoolValueManager Bools;
+};
+
+TEST_F(BoolValueDebugStringTest, AtomicBoolean) {
+  // B0
+  auto B = Bools.atom();
+
+  auto Expected = R"((B0))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Negation) {
+  // !B0
+  auto B = Bools.neg(Bools.atom());
+
+  auto Expected = R"((not
+(B0)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Conjunction) {
+  // B0 ^ B1
+  auto B = Bools.conj(Bools.atom(), Bools.atom());
+
+  auto Expected = R"((and
+(B0)
+(B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Disjunction) {
+  // B0 v B1
+  auto B = Bools.disj(Bools.atom(), Bools.atom());
+
+  auto Expected = R"((or
+(B0)
+(B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Implication) {
+  // B0 => B1, implemented as !B0 v B1
+  auto B = Bools.disj(Bools.neg(Bools.atom()), Bools.atom());
+
+  auto Expected = R"((or
+(not
+(B0))
+(B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Iff) {
+  // B0 <=> B1, implemented as (!B0 v B1) ^ (B0 v !B1)
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto B =
+  Bools.conj(Bools.disj(Bools.neg(B0), B1), Bools.disj(B0, Bools.neg(B1)));
+
+  auto Expected = R"((and
+(or
+(not
+(B0))
+(B1))
+(or
+(B0)
+(not
+(B1)";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, Xor) {
+  // (B0 ^ !B1) V (!B0 ^ B1)
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto B =
+  Bools.disj(Bools.conj(B0, Bools.neg(B1)), Bools.conj(Bools.neg(B0), B1));
+
+  auto Expected = R"((or
+(and
+(B0)
+(not
+(B1)))
+(and
+(not
+(B0))
+(B1";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, NestedBoolean) {
+  // B0 ^ (B1 v (B2 ^ (B3 v B4)))
+  auto B = Bools.conj(
+  Bools.atom(),
+  Bools.disj(
+  Bools.atom(),
+  Bools.conj(Bools.atom(), Bools.disj(Bools.atom(), Bools.atom();
+
+  auto Expected = R"((and
+(B0)
+(or
+(B1)
+(and
+(B2)
+(or
+(B3)
+(B4))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, AtomicBooleanWithName) {
+  // True
+  llvm::StringMap NamedBools;
+  auto True = cast(Bools.atom());
+  auto B = True;
+
+  auto Expected = R"((True))";
+  EXPECT_THAT(debugString(*B, {{True, "True"}}), StrEq(Expected));
+}
+
+TEST_F(BoolValueDebugStringTest, ComplexBooleanWithNames) {
+  // (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  auto Cond = cast(Bools.atom());
+  auto Then = cast(Bools.atom());
+  auto Else = cast(Bools.atom());
+  auto B = Bools.disj(
+  Bools.conj(Cond, Bools.

[PATCH] D129548: [clang][dataflow] Generate readable form of input and output of satisfiability checking for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 443917.
wyt added a comment.

Use std::vector as input to `debugString` to maintain order stability of 
boolean constraints to enable testing. `debugString` which takes a 
`llvm::DenseSet` is now a wrapper around the logic applied to 
std::vector.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129548/new/

https://reviews.llvm.org/D129548

Files:
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -9,6 +9,7 @@
 #include "clang/Analysis/FlowSensitive/DebugSupport.h"
 #include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -185,4 +186,269 @@
   StrEq(Expected));
 }
 
+class SATCheckDebugStringTest : public ::testing::Test {
+protected:
+  Solver::Result CheckSAT(std::vector Constraints) {
+llvm::DenseSet ConstraintsSet(Constraints.begin(),
+   Constraints.end());
+return WatchedLiteralsSolver().solve(std::move(ConstraintsSet));
+  }
+  test::BoolValueManager Bools;
+};
+
+TEST_F(SATCheckDebugStringTest, AtomicBoolean) {
+  // B0
+  std::vector Constraints({Bools.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+(B0)
+
+Satisfiable.
++--+---+
+| Atom | Value |
++--+---+
+| B0   | True  |
++--+---+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST_F(SATCheckDebugStringTest, AtomicBooleanAndNegation) {
+  // B0, !B0
+  auto B0 = Bools.atom();
+  std::vector Constraints({B0, Bools.neg(B0)});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+(B0)
+
+(not
+(B0))
+
+Unsatisfiable.
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST_F(SATCheckDebugStringTest, MultipleAtomicBooleans) {
+  // B0, B1
+  std::vector Constraints({Bools.atom(), Bools.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+(B0)
+
+(B1)
+
+Satisfiable.
++--+---+
+| Atom | Value |
++--+---+
+| B0   | True  |
++--+---+
+| B1   | True  |
++--+---+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST_F(SATCheckDebugStringTest, Implication) {
+  // B0, B0 => B1
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto Impl = Bools.disj(Bools.neg(B0), B1);
+  std::vector Constraints({B0, Impl});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+(B0)
+
+(or
+(not
+(B0))
+(B1))
+
+Satisfiable.
++--+---+
+| Atom | Value |
++--+---+
+| B0   | True  |
++--+---+
+| B1   | True  |
++--+---+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST_F(SATCheckDebugStringTest, Iff) {
+  // B0, B0 <=> B1
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto Iff =
+  Bools.conj(Bools.disj(Bools.neg(B0), B1), Bools.disj(B0, Bools.neg(B1)));
+  std::vector Constraints({B0, Iff});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+(B0)
+
+(and
+(or
+(not
+(B0))
+(B1))
+(or
+(B0)
+(not
+(B1
+
+Satisfiable.
++--+---+
+| Atom | Value |
++--+---+
+| B0   | True  |
++--+---+
+| B1   | True  |
++--+---+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST_F(SATCheckDebugStringTest, Xor) {
+  // B0, XOR(B0, B1)
+  auto B0 = Bools.atom();
+  auto B1 = Bools.atom();
+  auto XOR =
+  Bools.disj(Bools.conj(B0, Bools.neg(B1)), Bools.conj(Bools.neg(B0), B1));
+  std::vector Constraints({B0, XOR});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+(B0)
+
+(or
+(and
+(B0)
+(not
+(B1)))
+(and
+(not
+(B0))
+(B1)))
+
+Satisfiable.
++--+---+
+| Atom | Value |
++--+---+
+| B0   | True  |
++--+---+
+| B1   | False |
++--+---+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST_F(SATCheckDebugStringTest, ComplexBooleanWithNames) {
+  // Cond, (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  auto Cond = cast(Bools.atom());
+  auto Then = cast(Bools.atom());
+  auto Else = cast(Bools.atom

[PATCH] D129546: [clang][dataflow] Refactor boolean creation as a test utility.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 443938.
wyt marked 2 inline comments as done.
wyt added a comment.

Address comments on renaming. Removed test fixture class, replacing TEST_F with 
TEST.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129546/new/

https://reviews.llvm.org/D129546

Files:
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -220,6 +220,49 @@
 ///  `Name` must be unique in `ASTCtx`.
 const ValueDecl *findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name);
 
+/// Creates and owns constraints which are boolean values.
+class ConstraintContext {
+public:
+  // Creates an atomic boolean value.
+  BoolValue *atom() {
+Vals.push_back(std::make_unique());
+return Vals.back().get();
+  }
+
+  // Creates a boolean conjunction value.
+  BoolValue *conj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+Vals.push_back(
+std::make_unique(*LeftSubVal, *RightSubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean disjunction value.
+  BoolValue *disj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+Vals.push_back(
+std::make_unique(*LeftSubVal, *RightSubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean negation value.
+  BoolValue *neg(BoolValue *SubVal) {
+Vals.push_back(std::make_unique(*SubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean implication value.
+  BoolValue *impl(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+return disj(neg(LeftSubVal), RightSubVal);
+  }
+
+  // Creates a boolean biconditional value.
+  BoolValue *iff(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
+  }
+
+private:
+  std::vector> Vals;
+};
+
 } // namespace test
 } // namespace dataflow
 } // namespace clang
Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -7,6 +7,7 @@
 //===--===//
 
 #include "clang/Analysis/FlowSensitive/Solver.h"
+#include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
@@ -20,73 +21,33 @@
 using namespace clang;
 using namespace dataflow;
 
+using test::ConstraintContext;
 using testing::_;
 using testing::AnyOf;
 using testing::Optional;
 using testing::Pair;
 using testing::UnorderedElementsAre;
 
-class SolverTest : public ::testing::Test {
-protected:
-  // Checks if the conjunction of `Vals` is satisfiable and returns the
-  // corresponding result.
-  Solver::Result solve(llvm::DenseSet Vals) {
-return WatchedLiteralsSolver().solve(std::move(Vals));
-  }
-
-  // Creates an atomic boolean value.
-  BoolValue *atom() {
-Vals.push_back(std::make_unique());
-return Vals.back().get();
-  }
-
-  // Creates a boolean conjunction value.
-  BoolValue *conj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-Vals.push_back(
-std::make_unique(*LeftSubVal, *RightSubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean disjunction value.
-  BoolValue *disj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-Vals.push_back(
-std::make_unique(*LeftSubVal, *RightSubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean negation value.
-  BoolValue *neg(BoolValue *SubVal) {
-Vals.push_back(std::make_unique(*SubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean implication value.
-  BoolValue *impl(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-return disj(neg(LeftSubVal), RightSubVal);
-  }
-
-  // Creates a boolean biconditional value.
-  BoolValue *iff(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
-  }
-
-  void expectUnsatisfiable(Solver::Result Result) {
-EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Unsatisfiable);
-EXPECT_FALSE(Result.getSolution().has_value());
-  }
-
-  template 
-  void expectSatisfiable(Solver::Result Result, Matcher Solution) {
-EXPECT_EQ(Result.getStatus(), Solver::Result::Status::Satisfiable);
-EXPECT_THAT(Result.getSolution(), Optional(Solution));
-  }
-
-private:
-  std::vector> Vals;
-};
-
-TEST_F(SolverTest, Var) {
-  auto X = atom();
+// Checks if the conjunction of `Vals` is satisfiable and returns the
+// corresponding result.
+Solver::Result solve(llvm::DenseSet Vals) {
+  r

[PATCH] D129547: [clang][dataflow] Generate readable form of boolean values for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 443940.
wyt marked 10 inline comments as done.
wyt added a comment.

Address comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129547/new/

https://reviews.llvm.org/D129547

Files:
  clang/docs/tools/clang-formatted-files.txt
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/CMakeLists.txt
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
  llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn

Index: llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
===
--- llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
+++ llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
@@ -12,5 +12,6 @@
 "Transfer.cpp",
 "TypeErasedDataflowAnalysis.cpp",
 "WatchedLiteralsSolver.cpp",
+"DebugSupport.cpp",
   ]
 }
Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -0,0 +1,190 @@
+//===- unittests/Analysis/FlowSensitive/DebugSupportTest.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 "clang/Analysis/FlowSensitive/DebugSupport.h"
+#include "TestingSupport.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace clang;
+using namespace dataflow;
+
+using test::ConstraintContext;
+using testing::StrEq;
+
+TEST(BoolValueDebugStringTest, AtomicBoolean) {
+  // B0
+  ConstraintContext Ctx;
+  auto B = Ctx.atom();
+
+  auto Expected = R"(B0)";
+  debugString(*B);
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Negation) {
+  // !B0
+  ConstraintContext Ctx;
+  auto B = Ctx.neg(Ctx.atom());
+
+  auto Expected = R"((not
+B0))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Conjunction) {
+  // B0 ^ B1
+  ConstraintContext Ctx;
+  auto B = Ctx.conj(Ctx.atom(), Ctx.atom());
+
+  auto Expected = R"((and
+B0
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Disjunction) {
+  // B0 v B1
+  ConstraintContext Ctx;
+  auto B = Ctx.disj(Ctx.atom(), Ctx.atom());
+
+  auto Expected = R"((or
+B0
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Implication) {
+  // B0 => B1, implemented as !B0 v B1
+  ConstraintContext Ctx;
+  auto B = Ctx.disj(Ctx.neg(Ctx.atom()), Ctx.atom());
+
+  auto Expected = R"((or
+(not
+B0)
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Iff) {
+  // B0 <=> B1, implemented as (!B0 v B1) ^ (B0 v !B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto B = Ctx.conj(Ctx.disj(Ctx.neg(B0), B1), Ctx.disj(B0, Ctx.neg(B1)));
+
+  auto Expected = R"((and
+(or
+(not
+B0)
+B1)
+(or
+B0
+(not
+B1";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Xor) {
+  // (B0 ^ !B1) V (!B0 ^ B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto B = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
+
+  auto Expected = R"((or
+(and
+B0
+(not
+B1))
+(and
+(not
+B0)
+B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, NestedBoolean) {
+  // B0 ^ (B1 v (B2 ^ (B3 v B4)))
+  ConstraintContext Ctx;
+  auto B = Ctx.conj(
+  Ctx.atom(),
+  Ctx.disj(Ctx.atom(),
+   Ctx.conj(Ctx.atom(), Ctx.disj(Ctx.atom(), Ctx.atom();
+
+  auto Expected = R"((and
+B0
+(or
+B1
+(and
+B2
+(or
+B3
+B4)";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, AtomicBooleanWithName) {
+  // True
+  ConstraintContext Ctx;
+  auto True = cast(Ctx.atom());
+  auto B = True;
+
+  auto Expected = R"(True)";
+  EXPECT_THAT(debugString(*B, {{True, "True"}}), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, ComplexBooleanWithNames) {
+  // (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  ConstraintContext Ctx;
+  auto Cond = cast(Ctx.atom());
+  auto Then = cast(Ctx.atom());
+  auto Else = cast(Ctx.atom());
+ 

[PATCH] D129548: [clang][dataflow] Generate readable form of input and output of satisfiability checking for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 443941.
wyt marked 2 inline comments as done.
wyt added a comment.

Address comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129548/new/

https://reviews.llvm.org/D129548

Files:
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -9,6 +9,7 @@
 #include "clang/Analysis/FlowSensitive/DebugSupport.h"
 #include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -187,4 +188,242 @@
   StrEq(Expected));
 }
 
+Solver::Result CheckSAT(std::vector Constraints) {
+  llvm::DenseSet ConstraintsSet(Constraints.begin(),
+ Constraints.end());
+  return WatchedLiteralsSolver().solve(std::move(ConstraintsSet));
+}
+
+TEST(SATCheckDebugStringTest, AtomicBoolean) {
+  // B0
+  ConstraintContext Ctx;
+  std::vector Constraints({Ctx.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+Satisfiable.
+
+B0=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, AtomicBooleanAndNegation) {
+  // B0, !B0
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  std::vector Constraints({B0, Ctx.neg(B0)});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(not
+B0)
+
+Unsatisfiable.
+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, MultipleAtomicBooleans) {
+  // B0, B1
+  ConstraintContext Ctx;
+  std::vector Constraints({Ctx.atom(), Ctx.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+B1
+
+Satisfiable.
+
+B0=True
+B1=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Implication) {
+  // B0, B0 => B1
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto Impl = Ctx.disj(Ctx.neg(B0), B1);
+  std::vector Constraints({B0, Impl});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(or
+(not
+B0)
+B1)
+
+Satisfiable.
+
+B0=True
+B1=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Iff) {
+  // B0, B0 <=> B1
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto Iff = Ctx.conj(Ctx.disj(Ctx.neg(B0), B1), Ctx.disj(B0, Ctx.neg(B1)));
+  std::vector Constraints({B0, Iff});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(and
+(or
+(not
+B0)
+B1)
+(or
+B0
+(not
+B1)))
+
+Satisfiable.
+
+B0=True
+B1=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Xor) {
+  // B0, XOR(B0, B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto XOR = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
+  std::vector Constraints({B0, XOR});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(or
+(and
+B0
+(not
+B1))
+(and
+(not
+B0)
+B1))
+
+Satisfiable.
+
+B0=True
+B1=False
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, ComplexBooleanWithNames) {
+  // Cond, (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  ConstraintContext Ctx;
+  auto Cond = cast(Ctx.atom());
+  auto Then = cast(Ctx.atom());
+  auto Else = cast(Ctx.atom());
+  auto B = Ctx.disj(Ctx.conj(Cond, Ctx.conj(Then, Ctx.neg(Else))),
+Ctx.conj(Ctx.neg(Cond), Ctx.conj(Ctx.neg(Then), Else)));
+  std::vector Constraints({Cond, B});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+Cond
+
+(or
+(and
+Cond
+(and
+Then
+(not
+Else)))
+(and
+(not
+Cond)
+(and
+(not
+Then)
+Else)))
+
+Satisfiable.
+
+Cond=True
+Else=False
+Then=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result,
+  {{Cond, "Cond"}, {Then, "Then"}, {Else, "Else"}}),
+

[PATCH] D129548: [clang][dataflow] Generate readable form of input and output of satisfiability checking for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 443947.
wyt added a comment.

Fix comment, remove unused import.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129548/new/

https://reviews.llvm.org/D129548

Files:
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -9,6 +9,7 @@
 #include "clang/Analysis/FlowSensitive/DebugSupport.h"
 #include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -187,4 +188,242 @@
   StrEq(Expected));
 }
 
+Solver::Result CheckSAT(std::vector Constraints) {
+  llvm::DenseSet ConstraintsSet(Constraints.begin(),
+ Constraints.end());
+  return WatchedLiteralsSolver().solve(std::move(ConstraintsSet));
+}
+
+TEST(SATCheckDebugStringTest, AtomicBoolean) {
+  // B0
+  ConstraintContext Ctx;
+  std::vector Constraints({Ctx.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+Satisfiable.
+
+B0=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, AtomicBooleanAndNegation) {
+  // B0, !B0
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  std::vector Constraints({B0, Ctx.neg(B0)});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(not
+B0)
+
+Unsatisfiable.
+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, MultipleAtomicBooleans) {
+  // B0, B1
+  ConstraintContext Ctx;
+  std::vector Constraints({Ctx.atom(), Ctx.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+B1
+
+Satisfiable.
+
+B0=True
+B1=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Implication) {
+  // B0, B0 => B1
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto Impl = Ctx.disj(Ctx.neg(B0), B1);
+  std::vector Constraints({B0, Impl});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(or
+(not
+B0)
+B1)
+
+Satisfiable.
+
+B0=True
+B1=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Iff) {
+  // B0, B0 <=> B1
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto Iff = Ctx.conj(Ctx.disj(Ctx.neg(B0), B1), Ctx.disj(B0, Ctx.neg(B1)));
+  std::vector Constraints({B0, Iff});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(and
+(or
+(not
+B0)
+B1)
+(or
+B0
+(not
+B1)))
+
+Satisfiable.
+
+B0=True
+B1=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Xor) {
+  // B0, XOR(B0, B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto XOR = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
+  std::vector Constraints({B0, XOR});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(or
+(and
+B0
+(not
+B1))
+(and
+(not
+B0)
+B1))
+
+Satisfiable.
+
+B0=True
+B1=False
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, ComplexBooleanWithNames) {
+  // Cond, (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  ConstraintContext Ctx;
+  auto Cond = cast(Ctx.atom());
+  auto Then = cast(Ctx.atom());
+  auto Else = cast(Ctx.atom());
+  auto B = Ctx.disj(Ctx.conj(Cond, Ctx.conj(Then, Ctx.neg(Else))),
+Ctx.conj(Ctx.neg(Cond), Ctx.conj(Ctx.neg(Then), Else)));
+  std::vector Constraints({Cond, B});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+Cond
+
+(or
+(and
+Cond
+(and
+Then
+(not
+Else)))
+(and
+(not
+Cond)
+(and
+(not
+Then)
+Else)))
+
+Satisfiable.
+
+Cond=True
+Else=False
+Then=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result,
+  {{Cond, "Cond"}, {Then, "Then"}, {Else, "Else"}}),
+  StrEq(Expec

[PATCH] D129547: [clang][dataflow] Generate readable form of boolean values for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 443968.
wyt added a comment.

Fix use after move.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129547/new/

https://reviews.llvm.org/D129547

Files:
  clang/docs/tools/clang-formatted-files.txt
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/CMakeLists.txt
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
  llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn

Index: llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
===
--- llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
+++ llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
@@ -12,5 +12,6 @@
 "Transfer.cpp",
 "TypeErasedDataflowAnalysis.cpp",
 "WatchedLiteralsSolver.cpp",
+"DebugSupport.cpp",
   ]
 }
Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -0,0 +1,190 @@
+//===- unittests/Analysis/FlowSensitive/DebugSupportTest.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 "clang/Analysis/FlowSensitive/DebugSupport.h"
+#include "TestingSupport.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace clang;
+using namespace dataflow;
+
+using test::ConstraintContext;
+using testing::StrEq;
+
+TEST(BoolValueDebugStringTest, AtomicBoolean) {
+  // B0
+  ConstraintContext Ctx;
+  auto B = Ctx.atom();
+
+  auto Expected = R"(B0)";
+  debugString(*B);
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Negation) {
+  // !B0
+  ConstraintContext Ctx;
+  auto B = Ctx.neg(Ctx.atom());
+
+  auto Expected = R"((not
+B0))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Conjunction) {
+  // B0 ^ B1
+  ConstraintContext Ctx;
+  auto B = Ctx.conj(Ctx.atom(), Ctx.atom());
+
+  auto Expected = R"((and
+B0
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Disjunction) {
+  // B0 v B1
+  ConstraintContext Ctx;
+  auto B = Ctx.disj(Ctx.atom(), Ctx.atom());
+
+  auto Expected = R"((or
+B0
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Implication) {
+  // B0 => B1, implemented as !B0 v B1
+  ConstraintContext Ctx;
+  auto B = Ctx.disj(Ctx.neg(Ctx.atom()), Ctx.atom());
+
+  auto Expected = R"((or
+(not
+B0)
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Iff) {
+  // B0 <=> B1, implemented as (!B0 v B1) ^ (B0 v !B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto B = Ctx.conj(Ctx.disj(Ctx.neg(B0), B1), Ctx.disj(B0, Ctx.neg(B1)));
+
+  auto Expected = R"((and
+(or
+(not
+B0)
+B1)
+(or
+B0
+(not
+B1";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Xor) {
+  // (B0 ^ !B1) V (!B0 ^ B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto B = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
+
+  auto Expected = R"((or
+(and
+B0
+(not
+B1))
+(and
+(not
+B0)
+B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, NestedBoolean) {
+  // B0 ^ (B1 v (B2 ^ (B3 v B4)))
+  ConstraintContext Ctx;
+  auto B = Ctx.conj(
+  Ctx.atom(),
+  Ctx.disj(Ctx.atom(),
+   Ctx.conj(Ctx.atom(), Ctx.disj(Ctx.atom(), Ctx.atom();
+
+  auto Expected = R"((and
+B0
+(or
+B1
+(and
+B2
+(or
+B3
+B4)";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, AtomicBooleanWithName) {
+  // True
+  ConstraintContext Ctx;
+  auto True = cast(Ctx.atom());
+  auto B = True;
+
+  auto Expected = R"(True)";
+  EXPECT_THAT(debugString(*B, {{True, "True"}}), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, ComplexBooleanWithNames) {
+  // (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  ConstraintContext Ctx;
+  auto Cond = cast(Ctx.atom());
+  auto Then = cast(Ctx.atom());
+  auto Else = cast(Ctx.atom());
+  auto B = Ctx.disj(Ctx.conj(Cond, Ctx

[PATCH] D129568: [clang][dataflow] Rename `Status` field in a `Solver::Result` struct to `SATCheckStatus`.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, tschuett, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Previously, `Status` was named after the enum type `Status` which caused the 
enum to be hidden by the non-type declaration of the `Status` field. This patch 
fixes this issue by using different names for the field and type.

Depends On D129547 


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D129568

Files:
  clang/include/clang/Analysis/FlowSensitive/Solver.h


Index: clang/include/clang/Analysis/FlowSensitive/Solver.h
===
--- clang/include/clang/Analysis/FlowSensitive/Solver.h
+++ clang/include/clang/Analysis/FlowSensitive/Solver.h
@@ -60,7 +60,7 @@
 
 /// Returns the status of satisfiability checking on the queried boolean
 /// formula.
-Status getStatus() const { return Status; }
+Status getStatus() const { return SATCheckStatus; }
 
 /// Returns a truth assignment to boolean values that satisfies the queried
 /// boolean formula if available. Otherwise, an empty optional is returned.
@@ -71,11 +71,11 @@
 
   private:
 Result(
-enum Status Status,
+enum Status SATCheckStatus,
 llvm::Optional> Solution)
-: Status(Status), Solution(std::move(Solution)) {}
+: SATCheckStatus(SATCheckStatus), Solution(std::move(Solution)) {}
 
-Status Status;
+Status SATCheckStatus;
 llvm::Optional> Solution;
   };
 


Index: clang/include/clang/Analysis/FlowSensitive/Solver.h
===
--- clang/include/clang/Analysis/FlowSensitive/Solver.h
+++ clang/include/clang/Analysis/FlowSensitive/Solver.h
@@ -60,7 +60,7 @@
 
 /// Returns the status of satisfiability checking on the queried boolean
 /// formula.
-Status getStatus() const { return Status; }
+Status getStatus() const { return SATCheckStatus; }
 
 /// Returns a truth assignment to boolean values that satisfies the queried
 /// boolean formula if available. Otherwise, an empty optional is returned.
@@ -71,11 +71,11 @@
 
   private:
 Result(
-enum Status Status,
+enum Status SATCheckStatus,
 llvm::Optional> Solution)
-: Status(Status), Solution(std::move(Solution)) {}
+: SATCheckStatus(SATCheckStatus), Solution(std::move(Solution)) {}
 
-Status Status;
+Status SATCheckStatus;
 llvm::Optional> Solution;
   };
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D129548: [clang][dataflow] Generate readable form of input and output of satisfiability checking for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 443971.
wyt added a comment.

Remove unnecessary enum keyword.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129548/new/

https://reviews.llvm.org/D129548

Files:
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -9,6 +9,7 @@
 #include "clang/Analysis/FlowSensitive/DebugSupport.h"
 #include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -187,4 +188,242 @@
   StrEq(Expected));
 }
 
+Solver::Result CheckSAT(std::vector Constraints) {
+  llvm::DenseSet ConstraintsSet(Constraints.begin(),
+ Constraints.end());
+  return WatchedLiteralsSolver().solve(std::move(ConstraintsSet));
+}
+
+TEST(SATCheckDebugStringTest, AtomicBoolean) {
+  // B0
+  ConstraintContext Ctx;
+  std::vector Constraints({Ctx.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+Satisfiable.
+
+B0=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, AtomicBooleanAndNegation) {
+  // B0, !B0
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  std::vector Constraints({B0, Ctx.neg(B0)});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(not
+B0)
+
+Unsatisfiable.
+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, MultipleAtomicBooleans) {
+  // B0, B1
+  ConstraintContext Ctx;
+  std::vector Constraints({Ctx.atom(), Ctx.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+B1
+
+Satisfiable.
+
+B0=True
+B1=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Implication) {
+  // B0, B0 => B1
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto Impl = Ctx.disj(Ctx.neg(B0), B1);
+  std::vector Constraints({B0, Impl});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(or
+(not
+B0)
+B1)
+
+Satisfiable.
+
+B0=True
+B1=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Iff) {
+  // B0, B0 <=> B1
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto Iff = Ctx.conj(Ctx.disj(Ctx.neg(B0), B1), Ctx.disj(B0, Ctx.neg(B1)));
+  std::vector Constraints({B0, Iff});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(and
+(or
+(not
+B0)
+B1)
+(or
+B0
+(not
+B1)))
+
+Satisfiable.
+
+B0=True
+B1=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Xor) {
+  // B0, XOR(B0, B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto XOR = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
+  std::vector Constraints({B0, XOR});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(or
+(and
+B0
+(not
+B1))
+(and
+(not
+B0)
+B1))
+
+Satisfiable.
+
+B0=True
+B1=False
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, ComplexBooleanWithNames) {
+  // Cond, (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  ConstraintContext Ctx;
+  auto Cond = cast(Ctx.atom());
+  auto Then = cast(Ctx.atom());
+  auto Else = cast(Ctx.atom());
+  auto B = Ctx.disj(Ctx.conj(Cond, Ctx.conj(Then, Ctx.neg(Else))),
+Ctx.conj(Ctx.neg(Cond), Ctx.conj(Ctx.neg(Then), Else)));
+  std::vector Constraints({Cond, B});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+Cond
+
+(or
+(and
+Cond
+(and
+Then
+(not
+Else)))
+(and
+(not
+Cond)
+(and
+(not
+Then)
+Else)))
+
+Satisfiable.
+
+Cond=True
+Else=False
+Then=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result,
+  {{Cond, "Cond"}, {Then, "Then"}, {Else, "Else"}}),
+  StrEq(Expecte

[PATCH] D129547: [clang][dataflow] Generate readable form of boolean values for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 443982.
wyt added a comment.

Move `DebugStringGenerator` class into anonymous namespace.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129547/new/

https://reviews.llvm.org/D129547

Files:
  clang/docs/tools/clang-formatted-files.txt
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/CMakeLists.txt
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
  llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn

Index: llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
===
--- llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
+++ llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
@@ -12,5 +12,6 @@
 "Transfer.cpp",
 "TypeErasedDataflowAnalysis.cpp",
 "WatchedLiteralsSolver.cpp",
+"DebugSupport.cpp",
   ]
 }
Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -0,0 +1,190 @@
+//===- unittests/Analysis/FlowSensitive/DebugSupportTest.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 "clang/Analysis/FlowSensitive/DebugSupport.h"
+#include "TestingSupport.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace clang;
+using namespace dataflow;
+
+using test::ConstraintContext;
+using testing::StrEq;
+
+TEST(BoolValueDebugStringTest, AtomicBoolean) {
+  // B0
+  ConstraintContext Ctx;
+  auto B = Ctx.atom();
+
+  auto Expected = R"(B0)";
+  debugString(*B);
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Negation) {
+  // !B0
+  ConstraintContext Ctx;
+  auto B = Ctx.neg(Ctx.atom());
+
+  auto Expected = R"((not
+B0))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Conjunction) {
+  // B0 ^ B1
+  ConstraintContext Ctx;
+  auto B = Ctx.conj(Ctx.atom(), Ctx.atom());
+
+  auto Expected = R"((and
+B0
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Disjunction) {
+  // B0 v B1
+  ConstraintContext Ctx;
+  auto B = Ctx.disj(Ctx.atom(), Ctx.atom());
+
+  auto Expected = R"((or
+B0
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Implication) {
+  // B0 => B1, implemented as !B0 v B1
+  ConstraintContext Ctx;
+  auto B = Ctx.disj(Ctx.neg(Ctx.atom()), Ctx.atom());
+
+  auto Expected = R"((or
+(not
+B0)
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Iff) {
+  // B0 <=> B1, implemented as (!B0 v B1) ^ (B0 v !B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto B = Ctx.conj(Ctx.disj(Ctx.neg(B0), B1), Ctx.disj(B0, Ctx.neg(B1)));
+
+  auto Expected = R"((and
+(or
+(not
+B0)
+B1)
+(or
+B0
+(not
+B1";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Xor) {
+  // (B0 ^ !B1) V (!B0 ^ B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto B = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
+
+  auto Expected = R"((or
+(and
+B0
+(not
+B1))
+(and
+(not
+B0)
+B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, NestedBoolean) {
+  // B0 ^ (B1 v (B2 ^ (B3 v B4)))
+  ConstraintContext Ctx;
+  auto B = Ctx.conj(
+  Ctx.atom(),
+  Ctx.disj(Ctx.atom(),
+   Ctx.conj(Ctx.atom(), Ctx.disj(Ctx.atom(), Ctx.atom();
+
+  auto Expected = R"((and
+B0
+(or
+B1
+(and
+B2
+(or
+B3
+B4)";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, AtomicBooleanWithName) {
+  // True
+  ConstraintContext Ctx;
+  auto True = cast(Ctx.atom());
+  auto B = True;
+
+  auto Expected = R"(True)";
+  EXPECT_THAT(debugString(*B, {{True, "True"}}), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, ComplexBooleanWithNames) {
+  // (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  ConstraintContext Ctx;
+  auto Cond = cast(Ctx.atom());
+  auto Then = cast(Ctx.atom());
+  auto Else = cast(Ctx.atom());

[PATCH] D129548: [clang][dataflow] Generate readable form of input and output of satisfiability checking for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 443983.
wyt added a comment.

Change propagated from parent patch.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129548/new/

https://reviews.llvm.org/D129548

Files:
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -9,6 +9,7 @@
 #include "clang/Analysis/FlowSensitive/DebugSupport.h"
 #include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -187,4 +188,242 @@
   StrEq(Expected));
 }
 
+Solver::Result CheckSAT(std::vector Constraints) {
+  llvm::DenseSet ConstraintsSet(Constraints.begin(),
+ Constraints.end());
+  return WatchedLiteralsSolver().solve(std::move(ConstraintsSet));
+}
+
+TEST(SATCheckDebugStringTest, AtomicBoolean) {
+  // B0
+  ConstraintContext Ctx;
+  std::vector Constraints({Ctx.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+Satisfiable.
+
+B0=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, AtomicBooleanAndNegation) {
+  // B0, !B0
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  std::vector Constraints({B0, Ctx.neg(B0)});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(not
+B0)
+
+Unsatisfiable.
+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, MultipleAtomicBooleans) {
+  // B0, B1
+  ConstraintContext Ctx;
+  std::vector Constraints({Ctx.atom(), Ctx.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+B1
+
+Satisfiable.
+
+B0=True
+B1=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Implication) {
+  // B0, B0 => B1
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto Impl = Ctx.disj(Ctx.neg(B0), B1);
+  std::vector Constraints({B0, Impl});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(or
+(not
+B0)
+B1)
+
+Satisfiable.
+
+B0=True
+B1=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Iff) {
+  // B0, B0 <=> B1
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto Iff = Ctx.conj(Ctx.disj(Ctx.neg(B0), B1), Ctx.disj(B0, Ctx.neg(B1)));
+  std::vector Constraints({B0, Iff});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(and
+(or
+(not
+B0)
+B1)
+(or
+B0
+(not
+B1)))
+
+Satisfiable.
+
+B0=True
+B1=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Xor) {
+  // B0, XOR(B0, B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto XOR = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
+  std::vector Constraints({B0, XOR});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(or
+(and
+B0
+(not
+B1))
+(and
+(not
+B0)
+B1))
+
+Satisfiable.
+
+B0=True
+B1=False
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, ComplexBooleanWithNames) {
+  // Cond, (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  ConstraintContext Ctx;
+  auto Cond = cast(Ctx.atom());
+  auto Then = cast(Ctx.atom());
+  auto Else = cast(Ctx.atom());
+  auto B = Ctx.disj(Ctx.conj(Cond, Ctx.conj(Then, Ctx.neg(Else))),
+Ctx.conj(Ctx.neg(Cond), Ctx.conj(Ctx.neg(Then), Else)));
+  std::vector Constraints({Cond, B});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+Cond
+
+(or
+(and
+Cond
+(and
+Then
+(not
+Else)))
+(and
+(not
+Cond)
+(and
+(not
+Then)
+Else)))
+
+Satisfiable.
+
+Cond=True
+Else=False
+Then=True
+)";
+  EXPECT_THAT(debugString(Constraints, Result,
+  {{Cond, "Cond"}, {Then, "Then"}, {Else, "Else"}}),
+  StrEq(Exp

[PATCH] D129546: [clang][dataflow] Refactor boolean creation as a test utility.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 444026.
wyt marked an inline comment as done.
wyt added a comment.

Address comments: add used import, remove unused imports.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129546/new/

https://reviews.llvm.org/D129546

Files:
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -13,6 +13,13 @@
 #ifndef LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_
 #define LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_
 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Stmt.h"
@@ -30,17 +37,10 @@
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/Error.h"
-#include "llvm/Support/ErrorHandling.h"
 #include "llvm/Testing/Support/Annotations.h"
-#include 
-#include 
-#include 
-#include 
-#include 
 
 namespace clang {
 namespace dataflow {
@@ -220,6 +220,49 @@
 ///  `Name` must be unique in `ASTCtx`.
 const ValueDecl *findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name);
 
+/// Creates and owns constraints which are boolean values.
+class ConstraintContext {
+public:
+  // Creates an atomic boolean value.
+  BoolValue *atom() {
+Vals.push_back(std::make_unique());
+return Vals.back().get();
+  }
+
+  // Creates a boolean conjunction value.
+  BoolValue *conj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+Vals.push_back(
+std::make_unique(*LeftSubVal, *RightSubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean disjunction value.
+  BoolValue *disj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+Vals.push_back(
+std::make_unique(*LeftSubVal, *RightSubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean negation value.
+  BoolValue *neg(BoolValue *SubVal) {
+Vals.push_back(std::make_unique(*SubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean implication value.
+  BoolValue *impl(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+return disj(neg(LeftSubVal), RightSubVal);
+  }
+
+  // Creates a boolean biconditional value.
+  BoolValue *iff(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
+  }
+
+private:
+  std::vector> Vals;
+};
+
 } // namespace test
 } // namespace dataflow
 } // namespace clang
Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -7,6 +7,7 @@
 //===--===//
 
 #include "clang/Analysis/FlowSensitive/Solver.h"
+#include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
@@ -20,73 +21,33 @@
 using namespace clang;
 using namespace dataflow;
 
+using test::ConstraintContext;
 using testing::_;
 using testing::AnyOf;
 using testing::Optional;
 using testing::Pair;
 using testing::UnorderedElementsAre;
 
-class SolverTest : public ::testing::Test {
-protected:
-  // Checks if the conjunction of `Vals` is satisfiable and returns the
-  // corresponding result.
-  Solver::Result solve(llvm::DenseSet Vals) {
-return WatchedLiteralsSolver().solve(std::move(Vals));
-  }
-
-  // Creates an atomic boolean value.
-  BoolValue *atom() {
-Vals.push_back(std::make_unique());
-return Vals.back().get();
-  }
-
-  // Creates a boolean conjunction value.
-  BoolValue *conj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-Vals.push_back(
-std::make_unique(*LeftSubVal, *RightSubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean disjunction value.
-  BoolValue *disj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-Vals.push_back(
-std::make_unique(*LeftSubVal, *RightSubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean negation value.
-  BoolValue *neg(BoolValue *SubVal) {
-Vals.push_back(std::make_unique(*SubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean implication value.
-  BoolValue *impl(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-return disj(neg(LeftSubVal), RightSubVal);
-  }
-
-  // Creates a boolean biconditional value.
-  BoolValue *iff(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-return 

[PATCH] D129547: [clang][dataflow] Generate readable form of boolean values for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 444029.
wyt marked an inline comment as done.
wyt added a comment.

Address comment on renaming parameter. Add const qualifier to BoolValue input 
to debugString.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129547/new/

https://reviews.llvm.org/D129547

Files:
  clang/docs/tools/clang-formatted-files.txt
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/CMakeLists.txt
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
  llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn

Index: llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
===
--- llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
+++ llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
@@ -12,5 +12,6 @@
 "Transfer.cpp",
 "TypeErasedDataflowAnalysis.cpp",
 "WatchedLiteralsSolver.cpp",
+"DebugSupport.cpp",
   ]
 }
Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -0,0 +1,190 @@
+//===- unittests/Analysis/FlowSensitive/DebugSupportTest.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 "clang/Analysis/FlowSensitive/DebugSupport.h"
+#include "TestingSupport.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace clang;
+using namespace dataflow;
+
+using test::ConstraintContext;
+using testing::StrEq;
+
+TEST(BoolValueDebugStringTest, AtomicBoolean) {
+  // B0
+  ConstraintContext Ctx;
+  auto B = Ctx.atom();
+
+  auto Expected = R"(B0)";
+  debugString(*B);
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Negation) {
+  // !B0
+  ConstraintContext Ctx;
+  auto B = Ctx.neg(Ctx.atom());
+
+  auto Expected = R"((not
+B0))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Conjunction) {
+  // B0 ^ B1
+  ConstraintContext Ctx;
+  auto B = Ctx.conj(Ctx.atom(), Ctx.atom());
+
+  auto Expected = R"((and
+B0
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Disjunction) {
+  // B0 v B1
+  ConstraintContext Ctx;
+  auto B = Ctx.disj(Ctx.atom(), Ctx.atom());
+
+  auto Expected = R"((or
+B0
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Implication) {
+  // B0 => B1, implemented as !B0 v B1
+  ConstraintContext Ctx;
+  auto B = Ctx.disj(Ctx.neg(Ctx.atom()), Ctx.atom());
+
+  auto Expected = R"((or
+(not
+B0)
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Iff) {
+  // B0 <=> B1, implemented as (!B0 v B1) ^ (B0 v !B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto B = Ctx.conj(Ctx.disj(Ctx.neg(B0), B1), Ctx.disj(B0, Ctx.neg(B1)));
+
+  auto Expected = R"((and
+(or
+(not
+B0)
+B1)
+(or
+B0
+(not
+B1";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Xor) {
+  // (B0 ^ !B1) V (!B0 ^ B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto B = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
+
+  auto Expected = R"((or
+(and
+B0
+(not
+B1))
+(and
+(not
+B0)
+B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, NestedBoolean) {
+  // B0 ^ (B1 v (B2 ^ (B3 v B4)))
+  ConstraintContext Ctx;
+  auto B = Ctx.conj(
+  Ctx.atom(),
+  Ctx.disj(Ctx.atom(),
+   Ctx.conj(Ctx.atom(), Ctx.disj(Ctx.atom(), Ctx.atom();
+
+  auto Expected = R"((and
+B0
+(or
+B1
+(and
+B2
+(or
+B3
+B4)";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, AtomicBooleanWithName) {
+  // True
+  ConstraintContext Ctx;
+  auto True = cast(Ctx.atom());
+  auto B = True;
+
+  auto Expected = R"(True)";
+  EXPECT_THAT(debugString(*B, {{True, "True"}}), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, ComplexBooleanWithNames) {
+  // (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  ConstraintContext Ctx;
+  auto Cond = cast(Ctx.a

[PATCH] D129548: [clang][dataflow] Generate readable form of input and output of satisfiability checking for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 444030.
wyt marked 3 inline comments as done.
wyt added a comment.

Address comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129548/new/

https://reviews.llvm.org/D129548

Files:
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -9,6 +9,7 @@
 #include "clang/Analysis/FlowSensitive/DebugSupport.h"
 #include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -187,4 +188,242 @@
   StrEq(Expected));
 }
 
+Solver::Result CheckSAT(std::vector Constraints) {
+  llvm::DenseSet ConstraintsSet(Constraints.begin(),
+ Constraints.end());
+  return WatchedLiteralsSolver().solve(std::move(ConstraintsSet));
+}
+
+TEST(SATCheckDebugStringTest, AtomicBoolean) {
+  // B0
+  ConstraintContext Ctx;
+  std::vector Constraints({Ctx.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+Satisfiable.
+
+B0 = True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, AtomicBooleanAndNegation) {
+  // B0, !B0
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  std::vector Constraints({B0, Ctx.neg(B0)});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(not
+B0)
+
+Unsatisfiable.
+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, MultipleAtomicBooleans) {
+  // B0, B1
+  ConstraintContext Ctx;
+  std::vector Constraints({Ctx.atom(), Ctx.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+B1
+
+Satisfiable.
+
+B0 = True
+B1 = True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Implication) {
+  // B0, B0 => B1
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto Impl = Ctx.disj(Ctx.neg(B0), B1);
+  std::vector Constraints({B0, Impl});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(or
+(not
+B0)
+B1)
+
+Satisfiable.
+
+B0 = True
+B1 = True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Iff) {
+  // B0, B0 <=> B1
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto Iff = Ctx.conj(Ctx.disj(Ctx.neg(B0), B1), Ctx.disj(B0, Ctx.neg(B1)));
+  std::vector Constraints({B0, Iff});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(and
+(or
+(not
+B0)
+B1)
+(or
+B0
+(not
+B1)))
+
+Satisfiable.
+
+B0 = True
+B1 = True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Xor) {
+  // B0, XOR(B0, B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto XOR = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
+  std::vector Constraints({B0, XOR});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(or
+(and
+B0
+(not
+B1))
+(and
+(not
+B0)
+B1))
+
+Satisfiable.
+
+B0 = True
+B1 = False
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, ComplexBooleanWithNames) {
+  // Cond, (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  ConstraintContext Ctx;
+  auto Cond = cast(Ctx.atom());
+  auto Then = cast(Ctx.atom());
+  auto Else = cast(Ctx.atom());
+  auto B = Ctx.disj(Ctx.conj(Cond, Ctx.conj(Then, Ctx.neg(Else))),
+Ctx.conj(Ctx.neg(Cond), Ctx.conj(Ctx.neg(Then), Else)));
+  std::vector Constraints({Cond, B});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+Cond
+
+(or
+(and
+Cond
+(and
+Then
+(not
+Else)))
+(and
+(not
+Cond)
+(and
+(not
+Then)
+Else)))
+
+Satisfiable.
+
+Cond = True
+Else = False
+Then = True
+)";
+  EXPECT_THAT(debugString(Constraints, Result,
+  {{Cond, "Cond"}, {Then, "Then"},

[PATCH] D129548: [clang][dataflow] Generate readable form of input and output of satisfiability checking for debugging purposes.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt added a comment.






Comment at: clang/lib/Analysis/FlowSensitive/DebugSupport.cpp:119-133
+std::vector> LinesData;
+for (auto &AtomAssignment : AtomAssignments) {
+  auto Name = getAtomName(AtomAssignment.first);
+  MaxNameLength = std::max(MaxNameLength, Name.size());
+  LinesData.push_back({Name, debugString(AtomAssignment.second)});
+}
+llvm::sort(LinesData.begin(), LinesData.end());

xazax.hun wrote:
> In its current form, I think you could create the final strings in one step 
> and sort those strings instead of the pairs. Or do we expect alignment to 
> mess up the order in that case?
> In its current form, I think you could create the final strings in one step 
> and sort those strings instead of the pairs. Or do we expect alignment to 
> mess up the order in that case?

Removed `LinesData` and the intermediate step of storing a pair of strings. 
There is still a loop at the start of the function to find the max name length 
for alignment purposes. 



Comment at: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp:230
+
+Unsatisfiable.
+

@xazax.hun 
> I don't see a test case for `Unsatisfiable` constraints.
Here.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129548/new/

https://reviews.llvm.org/D129548

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D129546: [clang][dataflow] Refactor boolean creation as a test utility.

2022-07-12 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 444031.
wyt added a comment.

Removed unused imports in SolverTest.cpp


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129546/new/

https://reviews.llvm.org/D129546

Files:
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -13,6 +13,13 @@
 #ifndef LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_
 #define LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_
 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Stmt.h"
@@ -30,17 +37,10 @@
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/Error.h"
-#include "llvm/Support/ErrorHandling.h"
 #include "llvm/Testing/Support/Annotations.h"
-#include 
-#include 
-#include 
-#include 
-#include 
 
 namespace clang {
 namespace dataflow {
@@ -220,6 +220,49 @@
 ///  `Name` must be unique in `ASTCtx`.
 const ValueDecl *findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name);
 
+/// Creates and owns constraints which are boolean values.
+class ConstraintContext {
+public:
+  // Creates an atomic boolean value.
+  BoolValue *atom() {
+Vals.push_back(std::make_unique());
+return Vals.back().get();
+  }
+
+  // Creates a boolean conjunction value.
+  BoolValue *conj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+Vals.push_back(
+std::make_unique(*LeftSubVal, *RightSubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean disjunction value.
+  BoolValue *disj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+Vals.push_back(
+std::make_unique(*LeftSubVal, *RightSubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean negation value.
+  BoolValue *neg(BoolValue *SubVal) {
+Vals.push_back(std::make_unique(*SubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean implication value.
+  BoolValue *impl(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+return disj(neg(LeftSubVal), RightSubVal);
+  }
+
+  // Creates a boolean biconditional value.
+  BoolValue *iff(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
+  }
+
+private:
+  std::vector> Vals;
+};
+
 } // namespace test
 } // namespace dataflow
 } // namespace clang
Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -6,87 +6,47 @@
 //
 //===--===//
 
+#include 
+
+#include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Solver.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include 
-#include 
-#include 
 
 namespace {
 
 using namespace clang;
 using namespace dataflow;
 
+using test::ConstraintContext;
 using testing::_;
 using testing::AnyOf;
 using testing::Optional;
 using testing::Pair;
 using testing::UnorderedElementsAre;
 
-class SolverTest : public ::testing::Test {
-protected:
-  // Checks if the conjunction of `Vals` is satisfiable and returns the
-  // corresponding result.
-  Solver::Result solve(llvm::DenseSet Vals) {
-return WatchedLiteralsSolver().solve(std::move(Vals));
-  }
-
-  // Creates an atomic boolean value.
-  BoolValue *atom() {
-Vals.push_back(std::make_unique());
-return Vals.back().get();
-  }
-
-  // Creates a boolean conjunction value.
-  BoolValue *conj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-Vals.push_back(
-std::make_unique(*LeftSubVal, *RightSubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean disjunction value.
-  BoolValue *disj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-Vals.push_back(
-std::make_unique(*LeftSubVal, *RightSubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean negation value.
-  BoolValue *neg(BoolValue *SubVal) {
-Vals.push_back(std::make_unique(*SubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean implication value.
-  BoolValue *impl(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-return disj(neg(LeftSubVal), RightSubVal);
-  }
-
-  // Creates a boolean biconditional value.
-  BoolValue *iff(BoolValue *LeftSubVal, BoolValue *RightSu

[PATCH] D129546: [clang][dataflow] Refactor boolean creation as a test utility.

2022-07-13 Thread weiyi via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG632de855a042: [clang][dataflow] Refactor boolean creation as 
a test utility. (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129546/new/

https://reviews.llvm.org/D129546

Files:
  clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -13,6 +13,13 @@
 #ifndef LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_
 #define LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_TESTING_SUPPORT_H_
 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Stmt.h"
@@ -30,17 +37,10 @@
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/Error.h"
-#include "llvm/Support/ErrorHandling.h"
 #include "llvm/Testing/Support/Annotations.h"
-#include 
-#include 
-#include 
-#include 
-#include 
 
 namespace clang {
 namespace dataflow {
@@ -220,6 +220,49 @@
 ///  `Name` must be unique in `ASTCtx`.
 const ValueDecl *findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name);
 
+/// Creates and owns constraints which are boolean values.
+class ConstraintContext {
+public:
+  // Creates an atomic boolean value.
+  BoolValue *atom() {
+Vals.push_back(std::make_unique());
+return Vals.back().get();
+  }
+
+  // Creates a boolean conjunction value.
+  BoolValue *conj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+Vals.push_back(
+std::make_unique(*LeftSubVal, *RightSubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean disjunction value.
+  BoolValue *disj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+Vals.push_back(
+std::make_unique(*LeftSubVal, *RightSubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean negation value.
+  BoolValue *neg(BoolValue *SubVal) {
+Vals.push_back(std::make_unique(*SubVal));
+return Vals.back().get();
+  }
+
+  // Creates a boolean implication value.
+  BoolValue *impl(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+return disj(neg(LeftSubVal), RightSubVal);
+  }
+
+  // Creates a boolean biconditional value.
+  BoolValue *iff(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
+return conj(impl(LeftSubVal, RightSubVal), impl(RightSubVal, LeftSubVal));
+  }
+
+private:
+  std::vector> Vals;
+};
+
 } // namespace test
 } // namespace dataflow
 } // namespace clang
Index: clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SolverTest.cpp
@@ -6,87 +6,47 @@
 //
 //===--===//
 
+#include 
+
+#include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Solver.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include 
-#include 
-#include 
 
 namespace {
 
 using namespace clang;
 using namespace dataflow;
 
+using test::ConstraintContext;
 using testing::_;
 using testing::AnyOf;
 using testing::Optional;
 using testing::Pair;
 using testing::UnorderedElementsAre;
 
-class SolverTest : public ::testing::Test {
-protected:
-  // Checks if the conjunction of `Vals` is satisfiable and returns the
-  // corresponding result.
-  Solver::Result solve(llvm::DenseSet Vals) {
-return WatchedLiteralsSolver().solve(std::move(Vals));
-  }
-
-  // Creates an atomic boolean value.
-  BoolValue *atom() {
-Vals.push_back(std::make_unique());
-return Vals.back().get();
-  }
-
-  // Creates a boolean conjunction value.
-  BoolValue *conj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-Vals.push_back(
-std::make_unique(*LeftSubVal, *RightSubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean disjunction value.
-  BoolValue *disj(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-Vals.push_back(
-std::make_unique(*LeftSubVal, *RightSubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean negation value.
-  BoolValue *neg(BoolValue *SubVal) {
-Vals.push_back(std::make_unique(*SubVal));
-return Vals.back().get();
-  }
-
-  // Creates a boolean implication value.
-  BoolValue *impl(BoolValue *LeftSubVal, BoolValue *RightSubVal) {
-return disj(ne

[PATCH] D129547: [clang][dataflow] Generate readable form of boolean values for debugging purposes.

2022-07-13 Thread weiyi via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGc9666d2339e5: [clang][dataflow] Generate readable form of 
boolean values. (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129547/new/

https://reviews.llvm.org/D129547

Files:
  clang/docs/tools/clang-formatted-files.txt
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/CMakeLists.txt
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
  llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn

Index: llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
===
--- llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
+++ llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/BUILD.gn
@@ -12,5 +12,6 @@
 "Transfer.cpp",
 "TypeErasedDataflowAnalysis.cpp",
 "WatchedLiteralsSolver.cpp",
+"DebugSupport.cpp",
   ]
 }
Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -0,0 +1,190 @@
+//===- unittests/Analysis/FlowSensitive/DebugSupportTest.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 "clang/Analysis/FlowSensitive/DebugSupport.h"
+#include "TestingSupport.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using namespace clang;
+using namespace dataflow;
+
+using test::ConstraintContext;
+using testing::StrEq;
+
+TEST(BoolValueDebugStringTest, AtomicBoolean) {
+  // B0
+  ConstraintContext Ctx;
+  auto B = Ctx.atom();
+
+  auto Expected = R"(B0)";
+  debugString(*B);
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Negation) {
+  // !B0
+  ConstraintContext Ctx;
+  auto B = Ctx.neg(Ctx.atom());
+
+  auto Expected = R"((not
+B0))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Conjunction) {
+  // B0 ^ B1
+  ConstraintContext Ctx;
+  auto B = Ctx.conj(Ctx.atom(), Ctx.atom());
+
+  auto Expected = R"((and
+B0
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Disjunction) {
+  // B0 v B1
+  ConstraintContext Ctx;
+  auto B = Ctx.disj(Ctx.atom(), Ctx.atom());
+
+  auto Expected = R"((or
+B0
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Implication) {
+  // B0 => B1, implemented as !B0 v B1
+  ConstraintContext Ctx;
+  auto B = Ctx.disj(Ctx.neg(Ctx.atom()), Ctx.atom());
+
+  auto Expected = R"((or
+(not
+B0)
+B1))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Iff) {
+  // B0 <=> B1, implemented as (!B0 v B1) ^ (B0 v !B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto B = Ctx.conj(Ctx.disj(Ctx.neg(B0), B1), Ctx.disj(B0, Ctx.neg(B1)));
+
+  auto Expected = R"((and
+(or
+(not
+B0)
+B1)
+(or
+B0
+(not
+B1";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, Xor) {
+  // (B0 ^ !B1) V (!B0 ^ B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto B = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
+
+  auto Expected = R"((or
+(and
+B0
+(not
+B1))
+(and
+(not
+B0)
+B1)))";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, NestedBoolean) {
+  // B0 ^ (B1 v (B2 ^ (B3 v B4)))
+  ConstraintContext Ctx;
+  auto B = Ctx.conj(
+  Ctx.atom(),
+  Ctx.disj(Ctx.atom(),
+   Ctx.conj(Ctx.atom(), Ctx.disj(Ctx.atom(), Ctx.atom();
+
+  auto Expected = R"((and
+B0
+(or
+B1
+(and
+B2
+(or
+B3
+B4)";
+  EXPECT_THAT(debugString(*B), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, AtomicBooleanWithName) {
+  // True
+  ConstraintContext Ctx;
+  auto True = cast(Ctx.atom());
+  auto B = True;
+
+  auto Expected = R"(True)";
+  EXPECT_THAT(debugString(*B, {{True, "True"}}), StrEq(Expected));
+}
+
+TEST(BoolValueDebugStringTest, ComplexBooleanWithNames) {
+  // (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  Con

[PATCH] D129568: [clang][dataflow] Rename `Status` field in a `Solver::Result` struct to `SATCheckStatus`.

2022-07-13 Thread weiyi via Phabricator via cfe-commits
This revision was automatically updated to reflect the committed changes.
Closed by commit rG3ec2b2f4ec32: [clang][dataflow] Rename `Status` field in a 
`Solver::Result` struct to… (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129568/new/

https://reviews.llvm.org/D129568

Files:
  clang/include/clang/Analysis/FlowSensitive/Solver.h


Index: clang/include/clang/Analysis/FlowSensitive/Solver.h
===
--- clang/include/clang/Analysis/FlowSensitive/Solver.h
+++ clang/include/clang/Analysis/FlowSensitive/Solver.h
@@ -60,7 +60,7 @@
 
 /// Returns the status of satisfiability checking on the queried boolean
 /// formula.
-Status getStatus() const { return Status; }
+Status getStatus() const { return SATCheckStatus; }
 
 /// Returns a truth assignment to boolean values that satisfies the queried
 /// boolean formula if available. Otherwise, an empty optional is returned.
@@ -71,11 +71,11 @@
 
   private:
 Result(
-enum Status Status,
+enum Status SATCheckStatus,
 llvm::Optional> Solution)
-: Status(Status), Solution(std::move(Solution)) {}
+: SATCheckStatus(SATCheckStatus), Solution(std::move(Solution)) {}
 
-Status Status;
+Status SATCheckStatus;
 llvm::Optional> Solution;
   };
 


Index: clang/include/clang/Analysis/FlowSensitive/Solver.h
===
--- clang/include/clang/Analysis/FlowSensitive/Solver.h
+++ clang/include/clang/Analysis/FlowSensitive/Solver.h
@@ -60,7 +60,7 @@
 
 /// Returns the status of satisfiability checking on the queried boolean
 /// formula.
-Status getStatus() const { return Status; }
+Status getStatus() const { return SATCheckStatus; }
 
 /// Returns a truth assignment to boolean values that satisfies the queried
 /// boolean formula if available. Otherwise, an empty optional is returned.
@@ -71,11 +71,11 @@
 
   private:
 Result(
-enum Status Status,
+enum Status SATCheckStatus,
 llvm::Optional> Solution)
-: Status(Status), Solution(std::move(Solution)) {}
+: SATCheckStatus(SATCheckStatus), Solution(std::move(Solution)) {}
 
-Status Status;
+Status SATCheckStatus;
 llvm::Optional> Solution;
   };
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D129548: [clang][dataflow] Generate readable form of input and output of satisfiability checking for debugging purposes.

2022-07-13 Thread weiyi via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rGb8d83e8004e4: [clang][dataflow] Generate readable form of 
input and output of satisfiability… (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129548/new/

https://reviews.llvm.org/D129548

Files:
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
  clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/DebugSupportTest.cpp
@@ -9,6 +9,7 @@
 #include "clang/Analysis/FlowSensitive/DebugSupport.h"
 #include "TestingSupport.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
+#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -187,4 +188,242 @@
   StrEq(Expected));
 }
 
+Solver::Result CheckSAT(std::vector Constraints) {
+  llvm::DenseSet ConstraintsSet(Constraints.begin(),
+ Constraints.end());
+  return WatchedLiteralsSolver().solve(std::move(ConstraintsSet));
+}
+
+TEST(SATCheckDebugStringTest, AtomicBoolean) {
+  // B0
+  ConstraintContext Ctx;
+  std::vector Constraints({Ctx.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+Satisfiable.
+
+B0 = True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, AtomicBooleanAndNegation) {
+  // B0, !B0
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  std::vector Constraints({B0, Ctx.neg(B0)});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(not
+B0)
+
+Unsatisfiable.
+
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, MultipleAtomicBooleans) {
+  // B0, B1
+  ConstraintContext Ctx;
+  std::vector Constraints({Ctx.atom(), Ctx.atom()});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+B1
+
+Satisfiable.
+
+B0 = True
+B1 = True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Implication) {
+  // B0, B0 => B1
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto Impl = Ctx.disj(Ctx.neg(B0), B1);
+  std::vector Constraints({B0, Impl});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(or
+(not
+B0)
+B1)
+
+Satisfiable.
+
+B0 = True
+B1 = True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Iff) {
+  // B0, B0 <=> B1
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto Iff = Ctx.conj(Ctx.disj(Ctx.neg(B0), B1), Ctx.disj(B0, Ctx.neg(B1)));
+  std::vector Constraints({B0, Iff});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(and
+(or
+(not
+B0)
+B1)
+(or
+B0
+(not
+B1)))
+
+Satisfiable.
+
+B0 = True
+B1 = True
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, Xor) {
+  // B0, XOR(B0, B1)
+  ConstraintContext Ctx;
+  auto B0 = Ctx.atom();
+  auto B1 = Ctx.atom();
+  auto XOR = Ctx.disj(Ctx.conj(B0, Ctx.neg(B1)), Ctx.conj(Ctx.neg(B0), B1));
+  std::vector Constraints({B0, XOR});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+B0
+
+(or
+(and
+B0
+(not
+B1))
+(and
+(not
+B0)
+B1))
+
+Satisfiable.
+
+B0 = True
+B1 = False
+)";
+  EXPECT_THAT(debugString(Constraints, Result), StrEq(Expected));
+}
+
+TEST(SATCheckDebugStringTest, ComplexBooleanWithNames) {
+  // Cond, (Cond ^ Then ^ !Else) v (!Cond ^ !Then ^ Else)
+  ConstraintContext Ctx;
+  auto Cond = cast(Ctx.atom());
+  auto Then = cast(Ctx.atom());
+  auto Else = cast(Ctx.atom());
+  auto B = Ctx.disj(Ctx.conj(Cond, Ctx.conj(Then, Ctx.neg(Else))),
+Ctx.conj(Ctx.neg(Cond), Ctx.conj(Ctx.neg(Then), Else)));
+  std::vector Constraints({Cond, B});
+  auto Result = CheckSAT(Constraints);
+
+  auto Expected = R"(
+Constraints
+
+Cond
+
+(or
+(and
+Cond
+(and
+Then
+(not
+Else)))
+(and
+(not
+Cond)
+(and
+(not
+Then)
+Else)))
+
+Satisfiable.
+
+Cond = Tr

[PATCH] D131065: [clang][dataflow] Store DeclContext of block being analysed in Environment if available.

2022-08-11 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 451754.
wyt added a comment.

Initialise DeclCtx field with nullptr.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131065/new/

https://reviews.llvm.org/D131065

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -154,7 +154,7 @@
 : DACtx(&DACtx), FlowConditionToken(&DACtx.makeFlowConditionToken()) {}
 
 Environment::Environment(const Environment &Other)
-: DACtx(Other.DACtx), ReturnLoc(Other.ReturnLoc),
+: DACtx(Other.DACtx), DeclCtx(Other.DeclCtx), ReturnLoc(Other.ReturnLoc),
   ThisPointeeLoc(Other.ThisPointeeLoc), DeclToLoc(Other.DeclToLoc),
   ExprToLoc(Other.ExprToLoc), LocToVal(Other.LocToVal),
   MemberLocToStruct(Other.MemberLocToStruct),
@@ -168,9 +168,11 @@
 }
 
 Environment::Environment(DataflowAnalysisContext &DACtx,
- const DeclContext &DeclCtx)
+ const DeclContext &DeclCtxArg)
 : Environment(DACtx) {
-  if (const auto *FuncDecl = dyn_cast(&DeclCtx)) {
+  setDeclCtx(&DeclCtxArg);
+
+  if (const auto *FuncDecl = dyn_cast(DeclCtx)) {
 assert(FuncDecl->getBody() != nullptr);
 initGlobalVars(*FuncDecl->getBody(), *this);
 for (const auto *ParamDecl : FuncDecl->parameters()) {
@@ -185,7 +187,7 @@
 ReturnLoc = &createStorageLocation(ReturnType);
   }
 
-  if (const auto *MethodDecl = dyn_cast(&DeclCtx)) {
+  if (const auto *MethodDecl = dyn_cast(DeclCtx)) {
 auto *Parent = MethodDecl->getParent();
 assert(Parent != nullptr);
 if (Parent->isLambda())
@@ -210,6 +212,9 @@
 
   const auto *FuncDecl = Call->getDirectCallee();
   assert(FuncDecl != nullptr);
+
+  Env.setDeclCtx(FuncDecl);
+
   // FIXME: In order to allow the callee to reference globals, we probably need
   // to call `initGlobalVars` here in some way.
 
@@ -252,12 +257,12 @@
 
 void Environment::popCall(const Environment &CalleeEnv) {
   // We ignore `DACtx` because it's already the same in both. We don't want the
-  // callee's `ReturnLoc` or `ThisPointeeLoc`. We don't bring back `DeclToLoc`
-  // and `ExprToLoc` because we want to be able to later analyze the same callee
-  // in a different context, and `setStorageLocation` requires there to not
-  // already be a storage location assigned. Conceptually, these maps capture
-  // information from the local scope, so when popping that scope, we do not
-  // propagate the maps.
+  // callee's `DeclCtx`, `ReturnLoc` or `ThisPointeeLoc`. We don't bring back
+  // `DeclToLoc` and `ExprToLoc` because we want to be able to later analyze the
+  // same callee in a different context, and `setStorageLocation` requires there
+  // to not already be a storage location assigned. Conceptually, these maps
+  // capture information from the local scope, so when popping that scope, we do
+  // not propagate the maps.
   this->LocToVal = std::move(CalleeEnv.LocToVal);
   this->MemberLocToStruct = std::move(CalleeEnv.MemberLocToStruct);
   this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);
@@ -304,11 +309,13 @@
   assert(DACtx == Other.DACtx);
   assert(ReturnLoc == Other.ReturnLoc);
   assert(ThisPointeeLoc == Other.ThisPointeeLoc);
+  assert(DeclCtx == Other.DeclCtx);
 
   auto Effect = LatticeJoinEffect::Unchanged;
 
   Environment JoinedEnv(*DACtx);
 
+  JoinedEnv.setDeclCtx(DeclCtx);
   JoinedEnv.ReturnLoc = ReturnLoc;
   JoinedEnv.ThisPointeeLoc = ThisPointeeLoc;
 
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -347,6 +347,13 @@
   /// imply that `Val` is true.
   bool flowConditionImplies(BoolValue &Val) const;
 
+  /// Returns the `DeclContext` of the block being analysed, if any. Otherwise,
+  /// returns null.
+  const DeclContext *getDeclCtx() { return DeclCtx; }
+
+  /// Sets the `DeclContext` of the block being analysed.
+  void setDeclCtx(const DeclContext *Ctx) { DeclCtx = Ctx; }
+
   /// Returns the `ControlFlowContext` registered for `F`, if any. Otherwise,
   /// returns null.
   const ControlFlowContext *getControlFlowContext(const FunctionDecl *F) {
@@ -377,6 +384,9 @@
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
+  // `DeclContext` of the block being analysed if provided.
+  const DeclContext *DeclCtx = nullptr;
+
   // In a properly initialized `Environment`, `ReturnLoc` should only be null if
   // its `DeclContext` could not be cast to a `FunctionDecl`.
   StorageLocatio

[PATCH] D131065: [clang][dataflow] Store DeclContext of block being analysed in Environment if available.

2022-08-11 Thread weiyi via Phabricator via cfe-commits
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG2cb51449f0d9: [clang][dataflow] Store DeclContext of block 
being analysed in Environment if… (authored by wyt).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131065/new/

https://reviews.llvm.org/D131065

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
  clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -154,7 +154,7 @@
 : DACtx(&DACtx), FlowConditionToken(&DACtx.makeFlowConditionToken()) {}
 
 Environment::Environment(const Environment &Other)
-: DACtx(Other.DACtx), ReturnLoc(Other.ReturnLoc),
+: DACtx(Other.DACtx), DeclCtx(Other.DeclCtx), ReturnLoc(Other.ReturnLoc),
   ThisPointeeLoc(Other.ThisPointeeLoc), DeclToLoc(Other.DeclToLoc),
   ExprToLoc(Other.ExprToLoc), LocToVal(Other.LocToVal),
   MemberLocToStruct(Other.MemberLocToStruct),
@@ -168,9 +168,11 @@
 }
 
 Environment::Environment(DataflowAnalysisContext &DACtx,
- const DeclContext &DeclCtx)
+ const DeclContext &DeclCtxArg)
 : Environment(DACtx) {
-  if (const auto *FuncDecl = dyn_cast(&DeclCtx)) {
+  setDeclCtx(&DeclCtxArg);
+
+  if (const auto *FuncDecl = dyn_cast(DeclCtx)) {
 assert(FuncDecl->getBody() != nullptr);
 initGlobalVars(*FuncDecl->getBody(), *this);
 for (const auto *ParamDecl : FuncDecl->parameters()) {
@@ -185,7 +187,7 @@
 ReturnLoc = &createStorageLocation(ReturnType);
   }
 
-  if (const auto *MethodDecl = dyn_cast(&DeclCtx)) {
+  if (const auto *MethodDecl = dyn_cast(DeclCtx)) {
 auto *Parent = MethodDecl->getParent();
 assert(Parent != nullptr);
 if (Parent->isLambda())
@@ -210,6 +212,9 @@
 
   const auto *FuncDecl = Call->getDirectCallee();
   assert(FuncDecl != nullptr);
+
+  Env.setDeclCtx(FuncDecl);
+
   // FIXME: In order to allow the callee to reference globals, we probably need
   // to call `initGlobalVars` here in some way.
 
@@ -252,12 +257,12 @@
 
 void Environment::popCall(const Environment &CalleeEnv) {
   // We ignore `DACtx` because it's already the same in both. We don't want the
-  // callee's `ReturnLoc` or `ThisPointeeLoc`. We don't bring back `DeclToLoc`
-  // and `ExprToLoc` because we want to be able to later analyze the same callee
-  // in a different context, and `setStorageLocation` requires there to not
-  // already be a storage location assigned. Conceptually, these maps capture
-  // information from the local scope, so when popping that scope, we do not
-  // propagate the maps.
+  // callee's `DeclCtx`, `ReturnLoc` or `ThisPointeeLoc`. We don't bring back
+  // `DeclToLoc` and `ExprToLoc` because we want to be able to later analyze the
+  // same callee in a different context, and `setStorageLocation` requires there
+  // to not already be a storage location assigned. Conceptually, these maps
+  // capture information from the local scope, so when popping that scope, we do
+  // not propagate the maps.
   this->LocToVal = std::move(CalleeEnv.LocToVal);
   this->MemberLocToStruct = std::move(CalleeEnv.MemberLocToStruct);
   this->FlowConditionToken = std::move(CalleeEnv.FlowConditionToken);
@@ -304,11 +309,13 @@
   assert(DACtx == Other.DACtx);
   assert(ReturnLoc == Other.ReturnLoc);
   assert(ThisPointeeLoc == Other.ThisPointeeLoc);
+  assert(DeclCtx == Other.DeclCtx);
 
   auto Effect = LatticeJoinEffect::Unchanged;
 
   Environment JoinedEnv(*DACtx);
 
+  JoinedEnv.setDeclCtx(DeclCtx);
   JoinedEnv.ReturnLoc = ReturnLoc;
   JoinedEnv.ThisPointeeLoc = ThisPointeeLoc;
 
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -347,6 +347,13 @@
   /// imply that `Val` is true.
   bool flowConditionImplies(BoolValue &Val) const;
 
+  /// Returns the `DeclContext` of the block being analysed, if any. Otherwise,
+  /// returns null.
+  const DeclContext *getDeclCtx() { return DeclCtx; }
+
+  /// Sets the `DeclContext` of the block being analysed.
+  void setDeclCtx(const DeclContext *Ctx) { DeclCtx = Ctx; }
+
   /// Returns the `ControlFlowContext` registered for `F`, if any. Otherwise,
   /// returns null.
   const ControlFlowContext *getControlFlowContext(const FunctionDecl *F) {
@@ -377,6 +384,9 @@
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
+  // `DeclContext` of the block being analysed if provided.
+  const DeclContext *DeclCtx = nullptr;
+
   /

[PATCH] D131891: [clang][dataflow] Debug string for value kinds.

2022-08-15 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131891

Files:
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp


Index: clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
===
--- clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
+++ clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
@@ -31,6 +31,32 @@
 using llvm::fmt_pad;
 using llvm::formatv;
 
+std::string debugString(Value::Kind Kind) {
+  switch (Kind) {
+  case Value::Kind::Integer:
+return "Integer";
+  case Value::Kind::Reference:
+return "Reference";
+  case Value::Kind::Pointer:
+return "Pointer";
+  case Value::Kind::Struct:
+return "Struct";
+  case Value::Kind::AtomicBool:
+return "AtomicBool";
+  case Value::Kind::Conjunction:
+return "Conjunction";
+  case Value::Kind::Disjunction:
+return "Disjunction";
+  case Value::Kind::Negation:
+return "Negation";
+  case Value::Kind::Implication:
+return "Implication";
+  case Value::Kind::Biconditional:
+return "Biconditional";
+  }
+  llvm_unreachable("Unhandled value kind");
+}
+
 std::string debugString(Solver::Result::Assignment Assignment) {
   switch (Assignment) {
   case Solver::Result::Assignment::AssignedFalse:
Index: clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
===
--- clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
+++ clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
@@ -24,6 +24,9 @@
 namespace clang {
 namespace dataflow {
 
+/// Returns a string representation of a value kind.
+std::string debugString(Value::Kind Kind);
+
 /// Returns a string representation of a boolean assignment to true or false.
 std::string debugString(Solver::Result::Assignment Assignment);
 


Index: clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
===
--- clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
+++ clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
@@ -31,6 +31,32 @@
 using llvm::fmt_pad;
 using llvm::formatv;
 
+std::string debugString(Value::Kind Kind) {
+  switch (Kind) {
+  case Value::Kind::Integer:
+return "Integer";
+  case Value::Kind::Reference:
+return "Reference";
+  case Value::Kind::Pointer:
+return "Pointer";
+  case Value::Kind::Struct:
+return "Struct";
+  case Value::Kind::AtomicBool:
+return "AtomicBool";
+  case Value::Kind::Conjunction:
+return "Conjunction";
+  case Value::Kind::Disjunction:
+return "Disjunction";
+  case Value::Kind::Negation:
+return "Negation";
+  case Value::Kind::Implication:
+return "Implication";
+  case Value::Kind::Biconditional:
+return "Biconditional";
+  }
+  llvm_unreachable("Unhandled value kind");
+}
+
 std::string debugString(Solver::Result::Assignment Assignment) {
   switch (Assignment) {
   case Solver::Result::Assignment::AssignedFalse:
Index: clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
===
--- clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
+++ clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
@@ -24,6 +24,9 @@
 namespace clang {
 namespace dataflow {
 
+/// Returns a string representation of a value kind.
+std::string debugString(Value::Kind Kind);
+
 /// Returns a string representation of a boolean assignment to true or false.
 std::string debugString(Solver::Result::Assignment Assignment);
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131614: [clang][dataflow] Extend transfer functions for other `CFGElement`s

2022-08-16 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 452957.
wyt marked 9 inline comments as done.
wyt added a comment.

Address comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131614/new/

https://reviews.llvm.org/D131614

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -71,14 +71,22 @@
   std::vector> &BlockStates;
 };
 
+// Runs dataflow analysis (specified from `MakeAnalysis`) and the `PostVisitCFG`
+// function (if provided) on the body of the function that matches
+// `TargetFuncMatcher` in code snippet `Code`. `VerifyResults` checks that the
+// results from the analysis are correct.
+//
+// Requirements:
+//
+//  `AnalysisT` contains a type `Lattice`.
 template 
-llvm::Error checkDataflow(
+llvm::Error checkDataflowOnCFG(
 llvm::StringRef Code,
 ast_matchers::internal::Matcher TargetFuncMatcher,
 std::function MakeAnalysis,
-std::function
-PostVisitStmt,
+PostVisitCFG,
 std::function VerifyResults, ArrayRef Args,
 const tooling::FileContentMappings &VirtualMappedFiles = {}) {
   llvm::Annotations AnnotatedCode(Code);
@@ -112,13 +120,14 @@
   Environment Env(DACtx, *F);
   auto Analysis = MakeAnalysis(Context, Env);
 
-  std::function
-  PostVisitStmtClosure = nullptr;
-  if (PostVisitStmt != nullptr) {
-PostVisitStmtClosure = [&PostVisitStmt, &Context](
-   const CFGStmt &Stmt,
-   const TypeErasedDataflowAnalysisState &State) {
-  PostVisitStmt(Context, Stmt, State);
+  std::function
+  PostVisitCFGClosure = nullptr;
+  if (PostVisitCFG) {
+PostVisitCFGClosure = [&PostVisitCFG, &Context](
+  const CFGElement &Element,
+  const TypeErasedDataflowAnalysisState &State) {
+  PostVisitCFG(Context, Element, State);
 };
   }
 
@@ -130,7 +139,7 @@
 
   llvm::Expected>>
   MaybeBlockStates = runTypeErasedDataflowAnalysis(*CFCtx, Analysis, Env,
-   PostVisitStmtClosure);
+   PostVisitCFGClosure);
   if (!MaybeBlockStates)
 return MaybeBlockStates.takeError();
   auto &BlockStates = *MaybeBlockStates;
@@ -141,6 +150,33 @@
   return llvm::Error::success();
 }
 
+template 
+llvm::Error checkDataflow(
+llvm::StringRef Code,
+ast_matchers::internal::Matcher TargetFuncMatcher,
+std::function MakeAnalysis,
+std::function
+PostVisitStmt,
+std::function VerifyResults, ArrayRef Args,
+const tooling::FileContentMappings &VirtualMappedFiles = {}) {
+
+  std::function
+  PostVisitCFG = nullptr;
+  if (PostVisitStmt) {
+PostVisitCFG =
+[&PostVisitStmt](ASTContext &Context, const CFGElement &Element,
+ const TypeErasedDataflowAnalysisState &State) {
+  if (auto Stmt = Element.getAs()) {
+PostVisitStmt(Context, *Stmt, State);
+  }
+};
+  }
+  return checkDataflowOnCFG(Code, TargetFuncMatcher, MakeAnalysis, PostVisitCFG,
+VerifyResults, Args, VirtualMappedFiles);
+}
+
 // Runs dataflow on the body of the function that matches `TargetFuncMatcher` in
 // code snippet `Code`. Requires: `AnalysisT` contains a type `Lattice`.
 template 
@@ -157,9 +193,9 @@
 const tooling::FileContentMappings &VirtualMappedFiles = {}) {
   using StateT = DataflowAnalysisState;
 
-  return checkDataflow(
+  return checkDataflowOnCFG(
   Code, std::move(TargetFuncMatcher), std::move(MakeAnalysis),
-  /*PostVisitStmt=*/nullptr,
+  /*PostVisitCFG=*/nullptr,
   [&VerifyResults](AnalysisData AnalysisData) {
 if (AnalysisData.BlockStates.empty()) {
   VerifyResults({}, AnalysisData.ASTCtx);
@@ -180,9 +216,13 @@
   AnalysisData.CFCtx, AnalysisData.BlockStates, *Block,
   AnalysisData.Env, AnalysisData.Analysis,
   [&Results,
-   &Annotations](const clang::CFGStmt &Stmt,
+   &Annotations](const clang::CFGElement &Element,
  const TypeErasedDataflowAnalysisState &State) {
-auto It = Annotations.find(Stmt.getStmt());
+// FIXME: Extend testing annotations to non statement constructs
+auto Stmt = Element.getAs();
+if (!Stmt)
+  return;
+auto It = Annotations.find(Stmt->getStmt());
 if (It == Annotations.

[PATCH] D131616: [clang][dataflow] Generalise match switch utility to other AST types and add a `CFGMatchSwitch` which currently handles `CFGStmt` and `CFGInitializer`.

2022-08-16 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 452958.
wyt added a comment.
Herald added a subscriber: mgorny.

Move implementation to CFGMatchSwitch.h, add tests.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131616/new/

https://reviews.llvm.org/D131616

Files:
  clang/include/clang/Analysis/FlowSensitive/CFGMatchSwitch.h
  clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
  clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
@@ -5,12 +5,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===--===//
-//
-//  This file defines a simplistic version of Constant Propagation as an example
-//  of a forward, monotonic dataflow analysis. The analysis tracks all
-//  variables in the scope, but lacks escape analysis.
-//
-//===--===//
 
 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
 #include "TestingSupport.h"
Index: clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
===
--- clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -4,6 +4,7 @@
   )
 
 add_clang_unittest(ClangAnalysisFlowSensitiveTests
+  CFGMatchSwitchTest.cpp
   ChromiumCheckModelTest.cpp
   DataflowAnalysisContextTest.cpp
   DataflowEnvironmentTest.cpp
Index: clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
@@ -0,0 +1,132 @@
+//===- unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.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 "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Stmt.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+using namespace clang;
+using namespace dataflow;
+using namespace ast_matchers;
+
+namespace {
+// State for tracking the number of matches on each kind of CFGElement by the
+// CFGMatchSwitch. Currently only tracks CFGStmt and CFGInitializer.
+struct CFGElementMatches {
+  unsigned int StmtMatches = 0;
+  unsigned int InitializerMatches = 0;
+};
+
+// Returns a match switch that counts the number of local variables
+// (singly-declared) and fields initialized to the integer literal 42.
+auto buildCFGMatchSwitch() {
+  return CFGMatchSwitchBuilder()
+  .CaseOfCFGStmt(
+  declStmt(hasSingleDecl(
+  varDecl(hasInitializer(integerLiteral(equals(42)),
+  [](const DeclStmt *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.StmtMatches++; })
+  .CaseOfCFGInit(
+  cxxCtorInitializer(withInitializer(integerLiteral(equals(42,
+  [](const CXXCtorInitializer *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.InitializerMatches++; })
+  .Build();
+}
+
+// Runs the match switch `MS` on the control flow graph generated from `Code`,
+// tracking information in state `S`. For simplicity, this test utility is
+// restricted to CFGs with a single control flow block (excluding entry and
+// exit blocks) - generated by `Code` with sequential flow (i.e. no branching).
+//
+// Requirements:
+//
+//  `Code` must contain a function named `f`, the body of this function will be
+//  used to generate the CFG.
+template 
+void applySwitchToCode(CFGMatchSwitch &MS, State &S,
+   llvm::StringRef Code) {
+  auto Unit = tooling::buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
+  auto &Ctx = Unit->getASTContext();
+  const auto *F = selectFirst(
+  "f", match(functionDecl(isDefinition(), hasName("f")).bind("f"), Ctx));
+
+  CFG::BuildOptions BO;
+  BO.AddInitializers = true;
+
+  auto CFG = CFG::buildCFG(F, F->getBody(), &Ctx, BO);
+  auto CFGBlock = *CFG->getEntry().succ_begin();
+  for (auto &Elt : CF

[PATCH] D131614: [clang][dataflow] Extend transfer functions for other `CFGElement`s

2022-08-16 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 452964.
wyt added a comment.

Add FIXME.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131614/new/

https://reviews.llvm.org/D131614

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -71,14 +71,22 @@
   std::vector> &BlockStates;
 };
 
+// Runs dataflow analysis (specified from `MakeAnalysis`) and the `PostVisitCFG`
+// function (if provided) on the body of the function that matches
+// `TargetFuncMatcher` in code snippet `Code`. `VerifyResults` checks that the
+// results from the analysis are correct.
+//
+// Requirements:
+//
+//  `AnalysisT` contains a type `Lattice`.
 template 
-llvm::Error checkDataflow(
+llvm::Error checkDataflowOnCFG(
 llvm::StringRef Code,
 ast_matchers::internal::Matcher TargetFuncMatcher,
 std::function MakeAnalysis,
-std::function
-PostVisitStmt,
+PostVisitCFG,
 std::function VerifyResults, ArrayRef Args,
 const tooling::FileContentMappings &VirtualMappedFiles = {}) {
   llvm::Annotations AnnotatedCode(Code);
@@ -112,13 +120,14 @@
   Environment Env(DACtx, *F);
   auto Analysis = MakeAnalysis(Context, Env);
 
-  std::function
-  PostVisitStmtClosure = nullptr;
-  if (PostVisitStmt != nullptr) {
-PostVisitStmtClosure = [&PostVisitStmt, &Context](
-   const CFGStmt &Stmt,
-   const TypeErasedDataflowAnalysisState &State) {
-  PostVisitStmt(Context, Stmt, State);
+  std::function
+  PostVisitCFGClosure = nullptr;
+  if (PostVisitCFG) {
+PostVisitCFGClosure = [&PostVisitCFG, &Context](
+  const CFGElement &Element,
+  const TypeErasedDataflowAnalysisState &State) {
+  PostVisitCFG(Context, Element, State);
 };
   }
 
@@ -130,7 +139,7 @@
 
   llvm::Expected>>
   MaybeBlockStates = runTypeErasedDataflowAnalysis(*CFCtx, Analysis, Env,
-   PostVisitStmtClosure);
+   PostVisitCFGClosure);
   if (!MaybeBlockStates)
 return MaybeBlockStates.takeError();
   auto &BlockStates = *MaybeBlockStates;
@@ -141,6 +150,33 @@
   return llvm::Error::success();
 }
 
+template 
+llvm::Error checkDataflow(
+llvm::StringRef Code,
+ast_matchers::internal::Matcher TargetFuncMatcher,
+std::function MakeAnalysis,
+std::function
+PostVisitStmt,
+std::function VerifyResults, ArrayRef Args,
+const tooling::FileContentMappings &VirtualMappedFiles = {}) {
+
+  std::function
+  PostVisitCFG = nullptr;
+  if (PostVisitStmt) {
+PostVisitCFG =
+[&PostVisitStmt](ASTContext &Context, const CFGElement &Element,
+ const TypeErasedDataflowAnalysisState &State) {
+  if (auto Stmt = Element.getAs()) {
+PostVisitStmt(Context, *Stmt, State);
+  }
+};
+  }
+  return checkDataflowOnCFG(Code, TargetFuncMatcher, MakeAnalysis, PostVisitCFG,
+VerifyResults, Args, VirtualMappedFiles);
+}
+
 // Runs dataflow on the body of the function that matches `TargetFuncMatcher` in
 // code snippet `Code`. Requires: `AnalysisT` contains a type `Lattice`.
 template 
@@ -157,9 +193,9 @@
 const tooling::FileContentMappings &VirtualMappedFiles = {}) {
   using StateT = DataflowAnalysisState;
 
-  return checkDataflow(
+  return checkDataflowOnCFG(
   Code, std::move(TargetFuncMatcher), std::move(MakeAnalysis),
-  /*PostVisitStmt=*/nullptr,
+  /*PostVisitCFG=*/nullptr,
   [&VerifyResults](AnalysisData AnalysisData) {
 if (AnalysisData.BlockStates.empty()) {
   VerifyResults({}, AnalysisData.ASTCtx);
@@ -180,9 +216,13 @@
   AnalysisData.CFCtx, AnalysisData.BlockStates, *Block,
   AnalysisData.Env, AnalysisData.Analysis,
   [&Results,
-   &Annotations](const clang::CFGStmt &Stmt,
+   &Annotations](const clang::CFGElement &Element,
  const TypeErasedDataflowAnalysisState &State) {
-auto It = Annotations.find(Stmt.getStmt());
+// FIXME: Extend testing annotations to non statement constructs
+auto Stmt = Element.getAs();
+if (!Stmt)
+  return;
+auto It = Annotations.find(Stmt->getStmt());
 if (It == Annotations.end())
   return;
   

[PATCH] D131616: [clang][dataflow] Generalise match switch utility to other AST types and add a `CFGMatchSwitch` which currently handles `CFGStmt` and `CFGInitializer`.

2022-08-16 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 452965.
wyt added a comment.

Propagate change from parent patch.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131616/new/

https://reviews.llvm.org/D131616

Files:
  clang/include/clang/Analysis/FlowSensitive/CFGMatchSwitch.h
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
  clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
@@ -5,12 +5,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===--===//
-//
-//  This file defines a simplistic version of Constant Propagation as an example
-//  of a forward, monotonic dataflow analysis. The analysis tracks all
-//  variables in the scope, but lacks escape analysis.
-//
-//===--===//
 
 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
 #include "TestingSupport.h"
Index: clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
===
--- clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -4,6 +4,7 @@
   )
 
 add_clang_unittest(ClangAnalysisFlowSensitiveTests
+  CFGMatchSwitchTest.cpp
   ChromiumCheckModelTest.cpp
   DataflowAnalysisContextTest.cpp
   DataflowEnvironmentTest.cpp
Index: clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
@@ -0,0 +1,132 @@
+//===- unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.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 "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Stmt.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+using namespace clang;
+using namespace dataflow;
+using namespace ast_matchers;
+
+namespace {
+// State for tracking the number of matches on each kind of CFGElement by the
+// CFGMatchSwitch. Currently only tracks CFGStmt and CFGInitializer.
+struct CFGElementMatches {
+  unsigned int StmtMatches = 0;
+  unsigned int InitializerMatches = 0;
+};
+
+// Returns a match switch that counts the number of local variables
+// (singly-declared) and fields initialized to the integer literal 42.
+auto buildCFGMatchSwitch() {
+  return CFGMatchSwitchBuilder()
+  .CaseOfCFGStmt(
+  declStmt(hasSingleDecl(
+  varDecl(hasInitializer(integerLiteral(equals(42)),
+  [](const DeclStmt *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.StmtMatches++; })
+  .CaseOfCFGInit(
+  cxxCtorInitializer(withInitializer(integerLiteral(equals(42,
+  [](const CXXCtorInitializer *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.InitializerMatches++; })
+  .Build();
+}
+
+// Runs the match switch `MS` on the control flow graph generated from `Code`,
+// tracking information in state `S`. For simplicity, this test utility is
+// restricted to CFGs with a single control flow block (excluding entry and
+// exit blocks) - generated by `Code` with sequential flow (i.e. no branching).
+//
+// Requirements:
+//
+//  `Code` must contain a function named `f`, the body of this function will be
+//  used to generate the CFG.
+template 
+void applySwitchToCode(CFGMatchSwitch &MS, State &S,
+   llvm::StringRef Code) {
+  auto Unit = tooling::buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
+  auto &Ctx = Unit->getASTContext();
+  const auto *F = selectFirst(
+  "f", match(functionDecl(isDefinition(), hasName("f")).bind("f"), Ctx));
+
+  CFG::BuildOptions BO;
+  BO.AddInitializers = true;
+
+  auto CFG = CFG::buildCFG(F, F->getBody

[PATCH] D131614: [clang][dataflow] Extend transfer functions for other `CFGElement`s

2022-08-16 Thread weiyi via Phabricator via cfe-commits
wyt added a reviewer: xazax.hun.
wyt added inline comments.
Herald added a subscriber: rnkovacs.



Comment at: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h:103
+  virtual void transferCFGElement(const CFGElement *Element, Lattice &L,
+  Environment &Env) {}
+

gribozavr2 wrote:
> Instead of adding virtual function, please continue following the CRTP 
> pattern. Add the expected function declarations in the comment above the 
> class.
> Instead of adding virtual function, please continue following the CRTP 
> pattern. Add the expected function declarations in the comment above the 
> class.

As discussed, moving to a CRTP pattern causes the code to break as they have 
not been implemented by users yet. Added a FIXME to do so after users have been 
migrated to implement transferCFGElement.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131614/new/

https://reviews.llvm.org/D131614

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D131616: [clang][dataflow] Generalise match switch utility to other AST types and add a `CFGMatchSwitch` which currently handles `CFGStmt` and `CFGInitializer`.

2022-08-16 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 452966.
wyt added a comment.

Fix incorrect change.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131616/new/

https://reviews.llvm.org/D131616

Files:
  clang/include/clang/Analysis/FlowSensitive/CFGMatchSwitch.h
  clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
  clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
@@ -5,12 +5,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===--===//
-//
-//  This file defines a simplistic version of Constant Propagation as an example
-//  of a forward, monotonic dataflow analysis. The analysis tracks all
-//  variables in the scope, but lacks escape analysis.
-//
-//===--===//
 
 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
 #include "TestingSupport.h"
Index: clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
===
--- clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -4,6 +4,7 @@
   )
 
 add_clang_unittest(ClangAnalysisFlowSensitiveTests
+  CFGMatchSwitchTest.cpp
   ChromiumCheckModelTest.cpp
   DataflowAnalysisContextTest.cpp
   DataflowEnvironmentTest.cpp
Index: clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
@@ -0,0 +1,132 @@
+//===- unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.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 "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Stmt.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+using namespace clang;
+using namespace dataflow;
+using namespace ast_matchers;
+
+namespace {
+// State for tracking the number of matches on each kind of CFGElement by the
+// CFGMatchSwitch. Currently only tracks CFGStmt and CFGInitializer.
+struct CFGElementMatches {
+  unsigned int StmtMatches = 0;
+  unsigned int InitializerMatches = 0;
+};
+
+// Returns a match switch that counts the number of local variables
+// (singly-declared) and fields initialized to the integer literal 42.
+auto buildCFGMatchSwitch() {
+  return CFGMatchSwitchBuilder()
+  .CaseOfCFGStmt(
+  declStmt(hasSingleDecl(
+  varDecl(hasInitializer(integerLiteral(equals(42)),
+  [](const DeclStmt *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.StmtMatches++; })
+  .CaseOfCFGInit(
+  cxxCtorInitializer(withInitializer(integerLiteral(equals(42,
+  [](const CXXCtorInitializer *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.InitializerMatches++; })
+  .Build();
+}
+
+// Runs the match switch `MS` on the control flow graph generated from `Code`,
+// tracking information in state `S`. For simplicity, this test utility is
+// restricted to CFGs with a single control flow block (excluding entry and
+// exit blocks) - generated by `Code` with sequential flow (i.e. no branching).
+//
+// Requirements:
+//
+//  `Code` must contain a function named `f`, the body of this function will be
+//  used to generate the CFG.
+template 
+void applySwitchToCode(CFGMatchSwitch &MS, State &S,
+   llvm::StringRef Code) {
+  auto Unit = tooling::buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
+  auto &Ctx = Unit->getASTContext();
+  const auto *F = selectFirst(
+  "f", match(functionDecl(isDefinition(), hasName("f")).bind("f"), Ctx));
+
+  CFG::BuildOptions BO;
+  BO.AddInitializers = true;
+
+  auto CFG = CFG::buildCFG(F, F->getBody(), &Ctx, BO);
+  auto CFGBlock = *CFG->getEntry().succ_begin();
+  for (auto &Elt : CFGBlock->Elements) {
+MS(Elt, Ctx, S);
+  }
+}
+
+TEST(CFGMatc

[PATCH] D131891: [clang][dataflow] Debug string for value kinds.

2022-08-16 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 452968.
wyt added a comment.

Use llvm::StringRef for returning static strings.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131891/new/

https://reviews.llvm.org/D131891

Files:
  clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
  clang/lib/Analysis/FlowSensitive/DebugSupport.cpp


Index: clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
===
--- clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
+++ clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
@@ -18,6 +18,7 @@
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FormatAdapters.h"
@@ -31,7 +32,33 @@
 using llvm::fmt_pad;
 using llvm::formatv;
 
-std::string debugString(Solver::Result::Assignment Assignment) {
+llvm::StringRef debugString(Value::Kind Kind) {
+  switch (Kind) {
+  case Value::Kind::Integer:
+return "Integer";
+  case Value::Kind::Reference:
+return "Reference";
+  case Value::Kind::Pointer:
+return "Pointer";
+  case Value::Kind::Struct:
+return "Struct";
+  case Value::Kind::AtomicBool:
+return "AtomicBool";
+  case Value::Kind::Conjunction:
+return "Conjunction";
+  case Value::Kind::Disjunction:
+return "Disjunction";
+  case Value::Kind::Negation:
+return "Negation";
+  case Value::Kind::Implication:
+return "Implication";
+  case Value::Kind::Biconditional:
+return "Biconditional";
+  }
+  llvm_unreachable("Unhandled value kind");
+}
+
+llvm::StringRef debugString(Solver::Result::Assignment Assignment) {
   switch (Assignment) {
   case Solver::Result::Assignment::AssignedFalse:
 return "False";
@@ -41,7 +68,7 @@
   llvm_unreachable("Booleans can only be assigned true/false");
 }
 
-std::string debugString(Solver::Result::Status Status) {
+llvm::StringRef debugString(Solver::Result::Status Status) {
   switch (Status) {
   case Solver::Result::Status::Satisfiable:
 return "Satisfiable";
Index: clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
===
--- clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
+++ clang/include/clang/Analysis/FlowSensitive/DebugSupport.h
@@ -20,15 +20,19 @@
 #include "clang/Analysis/FlowSensitive/Solver.h"
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
 
 namespace clang {
 namespace dataflow {
 
+/// Returns a string representation of a value kind.
+llvm::StringRef debugString(Value::Kind Kind);
+
 /// Returns a string representation of a boolean assignment to true or false.
-std::string debugString(Solver::Result::Assignment Assignment);
+llvm::StringRef debugString(Solver::Result::Assignment Assignment);
 
 /// Returns a string representation of the result status of a SAT check.
-std::string debugString(Solver::Result::Status Status);
+llvm::StringRef debugString(Solver::Result::Status Status);
 
 /// Returns a string representation for the boolean value `B`.
 ///


Index: clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
===
--- clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
+++ clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
@@ -18,6 +18,7 @@
 #include "clang/Analysis/FlowSensitive/Value.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FormatAdapters.h"
@@ -31,7 +32,33 @@
 using llvm::fmt_pad;
 using llvm::formatv;
 
-std::string debugString(Solver::Result::Assignment Assignment) {
+llvm::StringRef debugString(Value::Kind Kind) {
+  switch (Kind) {
+  case Value::Kind::Integer:
+return "Integer";
+  case Value::Kind::Reference:
+return "Reference";
+  case Value::Kind::Pointer:
+return "Pointer";
+  case Value::Kind::Struct:
+return "Struct";
+  case Value::Kind::AtomicBool:
+return "AtomicBool";
+  case Value::Kind::Conjunction:
+return "Conjunction";
+  case Value::Kind::Disjunction:
+return "Disjunction";
+  case Value::Kind::Negation:
+return "Negation";
+  case Value::Kind::Implication:
+return "Implication";
+  case Value::Kind::Biconditional:
+return "Biconditional";
+  }
+  llvm_unreachable("Unhandled value kind");
+}
+
+llvm::StringRef debugString(Solver::Result::Assignment Assignment) {
   switch (Assignment) {
   case Solver::Result::Assignment::AssignedFalse:
 return "False";
@@ -41,7 +68,7 @@
   llvm_unreachable("Booleans can only be assigned true/false");
 }
 
-std::string debugString(Solver::Result::Status Status) {
+llvm::StringRef debugString(Solver::Result::Status Status) {
   switch (Status) {

[PATCH] D131614: [clang][dataflow] Extend transfer functions for other `CFGElement`s

2022-08-18 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 453702.
wyt marked 2 inline comments as done.
wyt added a comment.

Address comments. Unable to rename `check/runDataflowOnCFG` to 
`check/runDataflow` currently due to ambiguous use.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131614/new/

https://reviews.llvm.org/D131614

Files:
  clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
  clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
  clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -71,14 +71,25 @@
   std::vector> &BlockStates;
 };
 
+// FIXME: Rename to `checkDataflow` after usages of the overload that applies to
+// `CFGStmt` have been replaced.
+//
+/// Runs dataflow analysis (specified from `MakeAnalysis`) and the
+/// `PostVisitCFG` function (if provided) on the body of the function that
+/// matches `TargetFuncMatcher` in code snippet `Code`. `VerifyResults` checks
+/// that the results from the analysis are correct.
+///
+/// Requirements:
+///
+///  `AnalysisT` contains a type `Lattice`.
 template 
-llvm::Error checkDataflow(
+llvm::Error checkDataflowOnCFG(
 llvm::StringRef Code,
 ast_matchers::internal::Matcher TargetFuncMatcher,
 std::function MakeAnalysis,
-std::function
-PostVisitStmt,
+PostVisitCFG,
 std::function VerifyResults, ArrayRef Args,
 const tooling::FileContentMappings &VirtualMappedFiles = {}) {
   llvm::Annotations AnnotatedCode(Code);
@@ -112,13 +123,14 @@
   Environment Env(DACtx, *F);
   auto Analysis = MakeAnalysis(Context, Env);
 
-  std::function
-  PostVisitStmtClosure = nullptr;
-  if (PostVisitStmt != nullptr) {
-PostVisitStmtClosure = [&PostVisitStmt, &Context](
-   const CFGStmt &Stmt,
-   const TypeErasedDataflowAnalysisState &State) {
-  PostVisitStmt(Context, Stmt, State);
+  std::function
+  PostVisitCFGClosure = nullptr;
+  if (PostVisitCFG) {
+PostVisitCFGClosure = [&PostVisitCFG, &Context](
+  const CFGElement &Element,
+  const TypeErasedDataflowAnalysisState &State) {
+  PostVisitCFG(Context, Element, State);
 };
   }
 
@@ -130,7 +142,7 @@
 
   llvm::Expected>>
   MaybeBlockStates = runTypeErasedDataflowAnalysis(*CFCtx, Analysis, Env,
-   PostVisitStmtClosure);
+   PostVisitCFGClosure);
   if (!MaybeBlockStates)
 return MaybeBlockStates.takeError();
   auto &BlockStates = *MaybeBlockStates;
@@ -141,6 +153,33 @@
   return llvm::Error::success();
 }
 
+template 
+llvm::Error checkDataflow(
+llvm::StringRef Code,
+ast_matchers::internal::Matcher TargetFuncMatcher,
+std::function MakeAnalysis,
+std::function
+PostVisitStmt,
+std::function VerifyResults, ArrayRef Args,
+const tooling::FileContentMappings &VirtualMappedFiles = {}) {
+
+  std::function
+  PostVisitCFG = nullptr;
+  if (PostVisitStmt) {
+PostVisitCFG =
+[&PostVisitStmt](ASTContext &Context, const CFGElement &Element,
+ const TypeErasedDataflowAnalysisState &State) {
+  if (auto Stmt = Element.getAs()) {
+PostVisitStmt(Context, *Stmt, State);
+  }
+};
+  }
+  return checkDataflowOnCFG(Code, TargetFuncMatcher, MakeAnalysis, PostVisitCFG,
+VerifyResults, Args, VirtualMappedFiles);
+}
+
 // Runs dataflow on the body of the function that matches `TargetFuncMatcher` in
 // code snippet `Code`. Requires: `AnalysisT` contains a type `Lattice`.
 template 
@@ -157,9 +196,9 @@
 const tooling::FileContentMappings &VirtualMappedFiles = {}) {
   using StateT = DataflowAnalysisState;
 
-  return checkDataflow(
+  return checkDataflowOnCFG(
   Code, std::move(TargetFuncMatcher), std::move(MakeAnalysis),
-  /*PostVisitStmt=*/nullptr,
+  /*PostVisitCFG=*/nullptr,
   [&VerifyResults](AnalysisData AnalysisData) {
 if (AnalysisData.BlockStates.empty()) {
   VerifyResults({}, AnalysisData.ASTCtx);
@@ -180,9 +219,13 @@
   AnalysisData.CFCtx, AnalysisData.BlockStates, *Block,
   AnalysisData.Env, AnalysisData.Analysis,
   [&Results,
-   &Annotations](const clang::CFGStmt &Stmt,
+   &Annotations](const clang::CFGElement &Element,
  const TypeErasedDataflowAnalysisState &State) {
-auto It = Annotations.find(Stmt.getStmt());
+// FIXME: Extend testing annotations to n

[PATCH] D131616: [clang][dataflow] Generalise match switch utility to other AST types and add a `CFGMatchSwitch` which currently handles `CFGStmt` and `CFGInitializer`.

2022-08-18 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 453704.
wyt marked 5 inline comments as done.
wyt added a comment.

Address comments.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D131616/new/

https://reviews.llvm.org/D131616

Files:
  clang/include/clang/Analysis/FlowSensitive/CFGMatchSwitch.h
  clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
  clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
  clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
  clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/MatchSwitchTest.cpp
@@ -5,12 +5,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===--===//
-//
-//  This file defines a simplistic version of Constant Propagation as an example
-//  of a forward, monotonic dataflow analysis. The analysis tracks all
-//  variables in the scope, but lacks escape analysis.
-//
-//===--===//
 
 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
 #include "TestingSupport.h"
Index: clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
===
--- clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
+++ clang/unittests/Analysis/FlowSensitive/CMakeLists.txt
@@ -4,6 +4,7 @@
   )
 
 add_clang_unittest(ClangAnalysisFlowSensitiveTests
+  CFGMatchSwitchTest.cpp
   ChromiumCheckModelTest.cpp
   DataflowAnalysisContextTest.cpp
   DataflowEnvironmentTest.cpp
Index: clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
===
--- /dev/null
+++ clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
@@ -0,0 +1,132 @@
+//===- unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.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 "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Stmt.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+#include 
+#include 
+#include 
+#include 
+#include 
+
+using namespace clang;
+using namespace dataflow;
+using namespace ast_matchers;
+
+namespace {
+// State for tracking the number of matches on each kind of CFGElement by the
+// CFGMatchSwitch. Currently only tracks CFGStmt and CFGInitializer.
+struct CFGElementMatches {
+  unsigned StmtMatches = 0;
+  unsigned InitializerMatches = 0;
+};
+
+// Returns a match switch that counts the number of local variables
+// (singly-declared) and fields initialized to the integer literal 42.
+auto buildCFGMatchSwitch() {
+  return CFGMatchSwitchBuilder()
+  .CaseOfCFGStmt(
+  declStmt(hasSingleDecl(
+  varDecl(hasInitializer(integerLiteral(equals(42)),
+  [](const DeclStmt *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.StmtMatches++; })
+  .CaseOfCFGInit(
+  cxxCtorInitializer(withInitializer(integerLiteral(equals(42,
+  [](const CXXCtorInitializer *, const MatchFinder::MatchResult &,
+ CFGElementMatches &Counter) { Counter.InitializerMatches++; })
+  .Build();
+}
+
+// Runs the match switch `MS` on the control flow graph generated from `Code`,
+// tracking information in state `S`. For simplicity, this test utility is
+// restricted to CFGs with a single control flow block (excluding entry and
+// exit blocks) - generated by `Code` with sequential flow (i.e. no branching).
+//
+// Requirements:
+//
+//  `Code` must contain a function named `f`, the body of this function will be
+//  used to generate the CFG.
+template 
+void applySwitchToCode(CFGMatchSwitch &MS, State &S,
+   llvm::StringRef Code) {
+  auto Unit = tooling::buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"});
+  auto &Ctx = Unit->getASTContext();
+  const auto *F = selectFirst(
+  "f", match(functionDecl(isDefinition(), hasName("f")).bind("f"), Ctx));
+
+  CFG::BuildOptions BO;
+  BO.AddInitializers = true;
+
+  auto CFG = CFG::buildCFG(F, F->getBody(), &Ctx, BO);
+  auto CFGBlock = *CFG->getEntry().succ_begin();
+  for (auto &Elt : CFGBlock->Elements) {
+MS(Elt, Ctx, S

[PATCH] D132147: [clang][dataflow] Refactor `TestingSupport.h`

2022-08-18 Thread weiyi via Phabricator via cfe-commits
wyt created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a project: All.
wyt requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

- Add `AnalysisArguments` struct for `checkDataflow`.

- Remove compulsory binding from statement to annotations. Instead, 
`checkDataflow` in the most general form takes a `VerifyResults` callback which 
takes as input an `AnalysisData` struct. This struct contains the data 
structures generated by the analysis that can then be tested. We then introduce 
two overloads/wrappers of `checkDataflow` for different mechanisms of testing - 
one which exposes annotation line numbers and is not restricted to statements, 
and the other which exposes states computed after annotated statements. In the 
future, we should look at retrieving the analysis states for constructs other 
than statements.

Depends On D131616 


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D132147

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -1240,43 +1240,45 @@
 /*IgnoreSmartPointerDereference=*/true};
 std::vector Diagnostics;
 llvm::Error Error = checkDataflow(
-SourceCode, FuncMatcher,
-[Options](ASTContext &Ctx, Environment &) {
-  return UncheckedOptionalAccessModel(Ctx, Options);
+/*AA:AnalysisArguments=*/{
+.Code = SourceCode,
+.TargetFuncMatcher = FuncMatcher,
+.MakeAnalysis =
+[Options](ASTContext &Ctx, Environment &) {
+  return UncheckedOptionalAccessModel(Ctx, Options);
+},
+.PostVisitCFG =
+[&Diagnostics,
+ Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
+ASTContext &Ctx, const CFGElement &Elt,
+const TypeErasedDataflowAnalysisState &State) mutable {
+  auto Stmt = Elt.getAs();
+  if (!Stmt) {
+return;
+  }
+  auto StmtDiagnostics =
+  Diagnoser.diagnose(Ctx, Stmt->getStmt(), State.Env);
+  llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
+},
+.ASTBuildArgs = {"-fsyntax-only", "-std=c++17",
+ "-Wno-undefined-inline"},
+.ASTBuildVirtualMappedFiles = FileContents,
 },
-[&Diagnostics, Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
-ASTContext &Ctx, const CFGStmt &Stmt,
-const TypeErasedDataflowAnalysisState &State) mutable {
-  auto StmtDiagnostics =
-  Diagnoser.diagnose(Ctx, Stmt.getStmt(), State.Env);
-  llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
-},
-[&Diagnostics](AnalysisData AnalysisData) {
-  auto &SrcMgr = AnalysisData.ASTCtx.getSourceManager();
-
+/*VerifyResults=*/[&Diagnostics](llvm::DenseMap
+ &Annotations,
+ AnalysisData &AD) {
   llvm::DenseSet AnnotationLines;
-  for (const auto &Pair : AnalysisData.Annotations) {
-auto *Stmt = Pair.getFirst();
-AnnotationLines.insert(
-SrcMgr.getPresumedLineNumber(Stmt->getBeginLoc()));
-// We add both the begin and end locations, so that if the
-// statement spans multiple lines then the test will fail.
-//
-// FIXME: Going forward, we should change this to instead just
-// get the single line number from the annotation itself, rather
-// than looking at the statement it's attached to.
-AnnotationLines.insert(
-SrcMgr.getPresumedLineNumber(Stmt->getEndLoc()));
+  for (const auto &[Line, _] : Annotations) {
+AnnotationLines.insert(Line);
   }
-
+  auto &SrcMgr = AD.ASTCtx.getSourceManager();
   llvm::DenseSet DiagnosticLines;
   for (SourceLocation &Loc : Diagnostics) {
 DiagnosticLines.insert(SrcMgr.getPresumedLineNumber(Loc));
   }
 
   EXPECT_THAT(DiagnosticLines, ContainerEq(AnnotationLines));
-},
-{"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents);
+});
 if (Error)
   FAIL() << llvm::toString(std::move(Error));
   }
Index: clang/unittests/Analy

[PATCH] D132147: [clang][dataflow] Refactor `TestingSupport.h`

2022-08-18 Thread weiyi via Phabricator via cfe-commits
wyt updated this revision to Diff 453708.
wyt added a comment.

Small fixes.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132147/new/

https://reviews.llvm.org/D132147

Files:
  clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp
  clang/unittests/Analysis/FlowSensitive/TestingSupport.h
  clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp

Index: clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
===
--- clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -1240,43 +1240,45 @@
 /*IgnoreSmartPointerDereference=*/true};
 std::vector Diagnostics;
 llvm::Error Error = checkDataflow(
-SourceCode, FuncMatcher,
-[Options](ASTContext &Ctx, Environment &) {
-  return UncheckedOptionalAccessModel(Ctx, Options);
+/*AA:AnalysisArguments=*/{
+.Code = SourceCode,
+.TargetFuncMatcher = FuncMatcher,
+.MakeAnalysis =
+[Options](ASTContext &Ctx, Environment &) {
+  return UncheckedOptionalAccessModel(Ctx, Options);
+},
+.PostVisitCFG =
+[&Diagnostics,
+ Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
+ASTContext &Ctx, const CFGElement &Elt,
+const TypeErasedDataflowAnalysisState &State) mutable {
+  auto Stmt = Elt.getAs();
+  if (!Stmt) {
+return;
+  }
+  auto StmtDiagnostics =
+  Diagnoser.diagnose(Ctx, Stmt->getStmt(), State.Env);
+  llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
+},
+.ASTBuildArgs = {"-fsyntax-only", "-std=c++17",
+ "-Wno-undefined-inline"},
+.ASTBuildVirtualMappedFiles = FileContents,
 },
-[&Diagnostics, Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
-ASTContext &Ctx, const CFGStmt &Stmt,
-const TypeErasedDataflowAnalysisState &State) mutable {
-  auto StmtDiagnostics =
-  Diagnoser.diagnose(Ctx, Stmt.getStmt(), State.Env);
-  llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
-},
-[&Diagnostics](AnalysisData AnalysisData) {
-  auto &SrcMgr = AnalysisData.ASTCtx.getSourceManager();
-
+/*VerifyResults=*/[&Diagnostics](llvm::DenseMap
+ &Annotations,
+ AnalysisData &AD) {
   llvm::DenseSet AnnotationLines;
-  for (const auto &Pair : AnalysisData.Annotations) {
-auto *Stmt = Pair.getFirst();
-AnnotationLines.insert(
-SrcMgr.getPresumedLineNumber(Stmt->getBeginLoc()));
-// We add both the begin and end locations, so that if the
-// statement spans multiple lines then the test will fail.
-//
-// FIXME: Going forward, we should change this to instead just
-// get the single line number from the annotation itself, rather
-// than looking at the statement it's attached to.
-AnnotationLines.insert(
-SrcMgr.getPresumedLineNumber(Stmt->getEndLoc()));
+  for (const auto &[Line, _] : Annotations) {
+AnnotationLines.insert(Line);
   }
-
+  auto &SrcMgr = AD.ASTCtx.getSourceManager();
   llvm::DenseSet DiagnosticLines;
   for (SourceLocation &Loc : Diagnostics) {
 DiagnosticLines.insert(SrcMgr.getPresumedLineNumber(Loc));
   }
 
   EXPECT_THAT(DiagnosticLines, ContainerEq(AnnotationLines));
-},
-{"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents);
+});
 if (Error)
   FAIL() << llvm::toString(std::move(Error));
   }
Index: clang/unittests/Analysis/FlowSensitive/TestingSupport.h
===
--- clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -56,47 +56,130 @@
 
 namespace test {
 
-// Returns assertions based on annotations that are present after statements in
-// `AnnotatedCode`.
-llvm::Expected>
-buildStatementToAnnotationMapping(const FunctionDecl *Func,
-  llvm::Annotations AnnotatedCode);
+/// Arguments for building the dataflow analysis.
+template  struct AnalysisArguments {
+  /// Input code that is analyzed.
+  llvm::StringRef Code;
+  /// The body of the function which matches this matcher is analyzed.
+  ast_matchers::internal::Matcher TargetFuncMatcher;
+  /// The analysis to be ru

  1   2   >