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.
baloghadamsoftware added a parent revision: D62893: [Analyzer] Iterator 
Checkers - Make range errors and invalidated access fatal.

In case of invoking `std::advance`, `std::prev` and `std::next` range errors 
should be reported in the invoker and not inside the STL implementation code. 
Another problem is if the analyzer budget runs out just at the point these 
functions are invoked. This results in strange behavior such as 
`std::prev(v.end())` equals `v.end()`. To prevent this simulate these functions 
if they were not inlined.


Repository:
  rC Clang

https://reviews.llvm.org/D62895

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

Index: test/Analysis/iterator-range.cpp
===================================================================
--- test/Analysis/iterator-range.cpp
+++ test/Analysis/iterator-range.cpp
@@ -116,6 +116,17 @@
   }
 }
 
+void good_advance(std::vector<int> &vec) {
+  auto i = vec.end();
+  std::advance(i, -1);
+  *i; // no-warning
+}
+
+void good_prev(std::vector<int> &vec) {
+  auto i = vec.end();
+  *std::prev(i); // no-warning
+}
+
 void good_push_back(std::list<int> &L, int n) {
   auto i0 = --L.cend();
   L.push_back(n);
@@ -195,11 +206,31 @@
   --i0; // no-warning
 }
 
+void good_decr_end_advance(const std::list<int> &L) {
+  auto i0 = L.end();
+  std::advance(i0, -1); // no-warning
+}
+
+void good_decr_end_prev(const std::list<int> &L) {
+  auto i0 = L.end();
+  auto i1 = std::prev(i0); // no-warning
+}
+
 void bad_incr_end(const std::list<int> &L) {
   auto i0 = L.end();
   ++i0;  // expected-warning{{Iterator incremented behind the past-the-end iterator}}
 }
 
+void bad_decr_end_advance(const std::list<int> &L) {
+  auto i0 = L.end();
+  std::advance(i0, 1);  // expected-warning{{Iterator incremented behind the past-the-end }}
+}
+
+void bad_decr_end_next(const std::list<int> &L) {
+  auto i0 = L.end();
+  auto i1 = std::next(i0);  // expected-warning{{Iterator incremented behind the past-the-end }}
+}
+
 struct simple_iterator_base {
   simple_iterator_base();
   simple_iterator_base(const simple_iterator_base& rhs);
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
@@ -768,6 +768,15 @@
     return it;
   }
 
+  template <class ForwardIterator>
+  ForwardIterator
+  next (ForwardIterator it,
+        typename iterator_traits<ForwardIterator>::difference_type n =
+        1) {
+    advance(it, n);
+    return it;
+  }
+
   template <class InputIterator, class T>
   InputIterator find(InputIterator first, InputIterator last, const T &val);
 
Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -287,6 +287,9 @@
 bool isEraseCall(const FunctionDecl *Func);
 bool isEraseAfterCall(const FunctionDecl *Func);
 bool isEmplaceCall(const FunctionDecl *Func);
+bool isStdAdvanceCall(const FunctionDecl *Func);
+bool isStdPrevCall(const FunctionDecl *Func);
+bool isStdNextCall(const FunctionDecl *Func);
 bool isAssignmentOperator(OverloadedOperatorKind OK);
 bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
 bool isAccessOperator(OverloadedOperatorKind OK);
@@ -473,6 +476,12 @@
       verifyMatch(C, Call.getArgSVal(0),
                   InstCall->getCXXThisVal().getAsRegion());
     }
+  } else if (ChecksEnabled[CK_IteratorRangeChecker] &&
+             (isStdAdvanceCall(Func) || isStdNextCall(Func))) {
+    verifyRandomIncrOrDecr(C, OO_Plus, Call.getArgSVal(0), Call.getArgSVal(1));
+  } else if (ChecksEnabled[CK_IteratorRangeChecker] &&
+             isStdPrevCall(Func)) {
+    verifyRandomIncrOrDecr(C, OO_Minus, Call.getArgSVal(0), Call.getArgSVal(1));
   } else if (isa<CXXConstructorCall>(&Call)) {
     // Check match of first-last iterator pair in a constructor of a container
     if (Call.getNumArgs() < 2)
@@ -685,6 +694,24 @@
       }
     }
 
+    if (!C.wasInlined) {
+      if (isStdAdvanceCall(Func)) {
+        handleRandomIncrOrDecr(C, OO_PlusEqual, UndefinedVal(),
+                               Call.getArgSVal(0), Call.getArgSVal(1));
+        return;
+      }
+      if (isStdPrevCall(Func)) {
+        handleRandomIncrOrDecr(C, OO_Minus, Call.getReturnValue(),
+                               Call.getArgSVal(0), Call.getArgSVal(1));
+        return;
+      }
+      if (isStdNextCall(Func)) {
+        handleRandomIncrOrDecr(C, OO_Plus, Call.getReturnValue(),
+                               Call.getArgSVal(0), Call.getArgSVal(1));
+        return;
+      }
+    }
+
     const auto *OrigExpr = Call.getOriginExpr();
     if (!OrigExpr)
       return;
@@ -1825,6 +1852,38 @@
   return IdInfo->getName() == "erase_after";
 }
 
+bool isStdAdvanceCall(const FunctionDecl *Func) {
+  if (!Func->isInStdNamespace())
+    return false;
+  const auto *IdInfo = Func->getIdentifier();
+  if (!IdInfo)
+    return false;
+  return (Func->getNumParams() == 2 && IdInfo->getName() == "advance") ||
+         (Func->getNumParams() == 3 && IdInfo->getName() == "__advance");
+}
+
+bool isStdPrevCall(const FunctionDecl *Func) {
+  if (!Func->isInStdNamespace())
+    return false;
+  const auto *IdInfo = Func->getIdentifier();
+  if (!IdInfo)
+    return false;
+  if (Func->getNumParams() != 2)
+    return false;
+  return IdInfo->getName() == "prev";
+}
+
+bool isStdNextCall(const FunctionDecl *Func) {
+  if (!Func->isInStdNamespace())
+    return false;
+  const auto *IdInfo = Func->getIdentifier();
+  if (!IdInfo)
+    return false;
+  if (Func->getNumParams() != 2)
+    return false;
+  return IdInfo->getName() == "next";
+}
+
 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
  • [PATCH] D62895: [Analyzer] ... Balogh, Ádám via Phabricator via cfe-commits

Reply via email to