Author: charusso
Date: Wed Aug 21 19:57:59 2019
New Revision: 369615

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

Summary: -

Reviewed By: NoQ

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

Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
    cfe/trunk/test/Analysis/Inputs/llvm.h
    cfe/trunk/test/Analysis/cast-value-logic.cpp
    cfe/trunk/test/Analysis/cast-value-notes.cpp

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp?rev=369615&r1=369614&r2=369615&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp Wed Aug 21 
19:57:59 2019
@@ -16,6 +16,7 @@
 //
 
//===----------------------------------------------------------------------===//
 
+#include "clang/AST/DeclTemplate.h"
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -30,7 +31,7 @@ using namespace ento;
 
 namespace {
 class CastValueChecker : public Checker<eval::Call> {
-  enum class CallKind { Function, Method };
+  enum class CallKind { Function, Method, InstanceOf };
 
   using CastCheck =
       std::function<void(const CastValueChecker *, const CallEvent &Call,
@@ -45,6 +46,10 @@ public:
   //
   // 4) castAs: Has no parameter, the return value is non-null.
   // 5) getAs:  Has no parameter, the return value is null or non-null.
+  //
+  // We have two cases to check the parameter is an instance of the given type.
+  // 1) isa:             The parameter is non-null, returns boolean.
+  // 2) isa_and_nonnull: The parameter is null or non-null, returns boolean.
   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
 
@@ -63,7 +68,11 @@ private:
       {{{"clang", "castAs"}, 0},
        {&CastValueChecker::evalCastAs, CallKind::Method}},
       {{{"clang", "getAs"}, 0},
-       {&CastValueChecker::evalGetAs, CallKind::Method}}};
+       {&CastValueChecker::evalGetAs, CallKind::Method}},
+      {{{"llvm", "isa"}, 1},
+       {&CastValueChecker::evalIsa, CallKind::InstanceOf}},
+      {{{"llvm", "isa_and_nonnull"}, 1},
+       {&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}};
 
   void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
                 CheckerContext &C) const;
@@ -77,6 +86,10 @@ private:
                   CheckerContext &C) const;
   void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
                  CheckerContext &C) const;
+  void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
+               CheckerContext &C) const;
+  void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
+                         CheckerContext &C) const;
 };
 } // namespace
 
@@ -189,6 +202,42 @@ static void addCastTransition(const Call
       getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));
 }
 
+static void addInstanceOfTransition(const CallEvent &Call,
+                                    DefinedOrUnknownSVal DV,
+                                    ProgramStateRef State, CheckerContext &C,
+                                    bool IsInstanceOf) {
+  const FunctionDecl *FD = Call.getDecl()->getAsFunction();
+  QualType CastToTy = FD->getTemplateSpecializationArgs()->get(0).getAsType();
+  QualType CastFromTy = getRecordType(Call.parameters()[0]->getType());
+
+  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;
+
+  if (isInfeasibleCast(CastInfo, CastSucceeds)) {
+    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,
+                                      Call.getResultType(), IsInstanceOf);
+
+  C.addTransition(
+      State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
+                      C.getSValBuilder().makeTruthVal(CastSucceeds)),
+      getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), CastSucceeds,
+                 IsKnownCast));
+}
+
 
//===----------------------------------------------------------------------===//
 // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
 
//===----------------------------------------------------------------------===//
@@ -278,6 +327,41 @@ void CastValueChecker::evalGetAs(const C
 }
 
 
//===----------------------------------------------------------------------===//
+// Evaluating isa, isa_and_nonnull.
+//===----------------------------------------------------------------------===//
+
+void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
+                               CheckerContext &C) const {
+  ProgramStateRef NonNullState, NullState;
+  std::tie(NonNullState, NullState) = C.getState()->assume(DV);
+
+  if (NonNullState) {
+    addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);
+    addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);
+  }
+
+  if (NullState) {
+    C.generateSink(NullState, C.getPredecessor());
+  }
+}
+
+void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call,
+                                         DefinedOrUnknownSVal DV,
+                                         CheckerContext &C) const {
+  ProgramStateRef NonNullState, NullState;
+  std::tie(NonNullState, NullState) = C.getState()->assume(DV);
+
+  if (NonNullState) {
+    addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);
+    addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);
+  }
+
+  if (NullState) {
+    addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false);
+  }
+}
+
+//===----------------------------------------------------------------------===//
 // Main logic to evaluate a call.
 
//===----------------------------------------------------------------------===//
 
