baloghadamsoftware created this revision.
baloghadamsoftware added reviewers: NoQ, Szelethus.
baloghadamsoftware added a project: clang.
Herald added subscribers: Charusso, gamesh411, donat.nagy, mikhail.ramalho, 
a.sidorin, rnkovacs, szepet, xazax.hun, whisperity.

Modeling of the `empty()` method is essential for more accurate reporting 
past-the-end iterator access.


Repository:
  rC Clang

https://reviews.llvm.org/D62688

Files:
  lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
  test/Analysis/Inputs/system-header-simulator-cxx.h
  test/Analysis/diagnostics/explicit-suppression.cpp
  test/Analysis/iterator-range.cpp

Index: test/Analysis/iterator-range.cpp
===================================================================
--- test/Analysis/iterator-range.cpp
+++ test/Analysis/iterator-range.cpp
@@ -5,6 +5,12 @@
 
 void clang_analyzer_warnIfReached();
 
+extern void __assert_fail (__const char *__assertion, __const char *__file,
+    unsigned int __line, __const char *__function)
+     __attribute__ ((__noreturn__));
+#define assert(expr) \
+  ((expr)  ? (void)(0)  : __assert_fail (#expr, __FILE__, __LINE__, __func__))
+
 void simple_good_end(const std::vector<int> &v) {
   auto i = v.end();
   if (i != v.end()) {
@@ -236,3 +242,32 @@
     *i0; // no-warning
   }
 }
+
+void empty(const std::vector<int> &V) {
+  for (auto n: V) {}
+  *V.begin(); // expected-warning{{Past-the-end iterator dereferenced}}
+}
+
+void non_empty1(const std::vector<int> &V) {
+  assert(!V.empty());
+  for (auto n: V) {}
+  *V.begin(); // no-warning
+}
+
+void non_empty2(const std::vector<int> &V) {
+  for (auto n: V) {}
+  assert(!V.empty());
+  *V.begin(); // no-warning
+}
+
+bool is_empty_V() {
+  std::vector<int> V;
+  bool e = V.empty();
+  return e;
+}
+
+void deferred_emptiness_check() {
+  bool b = is_empty_V();
+  if (b) {
+  }
+}
Index: test/Analysis/diagnostics/explicit-suppression.cpp
===================================================================
--- test/Analysis/diagnostics/explicit-suppression.cpp
+++ test/Analysis/diagnostics/explicit-suppression.cpp
@@ -19,6 +19,6 @@
 void testCopyNull(C *I, C *E) {
   std::copy(I, E, (C *)0);
 #ifndef SUPPRESSED
-  // expected-warning@../Inputs/system-header-simulator-cxx.h:677 {{Called C++ object pointer is null}}
+  // expected-warning@../Inputs/system-header-simulator-cxx.h:685 {{Called C++ object pointer is null}}
 #endif
 }
Index: test/Analysis/Inputs/system-header-simulator-cxx.h
===================================================================
--- test/Analysis/Inputs/system-header-simulator-cxx.h
+++ test/Analysis/Inputs/system-header-simulator-cxx.h
@@ -316,6 +316,8 @@
     const T& front() const { return *begin(); }
     T& back() { return *(end() - 1); }
     const T& back() const { return *(end() - 1); }
+
+    bool empty() const;
   };
   
   template<typename T>
@@ -386,6 +388,8 @@
     const T& front() const { return *begin(); }
     T& back() { return *--end(); }
     const T& back() const { return *--end(); }
+
+    bool empty() const;
   };
 
   template<typename T>
@@ -466,6 +470,8 @@
     const T& front() const { return *begin(); }
     T& back() { return *(end() - 1); }
     const T& back() const { return *(end() - 1); }
+
+    bool empty() const;
   };
 
   template<typename T>
@@ -529,6 +535,8 @@
 
     T& front() { return *begin(); }
     const T& front() const { return *begin(); }
+
+    bool empty() const;
   };
 
   template <typename CharT>
Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -182,6 +182,8 @@
   void handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal,
                         const SVal &LVal, const SVal &RVal,
                         OverloadedOperatorKind Op) const;
+  void handleEmpty(CheckerContext &C, const Expr *CE, const SVal &Cont,
+                   const SVal &RetVal) const;
   void processComparison(CheckerContext &C, ProgramStateRef State,
                          SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
                          OverloadedOperatorKind Op) const;
