diff options
Diffstat (limited to 'lib/StaticAnalyzer/Core')
27 files changed, 1147 insertions, 603 deletions
diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 54c668cd2d6f9..15422633ba33c 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -23,6 +23,25 @@ using namespace clang; using namespace ento; using namespace llvm; +std::vector<StringRef> +AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { + static const StringRef StaticAnalyzerChecks[] = { +#define GET_CHECKERS +#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \ + FULLNAME, +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER +#undef GET_CHECKERS + }; + std::vector<StringRef> Result; + for (StringRef CheckName : StaticAnalyzerChecks) { + if (!CheckName.startswith("debug.") && + (IncludeExperimental || !CheckName.startswith("alpha."))) + Result.push_back(CheckName); + } + return Result; +} + AnalyzerOptions::UserModeKind AnalyzerOptions::getUserMode() { if (UserMode == UMK_NotSet) { StringRef ModeStr = @@ -344,3 +363,10 @@ bool AnalyzerOptions::shouldWidenLoops() { WidenLoops = getBooleanOption("widen-loops", /*Default=*/false); return WidenLoops.getValue(); } + +bool AnalyzerOptions::shouldDisplayNotesAsEvents() { + if (!DisplayNotesAsEvents.hasValue()) + DisplayNotesAsEvents = + getBooleanOption("notes-as-events", /*Default=*/false); + return DisplayNotesAsEvents.getValue(); +} diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp index 3c3f41a885e95..ebbace4e33b38 100644 --- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -33,6 +33,13 @@ void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID, ID.AddPointer(region); } +void PointerToMemberData::Profile( + llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D, + llvm::ImmutableList<const CXXBaseSpecifier *> L) { + ID.AddPointer(D); + ID.AddPointer(L.getInternalPointer()); +} + typedef std::pair<SVal, uintptr_t> SValData; typedef std::pair<SVal, SVal> SValPair; @@ -142,6 +149,49 @@ BasicValueFactory::getLazyCompoundValData(const StoreRef &store, return D; } +const PointerToMemberData *BasicValueFactory::getPointerToMemberData( + const DeclaratorDecl *DD, llvm::ImmutableList<const CXXBaseSpecifier*> L) { + llvm::FoldingSetNodeID ID; + PointerToMemberData::Profile(ID, DD, L); + void *InsertPos; + + PointerToMemberData *D = + PointerToMemberDataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!D) { + D = (PointerToMemberData*) BPAlloc.Allocate<PointerToMemberData>(); + new (D) PointerToMemberData(DD, L); + PointerToMemberDataSet.InsertNode(D, InsertPos); + } + + return D; +} + +const clang::ento::PointerToMemberData *BasicValueFactory::accumCXXBase( + llvm::iterator_range<CastExpr::path_const_iterator> PathRange, + const nonloc::PointerToMember &PTM) { + nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData(); + const DeclaratorDecl *DD = nullptr; + llvm::ImmutableList<const CXXBaseSpecifier *> PathList; + + if (PTMDT.isNull() || PTMDT.is<const DeclaratorDecl *>()) { + if (PTMDT.is<const DeclaratorDecl *>()) + DD = PTMDT.get<const DeclaratorDecl *>(); + + PathList = CXXBaseListFactory.getEmptyList(); + } else { // const PointerToMemberData * + const PointerToMemberData *PTMD = + PTMDT.get<const PointerToMemberData *>(); + DD = PTMD->getDeclaratorDecl(); + + PathList = PTMD->getCXXBaseList(); + } + + for (const auto &I : llvm::reverse(PathRange)) + PathList = prependCXXBase(I, PathList); + return getPointerToMemberData(DD, PathList); +} + const llvm::APSInt* BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, const llvm::APSInt& V1, const llvm::APSInt& V2) { diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index 488126b0088a1..53b4e699f7ad1 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -21,6 +21,7 @@ #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" #include "clang/Analysis/CFG.h" +#include "clang/Analysis/CFGStmtMap.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/SourceManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -78,7 +79,9 @@ static PathDiagnosticEventPiece * eventsDescribeSameCondition(PathDiagnosticEventPiece *X, PathDiagnosticEventPiece *Y) { // Prefer diagnostics that come from ConditionBRVisitor over - // those that came from TrackConstraintBRVisitor. + // those that came from TrackConstraintBRVisitor, + // unless the one from ConditionBRVisitor is + // its generic fallback diagnostic. const void *tagPreferred = ConditionBRVisitor::getTag(); const void *tagLesser = TrackConstraintBRVisitor::getTag(); @@ -86,10 +89,10 @@ eventsDescribeSameCondition(PathDiagnosticEventPiece *X, return nullptr; if (X->getTag() == tagPreferred && Y->getTag() == tagLesser) - return X; + return ConditionBRVisitor::isPieceMessageGeneric(X) ? Y : X; if (Y->getTag() == tagPreferred && X->getTag() == tagLesser) - return Y; + return ConditionBRVisitor::isPieceMessageGeneric(Y) ? X : Y; return nullptr; } @@ -112,15 +115,15 @@ static void removeRedundantMsgs(PathPieces &path) { path.pop_front(); switch (piece->getKind()) { - case clang::ento::PathDiagnosticPiece::Call: + case PathDiagnosticPiece::Call: removeRedundantMsgs(cast<PathDiagnosticCallPiece>(piece)->path); break; - case clang::ento::PathDiagnosticPiece::Macro: + case PathDiagnosticPiece::Macro: removeRedundantMsgs(cast<PathDiagnosticMacroPiece>(piece)->subPieces); break; - case clang::ento::PathDiagnosticPiece::ControlFlow: + case PathDiagnosticPiece::ControlFlow: break; - case clang::ento::PathDiagnosticPiece::Event: { + case PathDiagnosticPiece::Event: { if (i == N-1) break; @@ -140,6 +143,8 @@ static void removeRedundantMsgs(PathPieces &path) { } break; } + case PathDiagnosticPiece::Note: + break; } path.push_back(piece); } @@ -197,6 +202,9 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, } case PathDiagnosticPiece::ControlFlow: break; + + case PathDiagnosticPiece::Note: + break; } pieces.push_back(piece); @@ -3104,6 +3112,7 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD, R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>()); R->addVisitor(llvm::make_unique<ConditionBRVisitor>()); R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>()); + R->addVisitor(llvm::make_unique<CXXSelfAssignmentBRVisitor>()); BugReport::VisitorList visitors; unsigned origReportConfigToken, finalReportConfigToken; @@ -3277,6 +3286,19 @@ struct FRIEC_WLItem { }; } +static const CFGBlock *findBlockForNode(const ExplodedNode *N) { + ProgramPoint P = N->getLocation(); + if (auto BEP = P.getAs<BlockEntrance>()) + return BEP->getBlock(); + + // Find the node's current statement in the CFG. + if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) + return N->getLocationContext()->getAnalysisDeclContext() + ->getCFGStmtMap()->getBlock(S); + + return nullptr; +} + static BugReport * FindReportInEquivalenceClass(BugReportEquivClass& EQ, SmallVectorImpl<BugReport*> &bugReports) { @@ -3325,6 +3347,18 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, continue; } + // See if we are in a no-return CFG block. If so, treat this similarly + // to being post-dominated by a sink. This works better when the analysis + // is incomplete and we have never reached a no-return function + // we're post-dominated by. + // This is not quite enough to handle the incomplete analysis case. + // We may be post-dominated in subsequent blocks, or even + // inter-procedurally. However, it is not clear if more complicated + // cases are generally worth suppressing. + if (const CFGBlock *B = findBlockForNode(errorNode)) + if (B->hasNoReturnElement()) + continue; + // At this point we know that 'N' is not a sink and it has at least one // successor. Use a DFS worklist to find a non-sink end-of-path node. typedef FRIEC_WLItem WLItem; @@ -3402,25 +3436,28 @@ void BugReporter::FlushReport(BugReport *exampleReport, exampleReport->getUniqueingLocation(), exampleReport->getUniqueingDecl())); - MaxBugClassSize = std::max(bugReports.size(), - static_cast<size_t>(MaxBugClassSize)); + if (exampleReport->isPathSensitive()) { + // Generate the full path diagnostic, using the generation scheme + // specified by the PathDiagnosticConsumer. Note that we have to generate + // path diagnostics even for consumers which do not support paths, because + // the BugReporterVisitors may mark this bug as a false positive. + assert(!bugReports.empty()); + + MaxBugClassSize = + std::max(bugReports.size(), static_cast<size_t>(MaxBugClassSize)); - // Generate the full path diagnostic, using the generation scheme - // specified by the PathDiagnosticConsumer. Note that we have to generate - // path diagnostics even for consumers which do not support paths, because - // the BugReporterVisitors may mark this bug as a false positive. - if (!bugReports.empty()) if (!generatePathDiagnostic(*D.get(), PD, bugReports)) return; - MaxValidBugClassSize = std::max(bugReports.size(), - static_cast<size_t>(MaxValidBugClassSize)); + MaxValidBugClassSize = + std::max(bugReports.size(), static_cast<size_t>(MaxValidBugClassSize)); - // Examine the report and see if the last piece is in a header. Reset the - // report location to the last piece in the main source file. - AnalyzerOptions& Opts = getAnalyzerOptions(); - if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll) - D->resetDiagnosticLocationToMainFile(); + // Examine the report and see if the last piece is in a header. Reset the + // report location to the last piece in the main source file. + AnalyzerOptions &Opts = getAnalyzerOptions(); + if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll) + D->resetDiagnosticLocationToMainFile(); + } // If the path is empty, generate a single step path with the location // of the issue. @@ -3433,6 +3470,27 @@ void BugReporter::FlushReport(BugReport *exampleReport, D->setEndOfPath(std::move(piece)); } + PathPieces &Pieces = D->getMutablePieces(); + if (getAnalyzerOptions().shouldDisplayNotesAsEvents()) { + // For path diagnostic consumers that don't support extra notes, + // we may optionally convert those to path notes. + for (auto I = exampleReport->getNotes().rbegin(), + E = exampleReport->getNotes().rend(); I != E; ++I) { + PathDiagnosticNotePiece *Piece = I->get(); + PathDiagnosticEventPiece *ConvertedPiece = + new PathDiagnosticEventPiece(Piece->getLocation(), + Piece->getString()); + for (const auto &R: Piece->getRanges()) + ConvertedPiece->addRange(R); + + Pieces.push_front(ConvertedPiece); + } + } else { + for (auto I = exampleReport->getNotes().rbegin(), + E = exampleReport->getNotes().rend(); I != E; ++I) + Pieces.push_front(*I); + } + // Get the meta data. const BugReport::ExtraTextList &Meta = exampleReport->getExtraText(); for (BugReport::ExtraTextList::const_iterator i = Meta.begin(), @@ -3517,6 +3575,13 @@ LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const { // FIXME: Print which macro is being invoked. } +LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const { + llvm::errs() << "NOTE\n--------------\n"; + llvm::errs() << getString() << "\n"; + llvm::errs() << " ---- at ----\n"; + getLocation().dump(); +} + LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const { if (!isValid()) { llvm::errs() << "<INVALID>\n"; diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 0e505463bb5e4..7f20f0d7703eb 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -15,6 +15,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/Analysis/CFGStmtMap.h" +#include "clang/Lex/Lexer.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" @@ -916,7 +917,7 @@ static const Expr *peelOffOuterExpr(const Expr *Ex, if (PropRef && PropRef->isMessagingGetter()) { const Expr *GetterMessageSend = POE->getSemanticExpr(POE->getNumSemanticExprs() - 1); - assert(isa<ObjCMessageExpr>(GetterMessageSend)); + assert(isa<ObjCMessageExpr>(GetterMessageSend->IgnoreParenCasts())); return peelOffOuterExpr(GetterMessageSend, N); } } @@ -1271,7 +1272,22 @@ ConditionBRVisitor::VisitTerminator(const Stmt *Term, BugReporterContext &BRC) { const Expr *Cond = nullptr; + // In the code below, Term is a CFG terminator and Cond is a branch condition + // expression upon which the decision is made on this terminator. + // + // For example, in "if (x == 0)", the "if (x == 0)" statement is a terminator, + // and "x == 0" is the respective condition. + // + // Another example: in "if (x && y)", we've got two terminators and two + // conditions due to short-circuit nature of operator "&&": + // 1. The "if (x && y)" statement is a terminator, + // and "y" is the respective condition. + // 2. Also "x && ..." is another terminator, + // and "x" is its condition. + switch (Term->getStmtClass()) { + // FIXME: Stmt::SwitchStmtClass is worth handling, however it is a bit + // more tricky because there are more than two branches to account for. default: return nullptr; case Stmt::IfStmtClass: @@ -1280,6 +1296,24 @@ ConditionBRVisitor::VisitTerminator(const Stmt *Term, case Stmt::ConditionalOperatorClass: Cond = cast<ConditionalOperator>(Term)->getCond(); break; + case Stmt::BinaryOperatorClass: + // When we encounter a logical operator (&& or ||) as a CFG terminator, + // then the condition is actually its LHS; otheriwse, we'd encounter + // the parent, such as if-statement, as a terminator. + const auto *BO = cast<BinaryOperator>(Term); + assert(BO->isLogicalOp() && + "CFG terminator is not a short-circuit operator!"); + Cond = BO->getLHS(); + break; + } + + // However, when we encounter a logical operator as a branch condition, + // then the condition is actually its RHS, because LHS would be + // the condition for the logical operator terminator. + while (const auto *InnerBO = dyn_cast<BinaryOperator>(Cond)) { + if (!InnerBO->isLogicalOp()) + break; + Cond = InnerBO->getRHS()->IgnoreParens(); } assert(Cond); @@ -1294,34 +1328,54 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC, BugReport &R, const ExplodedNode *N) { - - const Expr *Ex = Cond; + // These will be modified in code below, but we need to preserve the original + // values in case we want to throw the generic message. + const Expr *CondTmp = Cond; + bool tookTrueTmp = tookTrue; while (true) { - Ex = Ex->IgnoreParenCasts(); - switch (Ex->getStmtClass()) { + CondTmp = CondTmp->IgnoreParenCasts(); + switch (CondTmp->getStmtClass()) { default: - return nullptr; + break; case Stmt::BinaryOperatorClass: - return VisitTrueTest(Cond, cast<BinaryOperator>(Ex), tookTrue, BRC, - R, N); + if (PathDiagnosticPiece *P = VisitTrueTest( + Cond, cast<BinaryOperator>(CondTmp), tookTrueTmp, BRC, R, N)) + return P; + break; case Stmt::DeclRefExprClass: - return VisitTrueTest(Cond, cast<DeclRefExpr>(Ex), tookTrue, BRC, - R, N); + if (PathDiagnosticPiece *P = VisitTrueTest( + Cond, cast<DeclRefExpr>(CondTmp), tookTrueTmp, BRC, R, N)) + return P; + break; case Stmt::UnaryOperatorClass: { - const UnaryOperator *UO = cast<UnaryOperator>(Ex); + const UnaryOperator *UO = cast<UnaryOperator>(CondTmp); if (UO->getOpcode() == UO_LNot) { - tookTrue = !tookTrue; - Ex = UO->getSubExpr(); + tookTrueTmp = !tookTrueTmp; + CondTmp = UO->getSubExpr(); continue; } - return nullptr; + break; } } + break; } + + // Condition too complex to explain? Just say something so that the user + // knew we've made some path decision at this point. + const LocationContext *LCtx = N->getLocationContext(); + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + if (!Loc.isValid() || !Loc.asLocation().isValid()) + return nullptr; + + PathDiagnosticEventPiece *Event = new PathDiagnosticEventPiece( + Loc, tookTrue ? GenericTrueMessage : GenericFalseMessage); + return Event; } -bool ConditionBRVisitor::patternMatch(const Expr *Ex, raw_ostream &Out, +bool ConditionBRVisitor::patternMatch(const Expr *Ex, + const Expr *ParentEx, + raw_ostream &Out, BugReporterContext &BRC, BugReport &report, const ExplodedNode *N, @@ -1329,6 +1383,47 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, raw_ostream &Out, const Expr *OriginalExpr = Ex; Ex = Ex->IgnoreParenCasts(); + // Use heuristics to determine if Ex is a macro expending to a literal and + // if so, use the macro's name. + SourceLocation LocStart = Ex->getLocStart(); + SourceLocation LocEnd = Ex->getLocEnd(); + if (LocStart.isMacroID() && LocEnd.isMacroID() && + (isa<GNUNullExpr>(Ex) || + isa<ObjCBoolLiteralExpr>(Ex) || + isa<CXXBoolLiteralExpr>(Ex) || + isa<IntegerLiteral>(Ex) || + isa<FloatingLiteral>(Ex))) { + + StringRef StartName = Lexer::getImmediateMacroNameForDiagnostics(LocStart, + BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); + StringRef EndName = Lexer::getImmediateMacroNameForDiagnostics(LocEnd, + BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); + bool beginAndEndAreTheSameMacro = StartName.equals(EndName); + + bool partOfParentMacro = false; + if (ParentEx->getLocStart().isMacroID()) { + StringRef PName = Lexer::getImmediateMacroNameForDiagnostics( + ParentEx->getLocStart(), BRC.getSourceManager(), + BRC.getASTContext().getLangOpts()); + partOfParentMacro = PName.equals(StartName); + } + + if (beginAndEndAreTheSameMacro && !partOfParentMacro ) { + // Get the location of the macro name as written by the caller. + SourceLocation Loc = LocStart; + while (LocStart.isMacroID()) { + Loc = LocStart; + LocStart = BRC.getSourceManager().getImmediateMacroCallerLoc(LocStart); + } + StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics( + Loc, BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); + + // Return the macro name. + Out << MacroName; + return false; + } + } + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) { const bool quotes = isa<VarDecl>(DR->getDecl()); if (quotes) { @@ -1389,10 +1484,10 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, SmallString<128> LhsString, RhsString; { llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString); - const bool isVarLHS = patternMatch(BExpr->getLHS(), OutLHS, BRC, R, N, - shouldPrune); - const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC, R, N, - shouldPrune); + const bool isVarLHS = patternMatch(BExpr->getLHS(), BExpr, OutLHS, + BRC, R, N, shouldPrune); + const bool isVarRHS = patternMatch(BExpr->getRHS(), BExpr, OutRHS, + BRC, R, N, shouldPrune); shouldInvert = !isVarLHS && isVarRHS; } @@ -1552,6 +1647,17 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, return event; } +const char *const ConditionBRVisitor::GenericTrueMessage = + "Assuming the condition is true"; +const char *const ConditionBRVisitor::GenericFalseMessage = + "Assuming the condition is false"; + +bool ConditionBRVisitor::isPieceMessageGeneric( + const PathDiagnosticPiece *Piece) { + return Piece->getString() == GenericTrueMessage || + Piece->getString() == GenericFalseMessage; +} + std::unique_ptr<PathDiagnosticPiece> LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, const ExplodedNode *N, @@ -1693,3 +1799,56 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, } return nullptr; } + +PathDiagnosticPiece * +CXXSelfAssignmentBRVisitor::VisitNode(const ExplodedNode *Succ, + const ExplodedNode *Pred, + BugReporterContext &BRC, BugReport &BR) { + if (Satisfied) + return nullptr; + + auto Edge = Succ->getLocation().getAs<BlockEdge>(); + if (!Edge.hasValue()) + return nullptr; + + auto Tag = Edge->getTag(); + if (!Tag) + return nullptr; + + if (Tag->getTagDescription() != "cplusplus.SelfAssignment") + return nullptr; + + Satisfied = true; + + const auto *Met = + dyn_cast<CXXMethodDecl>(Succ->getCodeDecl().getAsFunction()); + assert(Met && "Not a C++ method."); + assert((Met->isCopyAssignmentOperator() || Met->isMoveAssignmentOperator()) && + "Not a copy/move assignment operator."); + + const auto *LCtx = Edge->getLocationContext(); + + const auto &State = Succ->getState(); + auto &SVB = State->getStateManager().getSValBuilder(); + + const auto Param = + State->getSVal(State->getRegion(Met->getParamDecl(0), LCtx)); + const auto This = + State->getSVal(SVB.getCXXThis(Met, LCtx->getCurrentStackFrame())); + + auto L = PathDiagnosticLocation::create(Met, BRC.getSourceManager()); + + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + + SmallString<256> Buf; + llvm::raw_svector_ostream Out(Buf); + + Out << "Assuming " << Met->getParamDecl(0)->getName() << + ((Param == This) ? " == " : " != ") << "*this"; + + auto *Piece = new PathDiagnosticEventPiece(L, Out.str()); + Piece->addRange(Met->getSourceRange()); + + return Piece; +} diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 52613186677ac..420e2a6b5c8c2 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -382,6 +382,11 @@ bool AnyFunctionCall::argumentsMayEscape() const { if (II->isStr("funopen")) return true; + // - __cxa_demangle - can reallocate memory and can return the pointer to + // the input buffer. + if (II->isStr("__cxa_demangle")) + return true; + StringRef FName = II->getName(); // - CoreFoundation functions that end with "NoCopy" can free a passed-in @@ -552,7 +557,7 @@ void CXXInstanceCall::getInitialStackFrameContents( // FIXME: CallEvent maybe shouldn't be directly accessing StoreManager. bool Failed; - ThisVal = StateMgr.getStoreManager().evalDynamicCast(ThisVal, Ty, Failed); + ThisVal = StateMgr.getStoreManager().attemptDownCast(ThisVal, Ty, Failed); assert(!Failed && "Calling an incorrectly devirtualized method"); } diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index d8382e88691a5..79e204cdafec9 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -518,15 +518,6 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, expandGraphWithCheckers(C, Dst, Src); } -/// \brief True if at least one checker wants to check region changes. -bool CheckerManager::wantsRegionChangeUpdate(ProgramStateRef state) { - for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) - if (RegionChangesCheckers[i].WantUpdateFn(state)) - return true; - - return false; -} - /// \brief Run checkers for region changes. ProgramStateRef CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, @@ -539,8 +530,8 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, // bail out. if (!state) return nullptr; - state = RegionChangesCheckers[i].CheckFn(state, invalidated, - ExplicitRegions, Regions, Call); + state = RegionChangesCheckers[i](state, invalidated, + ExplicitRegions, Regions, Call); } return state; } @@ -726,10 +717,8 @@ void CheckerManager::_registerForDeadSymbols(CheckDeadSymbolsFunc checkfn) { DeadSymbolsCheckers.push_back(checkfn); } -void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn, - WantsRegionChangeUpdateFunc wantUpdateFn) { - RegionChangesCheckerInfo info = {checkfn, wantUpdateFn}; - RegionChangesCheckers.push_back(info); +void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn) { + RegionChangesCheckers.push_back(checkfn); } void CheckerManager::_registerForPointerEscape(CheckPointerEscapeFunc checkfn){ diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp index ba03e2f8a3c18..c9cb189a5b729 100644 --- a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp +++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp @@ -175,3 +175,22 @@ void CheckerRegistry::printHelp(raw_ostream &out, out << '\n'; } } + +void CheckerRegistry::printList( + raw_ostream &out, SmallVectorImpl<CheckerOptInfo> &opts) const { + std::sort(Checkers.begin(), Checkers.end(), checkerNameLT); + + // Collect checkers enabled by the options. + CheckerInfoSet enabledCheckers; + for (SmallVectorImpl<CheckerOptInfo>::iterator i = opts.begin(), + e = opts.end(); + i != e; ++i) { + collectCheckers(Checkers, Packages, *i, enabledCheckers); + } + + for (CheckerInfoSet::const_iterator i = enabledCheckers.begin(), + e = enabledCheckers.end(); + i != e; ++i) { + out << (*i)->FullName << '\n'; + } +} diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index da608f6c75588..4e2866c56f0ee 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -18,7 +18,6 @@ #include "clang/AST/StmtCXX.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" -#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/Casting.h" @@ -310,8 +309,19 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { assert (L.getLocationContext()->getCFG()->getExit().size() == 0 && "EXIT block cannot contain Stmts."); + // Get return statement.. + const ReturnStmt *RS = nullptr; + if (!L.getSrc()->empty()) { + if (Optional<CFGStmt> LastStmt = L.getSrc()->back().getAs<CFGStmt>()) { + if ((RS = dyn_cast<ReturnStmt>(LastStmt->getStmt()))) { + if (!RS->getRetValue()) + RS = nullptr; + } + } + } + // Process the final state transition. - SubEng.processEndOfFunction(BuilderCtx, Pred); + SubEng.processEndOfFunction(BuilderCtx, Pred, RS); // This path is done. Don't enqueue any more nodes. return; @@ -590,13 +600,14 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N, WList->enqueue(Succ, Block, Idx+1); } -ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N) { +ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N, + const ReturnStmt *RS) { // Create a CallExitBegin node and enqueue it. const StackFrameContext *LocCtx = cast<StackFrameContext>(N->getLocationContext()); // Use the callee location context. - CallExitBegin Loc(LocCtx); + CallExitBegin Loc(LocCtx, RS); bool isNew; ExplodedNode *Node = G.getNode(Loc, N->getState(), false, &isNew); @@ -620,12 +631,12 @@ void CoreEngine::enqueue(ExplodedNodeSet &Set, } } -void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set) { +void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS) { for (ExplodedNodeSet::iterator I = Set.begin(), E = Set.end(); I != E; ++I) { ExplodedNode *N = *I; // If we are in an inlined call, generate CallExitBegin node. if (N->getLocationContext()->getParent()) { - N = generateCallExitBeginNode(N); + N = generateCallExitBeginNode(N, RS); if (N) WList->enqueue(N); } else { diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index 02d382cc4885e..3bc8e09333b95 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -17,11 +17,9 @@ #include "clang/AST/Stmt.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" -#include <vector> using namespace clang; using namespace ento; diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 405aecdee0320..5b2119aeda27f 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -27,10 +27,9 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" -#include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/Statistic.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/raw_ostream.h" #ifndef NDEBUG #include "llvm/Support/GraphWriter.h" @@ -203,25 +202,32 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, MemRegionManager &MRMgr = StateMgr.getRegionManager(); StoreManager &StoreMgr = StateMgr.getStoreManager(); - // We need to be careful about treating a derived type's value as - // bindings for a base type. Unless we're creating a temporary pointer region, - // start by stripping and recording base casts. - SmallVector<const CastExpr *, 4> Casts; - const Expr *Inner = Ex->IgnoreParens(); - if (!Loc::isLocType(Result->getType())) { - while (const CastExpr *CE = dyn_cast<CastExpr>(Inner)) { - if (CE->getCastKind() == CK_DerivedToBase || - CE->getCastKind() == CK_UncheckedDerivedToBase) - Casts.push_back(CE); - else if (CE->getCastKind() != CK_NoOp) - break; + // MaterializeTemporaryExpr may appear out of place, after a few field and + // base-class accesses have been made to the object, even though semantically + // it is the whole object that gets materialized and lifetime-extended. + // + // For example: + // + // `-MaterializeTemporaryExpr + // `-MemberExpr + // `-CXXTemporaryObjectExpr + // + // instead of the more natural + // + // `-MemberExpr + // `-MaterializeTemporaryExpr + // `-CXXTemporaryObjectExpr + // + // Use the usual methods for obtaining the expression of the base object, + // and record the adjustments that we need to make to obtain the sub-object + // that the whole expression 'Ex' refers to. This trick is usual, + // in the sense that CodeGen takes a similar route. - Inner = CE->getSubExpr()->IgnoreParens(); - } - } + SmallVector<const Expr *, 2> CommaLHSs; + SmallVector<SubobjectAdjustment, 2> Adjustments; + + const Expr *Init = Ex->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); - // Create a temporary object region for the inner expression (which may have - // a more derived type) and bind the value into it. const TypedValueRegion *TR = nullptr; if (const MaterializeTemporaryExpr *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) { @@ -229,25 +235,37 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State, // If this object is bound to a reference with static storage duration, we // put it in a different region to prevent "address leakage" warnings. if (SD == SD_Static || SD == SD_Thread) - TR = MRMgr.getCXXStaticTempObjectRegion(Inner); + TR = MRMgr.getCXXStaticTempObjectRegion(Init); } if (!TR) - TR = MRMgr.getCXXTempObjectRegion(Inner, LC); + TR = MRMgr.getCXXTempObjectRegion(Init, LC); SVal Reg = loc::MemRegionVal(TR); + // Make the necessary adjustments to obtain the sub-object. + for (auto I = Adjustments.rbegin(), E = Adjustments.rend(); I != E; ++I) { + const SubobjectAdjustment &Adj = *I; + switch (Adj.Kind) { + case SubobjectAdjustment::DerivedToBaseAdjustment: + Reg = StoreMgr.evalDerivedToBase(Reg, Adj.DerivedToBase.BasePath); + break; + case SubobjectAdjustment::FieldAdjustment: + Reg = StoreMgr.getLValueField(Adj.Field, Reg); + break; + case SubobjectAdjustment::MemberPointerAdjustment: + // FIXME: Unimplemented. + State->bindDefault(Reg, UnknownVal()); + return State; + } + } + + // Try to recover some path sensitivity in case we couldn't compute the value. if (V.isUnknown()) V = getSValBuilder().conjureSymbolVal(Result, LC, TR->getValueType(), currBldrCtx->blockCount()); + // Bind the value of the expression to the sub-object region, and then bind + // the sub-object region to our expression. State = State->bindLoc(Reg, V); - - // Re-apply the casts (from innermost to outermost) for type sanity. - for (SmallVectorImpl<const CastExpr *>::reverse_iterator I = Casts.rbegin(), - E = Casts.rend(); - I != E; ++I) { - Reg = StoreMgr.evalDerivedToBase(Reg, *I); - } - State = State->BindExpr(Result, LC, Reg); return State; } @@ -263,10 +281,6 @@ ProgramStateRef ExprEngine::processAssume(ProgramStateRef state, return getCheckerManager().runCheckersForEvalAssume(state, cond, assumption); } -bool ExprEngine::wantsRegionChangeUpdate(ProgramStateRef state) { - return getCheckerManager().wantsRegionChangeUpdate(state); -} - ProgramStateRef ExprEngine::processRegionChanges(ProgramStateRef state, const InvalidatedSymbols *invalidated, @@ -493,7 +507,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init, } SVal InitVal; - if (BMI->getNumArrayIndices() > 0) { + if (Init->getType()->isArrayType()) { // Handle arrays of trivial type. We can represent this with a // primitive load/copy from the base array region. const ArraySubscriptExpr *ASE; @@ -597,9 +611,9 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, SVal dest = state->getLValue(varDecl, Pred->getLocationContext()); const MemRegion *Region = dest.castAs<loc::MemRegionVal>().getRegion(); - if (const ReferenceType *refType = varType->getAs<ReferenceType>()) { - varType = refType->getPointeeType(); - Region = state->getSVal(Region).getAsRegion(); + if (varType->isReferenceType()) { + Region = state->getSVal(Region).getAsRegion()->getBaseRegion(); + varType = cast<TypedValueRegion>(Region)->getValueType(); } VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false, @@ -847,6 +861,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPDistributeParallelForSimdDirectiveClass: case Stmt::OMPDistributeSimdDirectiveClass: case Stmt::OMPTargetParallelForSimdDirectiveClass: + case Stmt::OMPTargetSimdDirectiveClass: + case Stmt::OMPTeamsDistributeDirectiveClass: + case Stmt::OMPTeamsDistributeSimdDirectiveClass: + case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: + case Stmt::OMPTeamsDistributeParallelForDirectiveClass: + case Stmt::OMPTargetTeamsDirectiveClass: + case Stmt::OMPTargetTeamsDistributeDirectiveClass: + case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: llvm_unreachable("Stmt should not be in analyzer evaluation loop"); case Stmt::ObjCSubscriptRefExprClass: @@ -886,6 +908,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, // Cases not handled yet; but will handle some day. case Stmt::DesignatedInitExprClass: case Stmt::DesignatedInitUpdateExprClass: + case Stmt::ArrayInitLoopExprClass: + case Stmt::ArrayInitIndexExprClass: case Stmt::ExtVectorElementExprClass: case Stmt::ImaginaryLiteralClass: case Stmt::ObjCAtCatchStmtClass: @@ -1211,16 +1235,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::ObjCBridgedCastExprClass: { Bldr.takeNodes(Pred); const CastExpr *C = cast<CastExpr>(S); - // Handle the previsit checks. - ExplodedNodeSet dstPrevisit; - getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, C, *this); - - // Handle the expression itself. ExplodedNodeSet dstExpr; - for (ExplodedNodeSet::iterator i = dstPrevisit.begin(), - e = dstPrevisit.end(); i != e ; ++i) { - VisitCast(C, C->getSubExpr(), *i, dstExpr); - } + VisitCast(C, C->getSubExpr(), Pred, dstExpr); // Handle the postvisit checks. getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this); @@ -1773,7 +1789,8 @@ void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC, /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path /// nodes when the control reaches the end of a function. void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, - ExplodedNode *Pred) { + ExplodedNode *Pred, + const ReturnStmt *RS) { // FIXME: Assert that stackFrameDoesNotContainInitializedTemporaries(*Pred)). // We currently cannot enable this assert, as lifetime extended temporaries // are not modelled correctly. @@ -1795,7 +1812,7 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this); } - Engine.enqueueEndOfFunction(Dst); + Engine.enqueueEndOfFunction(Dst, RS); } /// ProcessSwitch - Called by CoreEngine. Used to generate successor @@ -1841,7 +1858,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { ProgramStateRef StateCase; if (Optional<NonLoc> NL = CondV.getAs<NonLoc>()) std::tie(StateCase, DefaultSt) = - DefaultSt->assumeWithinInclusiveRange(*NL, V1, V2); + DefaultSt->assumeInclusiveRange(*NL, V1, V2); else // UnknownVal StateCase = DefaultSt; @@ -1975,24 +1992,26 @@ void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr *A, const Expr *Base = A->getBase()->IgnoreParens(); const Expr *Idx = A->getIdx()->IgnoreParens(); - ExplodedNodeSet checkerPreStmt; - getCheckerManager().runCheckersForPreStmt(checkerPreStmt, Pred, A, *this); + ExplodedNodeSet CheckerPreStmt; + getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this); - StmtNodeBuilder Bldr(checkerPreStmt, Dst, *currBldrCtx); + ExplodedNodeSet EvalSet; + StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx); assert(A->isGLValue() || (!AMgr.getLangOpts().CPlusPlus && A->getType().isCForbiddenLValueType())); - for (ExplodedNodeSet::iterator it = checkerPreStmt.begin(), - ei = checkerPreStmt.end(); it != ei; ++it) { - const LocationContext *LCtx = (*it)->getLocationContext(); - ProgramStateRef state = (*it)->getState(); + for (auto *Node : CheckerPreStmt) { + const LocationContext *LCtx = Node->getLocationContext(); + ProgramStateRef state = Node->getState(); SVal V = state->getLValue(A->getType(), state->getSVal(Idx, LCtx), state->getSVal(Base, LCtx)); - Bldr.generateNode(A, *it, state->BindExpr(A, LCtx, V), nullptr, + Bldr.generateNode(A, Node, state->BindExpr(A, LCtx, V), nullptr, ProgramPoint::PostLValueKind); } + + getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this); } /// VisitMemberExpr - Transfer function for member expressions. @@ -2051,7 +2070,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, if (!M->isGLValue()) { assert(M->getType()->isArrayType()); const ImplicitCastExpr *PE = - dyn_cast<ImplicitCastExpr>((*I)->getParentMap().getParent(M)); + dyn_cast<ImplicitCastExpr>((*I)->getParentMap().getParentIgnoreParens(M)); if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) { llvm_unreachable("should always be wrapped in ArrayToPointerDecay"); } @@ -2521,26 +2540,10 @@ struct DOTGraphTraits<ExplodedNode*> : // FIXME: Since we do not cache error nodes in ExprEngine now, this does not // work. static std::string getNodeAttributes(const ExplodedNode *N, void*) { - -#if 0 - // FIXME: Replace with a general scheme to tell if the node is - // an error node. - if (GraphPrintCheckerState->isImplicitNullDeref(N) || - GraphPrintCheckerState->isExplicitNullDeref(N) || - GraphPrintCheckerState->isUndefDeref(N) || - GraphPrintCheckerState->isUndefStore(N) || - GraphPrintCheckerState->isUndefControlFlow(N) || - GraphPrintCheckerState->isUndefResult(N) || - GraphPrintCheckerState->isBadCall(N) || - GraphPrintCheckerState->isUndefArg(N)) - return "color=\"red\",style=\"filled\""; - - if (GraphPrintCheckerState->isNoReturnCall(N)) - return "color=\"blue\",style=\"filled\""; -#endif return ""; } + // De-duplicate some source location pretty-printing. static void printLocation(raw_ostream &Out, SourceLocation SLoc) { if (SLoc.isFileID()) { Out << "\\lline=" @@ -2550,6 +2553,12 @@ struct DOTGraphTraits<ExplodedNode*> : << "\\l"; } } + static void printLocation2(raw_ostream &Out, SourceLocation SLoc) { + if (SLoc.isFileID() && GraphPrintSourceManager->isInMainFile(SLoc)) + Out << "line " << GraphPrintSourceManager->getExpansionLineNumber(SLoc); + else + SLoc.print(Out, *GraphPrintSourceManager); + } static std::string getNodeLabel(const ExplodedNode *N, void*){ @@ -2563,12 +2572,6 @@ struct DOTGraphTraits<ExplodedNode*> : case ProgramPoint::BlockEntranceKind: { Out << "Block Entrance: B" << Loc.castAs<BlockEntrance>().getBlock()->getBlockID(); - if (const NamedDecl *ND = - dyn_cast<NamedDecl>(Loc.getLocationContext()->getDecl())) { - Out << " ("; - ND->printName(Out); - Out << ")"; - } break; } @@ -2693,13 +2696,6 @@ struct DOTGraphTraits<ExplodedNode*> : Out << "\\l"; } -#if 0 - // FIXME: Replace with a general scheme to determine - // the name of the check. - if (GraphPrintCheckerState->isUndefControlFlow(N)) { - Out << "\\|Control-flow based on\\lUndefined value.\\l"; - } -#endif break; } @@ -2721,27 +2717,6 @@ struct DOTGraphTraits<ExplodedNode*> : else if (Loc.getAs<PostLValue>()) Out << "\\lPostLValue\\l"; -#if 0 - // FIXME: Replace with a general scheme to determine - // the name of the check. - if (GraphPrintCheckerState->isImplicitNullDeref(N)) - Out << "\\|Implicit-Null Dereference.\\l"; - else if (GraphPrintCheckerState->isExplicitNullDeref(N)) - Out << "\\|Explicit-Null Dereference.\\l"; - else if (GraphPrintCheckerState->isUndefDeref(N)) - Out << "\\|Dereference of undefialied value.\\l"; - else if (GraphPrintCheckerState->isUndefStore(N)) - Out << "\\|Store to Undefined Loc."; - else if (GraphPrintCheckerState->isUndefResult(N)) - Out << "\\|Result of operation is undefined."; - else if (GraphPrintCheckerState->isNoReturnCall(N)) - Out << "\\|Call to function marked \"noreturn\"."; - else if (GraphPrintCheckerState->isBadCall(N)) - Out << "\\|Call to NULL/Undefined."; - else if (GraphPrintCheckerState->isUndefArg(N)) - Out << "\\|Argument in call is undefined"; -#endif - break; } } @@ -2749,6 +2724,40 @@ struct DOTGraphTraits<ExplodedNode*> : ProgramStateRef state = N->getState(); Out << "\\|StateID: " << (const void*) state.get() << " NodeID: " << (const void*) N << "\\|"; + + // Analysis stack backtrace. + Out << "Location context stack (from current to outer):\\l"; + const LocationContext *LC = Loc.getLocationContext(); + unsigned Idx = 0; + for (; LC; LC = LC->getParent(), ++Idx) { + Out << Idx << ". (" << (const void *)LC << ") "; + switch (LC->getKind()) { + case LocationContext::StackFrame: + if (const NamedDecl *D = dyn_cast<NamedDecl>(LC->getDecl())) + Out << "Calling " << D->getQualifiedNameAsString(); + else + Out << "Calling anonymous code"; + if (const Stmt *S = cast<StackFrameContext>(LC)->getCallSite()) { + Out << " at "; + printLocation2(Out, S->getLocStart()); + } + break; + case LocationContext::Block: + Out << "Invoking block"; + if (const Decl *D = cast<BlockInvocationContext>(LC)->getBlockDecl()) { + Out << " defined at "; + printLocation2(Out, D->getLocStart()); + } + break; + case LocationContext::Scope: + Out << "Entering scope"; + // FIXME: Add more info once ScopeContext is activated. + break; + } + Out << "\\l"; + } + Out << "\\l"; + state->printDOT(Out); Out << "\\l"; diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 175225ba0de2f..89fab1d56af00 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ExprCXX.h" +#include "clang/AST/DeclCXX.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" @@ -246,6 +247,38 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this); } +ProgramStateRef ExprEngine::handleLValueBitCast( + ProgramStateRef state, const Expr* Ex, const LocationContext* LCtx, + QualType T, QualType ExTy, const CastExpr* CastE, StmtNodeBuilder& Bldr, + ExplodedNode* Pred) { + // Delegate to SValBuilder to process. + SVal V = state->getSVal(Ex, LCtx); + V = svalBuilder.evalCast(V, T, ExTy); + // Negate the result if we're treating the boolean as a signed i1 + if (CastE->getCastKind() == CK_BooleanToSignedIntegral) + V = evalMinus(V); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + + return state; +} + +ProgramStateRef ExprEngine::handleLVectorSplat( + ProgramStateRef state, const LocationContext* LCtx, const CastExpr* CastE, + StmtNodeBuilder &Bldr, ExplodedNode* Pred) { + // Recover some path sensitivity by conjuring a new value. + QualType resultType = CastE->getType(); + if (CastE->isGLValue()) + resultType = getContext().getPointerType(resultType); + SVal result = svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, + resultType, + currBldrCtx->blockCount()); + state = state->BindExpr(CastE, LCtx, result); + Bldr.generateNode(CastE, Pred, state); + + return state; +} + void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNode *Pred, ExplodedNodeSet &Dst) { @@ -310,8 +343,21 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, continue; } case CK_MemberPointerToBoolean: - // FIXME: For now, member pointers are represented by void *. - // FALLTHROUGH + case CK_PointerToBoolean: { + SVal V = state->getSVal(Ex, LCtx); + auto PTMSV = V.getAs<nonloc::PointerToMember>(); + if (PTMSV) + V = svalBuilder.makeTruthVal(!PTMSV->isNullMemberPointer(), ExTy); + if (V.isUndef() || PTMSV) { + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + continue; + } + // Explicitly proceed with default handler for this case cascade. + state = + handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); + continue; + } case CK_Dependent: case CK_ArrayToPointerDecay: case CK_BitCast: @@ -319,8 +365,18 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_BooleanToSignedIntegral: case CK_NullToPointer: case CK_IntegralToPointer: - case CK_PointerToIntegral: - case CK_PointerToBoolean: + case CK_PointerToIntegral: { + SVal V = state->getSVal(Ex, LCtx); + if (V.getAs<nonloc::PointerToMember>()) { + state = state->BindExpr(CastE, LCtx, UnknownVal()); + Bldr.generateNode(CastE, Pred, state); + continue; + } + // Explicitly proceed with default handler for this case cascade. + state = + handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); + continue; + } case CK_IntegralToBoolean: case CK_IntegralToFloating: case CK_FloatingToIntegral: @@ -341,15 +397,11 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_AnyPointerToBlockPointerCast: case CK_ObjCObjectLValueCast: case CK_ZeroToOCLEvent: + case CK_ZeroToOCLQueue: + case CK_IntToOCLSampler: case CK_LValueBitCast: { - // Delegate to SValBuilder to process. - SVal V = state->getSVal(Ex, LCtx); - V = svalBuilder.evalCast(V, T, ExTy); - // Negate the result if we're treating the boolean as a signed i1 - if (CastE->getCastKind() == CK_BooleanToSignedIntegral) - V = evalMinus(V); - state = state->BindExpr(CastE, LCtx, V); - Bldr.generateNode(CastE, Pred, state); + state = + handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); continue; } case CK_IntegralCast: { @@ -385,7 +437,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, Failed = true; // Else, evaluate the cast. else - val = getStoreManager().evalDynamicCast(val, T, Failed); + val = getStoreManager().attemptDownCast(val, T, Failed); if (Failed) { if (T->isReferenceType()) { @@ -411,29 +463,55 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, Bldr.generateNode(CastE, Pred, state); continue; } + case CK_BaseToDerived: { + SVal val = state->getSVal(Ex, LCtx); + QualType resultType = CastE->getType(); + if (CastE->isGLValue()) + resultType = getContext().getPointerType(resultType); + + bool Failed = false; + + if (!val.isConstant()) { + val = getStoreManager().attemptDownCast(val, T, Failed); + } + + // Failed to cast or the result is unknown, fall back to conservative. + if (Failed || val.isUnknown()) { + val = + svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, resultType, + currBldrCtx->blockCount()); + } + state = state->BindExpr(CastE, LCtx, val); + Bldr.generateNode(CastE, Pred, state); + continue; + } case CK_NullToMemberPointer: { - // FIXME: For now, member pointers are represented by void *. - SVal V = svalBuilder.makeNull(); + SVal V = svalBuilder.getMemberPointer(nullptr); state = state->BindExpr(CastE, LCtx, V); Bldr.generateNode(CastE, Pred, state); continue; } + case CK_DerivedToBaseMemberPointer: + case CK_BaseToDerivedMemberPointer: + case CK_ReinterpretMemberPointer: { + SVal V = state->getSVal(Ex, LCtx); + if (auto PTMSV = V.getAs<nonloc::PointerToMember>()) { + SVal CastedPTMSV = svalBuilder.makePointerToMember( + getBasicVals().accumCXXBase( + llvm::make_range<CastExpr::path_const_iterator>( + CastE->path_begin(), CastE->path_end()), *PTMSV)); + state = state->BindExpr(CastE, LCtx, CastedPTMSV); + Bldr.generateNode(CastE, Pred, state); + continue; + } + // Explicitly proceed with default handler for this case cascade. + state = handleLVectorSplat(state, LCtx, CastE, Bldr, Pred); + continue; + } // Various C++ casts that are not handled yet. case CK_ToUnion: - case CK_BaseToDerived: - case CK_BaseToDerivedMemberPointer: - case CK_DerivedToBaseMemberPointer: - case CK_ReinterpretMemberPointer: case CK_VectorSplat: { - // Recover some path-sensitivty by conjuring a new value. - QualType resultType = CastE->getType(); - if (CastE->isGLValue()) - resultType = getContext().getPointerType(resultType); - SVal result = svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, - resultType, - currBldrCtx->blockCount()); - state = state->BindExpr(CastE, LCtx, result); - Bldr.generateNode(CastE, Pred, state); + state = handleLVectorSplat(state, LCtx, CastE, Bldr, Pred); continue; } } @@ -458,15 +536,7 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, Loc CLLoc = State->getLValue(CL, LCtx); State = State->bindLoc(CLLoc, V); - // Compound literal expressions are a GNU extension in C++. - // Unlike in C, where CLs are lvalues, in C++ CLs are prvalues, - // and like temporary objects created by the functional notation T() - // CLs are destroyed at the end of the containing full-expression. - // HOWEVER, an rvalue of array type is not something the analyzer can - // reason about, since we expect all regions to be wrapped in Locs. - // So we treat array CLs as lvalues as well, knowing that they will decay - // to pointers as soon as they are used. - if (CL->isGLValue() || CL->getType()->isArrayType()) + if (CL->isGLValue()) V = CLLoc; } @@ -596,23 +666,13 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, if (RHSVal.isUndef()) { X = RHSVal; } else { - DefinedOrUnknownSVal DefinedRHS = RHSVal.castAs<DefinedOrUnknownSVal>(); - ProgramStateRef StTrue, StFalse; - std::tie(StTrue, StFalse) = N->getState()->assume(DefinedRHS); - if (StTrue) { - if (StFalse) { - // We can't constrain the value to 0 or 1. - // The best we can do is a cast. - X = getSValBuilder().evalCast(RHSVal, B->getType(), RHS->getType()); - } else { - // The value is known to be true. - X = getSValBuilder().makeIntVal(1, B->getType()); - } - } else { - // The value is known to be false. - assert(StFalse && "Infeasible path!"); - X = getSValBuilder().makeIntVal(0, B->getType()); - } + // We evaluate "RHSVal != 0" expression which result in 0 if the value is + // known to be false, 1 if the value is known to be true and a new symbol + // when the assumption is unknown. + nonloc::ConcreteInt Zero(getBasicVals().getValue(0, B->getType())); + X = evalBinOp(N->getState(), BO_NE, + svalBuilder.evalCast(RHSVal, B->getType(), RHS->getType()), + Zero, B->getType()); } } Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X)); @@ -644,7 +704,7 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE, for (InitListExpr::const_reverse_iterator it = IE->rbegin(), ei = IE->rend(); it != ei; ++it) { SVal V = state->getSVal(cast<Expr>(*it), LCtx); - vals = getBasicVals().consVals(V, vals); + vals = getBasicVals().prependSVal(V, vals); } B.generateNode(IE, Pred, @@ -789,8 +849,24 @@ VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this); } -void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, - ExplodedNode *Pred, +void ExprEngine::handleUOExtension(ExplodedNodeSet::iterator I, + const UnaryOperator *U, + StmtNodeBuilder &Bldr) { + // FIXME: We can probably just have some magic in Environment::getSVal() + // that propagates values, instead of creating a new node here. + // + // Unary "+" is a no-op, similar to a parentheses. We still have places + // where it may be a block-level expression, so we need to + // generate an extra node that just propagates the value of the + // subexpression. + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + ProgramStateRef state = (*I)->getState(); + const LocationContext *LCtx = (*I)->getLocationContext(); + Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, + state->getSVal(Ex, LCtx))); +} + +void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, ExplodedNodeSet &Dst) { // FIXME: Prechecks eventually go in ::Visit(). ExplodedNodeSet CheckedSet; @@ -842,24 +918,30 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, break; } + case UO_AddrOf: { + // Process pointer-to-member address operation. + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Ex)) { + const ValueDecl *VD = DRE->getDecl(); + + if (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD)) { + ProgramStateRef State = (*I)->getState(); + const LocationContext *LCtx = (*I)->getLocationContext(); + SVal SV = svalBuilder.getMemberPointer(cast<DeclaratorDecl>(VD)); + Bldr.generateNode(U, *I, State->BindExpr(U, LCtx, SV)); + break; + } + } + // Explicitly proceed with default handler for this case cascade. + handleUOExtension(I, U, Bldr); + break; + } case UO_Plus: assert(!U->isGLValue()); // FALL-THROUGH. case UO_Deref: - case UO_AddrOf: case UO_Extension: { - // FIXME: We can probably just have some magic in Environment::getSVal() - // that propagates values, instead of creating a new node here. - // - // Unary "+" is a no-op, similar to a parentheses. We still have places - // where it may be a block-level expression, so we need to - // generate an extra node that just propagates the value of the - // subexpression. - const Expr *Ex = U->getSubExpr()->IgnoreParens(); - ProgramStateRef state = (*I)->getState(); - const LocationContext *LCtx = (*I)->getLocationContext(); - Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, - state->getSVal(Ex, LCtx))); + handleUOExtension(I, U, Bldr); break; } diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 556e2239abfb5..7e9b2033ca373 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -65,7 +65,7 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, if (Optional<Loc> L = V.getAs<Loc>()) V = Pred->getState()->getSVal(*L); else - assert(V.isUnknown()); + assert(V.isUnknownOrUndef()); const Expr *CallExpr = Call.getOriginExpr(); evalBind(Dst, CallExpr, Pred, ThisVal, V, true); @@ -346,6 +346,30 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, defaultEvalCall(Bldr, *I, *Call); } + // If the CFG was contructed without elements for temporary destructors + // and the just-called constructor created a temporary object then + // stop exploration if the temporary object has a noreturn constructor. + // This can lose coverage because the destructor, if it were present + // in the CFG, would be called at the end of the full expression or + // later (for life-time extended temporaries) -- but avoids infeasible + // paths when no-return temporary destructors are used for assertions. + const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext(); + if (!ADC->getCFGBuildOptions().AddTemporaryDtors) { + const MemRegion *Target = Call->getCXXThisVal().getAsRegion(); + if (Target && isa<CXXTempObjectRegion>(Target) && + Call->getDecl()->getParent()->isAnyDestructorNoReturn()) { + + for (ExplodedNode *N : DstEvaluated) { + Bldr.generateSink(CE, N, N->getState()); + } + + // There is no need to run the PostCall and PostStmtchecker + // callbacks because we just generated sinks on all nodes in th + // frontier. + return; + } + } + ExplodedNodeSet DstPostCall; getCheckerManager().runCheckersForPostCall(DstPostCall, DstEvaluated, *Call, *this); @@ -578,9 +602,9 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, const MemRegion *R = svalBuilder.getRegionManager().getCXXTempObjectRegion( LE, LocCtxt); SVal V = loc::MemRegionVal(R); - + ProgramStateRef State = Pred->getState(); - + // If we created a new MemRegion for the lambda, we should explicitly bind // the captures. CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin(); diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 3a18956e41393..f157c3dd6ce20 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -152,13 +152,30 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, } // Process the path. - unsigned n = path.size(); - unsigned max = n; - - for (PathPieces::const_reverse_iterator I = path.rbegin(), - E = path.rend(); - I != E; ++I, --n) - HandlePiece(R, FID, **I, n, max); + // Maintain the counts of extra note pieces separately. + unsigned TotalPieces = path.size(); + unsigned TotalNotePieces = + std::count_if(path.begin(), path.end(), + [](const IntrusiveRefCntPtr<PathDiagnosticPiece> &p) { + return isa<PathDiagnosticNotePiece>(p.get()); + }); + + unsigned TotalRegularPieces = TotalPieces - TotalNotePieces; + unsigned NumRegularPieces = TotalRegularPieces; + unsigned NumNotePieces = TotalNotePieces; + + for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) { + if (isa<PathDiagnosticNotePiece>(I->get())) { + // This adds diagnostic bubbles, but not navigation. + // Navigation through note pieces would be added later, + // as a separate pass through the piece list. + HandlePiece(R, FID, **I, NumNotePieces, TotalNotePieces); + --NumNotePieces; + } else { + HandlePiece(R, FID, **I, NumRegularPieces, TotalRegularPieces); + --NumRegularPieces; + } + } // Add line numbers, header, footer, etc. @@ -192,24 +209,38 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber(); // Add the name of the file as an <h1> tag. - { std::string s; llvm::raw_string_ostream os(s); os << "<!-- REPORTHEADER -->\n" - << "<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n" + << "<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n" "<tr><td class=\"rowname\">File:</td><td>" - << html::EscapeText(DirName) - << html::EscapeText(Entry->getName()) - << "</td></tr>\n<tr><td class=\"rowname\">Location:</td><td>" - "<a href=\"#EndPath\">line " - << LineNumber - << ", column " - << ColumnNumber - << "</a></td></tr>\n" - "<tr><td class=\"rowname\">Description:</td><td>" - << D.getVerboseDescription() << "</td></tr>\n"; + << html::EscapeText(DirName) + << html::EscapeText(Entry->getName()) + << "</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>" + "<a href=\"#EndPath\">line " + << LineNumber + << ", column " + << ColumnNumber + << "</a><br />" + << D.getVerboseDescription() << "</td></tr>\n"; + + // The navigation across the extra notes pieces. + unsigned NumExtraPieces = 0; + for (const auto &Piece : path) { + if (const auto *P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) { + int LineNumber = + P->getLocation().asLocation().getExpansionLineNumber(); + int ColumnNumber = + P->getLocation().asLocation().getExpansionColumnNumber(); + os << "<tr><td class=\"rowname\">Note:</td><td>" + << "<a href=\"#Note" << NumExtraPieces << "\">line " + << LineNumber << ", column " << ColumnNumber << "</a><br />" + << P->getString() << "</td></tr>"; + ++NumExtraPieces; + } + } // Output any other meta data. @@ -385,13 +416,20 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, // Create the html for the message. const char *Kind = nullptr; + bool IsNote = false; + bool SuppressIndex = (max == 1); switch (P.getKind()) { case PathDiagnosticPiece::Call: - llvm_unreachable("Calls should already be handled"); + llvm_unreachable("Calls and extra notes should already be handled"); case PathDiagnosticPiece::Event: Kind = "Event"; break; case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break; // Setting Kind to "Control" is intentional. case PathDiagnosticPiece::Macro: Kind = "Control"; break; + case PathDiagnosticPiece::Note: + Kind = "Note"; + IsNote = true; + SuppressIndex = true; + break; } std::string sbuf; @@ -399,7 +437,9 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, os << "\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\""; - if (num == max) + if (IsNote) + os << "Note" << num; + else if (num == max) os << "EndPath"; else os << "Path" << num; @@ -461,7 +501,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, os << "\">"; - if (max > 1) { + if (!SuppressIndex) { os << "<table class=\"msgT\"><tr><td valign=\"top\">"; os << "<div class=\"PathIndex"; if (Kind) os << " PathIndex" << Kind; @@ -501,7 +541,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, os << "':\n"; - if (max > 1) { + if (!SuppressIndex) { os << "</td>"; if (num < max) { os << "<td><div class=\"PathNav\"><a href=\"#"; @@ -523,7 +563,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, else { os << html::EscapeText(P.getString()); - if (max > 1) { + if (!SuppressIndex) { os << "</td>"; if (num < max) { os << "<td><div class=\"PathNav\"><a href=\"#"; diff --git a/lib/StaticAnalyzer/Core/IssueHash.cpp b/lib/StaticAnalyzer/Core/IssueHash.cpp index bd5c81179adc9..abdea88b1db6f 100644 --- a/lib/StaticAnalyzer/Core/IssueHash.cpp +++ b/lib/StaticAnalyzer/Core/IssueHash.cpp @@ -13,7 +13,6 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" #include "clang/Lex/Lexer.h" -#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index b7b6f42b29102..c4ba2ae199f88 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -31,28 +31,6 @@ using namespace ento; // MemRegion Construction. //===----------------------------------------------------------------------===// -template<typename RegionTy> struct MemRegionManagerTrait; - -template <typename RegionTy, typename A1> -RegionTy* MemRegionManager::getRegion(const A1 a1) { - const typename MemRegionManagerTrait<RegionTy>::SuperRegionTy *superRegion = - MemRegionManagerTrait<RegionTy>::getSuperRegion(*this, a1); - - llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, superRegion); - void *InsertPos; - RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, - InsertPos)); - - if (!R) { - R = A.Allocate<RegionTy>(); - new (R) RegionTy(a1, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - template <typename RegionTy, typename A1> RegionTy* MemRegionManager::getSubRegion(const A1 a1, const MemRegion *superRegion) { @@ -72,26 +50,6 @@ RegionTy* MemRegionManager::getSubRegion(const A1 a1, } template <typename RegionTy, typename A1, typename A2> -RegionTy* MemRegionManager::getRegion(const A1 a1, const A2 a2) { - const typename MemRegionManagerTrait<RegionTy>::SuperRegionTy *superRegion = - MemRegionManagerTrait<RegionTy>::getSuperRegion(*this, a1, a2); - - llvm::FoldingSetNodeID ID; - RegionTy::ProfileRegion(ID, a1, a2, superRegion); - void *InsertPos; - RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, - InsertPos)); - - if (!R) { - R = A.Allocate<RegionTy>(); - new (R) RegionTy(a1, a2, superRegion); - Regions.InsertNode(R, InsertPos); - } - - return R; -} - -template <typename RegionTy, typename A1, typename A2> RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const MemRegion *superRegion) { llvm::FoldingSetNodeID ID; diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 217d628a129c7..5675cb2026f04 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -60,6 +60,7 @@ PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {} PathDiagnosticCallPiece::~PathDiagnosticCallPiece() {} PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {} +PathDiagnosticNotePiece::~PathDiagnosticNotePiece() {} void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, bool ShouldFlattenMacros) const { @@ -95,6 +96,7 @@ void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, } case PathDiagnosticPiece::Event: case PathDiagnosticPiece::ControlFlow: + case PathDiagnosticPiece::Note: Current.push_back(Piece); break; } @@ -211,6 +213,12 @@ void PathDiagnosticConsumer::HandlePathDiagnostic( const SourceManager &SMgr = D->path.front()->getLocation().getManager(); SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); + SmallString<128> buf; + llvm::raw_svector_ostream warning(buf); + warning << "warning: Path diagnostic report is not generated. Current " + << "output format does not support diagnostics that cross file " + << "boundaries. Refer to --analyzer-output for valid output " + << "formats\n"; while (!WorkList.empty()) { const PathPieces &path = *WorkList.pop_back_val(); @@ -222,19 +230,25 @@ void PathDiagnosticConsumer::HandlePathDiagnostic( if (FID.isInvalid()) { FID = SMgr.getFileID(L); - } else if (SMgr.getFileID(L) != FID) - return; // FIXME: Emit a warning? + } else if (SMgr.getFileID(L) != FID) { + llvm::errs() << warning.str(); + return; + } // Check the source ranges. ArrayRef<SourceRange> Ranges = piece->getRanges(); for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { SourceLocation L = SMgr.getExpansionLoc(I->getBegin()); - if (!L.isFileID() || SMgr.getFileID(L) != FID) - return; // FIXME: Emit a warning? + if (!L.isFileID() || SMgr.getFileID(L) != FID) { + llvm::errs() << warning.str(); + return; + } L = SMgr.getExpansionLoc(I->getEnd()); - if (!L.isFileID() || SMgr.getFileID(L) != FID) - return; // FIXME: Emit a warning? + if (!L.isFileID() || SMgr.getFileID(L) != FID) { + llvm::errs() << warning.str(); + return; + } } if (const PathDiagnosticCallPiece *call = @@ -342,15 +356,16 @@ static Optional<bool> comparePiece(const PathDiagnosticPiece &X, } switch (X.getKind()) { - case clang::ento::PathDiagnosticPiece::ControlFlow: + case PathDiagnosticPiece::ControlFlow: return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X), cast<PathDiagnosticControlFlowPiece>(Y)); - case clang::ento::PathDiagnosticPiece::Event: + case PathDiagnosticPiece::Event: + case PathDiagnosticPiece::Note: return None; - case clang::ento::PathDiagnosticPiece::Macro: + case PathDiagnosticPiece::Macro: return compareMacro(cast<PathDiagnosticMacroPiece>(X), cast<PathDiagnosticMacroPiece>(Y)); - case clang::ento::PathDiagnosticPiece::Call: + case PathDiagnosticPiece::Call: return compareCall(cast<PathDiagnosticCallPiece>(X), cast<PathDiagnosticCallPiece>(Y)); } @@ -1098,6 +1113,10 @@ void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { ID.Add(**I); } +void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticSpotPiece::Profile(ID); +} + void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { ID.Add(getLocation()); ID.AddString(BugType); diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 8ad931acdf7f5..c5263ee0e5ca0 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -19,7 +19,6 @@ #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" -#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" using namespace clang; @@ -282,6 +281,9 @@ static void ReportPiece(raw_ostream &o, ReportMacro(o, cast<PathDiagnosticMacroPiece>(P), FM, SM, LangOpts, indent, depth); break; + case PathDiagnosticPiece::Note: + // FIXME: Extend the plist format to support those. + break; } } @@ -298,40 +300,42 @@ void PlistDiagnostics::FlushDiagnosticsImpl( SM = &Diags.front()->path.front()->getLocation().getManager(); - for (std::vector<const PathDiagnostic*>::iterator DI = Diags.begin(), - DE = Diags.end(); DI != DE; ++DI) { + auto AddPieceFID = [&FM, &Fids, SM](const PathDiagnosticPiece *Piece)->void { + AddFID(FM, Fids, *SM, Piece->getLocation().asLocation()); + ArrayRef<SourceRange> Ranges = Piece->getRanges(); + for (const SourceRange &Range : Ranges) { + AddFID(FM, Fids, *SM, Range.getBegin()); + AddFID(FM, Fids, *SM, Range.getEnd()); + } + }; - const PathDiagnostic *D = *DI; + for (const PathDiagnostic *D : Diags) { SmallVector<const PathPieces *, 5> WorkList; WorkList.push_back(&D->path); while (!WorkList.empty()) { - const PathPieces &path = *WorkList.pop_back_val(); - - for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; - ++I) { - const PathDiagnosticPiece *piece = I->get(); - AddFID(FM, Fids, *SM, piece->getLocation().asLocation()); - ArrayRef<SourceRange> Ranges = piece->getRanges(); - for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), - E = Ranges.end(); I != E; ++I) { - AddFID(FM, Fids, *SM, I->getBegin()); - AddFID(FM, Fids, *SM, I->getEnd()); - } + const PathPieces &Path = *WorkList.pop_back_val(); + + for (const auto &Iter : Path) { + const PathDiagnosticPiece *Piece = Iter.get(); + AddPieceFID(Piece); + + if (const PathDiagnosticCallPiece *Call = + dyn_cast<PathDiagnosticCallPiece>(Piece)) { + if (IntrusiveRefCntPtr<PathDiagnosticEventPiece> + CallEnterWithin = Call->getCallEnterWithinCallerEvent()) + AddPieceFID(CallEnterWithin.get()); - if (const PathDiagnosticCallPiece *call = - dyn_cast<PathDiagnosticCallPiece>(piece)) { - IntrusiveRefCntPtr<PathDiagnosticEventPiece> - callEnterWithin = call->getCallEnterWithinCallerEvent(); - if (callEnterWithin) - AddFID(FM, Fids, *SM, callEnterWithin->getLocation().asLocation()); + if (IntrusiveRefCntPtr<PathDiagnosticEventPiece> + CallEnterEvent = Call->getCallEnterEvent()) + AddPieceFID(CallEnterEvent.get()); - WorkList.push_back(&call->path); + WorkList.push_back(&Call->path); } - else if (const PathDiagnosticMacroPiece *macro = - dyn_cast<PathDiagnosticMacroPiece>(piece)) { - WorkList.push_back(¯o->subPieces); + else if (const PathDiagnosticMacroPiece *Macro = + dyn_cast<PathDiagnosticMacroPiece>(Piece)) { + WorkList.push_back(&Macro->subPieces); } } } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index adda7af08db8e..03ace35965cba 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -527,32 +527,17 @@ bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { } bool ScanReachableSymbols::scan(const SymExpr *sym) { - bool wasVisited = !visited.insert(sym).second; - if (wasVisited) - return true; - - if (!visitor.VisitSymbol(sym)) - return false; + for (SymExpr::symbol_iterator SI = sym->symbol_begin(), + SE = sym->symbol_end(); + SI != SE; ++SI) { + bool wasVisited = !visited.insert(*SI).second; + if (wasVisited) + continue; - // TODO: should be rewritten using SymExpr::symbol_iterator. - switch (sym->getKind()) { - case SymExpr::SymbolRegionValueKind: - case SymExpr::SymbolConjuredKind: - case SymExpr::SymbolDerivedKind: - case SymExpr::SymbolExtentKind: - case SymExpr::SymbolMetadataKind: - break; - case SymExpr::SymbolCastKind: - return scan(cast<SymbolCast>(sym)->getOperand()); - case SymExpr::SymIntExprKind: - return scan(cast<SymIntExpr>(sym)->getLHS()); - case SymExpr::IntSymExprKind: - return scan(cast<IntSymExpr>(sym)->getRHS()); - case SymExpr::SymSymExprKind: { - const SymSymExpr *x = cast<SymSymExpr>(sym); - return scan(x->getLHS()) && scan(x->getRHS()); - } + if (!visitor.VisitSymbol(*SI)) + return false; } + return true; } diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 77b0ad32b6b7c..15073bb82b366 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -18,7 +18,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableSet.h" -#include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -28,22 +27,17 @@ using namespace ento; /// guarantee that from <= to. Note that Range is immutable, so as not /// to subvert RangeSet's immutability. namespace { -class Range : public std::pair<const llvm::APSInt*, - const llvm::APSInt*> { +class Range : public std::pair<const llvm::APSInt *, const llvm::APSInt *> { public: Range(const llvm::APSInt &from, const llvm::APSInt &to) - : std::pair<const llvm::APSInt*, const llvm::APSInt*>(&from, &to) { + : std::pair<const llvm::APSInt *, const llvm::APSInt *>(&from, &to) { assert(from <= to); } bool Includes(const llvm::APSInt &v) const { return *first <= v && v <= *second; } - const llvm::APSInt &From() const { - return *first; - } - const llvm::APSInt &To() const { - return *second; - } + const llvm::APSInt &From() const { return *first; } + const llvm::APSInt &To() const { return *second; } const llvm::APSInt *getConcreteValue() const { return &From() == &To() ? &From() : nullptr; } @@ -54,7 +48,6 @@ public: } }; - class RangeTrait : public llvm::ImutContainerInfo<Range> { public: // When comparing if one Range is less than another, we should compare @@ -62,8 +55,8 @@ public: // consistent (instead of comparing by pointer values) and can potentially // be used to speed up some of the operations in RangeSet. static inline bool isLess(key_type_ref lhs, key_type_ref rhs) { - return *lhs.first < *rhs.first || (!(*rhs.first < *lhs.first) && - *lhs.second < *rhs.second); + return *lhs.first < *rhs.first || + (!(*rhs.first < *lhs.first) && *lhs.second < *rhs.second); } }; @@ -97,7 +90,7 @@ public: /// Construct a new RangeSet representing '{ [from, to] }'. RangeSet(Factory &F, const llvm::APSInt &from, const llvm::APSInt &to) - : ranges(F.add(F.getEmptySet(), Range(from, to))) {} + : ranges(F.add(F.getEmptySet(), Range(from, to))) {} /// Profile - Generates a hash profile of this RangeSet for use /// by FoldingSet. @@ -106,16 +99,14 @@ public: /// getConcreteValue - If a symbol is contrained to equal a specific integer /// constant then this method returns that value. Otherwise, it returns /// NULL. - const llvm::APSInt* getConcreteValue() const { + const llvm::APSInt *getConcreteValue() const { return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : nullptr; } private: void IntersectInRange(BasicValueFactory &BV, Factory &F, - const llvm::APSInt &Lower, - const llvm::APSInt &Upper, - PrimRangeSet &newRanges, - PrimRangeSet::iterator &i, + const llvm::APSInt &Lower, const llvm::APSInt &Upper, + PrimRangeSet &newRanges, PrimRangeSet::iterator &i, PrimRangeSet::iterator &e) const { // There are six cases for each range R in the set: // 1. R is entirely before the intersection range. @@ -135,8 +126,8 @@ private: if (i->Includes(Lower)) { if (i->Includes(Upper)) { - newRanges = F.add(newRanges, Range(BV.getValue(Lower), - BV.getValue(Upper))); + newRanges = + F.add(newRanges, Range(BV.getValue(Lower), BV.getValue(Upper))); break; } else newRanges = F.add(newRanges, Range(BV.getValue(Lower), i->To())); @@ -244,8 +235,8 @@ public: // range is taken to wrap around. This is equivalent to taking the // intersection with the two ranges [Min, Upper] and [Lower, Max], // or, alternatively, /removing/ all integers between Upper and Lower. - RangeSet Intersect(BasicValueFactory &BV, Factory &F, - llvm::APSInt Lower, llvm::APSInt Upper) const { + RangeSet Intersect(BasicValueFactory &BV, Factory &F, llvm::APSInt Lower, + llvm::APSInt Upper) const { if (!pin(Lower, Upper)) return F.getEmptySet(); @@ -291,53 +282,54 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintRange, RangeSet)) namespace { -class RangeConstraintManager : public SimpleConstraintManager{ - RangeSet GetRange(ProgramStateRef state, SymbolRef sym); +class RangeConstraintManager : public SimpleConstraintManager { + RangeSet getRange(ProgramStateRef State, SymbolRef Sym); + public: - RangeConstraintManager(SubEngine *subengine, SValBuilder &SVB) - : SimpleConstraintManager(subengine, SVB) {} + RangeConstraintManager(SubEngine *SE, SValBuilder &SVB) + : SimpleConstraintManager(SE, SVB) {} - ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) override; + ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) override; - ProgramStateRef assumeSymEQ(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) override; + ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) override; - ProgramStateRef assumeSymLT(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) override; + ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) override; - ProgramStateRef assumeSymGT(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) override; + ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) override; - ProgramStateRef assumeSymGE(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) override; + ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) override; - ProgramStateRef assumeSymLE(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& Int, - const llvm::APSInt& Adjustment) override; + ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) override; ProgramStateRef assumeSymbolWithinInclusiveRange( - ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, - const llvm::APSInt &To, const llvm::APSInt &Adjustment) override; + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) override; ProgramStateRef assumeSymbolOutOfInclusiveRange( - ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, - const llvm::APSInt &To, const llvm::APSInt &Adjustment) override; + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) override; - const llvm::APSInt* getSymVal(ProgramStateRef St, - SymbolRef sym) const override; + const llvm::APSInt *getSymVal(ProgramStateRef St, + SymbolRef Sym) const override; ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; ProgramStateRef removeDeadBindings(ProgramStateRef St, - SymbolReaper& SymReaper) override; + SymbolReaper &SymReaper) override; - void print(ProgramStateRef St, raw_ostream &Out, - const char* nl, const char *sep) override; + void print(ProgramStateRef St, raw_ostream &Out, const char *nl, + const char *sep) override; private: RangeSet::Factory F; @@ -364,9 +356,9 @@ ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { return llvm::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder()); } -const llvm::APSInt* RangeConstraintManager::getSymVal(ProgramStateRef St, - SymbolRef sym) const { - const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(sym); +const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St, + SymbolRef Sym) const { + const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(Sym); return T ? T->getConcreteValue() : nullptr; } @@ -397,30 +389,32 @@ ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State, /// Scan all symbols referenced by the constraints. If the symbol is not alive /// as marked in LSymbols, mark it as dead in DSymbols. ProgramStateRef -RangeConstraintManager::removeDeadBindings(ProgramStateRef state, - SymbolReaper& SymReaper) { - - ConstraintRangeTy CR = state->get<ConstraintRange>(); - ConstraintRangeTy::Factory& CRFactory = state->get_context<ConstraintRange>(); +RangeConstraintManager::removeDeadBindings(ProgramStateRef State, + SymbolReaper &SymReaper) { + bool Changed = false; + ConstraintRangeTy CR = State->get<ConstraintRange>(); + ConstraintRangeTy::Factory &CRFactory = State->get_context<ConstraintRange>(); for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) { - SymbolRef sym = I.getKey(); - if (SymReaper.maybeDead(sym)) - CR = CRFactory.remove(CR, sym); + SymbolRef Sym = I.getKey(); + if (SymReaper.maybeDead(Sym)) { + Changed = true; + CR = CRFactory.remove(CR, Sym); + } } - return state->set<ConstraintRange>(CR); + return Changed ? State->set<ConstraintRange>(CR) : State; } -RangeSet -RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) { - if (ConstraintRangeTy::data_type* V = state->get<ConstraintRange>(sym)) +RangeSet RangeConstraintManager::getRange(ProgramStateRef State, + SymbolRef Sym) { + if (ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym)) return *V; // Lazily generate a new RangeSet representing all possible values for the // given symbol type. BasicValueFactory &BV = getBasicVals(); - QualType T = sym->getType(); + QualType T = Sym->getType(); RangeSet Result(F, BV.getMinValue(T), BV.getMaxValue(T)); @@ -428,7 +422,7 @@ RangeConstraintManager::GetRange(ProgramStateRef state, SymbolRef sym) { if (T->isReferenceType()) { APSIntType IntType = BV.getAPSIntType(T); Result = Result.Intersect(BV, F, ++IntType.getZeroValue(), - --IntType.getZeroValue()); + --IntType.getZeroValue()); } return Result; @@ -462,7 +456,7 @@ RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym, // [Int-Adjustment+1, Int-Adjustment-1] // Notice that the lower bound is greater than the upper bound. - RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, Upper, Lower); + RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, Upper, Lower); return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); } @@ -477,7 +471,7 @@ RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym, // [Int-Adjustment, Int-Adjustment] llvm::APSInt AdjInt = AdjustmentType.convert(Int) - Adjustment; - RangeSet New = GetRange(St, Sym).Intersect(getBasicVals(), F, AdjInt, AdjInt); + RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, AdjInt, AdjInt); return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); } @@ -493,7 +487,7 @@ RangeSet RangeConstraintManager::getSymLTRange(ProgramStateRef St, case APSIntType::RTR_Within: break; case APSIntType::RTR_Above: - return GetRange(St, Sym); + return getRange(St, Sym); } // Special case for Int == Min. This is always false. @@ -506,7 +500,7 @@ RangeSet RangeConstraintManager::getSymLTRange(ProgramStateRef St, llvm::APSInt Upper = ComparisonVal - Adjustment; --Upper; - return GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); + return getRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); } ProgramStateRef @@ -517,15 +511,15 @@ RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym, return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); } -RangeSet -RangeConstraintManager::getSymGTRange(ProgramStateRef St, SymbolRef Sym, - const llvm::APSInt &Int, - const llvm::APSInt &Adjustment) { +RangeSet RangeConstraintManager::getSymGTRange(ProgramStateRef St, + SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: - return GetRange(St, Sym); + return getRange(St, Sym); case APSIntType::RTR_Within: break; case APSIntType::RTR_Above: @@ -542,7 +536,7 @@ RangeConstraintManager::getSymGTRange(ProgramStateRef St, SymbolRef Sym, llvm::APSInt Upper = Max - Adjustment; ++Lower; - return GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); + return getRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); } ProgramStateRef @@ -553,15 +547,15 @@ RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym, return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); } -RangeSet -RangeConstraintManager::getSymGERange(ProgramStateRef St, SymbolRef Sym, - const llvm::APSInt &Int, - const llvm::APSInt &Adjustment) { +RangeSet RangeConstraintManager::getSymGERange(ProgramStateRef St, + SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); switch (AdjustmentType.testInRange(Int, true)) { case APSIntType::RTR_Below: - return GetRange(St, Sym); + return getRange(St, Sym); case APSIntType::RTR_Within: break; case APSIntType::RTR_Above: @@ -572,13 +566,13 @@ RangeConstraintManager::getSymGERange(ProgramStateRef St, SymbolRef Sym, llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); llvm::APSInt Min = AdjustmentType.getMinValue(); if (ComparisonVal == Min) - return GetRange(St, Sym); + return getRange(St, Sym); llvm::APSInt Max = AdjustmentType.getMaxValue(); llvm::APSInt Lower = ComparisonVal - Adjustment; llvm::APSInt Upper = Max - Adjustment; - return GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); + return getRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); } ProgramStateRef @@ -589,10 +583,9 @@ RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym, return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); } -RangeSet -RangeConstraintManager::getSymLERange(const RangeSet &RS, - const llvm::APSInt &Int, - const llvm::APSInt &Adjustment) { +RangeSet RangeConstraintManager::getSymLERange(const RangeSet &RS, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); switch (AdjustmentType.testInRange(Int, true)) { @@ -617,10 +610,10 @@ RangeConstraintManager::getSymLERange(const RangeSet &RS, return RS.Intersect(getBasicVals(), F, Lower, Upper); } -RangeSet -RangeConstraintManager::getSymLERange(ProgramStateRef St, SymbolRef Sym, - const llvm::APSInt &Int, - const llvm::APSInt &Adjustment) { +RangeSet RangeConstraintManager::getSymLERange(ProgramStateRef St, + SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { // Before we do any real work, see if the value can even show up. APSIntType AdjustmentType(Adjustment); switch (AdjustmentType.testInRange(Int, true)) { @@ -629,20 +622,20 @@ RangeConstraintManager::getSymLERange(ProgramStateRef St, SymbolRef Sym, case APSIntType::RTR_Within: break; case APSIntType::RTR_Above: - return GetRange(St, Sym); + return getRange(St, Sym); } // Special case for Int == Max. This is always feasible. llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); llvm::APSInt Max = AdjustmentType.getMaxValue(); if (ComparisonVal == Max) - return GetRange(St, Sym); + return getRange(St, Sym); llvm::APSInt Min = AdjustmentType.getMinValue(); llvm::APSInt Lower = Min - Adjustment; llvm::APSInt Upper = ComparisonVal - Adjustment; - return GetRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); + return getRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); } ProgramStateRef @@ -653,8 +646,7 @@ RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym, return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); } -ProgramStateRef -RangeConstraintManager::assumeSymbolWithinInclusiveRange( +ProgramStateRef RangeConstraintManager::assumeSymbolWithinInclusiveRange( ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, const llvm::APSInt &Adjustment) { RangeSet New = getSymGERange(State, Sym, From, Adjustment); @@ -664,8 +656,7 @@ RangeConstraintManager::assumeSymbolWithinInclusiveRange( return New.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, New); } -ProgramStateRef -RangeConstraintManager::assumeSymbolOutOfInclusiveRange( +ProgramStateRef RangeConstraintManager::assumeSymbolOutOfInclusiveRange( ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, const llvm::APSInt &Adjustment) { RangeSet RangeLT = getSymLTRange(State, Sym, From, Adjustment); @@ -679,7 +670,7 @@ RangeConstraintManager::assumeSymbolOutOfInclusiveRange( //===------------------------------------------------------------------------===/ void RangeConstraintManager::print(ProgramStateRef St, raw_ostream &Out, - const char* nl, const char *sep) { + const char *nl, const char *sep) { ConstraintRangeTy Ranges = St->get<ConstraintRange>(); @@ -689,7 +680,8 @@ void RangeConstraintManager::print(ProgramStateRef St, raw_ostream &Out, } Out << nl << sep << "Ranges of symbol values:"; - for (ConstraintRangeTy::iterator I=Ranges.begin(), E=Ranges.end(); I!=E; ++I){ + for (ConstraintRangeTy::iterator I = Ranges.begin(), E = Ranges.end(); I != E; + ++I) { Out << nl << ' ' << I.getKey() << " : "; I.getData().print(Out); } diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index 0d173c4644816..15ca2c14f9440 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -26,7 +26,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" -#include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/raw_ostream.h" @@ -1675,7 +1674,8 @@ RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindingsConstRef B, // Lazy bindings are usually handled through getExistingLazyBinding(). // We should unify these two code paths at some point. - if (val.getAs<nonloc::LazyCompoundVal>()) + if (val.getAs<nonloc::LazyCompoundVal>() || + val.getAs<nonloc::CompoundVal>()) return val; llvm_unreachable("Unknown default value"); @@ -2073,11 +2073,10 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B, if (Init.getAs<nonloc::LazyCompoundVal>()) return bindAggregate(B, R, Init); - // Remaining case: explicit compound values. - if (Init.isUnknown()) - return setImplicitDefaultValue(B, R, ElementTy); + return bindAggregate(B, R, UnknownVal()); + // Remaining case: explicit compound values. const nonloc::CompoundVal& CV = Init.castAs<nonloc::CompoundVal>(); nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); uint64_t i = 0; diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index 72bcdd9ecb06b..10b0858b84887 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -36,8 +36,11 @@ DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) { if (type->isIntegralOrEnumerationType()) return makeIntVal(0, type); + if (type->isArrayType() || type->isRecordType() || type->isVectorType() || + type->isAnyComplexType()) + return makeCompoundVal(type, BasicVals.getEmptySValList()); + // FIXME: Handle floats. - // FIXME: Handle structs. return UnknownVal(); } @@ -182,11 +185,12 @@ SValBuilder::getConjuredHeapSymbolVal(const Expr *E, DefinedSVal SValBuilder::getMetadataSymbolVal(const void *symbolTag, const MemRegion *region, const Expr *expr, QualType type, + const LocationContext *LCtx, unsigned count) { assert(SymbolManager::canSymbolicate(type) && "Invalid metadata symbol type"); SymbolRef sym = - SymMgr.getMetadataSymbol(region, expr, type, count, symbolTag); + SymMgr.getMetadataSymbol(region, expr, type, LCtx, count, symbolTag); if (Loc::isLocType(type)) return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); @@ -213,6 +217,10 @@ SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, return nonloc::SymbolVal(sym); } +DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl* DD) { + return nonloc::PointerToMember(DD); +} + DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl *func) { return loc::MemRegionVal(MemMgr.getFunctionCodeRegion(func)); } diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index a30beed688b7a..9f2af3ffa7097 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -16,6 +16,7 @@ #include "clang/AST/ExprObjC.h" #include "clang/Basic/IdentifierTable.h" #include "llvm/Support/raw_ostream.h" +#include "clang/AST/DeclCXX.h" using namespace clang; using namespace ento; using llvm::APSInt; @@ -56,6 +57,10 @@ const FunctionDecl *SVal::getAsFunctionDecl() const { return FD; } + if (auto X = getAs<nonloc::PointerToMember>()) { + if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(X->getDecl())) + return MD; + } return nullptr; } @@ -155,6 +160,20 @@ const TypedValueRegion *nonloc::LazyCompoundVal::getRegion() const { return static_cast<const LazyCompoundValData*>(Data)->getRegion(); } +const DeclaratorDecl *nonloc::PointerToMember::getDecl() const { + const auto PTMD = this->getPTMData(); + if (PTMD.isNull()) + return nullptr; + + const DeclaratorDecl *DD = nullptr; + if (PTMD.is<const DeclaratorDecl *>()) + DD = PTMD.get<const DeclaratorDecl *>(); + else + DD = PTMD.get<const PointerToMemberData *>()->getDeclaratorDecl(); + + return DD; +} + //===----------------------------------------------------------------------===// // Other Iterators. //===----------------------------------------------------------------------===// @@ -167,6 +186,20 @@ nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { return getValue()->end(); } +nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const { + const PTMDataType PTMD = getPTMData(); + if (PTMD.is<const DeclaratorDecl *>()) + return nonloc::PointerToMember::iterator(); + return PTMD.get<const PointerToMemberData *>()->begin(); +} + +nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const { + const PTMDataType PTMD = getPTMData(); + if (PTMD.is<const DeclaratorDecl *>()) + return nonloc::PointerToMember::iterator(); + return PTMD.get<const PointerToMemberData *>()->end(); +} + //===----------------------------------------------------------------------===// // Useful predicates. //===----------------------------------------------------------------------===// @@ -299,6 +332,26 @@ void NonLoc::dumpToStream(raw_ostream &os) const { << '}'; break; } + case nonloc::PointerToMemberKind: { + os << "pointerToMember{"; + const nonloc::PointerToMember &CastRes = + castAs<nonloc::PointerToMember>(); + if (CastRes.getDecl()) + os << "|" << CastRes.getDecl()->getQualifiedNameAsString() << "|"; + bool first = true; + for (const auto &I : CastRes) { + if (first) { + os << ' '; first = false; + } + else + os << ", "; + + os << (*I).getType().getAsString(); + } + + os << '}'; + break; + } default: assert (false && "Pretty-printed not implemented for this NonLoc."); break; diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp index 4051242434ec9..0e512ff808617 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -30,22 +30,22 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const { if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) { switch (SIE->getOpcode()) { - // We don't reason yet about bitwise-constraints on symbolic values. - case BO_And: - case BO_Or: - case BO_Xor: - return false; - // We don't reason yet about these arithmetic constraints on - // symbolic values. - case BO_Mul: - case BO_Div: - case BO_Rem: - case BO_Shl: - case BO_Shr: - return false; - // All other cases. - default: - return true; + // We don't reason yet about bitwise-constraints on symbolic values. + case BO_And: + case BO_Or: + case BO_Xor: + return false; + // We don't reason yet about these arithmetic constraints on + // symbolic values. + case BO_Mul: + case BO_Div: + case BO_Rem: + case BO_Shl: + case BO_Shr: + return false; + // All other cases. + default: + return true; } } @@ -65,12 +65,12 @@ bool SimpleConstraintManager::canReasonAbout(SVal X) const { return true; } -ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, - DefinedSVal Cond, - bool Assumption) { +ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State, + DefinedSVal Cond, + bool Assumption) { // If we have a Loc value, cast it to a bool NonLoc first. if (Optional<Loc> LV = Cond.getAs<Loc>()) { - SValBuilder &SVB = state->getStateManager().getSValBuilder(); + SValBuilder &SVB = State->getStateManager().getSValBuilder(); QualType T; const MemRegion *MR = LV->getAsRegion(); if (const TypedRegion *TR = dyn_cast_or_null<TypedRegion>(MR)) @@ -81,19 +81,17 @@ ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, Cond = SVB.evalCast(*LV, SVB.getContext().BoolTy, T).castAs<DefinedSVal>(); } - return assume(state, Cond.castAs<NonLoc>(), Assumption); + return assume(State, Cond.castAs<NonLoc>(), Assumption); } -ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef state, - NonLoc cond, - bool assumption) { - state = assumeAux(state, cond, assumption); +ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State, + NonLoc Cond, bool Assumption) { + State = assumeAux(State, Cond, Assumption); if (NotifyAssumeClients && SU) - return SU->processAssume(state, cond, assumption); - return state; + return SU->processAssume(State, Cond, Assumption); + return State; } - ProgramStateRef SimpleConstraintManager::assumeAuxForSymbol(ProgramStateRef State, SymbolRef Sym, bool Assumption) { @@ -111,16 +109,16 @@ SimpleConstraintManager::assumeAuxForSymbol(ProgramStateRef State, return assumeSymEQ(State, Sym, zero, zero); } -ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, - NonLoc Cond, - bool Assumption) { +ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State, + NonLoc Cond, + bool Assumption) { // We cannot reason about SymSymExprs, and can only reason about some // SymIntExprs. if (!canReasonAbout(Cond)) { // Just add the constraint to the expression without trying to simplify. - SymbolRef sym = Cond.getAsSymExpr(); - return assumeAuxForSymbol(state, sym, Assumption); + SymbolRef Sym = Cond.getAsSymExpr(); + return assumeAuxForSymbol(State, Sym, Assumption); } switch (Cond.getSubKind()) { @@ -129,26 +127,26 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, case nonloc::SymbolValKind: { nonloc::SymbolVal SV = Cond.castAs<nonloc::SymbolVal>(); - SymbolRef sym = SV.getSymbol(); - assert(sym); + SymbolRef Sym = SV.getSymbol(); + assert(Sym); // Handle SymbolData. if (!SV.isExpression()) { - return assumeAuxForSymbol(state, sym, Assumption); + return assumeAuxForSymbol(State, Sym, Assumption); - // Handle symbolic expression. - } else if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(sym)) { + // Handle symbolic expression. + } else if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) { // We can only simplify expressions whose RHS is an integer. - BinaryOperator::Opcode op = SE->getOpcode(); - if (BinaryOperator::isComparisonOp(op)) { + BinaryOperator::Opcode Op = SE->getOpcode(); + if (BinaryOperator::isComparisonOp(Op)) { if (!Assumption) - op = BinaryOperator::negateComparisonOp(op); + Op = BinaryOperator::negateComparisonOp(Op); - return assumeSymRel(state, SE->getLHS(), op, SE->getRHS()); + return assumeSymRel(State, SE->getLHS(), Op, SE->getRHS()); } - } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(sym)) { + } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) { // Translate "a != b" to "(b - a) != 0". // We invert the order of the operands as a heuristic for how loop // conditions are usually written ("begin != end") as compared to length @@ -163,34 +161,40 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef state, assert(Loc::isLocType(SSE->getLHS()->getType())); assert(Loc::isLocType(SSE->getRHS()->getType())); QualType DiffTy = SymMgr.getContext().getPointerDiffType(); - SymbolRef Subtraction = SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, - SSE->getLHS(), DiffTy); + SymbolRef Subtraction = + SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy); const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy); Op = BinaryOperator::reverseComparisonOp(Op); if (!Assumption) Op = BinaryOperator::negateComparisonOp(Op); - return assumeSymRel(state, Subtraction, Op, Zero); + return assumeSymRel(State, Subtraction, Op, Zero); } // If we get here, there's nothing else we can do but treat the symbol as // opaque. - return assumeAuxForSymbol(state, sym, Assumption); + return assumeAuxForSymbol(State, Sym, Assumption); } case nonloc::ConcreteIntKind: { bool b = Cond.castAs<nonloc::ConcreteInt>().getValue() != 0; bool isFeasible = b ? Assumption : !Assumption; - return isFeasible ? state : nullptr; + return isFeasible ? State : nullptr; + } + + case nonloc::PointerToMemberKind: { + bool IsNull = !Cond.castAs<nonloc::PointerToMember>().isNullMemberPointer(); + bool IsFeasible = IsNull ? Assumption : !Assumption; + return IsFeasible ? State : nullptr; } case nonloc::LocAsIntegerKind: - return assume(state, Cond.castAs<nonloc::LocAsInteger>().getLoc(), + return assume(State, Cond.castAs<nonloc::LocAsInteger>().getLoc(), Assumption); } // end switch } -ProgramStateRef SimpleConstraintManager::assumeWithinInclusiveRange( +ProgramStateRef SimpleConstraintManager::assumeInclusiveRange( ProgramStateRef State, NonLoc Value, const llvm::APSInt &From, const llvm::APSInt &To, bool InRange) { @@ -207,7 +211,7 @@ ProgramStateRef SimpleConstraintManager::assumeWithinInclusiveRange( switch (Value.getSubKind()) { default: - llvm_unreachable("'assumeWithinInclusiveRange' is not implemented" + llvm_unreachable("'assumeInclusiveRange' is not implemented" "for this NonLoc"); case nonloc::LocAsIntegerKind: @@ -243,13 +247,26 @@ static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment) { } } -ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state, - const SymExpr *LHS, - BinaryOperator::Opcode op, - const llvm::APSInt& Int) { - assert(BinaryOperator::isComparisonOp(op) && +ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef State, + const SymExpr *LHS, + BinaryOperator::Opcode Op, + const llvm::APSInt &Int) { + assert(BinaryOperator::isComparisonOp(Op) && "Non-comparison ops should be rewritten as comparisons to zero."); + SymbolRef Sym = LHS; + + // Simplification: translate an assume of a constraint of the form + // "(exp comparison_op expr) != 0" to true into an assume of + // "exp comparison_op expr" to true. (And similarly, an assume of the form + // "(exp comparison_op expr) == 0" to true into an assume of + // "exp comparison_op expr" to false.) + if (Int == 0 && (Op == BO_EQ || Op == BO_NE)) { + if (const BinarySymExpr *SE = dyn_cast<BinarySymExpr>(Sym)) + if (BinaryOperator::isComparisonOp(SE->getOpcode())) + return assume(State, nonloc::SymbolVal(Sym), (Op == BO_NE ? true : false)); + } + // Get the type used for calculating wraparound. BasicValueFactory &BVF = getBasicVals(); APSIntType WraparoundType = BVF.getAPSIntType(LHS->getType()); @@ -261,7 +278,6 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state, // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to // the subclasses of SimpleConstraintManager to handle the adjustment. - SymbolRef Sym = LHS; llvm::APSInt Adjustment = WraparoundType.getZeroValue(); computeAdjustment(Sym, Adjustment); @@ -274,36 +290,33 @@ ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef state, ComparisonType.isUnsigned() && !WraparoundType.isUnsigned()) Adjustment.setIsSigned(false); - switch (op) { + switch (Op) { default: llvm_unreachable("invalid operation not caught by assertion above"); case BO_EQ: - return assumeSymEQ(state, Sym, ConvertedInt, Adjustment); + return assumeSymEQ(State, Sym, ConvertedInt, Adjustment); case BO_NE: - return assumeSymNE(state, Sym, ConvertedInt, Adjustment); + return assumeSymNE(State, Sym, ConvertedInt, Adjustment); case BO_GT: - return assumeSymGT(state, Sym, ConvertedInt, Adjustment); + return assumeSymGT(State, Sym, ConvertedInt, Adjustment); case BO_GE: - return assumeSymGE(state, Sym, ConvertedInt, Adjustment); + return assumeSymGE(State, Sym, ConvertedInt, Adjustment); case BO_LT: - return assumeSymLT(state, Sym, ConvertedInt, Adjustment); + return assumeSymLT(State, Sym, ConvertedInt, Adjustment); case BO_LE: - return assumeSymLE(state, Sym, ConvertedInt, Adjustment); + return assumeSymLE(State, Sym, ConvertedInt, Adjustment); } // end switch } -ProgramStateRef -SimpleConstraintManager::assumeSymWithinInclusiveRange(ProgramStateRef State, - SymbolRef Sym, - const llvm::APSInt &From, - const llvm::APSInt &To, - bool InRange) { +ProgramStateRef SimpleConstraintManager::assumeSymWithinInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, bool InRange) { // Get the type used for calculating wraparound. BasicValueFactory &BVF = getBasicVals(); APSIntType WraparoundType = BVF.getAPSIntType(Sym->getType()); diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h index b26bc9486110f..1128e775b3205 100644 --- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h +++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.h @@ -24,30 +24,28 @@ namespace ento { class SimpleConstraintManager : public ConstraintManager { SubEngine *SU; SValBuilder &SVB; + public: - SimpleConstraintManager(SubEngine *subengine, SValBuilder &SB) - : SU(subengine), SVB(SB) {} + SimpleConstraintManager(SubEngine *SE, SValBuilder &SB) : SU(SE), SVB(SB) {} ~SimpleConstraintManager() override; //===------------------------------------------------------------------===// // Common implementation for the interface provided by ConstraintManager. //===------------------------------------------------------------------===// - ProgramStateRef assume(ProgramStateRef state, DefinedSVal Cond, - bool Assumption) override; + ProgramStateRef assume(ProgramStateRef State, DefinedSVal Cond, + bool Assumption) override; - ProgramStateRef assume(ProgramStateRef state, NonLoc Cond, bool Assumption); + ProgramStateRef assume(ProgramStateRef State, NonLoc Cond, bool Assumption); - ProgramStateRef assumeWithinInclusiveRange(ProgramStateRef State, - NonLoc Value, - const llvm::APSInt &From, - const llvm::APSInt &To, - bool InRange) override; + ProgramStateRef assumeInclusiveRange(ProgramStateRef State, NonLoc Value, + const llvm::APSInt &From, + const llvm::APSInt &To, + bool InRange) override; - ProgramStateRef assumeSymRel(ProgramStateRef state, - const SymExpr *LHS, - BinaryOperator::Opcode op, - const llvm::APSInt& Int); + ProgramStateRef assumeSymRel(ProgramStateRef State, const SymExpr *LHS, + BinaryOperator::Opcode Op, + const llvm::APSInt &Int); ProgramStateRef assumeSymWithinInclusiveRange(ProgramStateRef State, SymbolRef Sym, @@ -55,47 +53,45 @@ public: const llvm::APSInt &To, bool InRange); - protected: - //===------------------------------------------------------------------===// // Interface that subclasses must implement. //===------------------------------------------------------------------===// - // Each of these is of the form "$sym+Adj <> V", where "<>" is the comparison + // Each of these is of the form "$Sym+Adj <> V", where "<>" is the comparison // operation for the method being invoked. - virtual ProgramStateRef assumeSymNE(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment) = 0; + virtual ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; - virtual ProgramStateRef assumeSymEQ(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment) = 0; + virtual ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; - virtual ProgramStateRef assumeSymLT(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment) = 0; + virtual ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; - virtual ProgramStateRef assumeSymGT(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment) = 0; + virtual ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; - virtual ProgramStateRef assumeSymLE(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment) = 0; - - virtual ProgramStateRef assumeSymGE(ProgramStateRef state, SymbolRef sym, - const llvm::APSInt& V, - const llvm::APSInt& Adjustment) = 0; + virtual ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; + virtual ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) = 0; virtual ProgramStateRef assumeSymbolWithinInclusiveRange( ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0; virtual ProgramStateRef assumeSymbolOutOfInclusiveRange( - ProgramStateRef state, SymbolRef Sym, const llvm::APSInt &From, + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0; + //===------------------------------------------------------------------===// // Internal implementation. //===------------------------------------------------------------------===// @@ -105,13 +101,11 @@ protected: bool canReasonAbout(SVal X) const override; - ProgramStateRef assumeAux(ProgramStateRef state, - NonLoc Cond, - bool Assumption); + ProgramStateRef assumeAux(ProgramStateRef State, NonLoc Cond, + bool Assumption); - ProgramStateRef assumeAuxForSymbol(ProgramStateRef State, - SymbolRef Sym, - bool Assumption); + ProgramStateRef assumeAuxForSymbol(ProgramStateRef State, SymbolRef Sym, + bool Assumption); }; } // end GR namespace diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 72b852b2e21df..28b43dd566d5c 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -69,6 +69,9 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { bool isLocType = Loc::isLocType(castTy); + if (val.getAs<nonloc::PointerToMember>()) + return val; + if (Optional<nonloc::LocAsInteger> LI = val.getAs<nonloc::LocAsInteger>()) { if (isLocType) return LI->getLoc(); @@ -335,6 +338,21 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, switch (lhs.getSubKind()) { default: return makeSymExprValNN(state, op, lhs, rhs, resultTy); + case nonloc::PointerToMemberKind: { + assert(rhs.getSubKind() == nonloc::PointerToMemberKind && + "Both SVals should have pointer-to-member-type"); + auto LPTM = lhs.castAs<nonloc::PointerToMember>(), + RPTM = rhs.castAs<nonloc::PointerToMember>(); + auto LPTMD = LPTM.getPTMData(), RPTMD = RPTM.getPTMData(); + switch (op) { + case BO_EQ: + return makeTruthVal(LPTMD == RPTMD, resultTy); + case BO_NE: + return makeTruthVal(LPTMD != RPTMD, resultTy); + default: + return UnknownVal(); + } + } case nonloc::LocAsIntegerKind: { Loc lhsL = lhs.castAs<nonloc::LocAsInteger>().getLoc(); switch (rhs.getSubKind()) { @@ -753,6 +771,12 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, // Note, heap base symbolic regions are assumed to not alias with // each other; for example, we assume that malloc returns different address // on each invocation. + // FIXME: ObjC object pointers always reside on the heap, but currently + // we treat their memory space as unknown, because symbolic pointers + // to ObjC objects may alias. There should be a way to construct + // possibly-aliasing heap-based regions. For instance, MacOSXApiChecker + // guesses memory space for ObjC object pointers manually instead of + // relying on us. if (LeftBase != RightBase && ((!isa<SymbolicRegion>(LeftBase) && !isa<SymbolicRegion>(RightBase)) || (isa<HeapSpaceRegion>(LeftMS) || isa<HeapSpaceRegion>(RightMS))) ){ @@ -857,6 +881,23 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, NonLoc rhs, QualType resultTy) { + if (op >= BO_PtrMemD && op <= BO_PtrMemI) { + if (auto PTMSV = rhs.getAs<nonloc::PointerToMember>()) { + if (PTMSV->isNullMemberPointer()) + return UndefinedVal(); + if (const FieldDecl *FD = PTMSV->getDeclAs<FieldDecl>()) { + SVal Result = lhs; + + for (const auto &I : *PTMSV) + Result = StateMgr.getStoreManager().evalDerivedToBase( + Result, I->getType(),I->isVirtual()); + return state->getLValue(FD, Result); + } + } + + return rhs; + } + assert(!BinaryOperator::isComparisonOp(op) && "arguments to comparison ops must be of the same type"); diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index de29f0eedd127..aca6e3b6255b8 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -292,7 +292,7 @@ static const CXXRecordDecl *getCXXRecordType(const MemRegion *MR) { return nullptr; } -SVal StoreManager::evalDynamicCast(SVal Base, QualType TargetType, +SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType, bool &Failed) { Failed = false; diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index b8b4af1179e52..4be85661b645f 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -85,7 +85,8 @@ void SymbolMetadata::dumpToStream(raw_ostream &os) const { void SymbolData::anchor() { } void SymbolRegionValue::dumpToStream(raw_ostream &os) const { - os << "reg_$" << getSymbolID() << "<" << R << ">"; + os << "reg_$" << getSymbolID() + << '<' << getType().getAsString() << ' ' << R << '>'; } bool SymExpr::symbol_iterator::operator==(const symbol_iterator &X) const { @@ -216,17 +217,18 @@ SymbolManager::getExtentSymbol(const SubRegion *R) { return cast<SymbolExtent>(SD); } -const SymbolMetadata* +const SymbolMetadata * SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt *S, QualType T, + const LocationContext *LCtx, unsigned Count, const void *SymbolTag) { llvm::FoldingSetNodeID profile; - SymbolMetadata::Profile(profile, R, S, T, Count, SymbolTag); + SymbolMetadata::Profile(profile, R, S, T, LCtx, Count, SymbolTag); void *InsertPos; SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); if (!SD) { SD = (SymExpr*) BPAlloc.Allocate<SymbolMetadata>(); - new (SD) SymbolMetadata(SymbolCounter, R, S, T, Count, SymbolTag); + new (SD) SymbolMetadata(SymbolCounter, R, S, T, LCtx, Count, SymbolTag); DataSet.InsertNode(SD, InsertPos); ++SymbolCounter; } |