namespace ento {
-class ExplodedNode;
-class SymExpr;
-
-using SymbolRef = const SymExpr *;
-
//===----------------------------------------------------------------------===//
// High-level interface for handlers of path-sensitive diagnostics.
//===----------------------------------------------------------------------===//
static PathDiagnosticLocation createDeclEnd(const LocationContext *LC,
const SourceManager &SM);
- /// Create a location corresponding to the given valid ExplodedNode.
+ /// Create a location corresponding to the given valid ProgramPoint.
static PathDiagnosticLocation create(const ProgramPoint &P,
const SourceManager &SMng);
- /// Create a location corresponding to the next valid ExplodedNode as end
- /// of path location.
- static PathDiagnosticLocation createEndOfPath(const ExplodedNode* N);
-
/// Convert the given location into a single kind location.
static PathDiagnosticLocation createSingleLocation(
const PathDiagnosticLocation &PDL);
+ /// Construct a source location that corresponds to either the beginning
+ /// or the end of the given statement, or a nearby valid source location
+ /// if the statement does not have a valid source location of its own.
+ static SourceLocation
+ getValidSourceLocation(const Stmt *S, LocationOrAnalysisDeclContext LAC,
+ bool UseEndOfStatement = false);
+
bool operator==(const PathDiagnosticLocation &X) const {
return K == X.K && Loc == X.Loc && Range == X.Range;
}
void Profile(llvm::FoldingSetNodeID &ID) const;
void dump() const;
-
- /// Given an exploded node, retrieve the statement that should be used
- /// for the diagnostic location.
- static const Stmt *getStmt(const ExplodedNode *N);
-
- /// Retrieve the statement corresponding to the successor node.
- static const Stmt *getNextStmt(const ExplodedNode *N);
};
class PathDiagnosticLocationPair {
/// Trivial nodes may be skipped while printing exploded graph.
bool isTrivial() const;
+ /// If the node's program point corresponds to a statement, retrieve that
+ /// statement. Useful for figuring out where to put a warning or a note.
+ /// If the statement belongs to a body-farmed definition,
+ /// retrieve the call site for that definition.
+ const Stmt *getStmtForDiagnostics() const;
+
+ /// Find the next statement that was executed on this node's execution path.
+ /// Useful for explaining control flow that follows the current node.
+ /// If the statement belongs to a body-farmed definition, retrieve the
+ /// call site for that definition.
+ const Stmt *getNextStmtForDiagnostics() const;
+
+ /// Find the statement that was executed immediately before this node.
+ /// Useful when the node corresponds to a CFG block entrance.
+ /// If the statement belongs to a body-farmed definition, retrieve the
+ /// call site for that definition.
+ const Stmt *getPreviousStmtForDiagnostics() const;
+
+ /// Find the statement that was executed at or immediately before this node.
+ /// Useful when any nearby statement will do.
+ /// If the statement belongs to a body-farmed definition, retrieve the
+ /// call site for that definition.
+ const Stmt *getCurrentOrPreviousStmtForDiagnostics() const;
+
private:
void replaceSuccessor(ExplodedNode *node) { Succs.replaceNode(node); }
void replacePredecessor(ExplodedNode *node) { Preds.replaceNode(node); }
if (Satisfied)
return nullptr;
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
return nullptr;
// Retrieve the associated statement.
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
return nullptr;
// Retrieve the associated statement.
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf))
return nullptr;
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
// allocated, and only report a single path.
PathDiagnosticLocation LocUsedForUniqueing;
const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
- const Stmt *AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
+ const Stmt *AllocStmt = AllocNode->getStmtForDiagnostics();
if (AllocStmt)
LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt,
if (!IsLeak)
return nullptr;
- PathDiagnosticLocation L =
- PathDiagnosticLocation::createEndOfPath(EndPathNode);
+ PathDiagnosticLocation L = BR.getLocation();
// Do not add the statement itself as a range in case of leak.
return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(),
false);
const MemRegion *Region = nullptr;
std::tie(AllocNode, Region) = getAllocationSite(N, Sym, C);
- const Stmt *AllocationStmt = PathDiagnosticLocation::getStmt(AllocNode);
+ const Stmt *AllocationStmt = AllocNode->getStmtForDiagnostics();
if (AllocationStmt)
LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocationStmt,
C.getSourceManager(),
const RefState *RS = state->get<RegionState>(Sym);
const RefState *RSPrev = statePrev->get<RegionState>(Sym);
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
// When dealing with containers, we sometimes want to give a note
// even if the statement is missing.
if (!S && (!RS || RS->getAllocationFamily() != AF_InnerBuffer))
return nullptr;
// Retrieve the associated statement.
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
Found = true;
PathDiagnosticLocation LocUsedForUniqueing;
const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
- if (const Stmt *MoveStmt = PathDiagnosticLocation::getStmt(MoveNode))
+ if (const Stmt *MoveStmt = MoveNode->getStmtForDiagnostics())
LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
MoveStmt, C.getSourceManager(), MoveNode->getLocationContext());
// Retrieve the associated statement.
const Stmt *S = TrackedNullab->getNullabilitySource();
if (!S || S->getBeginLoc().isInvalid()) {
- S = PathDiagnosticLocation::getStmt(N);
+ S = N->getStmtForDiagnostics();
}
if (!S)
const MemRegion* FirstBinding = AllocI.R;
BR.markInteresting(AllocI.InterestingMethodContext);
- // Compute an actual location for the leak. Sometimes a leak doesn't
- // occur at an actual statement (e.g., transition between blocks; end
- // of function) so we need to walk the graph and compute a real location.
- const ExplodedNode *LeakN = EndN;
- PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN);
+ PathDiagnosticLocation L = cast<RefLeakReport>(BR).getEndOfPath();
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
// FIXME: This will crash the analyzer if an allocation comes from an
// implicit call (ex: a destructor call).
// (Currently there are no such allocations in Cocoa, though.)
- AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
+ AllocStmt = AllocNode->getStmtForDiagnostics();
if (!AllocStmt) {
AllocBinding = nullptr;
public:
RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n,
SymbolRef sym, CheckerContext &Ctx);
-
PathDiagnosticLocation getLocation() const override {
assert(Location.isValid());
return Location;
}
+
+ PathDiagnosticLocation getEndOfPath() const {
+ return PathSensitiveBugReport::getLocation();
+ }
};
} // end namespace retaincountchecker
isTainted(N->getFirstPred()->getState(), V))
return nullptr;
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
if (!IsLeak)
return nullptr;
- PathDiagnosticLocation L =
- PathDiagnosticLocation::createEndOfPath(EndPathNode);
+ PathDiagnosticLocation L = BR.getLocation();
// Do not add the statement itself as a range in case of leak.
return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(),
false);
const ExplodedNode *StartNode = getStartCallSite(N, Reg);
PathDiagnosticLocation LocUsedForUniqueing;
- if (const Stmt *StartCallStmt = PathDiagnosticLocation::getStmt(StartNode))
+ if (const Stmt *StartCallStmt = StartNode->getStmtForDiagnostics())
LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
StartCallStmt, C.getSourceManager(), StartNode->getLocationContext());
ProgramStateRef State = N->getState();
ProgramStateRef StatePrev = N->getFirstPred()->getState();
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
}
//===----------------------------------------------------------------------===//
-// Helper routines for walking the ExplodedGraph and fetching statements.
-//===----------------------------------------------------------------------===//
-
-static const Stmt *GetPreviousStmt(const ExplodedNode *N) {
- for (N = N->getFirstPred(); N; N = N->getFirstPred())
- if (const Stmt *S = PathDiagnosticLocation::getStmt(N))
- return S;
-
- return nullptr;
-}
-
-static inline const Stmt*
-GetCurrentOrPreviousStmt(const ExplodedNode *N) {
- if (const Stmt *S = PathDiagnosticLocation::getStmt(N))
- return S;
-
- return GetPreviousStmt(N);
-}
-
-//===----------------------------------------------------------------------===//
// Diagnostic cleanup.
//===----------------------------------------------------------------------===//
PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues(
const PathDiagnosticConstruct &C) const {
- if (const Stmt *S = PathDiagnosticLocation::getNextStmt(C.getCurrentNode()))
+ if (const Stmt *S = C.getCurrentNode()->getNextStmtForDiagnostics())
return PathDiagnosticLocation(S, getSourceManager(),
C.getCurrLocationContext());
case Stmt::GotoStmtClass:
case Stmt::IndirectGotoStmtClass: {
- if (const Stmt *S = PathDiagnosticLocation::getNextStmt(C.getCurrentNode()))
+ if (const Stmt *S = C.getCurrentNode()->getNextStmtForDiagnostics())
C.getActivePath().push_front(generateDiagForGotoOP(C, S, Start));
break;
}
if (UL.isValid()) {
UL.Profile(hash);
} else {
- assert(ErrorNode);
- hash.AddPointer(GetCurrentOrPreviousStmt(ErrorNode));
+ // TODO: The statement may be null if the report was emitted before any
+ // statements were executed. In particular, some checkers by design
+ // occasionally emit their reports in empty functions (that have no
+ // statements in their body). Do we profile correctly in this case?
+ hash.AddPointer(ErrorNode->getCurrentOrPreviousStmtForDiagnostics());
}
for (SourceRange range : Ranges) {
if (Optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) {
CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit();
if (BE->getBlock() == &Exit)
- S = GetPreviousStmt(ErrorNode);
+ S = ErrorNode->getPreviousStmtForDiagnostics();
}
if (!S)
- S = PathDiagnosticLocation::getStmt(ErrorNode);
+ S = ErrorNode->getStmtForDiagnostics();
return S;
}
PathDiagnosticLocation
PathSensitiveBugReport::getLocation() const {
- return PathDiagnosticLocation::createEndOfPath(ErrorNode);
+ assert(ErrorNode && "Cannot create a location with a null node.");
+ const Stmt *S = ErrorNode->getStmtForDiagnostics();
+ ProgramPoint P = ErrorNode->getLocation();
+ const LocationContext *LC = P.getLocationContext();
+ SourceManager &SM =
+ ErrorNode->getState()->getStateManager().getContext().getSourceManager();
+
+ if (!S) {
+ // If this is an implicit call, return the implicit call point location.
+ if (Optional<PreImplicitCall> PIE = P.getAs<PreImplicitCall>())
+ return PathDiagnosticLocation(PIE->getLocation(), SM);
+ if (auto FE = P.getAs<FunctionExitPoint>()) {
+ if (const ReturnStmt *RS = FE->getStmt())
+ return PathDiagnosticLocation::createBegin(RS, SM, LC);
+ }
+ S = ErrorNode->getNextStmtForDiagnostics();
+ }
+
+ if (S) {
+ // For member expressions, return the location of the '.' or '->'.
+ if (const auto *ME = dyn_cast<MemberExpr>(S))
+ return PathDiagnosticLocation::createMemberLoc(ME, SM);
+
+ // For binary operators, return the location of the operator.
+ if (const auto *B = dyn_cast<BinaryOperator>(S))
+ return PathDiagnosticLocation::createOperatorLoc(B, SM);
+
+ if (P.getAs<PostStmtPurgeDeadSymbols>())
+ return PathDiagnosticLocation::createEnd(S, SM, LC);
+
+ if (S->getBeginLoc().isValid())
+ return PathDiagnosticLocation(S, SM, LC);
+
+ return PathDiagnosticLocation(
+ PathDiagnosticLocation::getValidSourceLocation(S, LC), SM);
+ }
+
+ return PathDiagnosticLocation::createDeclEnd(ErrorNode->getLocationContext(),
+ SM);
}
//===----------------------------------------------------------------------===//
// Inlined function: show signature.
const Decl* D = CE->getCalleeContext()->getDecl();
populateExecutedLinesWithFunctionSignature(D, SM, *ExecutedLines);
- } else if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) {
+ } else if (const Stmt *S = N->getStmtForDiagnostics()) {
populateExecutedLinesWithStmt(S, SM, *ExecutedLines);
// Show extra context for some parent kinds.
BugReporterVisitor::getDefaultEndPath(const BugReporterContext &BRC,
const ExplodedNode *EndPathNode,
const PathSensitiveBugReport &BR) {
- PathDiagnosticLocation L =
- PathDiagnosticLocation::createEndOfPath(EndPathNode);
-
+ PathDiagnosticLocation L = BR.getLocation();
const auto &Ranges = BR.getRanges();
// Only add the statement itself as a range if we didn't specify any
/// \return Source location of right hand side of an assignment
/// into \c RegionOfInterest, empty optional if none found.
Optional<SourceLocation> matchAssignment(const ExplodedNode *N) {
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
ProgramStateRef State = N->getState();
auto *LCtx = N->getLocationContext();
if (!S)
static const ExplodedNode* findNodeForExpression(const ExplodedNode *N,
const Expr *Inner) {
while (N) {
- if (PathDiagnosticLocation::getStmt(N) == Inner)
+ if (N->getStmtForDiagnostics() == Inner)
return N;
N = N->getFirstPred();
}
return BEP->getBlock();
// Find the node's current statement in the CFG.
- if (const Stmt *S = PathDiagnosticLocation::getStmt(this))
+ // FIXME: getStmtForDiagnostics() does nasty things in order to provide
+ // a valid statement for body farms, do we need this behavior here?
+ if (const Stmt *S = getStmtForDiagnostics())
return getLocationContext()
->getAnalysisDeclContext()
->getCFGStmtMap()
return nullptr;
}
+static const LocationContext *
+findTopAutosynthesizedParentContext(const LocationContext *LC) {
+ assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized());
+ const LocationContext *ParentLC = LC->getParent();
+ assert(ParentLC && "We don't start analysis from autosynthesized code");
+ while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
+ LC = ParentLC;
+ ParentLC = LC->getParent();
+ assert(ParentLC && "We don't start analysis from autosynthesized code");
+ }
+ return LC;
+}
+
+const Stmt *ExplodedNode::getStmtForDiagnostics() const {
+ // We cannot place diagnostics on autosynthesized code.
+ // Put them onto the call site through which we jumped into autosynthesized
+ // code for the first time.
+ const LocationContext *LC = getLocationContext();
+ if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
+ // It must be a stack frame because we only autosynthesize functions.
+ return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC))
+ ->getCallSite();
+ }
+ // Otherwise, see if the node's program point directly points to a statement.
+ // FIXME: Refactor into a ProgramPoint method?
+ ProgramPoint P = getLocation();
+ if (auto SP = P.getAs<StmtPoint>())
+ return SP->getStmt();
+ if (auto BE = P.getAs<BlockEdge>())
+ return BE->getSrc()->getTerminatorStmt();
+ if (auto CE = P.getAs<CallEnter>())
+ return CE->getCallExpr();
+ if (auto CEE = P.getAs<CallExitEnd>())
+ return CEE->getCalleeContext()->getCallSite();
+ if (auto PIPP = P.getAs<PostInitializer>())
+ return PIPP->getInitializer()->getInit();
+ if (auto CEB = P.getAs<CallExitBegin>())
+ return CEB->getReturnStmt();
+ if (auto FEP = P.getAs<FunctionExitPoint>())
+ return FEP->getStmt();
+
+ return nullptr;
+}
+
+const Stmt *ExplodedNode::getNextStmtForDiagnostics() const {
+ for (const ExplodedNode *N = getFirstSucc(); N; N = N->getFirstSucc()) {
+ if (const Stmt *S = N->getStmtForDiagnostics()) {
+ // Check if the statement is '?' or '&&'/'||'. These are "merges",
+ // not actual statement points.
+ switch (S->getStmtClass()) {
+ case Stmt::ChooseExprClass:
+ case Stmt::BinaryConditionalOperatorClass:
+ case Stmt::ConditionalOperatorClass:
+ continue;
+ case Stmt::BinaryOperatorClass: {
+ BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode();
+ if (Op == BO_LAnd || Op == BO_LOr)
+ continue;
+ break;
+ }
+ default:
+ break;
+ }
+ // We found the statement, so return it.
+ return S;
+ }
+ }
+
+ return nullptr;
+}
+
+const Stmt *ExplodedNode::getPreviousStmtForDiagnostics() const {
+ for (const ExplodedNode *N = getFirstPred(); N; N = N->getFirstPred())
+ if (const Stmt *S = N->getStmtForDiagnostics())
+ return S;
+
+ return nullptr;
+}
+
+const Stmt *ExplodedNode::getCurrentOrPreviousStmtForDiagnostics() const {
+ if (const Stmt *S = getStmtForDiagnostics())
+ return S;
+
+ return getPreviousStmtForDiagnostics();
+}
+
ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
ProgramStateRef State,
bool IsSink,
return true;
while (!N->pred_empty()) {
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ // FIXME: getStmtForDiagnostics() does nasty things in order to provide
+ // a valid statement for body farms, do we need this behavior here?
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S) {
N = N->getFirstPred();
continue;
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/None.h"
// PathDiagnosticLocation methods.
//===----------------------------------------------------------------------===//
-static SourceLocation getValidSourceLocation(const Stmt* S,
- LocationOrAnalysisDeclContext LAC,
- bool UseEnd = false) {
- SourceLocation L = UseEnd ? S->getEndLoc() : S->getBeginLoc();
- assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should "
- "be passed to PathDiagnosticLocation upon creation.");
+SourceLocation PathDiagnosticLocation::getValidSourceLocation(
+ const Stmt *S, LocationOrAnalysisDeclContext LAC, bool UseEndOfStatement) {
+ SourceLocation L = UseEndOfStatement ? S->getEndLoc() : S->getBeginLoc();
+ assert(!LAC.isNull() &&
+ "A valid LocationContext or AnalysisDeclContext should be passed to "
+ "PathDiagnosticLocation upon creation.");
// S might be a temporary statement that does not have a location in the
// source code, so find an enclosing statement and use its location.
break;
}
- L = UseEnd ? Parent->getEndLoc() : Parent->getBeginLoc();
+ L = UseEndOfStatement ? Parent->getEndLoc() : Parent->getBeginLoc();
} while (!L.isValid());
}
return PathDiagnosticLocation(S, SMng, P.getLocationContext());
}
-static const LocationContext *
-findTopAutosynthesizedParentContext(const LocationContext *LC) {
- assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized());
- const LocationContext *ParentLC = LC->getParent();
- assert(ParentLC && "We don't start analysis from autosynthesized code");
- while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
- LC = ParentLC;
- ParentLC = LC->getParent();
- assert(ParentLC && "We don't start analysis from autosynthesized code");
- }
- return LC;
-}
-
-const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) {
- // We cannot place diagnostics on autosynthesized code.
- // Put them onto the call site through which we jumped into autosynthesized
- // code for the first time.
- const LocationContext *LC = N->getLocationContext();
- if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
- // It must be a stack frame because we only autosynthesize functions.
- return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC))
- ->getCallSite();
- }
- // Otherwise, see if the node's program point directly points to a statement.
- ProgramPoint P = N->getLocation();
- if (auto SP = P.getAs<StmtPoint>())
- return SP->getStmt();
- if (auto BE = P.getAs<BlockEdge>())
- return BE->getSrc()->getTerminatorStmt();
- if (auto CE = P.getAs<CallEnter>())
- return CE->getCallExpr();
- if (auto CEE = P.getAs<CallExitEnd>())
- return CEE->getCalleeContext()->getCallSite();
- if (auto PIPP = P.getAs<PostInitializer>())
- return PIPP->getInitializer()->getInit();
- if (auto CEB = P.getAs<CallExitBegin>())
- return CEB->getReturnStmt();
- if (auto FEP = P.getAs<FunctionExitPoint>())
- return FEP->getStmt();
-
- return nullptr;
-}
-
-const Stmt *PathDiagnosticLocation::getNextStmt(const ExplodedNode *N) {
- for (N = N->getFirstSucc(); N; N = N->getFirstSucc()) {
- if (const Stmt *S = getStmt(N)) {
- // Check if the statement is '?' or '&&'/'||'. These are "merges",
- // not actual statement points.
- switch (S->getStmtClass()) {
- case Stmt::ChooseExprClass:
- case Stmt::BinaryConditionalOperatorClass:
- case Stmt::ConditionalOperatorClass:
- continue;
- case Stmt::BinaryOperatorClass: {
- BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode();
- if (Op == BO_LAnd || Op == BO_LOr)
- continue;
- break;
- }
- default:
- break;
- }
- // We found the statement, so return it.
- return S;
- }
- }
-
- return nullptr;
-}
-
-PathDiagnosticLocation
-PathDiagnosticLocation::createEndOfPath(const ExplodedNode *N) {
- assert(N && "Cannot create a location with a null node.");
- const Stmt *S = getStmt(N);
- const LocationContext *LC = N->getLocationContext();
- SourceManager &SM =
- N->getState()->getStateManager().getContext().getSourceManager();
-
- if (!S) {
- // If this is an implicit call, return the implicit call point location.
- if (Optional<PreImplicitCall> PIE = N->getLocationAs<PreImplicitCall>())
- return PathDiagnosticLocation(PIE->getLocation(), SM);
- if (auto FE = N->getLocationAs<FunctionExitPoint>()) {
- if (const ReturnStmt *RS = FE->getStmt())
- return PathDiagnosticLocation::createBegin(RS, SM, LC);
- }
- S = getNextStmt(N);
- }
-
- if (S) {
- ProgramPoint P = N->getLocation();
-
- // For member expressions, return the location of the '.' or '->'.
- if (const auto *ME = dyn_cast<MemberExpr>(S))
- return PathDiagnosticLocation::createMemberLoc(ME, SM);
-
- // For binary operators, return the location of the operator.
- if (const auto *B = dyn_cast<BinaryOperator>(S))
- return PathDiagnosticLocation::createOperatorLoc(B, SM);
-
- if (P.getAs<PostStmtPurgeDeadSymbols>())
- return PathDiagnosticLocation::createEnd(S, SM, LC);
-
- if (S->getBeginLoc().isValid())
- return PathDiagnosticLocation(S, SM, LC);
- return PathDiagnosticLocation(getValidSourceLocation(S, LC), SM);
- }
-
- return createDeclEnd(N->getLocationContext(), SM);
-}
-
PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation(
const PathDiagnosticLocation &PDL) {
FullSourceLoc L = PDL.asLocation();