@@ -272,6 +274,7 @@
 
 bool isIteratorType(const QualType &Type);
 bool isIterator(const CXXRecordDecl *CRD);
+bool isContainerType(const QualType &Type);
 bool isComparisonOperator(OverloadedOperatorKind OK);
 bool isBeginCall(const FunctionDecl *Func);
 bool isEndCall(const FunctionDecl *Func);
@@ -279,6 +282,7 @@
 bool isClearCall(const FunctionDecl *Func);
 bool isPushBackCall(const FunctionDecl *Func);
 bool isEmplaceBackCall(const FunctionDecl *Func);
+bool isEmptyCall(const FunctionDecl *Func);
 bool isPopBackCall(const FunctionDecl *Func);
 bool isPushFrontCall(const FunctionDecl *Func);
 bool isEmplaceFrontCall(const FunctionDecl *Func);
@@ -691,6 +695,17 @@
     if (!OrigExpr)
       return;
 
+    if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+      if (isContainerType(InstCall->getCXXThisExpr()->getType())) {
+        if (isEmptyCall(Func)) {
+          handleEmpty(C, OrigExpr, InstCall->getCXXThisVal(),
+                      Call.getReturnValue());
+        }
+      }
+    }
+
+
+
     if (!isIteratorType(Call.getResultType()))
       return;
 
@@ -888,6 +903,33 @@
   processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
 }
 
+void IteratorChecker::handleEmpty(CheckerContext &C, const Expr *CE,
+                                  const SVal &Cont, const SVal &RetVal) const {
+  const auto *ContReg = Cont.getAsRegion();
+  if (!ContReg)
+    return;
+
+  ContReg = ContReg->getMostDerivedObjectRegion();
+
+  // If the container already has a begin symbol then use it. Otherwise first
+  // create a new one.
+  auto State = C.getState();
+  auto BeginSym = getContainerBegin(State, ContReg);
+  if (!BeginSym) {
+    State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
+                                 C.getLocationContext(), C.blockCount());
+    BeginSym = getContainerBegin(State, ContReg);
+  }
+  auto EndSym = getContainerEnd(State, ContReg);
+  if (!EndSym) {
+    State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
+                                 C.getLocationContext(), C.blockCount());
+    EndSym = getContainerEnd(State, ContReg);
+  }
+
+  processComparison(C, State, BeginSym, EndSym, RetVal, OO_EqualEqual);
+}
+
 void IteratorChecker::processComparison(CheckerContext &C,
                                         ProgramStateRef State, SymbolRef Sym1,
                                         SymbolRef Sym2, const SVal &RetVal,
@@ -1622,6 +1664,7 @@
 
 namespace {
 
+bool isContainer(const CXXRecordDecl *CRD);
 bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
 bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
 bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
@@ -1686,6 +1729,39 @@
          HasPostIncrOp && HasDerefOp;
 }
 
+bool isContainerType(const QualType &Type) {
+  if (isIteratorType(Type))
+    return false;
+
+  const auto *CRD = Type.getNonReferenceType()
+    ->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
+  return isContainer(CRD);
+}
+
+bool isContainer(const CXXRecordDecl *CRD) {
+  if (!CRD)
+    return false;
+
+  for (const auto *Decl : CRD->decls()) {
+    const auto *TD = dyn_cast<TypeDecl>(Decl);
+    if (!TD)
+      continue;
+
+    const auto *Type = TD->getTypeForDecl();
+    if (!Type)
+      continue;
+    
+    const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
+    if(!CRD)
+      continue;
+    
+    if (isIterator(CRD))
+      return true;
+  }
+
+  return false;
+}
+
 bool isComparisonOperator(OverloadedOperatorKind OK) {
   return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less ||
          OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual;
@@ -1827,6 +1903,15 @@
   return IdInfo->getName() == "erase_after";
 }
 
+bool isEmptyCall(const FunctionDecl *Func) {
+  const auto *IdInfo = Func->getIdentifier();
+  if (!IdInfo)
+    return false;
+  if (Func->getNumParams() > 0)
+    return false;
+  return IdInfo->getName() == "empty";
+}
+
 bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
 
 bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to