Charusso created this revision.
Charusso added a reviewer: NoQ.
Charusso added a project: clang.
Herald added subscribers: cfe-commits, dkrupp, donat.nagy, Szelethus, 
mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, xazax.hun.

The key is `CXXRecordDecl::isDerivedFrom()`.


Repository:
  rC Clang

https://reviews.llvm.org/D67079

Files:
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
  clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
  clang/lib/StaticAnalyzer/Core/DynamicType.cpp
  clang/test/Analysis/cast-value-hierarchy.cpp
  clang/test/Analysis/cast-value-notes.cpp
  clang/test/Analysis/cast-value-state-dump.cpp
  clang/test/Analysis/expr-inspection.c

Index: clang/test/Analysis/expr-inspection.c
===================================================================
--- clang/test/Analysis/expr-inspection.c
+++ clang/test/Analysis/expr-inspection.c
@@ -38,7 +38,7 @@
 // CHECK-NEXT:     { "symbol": "reg_$0<int x>", "range": "{ [-2147483648, 13] }" }
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "dynamic_types": null,
-// CHECK-NEXT:   "dynamic_casts": null,
+// CHECK-NEXT:   "failed_casts": null,
 // CHECK-NEXT:   "constructing_objects": null,
 // CHECK-NEXT:   "checker_messages": null
 // CHECK-NEXT: }
Index: clang/test/Analysis/cast-value-state-dump.cpp
===================================================================
--- clang/test/Analysis/cast-value-state-dump.cpp
+++ clang/test/Analysis/cast-value-state-dump.cpp
@@ -11,19 +11,31 @@
 class Triangle : public Shape {};
 class Circle : public Shape {};
 class Square : public Shape {};
+class Octagram : public Shape {};
 } // namespace clang
 
 using namespace llvm;
 using namespace clang;
 
 void evalNonNullParamNonNullReturn(const Shape *S) {
+  if (isa<Triangle>(S)) {
+    // expected-note@-1 {{Assuming 'S' is not a 'Triangle'}}
+    // expected-note@-2 {{Taking false branch}}
+    return;
+  }
+
+  if (isa<Octagram>(S)) {
+    // expected-note@-1 {{Assuming 'S' is not an 'Octagram'}}
+    // expected-note@-2 {{Taking false branch}}
+    return;
+  }
+
   const auto *C = dyn_cast_or_null<Circle>(S);
   // expected-note@-1 {{Assuming 'S' is a 'Circle'}}
   // expected-note@-2 {{'C' initialized here}}
 
-  // FIXME: We assumed that 'S' is a 'Circle' therefore it is not a 'Square'.
   if (dyn_cast_or_null<Square>(S)) {
-    // expected-note@-1 {{Assuming 'S' is not a 'Square'}}
+    // expected-note@-1 {{'S' is not a 'Square'}}
     // expected-note@-2 {{Taking false branch}}
     return;
   }
@@ -33,11 +45,11 @@
   // CHECK:      "dynamic_types": [
   // CHECK-NEXT:   { "region": "SymRegion{reg_$0<const struct clang::Shape * S>}", "dyn_type": "const class clang::Circle", "sub_classable": true }
   // CHECK-NEXT: ],
-  // CHECK-NEXT: "dynamic_casts": [
-  // CHECK:        { "region": "SymRegion{reg_$0<const struct clang::Shape * S>}", "casts": [
-  // CHECK-NEXT:     { "from": "const struct clang::Shape *", "to": "const class clang::Circle *", "kind": "success" },
-  // CHECK-NEXT:     { "from": "const struct clang::Shape *", "to": "const class clang::Square *", "kind": "fail" }
+  // CHECK-NEXT: "failed_casts": [
+  // CHECK-NEXT:   { "region": "SymRegion{reg_$0<const struct clang::Shape * S>}", "casts": [
+  // CHECK-NEXT:     "const class clang::Square *", "class clang::Triangle *", "class clang::Octagram *"
   // CHECK-NEXT:   ]}
+  // CHECK-NEXT: ],
 
   (void)(1 / !C);
   // expected-note@-1 {{'C' is non-null}}
Index: clang/test/Analysis/cast-value-notes.cpp
===================================================================
--- clang/test/Analysis/cast-value-notes.cpp
+++ clang/test/Analysis/cast-value-notes.cpp
@@ -37,12 +37,6 @@
     return;
   }
 
