Author: charusso
Date: Thu Aug  8 19:24:42 2019
New Revision: 368383

URL: http://llvm.org/viewvc/llvm-project?rev=368383&view=rev
Log:
[analyzer] CastValueChecker: Model castAs(), getAs()

Summary: Thanks to Kristóf Umann for the great idea!

Reviewed By: NoQ

Differential Revision: https://reviews.llvm.org/D65889

Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
    cfe/trunk/test/Analysis/cast-value.cpp

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp?rev=368383&r1=368382&r2=368383&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp Thu Aug  8 
19:24:42 2019
@@ -16,168 +16,238 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
 #include "llvm/ADT/Optional.h"
+#include <utility>
 
 using namespace clang;
 using namespace ento;
 
 namespace {
 class CastValueChecker : public Checker<eval::Call> {
+  enum class CastKind { Function, Method };
+
   using CastCheck =
       std::function<void(const CastValueChecker *, const CallExpr *,
                          DefinedOrUnknownSVal, CheckerContext &)>;
 
+  using CheckKindPair = std::pair<CastCheck, CastKind>;
+
 public:
-  // We have three cases to evaluate a cast:
+  // We have five cases to evaluate a cast:
   // 1) The parameter is non-null, the return value is non-null
   // 2) The parameter is non-null, the return value is null
   // 3) The parameter is null, the return value is null
-  //
   // cast: 1;  dyn_cast: 1, 2;  cast_or_null: 1, 3;  dyn_cast_or_null: 1, 2, 3.
+  //
+  // 4) castAs: has no parameter, the return value is non-null.
+  // 5) getAs:  has no parameter, the return value is null or non-null.
   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
 
 private:
-  // These are known in the LLVM project.
-  const CallDescriptionMap<CastCheck> CDM = {
-      {{{"llvm", "cast"}, 1}, &CastValueChecker::evalCast},
-      {{{"llvm", "dyn_cast"}, 1}, &CastValueChecker::evalDynCast},
-      {{{"llvm", "cast_or_null"}, 1}, &CastValueChecker::evalCastOrNull},
+  // These are known in the LLVM project. The pairs are in the following form:
+  // {{{namespace, call}, argument-count}, {callback, kind}}
+  const CallDescriptionMap<CheckKindPair> CDM = {
+      {{{"llvm", "cast"}, 1},
+       {&CastValueChecker::evalCast, CastKind::Function}},
+      {{{"llvm", "dyn_cast"}, 1},
+       {&CastValueChecker::evalDynCast, CastKind::Function}},
+      {{{"llvm", "cast_or_null"}, 1},
+       {&CastValueChecker::evalCastOrNull, CastKind::Function}},
       {{{"llvm", "dyn_cast_or_null"}, 1},
-       &CastValueChecker::evalDynCastOrNull}};
+       {&CastValueChecker::evalDynCastOrNull, CastKind::Function}},
+      {{{"clang", "castAs"}, 0},
+       {&CastValueChecker::evalCastAs, CastKind::Method}},
+      {{{"clang", "getAs"}, 0},
+       {&CastValueChecker::evalGetAs, CastKind::Method}}};
 
-  void evalCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
+  void evalCast(const CallExpr *CE, DefinedOrUnknownSVal DV,
                 CheckerContext &C) const;
-  void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
+  void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal DV,
                    CheckerContext &C) const;
-  void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
+  void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal DV,
                       CheckerContext &C) const;
-  void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
+  void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal DV,
                          CheckerContext &C) const;
+  void evalCastAs(const CallExpr *CE, DefinedOrUnknownSVal DV,
+                  CheckerContext &C) const;
+  void evalGetAs(const CallExpr *CE, DefinedOrUnknownSVal DV,
+                 CheckerContext &C) const;
 };
 } // namespace
 
 static std::string getCastName(const Expr *Cast) {
-  return Cast->getType()->getPointeeCXXRecordDecl()->getNameAsString();
+  QualType Ty = Cast->getType();
+  if (const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl())
+    return RD->getNameAsString();
+
+  return Ty->getPointeeCXXRecordDecl()->getNameAsString();
 }
 
