merrymeerkat created this revision.
Herald added subscribers: martong, xazax.hun.
Herald added a reviewer: NoQ.
Herald added a project: All.
merrymeerkat requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

For example, previously, if the dataflow analysis wanted to warn the user about 
a CXXCtorInitializer, it could not show a precise warning because diagnosers 
only accepted Stmts.
Now, diagnosers receive a CFGElement and can thus warn about any type of C++ 
construct.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D137432

Files:
  clang/include/clang/Analysis/FlowSensitive/CFGMatchSwitch.h
  clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
  clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
  clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
  clang/unittests/Analysis/FlowSensitive/SignAnalysisTest.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
@@ -1235,6 +1235,7 @@
     UncheckedOptionalAccessModelOptions Options{
         /*IgnoreSmartPointerDereference=*/true};
     std::vector<SourceLocation> Diagnostics;
+    UncheckedOptionalAccessDiagnoser Diagnoser(Options);
     llvm::Error Error = checkDataflow<UncheckedOptionalAccessModel>(
         AnalysisInputs<UncheckedOptionalAccessModel>(
             SourceCode, std::move(FuncMatcher),
@@ -1242,8 +1243,7 @@
               return UncheckedOptionalAccessModel(Ctx, Options);
             })
             .withPostVisitCFG(
-                [&Diagnostics,
-                 Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
+                [&Diagnostics, &Diagnoser](
                     ASTContext &Ctx, const CFGElement &Elt,
                     const TypeErasedDataflowAnalysisState &State) mutable {
                   auto EltDiagnostics =
Index: clang/unittests/Analysis/FlowSensitive/SignAnalysisTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/SignAnalysisTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/SignAnalysisTest.cpp
@@ -84,7 +84,7 @@
           dyn_cast_or_null<BoolValue>(Val.getProperty("pos"))};
 }
 
-void transferUninitializedInt(const DeclStmt *D,
+void transferUninitializedInt(const CFGElement &Element, const DeclStmt *D,
                               const MatchFinder::MatchResult &M,
                               LatticeTransferState &State) {
   const auto *Var = M.Nodes.getNodeAs<clang::VarDecl>(kVar);
@@ -133,7 +133,8 @@
   return {UnaryOpValue, UnaryOpProps, OperandProps};
 }
 
-void transferBinary(const BinaryOperator *BO, const MatchFinder::MatchResult &M,
+void transferBinary(const CFGElement &Element, const BinaryOperator *BO,
+                    const MatchFinder::MatchResult &M,
                     LatticeTransferState &State) {
   StorageLocation *Loc = State.Env.getStorageLocation(*BO, SkipPast::None);
   if (!Loc) {
@@ -202,7 +203,7 @@
   }
 }
 
-void transferUnaryMinus(const UnaryOperator *UO,
+void transferUnaryMinus(const CFGElement &Element, const UnaryOperator *UO,
                         const MatchFinder::MatchResult &M,
                         LatticeTransferState &State) {
   auto [UnaryOpValue, UnaryOpProps, OperandProps] =
@@ -221,7 +222,7 @@
       State.Env.makeImplication(*OperandProps.Zero, *UnaryOpProps.Zero));
 }
 
-void transferUnaryNot(const UnaryOperator *UO,
+void transferUnaryNot(const CFGElement &Element, const UnaryOperator *UO,
                       const MatchFinder::MatchResult &M,
                       LatticeTransferState &State) {
   auto [UnaryOpValue, UnaryOpProps, OperandProps] =
@@ -246,7 +247,8 @@
   }
 }
 
-void transferExpr(const Expr *E, const MatchFinder::MatchResult &M,
+void transferExpr(const CFGElement &Element, const Expr *E,
+                  const MatchFinder::MatchResult &M,
                   LatticeTransferState &State) {
   const ASTContext &Context = *M.Context;
   StorageLocation *Loc = State.Env.getStorageLocation(*E, SkipPast::None);
Index: clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp
@@ -34,11 +34,13 @@
       .CaseOfCFGStmt<DeclStmt>(
           declStmt(hasSingleDecl(
               varDecl(hasInitializer(integerLiteral(equals(42)))))),
-          [](const DeclStmt *, const MatchFinder::MatchResult &,
+          [](const CFGElement &Element, const DeclStmt *,
+             const MatchFinder::MatchResult &,
              CFGElementMatches &Counter) { Counter.StmtMatches++; })
       .CaseOfCFGInit<CXXCtorInitializer>(
           cxxCtorInitializer(withInitializer(integerLiteral(equals(42)))),
-          [](const CXXCtorInitializer *, const MatchFinder::MatchResult &,
+          [](const CFGElement &Element, const CXXCtorInitializer *,
+             const MatchFinder::MatchResult &,
              CFGElementMatches &Counter) { Counter.InitializerMatches++; })
       .Build();
 }
Index: clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -277,7 +277,8 @@
   return &ValueLoc;
 }
 
-void initializeOptionalReference(const Expr *OptionalExpr,
+void initializeOptionalReference(const CFGElement &Element,
+                                 const Expr *OptionalExpr,
                                  const MatchFinder::MatchResult &,
                                  LatticeTransferState &State) {
   if (auto *OptionalVal =
@@ -316,7 +317,7 @@
   }
 }
 
-void transferMakeOptionalCall(const CallExpr *E,
+void transferMakeOptionalCall(const CFGElement &Element, const CallExpr *E,
                               const MatchFinder::MatchResult &,
                               LatticeTransferState &State) {
   auto &Loc = State.Env.createStorageLocation(*E);
@@ -325,7 +326,8 @@
       Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
 }
 
-void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
+void transferOptionalHasValueCall(const CFGElement &Element,
+                                  const CXXMemberCallExpr *CallExpr,
                                   const MatchFinder::MatchResult &,
                                   LatticeTransferState &State) {
   if (auto *HasValueVal = getHasValue(
@@ -369,7 +371,8 @@
   Env.addToFlowCondition(ModelPred(Env, *ExprValue, *HasValueVal));
 }
 
-void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
+void transferValueOrStringEmptyCall(const CFGElement &Element,
+                                    const clang::Expr *ComparisonExpr,
                                     const MatchFinder::MatchResult &Result,
                                     LatticeTransferState &State) {
   return transferValueOrImpl(ComparisonExpr, Result, State,
@@ -386,7 +389,8 @@
                              });
 }
 
-void transferValueOrNotEqX(const Expr *ComparisonExpr,
+void transferValueOrNotEqX(const CFGElement &Element,
+                           const Expr *ComparisonExpr,
                            const MatchFinder::MatchResult &Result,
                            LatticeTransferState &State) {
   transferValueOrImpl(ComparisonExpr, Result, State,
@@ -399,7 +403,7 @@
                       });
 }
 
-void transferCallReturningOptional(const CallExpr *E,
+void transferCallReturningOptional(const CFGElement &Element, const CallExpr *E,
                                    const MatchFinder::MatchResult &Result,
                                    LatticeTransferState &State) {
   if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
@@ -448,8 +452,8 @@
 }
 
 void transferValueOrConversionConstructor(
-    const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
-    LatticeTransferState &State) {
+    const CFGElement &Element, const CXXConstructExpr *E,
+    const MatchFinder::MatchResult &MatchRes, LatticeTransferState &State) {
   assert(E->getNumArgs() > 0);
 
   assignOptionalValue(*E, State,
@@ -474,8 +478,8 @@
 }
 
 void transferValueOrConversionAssignment(
-    const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
-    LatticeTransferState &State) {
+    const CFGElement &Element, const CXXOperatorCallExpr *E,
+    const MatchFinder::MatchResult &MatchRes, LatticeTransferState &State) {
   assert(E->getNumArgs() > 1);
   transferAssignment(E,
                      value_orConversionHasValue(*E->getDirectCallee(),
@@ -483,7 +487,8 @@
                      State);
 }
 
-void transferNulloptAssignment(const CXXOperatorCallExpr *E,
+void transferNulloptAssignment(const CFGElement &Element,
+                               const CXXOperatorCallExpr *E,
                                const MatchFinder::MatchResult &,
                                LatticeTransferState &State) {
   transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
@@ -502,7 +507,7 @@
   State.Env.setValue(OptionalLoc2, *OptionalVal1);
 }
 
-void transferSwapCall(const CXXMemberCallExpr *E,
+void transferSwapCall(const CFGElement &Element, const CXXMemberCallExpr *E,
                       const MatchFinder::MatchResult &,
                       LatticeTransferState &State) {
   assert(E->getNumArgs() == 1);
@@ -518,7 +523,8 @@
   transferSwap(*OptionalLoc1, *OptionalLoc2, State);
 }
 
-void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
+void transferStdSwapCall(const CFGElement &Element, const CallExpr *E,
+                         const MatchFinder::MatchResult &,
                          LatticeTransferState &State) {
   assert(E->getNumArgs() == 2);
 
@@ -573,14 +579,14 @@
       // optional::optional
       .CaseOfCFGStmt<CXXConstructExpr>(
           isOptionalInPlaceConstructor(),
-          [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
-             LatticeTransferState &State) {
+          [](const CFGElement &Element, const CXXConstructExpr *E,
+             const MatchFinder::MatchResult &, LatticeTransferState &State) {
             assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true));
           })
       .CaseOfCFGStmt<CXXConstructExpr>(
           isOptionalNulloptConstructor(),
-          [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
-             LatticeTransferState &State) {
+          [](const CFGElement &Element, const CXXConstructExpr *E,
+             const MatchFinder::MatchResult &, LatticeTransferState &State) {
             assignOptionalValue(*E, State,
                                 State.Env.getBoolLiteralValue(false));
           })
@@ -597,14 +603,14 @@
       // optional::value
       .CaseOfCFGStmt<CXXMemberCallExpr>(
           valueCall(IgnorableOptional),
-          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
-             LatticeTransferState &State) {
+          [](const CFGElement &Element, const CXXMemberCallExpr *E,
+             const MatchFinder::MatchResult &, LatticeTransferState &State) {
             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
           })
 
       // optional::operator*, optional::operator->
       .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
-                               [](const CallExpr *E,
+                               [](const CFGElement &Element, const CallExpr *E,
                                   const MatchFinder::MatchResult &,
                                   LatticeTransferState &State) {
                                  transferUnwrapCall(E, E->getArg(0), State);
@@ -623,8 +629,8 @@
       // optional::emplace
       .CaseOfCFGStmt<CXXMemberCallExpr>(
           isOptionalMemberCallWithName("emplace"),
-          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
-             LatticeTransferState &State) {
+          [](const CFGElement &Element, const CXXMemberCallExpr *E,
+             const MatchFinder::MatchResult &, LatticeTransferState &State) {
             assignOptionalValue(*E->getImplicitObjectArgument(), State,
                                 State.Env.getBoolLiteralValue(true));
           })
@@ -632,8 +638,8 @@
       // optional::reset
       .CaseOfCFGStmt<CXXMemberCallExpr>(
           isOptionalMemberCallWithName("reset"),
-          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
-             LatticeTransferState &State) {
+          [](const CFGElement &Element, const CXXMemberCallExpr *E,
+             const MatchFinder::MatchResult &, LatticeTransferState &State) {
             assignOptionalValue(*E->getImplicitObjectArgument(), State,
                                 State.Env.getBoolLiteralValue(false));
           })
@@ -687,16 +693,16 @@
       // optional::value
       .CaseOfCFGStmt<CXXMemberCallExpr>(
           valueCall(IgnorableOptional),
-          [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
-             const Environment &Env) {
+          [](const CFGElement &Element, const CXXMemberCallExpr *E,
+             const MatchFinder::MatchResult &, const Environment &Env) {
             return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env);
           })
 
       // optional::operator*, optional::operator->
       .CaseOfCFGStmt<CallExpr>(
           valueOperatorCall(IgnorableOptional),
-          [](const CallExpr *E, const MatchFinder::MatchResult &,
-             const Environment &Env) {
+          [](const CFGElement &Element, const CallExpr *E,
+             const MatchFinder::MatchResult &, const Environment &Env) {
             return diagnoseUnwrapCall(E, E->getArg(0), Env);
           })
       .Build();
Index: clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
+++ clang/include/clang/Analysis/FlowSensitive/MatchSwitch.h
@@ -52,7 +52,7 @@
 using MatchSwitchMatcher = ast_matchers::internal::Matcher<T>;
 
 template <typename T, typename State, typename Result = void>
-using MatchSwitchAction = std::function<Result(
+using ASTMatchSwitchAction = std::function<Result(
     const T *, const ast_matchers::MatchFinder::MatchResult &, State &)>;
 
 template <typename BaseT, typename State, typename Result = void>
@@ -94,8 +94,9 @@
   ///
   ///  `NodeT` should be derived from `BaseT`.
   template <typename NodeT>
-  ASTMatchSwitchBuilder &&CaseOf(MatchSwitchMatcher<BaseT> M,
-                                 MatchSwitchAction<NodeT, State, Result> A) && {
+  ASTMatchSwitchBuilder &&
+  CaseOf(MatchSwitchMatcher<BaseT> M,
+         ASTMatchSwitchAction<NodeT, State, Result> A) && {
     static_assert(std::is_base_of<BaseT, NodeT>::value,
                   "NodeT must be derived from BaseT.");
     Matchers.push_back(std::move(M));
@@ -158,7 +159,7 @@
   }
 
   std::vector<ast_matchers::internal::DynTypedMatcher> Matchers;
-  std::vector<MatchSwitchAction<BaseT, State, Result>> Actions;
+  std::vector<ASTMatchSwitchAction<BaseT, State, Result>> Actions;
 };
 
 // FIXME: Remove this alias when all usages of `MatchSwitchBuilder` are updated
Index: clang/include/clang/Analysis/FlowSensitive/CFGMatchSwitch.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/CFGMatchSwitch.h
+++ clang/include/clang/Analysis/FlowSensitive/CFGMatchSwitch.h
@@ -24,14 +24,47 @@
 #include "clang/Analysis/CFG.h"
 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
 #include <functional>
+#include <memory>
 #include <utility>
 
 namespace clang {
 namespace dataflow {
 
-template <typename State, typename Result = void>
-using CFGMatchSwitch =
-    std::function<Result(const CFGElement &, ASTContext &, State &)>;
+template <typename State, typename Result = void> class CFGMatchSwitch {
+public:
+  CFGMatchSwitch(std::unique_ptr<const CFGElement *> CurrentElement,
+                 ASTMatchSwitch<Stmt, State, Result> StmtMS,
+                 ASTMatchSwitch<CXXCtorInitializer, State, Result> InitMS)
+      : CurrentElement(std::move(CurrentElement)), StmtMS(std::move(StmtMS)),
+        InitMS(std::move(InitMS)) {}
+
+  Result operator()(const CFGElement &Element, ASTContext &Context, State &S) {
+    *CurrentElement = &Element;
+    switch (Element.getKind()) {
+    case CFGElement::Initializer:
+      return InitMS(*Element.castAs<CFGInitializer>().getInitializer(), Context,
+                    S);
+    case CFGElement::Statement:
+    case CFGElement::Constructor:
+    case CFGElement::CXXRecordTypedCall:
+      return StmtMS(*Element.castAs<CFGStmt>().getStmt(), Context, S);
+    default:
+      // FIXME: Handle other kinds of CFGElement.
+      return Result();
+    }
+  }
+
+private:
+  std::unique_ptr<const CFGElement *> CurrentElement;
+  ASTMatchSwitch<Stmt, State, Result> StmtMS;
+  ASTMatchSwitch<CXXCtorInitializer, State, Result> InitMS;
+};
+
+template <typename T, typename State, typename Result = void>
+using CFGMatchSwitchAction =
+    std::function<Result(const CFGElement &, const T *,
+                         const ast_matchers::MatchFinder::MatchResult &,
+                         State &)>;
 
 /// Collects cases of a "match switch": a collection of matchers paired with
 /// callbacks, which together define a switch that can be applied to an AST node
@@ -47,7 +80,26 @@
   template <typename NodeT>
   CFGMatchSwitchBuilder &&
   CaseOfCFGStmt(MatchSwitchMatcher<Stmt> M,
-                MatchSwitchAction<NodeT, State, Result> A) && {
+                CFGMatchSwitchAction<NodeT, State, Result> A) && {
+    std::move(StmtBuilder)
+        .template CaseOf<NodeT>(
+            M,
+            [A, CurrentElement = CurrentElement.get()](
+                const NodeT *N,
+                const ast_matchers::MatchFinder::MatchResult &MR,
+                State &S) -> Result { return A(**CurrentElement, N, MR, S); });
+    return std::move(*this);
+  }
+
+  // TODO: Refactor all callers to call the overload above and delete this
+  // function.
+  template <typename NodeT>
+  CFGMatchSwitchBuilder &&CaseOfCFGStmt(
+      MatchSwitchMatcher<Stmt> M,
+      std::function<Result(const NodeT *,
+                           const ast_matchers::MatchFinder::MatchResult &,
+                           State &)>
+          A) && {
     std::move(StmtBuilder).template CaseOf<NodeT>(M, A);
     return std::move(*this);
   }
@@ -62,34 +114,41 @@
   template <typename NodeT>
   CFGMatchSwitchBuilder &&
   CaseOfCFGInit(MatchSwitchMatcher<CXXCtorInitializer> M,
-                MatchSwitchAction<NodeT, State, Result> A) && {
+                CFGMatchSwitchAction<NodeT, State, Result> A) && {
+    std::move(InitBuilder)
+        .template CaseOf<NodeT>(
+            M,
+            [A, CurrentElement = CurrentElement.get()](
+                const NodeT *N,
+                const ast_matchers::MatchFinder::MatchResult &MR,
+                State &S) -> Result { return A(**CurrentElement, N, MR, S); });
+    return std::move(*this);
+  }
+
+  // TODO: Refactor all callers to call the overload above and delete this
+  // function.
+  template <typename NodeT>
+  CFGMatchSwitchBuilder &&CaseOfCFGInit(
+      MatchSwitchMatcher<CXXCtorInitializer> M,
+      std::function<Result(const NodeT *,
+                           const ast_matchers::MatchFinder::MatchResult &,
+                           State &)>
+          A) && {
     std::move(InitBuilder).template CaseOf<NodeT>(M, A);
     return std::move(*this);
   }
 
   CFGMatchSwitch<State, Result> Build() && {
-    return [StmtMS = std::move(StmtBuilder).Build(),
-            InitMS = std::move(InitBuilder).Build()](const CFGElement &Element,
-                                                     ASTContext &Context,
-                                                     State &S) -> Result {
-      switch (Element.getKind()) {
-      case CFGElement::Initializer:
-        return InitMS(*Element.castAs<CFGInitializer>().getInitializer(),
-                      Context, S);
-      case CFGElement::Statement:
-      case CFGElement::Constructor:
-      case CFGElement::CXXRecordTypedCall:
-        return StmtMS(*Element.castAs<CFGStmt>().getStmt(), Context, S);
-      default:
-        // FIXME: Handle other kinds of CFGElement.
-        return Result();
-      }
-    };
+    return CFGMatchSwitch<State, Result>(std::move(CurrentElement),
+                                         std::move(StmtBuilder).Build(),
+                                         std::move(InitBuilder).Build());
   }
 
 private:
   ASTMatchSwitchBuilder<Stmt, State, Result> StmtBuilder;
   ASTMatchSwitchBuilder<CXXCtorInitializer, State, Result> InitBuilder;
+  std::unique_ptr<const CFGElement *> CurrentElement =
+      std::make_unique<const CFGElement *>(nullptr);
 };
 
 } // namespace dataflow
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to