Index: cfe/trunk/include/clang/Analysis/ProgramPoint.h =================================================================== --- cfe/trunk/include/clang/Analysis/ProgramPoint.h +++ cfe/trunk/include/clang/Analysis/ProgramPoint.h @@ -641,6 +641,10 @@ 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; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp @@ -37,7 +37,9 @@ 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 @@ 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, Index: cfe/trunk/lib/StaticAnalyzer/Core/CoreEngine.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -307,10 +307,7 @@ 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()); } } Index: cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -742,6 +742,8 @@ return CEE->getCalleeContext()->getCallSite(); if (Optional PIPP = P.getAs()) return PIPP->getInitializer()->getInit(); + if (Optional CEB = P.getAs()) + return CEB->getReturnStmt(); return nullptr; } Index: cfe/trunk/test/Analysis/return-stmt-merge.cpp =================================================================== --- cfe/trunk/test/Analysis/return-stmt-merge.cpp +++ cfe/trunk/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