baloghadamsoftware created this revision.
baloghadamsoftware added reviewers: NoQ, vsavchenko, gamesh411.
baloghadamsoftware added a project: clang.
Herald added subscribers: ASDenysPetrov, martong, steakhal, Charusso, dkrupp, 
donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, rnkovacs, szepet, xazax.hun, 
whisperity.
Herald added a reviewer: Szelethus.
baloghadamsoftware requested review of this revision.

Model default constructors of containers by setting their abstract `begin()` 
and `end()` symbols to equal.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D87388

Files:
  clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
  clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
  clang/lib/StaticAnalyzer/Checkers/Iterator.h
  clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
  clang/test/Analysis/container-modeling.cpp

Index: clang/test/Analysis/container-modeling.cpp
===================================================================
--- clang/test/Analysis/container-modeling.cpp
+++ clang/test/Analysis/container-modeling.cpp
@@ -32,6 +32,23 @@
                                                            // expected-note@-1{{$V.end()}}
 }
 
+////////////////////////////////////////////////////////////////////////////////
+///
+/// C O N T A I N E R   C O N S T R U C T O R S
+///
+////////////////////////////////////////////////////////////////////////////////
+
+// Default Constructor
+
+void default_constructor() {
+  std::vector<int> V;
+
+  clang_analyzer_eval(clang_analyzer_container_begin(V) ==
+                      clang_analyzer_container_end(V));
+  // expected-warning@-2{{TRUE}}
+  // expected-note@-3   {{TRUE}}
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 ///
 /// C O N T A I N E R   A S S I G N M E N T S
Index: clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
+++ clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
@@ -157,8 +157,6 @@
 bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
 bool isSimpleComparisonOperator(BinaryOperatorKind OK);
 ProgramStateRef removeIteratorPosition(ProgramStateRef State, SVal Val);
-ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
-                              SymbolRef Sym2, bool Equal);
 const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call);
 
 } // namespace
@@ -777,38 +775,6 @@
   return State;
 }
 
-ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
-                              SymbolRef Sym2, bool Equal) {
-  auto &SVB = State->getStateManager().getSValBuilder();
-
-  // FIXME: This code should be reworked as follows:
-  // 1. Subtract the operands using evalBinOp().
-  // 2. Assume that the result doesn't overflow.
-  // 3. Compare the result to 0.
-  // 4. Assume the result of the comparison.
-  const auto comparison =
-    SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
-                  nonloc::SymbolVal(Sym2), SVB.getConditionType());
-
-  assert(comparison.getAs<DefinedSVal>() &&
-    "Symbol comparison must be a `DefinedSVal`");
-
-  auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
-  if (!NewState)
-    return nullptr;
-
-  if (const auto CompSym = comparison.getAsSymbol()) {
-    assert(isa<SymIntExpr>(CompSym) &&
-           "Symbol comparison must be a `SymIntExpr`");
-    assert(BinaryOperator::isComparisonOp(
-               cast<SymIntExpr>(CompSym)->getOpcode()) &&
-           "Symbol comparison must be a comparison");
-    return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
-  }
-
-  return NewState;
-}
-
 const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) {
   while (Node) {
     ProgramPoint PP = Node->getLocation();
Index: clang/lib/StaticAnalyzer/Checkers/Iterator.h
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/Iterator.h
+++ clang/lib/StaticAnalyzer/Checkers/Iterator.h
@@ -144,7 +144,8 @@
 
 namespace iterator {
 
-bool isIteratorType(const QualType &Type);
+bool isIteratorType(const QualType &QTyp);
+bool isIteratorType(const Type* Typ);
 bool isIterator(const CXXRecordDecl *CRD);
 bool isComparisonOperator(OverloadedOperatorKind OK);
 bool isInsertCall(const FunctionDecl *Func);
@@ -179,6 +180,8 @@
                                 const SVal &Distance);
 ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
                                  long Scale);
+ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
+                              SymbolRef Sym2, bool Equal);
 bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
              BinaryOperator::Opcode Opc);
 bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
Index: clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
+++ clang/lib/StaticAnalyzer/Checkers/Iterator.cpp
@@ -18,11 +18,21 @@
 namespace ento {
 namespace iterator {
 
-bool isIteratorType(const QualType &Type) {
-  if (Type->isPointerType())
+bool isIteratorType(const QualType &QTyp) {
+  return isIteratorType(QTyp.getTypePtr());
+}
+
+bool isIteratorType(const Type *Typ) {
+  if (Typ->isPointerType())
     return true;
 
-  const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
+  const auto *CRD = Typ->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
+  if (!CRD)
+    return false;
+
+  if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(CRD))
+    CRD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
+
   return isIterator(CRD);
 }
 
@@ -35,23 +45,16 @@
         Name.endswith_lower("it")))
     return false;
 
