[clang] [analyzer] Add std::any checker (PR #76580)

Gábor Spaits via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 10 09:43:12 PST 2024


https://github.com/spaits updated https://github.com/llvm/llvm-project/pull/76580

>From a19329050600d4d89cc698b686d7aaa13e0c22c2 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Fri, 29 Dec 2023 17:54:34 +0100
Subject: [PATCH 01/20] [analyzer] Add std::any checker

---
 clang/docs/analyzer/checkers.rst              |  21 ++
 .../clang/StaticAnalyzer/Checkers/Checkers.td |   4 +
 .../StaticAnalyzer/Checkers/CMakeLists.txt    |   1 +
 .../StaticAnalyzer/Checkers/StdAnyChecker.cpp | 201 ++++++++++++++++++
 .../Checkers/StdVariantChecker.cpp            |  46 ++--
 .../Checkers/TaggedUnionModeling.h            |   9 +-
 .../Inputs/system-header-simulator-cxx.h      |  59 +++++
 clang/test/Analysis/std-any-checker.cpp       | 170 +++++++++++++++
 clang/test/Analysis/std-variant-checker.cpp   |   8 +
 9 files changed, 490 insertions(+), 29 deletions(-)
 create mode 100644 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
 create mode 100644 clang/test/Analysis/std-any-checker.cpp

diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index bb637cf1b8007b..867bdfc9012253 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -2095,6 +2095,27 @@ This checker is a part of ``core.StackAddressEscape``, but is temporarily disabl
                  //       returned block
  }
 