-  if (dyn_cast_or_null<Triangle>(C)) {
-    // expected-note@-1 {{Assuming 'C' is not a 'Triangle'}}
-    // expected-note@-2 {{Taking false branch}}
-    return;
-  }
-
   if (isa<Triangle>(C)) {
     // expected-note@-1 {{'C' is not a 'Triangle'}}
     // expected-note@-2 {{Taking false branch}}
@@ -65,14 +59,8 @@
   // expected-note@-1 {{'S' is a 'Circle'}}
   // expected-note@-2 {{'C' initialized here}}
 
-  if (!isa<Triangle>(C)) {
-    // expected-note@-1 {{Assuming 'C' is a 'Triangle'}}
-    // expected-note@-2 {{Taking false branch}}
-    return;
-  }
-
-  if (!isa<Triangle>(C)) {
-    // expected-note@-1 {{'C' is a 'Triangle'}}
+  if (isa<Triangle>(C)) {
+    // expected-note@-1 {{'C' is not a 'Triangle'}}
     // expected-note@-2 {{Taking false branch}}
     return;
   }
Index: clang/test/Analysis/cast-value-hierarchy.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/cast-value-hierarchy.cpp
@@ -0,0 +1,54 @@
+// RUN: %clang_analyze_cc1 \
+// RUN:  -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\
+// RUN:  -verify %s
+
+#include "Inputs/llvm.h"
+
+using namespace llvm;
+
+void clang_analyzer_numTimesReached();
+void clang_analyzer_printState();
+void clang_analyzer_warnIfReached();
+void clang_analyzer_eval(bool);
+
+struct A {};        //     A
+struct B : A {};    //    / \.
+struct C : A {};    //   B   C
+struct D : B {};    //  / \.
+struct E : B {};    // D   E
+struct F : D, E {}; //  \ / \.
+struct G : E {};    //   F   G
+
+void test_downcast(const A *a) {
+  const B *b = dyn_cast<B>(a);
+
+  if (dyn_cast<E>(b))
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+}
+
+void test_isa_downcast(const A *a) {
+  if (isa<B>(a)) {
+    if (isa<E>(a))
+      clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  }
+}
+
+void test_diamond(const A *a) {
+  if (isa<B>(a))
+    if (isa<D>(a) || isa<E>(a))
+      if (isa<F>(a))
+        clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+}
+
+void test_upcast(const A *a) {
+  const E *e = dyn_cast<E>(a);
+
+  if (dyn_cast<B>(e))
+    clang_analyzer_warnIfReached(); // no-warning
+}
+
+void test_isa_upcast(const A *a) {
+  if (isa<E>(a))
+    if (isa<B>(a))
+      clang_analyzer_warnIfReached(); // no-warning
+}
Index: clang/lib/StaticAnalyzer/Core/DynamicType.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/DynamicType.cpp
+++ clang/lib/StaticAnalyzer/Core/DynamicType.cpp
@@ -27,18 +27,22 @@
 REGISTER_MAP_WITH_PROGRAMSTATE(DynamicTypeMap, const clang::ento::MemRegion *,
                                clang::ento::DynamicTypeInfo)
 
-/// A set factory of dynamic cast informations.
-REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(CastSet, clang::ento::DynamicCastInfo)
+/// A set factory of dynamic cast informations as type informations.
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(CastSet, clang::ento::DynamicTypeInfo)
 
-/// A map from symbols to cast informations.
-REGISTER_MAP_WITH_PROGRAMSTATE(DynamicCastMap, const clang::ento::MemRegion *,
+/// A map from symbols to failed cast informations.
+REGISTER_MAP_WITH_PROGRAMSTATE(FailedCastMap, const clang::ento::MemRegion *,
                                CastSet)
 
 namespace clang {
 namespace ento {
 
+const MemRegion *getSimplifiedRegion(const MemRegion *MR) {
+  return MR->StripCasts();
+}
+
 DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR) {
-  MR = MR->StripCasts();
+  MR = getSimplifiedRegion(MR);
 
   // Look up the dynamic type in the GDM.
   if (const DynamicTypeInfo *DTI = State->get<DynamicTypeMap>(MR))
@@ -58,27 +62,40 @@
 
 const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State,
                                              const MemRegion *MR) {
+  if (!MR)
+    return nullptr;
+
+  MR = getSimplifiedRegion(MR);
   return State->get<DynamicTypeMap>(MR);
 }
 
-const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
-                                          const MemRegion *MR,
-                                          QualType CastFromTy,
-                                          QualType CastToTy) {
-  const auto *Lookup = State->get<DynamicCastMap>().lookup(MR);
+const DynamicTypeInfo *getFailedCastInfo(ProgramStateRef State,
+                                         const MemRegion *MR,
+                                         QualType CastToTy) {
+  if (!MR)
+    return nullptr;
+
+  MR = getSimplifiedRegion(MR);
+  const auto *Lookup = State->get<FailedCastMap>().lookup(MR);
   if (!Lookup)
     return nullptr;
 
-  for (const DynamicCastInfo &Cast : *Lookup)
-    if (Cast.equals(CastFromTy, CastToTy))
+  for (const DynamicTypeInfo &Cast : *Lookup) {
+    if (Cast.getType()->getPointeeCXXRecordDecl() ==
+        CastToTy->getPointeeCXXRecordDecl())
       return &Cast;
+  }
 
   return nullptr;
 }
 
 ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
                                    DynamicTypeInfo NewTy) {
-  State = State->set<DynamicTypeMap>(MR->StripCasts(), NewTy);
+  assert((NewTy.getType()->isAnyPointerType() ||
+          NewTy.getType()->isReferenceType()) &&
+         "DynamicTypeInfo should always be a pointer.");
+  MR = getSimplifiedRegion(MR);
+  State = State->set<DynamicTypeMap>(MR, NewTy);
   assert(State);
   return State;
 }
@@ -90,29 +107,31 @@
 
 ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State,
                                           const MemRegion *MR,
-                                          QualType CastFromTy,
                                           QualType CastToTy,
+                                          const DynamicTypeInfo *CastInfo,
                                           bool CastSucceeds) {
   if (!MR)
     return State;
 
-  if (CastSucceeds) {
-    assert((CastToTy->isAnyPointerType() || CastToTy->isReferenceType()) &&
-           "DynamicTypeInfo should always be a pointer.");
-    State = State->set<DynamicTypeMap>(MR, CastToTy);
-  }
+  MR = getSimplifiedRegion(MR);
 
-  DynamicCastInfo::CastResult ResultKind =
-      CastSucceeds ? DynamicCastInfo::CastResult::Success
-                   : DynamicCastInfo::CastResult::Failure;
+  if (CastSucceeds) {
+    State = setDynamicTypeInfo(State, MR, CastToTy);
+  } else {
+    CastSet::Factory &F = State->get_context<CastSet>();
 
-  CastSet::Factory &F = State->get_context<CastSet>();
+    const CastSet *TempSet = State->get<FailedCastMap>(MR);
+    CastSet Set = TempSet ? *TempSet : F.getEmptySet();
 
-  const CastSet *TempSet = State->get<DynamicCastMap>(MR);
-  CastSet Set = TempSet ? *TempSet : F.getEmptySet();
+    // If the 'CastToTy' is more derived remove the old cast information.
+    if (CastInfo && CastToTy->getPointeeCXXRecordDecl()->isDerivedFrom(
+                        CastInfo->getType()->getPointeeCXXRecordDecl())) {
+      Set = F.remove(Set, *CastInfo);
+    }
 
-  Set = F.add(Set, {CastFromTy, CastToTy, ResultKind});
-  State = State->set<DynamicCastMap>(MR, Set);
+    Set = F.add(Set, CastToTy);
+    State = State->set<FailedCastMap>(MR, Set);
+  }
 
   assert(State);
   return State;
@@ -123,7 +142,7 @@
                            SymbolReaper &SR) {
   for (const auto &Elem : Map)
     if (!SR.isLiveRegion(Elem.first))
-      State = State->remove<DynamicCastMap>(Elem.first);
+      State = State->remove<FailedCastMap>(Elem.first);
 
   return State;
 }
@@ -133,7 +152,7 @@
 }
 
 ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR) {
-  return removeDead(State, State->get<DynamicCastMap>(), SR);
+  return removeDead(State, State->get<FailedCastMap>(), SR);
 }
 
 static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State,
