From: George Karpenkov Date: Fri, 2 Feb 2018 02:19:43 +0000 (+0000) Subject: [analyzer] Expose return statement from CallExit program point X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5885c75b8cfb19ed8ab6b4b7c4cec8fc5207cf13;p=clang [analyzer] Expose return statement from CallExit program point If the return statement is stored, we might as well allow querying against it. Also fix the bug where the return statement is not stored if there is no return value. This change un-merges two ExplodedNodes during call exit when the state is otherwise identical - the CallExitBegin node itself and the "Bind Return Value"-tagged node. And expose the return statement through getStatement helper function. Differential Revision: https://reviews.llvm.org/D42130 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@324052 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Analysis/ProgramPoint.h b/include/clang/Analysis/ProgramPoint.h index 8a72399069..f7bd1be0da 100644 --- a/include/clang/Analysis/ProgramPoint.h +++ b/include/clang/Analysis/ProgramPoint.h @@ -641,6 +641,10 @@ public: CallExitBegin(const StackFrameContext *L, const ReturnStmt *RS) : ProgramPoint(RS, CallExitBeginKind, L, nullptr) { } + const ReturnStmt *getReturnStmt() const { + return static_cast(getData1()); + } + private: friend class ProgramPoint; CallExitBegin() = default; diff --git a/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp index e2a35c0426..dc9dd5bd06 100644 --- a/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp @@ -37,7 +37,9 @@ class AnalysisOrderChecker check::PostCall, check::NewAllocator, check::Bind, - check::RegionChanges> { + check::RegionChanges, + check::LiveSymbols> { + bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const { return Opts.getBooleanOption("*", false, this) || Opts.getBooleanOption(CallbackName, false, this); @@ -118,6 +120,11 @@ public: llvm::errs() << "Bind\n"; } + void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SymReaper) const { + if (isCallbackEnabled(State, "LiveSymbols")) + llvm::errs() << "LiveSymbols\n"; + } + ProgramStateRef checkRegionChanges(ProgramStateRef State, const InvalidatedSymbols *Invalidated, diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index a06c311590..115b5a1c29 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -307,10 +307,7 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { const ReturnStmt *RS = nullptr; if (!L.getSrc()->empty()) { if (Optional LastStmt = L.getSrc()->back().getAs()) { - if ((RS = dyn_cast(LastStmt->getStmt()))) { - if (!RS->getRetValue()) - RS = nullptr; - } + RS = dyn_cast(LastStmt->getStmt()); } } diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp index 2b04124162..3d4b377627 100644 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -742,6 +742,8 @@ const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) { return CEE->getCalleeContext()->getCallSite(); if (Optional PIPP = P.getAs()) return PIPP->getInitializer()->getInit(); + if (Optional CEB = P.getAs()) + return CEB->getReturnStmt(); return nullptr; } diff --git a/test/Analysis/return-stmt-merge.cpp b/test/Analysis/return-stmt-merge.cpp new file mode 100644 index 0000000000..a6bacc5145 --- /dev/null +++ b/test/Analysis/return-stmt-merge.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.AnalysisOrder,debug.ExprInspection -analyzer-config debug.AnalysisOrder:PreCall=true,debug.AnalysisOrder:PostCall=true,debug.AnalysisOrder:LiveSymbols=true %s 2>&1 | FileCheck %s + +// This test ensures that check::LiveSymbols is called as many times on the +// path through the second "return" as it is through the first "return" +// (three), and therefore the two paths were not merged prematurely before the +// respective return statement is evaluated. +// The paths would still be merged later, so we'd have only one post-call for +// foo(), but it is incorrect to merge them in the middle of evaluating two +// different statements. +int coin(); + +void foo() { + int x = coin(); + if (x > 0) + return; + else + return; +} + +void bar() { + foo(); +} + +// CHECK: LiveSymbols +// CHECK-NEXT: LiveSymbols +// CHECK-NEXT: PreCall (foo) +// CHECK-NEXT: LiveSymbols +// CHECK-NEXT: LiveSymbols +// CHECK-NEXT: PreCall (coin) +// CHECK-NEXT: PostCall (coin) +// CHECK-NEXT: LiveSymbols +// CHECK-NEXT: LiveSymbols +// CHECK-NEXT: LiveSymbols +// CHECK-NEXT: PostCall (foo) +// CHECK-NEXT: LiveSymbols +// CHECK-NEXT: LiveSymbols +// CHECK-NEXT: LiveSymbols