+.. _alpha-core-StdAny:
+
+alpha.core.StdAny (C++)
+"""""""""""""""""""""""
+Check if a value of active type is retrieved from an ``std::any`` instance with ``std::any_cats``
+or if the ``std::any`` instance holds type. In case of bad any type access
+(the accessed type differs from the active type, or the instance has no stored value)
+a warning is emitted. Currently, this checker does not take exception handling into account.
+
+.. code-block:: cpp
+
+ void anyCast() {
+  std::any a = 5;
+  char c = std::any_cast<char>(a); // // warn: "int" is the active alternative
+ }
+
+ void noTypeHeld() {
+  std::any a;
+  int i = std::any_cast<int>(a); // warn: any 'a' is empty
+ }
+
 .. _alpha-core-StdVariant:
 
 alpha.core.StdVariant (C++)
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index e7774e5a9392d2..954584fadb225d 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -321,6 +321,10 @@ def C11LockChecker : Checker<"C11Lock">,
   Dependencies<[PthreadLockBase]>,
   Documentation<HasDocumentation>;
 
+def StdAnyChecker : Checker<"StdAny">,
+  HelpText<"Check for bad type access for std::any.">,
+  Documentation<HasDocumentation>;
+
 def StdVariantChecker : Checker<"StdVariant">,
   HelpText<"Check for bad type access for std::variant.">,
   Documentation<HasDocumentation>;
diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 4443ffd0929388..4e3475ff7f37da 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -107,6 +107,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   SmartPtrChecker.cpp
   SmartPtrModeling.cpp
   StackAddrEscapeChecker.cpp
+  StdAnyChecker.cpp
   StdLibraryFunctionsChecker.cpp
   StdVariantChecker.cpp
   STLAlgorithmModeling.cpp
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
new file mode 100644
index 00000000000000..647ee72e555140
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -0,0 +1,201 @@
+//===- StdAnyChecker.cpp -------------------------------------*- C++ -*----===//
+//
+// 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/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/Support/ErrorHandling.h"
+
+#include "TaggedUnionModeling.h"
+
+using namespace clang;
+using namespace ento;
+using namespace tagged_union_modeling;
+
+REGISTER_MAP_WITH_PROGRAMSTATE(AnyHeldTypeMap, const MemRegion *, QualType)
+
+class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
+  CallDescription AnyConstructor{{"std", "any", "any"}};
+  CallDescription AnyAsOp{{"std", "any", "operator="}};
+  CallDescription AnyReset{{"std", "any", "reset"}, 0, 0};
+  CallDescription AnyCast{{"std", "any_cast"}, 1, 1};
+
+  BugType BadAnyType{this, "BadAnyType", "BadAnyType"};
+  BugType NullAnyType{this, "NullAnyType", "NullAnyType"};
+
+public:
+  ProgramStateRef
+  checkRegionChanges(ProgramStateRef State,
+                     const InvalidatedSymbols *Invalidated,
+                     ArrayRef<const MemRegion *> ExplicitRegions,
+                     ArrayRef<const MemRegion *> Regions,
+                     const LocationContext *LCtx, const CallEvent *Call) const {
+    if (!Call)
+      return State;
+
+    return removeInformationStoredForDeadInstances<AnyHeldTypeMap>(*Call, State,
+                                                                   Regions);
+  }
+
+  bool evalCall(const CallEvent &Call, CheckerContext &C) const {
+    // Do not take implementation details into consideration
+    if (Call.isCalledFromSystemHeader())
+      return false;
+
+    if (AnyCast.matches(Call))
+      return handleAnyCastCall(Call, C);
+
+    if (AnyReset.matches(Call)) {
+      const auto *AsMemberCall = dyn_cast<CXXMemberCall>(&Call);
+      if (!AsMemberCall)
+        return false;
+
+      const auto *ThisMemRegion = AsMemberCall->getCXXThisVal().getAsRegion();
+      if (!ThisMemRegion)
+        return false;
+
+      setNullTypeAny(ThisMemRegion, C);
+      return true;
+    }
+
+    bool IsAnyConstructor =
+        isa<CXXConstructorCall>(Call) && AnyConstructor.matches(Call);
+    bool IsAnyAssignmentOperatorCall =
+        isa<CXXMemberOperatorCall>(Call) && AnyAsOp.matches(Call);
+
+    if (IsAnyConstructor || IsAnyAssignmentOperatorCall) {
+      auto State = C.getState();
+      SVal ThisSVal = [&]() {
+        if (IsAnyConstructor) {
+          const auto *AsConstructorCall = dyn_cast<CXXConstructorCall>(&Call);
+          return AsConstructorCall->getCXXThisVal();
+        }
+        if (IsAnyAssignmentOperatorCall) {
+          const auto *AsMemberOpCall = dyn_cast<CXXMemberOperatorCall>(&Call);
+          return AsMemberOpCall->getCXXThisVal();
+        }
+        llvm_unreachable("We must have an assignment operator or constructor");
+      }();
+
+      // default constructor call
+      // in this case the any holds a null type
+      if (Call.getNumArgs() == 0) {
+        const auto *ThisMemRegion = ThisSVal.getAsRegion();
+        setNullTypeAny(ThisMemRegion, C);
+        return true;
+      }
+
+      if (Call.getNumArgs() != 1)
+        return false;
+
+      handleConstructorAndAssignment<AnyHeldTypeMap>(Call, C, ThisSVal);
+      return true;
+    }
+    return false;
+  }
+
+private:
+  // When an std::any is rested or default constructed it has a null type.
+  // We represent it by storing a null QualType.
+  void setNullTypeAny(const MemRegion *Mem, CheckerContext &C) const {
+    auto State = C.getState();
+    State = State->set<AnyHeldTypeMap>(Mem, QualType{});
+    C.addTransition(State);
+  }
+
+  // this function name is terrible
+  bool handleAnyCastCall(const CallEvent &Call, CheckerContext &C) const {
+    auto State = C.getState();
+
+    if (Call.getNumArgs() != 1)
+      return false;
+
+    auto ArgSVal = Call.getArgSVal(0);
+
+    // The argument is aether a const reference or a right value reference
+    //  We need the type referred
+    const auto *ArgType = ArgSVal.getType(C.getASTContext())
+                              .getTypePtr()
+                              ->getPointeeType()
+                              .getTypePtr();
+    if (!isStdAny(ArgType))
+      return false;
+
+    const auto *AnyMemRegion = ArgSVal.getAsRegion();
+
+    if (!State->contains<AnyHeldTypeMap>(AnyMemRegion))
+      return false;
+
+    // get the type we are trying to get from any
+    const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr());
+    const FunctionDecl *FD = CE->getDirectCallee();
+    if (FD->getTemplateSpecializationArgs()->size() < 1)
+      return false;
+
+    const auto &FirstTemplateArgument =
+        FD->getTemplateSpecializationArgs()->asArray()[0];
+    if (FirstTemplateArgument.getKind() != TemplateArgument::ArgKind::Type)
+      return false;
+
+    auto TypeOut = FirstTemplateArgument.getAsType();
+    const auto *TypeStored = State->get<AnyHeldTypeMap>(AnyMemRegion);
+
+    // Report when we try to use std::any_cast on an std::any that held a null
+    // type
+    if (TypeStored->isNull()) {
+      ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
+      if (!ErrNode)
+        return false;
+      llvm::SmallString<128> Str;
+      llvm::raw_svector_ostream OS(Str);
+      OS << "std::any " << AnyMemRegion->getDescriptiveName() << " is empty.";
+      auto R = std::make_unique<PathSensitiveBugReport>(NullAnyType, OS.str(),
+                                                        ErrNode);
+      C.emitReport(std::move(R));
+      return true;
+    }
+
+    // Check if the right type is being accessed
+    // There is spacial case for object types.
+    QualType RetrievedCanonicalType = TypeOut.getCanonicalType();
+    QualType StoredCanonicalType = TypeStored->getCanonicalType();
+    if (RetrievedCanonicalType == StoredCanonicalType)
+      return true;
+
+    // Report when the type we want to get out of std::any is wrong
+    ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
+    if (!ErrNode)
+      return false;
+    llvm::SmallString<128> Str;
+    llvm::raw_svector_ostream OS(Str);
+    std::string StoredTypeName = StoredCanonicalType.getAsString();
+    std::string RetrievedTypeName = RetrievedCanonicalType.getAsString();
+    OS << "std::any " << AnyMemRegion->getDescriptiveName() << " held "
+       << indefiniteArticleBasedOnVowel(StoredTypeName[0]) << " \'"
+       << StoredTypeName << "\', not "
+       << indefiniteArticleBasedOnVowel(RetrievedTypeName[0]) << " \'"
+       << RetrievedTypeName << "\'";
+    auto R =
+        std::make_unique<PathSensitiveBugReport>(BadAnyType, OS.str(), ErrNode);
+    C.emitReport(std::move(R));
+    return true;
+  }
+};
+
+bool clang::ento::shouldRegisterStdAnyChecker(
+    clang::ento::CheckerManager const &mgr) {
+  return true;
+}
+
+void clang::ento::registerStdAnyChecker(clang::ento::CheckerManager &mgr) {
+  mgr.registerChecker<StdAnyChecker>();
+}
\ No newline at end of file
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
index f7b7befe28ee7d..62ca9f0ae836e0 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
@@ -17,9 +17,7 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Casting.h"
 #include <optional>
-#include <string_view>
 
 #include "TaggedUnionModeling.h"
 
@@ -87,6 +85,28 @@ bool isStdVariant(const Type *Type) {
   return isStdType(Type, llvm::StringLiteral("variant"));
 }
 
+bool isStdAny(const Type *Type) {
+  return isStdType(Type, llvm::StringLiteral("any"));
+}
+
+bool isVowel(char a) {
+  switch (a) {
+  case 'a':
+  case 'e':
+  case 'i':
+  case 'o':
+  case 'u':
+    return true;
+  default:
+    return false;
+  }
+}
+
+llvm::StringRef indefiniteArticleBasedOnVowel(char a) {
+  if (isVowel(a))
+    return "an";
+  return "a";
+}
 } // end of namespace clang::ento::tagged_union_modeling
 
 static std::optional<ArrayRef<TemplateArgument>>
@@ -108,25 +128,6 @@ getNthTemplateTypeArgFromVariant(const Type *varType, unsigned i) {
   return (*VariantTemplates)[i].getAsType();
 }
 
-static bool isVowel(char a) {
-  switch (a) {
-  case 'a':
-  case 'e':
-  case 'i':
-  case 'o':
-  case 'u':
-    return true;
-  default:
-    return false;
-  }
-}
-
-static llvm::StringRef indefiniteArticleBasedOnVowel(char a) {
-  if (isVowel(a))
-    return "an";
-  return "a";
-}
-
 class StdVariantChecker : public Checker<eval::Call, check::RegionChanges> {
   // Call descriptors to find relevant calls
   CallDescription VariantConstructor{{"std", "variant", "variant"}};
@@ -184,9 +185,8 @@ class StdVariantChecker : public Checker<eval::Call, check::RegionChanges> {
       } else if (IsVariantAssignmentOperatorCall) {
         const auto &AsMemberOpCall = cast<CXXMemberOperatorCall>(Call);
         ThisSVal = AsMemberOpCall.getCXXThisVal();
-      } else {
+      } else
         return false;
-      }
 
       handleConstructorAndAssignment<VariantHeldTypeMap>(Call, C, ThisSVal);
       return true;
diff --git a/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h b/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h
index 6de33da107a3f9..760622f954f550 100644
--- a/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h
@@ -9,15 +9,9 @@
 #ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAGGEDUNIONMODELING_H
 #define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAGGEDUNIONMODELING_H
 
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "llvm/ADT/FoldingSet.h"
-#include <numeric>
 
 namespace clang::ento::tagged_union_modeling {
 
@@ -30,6 +24,9 @@ bool isMoveAssignmentCall(const CallEvent &Call);
 bool isMoveConstructorCall(const CallEvent &Call);
 bool isStdType(const Type *Type, const std::string &TypeName);
 bool isStdVariant(const Type *Type);
+bool isStdAny(const Type *Type);
+bool isVowel(char a);
+llvm::StringRef indefiniteArticleBasedOnVowel(char a);
 
 // When invalidating regions, we also have to follow that by invalidating the
 // corresponding custom data in the program state.
diff --git a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h
index 3ef7af2ea6c6ab..e1ad590526eb38 100644
--- a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h
+++ b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h
@@ -1372,6 +1372,65 @@ template <typename Ret, typename... Args> class packaged_task<Ret(Args...)> {
             typename = std::enable_if_t<!is_same_v<std::variant<Types...>, decay_t<T>>>>
     variant& operator=(T&&);
   };
+
+  class bad_any_cast;
+ 
+  // class any
+  class any;
+ 
+  // non-member functions
+  void swap(any& x, any& y) noexcept;
+ 
+  template<class T, class... Args>
+    any make_any(Args&&... args);
+  template<class T, class U, class... Args>
+    any make_any(initializer_list<U> il, Args&&... args);
+ 
+  template<class T>
+    T any_cast(const any& operand);
+  template<class T>
+    T any_cast(any& operand);
+  template<class T>
+    T any_cast(any&& operand);
+ 
+  template<class T>
+    const T* any_cast(const any* operand) noexcept;
+  template<class T>
+    T* any_cast(any* operand) noexcept;
+
+  class any {
+  public:
+    // construction and destruction
+    constexpr any() noexcept;
+ 
+    any(const any& other);
+    any(any&& other) noexcept;
+ 
+    template<class T>
+    any(T&& value);
+ 
+    ~any();
+ 
+    // assignments
+    any& operator=(const any& rhs);    
+    any& operator=(any&& rhs) noexcept;
+ 
+    template<typename T,
+            typename = std::enable_if_t<!is_same_v<std::any, decay_t<T>>>>
+    any& operator=(T&& rhs);
+ 
+    // modifiers
+    template<class T, class... Args>
+      decay_t<T>& emplace(Args&&...);
+    template<class T, class U, class... Args>
+      decay_t<T>& emplace(initializer_list<U>, Args&&...);
+    void reset() noexcept;
+    void swap(any& rhs) noexcept;
+ 
+    // observers
+    bool has_value() const noexcept;
+  };
+
   #endif
 
 } // namespace std
diff --git a/clang/test/Analysis/std-any-checker.cpp b/clang/test/Analysis/std-any-checker.cpp
new file mode 100644
index 00000000000000..d28d311b32d843
--- /dev/null
+++ b/clang/test/Analysis/std-any-checker.cpp
@@ -0,0 +1,170 @@
+// RUN: %clang %s -Xclang -verify --analyze \
+// RUN:   -Xclang -analyzer-checker=core \
+// RUN:   -Xclang -analyzer-checker=debug.ExprInspection \
+// RUN:   -Xclang -analyzer-checker=core,alpha.core.StdAny
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+void clang_analyzer_warnIfReached();
+void clang_analyzer_eval(int);
+
+
+class DummyClass{
+  public:
+  void foo(){};
+};
+
+void nonInlined(std::any &a);
+void nonInlinedConst(const std::any & a);
+
+void inlined(std::any &a) {
+  a = 5;
+}
+
+using any_t = std::any;
+using any_tt = any_t;
+
+
+//----------------------------------------------------------------------------//
+// std::any_cast
+//----------------------------------------------------------------------------//
+void objectHeld() {
+  std::any a = DummyClass{};
+  DummyClass d = std::any_cast<DummyClass>(a);
+  d.foo();
+}
+
+void formVariable() {
+  std::any a = 5;
+  int b = std::any_cast<int>(a);
+  char c = std::any_cast<char>(a); // expected-warning {{std::any 'a' held an 'int', not a 'char'}}
+  (void)b;
+  (void)c;
+}
+
+void pointerHeld() {
+  int i = 5;
+  std::any a = &i;
+  int* x = std::any_cast<int*>(a);
+  char c = std::any_cast<char>(a); // expected-warning {{std::any 'a' held an 'int *', not a 'char'}}
+  (void)x;
+  (void)c;
+}
+
+//----------------------------------------------------------------------------//
+// Empty std::any
+//----------------------------------------------------------------------------//
+
+void noTypeHeld() {
+  std::any a;
+  int i = std::any_cast<int>(a); // expected-warning {{any 'a' is empty}}
+  (void)i;
+}
+
+void reset() {
+  std::any a = 15;
+  a.reset();
+  int i = std::any_cast<int>(a); // expected-warning {{any 'a' is empty}}
+  (void)i;
+}
+
+
+//----------------------------------------------------------------------------//
+// Typedefs
+//----------------------------------------------------------------------------//
+
+void typedefedAny () {
+  any_t a = 5;
+  int i = std::any_cast<int>(a);
+  char c = std::any_cast<char>(a); // expected-warning {{std::any 'a' held an 'int', not a 'char'}}
+  (void)i;
+  (void)c;
+}
+
+void typedefedTypedefedAny () {
+  any_tt a = 5;
+  int i = std::any_cast<int>(a);
+  char c = std::any_cast<char>(a); // expected-warning {{std::any 'a' held an 'int', not a 'char'}}
+  (void)i;
+  (void)c;
+}
+
+//----------------------------------------------------------------------------//
+// Constructors and assignments
+//----------------------------------------------------------------------------//
+
+void assignmentOp () {
+  std::any a;
+  a = 5;
+  int i = std::any_cast<int>(a);
+  char c = std::any_cast<char>(a); // expected-warning {{std::any 'a' held an 'int', not a 'char'}}
+  (void)i;
+  (void)c;
+}
+
+void constructor() {
+  std::any a(5);
+  int i = std::any_cast<int>(a);
+  char c = std::any_cast<char>(a); // expected-warning {{std::any 'a' held an 'int', not a 'char'}}
+  (void)i;
+  (void)c;
+}
+
+void copyCtor() {
+  std::any a(5);
+  std::any b(a);
+  int i = std::any_cast<int>(a);
+  char c = std::any_cast<char>(a); // expected-warning {{std::any 'a' held an 'int', not a 'char'}}
+  (void)i;
+  (void)c;
+}
+
+void copyCtorNullType() {
+  std::any a;
+  std::any b(a);
+  char c = std::any_cast<char>(a); // expected-warning {{any 'a' is empty}}
+  (void)c;
+}
+
+void copyAssignment() {
+  std::any a = 5;
+  std::any b = 'c';
+  char c = std::any_cast<char>(b);
+  (void)c;
+  b = a;
+  int i = std::any_cast<int>(b);
+  c = std::any_cast<char>(b); // expected-warning {{std::any 'b' held an 'int', not a 'char'}}
+  (void)i;
+  (void)c;
+}
+
+//----------------------------------------------------------------------------//
+// Function calls
+//----------------------------------------------------------------------------//
+
+void nonInlinedRefCall() {
+  std::any a = 5;
+  nonInlined(a);
+  int i = std::any_cast<int>(a);
+  char c = std::any_cast<char>(a);
+  (void)i;
+  (void)c;
+}
+
+void nonInlinedConstRefCall() {
+  std::any a = 5;
+  nonInlinedConst(a);
+  int i = std::any_cast<int>(a);
+  char c = std::any_cast<char>(a); // expected-warning {{std::any 'a' held an 'int', not a 'char'}}
+  (void)i;
+  (void)c;
+}
+
+void inlinedCall() {
+  std::any a = 'c';
+  inlined(a);
+  int i = std::any_cast<int>(a);
+  char c = std::any_cast<char>(a); // expected-warning {{std::any 'a' held an 'int', not a 'char'}}
+  (void)i;
+  (void)c;
+}
\ No newline at end of file
diff --git a/clang/test/Analysis/std-variant-checker.cpp b/clang/test/Analysis/std-variant-checker.cpp
index 7f136c06b19cc6..bdae26742826af 100644
--- a/clang/test/Analysis/std-variant-checker.cpp
+++ b/clang/test/Analysis/std-variant-checker.cpp
@@ -58,6 +58,14 @@ void wontConfuseStdGets() {
 //----------------------------------------------------------------------------//
 // std::get
 //----------------------------------------------------------------------------//
+void stdGetType2() {
+  std::variant<int, char> v = {25};
+  int a = std::get<int>(v);
+  char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}}
+  (void)a;
+  (void)c;
+}
+
 void stdGetType() {
   std::variant<int, char> v = 25;
   int a = std::get<int>(v);

>From a0679b9dfc1a91372ef5108c3ee6f66f9130c64c Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Fri, 29 Dec 2023 21:33:16 +0100
Subject: [PATCH 02/20] Omit unused params for checkRegionChanges

---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 647ee72e555140..70852b832de518 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -33,12 +33,12 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
   BugType NullAnyType{this, "NullAnyType", "NullAnyType"};
 
 public:
-  ProgramStateRef
-  checkRegionChanges(ProgramStateRef State,
-                     const InvalidatedSymbols *Invalidated,
-                     ArrayRef<const MemRegion *> ExplicitRegions,
-                     ArrayRef<const MemRegion *> Regions,
-                     const LocationContext *LCtx, const CallEvent *Call) const {
+  ProgramStateRef checkRegionChanges(ProgramStateRef State,
+                                     const InvalidatedSymbols *,
+                                     ArrayRef<const MemRegion *>,
+                                     ArrayRef<const MemRegion *> Regions,
+                                     const LocationContext *,
+                                     const CallEvent *Call) const {
     if (!Call)
       return State;
 

>From f204040aed16c7ae0030a3db586e5239240dd342 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Fri, 29 Dec 2023 21:40:32 +0100
Subject: [PATCH 03/20] Add better explanation for BugTypes

---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 70852b832de518..2faee3cd1374f8 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -29,8 +29,8 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
   CallDescription AnyReset{{"std", "any", "reset"}, 0, 0};
   CallDescription AnyCast{{"std", "any_cast"}, 1, 1};
 
-  BugType BadAnyType{this, "BadAnyType", "BadAnyType"};
-  BugType NullAnyType{this, "NullAnyType", "NullAnyType"};
+  const BugType BadAnyType{this, "Bad std::any type access.", "BadAnyType"};
+  const BugType NullAnyType{this, "std::any has no value", "NullAnyType"};
 
 public:
   ProgramStateRef checkRegionChanges(ProgramStateRef State,

>From 065e61e09d6a151ea6b5ff9ff82ac11350e0f747 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Fri, 29 Dec 2023 21:41:26 +0100
Subject: [PATCH 04/20] Add explicit flag to use c++ 17 for tests

---
 clang/test/Analysis/std-any-checker.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Analysis/std-any-checker.cpp b/clang/test/Analysis/std-any-checker.cpp
index d28d311b32d843..4e495d4d62b48a 100644
--- a/clang/test/Analysis/std-any-checker.cpp
+++ b/clang/test/Analysis/std-any-checker.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang %s -Xclang -verify --analyze \
+// RUN: %clang %s -std=c++17 -Xclang -verify --analyze \
 // RUN:   -Xclang -analyzer-checker=core \
 // RUN:   -Xclang -analyzer-checker=debug.ExprInspection \
 // RUN:   -Xclang -analyzer-checker=core,alpha.core.StdAny

>From e88d89d20d6259dc05e52f065aad371b684e3967 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Fri, 29 Dec 2023 21:45:15 +0100
Subject: [PATCH 05/20] Change comments according to llvm style

---
 .../StaticAnalyzer/Checkers/StdAnyChecker.cpp  | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 2faee3cd1374f8..5f8af1e651ba8f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -86,8 +86,8 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
         llvm_unreachable("We must have an assignment operator or constructor");
       }();
 
-      // default constructor call
-      // in this case the any holds a null type
+      // Default constructor call.
+      // In this case the any holds a null type.
       if (Call.getNumArgs() == 0) {
         const auto *ThisMemRegion = ThisSVal.getAsRegion();
         setNullTypeAny(ThisMemRegion, C);
@@ -112,7 +112,6 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
     C.addTransition(State);
   }
 
-  // this function name is terrible
   bool handleAnyCastCall(const CallEvent &Call, CheckerContext &C) const {
     auto State = C.getState();
 
@@ -121,8 +120,8 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
 
     auto ArgSVal = Call.getArgSVal(0);
 
-    // The argument is aether a const reference or a right value reference
-    //  We need the type referred
+    // The argument is ether a const reference or a right value reference.
+    // We need the type referred.
     const auto *ArgType = ArgSVal.getType(C.getASTContext())
                               .getTypePtr()
                               ->getPointeeType()
@@ -135,7 +134,7 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
     if (!State->contains<AnyHeldTypeMap>(AnyMemRegion))
       return false;
 
-    // get the type we are trying to get from any
+    // Get the type we are trying to retrieve from any.
     const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr());
     const FunctionDecl *FD = CE->getDirectCallee();
     if (FD->getTemplateSpecializationArgs()->size() < 1)
@@ -150,7 +149,7 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
     const auto *TypeStored = State->get<AnyHeldTypeMap>(AnyMemRegion);
 
     // Report when we try to use std::any_cast on an std::any that held a null
-    // type
+    // type.
     if (TypeStored->isNull()) {
       ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
       if (!ErrNode)
@@ -164,14 +163,13 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
       return true;
     }
 
-    // Check if the right type is being accessed
-    // There is spacial case for object types.
+    // Check if the right type is being accessed.
     QualType RetrievedCanonicalType = TypeOut.getCanonicalType();
     QualType StoredCanonicalType = TypeStored->getCanonicalType();
     if (RetrievedCanonicalType == StoredCanonicalType)
       return true;
 
-    // Report when the type we want to get out of std::any is wrong
+    // Report when the type we want to get out of std::any is wrong.
     ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
     if (!ErrNode)
       return false;

>From 4c249ee7c4fc3ec2b992b664b66895436dbd0da4 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Fri, 29 Dec 2023 22:02:47 +0100
Subject: [PATCH 06/20] Change less to not equeal comparison

---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 5f8af1e651ba8f..c1b2af562e78d3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -137,7 +137,7 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
     // Get the type we are trying to retrieve from any.
     const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr());
     const FunctionDecl *FD = CE->getDirectCallee();
-    if (FD->getTemplateSpecializationArgs()->size() < 1)
+    if (FD->getTemplateSpecializationArgs()->size() != 1)
       return false;
 
     const auto &FirstTemplateArgument =

>From 7108efbb360861703aa73525df11c3d843b51aea Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Fri, 29 Dec 2023 22:11:57 +0100
Subject: [PATCH 07/20] Remove state manipulation from setNullTypeAny

---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index c1b2af562e78d3..df7b9c9c152bf2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -13,6 +13,7 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
 #include "llvm/Support/ErrorHandling.h"
 
 #include "TaggedUnionModeling.h"
@@ -63,7 +64,7 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
       if (!ThisMemRegion)
         return false;
 
-      setNullTypeAny(ThisMemRegion, C);
+      C.addTransition(setNullTypeAny(ThisMemRegion, C));
       return true;
     }
 
@@ -90,7 +91,7 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
       // In this case the any holds a null type.
       if (Call.getNumArgs() == 0) {
         const auto *ThisMemRegion = ThisSVal.getAsRegion();
-        setNullTypeAny(ThisMemRegion, C);
+        C.addTransition(setNullTypeAny(ThisMemRegion, C));
         return true;
       }
 
@@ -106,10 +107,10 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
 private:
   // When an std::any is rested or default constructed it has a null type.
   // We represent it by storing a null QualType.
-  void setNullTypeAny(const MemRegion *Mem, CheckerContext &C) const {
+  ProgramStateRef setNullTypeAny(const MemRegion *Mem, CheckerContext &C) const {
     auto State = C.getState();
-    State = State->set<AnyHeldTypeMap>(Mem, QualType{});
-    C.addTransition(State);
+    return State->set<AnyHeldTypeMap>(Mem, QualType{});
+    //C.addTransition(State);
   }
 
   bool handleAnyCastCall(const CallEvent &Call, CheckerContext &C) const {

>From 0631181759f5efc5f8268c6d736120567cd4ad4a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20Spaits?=
 <48805437+spaits at users.noreply.github.com>
Date: Wed, 3 Jan 2024 15:27:39 +0100
Subject: [PATCH 08/20] Update
 clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h

Co-authored-by: whisperity <whisperity at gmail.com>
---
 clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h b/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h
index 760622f954f550..75a804219f1b1c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h
@@ -23,8 +23,8 @@ bool isCopyAssignmentCall(const CallEvent &Call);
 bool isMoveAssignmentCall(const CallEvent &Call);
 bool isMoveConstructorCall(const CallEvent &Call);
 bool isStdType(const Type *Type, const std::string &TypeName);
-bool isStdVariant(const Type *Type);
 bool isStdAny(const Type *Type);
+bool isStdVariant(const Type *Type);
 bool isVowel(char a);
 llvm::StringRef indefiniteArticleBasedOnVowel(char a);
 

>From 8505607ee313709fa2682edc2be377b7128f59e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20Spaits?=
 <48805437+spaits at users.noreply.github.com>
Date: Wed, 3 Jan 2024 15:28:00 +0100
Subject: [PATCH 09/20] Update clang/docs/analyzer/checkers.rst

Co-authored-by: whisperity <whisperity at gmail.com>
---
 clang/docs/analyzer/checkers.rst | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index 867bdfc9012253..b4f58b09d3bdff 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -2099,10 +2099,12 @@ This checker is a part of ``core.StackAddressEscape``, but is temporarily disabl
 
 alpha.core.StdAny (C++)
 """""""""""""""""""""""
