Charusso updated this revision to Diff 213952.
Charusso marked 5 inline comments as done.
Charusso added a comment.

- Remove symbol-conjuring.
- Add a forgotten test case.


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

https://reviews.llvm.org/D65889

Files:
  clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
  clang/lib/StaticAnalyzer/Core/CallEvent.cpp
  clang/test/Analysis/cast-value.cpp

Index: clang/test/Analysis/cast-value.cpp
===================================================================
--- clang/test/Analysis/cast-value.cpp
+++ clang/test/Analysis/cast-value.cpp
@@ -23,11 +23,20 @@
 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,7 +100,43 @@
   if (!S)
     clang_analyzer_eval(!C); // logic-warning {{TRUE}}
 }
+} // namespace test_dyn_cast_or_null
+
+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 evalNonNullParamNonNullReturn(const Shape *S) {
   const auto *C = dyn_cast_or_null<Circle>(S);
   // expected-note@-1 {{Assuming dynamic cast from 'Shape' to 'Circle' succeeds}}
@@ -134,4 +179,28 @@
   // expected-warning@-2 {{Division by zero}}
   // logic-warning@-3 {{Division by zero}}
 }
-} // namespace test_dyn_cast_or_null
+
+void evalZeroParamNonNullReturn(const Shape *S) {
+  const auto *C = S->castAs<Circle>();
+  // expected-note@-1 {{Assuming pointer value is null}}
+  // expected-note@-2 {{Assuming dynamic 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 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
Index: clang/lib/StaticAnalyzer/Core/CallEvent.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -714,7 +714,8 @@
     return UnknownVal();
 
   SVal ThisVal = getSVal(Base);
-  assert(ThisVal.isUnknownOrUndef() || ThisVal.getAs<Loc>());
+  assert(ThisVal.isUnknownOrUndef() || ThisVal.getAs<Loc>() ||
+         ThisVal.getAs<nonloc::ConcreteInt>()->getValue().getExtValue() == 1);
   return ThisVal;
 }
 
Index: clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -22,27 +22,35 @@
 
 namespace {
 class CastValueChecker : public Checker<eval::Call> {
+  enum class CastKind { Checking, Sugar };
+
   using CastCheck =
       std::function<void(const CastValueChecker *, const CallExpr *,
                          DefinedOrUnknownSVal, CheckerContext &)>;
 
 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},
+  // These are known in the LLVM project. The pairs are in the following form:
+  // {{{namespace, call}, argument-count}, callback}
+  const CallDescriptionMap<CastCheck> CheckingCastCDM = {
+      {{{"llvm", "dyn_cast_or_null"}, 1}, &CastValueChecker::evalDynCastOrNull},
       {{{"llvm", "cast_or_null"}, 1}, &CastValueChecker::evalCastOrNull},
-      {{{"llvm", "dyn_cast_or_null"}, 1},
-       &CastValueChecker::evalDynCastOrNull}};
+      {{{"llvm", "dyn_cast"}, 1}, &CastValueChecker::evalDynCast},
+      {{{"llvm", "cast"}, 1}, &CastValueChecker::evalCast}};
+
+  const CallDescriptionMap<CastCheck> SugarCastCDM = {
+      {{{"clang", "castAs"}, 0}, &CastValueChecker::evalCastAs},
+      {{{"clang", "getAs"}, 0}, &CastValueChecker::evalGetAs}};
 
   void evalCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
                 CheckerContext &C) const;
@@ -52,6 +60,11 @@
                       CheckerContext &C) const;
   void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
                          CheckerContext &C) const;
+
+  void evalCastAs(const CallExpr *CE, DefinedOrUnknownSVal ArgumentDV,
+                  CheckerContext &C) const;
+  void evalGetAs(const CallExpr *CE, DefinedOrUnknownSVal ArgumentDV,
+                 CheckerContext &C) const;
 };
 } // namespace
 
@@ -59,74 +72,68 @@
   return Cast->getType()->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) {
+  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](BugReport &) -> std::string {
         SmallString<128> Msg;
         llvm::raw_svector_ostream Out(Msg);
 
-        Out << "Assuming dynamic cast from '" << CastFromName << "' to '"
-            << CastToName << "' succeeds";
+        Out << "Assuming dynamic cast ";
+        if (CastFromName)
+          Out << "from '" << *CastFromName << "' ";
+
+        Out << "to '" << CastToName << "' "
+            << (!IsNullReturn ? "succeeds" : "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);
+}
+
+//===----------------------------------------------------------------------===//
+// Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
+//===----------------------------------------------------------------------===//
+
+static void evalNonNullParamNonNullReturn(const CallExpr *CE,
+                                          DefinedOrUnknownSVal ParamDV,
+                                          CheckerContext &C) {
+  bool IsNullReturn = false;
+  if (ProgramStateRef State = C.getState()->assume(ParamDV, true))
+    C.addTransition(getState(IsNullReturn, ParamDV, CE, State, C),
+                    getCastTag(IsNullReturn, CE, C));
 }
 
 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);
