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(Func)) { + if (!isContainer(Ctor->getParent())) + return; + + if (Ctor->getNumParams() == 0) + handleDefaultConstruction(C, Call.getOriginExpr(), + *Call.getReturnValueUnderConstruction()); } else if (const auto *InstCall = dyn_cast(&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(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); 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(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(Method)) { - if (Ctor->isCopyConstructor()) { - HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public; - } - continue; - } - if (const auto *Dtor = dyn_cast(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() && + "Symbol comparison must be a `DefinedSVal`"); + + auto NewState = State->assume(comparison.castAs(), Equal); + if (!NewState) + return nullptr; + + if (const auto CompSym = comparison.getAsSymbol()) { + assert(isa(CompSym) && + "Symbol comparison must be a `SymIntExpr`"); + assert(BinaryOperator::isComparisonOp( + cast(CompSym)->getOpcode()) && + "Symbol comparison must be a comparison"); + return assumeNoOverflow(NewState, cast(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/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() && - "Symbol comparison must be a `DefinedSVal`"); - - auto NewState = State->assume(comparison.castAs(), Equal); - if (!NewState) - return nullptr; - - if (const auto CompSym = comparison.getAsSymbol()) { - assert(isa(CompSym) && - "Symbol comparison must be a `SymIntExpr`"); - assert(BinaryOperator::isComparisonOp( - cast(CompSym)->getOpcode()) && - "Symbol comparison must be a comparison"); - return assumeNoOverflow(NewState, cast(CompSym)->getLHS(), 2); - } - - return NewState; -} - const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) { while (Node) { ProgramPoint PP = Node->getLocation(); 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 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