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.
Charusso added a parent revision: D66325: [analyzer] CastValueChecker: Store 
the dynamic types and casts.

-


Repository:
  rC Clang

https://reviews.llvm.org/D66423

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

Index: clang/test/Analysis/cast-value-notes.cpp
===================================================================
--- clang/test/Analysis/cast-value-notes.cpp
+++ clang/test/Analysis/cast-value-notes.cpp
@@ -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
 
 namespace llvm {
@@ -18,6 +18,12 @@
 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
 
 namespace clang {
@@ -79,10 +85,21 @@
     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<Triangle>(C)) {
+    // expected-note@-1 {{'C' with type 'Circle' is not the instance of 'Triangle'}}
+    // expected-note@-2 {{Taking false branch}}
+    return;
+  }
+
+  if (isa<Circle>(C)) {
+    // expected-note@-1 {{'C' with type 'Circle' 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) {
@@ -90,8 +107,14 @@
   // expected-note@-1 {{Checked cast from 'Shape' to 'Circle' succeeds}}
   // expected-note@-2 {{'C' initialized here}}
 
-  if (!cast<Triangle>(C)) {
-    // expected-note@-1 {{Checked cast from 'Circle' to 'Triangle' succeeds}}
+  if (isa<Triangle>(C)) {
+    // expected-note@-1 {{Assuming 'C' with type 'Circle' is not the instance of 'Triangle'}}
+    // expected-note@-2 {{Taking false branch}}
+    return;
+  }
+
+  if (isa<Triangle>(C)) {
+    // expected-note@-1 {{'C' with type 'Circle' is not the instance of 'Triangle'}}
     // expected-note@-2 {{Taking false branch}}
     return;
   }
Index: clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -10,6 +10,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/AST/DeclTemplate.h"
+#include "clang/Lex/Lexer.h"
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -24,7 +26,7 @@
 
 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,
@@ -39,6 +41,10 @@
   //
   // 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;
 
 private:
@@ -56,7 +62,11 @@
       {{{"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;
@@ -70,6 +80,10 @@
                   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
 
@@ -85,6 +99,13 @@
   return Ty.getUnqualifiedType();
 }
 
+static bool isInfeasibleCast(const DynamicCastInfo *Cast, bool IsCastSucceeds) {
+  if (!Cast)
+    return false;
+
+  return IsCastSucceeds ? Cast->isFails() : Cast->isSucceeds();
+}
+
 //===----------------------------------------------------------------------===//
 // Main logic to evaluate a cast.
 //===----------------------------------------------------------------------===//
@@ -114,13 +135,9 @@
   else
     IsCastSucceeds = IsCheckedCast || IsNonNullReturn || CastFromTy == CastToTy;
 
-  // Check for infeasible casts.
-  if (!IsCheckedCast && Cast) {
-    if ((IsCastSucceeds && Cast->isFails()) ||
-        (!IsCastSucceeds && Cast->isSucceeds())) {
-      C.generateSink(State, C.getPredecessor());
-      return;
-    }
+  if (!IsCheckedCast && isInfeasibleCast(Cast, IsCastSucceeds)) {
+    C.generateSink(State, C.getPredecessor());
+    return;
   }
 
   // Store the type and the cast information.
@@ -154,6 +171,75 @@
       Tag);
 }
 
+static void addInstanceOfTransition(const CallEvent &Call,
+                                    DefinedOrUnknownSVal DV,
+                                    ProgramStateRef State, CheckerContext &C,
+                                    bool IsInstanceOf) {
+  const auto *CE = cast<CallExpr>(Call.getOriginExpr());
+
+  const DeclRefExpr *DRE = nullptr;
+  const Stmt *Body = CE->getCalleeDecl()->getBody();
+  if (Body)
+    DRE = dyn_cast<DeclRefExpr>(Body);
+
+  if (!Body || !DRE)
+    DRE = dyn_cast<DeclRefExpr>(CE->getCallee()->IgnoreParenImpCasts());
+
+  QualType CastToTy = DRE->getTemplateArgs()->getArgument().getAsType();
+  QualType CastFromTy = getRecordType(Call.parameters()[0]->getType());
+
+  const DynamicCastInfo *Cast = getDynamicCastInfo(State, CastFromTy, CastToTy);
+
+  bool IsCastSucceeds;
+  if (Cast)
+    IsCastSucceeds = IsInstanceOf && Cast->isSucceeds();
+  else
+    IsCastSucceeds = IsInstanceOf || CastFromTy == CastToTy;
+
+  if (isInfeasibleCast(Cast, IsCastSucceeds)) {
+    C.generateSink(State, C.getPredecessor());
+    return;
+  }
+
+  // Store the type and the cast information.
+  const MemRegion *MR = DV.getAsRegion();
+  bool IsKnownCast = Cast || CastFromTy == CastToTy;
+  if (!IsKnownCast)
+    State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
+                                      Call.getResultType(), IsInstanceOf);
+
+  std::string ObjectName = Lexer::getSourceText(
+      CharSourceRange::getTokenRange(CE->getArg(0)->getSourceRange()),
+      C.getSourceManager(), C.getASTContext().getLangOpts());
+
+  const NoteTag *Tag = C.getNoteTag(
+      [=](BugReport &) -> std::string {
+        SmallString<128> Msg;
+        llvm::raw_svector_ostream Out(Msg);
+
+        Out << (IsKnownCast ? "'" : "Assuming '") << ObjectName
+            << "' with type '"
+            << CastFromTy->getAsCXXRecordDecl()->getNameAsString() << "' ";
+
+        if (CastFromTy != CastToTy) {
+          Out << (IsInstanceOf ? "is also an" : "is not the") << " instance of";
+        } else {
+          Out << "is a";
+        };
+
+        Out << " '" << CastToTy->getAsCXXRecordDecl()->getNameAsString()
+            << '\'';
+
+        return Out.str();
+      },
+      /*IsPrunable=*/true);
+
+  C.addTransition(
+      State->BindExpr(CE, C.getLocationContext(),
+                      C.getSValBuilder().makeTruthVal(IsInstanceOf)),
+      Tag);
+}
+
 //===----------------------------------------------------------------------===//
 // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
 //===----------------------------------------------------------------------===//
@@ -242,6 +328,41 @@
   evalZeroParamNullReturn(Call, DV, 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.
 //===----------------------------------------------------------------------===//
@@ -252,12 +373,15 @@
   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 (!Call.getResultType()->getPointeeCXXRecordDecl())
+  QualType ResultTy = Call.getResultType();
+  if (Kind != CallKind::InstanceOf && !ResultTy->getPointeeCXXRecordDecl())
     return false;
 
-  const CastCheck &Check = Lookup->first;
-  CallKind Kind = Lookup->second;
+  const auto *CE = cast<CallExpr>(Call.getOriginExpr());
   Optional<DefinedOrUnknownSVal> DV;
 
   switch (Kind) {
@@ -270,6 +394,23 @@
     DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
     break;
   }
+  case CallKind::InstanceOf: {
+    // If we cannot obtain the call's 'DRE' we cannot be sure how to model it.
+    const DeclRefExpr *DRE = nullptr;
+    const Stmt *Body = CE->getCalleeDecl()->getBody();
+    if (Body)
+      DRE = dyn_cast<DeclRefExpr>(Body);
+
+    if (!Body || !DRE)
+      DRE = dyn_cast<DeclRefExpr>(CE->getCallee()->IgnoreParenImpCasts());
+
+    // We have to obtain the only template argument to determinte the type.
+    if (!DRE || !DRE->getTemplateArgs())
+      return false;
+
+    DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
+    break;
+  }
   case CallKind::Method:
     const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call);
     if (!InstanceCall)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to