@@ -287,12 +371,14 @@ bool CastValueChecker::evalCall(const Ca
   if (!Lookup)
     return false;
 
+  const CastCheck &Check = Lookup->first;
+  CallKind Kind = Lookup->second;
+
   // We need to obtain the record type of the call's result to model it.
-  if (!getRecordType(Call.getResultType())->isRecordType())
+  if (Kind != CallKind::InstanceOf &&
+      !getRecordType(Call.getResultType())->isRecordType())
     return false;
 
-  const CastCheck &Check = Lookup->first;
-  CallKind Kind = Lookup->second;
   Optional<DefinedOrUnknownSVal> DV;
 
   switch (Kind) {
@@ -302,6 +388,15 @@ bool CastValueChecker::evalCall(const Ca
       return false;
 
     DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
+    break;
+  }
+  case CallKind::InstanceOf: {
+    // We need to obtain the only template argument to determinte the type.
+    const FunctionDecl *FD = Call.getDecl()->getAsFunction();
+    if (!FD || !FD->getTemplateSpecializationArgs())
+      return false;
+
+    DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
     break;
   }
   case CallKind::Method:

Modified: cfe/trunk/test/Analysis/Inputs/llvm.h
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/Inputs/llvm.h?rev=369615&r1=369614&r2=369615&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/Inputs/llvm.h (original)
+++ cfe/trunk/test/Analysis/Inputs/llvm.h Wed Aug 21 19:57:59 2019
@@ -16,4 +16,10 @@ template <class X, class Y>
 const X *dyn_cast_or_null(Y *Value);
 template <class X, class Y>
 const X *dyn_cast_or_null(Y &Value);
+
+template <class X, class Y>
+bool isa(Y Value);
+
+template <class X, class Y>
+bool isa_and_nonnull(Y Value);
 } // namespace llvm

Modified: cfe/trunk/test/Analysis/cast-value-logic.cpp
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/cast-value-logic.cpp?rev=369615&r1=369614&r2=369615&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/cast-value-logic.cpp (original)
+++ cfe/trunk/test/Analysis/cast-value-logic.cpp Wed Aug 21 19:57:59 2019
@@ -23,11 +23,16 @@ class Circle : public Shape {};
 using namespace llvm;
 using namespace clang;
 
-void test_regions(const Shape *A, const Shape *B) {
+void test_regions_dyn_cast(const Shape *A, const Shape *B) {
   if (dyn_cast<Circle>(A) && !dyn_cast<Circle>(B))
     clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
 }
 
+void test_regions_isa(const Shape *A, const Shape *B) {
+  if (isa<Circle>(A) && !isa<Circle>(B))
+    clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+}
+
 namespace test_cast {
 void evalLogic(const Shape *S) {
   const Circle *C = cast<Circle>(S);

Modified: cfe/trunk/test/Analysis/cast-value-notes.cpp
URL: 
http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/cast-value-notes.cpp?rev=369615&r1=369614&r2=369615&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/cast-value-notes.cpp (original)
+++ cfe/trunk/test/Analysis/cast-value-notes.cpp Wed Aug 21 19:57:59 2019
@@ -1,5 +1,5 @@
 // RUN: %clang_analyze_cc1 \
-// RUN:  -analyzer-checker=core,apiModeling.llvm.CastValue \
+// RUN:  
-analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\
 // RUN:  -analyzer-output=text -verify %s
 
 #include "Inputs/llvm.h"
@@ -43,16 +43,21 @@ void evalNonNullParamNonNullReturnRefere
     return;
   }
 
-  if (dyn_cast_or_null<Triangle>(C)) {
+  if (isa<Triangle>(C)) {
     // expected-note@-1 {{'C' is not a 'Triangle'}}
     // expected-note@-2 {{Taking false branch}}
     return;
   }
 
-  (void)(1 / !C);
-  // expected-note@-1 {{'C' is non-null}}
-  // expected-note@-2 {{Division by zero}}
-  // expected-warning@-3 {{Division by zero}}
+  if (isa<Circle>(C)) {
+    // expected-note@-1 {{'C' is a 'Circle'}}
+    // expected-note@-2 {{Taking true branch}}
+
+    (void)(1 / !C);
+    // expected-note@-1 {{'C' is non-null}}
+    // expected-note@-2 {{Division by zero}}
+    // expected-warning@-3 {{Division by zero}}
+  }
 }
 
 void evalNonNullParamNonNullReturn(const Shape *S) {
@@ -60,7 +65,13 @@ void evalNonNullParamNonNullReturn(const
   // expected-note@-1 {{'S' is a 'Circle'}}
   // expected-note@-2 {{'C' initialized here}}
 
-  if (!cast<Triangle>(C)) {
+  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'}}
     // expected-note@-2 {{Taking false branch}}
     return;


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

Reply via email to