Index: clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h +++ clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h @@ -216,6 +216,47 @@ "' inside " + Visit(R->getSuperRegion()); } + std::string VisitParamRegion(const ParamRegion *R) { + std::string Str; + llvm::raw_string_ostream OS(Str); + + OS << "parameter " << R->getIndex() << " of"; + ParmVarDecl *PVD = nullptr; + if (const auto *CE = dyn_cast(R->getOriginExpr())) { + const FunctionDecl *FD = CE->getDirectCallee(); + if (FD) { + assert(R->getIndex() < FD->param_size()); + OS << " function '" << FD->getQualifiedNameAsString() << "()'"; + PVD = FD->parameters()[R->getIndex()]; + } + } else if (const auto *CCE = + dyn_cast(R->getOriginExpr())) { + const CXXConstructorDecl *CCD = CCE->getConstructor(); + assert(R->getIndex() < CCD->param_size()); + OS << " C++ constructor '" << CCD->getQualifiedNameAsString() << "()'"; + PVD = CCD->parameters()[R->getIndex()]; + } else if (const auto *OCME = + dyn_cast(R->getOriginExpr())) { + const ObjCMethodDecl *OCMD = OCME->getMethodDecl(); + assert(R->getIndex() < OCMD->param_size()); + OS << " Objective-C method '" << OCMD->getQualifiedNameAsString() << "'"; + PVD = OCMD->parameters()[R->getIndex()]; + } else { + // FIXME: Any other kind of `Expr`? + llvm_unreachable("Maybe we forgot something..."); + } + + if (PVD) { + std::string ParamName = PVD->getQualifiedNameAsString(); + if (!ParamName.empty()) + OS << " named '" << ParamName <<"'"; + } else { + OS << R->getOriginExpr()->getID(R->getContext()); + } + + return Str; + } + std::string VisitSVal(SVal V) { std::string Str; llvm::raw_string_ostream OS(Str); Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -400,8 +400,8 @@ /// Returns memory location for a parameter variable within the callee stack /// frame. May fail; returns null on failure. - const VarRegion *getParameterLocation(unsigned Index, - unsigned BlockCount) const; + const TypedValueRegion *getParameterLocation(unsigned Index, + unsigned BlockCount) const; /// Returns true if on the current path, the argument was constructed by /// calling a C++ constructor over it. This is an internal detail of the @@ -431,6 +431,16 @@ return CallArgumentIndex; } + /// If the call returns a C++ record type then the call has a construction + /// context from where the region of its return value can be retrieved. + const ConstructionContext *getConstructionContext(unsigned BlockCount) const; + + Optional getReturnValueUnderConstruction(unsigned BlockCount) const; + + Optional getArgObject(unsigned Index, unsigned BlockCount) const; + + Optional getReturnObject(unsigned BlockCount) const; + // Iterator access to formal parameters and their types. private: struct GetTypeFn { Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -686,6 +686,11 @@ const CallEvent &Call, const EvalCallOptions &CallOpts = {}); + /// + static Optional retrieveFromConstructionContext( + ProgramStateRef State, const LocationContext *LCtx, + const ConstructionContext *CC); + private: ProgramStateRef finishArgumentConstruction(ProgramStateRef State, const CallEvent &Call); Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -704,8 +704,8 @@ return cast(*R); } - const VarRegion *getOriginalRegion() const { - return cast(*OriginalR); + const TypedValueRegion *getOriginalRegion() const { + return cast(*OriginalR); } bool operator==(const referenced_vars_iterator &I) const { @@ -727,7 +727,7 @@ /// Return the original region for a captured region, if /// one exists. - const VarRegion *getOriginalRegion(const VarRegion *VR) const; + const TypedValueRegion *getOriginalRegion(const VarRegion *VR) const; referenced_vars_iterator referenced_vars_begin() const; referenced_vars_iterator referenced_vars_end() const; @@ -742,7 +742,7 @@ private: void LazyInitializeReferencedVars(); - std::pair + std::pair getCaptureRegions(const VarDecl *VD); }; @@ -923,6 +923,8 @@ // which, unlike everything else on this list, are not memory spaces. assert(isa(sReg) || isa(sReg) || isa(sReg) || isa(sReg)); + // assert(!isa(vd) && "For `ParmVarDecl` `ParamRegion` should " + // "be used."); } static void ProfileRegion(llvm::FoldingSetNodeID& ID, const VarDecl *VD, @@ -1041,6 +1043,43 @@ } }; +class ParamRegion : public TypedValueRegion { + friend class MemRegionManager; + + const Expr *OriginExpr; + unsigned Index; + + ParamRegion(const Expr *OE, unsigned Idx, const MemRegion *SReg) + : TypedValueRegion(SReg, ParamRegionKind), OriginExpr(OE), Index(Idx) {} + + static void ProfileRegion(llvm::FoldingSetNodeID &ID, const Expr *OE, + unsigned Idx, const MemRegion *SReg); + +public: + const Expr *getOriginExpr() const { return OriginExpr; } + unsigned getIndex() const { return Index; } + + void Profile(llvm::FoldingSetNodeID& ID) const override; + + void dumpToStream(raw_ostream &os) const override; + + const StackFrameContext *getStackFrame() const; + + QualType getValueType() const override; + const ParmVarDecl* getDecl() const; + const Expr* getArgExpr() const; + + bool canPrintPrettyAsExpr() const override; + void printPrettyAsExpr(raw_ostream &os) const override; + + static bool classof(const MemRegion* R) { + return R->getKind() == ParamRegionKind; + } + + static std::pair + getExprAndIndexForParam(const ParmVarDecl *PVD, const LocationContext *LC); +}; + //===----------------------------------------------------------------------===// // Auxiliary data classes for use with MemRegions. //===----------------------------------------------------------------------===// @@ -1395,6 +1434,11 @@ /// super-region used. const CXXTempObjectRegion *getCXXStaticTempObjectRegion(const Expr *Ex); + /// getParamRegion - Retrieve or create the memory region + /// associated with a specified CallExpr, Index and LocationContext. + const ParamRegion *getParamRegion(const Expr *OE, unsigned Index, + const LocationContext *LC); + private: template Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -305,6 +305,10 @@ Loc getLValue(const CXXRecordDecl *BaseClass, const SubRegion *Super, bool IsVirtual) const; + /// Get the lvalue for a parameter. + Loc getLValue(const Expr *Call, unsigned Index, + const LocationContext *LC) const; + /// Get the lvalue for a variable reference. Loc getLValue(const VarDecl *D, const LocationContext *LC) const; Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def @@ -79,10 +79,11 @@ REGION(ElementRegion, TypedValueRegion) REGION(ObjCStringRegion, TypedValueRegion) REGION(StringRegion, TypedValueRegion) + REGION(ParamRegion, TypedValueRegion) REGION_RANGE(TYPED_VALUE_REGIONS, CompoundLiteralRegionKind, - StringRegionKind) + ParamRegionKind) REGION_RANGE(TYPED_REGIONS, BlockDataRegionKind, - StringRegionKind) + ParamRegionKind) #undef REGION_RANGE #undef ABSTRACT_REGION Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/Store.h @@ -131,9 +131,7 @@ SValBuilder& getSValBuilder() { return svalBuilder; } - virtual Loc getLValueVar(const VarDecl *VD, const LocationContext *LC) { - return svalBuilder.makeLoc(MRMgr.getVarRegion(VD, LC)); - } + virtual Loc getLValueVar(const VarDecl *VD, const LocationContext *LC); Loc getLValueCompoundLiteral(const CompoundLiteralExpr *CL, const LocationContext *LC) { Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -584,6 +584,7 @@ bool isLiveRegion(const MemRegion *region); bool isLive(const Stmt *ExprVal, const LocationContext *LCtx) const; bool isLive(const VarRegion *VR, bool includeStoreBindings = false) const; + bool isLive(const ParamRegion *VR, bool includeStoreBindings = false) const; /// Unconditionally marks a symbol as live. /// Index: clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp +++ clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp @@ -32,9 +32,9 @@ class ContainerModeling : public Checker { - void handleBegin(CheckerContext &C, const Expr *CE, SVal RetVal, + void handleBegin(CheckerContext &C, const Expr *CE, Optional RetVal, SVal Cont) const; - void handleEnd(CheckerContext &C, const Expr *CE, SVal RetVal, + void handleEnd(CheckerContext &C, const Expr *CE, Optional RetVal, SVal Cont) const; void handleAssignment(CheckerContext &C, SVal Cont, const Expr *CE = nullptr, SVal OldCont = UndefinedVal()) const; @@ -179,6 +179,22 @@ } } else { if (const auto *InstCall = dyn_cast(&Call)) { + const auto *OrigExpr = Call.getOriginExpr(); + if (!OrigExpr) + return; + + if (isBeginCall(Func)) { + handleBegin(C, OrigExpr, Call.getReturnObject(C.blockCount()), + InstCall->getCXXThisVal()); + return; + } + + if (isEndCall(Func)) { + handleEnd(C, OrigExpr, Call.getReturnObject(C.blockCount()), + InstCall->getCXXThisVal()); + return; + } + const NoItParamFn *Handler0 = NoIterParamFunctions.lookup(Call); if (Handler0) { (this->**Handler0)(C, InstCall->getCXXThisVal(), @@ -186,32 +202,29 @@ return; } - const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call); - if (Handler1) { - (this->**Handler1)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0)); + if (Call.getNumArgs() < 1) return; - } - const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call); - if (Handler2) { - (this->**Handler2)(C, InstCall->getCXXThisVal(), Call.getArgSVal(0), - Call.getArgSVal(1)); + Optional Arg0 = Call.getArgObject(0, C.blockCount()); + if (!Arg0.hasValue()) + return; + + const OneItParamFn *Handler1 = OneIterParamFunctions.lookup(Call); + if (Handler1) { + (this->**Handler1)(C, InstCall->getCXXThisVal(), *Arg0); return; } - const auto *OrigExpr = Call.getOriginExpr(); - if (!OrigExpr) + if (Call.getNumArgs() < 2) return; - if (isBeginCall(Func)) { - handleBegin(C, OrigExpr, Call.getReturnValue(), - InstCall->getCXXThisVal()); + Optional Arg1 = Call.getArgObject(1, C.blockCount()); + if (!Arg1.hasValue()) return; - } - if (isEndCall(Func)) { - handleEnd(C, OrigExpr, Call.getReturnValue(), - InstCall->getCXXThisVal()); + const TwoItParamFn *Handler2 = TwoIterParamFunctions.lookup(Call); + if (Handler2) { + (this->**Handler2)(C, InstCall->getCXXThisVal(), *Arg0, *Arg1); return; } } @@ -257,7 +270,7 @@ } void ContainerModeling::handleBegin(CheckerContext &C, const Expr *CE, - SVal RetVal, SVal Cont) const { + Optional RetVal, SVal Cont) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -273,13 +286,16 @@ C.getLocationContext(), C.blockCount()); BeginSym = getContainerBegin(State, ContReg); } - State = setIteratorPosition(State, RetVal, - IteratorPosition::getPosition(ContReg, BeginSym)); + + if (RetVal.hasValue()) + State = + setIteratorPosition(State, *RetVal, + IteratorPosition::getPosition(ContReg, BeginSym)); C.addTransition(State); } void ContainerModeling::handleEnd(CheckerContext &C, const Expr *CE, - SVal RetVal, SVal Cont) const { + Optional RetVal, SVal Cont) const { const auto *ContReg = Cont.getAsRegion(); if (!ContReg) return; @@ -295,8 +311,10 @@ C.getLocationContext(), C.blockCount()); EndSym = getContainerEnd(State, ContReg); } - State = setIteratorPosition(State, RetVal, - IteratorPosition::getPosition(ContReg, EndSym)); + + if (RetVal.hasValue()) + State = setIteratorPosition(State, *RetVal, + IteratorPosition::getPosition(ContReg, EndSym)); C.addTransition(State); } Index: clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/InvalidatedIteratorChecker.cpp @@ -60,7 +60,11 @@ if (const auto *InstCall = dyn_cast(&Call)) { verifyAccess(C, InstCall->getCXXThisVal()); } else { - verifyAccess(C, Call.getArgSVal(0)); + Optional Arg0 = Call.getArgObject(0, C.blockCount()); + if (!Arg0.hasValue()) + return; + + verifyAccess(C, *Arg0); } } } Index: clang/lib/StaticAnalyzer/Checkers/Iterator.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/Iterator.cpp +++ clang/lib/StaticAnalyzer/Checkers/Iterator.cpp @@ -158,8 +158,6 @@ return State->get(Reg); } else if (const auto Sym = Val.getAsSymbol()) { return State->get(Sym); - } else if (const auto LCVal = Val.getAs()) { - return State->get(LCVal->getRegion()); } return nullptr; } @@ -171,10 +169,8 @@ return State->set(Reg, Pos); } else if (const auto Sym = Val.getAsSymbol()) { return State->set(Sym, Pos); - } else if (const auto LCVal = Val.getAs()) { - return State->set(LCVal->getRegion(), Pos); } - return nullptr; + return State; } ProgramStateRef createIteratorPosition(ProgramStateRef State, const SVal &Val, Index: clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp +++ clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp @@ -83,11 +83,12 @@ namespace { class IteratorModeling - : public Checker, - check::Bind, check::LiveSymbols, check::DeadSymbols> { + : public Checker { using AdvanceFn = void (IteratorModeling::*)(CheckerContext &, const Expr *, - SVal, SVal, SVal) const; + Optional, SVal, + SVal) const; void handleOverloadedOperator(CheckerContext &C, const CallEvent &Call, OverloadedOperatorKind Op) const; @@ -96,25 +97,25 @@ const AdvanceFn *Handler) const; void handleComparison(CheckerContext &C, const Expr *CE, SVal RetVal, - const SVal &LVal, const SVal &RVal, - OverloadedOperatorKind Op) const; + SVal LVal, SVal RVal, OverloadedOperatorKind Op) const; void processComparison(CheckerContext &C, ProgramStateRef State, - SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal, + SymbolRef Sym1, SymbolRef Sym2, SVal RetVal, OverloadedOperatorKind Op) const; - void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, - bool Postfix) const; - void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, - bool Postfix) const; + void handleIncrement(CheckerContext &C, Optional RetVal, + SVal Iter, bool Postfix) const; + void handleDecrement(CheckerContext &C, Optional RetVal, + SVal Iter, bool Postfix) const; void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, - OverloadedOperatorKind Op, const SVal &RetVal, - const SVal &LHS, const SVal &RHS) const; - void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, - SVal Amount) const; - void handlePrev(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, - SVal Amount) const; - void handleNext(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter, - SVal Amount) const; - void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal, + OverloadedOperatorKind Op, + Optional RetVal, SVal LHS, + SVal RHS) const; + void handleAdvance(CheckerContext &C, const Expr *CE, Optional RetVal, + SVal Iter, SVal Amount) const; + void handlePrev(CheckerContext &C, const Expr *CE, Optional RetVal, + SVal Iter, SVal Amount) const; + void handleNext(CheckerContext &C, const Expr *CE, Optional RetVal, + SVal Iter, SVal Amount) const; + void assignToContainer(CheckerContext &C, const Expr *CE, SVal RetVal, const MemRegion *Cont) const; bool noChangeInAdvance(CheckerContext &C, SVal Iter, const Expr *CE) const; void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, @@ -146,8 +147,6 @@ void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const; - void checkPostStmt(const MaterializeTemporaryExpr *MTE, - CheckerContext &C) const; void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; }; @@ -156,8 +155,6 @@ ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val); ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2, bool Equal); -bool isBoundThroughLazyCompoundVal(const Environment &Env, - const MemRegion *Reg); const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call); } // namespace @@ -191,15 +188,23 @@ auto State = C.getState(); // Already bound to container? - if (getIteratorPosition(State, Call.getReturnValue())) + Optional RetVal = Call.getReturnObject(C.blockCount()); + if (!RetVal.hasValue()) + return; + + if (getIteratorPosition(State, *RetVal)) return; // Copy-like and move constructors if (isa(&Call) && Call.getNumArgs() == 1) { - if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) { - State = setIteratorPosition(State, Call.getReturnValue(), *Pos); + Optional Arg = Call.getArgObject(0, C.blockCount()); + if (!Arg.hasValue()) + return; + + if (const auto *Pos = getIteratorPosition(State, *Arg)) { + State = setIteratorPosition(State, *RetVal, *Pos); if (cast(Func)->isMoveConstructor()) { - State = removeIteratorPosition(State, Call.getArgSVal(0)); + State = removeIteratorPosition(State, *Arg); } C.addTransition(State); return; @@ -213,11 +218,16 @@ // FIXME: Add a more conservative mode for (unsigned i = 0; i < Call.getNumArgs(); ++i) { if (isIteratorType(Call.getArgExpr(i)->getType())) { - if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) { - assignToContainer(C, OrigExpr, Call.getReturnValue(), - Pos->getContainer()); + Optional Arg = Call.getArgObject(i, C.blockCount()); + if (!Arg.hasValue()) return; - } + + const auto *Pos = getIteratorPosition(State, *Arg); + if (!Pos) + return; + + assignToContainer(C, OrigExpr, *RetVal, Pos->getContainer()); + return; } } } @@ -225,6 +235,9 @@ void IteratorModeling::checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const { auto State = C.getState(); + if (Val.getAs()) + return; + const auto *Pos = getIteratorPosition(State, Val); if (Pos) { State = setIteratorPosition(State, Loc, *Pos); @@ -238,17 +251,6 @@ } } -void IteratorModeling::checkPostStmt(const MaterializeTemporaryExpr *MTE, - CheckerContext &C) const { - /* Transfer iterator state to temporary objects */ - auto State = C.getState(); - const auto *Pos = getIteratorPosition(State, C.getSVal(MTE->getSubExpr())); - if (!Pos) - return; - State = setIteratorPosition(State, C.getSVal(MTE), *Pos); - C.addTransition(State); -} - void IteratorModeling::checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const { // Keep symbolic expressions of iterator positions alive @@ -256,8 +258,9 @@ for (const auto &Reg : RegionMap) { const auto Offset = Reg.second.getOffset(); for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) - if (isa(*i)) + if (isa(*i)) { SR.markLive(*i); + } } auto SymbolMap = State->get(); @@ -278,12 +281,7 @@ auto RegionMap = State->get(); for (const auto &Reg : RegionMap) { if (!SR.isLiveRegion(Reg.first)) { - // The region behind the `LazyCompoundVal` is often cleaned up before - // the `LazyCompoundVal` itself. If there are iterator positions keyed - // by these regions their cleanup must be deferred. - if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) { - State = State->remove(Reg.first); - } + State = State->remove(Reg.first); } } @@ -301,61 +299,83 @@ IteratorModeling::handleOverloadedOperator(CheckerContext &C, const CallEvent &Call, OverloadedOperatorKind Op) const { - if (isSimpleComparisonOperator(Op)) { - const auto *OrigExpr = Call.getOriginExpr(); - if (!OrigExpr) - return; + const auto *OrigExpr = Call.getOriginExpr(); + if (!OrigExpr) + return; - if (const auto *InstCall = dyn_cast(&Call)) { - handleComparison(C, OrigExpr, Call.getReturnValue(), - InstCall->getCXXThisVal(), Call.getArgSVal(0), Op); - return; - } + if (isSimpleComparisonOperator(Op)) { + const auto *OrigExpr = Call.getOriginExpr(); + if (!OrigExpr) + return; - handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0), - Call.getArgSVal(1), Op); + Optional Arg0 = Call.getArgObject(0, C.blockCount()); + if (!Arg0.hasValue()) return; - } else if (isRandomIncrOrDecrOperator(Op)) { - const auto *OrigExpr = Call.getOriginExpr(); - if (!OrigExpr) - return; - if (const auto *InstCall = dyn_cast(&Call)) { - if (Call.getNumArgs() >= 1 && - Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { - handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(), - InstCall->getCXXThisVal(), Call.getArgSVal(0)); - return; - } - } else { - if (Call.getNumArgs() >= 2 && - Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { - handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(), - Call.getArgSVal(0), Call.getArgSVal(1)); - return; - } - } - } else if (isIncrementOperator(Op)) { - if (const auto *InstCall = dyn_cast(&Call)) { - handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), - Call.getNumArgs()); + if (const auto *InstCall = dyn_cast(&Call)) { + handleComparison(C, OrigExpr, Call.getReturnValue(), + InstCall->getCXXThisVal(), *Arg0, Op); + return; + } + + Optional Arg1 = Call.getArgObject(1, C.blockCount()); + if (!Arg1.hasValue()) + return; + + handleComparison(C, OrigExpr, Call.getReturnValue(), *Arg0, *Arg1, Op); + return; + } else if (isRandomIncrOrDecrOperator(Op)) { + if (const auto *InstCall = dyn_cast(&Call)) { + if (Call.getNumArgs() >= 1 && + Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { + handleRandomIncrOrDecr(C, OrigExpr, Op, + Call.getReturnObject(C.blockCount()), + InstCall->getCXXThisVal(), + Call.getArgSVal(0)); return; } + } else { + if (Call.getNumArgs() >= 2 && + Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { + Optional Arg0 = Call.getArgObject(0, C.blockCount()); + if (!Arg0.hasValue()) + return; - handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0), - Call.getNumArgs()); - return; - } else if (isDecrementOperator(Op)) { - if (const auto *InstCall = dyn_cast(&Call)) { - handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), - Call.getNumArgs()); + handleRandomIncrOrDecr(C, OrigExpr, Op, + Call.getReturnObject(C.blockCount()), *Arg0, + Call.getArgSVal(1)); return; } + } + } else if (isIncrementOperator(Op)) { + if (const auto *InstCall = dyn_cast(&Call)) { + handleIncrement(C, Call.getReturnObject(C.blockCount()), + InstCall->getCXXThisVal(), Call.getNumArgs()); + return; + } + + Optional Arg0 = Call.getArgObject(0, C.blockCount()); + if (!Arg0.hasValue()) + return; - handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0), - Call.getNumArgs()); + handleIncrement(C, Call.getReturnObject(C.blockCount()), *Arg0, + Call.getNumArgs()); + return; + } else if (isDecrementOperator(Op)) { + if (const auto *InstCall = dyn_cast(&Call)) { + handleDecrement(C, Call.getReturnObject(C.blockCount()), + InstCall->getCXXThisVal(), Call.getNumArgs()); return; } + + Optional Arg0 = Call.getArgObject(0, C.blockCount()); + if (!Arg0.hasValue()) + return; + + handleDecrement(C, Call.getReturnObject(C.blockCount()), *Arg0, + Call.getNumArgs()); + return; + } } void @@ -363,9 +383,13 @@ const CallEvent &Call, const Expr *OrigExpr, const AdvanceFn *Handler) const { - if (!C.wasInlined) { - (this->**Handler)(C, OrigExpr, Call.getReturnValue(), - Call.getArgSVal(0), Call.getArgSVal(1)); + Optional Arg0 = Call.getArgObject(0, C.blockCount()); + if (!Arg0.hasValue()) + return; + + if (!C.wasInlined) { + (this->**Handler)(C, OrigExpr, Call.getReturnObject(C.blockCount()), *Arg0, + Call.getArgSVal(1)); return; } @@ -374,23 +398,22 @@ const auto *IdInfo = cast(Call.getDecl())->getIdentifier(); if (IdInfo) { if (IdInfo->getName() == "advance") { - if (noChangeInAdvance(C, Call.getArgSVal(0), OrigExpr)) { - (this->**Handler)(C, OrigExpr, Call.getReturnValue(), - Call.getArgSVal(0), Call.getArgSVal(1)); + if (noChangeInAdvance(C, *Arg0, OrigExpr)) { + (this->**Handler)(C, OrigExpr, Call.getReturnObject(C.blockCount()), + *Arg0, Call.getArgSVal(1)); } } } } void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE, - SVal RetVal, const SVal &LVal, - const SVal &RVal, - OverloadedOperatorKind Op) const { + SVal RetVal, SVal LVal, SVal RVal, + OverloadedOperatorKind Op) const { // Record the operands and the operator of the comparison for the next // evalAssume, if the result is a symbolic expression. If it is a concrete // value (only one branch is possible), then transfer the state between // the operands according to the operator and the result - auto State = C.getState(); + auto State = C.getState(); const auto *LPos = getIteratorPosition(State, LVal); const auto *RPos = getIteratorPosition(State, RVal); const MemRegion *Cont = nullptr; @@ -437,7 +460,7 @@ void IteratorModeling::processComparison(CheckerContext &C, ProgramStateRef State, SymbolRef Sym1, - SymbolRef Sym2, const SVal &RetVal, + SymbolRef Sym2, SVal RetVal, OverloadedOperatorKind Op) const { if (const auto TruthVal = RetVal.getAs()) { if ((State = relateSymbols(State, Sym1, Sym2, @@ -465,8 +488,8 @@ } } -void IteratorModeling::handleIncrement(CheckerContext &C, const SVal &RetVal, - const SVal &Iter, bool Postfix) const { +void IteratorModeling::handleIncrement(CheckerContext &C, Optional RetVal, + SVal Iter, bool Postfix) const { // Increment the symbolic expressions which represents the position of the // iterator auto State = C.getState(); @@ -487,12 +510,13 @@ "Iterator should have position after successful advancement"); State = setIteratorPosition(State, Iter, *NewPos); - State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos); + if (RetVal.hasValue()) + State = setIteratorPosition(State, *RetVal, Postfix ? *Pos : *NewPos); C.addTransition(State); } -void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal, - const SVal &Iter, bool Postfix) const { +void IteratorModeling::handleDecrement(CheckerContext &C, Optional RetVal, + SVal Iter, bool Postfix) const { // Decrement the symbolic expressions which represents the position of the // iterator auto State = C.getState(); @@ -513,64 +537,69 @@ "Iterator should have position after successful advancement"); State = setIteratorPosition(State, Iter, *NewPos); - State = setIteratorPosition(State, RetVal, Postfix ? *Pos : *NewPos); + if (RetVal.hasValue()) + State = setIteratorPosition(State, *RetVal, Postfix ? *Pos : *NewPos); C.addTransition(State); } void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE, OverloadedOperatorKind Op, - const SVal &RetVal, - const SVal &LHS, - const SVal &RHS) const { + Optional RetVal, SVal Iter, + SVal Amount) const { // Increment or decrement the symbolic expressions which represents the // position of the iterator auto State = C.getState(); - const auto *Pos = getIteratorPosition(State, LHS); + const auto *Pos = getIteratorPosition(State, Iter); if (!Pos) return; - const auto *value = &RHS; - if (auto loc = RHS.getAs()) { - const auto val = State->getRawSVal(*loc); - value = &val; + const auto *AmountVal = &Amount; + if (auto L = Amount.getAs()) { + const auto AmountV = State->getRawSVal(*L); + AmountVal = &AmountV; } - auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal; + Optional TgtVal = + (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iter : RetVal; + if (!TgtVal.hasValue()) + return; auto NewState = - advancePosition(State, LHS, Op, *value); + advancePosition(State, Iter, Op, *AmountVal); if (NewState) { - const auto *NewPos = getIteratorPosition(NewState, LHS); + const auto *NewPos = getIteratorPosition(NewState, Iter); assert(NewPos && "Iterator should have position after successful advancement"); - State = setIteratorPosition(NewState, TgtVal, *NewPos); + State = setIteratorPosition(NewState, *TgtVal, *NewPos); C.addTransition(State); } else { - assignToContainer(C, CE, TgtVal, Pos->getContainer()); + assignToContainer(C, CE, *TgtVal, Pos->getContainer()); } } void IteratorModeling::handleAdvance(CheckerContext &C, const Expr *CE, - SVal RetVal, SVal Iter, + Optional RetVal, SVal Iter, SVal Amount) const { handleRandomIncrOrDecr(C, CE, OO_PlusEqual, RetVal, Iter, Amount); } void IteratorModeling::handlePrev(CheckerContext &C, const Expr *CE, - SVal RetVal, SVal Iter, SVal Amount) const { + Optional RetVal, SVal Iter, + SVal Amount) const { handleRandomIncrOrDecr(C, CE, OO_Minus, RetVal, Iter, Amount); } void IteratorModeling::handleNext(CheckerContext &C, const Expr *CE, - SVal RetVal, SVal Iter, SVal Amount) const { + Optional RetVal, SVal Iter, + SVal Amount) const { handleRandomIncrOrDecr(C, CE, OO_Plus, RetVal, Iter, Amount); } void IteratorModeling::assignToContainer(CheckerContext &C, const Expr *CE, - const SVal &RetVal, + SVal RetVal, const MemRegion *Cont) const { Cont = Cont->getMostDerivedObjectRegion(); @@ -621,6 +650,7 @@ Pos.getContainer()->dumpToStream(Out); Out<<" ; Offset == "; Pos.getOffset()->dumpToStream(Out); + Out<<"\n"; } for (const auto &Reg : RegionMap) { @@ -631,6 +661,7 @@ Pos.getContainer()->dumpToStream(Out); Out<<" ; Offset == "; Pos.getOffset()->dumpToStream(Out); + Out<<"\n"; } } } @@ -647,10 +678,8 @@ return State->remove(Reg); } else if (const auto Sym = Val.getAsSymbol()) { return State->remove(Sym); - } else if (const auto LCVal = Val.getAs()) { - return State->remove(LCVal->getRegion()); } - return nullptr; + return State; } ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1, @@ -685,18 +714,6 @@ return NewState; } -bool isBoundThroughLazyCompoundVal(const Environment &Env, - const MemRegion *Reg) { - for (const auto &Binding : Env) { - if (const auto LCVal = Binding.second.getAs()) { - if (LCVal->getRegion() == Reg) - return true; - } - } - - return false; -} - const ExplodedNode *findCallEnter(const ExplodedNode *Node, const Expr *Call) { while (Node) { ProgramPoint PP = Node->getLocation(); Index: clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp @@ -76,14 +76,18 @@ if (!Func) return; + Optional Arg0 = None; + if (Call.getNumArgs() >= 1) + Arg0 = Call.getArgObject(0, C.blockCount()); + if (Func->isOverloadedOperator()) { if (isIncrementOperator(Func->getOverloadedOperator())) { // Check for out-of-range incrementions if (const auto *InstCall = dyn_cast(&Call)) { verifyIncrement(C, InstCall->getCXXThisVal()); } else { - if (Call.getNumArgs() >= 1) { - verifyIncrement(C, Call.getArgSVal(0)); + if (Arg0.hasValue()) { + verifyIncrement(C, *Arg0); } } } else if (isDecrementOperator(Func->getOverloadedOperator())) { @@ -91,8 +95,8 @@ if (const auto *InstCall = dyn_cast(&Call)) { verifyDecrement(C, InstCall->getCXXThisVal()); } else { - if (Call.getNumArgs() >= 1) { - verifyDecrement(C, Call.getArgSVal(0)); + if (Arg0.hasValue()) { + verifyDecrement(C, *Arg0); } } } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { @@ -105,29 +109,31 @@ Call.getArgSVal(0)); } } else { - if (Call.getNumArgs() >= 2 && + if (Arg0.hasValue() && Call.getNumArgs() >= 2 && Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), - Call.getArgSVal(0), Call.getArgSVal(1)); + *Arg0, Call.getArgSVal(1)); } } } else if (isDereferenceOperator(Func->getOverloadedOperator())) { // Check for dereference of out-of-range iterators if (const auto *InstCall = dyn_cast(&Call)) { verifyDereference(C, InstCall->getCXXThisVal()); - } else { - verifyDereference(C, Call.getArgSVal(0)); + } else if (Arg0.hasValue()) { + verifyDereference(C, *Arg0); } } } else { const AdvanceFn *Verifier = AdvanceFunctions.lookup(Call); if (Verifier) { + Optional Arg0 = Call.getArgObject(0, C.blockCount()); + if (Call.getNumArgs() > 1) { - (this->**Verifier)(C, Call.getArgSVal(0), Call.getArgSVal(1)); + (this->**Verifier)(C, *Arg0, Call.getArgSVal(1)); } else { auto &BVF = C.getSValBuilder().getBasicValueFactory(); (this->**Verifier)( - C, Call.getArgSVal(0), + C, *Arg0, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); } } Index: clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp @@ -32,10 +32,8 @@ std::unique_ptr MismatchedBugType; - void verifyMatch(CheckerContext &C, const SVal &Iter, - const MemRegion *Cont) const; - void verifyMatch(CheckerContext &C, const SVal &Iter1, - const SVal &Iter2) const; + void verifyMatch(CheckerContext &C, SVal Iter, const MemRegion *Cont) const; + void verifyMatch(CheckerContext &C, SVal Iter1, SVal Iter2) const; void reportBug(const StringRef &Message, const SVal &Val1, const SVal &Val2, CheckerContext &C, ExplodedNode *ErrNode) const; @@ -65,51 +63,70 @@ if (!Func) return; + // If the call has no arguments, there is nothing to check here + if (Call.getNumArgs() < 1) + return; + if (Func->isOverloadedOperator() && isComparisonOperator(Func->getOverloadedOperator())) { // Check for comparisons of iterators of different containers - if (const auto *InstCall = dyn_cast(&Call)) { - if (Call.getNumArgs() < 1) - return; + Optional Arg0 = Call.getArgObject(0, C.blockCount()); + if (!Arg0.hasValue()) + return; + if (const auto *InstCall = dyn_cast(&Call)) { if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) || !isIteratorType(Call.getArgExpr(0)->getType())) return; - verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0)); + verifyMatch(C, InstCall->getCXXThisVal(), *Arg0); } else { if (Call.getNumArgs() < 2) return; + Optional Arg1 = Call.getArgObject(1, C.blockCount()); + if (!Arg1.hasValue()) + return; + if (!isIteratorType(Call.getArgExpr(0)->getType()) || !isIteratorType(Call.getArgExpr(1)->getType())) return; - verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1)); + verifyMatch(C, *Arg0, *Arg1); } } else if (const auto *InstCall = dyn_cast(&Call)) { const auto *ContReg = InstCall->getCXXThisVal().getAsRegion(); if (!ContReg) return; + + Optional Arg0 = Call.getArgObject(0, C.blockCount()); + if (!Arg0.hasValue()) + return; + // Check for erase, insert and emplace using iterator of another container if (isEraseCall(Func) || isEraseAfterCall(Func)) { - verifyMatch(C, Call.getArgSVal(0), - InstCall->getCXXThisVal().getAsRegion()); + verifyMatch(C, *Arg0, InstCall->getCXXThisVal().getAsRegion()); if (Call.getNumArgs() == 2) { - verifyMatch(C, Call.getArgSVal(1), - InstCall->getCXXThisVal().getAsRegion()); + Optional Arg1 = Call.getArgObject(1, C.blockCount()); + if (!Arg1.hasValue()) + return; + + verifyMatch(C, *Arg1, InstCall->getCXXThisVal().getAsRegion()); } } else if (isInsertCall(Func)) { - verifyMatch(C, Call.getArgSVal(0), - InstCall->getCXXThisVal().getAsRegion()); + verifyMatch(C, *Arg0, InstCall->getCXXThisVal().getAsRegion()); if (Call.getNumArgs() == 3 && isIteratorType(Call.getArgExpr(1)->getType()) && isIteratorType(Call.getArgExpr(2)->getType())) { - verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2)); + Optional Arg1 = Call.getArgObject(1, C.blockCount()); + Optional Arg2 = Call.getArgObject(2, C.blockCount()); + if (!Arg1.hasValue() || !Arg2.hasValue()) + return; + + verifyMatch(C, *Arg1, *Arg2); } } else if (isEmplaceCall(Func)) { - verifyMatch(C, Call.getArgSVal(0), - InstCall->getCXXThisVal().getAsRegion()); + verifyMatch(C, *Arg0, InstCall->getCXXThisVal().getAsRegion()); } } else if (isa(&Call)) { // Check match of first-last iterator pair in a constructor of a container @@ -128,7 +145,12 @@ !isIteratorType(Call.getArgExpr(1)->getType())) return; - verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1)); + Optional Arg0 = Call.getArgObject(0, C.blockCount()); + Optional Arg1 = Call.getArgObject(1, C.blockCount()); + if (!Arg0.hasValue() || !Arg1.hasValue()) + return; + + verifyMatch(C, *Arg0, *Arg1); } else { // The main purpose of iterators is to abstract away from different // containers and provide a (maybe limited) uniform access to them. @@ -166,7 +188,7 @@ if (!isIteratorType(TAType)) continue; - SVal LHS = UndefinedVal(); + Optional LHS = None; // For every template parameter which is an iterator type in the // instantiation look for all functions' parameters' type by it and @@ -178,17 +200,22 @@ if (!ParamType || ParamType->getReplacedParameter()->getDecl() != TPDecl) continue; - if (LHS.isUndef()) { - LHS = Call.getArgSVal(J); + + const Optional ArgJ = Call.getArgObject(J, C.blockCount()); + if (!ArgJ.hasValue()) + continue; + + if (!LHS.hasValue()) { + LHS = ArgJ; } else { - verifyMatch(C, LHS, Call.getArgSVal(J)); + verifyMatch(C, *LHS, *ArgJ); } } } } } -void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter, +void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, SVal Iter, const MemRegion *Cont) const { // Verify match between a container and the container of an iterator Cont = Cont->getMostDerivedObjectRegion(); @@ -219,14 +246,13 @@ if (!N) { return; } - reportBug("Container accessed using foreign iterator argument.", - Iter, Cont, C, N); + reportBug("Container accessed using foreign iterator argument.", Iter, Cont, + C, N); } } -void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, - const SVal &Iter1, - const SVal &Iter2) const { +void MismatchedIteratorChecker::verifyMatch(CheckerContext &C, SVal Iter1, + SVal Iter2) const { // Verify match between the containers of two iterators auto State = C.getState(); const auto *Pos1 = getIteratorPosition(State, Iter1); Index: clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -193,6 +193,11 @@ stackReg = dyn_cast(VR->getMemorySpace())) if (stackReg->getStackFrame() == SFC) return VR->getValueType(); + if (const ParamRegion *PR = R->getAs()) + if (const StackArgumentsSpaceRegion * + stackReg = dyn_cast(PR->getMemorySpace())) + if (stackReg->getStackFrame() == SFC) + return PR->getValueType(); } return QualType(); Index: clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -507,13 +507,20 @@ return false; const auto *VR = dyn_cast(R); + const auto *PR = dyn_cast(R); - if (!R->hasStackStorage() || !VR) + if (!R->hasStackStorage() || (!VR && !PR)) return true; - const VarDecl *VD = VR->getDecl(); - if (!VD->hasAttr()) - return false; // CleanupAttr attaches destructors, which cause escaping. + if (VR) { + const VarDecl *VD = VR->getDecl(); + if (!VD->hasAttr()) + return false; // CleanupAttr attaches destructors, which cause escaping. + } else { + const ParmVarDecl *PVD = PR->getDecl(); + if (!PVD->hasAttr()) + return false; // CleanupAttr attaches destructors, which cause escaping. + } return true; } Index: clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -432,13 +432,22 @@ getRefBinding(N->getFirstPred()->getState(), Sym)) return nullptr; - const auto *VR = cast(cast(Sym)->getRegion()); - const auto *PVD = cast(VR->getDecl()); - PathDiagnosticLocation L = PathDiagnosticLocation(PVD, SM); + const VarDecl *VD; + if (const auto *VR = + dyn_cast(cast(Sym)->getRegion())) { + VD = cast(VR->getDecl()); + } else if (const auto *PR = + dyn_cast(cast(Sym)->getRegion())) { + VD = cast(PR->getDecl()); + } + + assert(VD); + + PathDiagnosticLocation L = PathDiagnosticLocation(VD, SM); std::string s; llvm::raw_string_ostream os(s); - os << "Parameter '" << PVD->getNameAsString() << "' starts at +"; + os << "Parameter '" << VD->getNameAsString() << "' starts at +"; if (CurrT->getCount() == 1) { os << "1, as it is marked as consuming"; } else { @@ -606,6 +615,8 @@ static Optional describeRegion(const MemRegion *MR) { if (const auto *VR = dyn_cast_or_null(MR)) return std::string(VR->getDecl()->getName()); + if (const auto *PR = dyn_cast_or_null(MR)) + return std::string(PR->getDecl()->getName()); // Once we support more storage locations for bindings, // this would need to be improved. return None; Index: clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp +++ clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp @@ -24,12 +24,12 @@ namespace { class STLAlgorithmModeling : public Checker { - bool evalFind(CheckerContext &C, const CallExpr *CE) const; + void evalFind(CheckerContext &C, const CallExpr *CE, SVal Begin, + SVal End) const; - void Find(CheckerContext &C, const CallExpr *CE, unsigned paramNum) const; - - using FnCheck = bool (STLAlgorithmModeling::*)(CheckerContext &, - const CallExpr *) const; + using FnCheck = void (STLAlgorithmModeling::*)(CheckerContext &, + const CallExpr *, SVal Begin, + SVal End) const; const CallDescriptionMap Callbacks = { {{{"std", "find"}, 3}, &STLAlgorithmModeling::evalFind}, @@ -67,51 +67,73 @@ bool STLAlgorithmModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { - const auto *CE = dyn_cast_or_null(Call.getOriginExpr()); - if (!CE) - return false; - - const FnCheck *Handler = Callbacks.lookup(Call); - if (!Handler) - return false; - - return (this->**Handler)(C, CE); -} - -bool STLAlgorithmModeling::evalFind(CheckerContext &C, - const CallExpr *CE) const { // std::find()-like functions either take their primary range in the first // two parameters, or if the first parameter is "execution policy" then in // the second and third. This means that the second parameter must always be // an iterator. - if (!isIteratorType(CE->getArg(1)->getType())) + if (Call.getNumArgs() < 2 || !isIteratorType(Call.getArgExpr(1)->getType())) return false; + unsigned ArgNum = 999; + // If no "execution policy" parameter is used then the first argument is the // beginning of the range. - if (isIteratorType(CE->getArg(0)->getType())) { - Find(C, CE, 0); - return true; + if (isIteratorType(Call.getArgExpr(0)->getType())) { + ArgNum = 0; } // If "execution policy" parameter is used then the second argument is the // beginning of the range. - if (isIteratorType(CE->getArg(2)->getType())) { - Find(C, CE, 1); - return true; + if (ArgNum > 0 && + Call.getNumArgs() > 2 && isIteratorType(Call.getArgExpr(2)->getType())) { + ArgNum = 1; + } + + if (ArgNum == 999) + return false; + + Optional ArgB = Call.getArgSVal(ArgNum); + llvm::errs()<<"Original ArgB: "<<*ArgB<<"\n"; + if (ArgB->getAs()) { + const MemRegion *ArgLoc = Call.getParameterLocation(ArgNum, C.blockCount()); + if (!ArgLoc) + return false; + + ArgB = loc::MemRegionVal(ArgLoc); + } + llvm::errs()<<"Updated ArgB: "<<*ArgB<<"\n"; + + Optional ArgE = Call.getArgSVal(ArgNum + 1); + llvm::errs()<<"Original ArgE: "<<*ArgE<<"\n"; + if (ArgE->getAs()) { + const MemRegion *ArgLoc = Call.getParameterLocation(ArgNum + 1, + C.blockCount()); + if (!ArgLoc) + return false; + + ArgE = loc::MemRegionVal(ArgLoc); } + llvm::errs()<<"Updated ArgE: "<<*ArgE<<"\n"; - return false; + const auto *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) + return false; + + const FnCheck *Handler = Callbacks.lookup(Call); + if (!Handler) + return false; + + (this->**Handler)(C, CE, *ArgB, *ArgE); + return true; } -void STLAlgorithmModeling::Find(CheckerContext &C, const CallExpr *CE, - unsigned paramNum) const { +void STLAlgorithmModeling::evalFind(CheckerContext &C, const CallExpr *CE, + SVal Begin, SVal End) const { auto State = C.getState(); auto &SVB = C.getSValBuilder(); const auto *LCtx = C.getLocationContext(); SVal RetVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); - SVal Param = State->getSVal(CE->getArg(paramNum), LCtx); auto StateFound = State->BindExpr(CE, LCtx, RetVal); @@ -119,7 +141,7 @@ // assume that in case of successful search the position of the found element // is not ahead of it. // FIXME: Reverse iterators - const auto *Pos = getIteratorPosition(State, Param); + const auto *Pos = getIteratorPosition(State, Begin); if (Pos) { StateFound = createIteratorPosition(StateFound, RetVal, Pos->getContainer(), CE, LCtx, C.blockCount()); @@ -135,13 +157,11 @@ StateFound = StateFound->assume(GreaterOrEqual.castAs(), true); } - Param = State->getSVal(CE->getArg(paramNum + 1), LCtx); - // If we have an iterator position for the range-end argument then we can // assume that in case of successful search the position of the found element // is ahead of it. // FIXME: Reverse iterators - Pos = getIteratorPosition(State, Param); + Pos = getIteratorPosition(State, End); if (Pos) { StateFound = createIteratorPosition(StateFound, RetVal, Pos->getContainer(), CE, LCtx, C.blockCount()); @@ -160,7 +180,7 @@ C.addTransition(StateFound); if (AggressiveStdFindModeling) { - auto StateNotFound = State->BindExpr(CE, LCtx, Param); + auto StateNotFound = State->BindExpr(CE, LCtx, End); C.addTransition(StateNotFound); } } Index: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -743,12 +743,19 @@ os << Sep; // Can only reasonably pretty-print DeclRegions. - if (!isa(R)) - return false; + if (const auto *DR = dyn_cast(R)) { + Sep = DR->getValueType()->isAnyPointerType() ? "->" : "."; + DR->getDecl()->getDeclName().print(os, PP); + } else if (const auto *PR = dyn_cast(R)) { + const ParmVarDecl *D = PR->getDecl(); + if (!D) + return false; - const auto *DR = cast(R); - Sep = DR->getValueType()->isAnyPointerType() ? "->" : "."; - DR->getDecl()->getDeclName().print(os, PP); + Sep = PR->getValueType()->isAnyPointerType() ? "->" : "."; + D->getDeclName().print(os, PP); + } else { + return false; + } } if (Sep.empty()) @@ -1240,6 +1247,32 @@ return FrameSpace->getStackFrame() == LCtx->getStackFrame(); } +/// Returns true if \p N represents the DeclStmt declaring and initializing +/// \p PR. +static bool isInitializationOfParam(const ExplodedNode *N, + const ParamRegion *PR) { + Optional P = N->getLocationAs(); + if (!P) + return false; + + const DeclStmt *DS = P->getStmtAs(); + if (!DS) + return false; + + const ParmVarDecl *PVD = PR->getDecl(); + if (!PVD) + return false; + + if (DS->getSingleDecl() != PVD) + return false; + + const MemSpaceRegion *ParamSpace = PR->getMemorySpace(); + const auto *FrameSpace = dyn_cast(ParamSpace); + + const LocationContext *LCtx = N->getLocationContext(); + return FrameSpace->getStackFrame() == LCtx->getStackFrame(); +} + /// Show diagnostics for initializing or declaring a region \p R with a bad value. static void showBRDiagnostics(const char *action, llvm::raw_svector_ostream &os, const MemRegion *R, SVal V, const DeclStmt *DS) { @@ -1284,14 +1317,11 @@ /// Display diagnostics for passing bad region as a parameter. static void showBRParamDiagnostics(llvm::raw_svector_ostream& os, - const VarRegion *VR, - SVal V) { - const auto *Param = cast(VR->getDecl()); - + const ParamRegion *PR, SVal V) { os << "Passing "; if (V.getAs()) { - if (Param->getType()->isObjCObjectPointerType()) + if (PR->getValueType()->isObjCObjectPointerType()) os << "nil object reference"; else os << "null pointer value"; @@ -1304,11 +1334,11 @@ } // Printed parameter indexes are 1-based, not 0-based. - unsigned Idx = Param->getFunctionScopeIndex() + 1; + unsigned Idx = PR->getIndex() + 1; os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter"; - if (VR->canPrintPretty()) { + if (PR->canPrintPretty()) { os << " "; - VR->printPretty(os); + PR->printPretty(os); } } @@ -1377,6 +1407,14 @@ } } + // First see if we reached the declaration of the region. + if (const auto *PR = dyn_cast(R)) { + if (isInitializationOfParam(Pred, PR)) { + StoreSite = Pred; + InitE = PR->getDecl()->getInit(); + } + } + // If this is a post initializer expression, initializing the region, we // should track the initializer expression. if (Optional PIP = Pred->getLocationAs()) { @@ -1416,7 +1454,14 @@ // 'this' should never be NULL, but this visitor isn't just for NULL and // UndefinedVal.) if (Optional CE = Succ->getLocationAs()) { - if (const auto *VR = dyn_cast(R)) { + if (const auto *PR = dyn_cast(R)) { + ProgramStateManager &StateMgr = BRC.getStateManager(); + CallEventManager &CallMgr = StateMgr.getCallEventManager(); + + CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(), + Succ->getState()); + InitE = Call->getArgExpr(PR->getIndex()); + } else if (const auto *VR = dyn_cast(R)) { if (const auto *Param = dyn_cast(VR->getDecl())) { ProgramStateManager &StateMgr = BRC.getStateManager(); @@ -1482,7 +1527,7 @@ SVal V = StoreSite->getSVal(S); if (const auto *BDR = dyn_cast_or_null(V.getAsRegion())) { - if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) { + if (const TypedValueRegion *OriginalR = BDR->getOriginalRegion(VR)) { if (auto KV = State->getSVal(OriginalR).getAs()) BR.addVisitor(std::make_unique( *KV, OriginalR, EnableNullFPSuppression, TKind, OriginSFC)); @@ -1494,8 +1539,9 @@ showBRDiagnostics(action, os, R, V, DS); } else if (StoreSite->getLocation().getAs()) { - if (const auto *VR = dyn_cast(R)) - showBRParamDiagnostics(os, VR, V); + if (const auto *PR = dyn_cast(R)) { + showBRParamDiagnostics(os, PR, V); + } } if (os.str().empty()) @@ -1834,7 +1880,16 @@ return nullptr; ProgramStateManager &StateMgr = N->getState()->getStateManager(); MemRegionManager &MRMgr = StateMgr.getRegionManager(); - return MRMgr.getVarRegion(VD, N->getLocationContext()); + const LocationContext *LCtx = N->getLocationContext(); + if (const auto *PVD = dyn_cast(VD)) { + const Expr* Call; + unsigned Index; + std::tie(Call, Index) = ParamRegion::getExprAndIndexForParam(PVD, LCtx); + + if (Call) + return MRMgr.getParamRegion(Call, Index, LCtx); + } + return MRMgr.getVarRegion(VD, LCtx); } } Index: clang/lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -178,15 +178,15 @@ // situation because there's a loss of precision anyway because we cannot // inline the call. RuntimeDefinition RD = getRuntimeDefinition(); - if (!RD.getDecl()) - return nullptr; + // if (!RD.getDecl()) + // return nullptr; AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext()->getManager()->getContext(D); // TODO: For now we skip virtual functions, because this also rises // the problem of which decl to use, but now it's across different classes. - if (RD.mayHaveOtherDefinitions() || RD.getDecl() != ADC->getDecl()) + if (RD.getDecl() && (RD.mayHaveOtherDefinitions() || RD.getDecl() != ADC->getDecl())) return nullptr; return ADC; @@ -222,39 +222,113 @@ return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, BlockCount, Idx); } -const VarRegion *CallEvent::getParameterLocation(unsigned Index, - unsigned BlockCount) const { +const TypedValueRegion* +CallEvent::getParameterLocation(unsigned Index, + unsigned BlockCount) const { const StackFrameContext *SFC = getCalleeStackFrame(BlockCount); // We cannot construct a VarRegion without a stack frame. if (!SFC) return nullptr; - // Retrieve parameters of the definition, which are different from // CallEvent's parameters() because getDecl() isn't necessarily // the definition. SFC contains the definition that would be used // during analysis. + const Decl *D = SFC->getDecl(); // TODO: Refactor into a virtual method of CallEvent, like parameters(). const ParmVarDecl *PVD = nullptr; - if (const auto *FD = dyn_cast(D)) - PVD = FD->parameters()[Index]; - else if (const auto *BD = dyn_cast(D)) + if (const auto *FD = dyn_cast(D)) { + //if (FD->isDefined()) + PVD = FD->parameters()[Index]; + } else if (const auto *BD = dyn_cast(D)) { PVD = BD->parameters()[Index]; - else if (const auto *MD = dyn_cast(D)) - PVD = MD->parameters()[Index]; - else if (const auto *CD = dyn_cast(D)) - PVD = CD->parameters()[Index]; - assert(PVD && "Unexpected Decl kind!"); + } else if (const auto *MD = dyn_cast(D)) { + //if (MD->isDefined()) + PVD = MD->parameters()[Index]; + } else if (const auto *CD = dyn_cast(D)) { + //if (CD->isDefined()) + PVD = CD->parameters()[Index]; + } else { + llvm_unreachable("Unexpected Decl kind!"); + } - const VarRegion *VR = + /*if (PVD) { + const VarRegion *VR = State->getStateManager().getRegionManager().getVarRegion(PVD, SFC); - // This sanity check would fail if our parameter declaration doesn't - // correspond to the stack frame's function declaration. - assert(VR->getStackFrame() == SFC); + // This sanity check would fail if our parameter declaration doesn't + // correspond to the stack frame's function declaration. + assert(VR->getStackFrame() == SFC); + + return VR; + } + return nullptr;*/ + if (isa(this)) + ++Index; + const ParamRegion *PR = + State->getStateManager().getRegionManager().getParamRegion(getOriginExpr(), + Index, SFC); + return PR; +} + +const ConstructionContext +*CallEvent::getConstructionContext(unsigned BlockCount) const { + const CFGBlock *Block; + unsigned Index; + + const StackFrameContext *StackFrame = getCalleeStackFrame(BlockCount); + if (!StackFrame) + return nullptr; + + Block = StackFrame->getCallSiteBlock(); + if (!Block) + return nullptr; + + Index = StackFrame->getIndex(); + + if(const auto Ctor = (*Block)[Index].getAs()) { + return Ctor->getConstructionContext(); + } + + if (const auto RecCall = (*Block)[Index].getAs()) { + return RecCall->getConstructionContext(); + } + + return nullptr; +} + +Optional +CallEvent::getReturnValueUnderConstruction(unsigned BlockCount) const { + const auto *CC = getConstructionContext(BlockCount); + if (!CC) + return None; + + Optional RetVal = + ExprEngine::retrieveFromConstructionContext(getState(), + getLocationContext(), CC); + return RetVal; +} + +Optional CallEvent::getArgObject(unsigned Index, + unsigned BlockCount) const { + SVal Arg = getArgSVal(Index); + if (Arg.getAsSymbol() || Arg.getAsRegion()) + return Arg; - return VR; + const MemRegion *ArgLoc = getParameterLocation(Index, BlockCount); + if (ArgLoc) + return loc::MemRegionVal(ArgLoc); + + return None; +} + +Optional CallEvent::getReturnObject(unsigned BlockCount) const { + SVal RetVal = getReturnValue(); + if (RetVal.getAsSymbol() || RetVal.getAsRegion()) + return RetVal; + + return getReturnValueUnderConstruction(BlockCount); } /// Returns true if a type is a pointer-to-const or reference-to-const @@ -325,8 +399,9 @@ if (getKind() != CE_CXXAllocator) if (isArgumentConstructedDirectly(Idx)) if (auto AdjIdx = getAdjustedParameterIndex(Idx)) - if (const VarRegion *VR = getParameterLocation(*AdjIdx, BlockCount)) - ValuesToInvalidate.push_back(loc::MemRegionVal(VR)); + if (const TypedValueRegion *TVR = + getParameterLocation(*AdjIdx, BlockCount)) + ValuesToInvalidate.push_back(loc::MemRegionVal(TVR)); } // Invalidate designated regions using the batch invalidation API. @@ -528,8 +603,28 @@ // which makes getArgSVal() fail and return UnknownVal. SVal ArgVal = Call.getArgSVal(Idx); if (!ArgVal.isUnknown()) { - Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx)); - Bindings.push_back(std::make_pair(ParamLoc, ArgVal)); + unsigned Index = Idx; + if (isa(Call)) + ++Index; + + // For C++ inherited constructor calls we take the construct + // expression since it is the real owner of the argument. + if (const auto *ICC = dyn_cast(&Call)) { + Loc ParamLoc = + SVB.makeLoc(MRMgr.getParamRegion(ICC->getInheritingConstructor(), + Index, CalleeCtx)); + Bindings.push_back(std::make_pair(ParamLoc, ArgVal)); + } else { + Loc ParamLoc = + SVB.makeLoc(MRMgr.getParamRegion(Call.getOriginExpr(), Index, + CalleeCtx)); + Bindings.push_back(std::make_pair(ParamLoc, ArgVal)); + } + /*Loc ParamLoc = + SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, + CalleeCtx)); + llvm::errs()<<"Binding "<get().isEmpty()) { ++Space; Out << '[' << NL; Index: clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -218,7 +218,7 @@ auto CE = BD->capture_end(); for (; I != E; ++I) { const VarRegion *capturedR = I.getCapturedRegion(); - const VarRegion *originalR = I.getOriginalRegion(); + const TypedValueRegion *originalR = I.getOriginalRegion(); // If the capture had a copy expression, use the result of evaluating // that expression, otherwise use the original value. Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -109,6 +109,97 @@ return LValue; } +Optional ExprEngine::retrieveFromConstructionContext( + ProgramStateRef State, const LocationContext *LCtx, + const ConstructionContext *CC) { + if (CC) { + switch (CC->getKind()) { + case ConstructionContext::CXX17ElidedCopyVariableKind: + case ConstructionContext::SimpleVariableKind: { + const auto *DSCC = cast(CC); + const auto *DS = DSCC->getDeclStmt(); + return getObjectUnderConstruction(State, DS, LCtx); + } + case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: + case ConstructionContext::SimpleConstructorInitializerKind: { + const auto *ICC = cast(CC); + const auto *Init = ICC->getCXXCtorInitializer(); + return getObjectUnderConstruction(State, Init, LCtx); + } + case ConstructionContext::SimpleReturnedValueKind: + case ConstructionContext::CXX17ElidedCopyReturnedValueKind: { + const StackFrameContext *SFC = LCtx->getStackFrame(); + if (const LocationContext *CallerLCtx = SFC->getParent()) { + auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()] + .getAs(); + if (!RTC) { + // We were unable to find the correct construction context for the + // call in the parent stack frame. This is equivalent to not being + // able to find construction context at all. + break; + } + if (isa(CallerLCtx)) { + // Unwrap block invocation contexts. They're mostly part of + // the current stack frame. + CallerLCtx = CallerLCtx->getParent(); + assert(!isa(CallerLCtx)); + } + return retrieveFromConstructionContext( + State, CallerLCtx, RTC->getConstructionContext()); + } + break; + } + case ConstructionContext::ElidedTemporaryObjectKind: { + assert(State->getAnalysisManager().getAnalyzerOptions() + .ShouldElideConstructors); + const auto *TCC = cast(CC); + Optional RetVal = retrieveFromConstructionContext( + State, LCtx, TCC->getConstructionContextAfterElision()); + if (RetVal.hasValue()) + return RetVal; + + LLVM_FALLTHROUGH; + } + case ConstructionContext::SimpleTemporaryObjectKind: { + const auto *TCC = cast(CC); + const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr(); + Optional RetVal; + if (BTE) { + RetVal = getObjectUnderConstruction(State, BTE, LCtx); + if (RetVal.hasValue()) + return RetVal; + } + + const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr(); + if (MTE) + RetVal = getObjectUnderConstruction(State, MTE, LCtx); + + return RetVal; + } + case ConstructionContext::ArgumentKind: { + const auto *ACC = cast(CC); + const Expr *E = ACC->getCallLikeExpr(); + unsigned Idx = ACC->getIndex(); + if (const auto *CE = dyn_cast(E)) { + return getObjectUnderConstruction(State, {CE, Idx}, LCtx); + } else if (const auto *CCE = dyn_cast(E)) { + return getObjectUnderConstruction(State, {CCE, Idx}, LCtx); + } else if (const auto *ME = dyn_cast(E)) { + return getObjectUnderConstruction(State, {ME, Idx}, LCtx); + } else if (const auto *BTE = ACC->getCXXBindTemporaryExpr()) { + return getObjectUnderConstruction(State, BTE, LCtx); + } + + LLVM_FALLTHROUGH; + } + default: + return None; + } + } + + return None; +} + std::pair ExprEngine::handleConstructionContext( const Expr *E, ProgramStateRef State, const LocationContext *LCtx, const ConstructionContext *CC, EvalCallOptions &CallOpts) { @@ -342,20 +433,19 @@ // Operator arguments do not correspond to operator parameters // because this-argument is implemented as a normal argument in // operator call expressions but not in operator declarations. - const VarRegion *VR = Caller->getParameterLocation( + const TypedValueRegion *TVR = Caller->getParameterLocation( *Caller->getAdjustedParameterIndex(Idx), currBldrCtx->blockCount()); - if (!VR) + if (!TVR) return None; - return loc::MemRegionVal(VR); + return loc::MemRegionVal(TVR); }; if (const auto *CE = dyn_cast(E)) { CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx); if (auto OptV = getArgLoc(Caller)) V = *OptV; - else - break; + else break; State = addObjectUnderConstruction(State, {CE, Idx}, LCtx, V); } else if (const auto *CCE = dyn_cast(E)) { // Don't bother figuring out the target region for the future Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -537,9 +537,11 @@ getObjectUnderConstruction(State, {E, I}, LC)) { SVal VV = *V; (void)VV; - assert(cast(VV.castAs().getRegion()) - ->getStackFrame()->getParent() - ->getStackFrame() == LC->getStackFrame()); + if (const auto *VR = + dyn_cast(VV.castAs().getRegion())) { + assert(VR->getStackFrame()->getParent() + ->getStackFrame() == LC->getStackFrame()); + } State = finishObjectConstruction(State, {E, I}, LC); } } Index: clang/lib/StaticAnalyzer/Core/MemRegion.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -159,6 +159,11 @@ return SSR ? SSR->getStackFrame() : nullptr; } +const StackFrameContext *ParamRegion::getStackFrame() const { + const auto *SSR = dyn_cast(getMemorySpace()); + return SSR ? SSR->getStackFrame() : nullptr; +} + ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg) : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {} @@ -178,6 +183,123 @@ return QualType(getDecl()->getTypeForDecl(), 0); } +QualType ParamRegion::getValueType() const { + if (const ParmVarDecl *PVD = getDecl()) + return PVD->getType(); + + const Expr *Arg = getArgExpr(); + assert(Arg && "Both ParamRegion::getDecl() and ParamRegion::getArgExpr()" + " must not return null for the same region."); + + switch (Arg->getValueKind()) { + case VK_RValue: + return Arg->getType(); + case VK_LValue: + return getContext().getLValueReferenceType(Arg->getType()); + case VK_XValue: + return getContext().getRValueReferenceType(Arg->getType()); + default: + llvm_unreachable("Invalid value kind."); + } +} + +const ParmVarDecl *ParamRegion::getDecl() const { + const Decl *D = getStackFrame()->getDecl(); + /*llvm::errs()<<"ParamRegion::getDecl()\n\nDecl:\n"; + D->dump(); + llvm::errs()<<"\n\nOriginExpr:\n"; + OriginExpr->dump(); + llvm::errs()<<"\n\nIndex: "<(D)) { + if (isa(OriginExpr) && isa(FD)) { + assert(Index <= FD->param_size()); + /*llvm::errs()<<"Returning param "<<(Index - 1)<<":\n"; + FD->parameters()[Index - 1]->dump(); + llvm::errs()<<"\n";*/ + return FD->parameters()[Index - 1]; + } + + if (Index >= FD->param_size()) + return nullptr; + + /*llvm::errs()<<"Returning param "<parameters()[Index]->dump(); + llvm::errs()<<"\n";*/ + return FD->parameters()[Index]; + } else if (const auto *BD = dyn_cast(D)) { + assert (Index < BD->param_size()); + return BD->parameters()[Index]; + } else if (const auto *MD = dyn_cast(D)) { + assert (Index < MD->param_size()); + return MD->parameters()[Index]; + } else if (const auto *CD = dyn_cast(D)) { + assert (Index < CD->param_size()); + return CD->parameters()[Index]; + } else { + llvm_unreachable("Unexpected Decl kind!"); + } + /* if (const auto *CE = dyn_cast(OriginExpr)) { + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return nullptr; + + if (isa(CE) && isa(FD)) { + assert(Index <= FD->param_size()); + return FD->parameters()[Index - 1]; + } + + if (Index >= FD->param_size()) + return nullptr; + + return FD->parameters()[Index]; + } else if (const auto *CCE = dyn_cast(OriginExpr)) { + const auto *CCD = CCE->getConstructor(); + assert(CCD && "Constructor not found for CXXConstructExpr"); + assert(Index < CCD->param_size()); + return CCD->parameters()[Index]; + } else if (const auto *OCME = dyn_cast(OriginExpr)) { + const auto *OCMD = OCME->getMethodDecl(); + assert(OCMD && "Method not found for CXXObjCMessageExpr"); + assert(Index < OCMD->param_size()); + return OCMD->parameters()[Index]; + } else if (const auto *CNE = dyn_cast(OriginExpr)) { + const auto *FD = CNE->getOperatorNew(); + assert(FD && "New operator not found for CXXNewExpr"); + assert(Index < FD->param_size()); + return FD->parameters()[Index]; + } else { + llvm::errs()<<"Unknown Expression:\n"; + OriginExpr->dump(); + llvm::errs()<<"\n"; + // FIXME: Any other kind of `Expr`? + llvm_unreachable("Unknown Expression type for ParamRegion."); + }*/ +} + +const Expr *ParamRegion::getArgExpr() const { + /*llvm::errs()<<"ParamRegion::getArgExpr()\n\nOriginExpr:\n"; + OriginExpr->dump(); + llvm::errs()<<"\n\nIndex: "<(OriginExpr)) { + assert(Index < CE->getNumArgs()); + return CE->getArg(Index); + } else if (const auto *CCE = dyn_cast(OriginExpr)) { + assert(Index < CCE->getNumArgs()); + return CCE->getArg(Index); + } else if (const auto *OCME = dyn_cast(OriginExpr)) { + assert(Index < OCME->getNumArgs()); + return OCME->getArg(Index); + } else if (const auto *CNE = dyn_cast(OriginExpr)) { + if (Index > 0) + return CNE->getPlacementArg(Index - 1); + return nullptr; + } else { + // FIXME: Any other kind of `Expr`? + llvm_unreachable("Unknown Expression type for ParamRegion."); + } +} + //===----------------------------------------------------------------------===// // FoldingSet profiling. //===----------------------------------------------------------------------===// @@ -368,6 +490,18 @@ ProfileRegion(ID, getDecl(), superRegion); } +void ParamRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, const Expr *OE, + unsigned Idx, const MemRegion *SReg) { + ID.AddInteger(static_cast(ParamRegionKind)); + ID.AddPointer(OE); + ID.AddInteger(Idx); + ID.AddPointer(SReg); +} + +void ParamRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ProfileRegion(ID, getOriginExpr(), getIndex(), superRegion); +} + //===----------------------------------------------------------------------===// // Region anchors. //===----------------------------------------------------------------------===// @@ -531,6 +665,19 @@ os << "StackLocalsSpaceRegion"; } +void ParamRegion::dumpToStream(raw_ostream &os) const { + if (const ParmVarDecl *PVD = getDecl()) { + if (const IdentifierInfo *ID = PVD->getIdentifier()) { + os << ID->getName(); + } else { + os << "ParamRegion{P" << PVD->getID() << '}'; + } + } else { + os << "ParamRegion{P("<getID(getContext()) << ", " + << Index << ")}\n"; + } +} + bool MemRegion::canPrintPretty() const { return canPrintPrettyAsExpr(); } @@ -606,6 +753,14 @@ superRegion->printPrettyAsExpr(os); } +bool ParamRegion::canPrintPrettyAsExpr() const { + return getDecl(); +} + +void ParamRegion::printPrettyAsExpr(raw_ostream &os) const { + os << getDecl()->getName(); +} + std::string MemRegion::getDescriptiveName(bool UseQuotes) const { std::string VariableName; std::string ArrayIndices; @@ -695,7 +850,8 @@ case MemRegion::ObjCIvarRegionKind: case MemRegion::VarRegionKind: case MemRegion::ElementRegionKind: - case MemRegion::ObjCStringRegionKind: { + case MemRegion::ObjCStringRegionKind: + case MemRegion::ParamRegionKind: { QualType Ty = cast(SR)->getDesugaredValueType(Ctx); if (isa(Ty)) return nonloc::SymbolVal(SymMgr.getExtentSymbol(SR)); @@ -847,9 +1003,14 @@ for (BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(), E = BR->referenced_vars_end(); I != E; ++I) { - const VarRegion *VR = I.getOriginalRegion(); - if (VR->getDecl() == VD) - return cast(I.getCapturedRegion()); + const TypedValueRegion *OrigR = I.getOriginalRegion(); + if (const auto *VR = dyn_cast(OrigR)) { + if (VR->getDecl() == VD) + return cast(I.getCapturedRegion()); + } else if (const auto *PR = dyn_cast(OrigR)) { + if (PR->getDecl() && PR->getDecl() == VD) + return cast(I.getCapturedRegion()); + } } } @@ -1153,6 +1314,14 @@ return dyn_cast(R); } +const ParamRegion* +MemRegionManager::getParamRegion(const Expr *OE, unsigned Index, + const LocationContext *LC) { + const StackFrameContext *STC = LC->getStackFrame(); + assert(STC); + return getSubRegion(OE, Index, getStackArgumentsRegion(STC)); +} + bool MemRegion::hasStackStorage() const { return isa(getMemorySpace()); } @@ -1343,6 +1512,7 @@ case MemRegion::ObjCStringRegionKind: case MemRegion::VarRegionKind: case MemRegion::CXXTempObjectRegionKind: + case MemRegion::ParamRegionKind: // Usual base regions. goto Finish; @@ -1490,15 +1660,25 @@ // BlockDataRegion //===----------------------------------------------------------------------===// -std::pair +std::pair BlockDataRegion::getCaptureRegions(const VarDecl *VD) { MemRegionManager &MemMgr = getMemRegionManager(); const VarRegion *VR = nullptr; - const VarRegion *OriginalVR = nullptr; + const TypedValueRegion *OriginalVR = nullptr; if (!VD->hasAttr() && VD->hasLocalStorage()) { VR = MemMgr.getVarRegion(VD, this); - OriginalVR = MemMgr.getVarRegion(VD, LC); + if (const auto *PVD = dyn_cast(VD)) { + const Expr* Call; + unsigned Index; + std::tie(Call, Index) = ParamRegion::getExprAndIndexForParam(PVD, LC); + + if (Call) + OriginalVR = MemMgr.getParamRegion(Call, Index, LC); + else + OriginalVR = MemMgr.getVarRegion(VD, LC); + } else + OriginalVR = MemMgr.getVarRegion(VD, LC); } else { if (LC) { @@ -1540,7 +1720,7 @@ for (const auto *VD : ReferencedBlockVars) { const VarRegion *VR = nullptr; - const VarRegion *OriginalVR = nullptr; + const TypedValueRegion *OriginalVR = nullptr; std::tie(VR, OriginalVR) = getCaptureRegions(VD); assert(VR); assert(OriginalVR); @@ -1584,7 +1764,8 @@ VecOriginal->end()); } -const VarRegion *BlockDataRegion::getOriginalRegion(const VarRegion *R) const { +const TypedValueRegion +*BlockDataRegion::getOriginalRegion(const VarRegion *R) const { for (referenced_vars_iterator I = referenced_vars_begin(), E = referenced_vars_end(); I != E; ++I) { @@ -1635,3 +1816,36 @@ return false; } + +//===----------------------------------------------------------------------===// +// Utility functions +//===----------------------------------------------------------------------===// + +std::pair +ParamRegion::getExprAndIndexForParam(const ParmVarDecl *PVD, + const LocationContext *LC) { + unsigned Index = PVD->getFunctionScopeIndex(); + const StackFrameContext *SFC = LC->getStackFrame(); + const Stmt *CallSite = SFC->getCallSite(); + if (!CallSite) + return std::make_pair(nullptr, UINT_MAX); + + const Decl *D = SFC->getDecl(); + if (const auto *FD = dyn_cast(D)) { + if (Index >= FD->param_size() || FD->parameters()[Index] != PVD) + return std::make_pair(nullptr, UINT_MAX); + } else if (const auto *BD = dyn_cast(D)) { + if (Index >= BD->param_size() || BD->parameters()[Index] != PVD) + return std::make_pair(nullptr, UINT_MAX); + } + + // For `CallExpr` of C++ member operators we must shift the index by 1 + if (isa(CallSite)) { + if (const auto *CMD = dyn_cast(D)) { + if (CMD->isOverloadedOperator()) + ++Index; + } + } + + return std::make_pair(cast(CallSite), Index); +} Index: clang/lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -569,6 +569,8 @@ SVal getBindingForVar(RegionBindingsConstRef B, const VarRegion *R); + SVal getBindingForParam(RegionBindingsConstRef B, const ParamRegion *R); + SVal getBindingForLazySymbol(const TypedValueRegion *R); SVal getBindingForFieldOrElementCommon(RegionBindingsConstRef B, @@ -1510,6 +1512,16 @@ return CastRetrievedVal(getBindingForVar(B, VR), VR, T); } + if (const ParamRegion *PR = dyn_cast(R)) { + // FIXME: Here we actually perform an implicit conversion from the loaded + // value to the variable type. What we should model is stores to variables + // that blow past the extent of the variable. If the address of the + // variable is reinterpretted, it is possible we stored a different value + // that could fit within the variable. Either we need to cast these when + // storing them or reinterpret them lazily (as we do here). + return CastRetrievedVal(getBindingForParam(B, PR), PR, T); + } + const SVal *V = B.lookup(R, BindingKey::Direct); // Check if the region has a binding. @@ -2004,6 +2016,24 @@ return UndefinedVal(); } +SVal RegionStoreManager::getBindingForParam(RegionBindingsConstRef B, + const ParamRegion *R) { + // Check if the region has a binding. + if (Optional V = B.getDirectBinding(R)) + return *V; + + if (Optional V = B.getDefaultBinding(R)) + return *V; + + // Lazily derive a value for the ParamRegion. + const MemSpaceRegion *MS = R->getMemorySpace(); + + assert (isa(MS)); + + SVal V = svalBuilder.getRegionValueSymbolVal(R); + return V; +} + SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) { // All other values are symbolic. return svalBuilder.getRegionValueSymbolVal(R); @@ -2505,6 +2535,13 @@ return; } + if (const ParamRegion *PR = dyn_cast(baseR)) { + if (SymReaper.isLive(PR)) + AddToWorkList(baseR, &C); + + return; + } + if (const SymbolicRegion *SR = dyn_cast(baseR)) { if (SymReaper.isLive(SR->getSymbol())) AddToWorkList(SR, &C); Index: clang/lib/StaticAnalyzer/Core/Store.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/Store.cpp +++ clang/lib/StaticAnalyzer/Core/Store.cpp @@ -51,8 +51,9 @@ SmallVector InitialBindings; Call.getInitialStackFrameContents(LCtx, InitialBindings); - for (const auto &I : InitialBindings) + for (const auto &I : InitialBindings) { Store = Bind(Store.getStore(), I.first.castAs(), I.second); + } return Store; } @@ -138,6 +139,7 @@ case MemRegion::CXXTempObjectRegionKind: case MemRegion::CXXBaseObjectRegionKind: case MemRegion::CXXDerivedObjectRegionKind: + case MemRegion::ParamRegionKind: return MakeElementRegion(cast(R), PointeeTy); case MemRegion::ElementRegionKind: { @@ -435,6 +437,19 @@ return svalBuilder.dispatchCast(V, castTy); } +Loc StoreManager::getLValueVar(const VarDecl *VD, const LocationContext *LC) { + if (const auto *PVD = dyn_cast(VD)) { + const Expr* Call; + unsigned Index; + std::tie(Call, Index) = ParamRegion::getExprAndIndexForParam(PVD, LC); + + if (Call) + return svalBuilder.makeLoc(MRMgr.getParamRegion(Call, Index, LC)); + } + + return svalBuilder.makeLoc(MRMgr.getVarRegion(VD, LC)); +} + SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { if (Base.isUnknownOrUndef()) return Base; Index: clang/lib/StaticAnalyzer/Core/SymbolManager.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ clang/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -440,6 +440,9 @@ if (const auto *VR = dyn_cast(MR)) return isLive(VR, true); + if (const auto *PR = dyn_cast(MR)) + return isLive(PR, true); + // FIXME: This is a gross over-approximation. What we really need is a way to // tell if anything still refers to this region. Unlike SymbolicRegions, // AllocaRegions don't have associated symbols, though, so we don't actually @@ -573,3 +576,53 @@ return VarContext->isParentOf(CurrentContext); } + +bool SymbolReaper::isLive(const ParamRegion *PR, + bool includeStoreBindings) const{ + const StackFrameContext *ParamContext = PR->getStackFrame(); + + if (!ParamContext) + return true; + + if (!LCtx) + return false; + const StackFrameContext *CurrentContext = LCtx->getStackFrame(); + + if (ParamContext == CurrentContext) { + // If no statement is provided, everything is live. + if (!Loc) + return true; + + // Anonymous parameters of an inheriting constructor are live for the entire + // duration of the constructor. + if (isa(Loc)) + return true; + + if (const ParmVarDecl *PVD = PR->getDecl()) { + if (LCtx->getAnalysis()->isLive(Loc, PVD)) + return true; + } + + if (!includeStoreBindings) + return false; + + unsigned &cachedQuery = + const_cast(this)->includedRegionCache[PR]; + + if (cachedQuery) { + return cachedQuery == 1; + } + + // Query the store to see if the region occurs in any live bindings. + if (Store store = reapedStore.getStore()) { + bool hasRegion = + reapedStore.getStoreManager().includedInBindings(store, PR); + cachedQuery = hasRegion ? 1 : 2; + return hasRegion; + } + + return false; + } + + return ParamContext->isParentOf(CurrentContext); +} Index: clang/test/Analysis/container-modeling.cpp =================================================================== --- clang/test/Analysis/container-modeling.cpp +++ clang/test/Analysis/container-modeling.cpp @@ -17,7 +17,7 @@ void clang_analyzer_warnIfReached(); void begin(const std::vector &V) { - V.begin(); + const auto i0 = V.begin(); clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); clang_analyzer_express(clang_analyzer_container_begin(V)); // expected-warning{{$V.begin()}} @@ -25,7 +25,7 @@ } void end(const std::vector &V) { - V.end(); + const auto i0 = V.end(); clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); clang_analyzer_express(clang_analyzer_container_end(V)); // expected-warning{{$V.end()}} @@ -41,10 +41,10 @@ // Move void move_assignment(std::vector &V1, std::vector &V2) { - V1.cbegin(); - V1.cend(); - V2.cbegin(); - V2.cend(); + const auto i0 = V1.cbegin(); + const auto i1 = V1.cend(); + const auto i2 = V2.cbegin(); + const auto i3 = V2.cend(); long B1 = clang_analyzer_container_begin(V1); long E1 = clang_analyzer_container_end(V1); long B2 = clang_analyzer_container_begin(V2); @@ -70,8 +70,8 @@ void clang_analyzer_dump(void*); void push_back(std::vector &V, int n) { - V.cbegin(); - V.cend(); + const auto i0 = V.cbegin(); + const auto i1 = V.cend(); clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); @@ -90,8 +90,8 @@ /// past-the-end position of the container is incremented). void emplace_back(std::vector &V, int n) { - V.cbegin(); - V.cend(); + const auto i0 = V.cbegin(); + const auto i1 = V.cend(); clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); @@ -110,8 +110,8 @@ /// past-the-end position of the container is decremented). void pop_back(std::vector &V, int n) { - V.cbegin(); - V.cend(); + const auto i0 = V.cbegin(); + const auto i1 = V.cend(); clang_analyzer_denote(clang_analyzer_container_begin(V), "$V.begin()"); clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); @@ -131,8 +131,8 @@ /// position of the container is decremented). void push_front(std::list &L, int n) { - L.cbegin(); - L.cend(); + const auto i0 = L.cbegin(); + const auto i1 = L.cend(); clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); @@ -151,8 +151,8 @@ /// position of the container is decremented). void emplace_front(std::list &L, int n) { - L.cbegin(); - L.cend(); + const auto i0 = L.cbegin(); + const auto i1 = L.cend(); clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); @@ -171,8 +171,8 @@ /// position of the container is incremented). void pop_front(std::list &L, int n) { - L.cbegin(); - L.cend(); + const auto i0 = L.cbegin(); + const auto i1 = L.cend(); clang_analyzer_denote(clang_analyzer_container_begin(L), "$L.begin()"); clang_analyzer_denote(clang_analyzer_container_end(L), "$L.end()"); @@ -195,7 +195,7 @@ void push_back() { std::vector V; - V.end(); + const auto i0 = V.end(); clang_analyzer_denote(clang_analyzer_container_end(V), "$V.end()"); @@ -208,10 +208,10 @@ /// Track the right container only void push_back1(std::vector &V1, std::vector &V2, int n) { - V1.cbegin(); - V1.cend(); - V2.cbegin(); - V2.cend(); + const auto i0 = V1.cbegin(); + const auto i1 = V1.cend(); + const auto i2 = V2.cbegin(); + const auto i3 = V2.cend(); clang_analyzer_denote(clang_analyzer_container_begin(V1), "$V1.begin()"); @@ -222,10 +222,10 @@ } void push_back2(std::vector &V1, std::vector &V2, int n) { - V1.cbegin(); - V1.cend(); - V2.cbegin(); - V2.cend(); + const auto i0 = V1.cbegin(); + const auto i1 = V1.cend(); + const auto i2 = V2.cbegin(); + const auto i3 = V2.cend(); clang_analyzer_denote(clang_analyzer_container_begin(V1), "$V1.begin()"); clang_analyzer_denote(clang_analyzer_container_begin(V2), "$V2.begin()"); @@ -245,7 +245,7 @@ void clang_analyzer_printState(); void print_state(std::vector &V) { - V.cbegin(); + const auto i0 = V.cbegin(); clang_analyzer_printState(); // CHECK: "checker_messages": [ @@ -263,3 +263,4 @@ // CHECK-NEXT: "SymRegion{reg_$[[#]] & V>} : [ conj_$[[#]]{long, LC[[#]], S[[#]], #[[#]]} .. conj_$[[#]]{long, LC[[#]], S[[#]], #[[#]]} ]" // CHECK-NEXT: ]} } + Index: clang/test/Analysis/explain-svals.cpp =================================================================== --- clang/test/Analysis/explain-svals.cpp +++ clang/test/Analysis/explain-svals.cpp @@ -93,6 +93,6 @@ } // end of anonymous namespace void test_6() { - clang_analyzer_explain(conjure_S()); // expected-warning-re{{{{^lazily frozen compound value of temporary object constructed at statement 'conjure_S\(\)'$}}}} + clang_analyzer_explain(conjure_S()); // expected-warning-re{{{{^lazily frozen compound value of parameter 0 of function 'clang_analyzer_explain\(\)'$}}}} clang_analyzer_explain(conjure_S().z); // expected-warning-re{{{{^value derived from \(symbol of type 'int' conjured at statement 'conjure_S\(\)'\) for field 'z' of temporary object constructed at statement 'conjure_S\(\)'$}}}} } Index: clang/test/Analysis/iterator-modeling.cpp =================================================================== --- clang/test/Analysis/iterator-modeling.cpp +++ clang/test/Analysis/iterator-modeling.cpp @@ -1862,7 +1862,7 @@ void clang_analyzer_printState(); void print_state(std::vector &V) { - const auto i0 = V.cbegin(); + auto i0 = V.cbegin(); clang_analyzer_printState(); // CHECK: "checker_messages": [ @@ -1871,7 +1871,8 @@ // CHECK-NEXT: "i0 : Valid ; Container == SymRegion{reg_$[[#]] & V>} ; Offset == conj_$[[#]]{long, LC[[#]], S[[#]], #[[#]]}" // CHECK-NEXT: ]} - const auto i1 = V.cend(); + ++i0; + auto i1 = V.cend(); clang_analyzer_printState(); // CHECK: "checker_messages": [ @@ -1879,4 +1880,6 @@ // CHECK-NEXT: "Iterator Positions :", // CHECK-NEXT: "i1 : Valid ; Container == SymRegion{reg_$[[#]] & V>} ; Offset == conj_$[[#]]{long, LC[[#]], S[[#]], #[[#]]}" // CHECK-NEXT: ]} + + --i1; } Index: clang/test/Analysis/temporaries.cpp =================================================================== --- clang/test/Analysis/temporaries.cpp +++ clang/test/Analysis/temporaries.cpp @@ -56,7 +56,7 @@ } namespace rdar13265460 { - struct TrivialSubclass : public Trivial { +struct TrivialSubclass : public Trivial { TrivialSubclass(int x) : Trivial(x), anotherValue(-x) {} int anotherValue; }; @@ -890,12 +890,9 @@ public: ~C() { glob = 1; - // FIXME: Why is destructor not inlined in C++17 clang_analyzer_checkInlined(true); #ifdef TEMPORARY_DTORS -#if __cplusplus < 201703L - // expected-warning@-3{{TRUE}} -#endif + // expected-warning@-2{{TRUE}} #endif } }; @@ -914,16 +911,11 @@ // temporaries returned from functions, so we took the wrong branch. coin && is(get()); // no-crash if (coin) { - // FIXME: Why is destructor not inlined in C++17 clang_analyzer_eval(glob); #ifdef TEMPORARY_DTORS -#if __cplusplus < 201703L - // expected-warning@-3{{TRUE}} + // expected-warning@-2{{TRUE}} #else - // expected-warning@-5{{UNKNOWN}} -#endif -#else - // expected-warning@-8{{UNKNOWN}} + // expected-warning@-4{{UNKNOWN}} #endif } else { // The destructor is not called on this branch. @@ -1237,7 +1229,7 @@ struct S { S() {} S(const S &) {} -}; + }; void test() { int x = 0;