-
-  const NoteTag *CastTag = C.getNoteTag(
-      [CastFromName, CastToName](BugReport &) -> std::string {
-        SmallString<128> Msg;
-        llvm::raw_svector_ostream Out(Msg);
-
-        Out << "Assuming dynamic cast from '" << CastFromName << "' to '"
-            << CastToName << "' fails";
-        return Out.str();
-      },
-      /*IsPrunable=*/true);
-
-  C.addTransition(State, CastTag);
+  bool IsNullReturn = true;
+  if (ProgramStateRef State = C.getState()->assume(ParamDV, true))
+    C.addTransition(getState(IsNullReturn, ParamDV, CE, State, C),
+                    getCastTag(IsNullReturn, CE, C));
 }
 
 static void evalNullParamNullReturn(const CallExpr *CE,
                                     DefinedOrUnknownSVal ParamDV,
                                     CheckerContext &C) {
-  ProgramStateRef State = C.getState()->assume(ParamDV, false);
-  if (!State)
-    return;
-
-  State = State->BindExpr(CE, C.getLocationContext(),
-                          C.getSValBuilder().makeNull(), false);
-
-  const NoteTag *CastTag =
-      C.getNoteTag("Assuming null pointer is passed into cast",
-                   /*IsPrunable=*/true);
-
-  C.addTransition(State, CastTag);
+  if (ProgramStateRef State = C.getState()->assume(ParamDV, false))
+    C.addTransition(getState(/*IsNullReturn=*/true, ParamDV, CE, State, C),
+                    C.getNoteTag("Assuming null pointer is passed into cast",
+                                 /*IsPrunable=*/true));
 }
 
 void CastValueChecker::evalCast(const CallExpr *CE,
@@ -157,27 +164,83 @@
   evalNullParamNullReturn(CE, ParamDV, C);
 }
 
+//===----------------------------------------------------------------------===//
+// Evaluating castAs, getAs.
+//===----------------------------------------------------------------------===//
+
+static void evalZeroParamNonNullReturn(const CallExpr *CE,
+                                       DefinedOrUnknownSVal ArgumentDV,
+                                       CheckerContext &C) {
+  bool IsNullReturn = false;
+  if (ProgramStateRef State = C.getState()->assume(ArgumentDV, true))
+    C.addTransition(getState(IsNullReturn, ArgumentDV, CE, C.getState(), C),
+                    getCastTag(IsNullReturn, CE, C));
+}
+
+static void evalZeroParamNullReturn(const CallExpr *CE,
+                                    DefinedOrUnknownSVal ArgumentDV,
+                                    CheckerContext &C) {
+  bool IsNullReturn = true;
+  if (ProgramStateRef State = C.getState()->assume(ArgumentDV, true))
+    C.addTransition(getState(IsNullReturn, ArgumentDV, CE, C.getState(), C),
+                    getCastTag(IsNullReturn, CE, C));
+}
+
+void CastValueChecker::evalCastAs(const CallExpr *CE, DefinedOrUnknownSVal DV,
+                                  CheckerContext &C) const {
+  evalZeroParamNonNullReturn(CE, DV, C);
+}
+
+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);
+  const CastCheck *Check = CheckingCastCDM.lookup(Call);
+  CastKind Kind = CastKind::Checking;
+
+  if (!Check) {
+    Check = SugarCastCDM.lookup(Call);
+    Kind = CastKind::Sugar;
+  }
+
   if (!Check)
     return false;
 
+  // If we cannot obtain the call's class we cannot be sure how to model it.
   const auto *CE = cast<CallExpr>(Call.getOriginExpr());
-  if (!CE)
+  if (!CE->getType()->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;
+  Optional<DefinedOrUnknownSVal> DV;
+
+  switch (Kind) {
+  case CastKind::Checking:
+    // If we cannot obtain the arg's class we cannot be sure how to model it.
+    if (!CE->getArg(0)->getType()->getPointeeCXXRecordDecl())
+      return false;
+
+    DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
+    break;
+
+  case CastKind::Sugar:
+    // If we cannot obtain 'MCE' we cannot be sure how to model it.
+    const auto *MCE = dyn_cast<CXXMemberCallExpr>(CE->IgnoreParenImpCasts());
+    if (!MCE)
+      return false;
+
+    DV = C.getSVal(MCE->getImplicitObjectArgument())
+             .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;
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to