@@ -175,9 +194,9 @@
 static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State,
                                   const char *NL, unsigned int Space,
                                   bool IsDot) {
-  Indent(Out, Space, IsDot) << "\"dynamic_casts\": ";
+  Indent(Out, Space, IsDot) << "\"failed_casts\": ";
 
-  const DynamicCastMapTy &Map = State->get<DynamicCastMap>();
+  const FailedCastMapTy &Map = State->get<FailedCastMap>();
   if (Map.isEmpty()) {
     Out << "null," << NL;
     return;
@@ -185,7 +204,7 @@
 
   ++Space;
   Out << '[' << NL;
-  for (DynamicCastMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
+  for (FailedCastMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
     const MemRegion *MR = I->first;
     const CastSet &Set = I->second;
 
@@ -195,16 +214,14 @@
     } else {
       ++Space;
       Out << '[' << NL;
+      Indent(Out, Space, IsDot);
       for (CastSet::iterator SI = Set.begin(); SI != Set.end(); ++SI) {
-        Indent(Out, Space, IsDot)
-            << "{ \"from\": \"" << SI->from().getAsString() << "\", \"to\": \""
-            << SI->to().getAsString() << "\", \"kind\": \""
-            << (SI->succeeds() ? "success" : "fail") << "\" }";
+        Out << '\"' << SI->getType().getAsString() << '\"';
 
         if (std::next(SI) != Set.end())
-          Out << ',';
-        Out << NL;
+          Out << ", ";
       }
+      Out << NL;
       --Space;
       Indent(Out, Space, IsDot) << ']';
     }
Index: clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -8,12 +8,6 @@
 //
 //  This defines CastValueChecker which models casts of custom RTTIs.
 //
-// TODO list:
-// - It only allows one succesful cast between two types however in the wild
-//   the object could be casted to multiple types.
-// - It needs to check the most likely type information from the dynamic type
-//   map to increase precision of dynamic casting.
-//
 //===----------------------------------------------------------------------===//
 
 #include "clang/AST/DeclTemplate.h"
@@ -92,21 +86,103 @@
 };
 } // namespace
 
-static bool isInfeasibleCast(const DynamicCastInfo *CastInfo,
+static bool isDerivedFrom(QualType X, QualType Y) {
+  const CXXRecordDecl *XRD = X->isPointerType() ? X->getPointeeCXXRecordDecl()
+                                                : X->getAsCXXRecordDecl();
+  const CXXRecordDecl *YRD = Y->isPointerType() ? Y->getPointeeCXXRecordDecl()
+                                                : Y->getAsCXXRecordDecl();
+  if (!XRD || !YRD)
+    return true;
+
+  return XRD->isDerivedFrom(YRD);
+}
+
+static bool isInfeasibleCast(const DynamicTypeInfo *SucceededCastInfo,
+                             const DynamicTypeInfo *FailedCastInfo,
+                             QualType CastFromTy, QualType CastToTy,
                              bool CastSucceeds) {
-  if (!CastInfo)
-    return false;
+  // Load the proper dynamic cast information.
+  if (CastSucceeds && SucceededCastInfo)
+    CastFromTy = SucceededCastInfo->getType();
+  else if (!CastSucceeds && FailedCastInfo)
+    CastFromTy = FailedCastInfo->getType();
+
+  // Upcast is infeasible.
+  if (CastSucceeds && isDerivedFrom(CastFromTy, CastToTy))
+    return true;
 
-  return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds();
+  return false;
 }
 
-static const NoteTag *getNoteTag(CheckerContext &C,
-                                 const DynamicCastInfo *CastInfo,
-                                 QualType CastToTy, const Expr *Object,
-                                 bool CastSucceeds, bool IsKnownCast) {
+static bool isSucceededDowncast(const DynamicTypeInfo *CastInfo,
+                                QualType CastToTy) {
+  const CXXRecordDecl *CurrentRD = CastToTy->getPointeeCXXRecordDecl();
+  const CXXRecordDecl *PreviousRD =
+      CastInfo->getType()->getPointeeCXXRecordDecl();
+
+  if (CurrentRD == PreviousRD)
+    return true;
+
+  // If the casts have a common anchestor it could not be a succeeded downcast.
+  for (const auto &PreviousBase : PreviousRD->bases())
+    for (const auto &CurrentBase : CurrentRD->bases())
+      if (PreviousBase.getType()->getAsCXXRecordDecl() ==
+          CurrentBase.getType()->getAsCXXRecordDecl())
+        return false;
+
+  return true;
+}
+
+namespace {
+struct CastContext {
+  CastContext(ProgramStateRef State, const MemRegion *MR, QualType CastFromTy,
+              QualType CastToTy, bool Assumption, bool IsCheckedCast) {
+    const DynamicTypeInfo *SucceededCastInfo = getRawDynamicTypeInfo(State, MR);
+    const DynamicTypeInfo *FailedCastInfo =
+        getFailedCastInfo(State, MR, CastToTy);
+
+    // We assume that every checked cast succeeds.
+    bool IsAlwaysSucceeds =
+        IsCheckedCast || CastFromTy->getPointeeCXXRecordDecl() ==
+                             CastToTy->getPointeeCXXRecordDecl();
+
+    CastSucceeds = Assumption || IsAlwaysSucceeds;
+
+    // We need to check if the current new succeeded cast is a new downcast.
+    IsKnownCast = true;
+    if (CastSucceeds && SucceededCastInfo && !IsAlwaysSucceeds) {
+      CastSucceeds = isSucceededDowncast(SucceededCastInfo, CastToTy);
+      if (CastSucceeds) // A succeeded downcast is a new assumption.
+        IsKnownCast = false;
+    }
+
+    if (IsKnownCast)
+      IsKnownCast = IsAlwaysSucceeds || SucceededCastInfo ||
+                    (!CastSucceeds && FailedCastInfo);
+
+    CastInfo = CastSucceeds ? SucceededCastInfo : FailedCastInfo;
+
+    IsInfeasible = isInfeasibleCast(SucceededCastInfo, FailedCastInfo,
+                                    CastFromTy, CastToTy, CastSucceeds);
+  }
+
+  bool IsKnownCast, CastSucceeds, IsInfeasible;
+  const DynamicTypeInfo *CastInfo;
+};
+} // namespace
+
+constexpr llvm::StringLiteral Vowels = "aeiou";
+
+static const NoteTag *getNoteTag(CheckerContext &C, const Expr *Object,
+                                 QualType CastToTy, CastContext &CastCtx) {
+  QualType KnownCastToTy;
+  if (CastCtx.CastInfo)
+    KnownCastToTy = CastCtx.CastInfo->getType();
+
   std::string CastToName =
-      CastInfo ? CastInfo->to()->getPointeeCXXRecordDecl()->getNameAsString()
-               : CastToTy->getPointeeCXXRecordDecl()->getNameAsString();
+      CastCtx.CastInfo && CastCtx.IsKnownCast
+          ? KnownCastToTy->getPointeeCXXRecordDecl()->getNameAsString()
+          : CastToTy->getPointeeCXXRecordDecl()->getNameAsString();
   Object = Object->IgnoreParenImpCasts();
 
   return C.getNoteTag(
@@ -114,24 +190,25 @@
         SmallString<128> Msg;
         llvm::raw_svector_ostream Out(Msg);
 
-        if (!IsKnownCast)
+        if (!CastCtx.IsKnownCast)
           Out << "Assuming ";
 
         if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {
           Out << '\'' << DRE->getDecl()->getNameAsString() << '\'';
         } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {
-          Out << (IsKnownCast ? "Field '" : "field '")
+          Out << (CastCtx.IsKnownCast ? "Field '" : "field '")
               << ME->getMemberDecl()->getNameAsString() << '\'';
         } else {
-          Out << (IsKnownCast ? "The object" : "the object");
+          Out << (CastCtx.IsKnownCast ? "The object" : "the object");
         }
 
-        Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName
-            << '\'';
+        Out << ' ' << (CastCtx.CastSucceeds ? "is " : "is not ")
+            << (Vowels.contains_lower(CastToName[0]) ? "an" : "a") << " '"
+            << CastToName << '\'';
 
         return Out.str();
       },
-      /*IsPrunable=*/true);
+      /*IsPrunable=*/!CastCtx.CastSucceeds);
 }
 
 //===----------------------------------------------------------------------===//
@@ -157,12 +234,14 @@
                               bool IsNonNullReturn,
                               bool IsCheckedCast = false) {
   ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam);