-  bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
-       HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
+  if ((!CRD->hasSimpleCopyConstructor() &&
+       !CRD->hasUserDeclaredCopyConstructor()) ||
+      (!CRD->hasSimpleCopyAssignment() &&
+       !CRD->hasUserDeclaredCopyAssignment()) ||
+      (!CRD->hasSimpleDestructor() &&
+       !CRD->hasUserDeclaredDestructor()))
+    return false;
+
+  bool HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
   for (const auto *Method : CRD->methods()) {
-    if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
-      if (Ctor->isCopyConstructor()) {
-        HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
-      }
-      continue;
-    }
-    if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
-      HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
-      continue;
-    }
-    if (Method->isCopyAssignmentOperator()) {
-      HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
-      continue;
-    }
     if (!Method->isOverloadedOperator())
       continue;
     const auto OPK = Method->getOverloadedOperator();
@@ -66,8 +69,7 @@
     }
   }
 
-  return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
-         HasPostIncrOp && HasDerefOp;
+  return HasPreIncrOp && HasPostIncrOp && HasDerefOp;
 }
 
 bool isComparisonOperator(OverloadedOperatorKind OK) {
@@ -294,6 +296,38 @@
   return NewState;
 }
 
+ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
+                              SymbolRef Sym2, bool Equal) {
+  auto &SVB = State->getStateManager().getSValBuilder();
+
+  // FIXME: This code should be reworked as follows:
+  // 1. Subtract the operands using evalBinOp().
+  // 2. Assume that the result doesn't overflow.
+  // 3. Compare the result to 0.
+  // 4. Assume the result of the comparison.
+  const auto comparison =
+    SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
+                  nonloc::SymbolVal(Sym2), SVB.getConditionType());
+
+  assert(comparison.getAs<DefinedSVal>() &&
+    "Symbol comparison must be a `DefinedSVal`");
+
+  auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
+  if (!NewState)
+    return nullptr;
+
+  if (const auto CompSym = comparison.getAsSymbol()) {
+    assert(isa<SymIntExpr>(CompSym) &&
+           "Symbol comparison must be a `SymIntExpr`");
+    assert(BinaryOperator::isComparisonOp(
+               cast<SymIntExpr>(CompSym)->getOpcode()) &&
+           "Symbol comparison must be a comparison");
+    return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
+  }
+
+  return NewState;
+}
+
 bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
              BinaryOperator::Opcode Opc) {
   return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
Index: clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
+++ clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
@@ -36,6 +36,8 @@
                    SVal Cont) const;
   void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal,
                  SVal Cont) const;
+  void handleDefaultConstruction(CheckerContext &C, const Expr *CE,
+                                 SVal Cont) const;
   void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr,
                         SVal OldCont = UndefinedVal()) const;
   void handleAssign(CheckerContext &C, SVal Cont, const Expr *ContE) const;
@@ -111,6 +113,7 @@
 
 bool isBeginCall(const FunctionDecl *Func);
 bool isEndCall(const FunctionDecl *Func);
+bool isContainer(const CXXRecordDecl *CRD);
 bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
 bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
 bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
@@ -177,6 +180,13 @@
       handleAssignment(C, InstCall->getCXXThisVal());
       return;
     }
+  } else if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
+    if (!isContainer(Ctor->getParent()))
+      return;
+
+    if (Ctor->getNumParams() == 0)
+      handleDefaultConstruction(C, Call.getOriginExpr(),
+                                *Call.getReturnValueUnderConstruction());
   } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
     const auto *OrigExpr = Call.getOriginExpr();
     if (!OrigExpr)
@@ -307,6 +317,26 @@
   C.addTransition(State);
 }
 
+void ContainerModeling::handleDefaultConstruction(CheckerContext &C,
+                                                  const Expr *CE,
+                                                  SVal Cont) const {
+  const auto *ContReg = Cont.getAsRegion();
+  ContReg = ContReg->getMostDerivedObjectRegion();
+
+  auto State = C.getState();
+  State = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
+                               C.getLocationContext(), C.blockCount());
+  auto BeginSym = getContainerBegin(State, ContReg);
+  State = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
+                             C.getLocationContext(), C.blockCount());
+  auto EndSym = getContainerEnd(State, ContReg);
+
+  State = relateSymbols(State, BeginSym, EndSym, true);
+  assert(State &&
+         "Setting size of newly constructed containers must always succeed");
+  C.addTransition(State);
+}
+
 void ContainerModeling::handleAssignment(CheckerContext &C, SVal Cont,
                                          const Expr *CE, SVal OldCont) const {
   const auto *ContReg = Cont.getAsRegion();
@@ -780,6 +810,26 @@
   return IdInfo->getName().endswith_lower("end");
 }
 
+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 *Typ = TD->getTypeForDecl();
+    if (!Typ)
+      continue;
+
+    if (isIteratorType(Typ))
+      return true;
+  }
+
+  return false;
+}
+
 const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
                                       const MemRegion *Reg) {
   auto TI = getDynamicTypeInfo(State, Reg);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to