-Check if a value of active type is retrieved from an ``std::any`` instance with ``std::any_cats``
-or if the ``std::any`` instance holds type. In case of bad any type access
-(the accessed type differs from the active type, or the instance has no stored value)
-a warning is emitted. Currently, this checker does not take exception handling into account.
+Check if a value of active type is retrieved from a ``std::any`` instance with ``std::any_cast``
+or if the instance holds a value. In case of bad access
+(the accessed type differs from the active type, or the instance is empty)
+a warning is emitted.
+
+**Limitation:** The checker does not take exception handling into account.
 
 .. code-block:: cpp
 

>From 023ab28acdd8c692d16db97233721d9589e83c66 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20Spaits?=
 <48805437+spaits at users.noreply.github.com>
Date: Wed, 3 Jan 2024 15:28:19 +0100
Subject: [PATCH 10/20] Update
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp

Co-authored-by: whisperity <whisperity at gmail.com>
---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index df7b9c9c152bf2..35ec5bb1f229ce 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -105,8 +105,8 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
   }
 
 private:
-  // When an std::any is rested or default constructed it has a null type.
-  // We represent it by storing a null QualType.
+  // When a std::any is reset or default constructed it has a null type.
+  // We represent it by storing an empty QualType.
   ProgramStateRef setNullTypeAny(const MemRegion *Mem, CheckerContext &C) const {
     auto State = C.getState();
     return State->set<AnyHeldTypeMap>(Mem, QualType{});

>From 68a4d471e3b28f76dfc38587999f4b5bb3ca7d9a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20Spaits?=
 <48805437+spaits at users.noreply.github.com>
Date: Wed, 3 Jan 2024 15:28:48 +0100
Subject: [PATCH 11/20] Update
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp

Co-authored-by: whisperity <whisperity at gmail.com>
---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 35ec5bb1f229ce..98d9465d005bc4 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -121,7 +121,7 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
 
     auto ArgSVal = Call.getArgSVal(0);
 
-    // The argument is ether a const reference or a right value reference.
+    // The argument is either a const reference or a right value reference.
     // We need the type referred.
     const auto *ArgType = ArgSVal.getType(C.getASTContext())
                               .getTypePtr()

>From c6ce2ab3583c83eb31afab0f4949c1107cceb8b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20Spaits?=
 <48805437+spaits at users.noreply.github.com>
Date: Wed, 3 Jan 2024 15:29:15 +0100
Subject: [PATCH 12/20] Update
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp

Co-authored-by: whisperity <whisperity at gmail.com>
---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 98d9465d005bc4..2d6e17db84bb34 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -149,7 +149,7 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
     auto TypeOut = FirstTemplateArgument.getAsType();
     const auto *TypeStored = State->get<AnyHeldTypeMap>(AnyMemRegion);
 
-    // Report when we try to use std::any_cast on an std::any that held a null
+    // Report when we try to use std::any_cast on a std::any that held a null
     // type.
     if (TypeStored->isNull()) {
       ExplodedNode *ErrNode = C.generateNonFatalErrorNode();

>From baf9e52827b0ff3a7d3518604619a48082ee2702 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 4 Jan 2024 20:10:29 +0100
Subject: [PATCH 13/20] Remove redundant test case

---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp | 4 ++--
 clang/test/Analysis/std-variant-checker.cpp         | 8 --------
 2 files changed, 2 insertions(+), 10 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 2d6e17db84bb34..31d83993f1c3b2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -107,10 +107,10 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
 private:
   // When a std::any is reset or default constructed it has a null type.
   // We represent it by storing an empty QualType.
-  ProgramStateRef setNullTypeAny(const MemRegion *Mem, CheckerContext &C) const {
+  ProgramStateRef setNullTypeAny(const MemRegion *Mem,
+                                 CheckerContext &C) const {
     auto State = C.getState();
     return State->set<AnyHeldTypeMap>(Mem, QualType{});
-    //C.addTransition(State);
   }
 
   bool handleAnyCastCall(const CallEvent &Call, CheckerContext &C) const {
diff --git a/clang/test/Analysis/std-variant-checker.cpp b/clang/test/Analysis/std-variant-checker.cpp
index bdae26742826af..7f136c06b19cc6 100644
--- a/clang/test/Analysis/std-variant-checker.cpp
+++ b/clang/test/Analysis/std-variant-checker.cpp
@@ -58,14 +58,6 @@ void wontConfuseStdGets() {
 //----------------------------------------------------------------------------//
 // std::get
 //----------------------------------------------------------------------------//
-void stdGetType2() {
-  std::variant<int, char> v = {25};
-  int a = std::get<int>(v);
-  char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int', not a 'char'}}
-  (void)a;
-  (void)c;
-}
-
 void stdGetType() {
   std::variant<int, char> v = 25;
   int a = std::get<int>(v);

>From 2652148b44bba07373a2d8a9cfdc758db748ec2b Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 4 Jan 2024 20:15:19 +0100
Subject: [PATCH 14/20] Generate fatal errors for bad type access

---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp     | 2 +-
 clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 31d83993f1c3b2..62adf0ab4eac3b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -152,7 +152,7 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
     // Report when we try to use std::any_cast on a std::any that held a null
     // type.
     if (TypeStored->isNull()) {
-      ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
+      ExplodedNode *ErrNode = C.generateErrorNode();
       if (!ErrNode)
         return false;
       llvm::SmallString<128> Str;
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
index 62ca9f0ae836e0..1157b4f854963b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
@@ -269,7 +269,7 @@ class StdVariantChecker : public Checker<eval::Call, check::RegionChanges> {
     if (RetrievedCanonicalType == StoredCanonicalType)
       return true;
 
-    ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
+    ExplodedNode *ErrNode = C.generateErrorNode();
     if (!ErrNode)
       return false;
     llvm::SmallString<128> Str;

>From eecbe9d3eab99cc3b8101ecc05216454c3c95174 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 4 Jan 2024 20:23:38 +0100
Subject: [PATCH 15/20] Remove redundant escapes

---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp     | 8 ++++----
 clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp | 8 ++++----
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 62adf0ab4eac3b..87c8e8020d6117 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -179,10 +179,10 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
     std::string StoredTypeName = StoredCanonicalType.getAsString();
     std::string RetrievedTypeName = RetrievedCanonicalType.getAsString();
     OS << "std::any " << AnyMemRegion->getDescriptiveName() << " held "
-       << indefiniteArticleBasedOnVowel(StoredTypeName[0]) << " \'"
-       << StoredTypeName << "\', not "
-       << indefiniteArticleBasedOnVowel(RetrievedTypeName[0]) << " \'"
-       << RetrievedTypeName << "\'";
+       << indefiniteArticleBasedOnVowel(StoredTypeName[0]) << " '"
+       << StoredTypeName << "', not "
+       << indefiniteArticleBasedOnVowel(RetrievedTypeName[0]) << " '"
+       << RetrievedTypeName << "'";
     auto R =
         std::make_unique<PathSensitiveBugReport>(BadAnyType, OS.str(), ErrNode);
     C.emitReport(std::move(R));
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
index 1157b4f854963b..30bd6aeb1612ea 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
@@ -277,10 +277,10 @@ class StdVariantChecker : public Checker<eval::Call, check::RegionChanges> {
     std::string StoredTypeName = StoredType->getAsString();
     std::string RetrievedTypeName = RetrievedType.getAsString();
     OS << "std::variant " << ArgMemRegion->getDescriptiveName() << " held "
-       << indefiniteArticleBasedOnVowel(StoredTypeName[0]) << " \'"
-       << StoredTypeName << "\', not "
-       << indefiniteArticleBasedOnVowel(RetrievedTypeName[0]) << " \'"
-       << RetrievedTypeName << "\'";
+       << indefiniteArticleBasedOnVowel(StoredTypeName[0]) << " '"
+       << StoredTypeName << "', not "
+       << indefiniteArticleBasedOnVowel(RetrievedTypeName[0]) << " '"
+       << RetrievedTypeName << "'";
     auto R = std::make_unique<PathSensitiveBugReport>(BadVariantType, OS.str(),
                                                       ErrNode);
     C.emitReport(std::move(R));

>From eb179836b91370e0ee2704d4175d8ac5e133847f Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 4 Jan 2024 20:27:28 +0100
Subject: [PATCH 16/20] Use get instead of asArray()[0]

---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp     | 2 +-
 clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 87c8e8020d6117..4c4ef2a38eeaa2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -142,7 +142,7 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
       return false;
 
     const auto &FirstTemplateArgument =
-        FD->getTemplateSpecializationArgs()->asArray()[0];
+        FD->getTemplateSpecializationArgs()->get(0);
     if (FirstTemplateArgument.getKind() != TemplateArgument::ArgKind::Type)
       return false;
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
index 30bd6aeb1612ea..182b91439166be 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
@@ -240,7 +240,7 @@ class StdVariantChecker : public Checker<eval::Call, check::RegionChanges> {
     if (FD->getTemplateSpecializationArgs()->size() < 1)
       return false;
 
-    const auto &TypeOut = FD->getTemplateSpecializationArgs()->asArray()[0];
+    const auto &TypeOut = FD->getTemplateSpecializationArgs()->get(0);
     // std::get's first template parameter can be the type we want to get
     // out of the std::variant or a natural number which is the position of
     // the requested type in the argument type list of the std::variant's

>From a2569cd47574657ad919251c9ecbe6557cb2ce86 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 4 Jan 2024 20:34:05 +0100
Subject: [PATCH 17/20] Do note use State->contains() unnecessarily

---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 4c4ef2a38eeaa2..aa3a8587ca13d3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -132,7 +132,8 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
 
     const auto *AnyMemRegion = ArgSVal.getAsRegion();
 
-    if (!State->contains<AnyHeldTypeMap>(AnyMemRegion))
+    const auto *TypeStored = State->get<AnyHeldTypeMap>(AnyMemRegion);
+    if (!TypeStored)
       return false;
 
     // Get the type we are trying to retrieve from any.
@@ -147,7 +148,6 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
       return false;
 
     auto TypeOut = FirstTemplateArgument.getAsType();
-    const auto *TypeStored = State->get<AnyHeldTypeMap>(AnyMemRegion);
 
     // Report when we try to use std::any_cast on a std::any that held a null
     // type.

>From b62d0d105866dfcc9daa06095252fb24bf60aaff Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Thu, 4 Jan 2024 20:43:05 +0100
Subject: [PATCH 18/20] Move handlig of any.reset to a handler

---
 .../StaticAnalyzer/Checkers/StdAnyChecker.cpp | 27 ++++++++++---------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index aa3a8587ca13d3..55154443403108 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -55,18 +55,8 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
     if (AnyCast.matches(Call))
       return handleAnyCastCall(Call, C);
 
-    if (AnyReset.matches(Call)) {
-      const auto *AsMemberCall = dyn_cast<CXXMemberCall>(&Call);
-      if (!AsMemberCall)
-        return false;
-
-      const auto *ThisMemRegion = AsMemberCall->getCXXThisVal().getAsRegion();
-      if (!ThisMemRegion)
-        return false;
-
-      C.addTransition(setNullTypeAny(ThisMemRegion, C));
-      return true;
-    }
+    if (AnyReset.matches(Call))
+      return handleResetCall(Call, C);
 
     bool IsAnyConstructor =
         isa<CXXConstructorCall>(Call) && AnyConstructor.matches(Call);
@@ -188,6 +178,19 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
     C.emitReport(std::move(R));
     return true;
   }
+
+  bool handleResetCall(const CallEvent &Call, CheckerContext &C) const {
+    const auto *AsMemberCall = dyn_cast<CXXMemberCall>(&Call);
+    if (!AsMemberCall)
+      return false;
+
+    const auto *ThisMemRegion = AsMemberCall->getCXXThisVal().getAsRegion();
+    if (!ThisMemRegion)
+      return false;
+
+    C.addTransition(setNullTypeAny(ThisMemRegion, C));
+    return true;
+  }
 };
 
 bool clang::ento::shouldRegisterStdAnyChecker(

>From a867b4070ca56aab42f2f9cb0bda57d4b511f1a2 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Wed, 10 Jan 2024 18:31:26 +0100
Subject: [PATCH 19/20] Remove IIFE :(

---
 .../StaticAnalyzer/Checkers/StdAnyChecker.cpp | 52 ++++++++-----------
 1 file changed, 22 insertions(+), 30 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 55154443403108..5e4d3ca3f0594b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -58,39 +58,31 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
     if (AnyReset.matches(Call))
       return handleResetCall(Call, C);
 
-    bool IsAnyConstructor =
-        isa<CXXConstructorCall>(Call) && AnyConstructor.matches(Call);
-    bool IsAnyAssignmentOperatorCall =
-        isa<CXXMemberOperatorCall>(Call) && AnyAsOp.matches(Call);
-
-    if (IsAnyConstructor || IsAnyAssignmentOperatorCall) {
-      auto State = C.getState();
-      SVal ThisSVal = [&]() {
-        if (IsAnyConstructor) {
-          const auto *AsConstructorCall = dyn_cast<CXXConstructorCall>(&Call);
-          return AsConstructorCall->getCXXThisVal();
-        }
-        if (IsAnyAssignmentOperatorCall) {
-          const auto *AsMemberOpCall = dyn_cast<CXXMemberOperatorCall>(&Call);
-          return AsMemberOpCall->getCXXThisVal();
-        }
-        llvm_unreachable("We must have an assignment operator or constructor");
-      }();
-
-      // Default constructor call.
-      // In this case the any holds a null type.
-      if (Call.getNumArgs() == 0) {
-        const auto *ThisMemRegion = ThisSVal.getAsRegion();
-        C.addTransition(setNullTypeAny(ThisMemRegion, C));
-        return true;
-      }
-
-      if (Call.getNumArgs() != 1)
-        return false;
+    const auto *AsCtorCall = dyn_cast_or_null<CXXConstructorCall>(
+        AnyConstructor.matches(Call) ? &Call : nullptr);
+    const auto *AsAssignCall = dyn_cast_or_null<CXXMemberOperatorCall>(
+        AnyAsOp.matches(Call) ? &Call : nullptr);
+
+    if (!AsCtorCall && !AsAssignCall)
+      return false;
 
-      handleConstructorAndAssignment<AnyHeldTypeMap>(Call, C, ThisSVal);
+    SVal ThisSVal = AsCtorCall ? AsCtorCall->getCXXThisVal()
+                               : AsAssignCall->getCXXThisVal();
+
+    // Default constructor call.
+    // In this case the any holds a null type.
+    if (Call.getNumArgs() == 0) {
+      const auto *ThisMemRegion = ThisSVal.getAsRegion();
+      C.addTransition(setNullTypeAny(ThisMemRegion, C));
       return true;
     }
+
+    if (Call.getNumArgs() != 1)
+      return false;
+
+    handleConstructorAndAssignment<AnyHeldTypeMap>(Call, C, ThisSVal);
+    return true;
+
     return false;
   }
 

>From 52c62979626a34eba6d28fd9e9e93adec31bf3ed Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Wed, 10 Jan 2024 18:42:28 +0100
Subject: [PATCH 20/20] Use Expr instead of SVal when dealing with
 std::any_cast

---
 clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
index 5e4d3ca3f0594b..a6e1077497a31d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdAnyChecker.cpp
@@ -102,13 +102,11 @@ class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
       return false;
 
     auto ArgSVal = Call.getArgSVal(0);
+    const auto *ArgExpr = Call.getArgExpr(0);
+    if (!ArgExpr)
+      return false;
 
-    // The argument is either a const reference or a right value reference.
-    // We need the type referred.
-    const auto *ArgType = ArgSVal.getType(C.getASTContext())
-                              .getTypePtr()
-                              ->getPointeeType()
-                              .getTypePtr();
+    const auto *ArgType = ArgExpr->getType().getTypePtr();
     if (!isStdAny(ArgType))
       return false;
 



More information about the cfe-commits mailing list