-  if (!State)
+  if (!State) {
+    C.generateSink(C.getState(), C.getPredecessor());
     return;
+  }
 
   const Expr *Object;
   QualType CastFromTy;
-  QualType CastToTy = Call.getResultType();
+  QualType CastToTy = Call.getResultType().getCanonicalType();
 
   if (Call.getNumArgs() > 0) {
     Object = Call.getArgExpr(0);
@@ -182,35 +261,24 @@
   }
 
   const MemRegion *MR = DV.getAsRegion();
-  const DynamicCastInfo *CastInfo =
-      getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
-
-  // We assume that every checked cast succeeds.
-  bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy;
-  if (!CastSucceeds) {
-    if (CastInfo)
-      CastSucceeds = IsNonNullReturn && CastInfo->succeeds();
-    else
-      CastSucceeds = IsNonNullReturn;
-  }
+  CastContext CastCtx(State, MR, CastFromTy, CastToTy,
+                      /*Assumption=*/IsNonNullReturn, IsCheckedCast);
 
-  // Check for infeasible casts.
-  if (isInfeasibleCast(CastInfo, CastSucceeds)) {
+  if (CastCtx.IsInfeasible) {
     C.generateSink(State, C.getPredecessor());
     return;
   }
 
-  // Store the type and the cast information.
-  bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy;
-  if (!IsKnownCast || IsCheckedCast)
-    State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
-                                      CastSucceeds);
+  State = setDynamicTypeAndCastInfo(State, MR, CastToTy, CastCtx.CastInfo,
+                                    CastCtx.CastSucceeds);
+
+  SVal V = CastCtx.CastSucceeds
+               ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)
+               : C.getSValBuilder().makeNull();
 