-static void evalNonNullParamNonNullReturn(const CallExpr *CE,
-                                          DefinedOrUnknownSVal ParamDV,
-                                          CheckerContext &C) {
-  ProgramStateRef State = C.getState()->assume(ParamDV, true);
-  if (!State)
-    return;
-
-  State = State->BindExpr(CE, C.getLocationContext(), ParamDV, false);
-
-  std::string CastFromName = getCastName(CE->getArg(0));
+static const NoteTag *getCastTag(bool IsNullReturn, const CallExpr *CE,
+                                 CheckerContext &C,
+                                 bool IsCheckedCast = false) {
+  Optional<std::string> CastFromName = (CE->getNumArgs() > 0)
+                                           ? getCastName(CE->getArg(0))
+                                           : Optional<std::string>();
   std::string CastToName = getCastName(CE);
 
-  const NoteTag *CastTag = C.getNoteTag(
-      [CastFromName, CastToName](BugReport &) -> std::string {
+  return C.getNoteTag(
+      [CastFromName, CastToName, IsNullReturn,
+       IsCheckedCast](BugReport &) -> std::string {
         SmallString<128> Msg;
         llvm::raw_svector_ostream Out(Msg);
 
-        Out << "Assuming dynamic cast from '" << CastFromName << "' to '"
-            << CastToName << "' succeeds";
-        return Out.str();
-      },
-      /*IsPrunable=*/true);
-
-  C.addTransition(State, CastTag);
-}
-
-static void evalNonNullParamNullReturn(const CallExpr *CE,
-                                       DefinedOrUnknownSVal ParamDV,
-                                       CheckerContext &C) {
-  ProgramStateRef State = C.getState()->assume(ParamDV, true);
-  if (!State)
-    return;
-
-  State = State->BindExpr(CE, C.getLocationContext(),
-                          C.getSValBuilder().makeNull(), false);
-
-  std::string CastFromName = getCastName(CE->getArg(0));
-  std::string CastToName = getCastName(CE);
+        Out << (!IsCheckedCast ? "Assuming dynamic cast " : "Checked cast ");
+        if (CastFromName)
+          Out << "from '" << *CastFromName << "' ";
 
-  const NoteTag *CastTag = C.getNoteTag(
-      [CastFromName, CastToName](BugReport &) -> std::string {
-        SmallString<128> Msg;
-        llvm::raw_svector_ostream Out(Msg);
+        Out << "to '" << CastToName << "' "
+            << (!IsNullReturn ? "succeeds" : "fails");
 
-        Out << "Assuming dynamic cast from '" << CastFromName << "' to '"
-            << CastToName << "' fails";
         return Out.str();
       },
       /*IsPrunable=*/true);
+}
 
-  C.addTransition(State, CastTag);
+static ProgramStateRef getState(bool IsNullReturn,
+                                DefinedOrUnknownSVal ReturnDV,
+                                const CallExpr *CE, ProgramStateRef State,
+                                CheckerContext &C) {
+  return State->BindExpr(
+      CE, C.getLocationContext(),
+      IsNullReturn ? C.getSValBuilder().makeNull() : ReturnDV, false);
 }
 
-static void evalNullParamNullReturn(const CallExpr *CE,
-                                    DefinedOrUnknownSVal ParamDV,
-                                    CheckerContext &C) {
-  ProgramStateRef State = C.getState()->assume(ParamDV, false);
-  if (!State)
-    return;
+//===----------------------------------------------------------------------===//
+// Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
+//===----------------------------------------------------------------------===//
 
-  State = State->BindExpr(CE, C.getLocationContext(),
-                          C.getSValBuilder().makeNull(), false);
+static void evalNonNullParamNonNullReturn(const CallExpr *CE,
+                                          DefinedOrUnknownSVal DV,
+                                          CheckerContext &C,
+                                          bool IsCheckedCast = false) {
+  bool IsNullReturn = false;
+  if (ProgramStateRef State = C.getState()->assume(DV, true))
+    C.addTransition(getState(IsNullReturn, DV, CE, State, C),
+                    getCastTag(IsNullReturn, CE, C, IsCheckedCast));
+}
 
-  const NoteTag *CastTag =
-      C.getNoteTag("Assuming null pointer is passed into cast",
-                   /*IsPrunable=*/true);
+static void evalNonNullParamNullReturn(const CallExpr *CE,
+                                       DefinedOrUnknownSVal DV,
+                                       CheckerContext &C) {
+  bool IsNullReturn = true;
+  if (ProgramStateRef State = C.getState()->assume(DV, true))
+    C.addTransition(getState(IsNullReturn, DV, CE, State, C),
+                    getCastTag(IsNullReturn, CE, C));
+}
 
-  C.addTransition(State, CastTag);
+static void evalNullParamNullReturn(const CallExpr *CE, DefinedOrUnknownSVal 
DV,
+                                    CheckerContext &C) {
+  if (ProgramStateRef State = C.getState()->assume(DV, false))
+    C.addTransition(getState(/*IsNullReturn=*/true, DV, CE, State, C),
+                    C.getNoteTag("Assuming null pointer is passed into cast",
+                                 /*IsPrunable=*/true));
 }
 
-void CastValueChecker::evalCast(const CallExpr *CE,
-                                DefinedOrUnknownSVal ParamDV,
+void CastValueChecker::evalCast(const CallExpr *CE, DefinedOrUnknownSVal DV,
                                 CheckerContext &C) const {
-  evalNonNullParamNonNullReturn(CE, ParamDV, C);
+  evalNonNullParamNonNullReturn(CE, DV, C, /*IsCheckedCast=*/true);
 }
 
-void CastValueChecker::evalDynCast(const CallExpr *CE,
-                                   DefinedOrUnknownSVal ParamDV,
+void CastValueChecker::evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal DV,
                                    CheckerContext &C) const {
-  evalNonNullParamNonNullReturn(CE, ParamDV, C);
-  evalNonNullParamNullReturn(CE, ParamDV, C);
+  evalNonNullParamNonNullReturn(CE, DV, C);
+  evalNonNullParamNullReturn(CE, DV, C);
 }
 
 void CastValueChecker::evalCastOrNull(const CallExpr *CE,
-                                      DefinedOrUnknownSVal ParamDV,
+                                      DefinedOrUnknownSVal DV,
                                       CheckerContext &C) const {
-  evalNonNullParamNonNullReturn(CE, ParamDV, C);
-  evalNullParamNullReturn(CE, ParamDV, C);
+  evalNonNullParamNonNullReturn(CE, DV, C);
+  evalNullParamNullReturn(CE, DV, C);
 }
 
 void CastValueChecker::evalDynCastOrNull(const CallExpr *CE,
-                                         DefinedOrUnknownSVal ParamDV,
+                                         DefinedOrUnknownSVal DV,
                                          CheckerContext &C) const {
-  evalNonNullParamNonNullReturn(CE, ParamDV, C);
-  evalNonNullParamNullReturn(CE, ParamDV, C);
-  evalNullParamNullReturn(CE, ParamDV, C);
+  evalNonNullParamNonNullReturn(CE, DV, C);
+  evalNonNullParamNullReturn(CE, DV, C);
+  evalNullParamNullReturn(CE, DV, C);
+}
+
+//===----------------------------------------------------------------------===//
+// Evaluating castAs, getAs.
+//===----------------------------------------------------------------------===//
+
+static void evalZeroParamNonNullReturn(const CallExpr *CE,
+                                       DefinedOrUnknownSVal DV,
+                                       CheckerContext &C,
+                                       bool IsCheckedCast = false) {
+  bool IsNullReturn = false;
+  if (ProgramStateRef State = C.getState()->assume(DV, true))
+    C.addTransition(getState(IsNullReturn, DV, CE, C.getState(), C),
+                    getCastTag(IsNullReturn, CE, C, IsCheckedCast));
+}
+
+static void evalZeroParamNullReturn(const CallExpr *CE, DefinedOrUnknownSVal 
DV,
+                                    CheckerContext &C) {
+  bool IsNullReturn = true;
+  if (ProgramStateRef State = C.getState()->assume(DV, true))
+    C.addTransition(getState(IsNullReturn, DV, CE, C.getState(), C),
+                    getCastTag(IsNullReturn, CE, C));
+}
+
+void CastValueChecker::evalCastAs(const CallExpr *CE, DefinedOrUnknownSVal DV,
+                                  CheckerContext &C) const {
+  evalZeroParamNonNullReturn(CE, DV, C, /*IsCheckedCast=*/true);
+}
+
+void CastValueChecker::evalGetAs(const CallExpr *CE, DefinedOrUnknownSVal DV,
+                                 CheckerContext &C) const {
+  evalZeroParamNonNullReturn(CE, DV, C);
+  evalZeroParamNullReturn(CE, DV, C);
 }
 
 bool CastValueChecker::evalCall(const CallEvent &Call,
                                 CheckerContext &C) const {
-  const CastCheck *Check = CDM.lookup(Call);
-  if (!Check)
+  const auto *Lookup = CDM.lookup(Call);
+  if (!Lookup)
     return false;
 
-  const auto *CE = cast<CallExpr>(Call.getOriginExpr());
-  if (!CE)
+  // If we cannot obtain the call's class we cannot be sure how to model it.
+  QualType ResultTy = Call.getResultType();
+  if (!ResultTy->getPointeeCXXRecordDecl())
     return false;
 
-  // If we cannot obtain both of the classes we cannot be sure how to model it.
-  if (!CE->getType()->getPointeeCXXRecordDecl() ||
-      !CE->getArg(0)->getType()->getPointeeCXXRecordDecl())
-    return false;
+  const CastCheck &Check = Lookup->first;
+  CastKind Kind = Lookup->second;
+
+  const auto *CE = cast<CallExpr>(Call.getOriginExpr());
+  Optional<DefinedOrUnknownSVal> DV;
+
+  switch (Kind) {
+  case CastKind::Function: {
+    // If we cannot obtain the arg's class we cannot be sure how to model it.
+    QualType ArgTy = Call.parameters()[0]->getType();
+    if (!ArgTy->getAsCXXRecordDecl() && !ArgTy->getPointeeCXXRecordDecl())
+      return false;
+
+    DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
+    break;
+  }
+  case CastKind::Method:
+    // If we cannot obtain the 'InstanceCall' we cannot be sure how to model 
it.
+    const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call);
+    if (!InstanceCall)
+      return false;
+
+    DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>();
+    break;
+  }
 
-  SVal ParamV = Call.getArgSVal(0);
-  auto ParamDV = ParamV.getAs<DefinedOrUnknownSVal>();
-  if (!ParamDV)
+  if (!DV)
     return false;
 
-  (*Check)(this, CE, *ParamDV, C);
+  Check(this, CE, *DV, C);
   return true;
 }
 

Modified: cfe/trunk/test/Analysis/cast-value.cpp
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/cast-value.cpp?rev=368383&r1=368382&r2=368383&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/cast-value.cpp (original)
+++ cfe/trunk/test/Analysis/cast-value.cpp Thu Aug  8 19:24:42 2019
@@ -14,20 +14,33 @@ template <class X, class Y>
 const X *cast(Y Value);
 
 template <class X, class Y>
-const X *dyn_cast(Y Value);
+const X *dyn_cast(Y *Value);
+template <class X, class Y>
+const X &dyn_cast(Y &Value);
 
 template <class X, class Y>
 const X *cast_or_null(Y Value);
 
 template <class X, class Y>
-const X *dyn_cast_or_null(Y Value);
+const X *dyn_cast_or_null(Y *Value);
+template <class X, class Y>
+const X *dyn_cast_or_null(Y &Value);
 } // namespace llvm
 
-using namespace llvm;
-
-class Shape {};
+namespace clang {
+struct Shape {
+  template <typename T>
+  const T *castAs() const;
+
+  template <typename T>
+  const T *getAs() const;
+};
 class Triangle : public Shape {};
 class Circle : public Shape {};
+} // namespace clang
+
+using namespace llvm;
+using namespace clang;
 
 namespace test_cast {
 void evalLogic(const Shape *S) {
@@ -91,8 +104,52 @@ void evalLogic(const Shape *S) {
   if (!S)
     clang_analyzer_eval(!C); // logic-warning {{TRUE}}
 }
+} // namespace test_dyn_cast_or_null
 
-void evalNonNullParamNonNullReturn(const Shape *S) {
+namespace test_cast_as {
+void evalLogic(const Shape *S) {
+  const Circle *C = S->castAs<Circle>();
+  clang_analyzer_numTimesReached(); // logic-warning {{1}}
+
+  if (S && C)
+    clang_analyzer_eval(C == S);
+  // logic-warning@-1 {{TRUE}}
+
+  if (S && !C)
+    clang_analyzer_warnIfReached(); // no-warning
+
+  if (!S)
+    clang_analyzer_warnIfReached(); // no-warning
+}
+} // namespace test_cast_as
+
+namespace test_get_as {
+void evalLogic(const Shape *S) {
+  const Circle *C = S->getAs<Circle>();
+  clang_analyzer_numTimesReached(); // logic-warning {{2}}
+
+  if (S && C)
+    clang_analyzer_eval(C == S);
+  // logic-warning@-1 {{TRUE}}
+
+  if (S && !C)
+    clang_analyzer_warnIfReached(); // logic-warning {{REACHABLE}}
+
+  if (!S)
+    clang_analyzer_warnIfReached(); // no-warning
+}
+} // namespace test_get_as
+
+namespace test_notes {
+void evalReferences(const Shape &S) {
+  const auto &C = dyn_cast<Circle>(S);
+  // expected-note@-1 {{Assuming dynamic cast from 'Shape' to 'Circle' fails}}
+  // expected-note@-2 {{Dereference of null pointer}}
+  // expected-warning@-3 {{Dereference of null pointer}}
+  // logic-warning@-4 {{Dereference of null pointer}}
+}
+
+void evalNonNullParamNonNullReturnReference(const Shape &S) {
   const auto *C = dyn_cast_or_null<Circle>(S);
   // expected-note@-1 {{Assuming dynamic cast from 'Shape' to 'Circle' 
succeeds}}
   // expected-note@-2 {{Assuming pointer value is null}}
@@ -105,6 +162,19 @@ void evalNonNullParamNonNullReturn(const
   // logic-warning@-4 {{Division by zero}}
 }
 
+void evalNonNullParamNonNullReturn(const Shape *S) {
+  const auto *C = cast<Circle>(S);
+  // expected-note@-1 {{Checked cast from 'Shape' to 'Circle' succeeds}}
+  // expected-note@-2 {{Assuming pointer value is null}}
+  // expected-note@-3 {{'C' initialized here}}
+
+  (void)(1 / !(bool)C);
+  // expected-note@-1 {{'C' is non-null}}
+  // expected-note@-2 {{Division by zero}}
+  // expected-warning@-3 {{Division by zero}}
+  // logic-warning@-4 {{Division by zero}}
+}
+
 void evalNonNullParamNullReturn(const Shape *S) {
   const auto *C = dyn_cast_or_null<Circle>(S);
   // expected-note@-1 {{Assuming dynamic cast from 'Shape' to 'Circle' fails}}
@@ -134,4 +204,40 @@ void evalNullParamNullReturn(const Shape
   // expected-warning@-2 {{Division by zero}}
   // logic-warning@-3 {{Division by zero}}
 }
-} // namespace test_dyn_cast_or_null
+
+void evalZeroParamNonNullReturnPointer(const Shape *S) {
+  const auto *C = S->castAs<Circle>();
+  // expected-note@-1 {{Assuming pointer value is null}}
+  // expected-note@-2 {{Checked cast to 'Circle' succeeds}}
+  // expected-note@-3 {{'C' initialized here}}
+
+  (void)(1 / !(bool)C);
+  // expected-note@-1 {{'C' is non-null}}
+  // expected-note@-2 {{Division by zero}}
+  // expected-warning@-3 {{Division by zero}}
+  // logic-warning@-4 {{Division by zero}}
+}
+
+void evalZeroParamNonNullReturn(const Shape &S) {
+  const auto *C = S.castAs<Circle>();
+  // expected-note@-1 {{Checked cast to 'Circle' succeeds}}
+  // expected-note@-2 {{'C' initialized here}}
+
+  (void)(1 / !(bool)C);
+  // expected-note@-1 {{'C' is non-null}}
+  // expected-note@-2 {{Division by zero}}
+  // expected-warning@-3 {{Division by zero}}
+  // logic-warning@-4 {{Division by zero}}
+}
+
+void evalZeroParamNullReturn(const Shape &S) {
+  const auto *C = S.getAs<Circle>();
+  // expected-note@-1 {{Assuming dynamic cast to 'Circle' fails}}
+  // expected-note@-2 {{'C' initialized to a null pointer value}}
+
+  (void)(1 / (bool)C);
+  // expected-note@-1 {{Division by zero}}
+  // expected-warning@-2 {{Division by zero}}
+  // logic-warning@-3 {{Division by zero}}
+}
+} // namespace test_notes


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

Reply via email to