-  SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)
-                        : C.getSValBuilder().makeNull();
   C.addTransition(
       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false),
-      getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));
+      getNoteTag(C, Object, CastToTy, CastCtx));
 }
 
 static void addInstanceOfTransition(const CallEvent &Call,
@@ -220,6 +288,7 @@
   const FunctionDecl *FD = Call.getDecl()->getAsFunction();
   QualType CastFromTy = Call.parameters()[0]->getType();
   QualType CastToTy = FD->getTemplateSpecializationArgs()->get(0).getAsType();
+
   if (CastFromTy->isPointerType())
     CastToTy = C.getASTContext().getPointerType(CastToTy);
   else if (CastFromTy->isReferenceType())
@@ -228,31 +297,21 @@
     return;
 
   const MemRegion *MR = DV.getAsRegion();
-  const DynamicCastInfo *CastInfo =
-      getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
-
-  bool CastSucceeds;
-  if (CastInfo)
-    CastSucceeds = IsInstanceOf && CastInfo->succeeds();
-  else
-    CastSucceeds = IsInstanceOf || CastFromTy == CastToTy;
+  CastContext CastCtx(State, MR, CastFromTy, CastToTy,
+                      /*Assumption=*/IsInstanceOf, /*IsCheckedCast=*/false);
 
-  if (isInfeasibleCast(CastInfo, CastSucceeds)) {
+  if (CastCtx.IsInfeasible) {
     C.generateSink(State, C.getPredecessor());
     return;
   }
 
-  // Store the type and the cast information.
-  bool IsKnownCast = CastInfo || CastFromTy == CastToTy;
-  if (!IsKnownCast)
-    State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
-                                      IsInstanceOf);
+  State = setDynamicTypeAndCastInfo(State, MR, CastToTy, CastCtx.CastInfo,
+                                    CastCtx.CastSucceeds);
 
   C.addTransition(
       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
-                      C.getSValBuilder().makeTruthVal(CastSucceeds)),
-      getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), CastSucceeds,
-                 IsKnownCast));
+                      C.getSValBuilder().makeTruthVal(CastCtx.CastSucceeds)),
+      getNoteTag(C, Call.getArgExpr(0), CastToTy, CastCtx));
 }
 
 //===----------------------------------------------------------------------===//
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
@@ -18,32 +18,33 @@
 /// of a region in a given state along the analysis path.
 class DynamicTypeInfo {
 public:
-  DynamicTypeInfo() : DynTy(QualType()) {}
+  DynamicTypeInfo() : Ty(QualType()) {}
 
-  DynamicTypeInfo(QualType Ty, bool CanBeSub = true)
-      : DynTy(Ty), CanBeASubClass(CanBeSub) {}
+  DynamicTypeInfo(QualType ty, bool CanBeSub = true)
+      : Ty(ty), CanBeASubClass(CanBeSub) {}
 
   /// Returns false if the type information is precise (the type 'DynTy' is
   /// the only type in the lattice), true otherwise.
   bool canBeASubClass() const { return CanBeASubClass; }
 
   /// Returns true if the dynamic type info is available.
-  bool isValid() const { return !DynTy.isNull(); }
+  bool isValid() const { return !Ty.isNull(); }
 
   /// Returns the currently inferred upper bound on the runtime type.
-  QualType getType() const { return DynTy; }
+  QualType getType() const { return Ty; }
 
   bool operator==(const DynamicTypeInfo &RHS) const {
-    return DynTy == RHS.DynTy && CanBeASubClass == RHS.CanBeASubClass;
+    return Ty == RHS.Ty && CanBeASubClass == RHS.CanBeASubClass;
   }
+  bool operator<(const DynamicTypeInfo &RHS) const { return Ty < RHS.Ty; }
 
   void Profile(llvm::FoldingSetNodeID &ID) const {
-    ID.Add(DynTy);
+    ID.Add(Ty);
     ID.AddBoolean(CanBeASubClass);
   }
 
 private:
-  QualType DynTy;
+  QualType Ty;
   bool CanBeASubClass;
 };
 
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
@@ -16,7 +16,6 @@
 #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPE_H
 
 #include "clang/AST/Type.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
@@ -37,10 +36,9 @@
                                              const MemRegion *MR);
 
 /// Get dynamic cast information from \p CastFromTy to \p CastToTy of \p MR.
-const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
-                                          const MemRegion *MR,
-                                          QualType CastFromTy,
-                                          QualType CastToTy);
+const DynamicTypeInfo *getFailedCastInfo(ProgramStateRef State,
+                                         const MemRegion *MR,
+                                         QualType CastToTy);
 
 /// Set dynamic type information of the region; return the new state.
 ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
@@ -53,9 +51,9 @@
 /// Set dynamic type and cast information of the region; return the new state.
 ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State,
                                           const MemRegion *MR,
-                                          QualType CastFromTy,
                                           QualType CastToTy,
-                                          bool IsCastSucceeds);
+                                          const DynamicTypeInfo *CastInfo,
+                                          bool CastSucceeds);
 
 /// Removes the dead type informations from \p State.
 ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR);
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//===- DynamicCastInfo.h - Runtime cast information -------------*- 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H
-#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H
-
-#include "clang/AST/Type.h"
-
-namespace clang {
-namespace ento {
-
-class DynamicCastInfo {
-public:
-  enum CastResult { Success, Failure };
-
-  DynamicCastInfo(QualType from, QualType to, CastResult resultKind)
-      : From(from), To(to), ResultKind(resultKind) {}
-
-  QualType from() const { return From; }
-  QualType to() const { return To; }
-
-  bool equals(QualType from, QualType to) const {
-    return From == from && To == to;
-  }
-
-  bool succeeds() const { return ResultKind == CastResult::Success; }
-  bool fails() const { return ResultKind == CastResult::Failure; }
-
-  bool operator==(const DynamicCastInfo &RHS) const {
-    return From == RHS.From && To == RHS.To;
-  }
-  bool operator<(const DynamicCastInfo &RHS) const {
-    return From < RHS.From && To < RHS.To;
-  }
-
-  void Profile(llvm::FoldingSetNodeID &ID) const {
-    ID.Add(From);
-    ID.Add(To);
-    ID.AddInteger(ResultKind);
-  }
-
-private:
-  QualType From, To;
-  CastResult ResultKind;
-};
-
-} // namespace ento
-} // namespace clang
-
-#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to