[analyzer] Refactor PathDiagnosticLocation: Make PathDiagnosticLocation(SourceLocation...) private. Most of the effort here goes to making BugReport refer to a PathDiagnosticLocation instead of FullSourceLocation.

(Another step closer to the goal of having Diagnostics which can recover from invalid SourceLocations.)

llvm-svn: 140182
This commit is contained in:
Anna Zaks 2011-09-20 21:38:35 +00:00
parent 61a003315e
commit c29bed3989
17 changed files with 201 additions and 98 deletions

View File

@ -17,6 +17,7 @@
#include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceLocation.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/ImmutableList.h"
@ -34,8 +35,6 @@ class ParentMap;
namespace ento { namespace ento {
class PathDiagnostic; class PathDiagnostic;
class PathDiagnosticPiece;
class PathDiagnosticClient;
class ExplodedNode; class ExplodedNode;
class ExplodedGraph; class ExplodedGraph;
class BugReport; class BugReport;
@ -70,7 +69,7 @@ protected:
BugType& BT; BugType& BT;
std::string ShortDescription; std::string ShortDescription;
std::string Description; std::string Description;
FullSourceLoc Location; PathDiagnosticLocation Location;
const ExplodedNode *ErrorNode; const ExplodedNode *ErrorNode;
SmallVector<SourceRange, 4> Ranges; SmallVector<SourceRange, 4> Ranges;
ExtraTextList ExtraText; ExtraTextList ExtraText;
@ -91,7 +90,7 @@ public:
: BT(bt), ShortDescription(shortDesc), Description(desc), : BT(bt), ShortDescription(shortDesc), Description(desc),
ErrorNode(errornode), Callbacks(F.getEmptyList()) {} ErrorNode(errornode), Callbacks(F.getEmptyList()) {}
BugReport(BugType& bt, StringRef desc, FullSourceLoc l) BugReport(BugType& bt, StringRef desc, PathDiagnosticLocation l)
: BT(bt), Description(desc), Location(l), ErrorNode(0), : BT(bt), Description(desc), Location(l), ErrorNode(0),
Callbacks(F.getEmptyList()) {} Callbacks(F.getEmptyList()) {}
@ -124,7 +123,7 @@ public:
/// While a bug can span an entire path, usually there is a specific /// While a bug can span an entire path, usually there is a specific
/// location that can be used to identify where the key issue occurred. /// location that can be used to identify where the key issue occurred.
/// This location is used by clients rendering diagnostics. /// This location is used by clients rendering diagnostics.
virtual SourceLocation getLocation() const; virtual PathDiagnosticLocation getLocation(const SourceManager &SM) const;
const Stmt *getStmt() const; const Stmt *getStmt() const;
@ -296,31 +295,31 @@ public:
void EmitReport(BugReport *R); void EmitReport(BugReport *R);
void EmitBasicReport(StringRef BugName, StringRef BugStr, void EmitBasicReport(StringRef BugName, StringRef BugStr,
SourceLocation Loc, PathDiagnosticLocation Loc,
SourceRange* RangeBeg, unsigned NumRanges); SourceRange* RangeBeg, unsigned NumRanges);
void EmitBasicReport(StringRef BugName, StringRef BugCategory, void EmitBasicReport(StringRef BugName, StringRef BugCategory,
StringRef BugStr, SourceLocation Loc, StringRef BugStr, PathDiagnosticLocation Loc,
SourceRange* RangeBeg, unsigned NumRanges); SourceRange* RangeBeg, unsigned NumRanges);
void EmitBasicReport(StringRef BugName, StringRef BugStr, void EmitBasicReport(StringRef BugName, StringRef BugStr,
SourceLocation Loc) { PathDiagnosticLocation Loc) {
EmitBasicReport(BugName, BugStr, Loc, 0, 0); EmitBasicReport(BugName, BugStr, Loc, 0, 0);
} }
void EmitBasicReport(StringRef BugName, StringRef BugCategory, void EmitBasicReport(StringRef BugName, StringRef BugCategory,
StringRef BugStr, SourceLocation Loc) { StringRef BugStr, PathDiagnosticLocation Loc) {
EmitBasicReport(BugName, BugCategory, BugStr, Loc, 0, 0); EmitBasicReport(BugName, BugCategory, BugStr, Loc, 0, 0);
} }
void EmitBasicReport(StringRef BugName, StringRef BugStr, void EmitBasicReport(StringRef BugName, StringRef BugStr,
SourceLocation Loc, SourceRange R) { PathDiagnosticLocation Loc, SourceRange R) {
EmitBasicReport(BugName, BugStr, Loc, &R, 1); EmitBasicReport(BugName, BugStr, Loc, &R, 1);
} }
void EmitBasicReport(StringRef BugName, StringRef Category, void EmitBasicReport(StringRef BugName, StringRef Category,
StringRef BugStr, SourceLocation Loc, StringRef BugStr, PathDiagnosticLocation Loc,
SourceRange R) { SourceRange R) {
EmitBasicReport(BugName, Category, BugStr, Loc, &R, 1); EmitBasicReport(BugName, Category, BugStr, Loc, &R, 1);
} }

View File

@ -29,6 +29,7 @@ class BinaryOperator;
class CompoundStmt; class CompoundStmt;
class Decl; class Decl;
class LocationContext; class LocationContext;
class MemberExpr;
class ParentMap; class ParentMap;
class ProgramPoint; class ProgramPoint;
class SourceManager; class SourceManager;
@ -103,6 +104,12 @@ private:
FullSourceLoc Loc; FullSourceLoc Loc;
PathDiagnosticRange Range; PathDiagnosticRange Range;
PathDiagnosticLocation(SourceLocation L, const SourceManager &sm,
Kind kind)
: K(kind), R(L, L), S(0), D(0), SM(&sm),
Loc(genLocation()), Range(genRange()) {
}
FullSourceLoc FullSourceLoc
genLocation(LocationOrAnalysisContext LAC = (AnalysisContext*)0) const; genLocation(LocationOrAnalysisContext LAC = (AnalysisContext*)0) const;
PathDiagnosticRange PathDiagnosticRange
@ -118,12 +125,6 @@ public:
Loc(genLocation()), Range(genRange()) { Loc(genLocation()), Range(genRange()) {
} }
PathDiagnosticLocation(SourceLocation L, const SourceManager &sm,
Kind kind = SingleLocK)
: K(kind), R(L, L), S(0), D(0), SM(&sm),
Loc(genLocation()), Range(genRange()) {
}
PathDiagnosticLocation(const Stmt *s, PathDiagnosticLocation(const Stmt *s,
const SourceManager &sm, const SourceManager &sm,
LocationOrAnalysisContext lac) LocationOrAnalysisContext lac)
@ -136,16 +137,30 @@ public:
Loc(genLocation()), Range(genRange()) { Loc(genLocation()), Range(genRange()) {
} }
// Create a location for the beginning of the statement. static PathDiagnosticLocation create(const Decl *D,
static PathDiagnosticLocation createBeginStmt(const Stmt *S, const SourceManager &SM) {
const SourceManager &SM, return PathDiagnosticLocation(D, SM);
LocationOrAnalysisContext LAC); }
/// Create a location for the beginning of the declaration.
static PathDiagnosticLocation createBegin(const Decl *D,
const SourceManager &SM);
/// Create a location for the beginning of the statement.
static PathDiagnosticLocation createBegin(const Stmt *S,
const SourceManager &SM,
const LocationOrAnalysisContext LAC);
/// Create the location for the operator of the binary expression. /// Create the location for the operator of the binary expression.
/// Assumes the statement has a valid location. /// Assumes the statement has a valid location.
static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO, static PathDiagnosticLocation createOperatorLoc(const BinaryOperator *BO,
const SourceManager &SM); const SourceManager &SM);
/// For member expressions, return the location of the '.' or '->'.
/// Assumes the statement has a valid location.
static PathDiagnosticLocation createMemberLoc(const MemberExpr *ME,
const SourceManager &SM);
/// Create a location for the beginning of the compound statement. /// Create a location for the beginning of the compound statement.
/// Assumes the statement has a valid location. /// Assumes the statement has a valid location.
static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS, static PathDiagnosticLocation createBeginBrace(const CompoundStmt *CS,

View File

@ -94,7 +94,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
<< (Eng.hasEmptyWorkList() ? "yes" : "no"); << (Eng.hasEmptyWorkList() ? "yes" : "no");
B.EmitBasicReport("Analyzer Statistics", "Internal Statistics", output.str(), B.EmitBasicReport("Analyzer Statistics", "Internal Statistics", output.str(),
D->getLocation()); PathDiagnosticLocation(D, SM));
// Emit warning for each block we bailed out on // Emit warning for each block we bailed out on
typedef CoreEngine::BlocksExhausted::const_iterator ExhaustedIterator; typedef CoreEngine::BlocksExhausted::const_iterator ExhaustedIterator;
@ -106,7 +106,8 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
const CFGElement &CE = Exit->front(); const CFGElement &CE = Exit->front();
if (const CFGStmt *CS = dyn_cast<CFGStmt>(&CE)) if (const CFGStmt *CS = dyn_cast<CFGStmt>(&CE))
B.EmitBasicReport("Bailout Point", "Internal Statistics", "The analyzer " B.EmitBasicReport("Bailout Point", "Internal Statistics", "The analyzer "
"stopped analyzing at this point", CS->getStmt()->getLocStart()); "stopped analyzing at this point",
PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC));
} }
} }

View File

@ -166,6 +166,9 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D,
} }
} }
PathDiagnosticLocation DLoc =
PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
if (!MD) { // No dealloc found. if (!MD) { // No dealloc found.
const char* name = LOpts.getGC() == LangOptions::NonGC const char* name = LOpts.getGC() == LangOptions::NonGC
@ -176,7 +179,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D,
llvm::raw_string_ostream os(buf); llvm::raw_string_ostream os(buf);
os << "Objective-C class '" << D << "' lacks a 'dealloc' instance method"; os << "Objective-C class '" << D << "' lacks a 'dealloc' instance method";
BR.EmitBasicReport(name, os.str(), D->getLocStart()); BR.EmitBasicReport(name, os.str(), DLoc);
return; return;
} }
@ -193,7 +196,7 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D,
<< "' does not send a 'dealloc' message to its super class" << "' does not send a 'dealloc' message to its super class"
" (missing [super dealloc])"; " (missing [super dealloc])";
BR.EmitBasicReport(name, os.str(), D->getLocStart()); BR.EmitBasicReport(name, os.str(), DLoc);
return; return;
} }
@ -257,7 +260,10 @@ static void checkObjCDealloc(const ObjCImplementationDecl *D,
"but was released in 'dealloc'"; "but was released in 'dealloc'";
} }
BR.EmitBasicReport(name, category, os.str(), (*I)->getLocation()); PathDiagnosticLocation SDLoc =
PathDiagnosticLocation::createBegin((*I), BR.getSourceManager());
BR.EmitBasicReport(name, category, os.str(), SDLoc);
} }
} }
} }

View File

@ -66,8 +66,12 @@ static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,
<< "'. These two types are incompatible, and may result in undefined " << "'. These two types are incompatible, and may result in undefined "
"behavior for clients of these classes."; "behavior for clients of these classes.";
PathDiagnosticLocation MethDLoc =
PathDiagnosticLocation::createBegin(MethDerived,
BR.getSourceManager());
BR.EmitBasicReport("Incompatible instance method return type", BR.EmitBasicReport("Incompatible instance method return type",
os.str(), MethDerived->getLocStart()); os.str(), MethDLoc);
} }
} }

View File

@ -12,12 +12,14 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "ClangSACheckers.h" #include "ClangSACheckers.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/AST/StmtVisitor.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang; using namespace clang;
using namespace ento; using namespace ento;
@ -34,14 +36,16 @@ static bool isArc4RandomAvailable(const ASTContext &Ctx) {
namespace { namespace {
class WalkAST : public StmtVisitor<WalkAST> { class WalkAST : public StmtVisitor<WalkAST> {
BugReporter &BR; BugReporter &BR;
AnalysisContext* AC;
enum { num_setids = 6 }; enum { num_setids = 6 };
IdentifierInfo *II_setid[num_setids]; IdentifierInfo *II_setid[num_setids];
const bool CheckRand; const bool CheckRand;
public: public:
WalkAST(BugReporter &br) : BR(br), II_setid(), WalkAST(BugReporter &br, AnalysisContext* ac)
CheckRand(isArc4RandomAvailable(BR.getContext())) {} : BR(br), AC(ac), II_setid(),
CheckRand(isArc4RandomAvailable(BR.getContext())) {}
// Statement visitor methods. // Statement visitor methods.
void VisitCallExpr(CallExpr *CE); void VisitCallExpr(CallExpr *CE);
@ -247,8 +251,11 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
ranges.push_back(drInc->getSourceRange()); ranges.push_back(drInc->getSourceRange());
const char *bugType = "Floating point variable used as loop counter"; const char *bugType = "Floating point variable used as loop counter";
PathDiagnosticLocation FSLoc =
PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
BR.EmitBasicReport(bugType, "Security", os.str(), BR.EmitBasicReport(bugType, "Security", os.str(),
FS->getLocStart(), ranges.data(), ranges.size()); FSLoc, ranges.data(), ranges.size());
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -278,11 +285,13 @@ void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a warning. // Issue a warning.
SourceRange R = CE->getCallee()->getSourceRange(); SourceRange R = CE->getCallee()->getSourceRange();
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport("Potential buffer overflow in call to 'gets'", BR.EmitBasicReport("Potential buffer overflow in call to 'gets'",
"Security", "Security",
"Call to function 'gets' is extremely insecure as it can " "Call to function 'gets' is extremely insecure as it can "
"always result in a buffer overflow", "always result in a buffer overflow",
CE->getLocStart(), &R, 1); CELoc, &R, 1);
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -314,11 +323,13 @@ void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a warning. // Issue a warning.
SourceRange R = CE->getCallee()->getSourceRange(); SourceRange R = CE->getCallee()->getSourceRange();
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport("Potential buffer overflow in call to 'getpw'", BR.EmitBasicReport("Potential buffer overflow in call to 'getpw'",
"Security", "Security",
"The getpw() function is dangerous as it may overflow the " "The getpw() function is dangerous as it may overflow the "
"provided buffer. It is obsoleted by getpwuid().", "provided buffer. It is obsoleted by getpwuid().",
CE->getLocStart(), &R, 1); CELoc, &R, 1);
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -347,11 +358,13 @@ void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a waring. // Issue a waring.
SourceRange R = CE->getCallee()->getSourceRange(); SourceRange R = CE->getCallee()->getSourceRange();
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'", BR.EmitBasicReport("Potential insecure temporary file in call 'mktemp'",
"Security", "Security",
"Call to function 'mktemp' is insecure as it always " "Call to function 'mktemp' is insecure as it always "
"creates or uses insecure temporary file. Use 'mkstemp' instead", "creates or uses insecure temporary file. Use 'mkstemp' instead",
CE->getLocStart(), &R, 1); CELoc, &R, 1);
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -366,6 +379,8 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a warning. // Issue a warning.
SourceRange R = CE->getCallee()->getSourceRange(); SourceRange R = CE->getCallee()->getSourceRange();
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport("Potential insecure memory buffer bounds restriction in " BR.EmitBasicReport("Potential insecure memory buffer bounds restriction in "
"call 'strcpy'", "call 'strcpy'",
"Security", "Security",
@ -373,7 +388,7 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
"provide bounding of the memory buffer. Replace " "provide bounding of the memory buffer. Replace "
"unbounded copy functions with analogous functions that " "unbounded copy functions with analogous functions that "
"support length arguments such as 'strncpy'. CWE-119.", "support length arguments such as 'strncpy'. CWE-119.",
CE->getLocStart(), &R, 1); CELoc, &R, 1);
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -388,6 +403,8 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a warning. // Issue a warning.
SourceRange R = CE->getCallee()->getSourceRange(); SourceRange R = CE->getCallee()->getSourceRange();
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport("Potential insecure memory buffer bounds restriction in " BR.EmitBasicReport("Potential insecure memory buffer bounds restriction in "
"call 'strcat'", "call 'strcat'",
"Security", "Security",
@ -395,7 +412,7 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
"provide bounding of the memory buffer. Replace " "provide bounding of the memory buffer. Replace "
"unbounded copy functions with analogous functions that " "unbounded copy functions with analogous functions that "
"support length arguments such as 'strncat'. CWE-119.", "support length arguments such as 'strncat'. CWE-119.",
CE->getLocStart(), &R, 1); CELoc, &R, 1);
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -467,7 +484,9 @@ void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
<< " Use 'arc4random' instead"; << " Use 'arc4random' instead";
SourceRange R = CE->getCallee()->getSourceRange(); SourceRange R = CE->getCallee()->getSourceRange();
BR.EmitBasicReport(os1.str(), "Security", os2.str(),CE->getLocStart(), &R, 1); PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport(os1.str(), "Security", os2.str(), CELoc, &R, 1);
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -490,11 +509,13 @@ void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
// Issue a warning. // Issue a warning.
SourceRange R = CE->getCallee()->getSourceRange(); SourceRange R = CE->getCallee()->getSourceRange();
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport("'random' is not a secure random number generator", BR.EmitBasicReport("'random' is not a secure random number generator",
"Security", "Security",
"The 'random' function produces a sequence of values that " "The 'random' function produces a sequence of values that "
"an adversary may be able to predict. Use 'arc4random' " "an adversary may be able to predict. Use 'arc4random' "
"instead", CE->getLocStart(), &R, 1); "instead", CELoc, &R, 1);
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -554,7 +575,9 @@ void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
<< "', the following code may execute with unexpected privileges"; << "', the following code may execute with unexpected privileges";
SourceRange R = CE->getCallee()->getSourceRange(); SourceRange R = CE->getCallee()->getSourceRange();
BR.EmitBasicReport(os1.str(), "Security", os2.str(),CE->getLocStart(), &R, 1); PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
BR.EmitBasicReport(os1.str(), "Security", os2.str(), CELoc, &R, 1);
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -566,7 +589,7 @@ class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
public: public:
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) const { BugReporter &BR) const {
WalkAST walker(BR); WalkAST walker(BR, mgr.getAnalysisContext(D));
walker.Visit(D->getBody()); walker.Visit(D->getBody());
} }
}; };

View File

@ -13,9 +13,10 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "ClangSACheckers.h" #include "ClangSACheckers.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/AST/StmtVisitor.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
using namespace clang; using namespace clang;
using namespace ento; using namespace ento;
@ -23,9 +24,10 @@ using namespace ento;
namespace { namespace {
class WalkAST : public StmtVisitor<WalkAST> { class WalkAST : public StmtVisitor<WalkAST> {
BugReporter &BR; BugReporter &BR;
AnalysisContext* AC;
public: public:
WalkAST(BugReporter &br) : BR(br) {} WalkAST(BugReporter &br, AnalysisContext* ac) : BR(br), AC(ac) {}
void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E); void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E);
void VisitStmt(Stmt *S) { VisitChildren(S); } void VisitStmt(Stmt *S) { VisitChildren(S); }
void VisitChildren(Stmt *S); void VisitChildren(Stmt *S);
@ -59,11 +61,13 @@ void WalkAST::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
return; return;
SourceRange R = ArgEx->getSourceRange(); SourceRange R = ArgEx->getSourceRange();
PathDiagnosticLocation ELoc =
PathDiagnosticLocation::createBegin(E, BR.getSourceManager(), AC);
BR.EmitBasicReport("Potential unintended use of sizeof() on pointer type", BR.EmitBasicReport("Potential unintended use of sizeof() on pointer type",
"Logic", "Logic",
"The code calls sizeof() on a pointer type. " "The code calls sizeof() on a pointer type. "
"This can produce an unexpected result.", "This can produce an unexpected result.",
E->getLocStart(), &R, 1); ELoc, &R, 1);
} }
} }
@ -76,7 +80,7 @@ class SizeofPointerChecker : public Checker<check::ASTCodeBody> {
public: public:
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) const { BugReporter &BR) const {
WalkAST walker(BR); WalkAST walker(BR, mgr.getAnalysisContext(D));
walker.Visit(D->getBody()); walker.Visit(D->getBody());
} }
}; };

View File

@ -72,6 +72,7 @@ class DeadStoreObs : public LiveVariables::Observer {
const CFG &cfg; const CFG &cfg;
ASTContext &Ctx; ASTContext &Ctx;
BugReporter& BR; BugReporter& BR;
AnalysisContext* AC;
ParentMap& Parents; ParentMap& Parents;
llvm::SmallPtrSet<const VarDecl*, 20> Escaped; llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
llvm::OwningPtr<ReachableCode> reachableCode; llvm::OwningPtr<ReachableCode> reachableCode;
@ -81,15 +82,15 @@ class DeadStoreObs : public LiveVariables::Observer {
public: public:
DeadStoreObs(const CFG &cfg, ASTContext &ctx, DeadStoreObs(const CFG &cfg, ASTContext &ctx,
BugReporter& br, ParentMap& parents, BugReporter& br, AnalysisContext* ac, ParentMap& parents,
llvm::SmallPtrSet<const VarDecl*, 20> &escaped) llvm::SmallPtrSet<const VarDecl*, 20> &escaped)
: cfg(cfg), Ctx(ctx), BR(br), Parents(parents), : cfg(cfg), Ctx(ctx), BR(br), AC(ac), Parents(parents),
Escaped(escaped), currentBlock(0) {} Escaped(escaped), currentBlock(0) {}
virtual ~DeadStoreObs() {} virtual ~DeadStoreObs() {}
void Report(const VarDecl *V, DeadStoreKind dsk, void Report(const VarDecl *V, DeadStoreKind dsk,
SourceLocation L, SourceRange R) { PathDiagnosticLocation L, SourceRange R) {
if (Escaped.count(V)) if (Escaped.count(V))
return; return;
@ -146,9 +147,12 @@ public:
return; return;
if (!Live.isLive(VD) && if (!Live.isLive(VD) &&
!(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>())) !(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>())) {
Report(VD, dsk, Ex->getSourceRange().getBegin(),
Val->getSourceRange()); PathDiagnosticLocation ExLoc =
PathDiagnosticLocation::createBegin(Ex, BR.getSourceManager(), AC);
Report(VD, dsk, ExLoc, Val->getSourceRange());
}
} }
void CheckDeclRef(const DeclRefExpr *DR, const Expr *Val, DeadStoreKind dsk, void CheckDeclRef(const DeclRefExpr *DR, const Expr *Val, DeadStoreKind dsk,
@ -293,7 +297,9 @@ public:
return; return;
} }
Report(V, DeadInit, V->getLocation(), E->getSourceRange()); PathDiagnosticLocation Loc =
PathDiagnosticLocation::create(V, BR.getSourceManager());
Report(V, DeadInit, Loc, E->getSourceRange());
} }
} }
} }
@ -344,10 +350,11 @@ public:
BugReporter &BR) const { BugReporter &BR) const {
if (LiveVariables *L = mgr.getLiveVariables(D)) { if (LiveVariables *L = mgr.getLiveVariables(D)) {
CFG &cfg = *mgr.getCFG(D); CFG &cfg = *mgr.getCFG(D);
AnalysisContext *AC = mgr.getAnalysisContext(D);
ParentMap &pmap = mgr.getParentMap(D); ParentMap &pmap = mgr.getParentMap(D);
FindEscaped FS(&cfg); FindEscaped FS(&cfg);
FS.getCFG().VisitBlockStmts(FS); FS.getCFG().VisitBlockStmts(FS);
DeadStoreObs A(cfg, BR.getContext(), BR, pmap, FS.Escaped); DeadStoreObs A(cfg, BR.getContext(), BR, AC, pmap, FS.Escaped);
L->runOnAllBlocks(A); L->runOnAllBlocks(A);
} }
} }

View File

@ -175,9 +175,10 @@ void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) {
// Okay, badness! Report an error. // Okay, badness! Report an error.
const char *desc = "StringRef should not be bound to temporary " const char *desc = "StringRef should not be bound to temporary "
"std::string that it outlives"; "std::string that it outlives";
PathDiagnosticLocation VDLoc =
PathDiagnosticLocation::createBegin(VD, BR.getSourceManager());
BR.EmitBasicReport(desc, "LLVM Conventions", desc, BR.EmitBasicReport(desc, "LLVM Conventions", desc,
VD->getLocStart(), Init->getSourceRange()); VDLoc, Init->getSourceRange());
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -279,8 +280,10 @@ void ASTFieldVisitor::ReportError(QualType T) {
// just report warnings when we see an out-of-line method definition for a // just report warnings when we see an out-of-line method definition for a
// class, as that heuristic doesn't always work (the complete definition of // class, as that heuristic doesn't always work (the complete definition of
// the class may be in the header file, for example). // the class may be in the header file, for example).
PathDiagnosticLocation L = PathDiagnosticLocation::createBegin(
FieldChain.front(), BR.getSourceManager());
BR.EmitBasicReport("AST node allocates heap memory", "LLVM Conventions", BR.EmitBasicReport("AST node allocates heap memory", "LLVM Conventions",
os.str(), FieldChain.front()->getLocStart()); os.str(), L);
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -212,8 +212,10 @@ void MallocOverflowSecurityChecker::OutputPossibleOverflows(
++i) { ++i) {
SourceRange R = i->mulop->getSourceRange(); SourceRange R = i->mulop->getSourceRange();
BR.EmitBasicReport("MallocOverflowSecurityChecker", BR.EmitBasicReport("MallocOverflowSecurityChecker",
"the computation of the size of the memory allocation may overflow", "the computation of the size of the memory allocation may overflow",
i->mulop->getOperatorLoc(), &R, 1); PathDiagnosticLocation::createOperatorLoc(i->mulop,
BR.getSourceManager()),
&R, 1);
} }
} }

View File

@ -67,11 +67,15 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(ObjCMessage msg,
return; return;
SourceRange R = msg.getSourceRange(); SourceRange R = msg.getSourceRange();
BugReporter &BR = C.getBugReporter();
const LocationContext *LC = C.getPredecessor()->getLocationContext();
const SourceManager &SM = BR.getSourceManager();
const Expr *E = msg.getMsgOrPropExpr();
PathDiagnosticLocation L = PathDiagnosticLocation::createBegin(E, SM, LC);
C.getBugReporter().EmitBasicReport("Use -drain instead of -release", C.getBugReporter().EmitBasicReport("Use -drain instead of -release",
"API Upgrade (Apple)", "API Upgrade (Apple)",
"Use -drain instead of -release when using NSAutoreleasePool " "Use -drain instead of -release when using NSAutoreleasePool "
"and garbage collection", R.getBegin(), &R, 1); "and garbage collection", L, &R, 1);
} }
void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) { void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) {

View File

@ -72,8 +72,10 @@ void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D,
const char *err = "Method accepting NSError** " const char *err = "Method accepting NSError** "
"should have a non-void return value to indicate whether or not an " "should have a non-void return value to indicate whether or not an "
"error occurred"; "error occurred";
PathDiagnosticLocation L =
PathDiagnosticLocation::create(D, BR.getSourceManager());
BR.EmitBasicReport("Bad return type when passing NSError**", BR.EmitBasicReport("Bad return type when passing NSError**",
"Coding conventions (Apple)", err, D->getLocation()); "Coding conventions (Apple)", err, L);
} }
} }
@ -118,8 +120,10 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
const char *err = "Function accepting CFErrorRef* " const char *err = "Function accepting CFErrorRef* "
"should have a non-void return value to indicate whether or not an " "should have a non-void return value to indicate whether or not an "
"error occurred"; "error occurred";
PathDiagnosticLocation L =
PathDiagnosticLocation::create(D, BR.getSourceManager());
BR.EmitBasicReport("Bad return type when passing CFErrorRef*", BR.EmitBasicReport("Bad return type when passing CFErrorRef*",
"Coding conventions (Apple)", err, D->getLocation()); "Coding conventions (Apple)", err, L);
} }
} }

View File

@ -159,8 +159,10 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
<< "' is never used by the methods in its @implementation " << "' is never used by the methods in its @implementation "
"(although it may be used by category methods)."; "(although it may be used by category methods).";
PathDiagnosticLocation L =
PathDiagnosticLocation::create(I->first, BR.getSourceManager());
BR.EmitBasicReport("Unused instance variable", "Optimization", BR.EmitBasicReport("Unused instance variable", "Optimization",
os.str(), I->first->getLocation()); os.str(), L);
} }
} }

View File

@ -1756,7 +1756,6 @@ namespace {
}; };
class CFRefLeakReport : public CFRefReport { class CFRefLeakReport : public CFRefReport {
SourceLocation AllocSite;
const MemRegion* AllocBinding; const MemRegion* AllocBinding;
public: public:
@ -1764,7 +1763,10 @@ namespace {
const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym, const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
ExprEngine &Eng); ExprEngine &Eng);
SourceLocation getLocation() const { return AllocSite; } PathDiagnosticLocation getLocation(const SourceManager &SM) const {
assert(Location.isValid());
return Location;
}
}; };
} // end anonymous namespace } // end anonymous namespace
@ -2219,18 +2221,20 @@ CFRefLeakReport::CFRefLeakReport(CFRefBug &D, const LangOptions &LOpts,
// same SourceLocation. // same SourceLocation.
const ExplodedNode *AllocNode = 0; const ExplodedNode *AllocNode = 0;
const SourceManager& SMgr = Eng.getContext().getSourceManager();
llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding. llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding.
GetAllocationSite(Eng.getStateManager(), getErrorNode(), sym); GetAllocationSite(Eng.getStateManager(), getErrorNode(), sym);
// Get the SourceLocation for the allocation site. // Get the SourceLocation for the allocation site.
ProgramPoint P = AllocNode->getLocation(); ProgramPoint P = AllocNode->getLocation();
AllocSite = cast<PostStmt>(P).getStmt()->getLocStart(); const Stmt *AllocStmt = cast<PostStmt>(P).getStmt();
Location = PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
n->getLocationContext());
// Fill in the description of the bug. // Fill in the description of the bug.
Description.clear(); Description.clear();
llvm::raw_string_ostream os(Description); llvm::raw_string_ostream os(Description);
SourceManager& SMgr = Eng.getContext().getSourceManager(); unsigned AllocLine = SMgr.getExpansionLineNumber(AllocStmt->getLocStart());
unsigned AllocLine = SMgr.getExpansionLineNumber(AllocSite);
os << "Potential leak "; os << "Potential leak ";
if (GCEnabled) if (GCEnabled)
os << "(when using garbage collection) "; os << "(when using garbage collection) ";

View File

@ -60,11 +60,12 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
CFG *C = 0; CFG *C = 0;
ParentMap *PM = 0; ParentMap *PM = 0;
const LocationContext *LC = 0;
// Iterate over ExplodedGraph // Iterate over ExplodedGraph
for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end(); for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end();
I != E; ++I) { I != E; ++I) {
const ProgramPoint &P = I->getLocation(); const ProgramPoint &P = I->getLocation();
const LocationContext *LC = P.getLocationContext(); LC = P.getLocationContext();
// Save the CFG if we don't have it already // Save the CFG if we don't have it already
if (!C) if (!C)
@ -128,11 +129,13 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
// We found a block that wasn't covered - find the statement to report // We found a block that wasn't covered - find the statement to report
SourceRange SR; SourceRange SR;
PathDiagnosticLocation DL;
SourceLocation SL; SourceLocation SL;
if (const Stmt *S = getUnreachableStmt(CB)) { if (const Stmt *S = getUnreachableStmt(CB)) {
SR = S->getSourceRange(); SR = S->getSourceRange();
SL = S->getLocStart(); DL = PathDiagnosticLocation::createBegin(S, B.getSourceManager(), LC);
if (SR.isInvalid() || SL.isInvalid()) SL = DL.asLocation();
if (SR.isInvalid() || !SL.isValid())
continue; continue;
} }
else else
@ -144,7 +147,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
continue; continue;
B.EmitBasicReport("Unreachable code", "Dead code", "This statement is never" B.EmitBasicReport("Unreachable code", "Dead code", "This statement is never"
" executed", SL, SR); " executed", DL, SR);
} }
} }

View File

@ -435,13 +435,13 @@ public:
return true; return true;
// Create the diagnostic. // Create the diagnostic.
FullSourceLoc L(S->getLocStart(), BR.getSourceManager());
if (Loc::isLocType(VD->getType())) { if (Loc::isLocType(VD->getType())) {
llvm::SmallString<64> buf; llvm::SmallString<64> buf;
llvm::raw_svector_ostream os(buf); llvm::raw_svector_ostream os(buf);
os << '\'' << VD << "' now aliases '" << MostRecent << '\''; os << '\'' << VD << "' now aliases '" << MostRecent << '\'';
PathDiagnosticLocation L =
PathDiagnosticLocation::createBegin(S, BR.getSourceManager(),
Pred->getLocationContext());
PD.push_front(new PathDiagnosticEventPiece(L, os.str())); PD.push_front(new PathDiagnosticEventPiece(L, os.str()));
} }
@ -533,7 +533,9 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
if (!T) if (!T)
continue; continue;
FullSourceLoc Start(T->getLocStart(), SMgr); PathDiagnosticLocation Start =
PathDiagnosticLocation::createBegin(T, SMgr,
N->getLocationContext());
switch (T->getStmtClass()) { switch (T->getStmtClass()) {
default: default:
@ -1231,8 +1233,13 @@ BugReport::~BugReport() {
void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { void BugReport::Profile(llvm::FoldingSetNodeID& hash) const {
hash.AddPointer(&BT); hash.AddPointer(&BT);
hash.AddInteger(getLocation().getRawEncoding());
hash.AddString(Description); hash.AddString(Description);
if (Location.isValid()) {
Location.Profile(hash);
} else {
assert(ErrorNode);
hash.AddPointer(GetCurrentOrPreviousStmt(ErrorNode));
}
for (SmallVectorImpl<SourceRange>::const_iterator I = for (SmallVectorImpl<SourceRange>::const_iterator I =
Ranges.begin(), E = Ranges.end(); I != E; ++I) { Ranges.begin(), E = Ranges.end(); I != E; ++I) {
@ -1280,28 +1287,29 @@ BugReport::getRanges() {
return std::make_pair(Ranges.begin(), Ranges.end()); return std::make_pair(Ranges.begin(), Ranges.end());
} }
SourceLocation BugReport::getLocation() const { PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const {
if (ErrorNode) { if (ErrorNode) {
(Location.isInvalid() && assert(!Location.isValid() &&
"Either Location or ErrorNode should be specified but not both."); "Either Location or ErrorNode should be specified but not both.");
if (const Stmt *S = GetCurrentOrPreviousStmt(ErrorNode)) { if (const Stmt *S = GetCurrentOrPreviousStmt(ErrorNode)) {
const LocationContext *LC = ErrorNode->getLocationContext();
// For member expressions, return the location of the '.' or '->'. // For member expressions, return the location of the '.' or '->'.
if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) if (const MemberExpr *ME = dyn_cast<MemberExpr>(S))
return ME->getMemberLoc(); return PathDiagnosticLocation::createMemberLoc(ME, SM);
// For binary operators, return the location of the operator. // For binary operators, return the location of the operator.
if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S))
return B->getOperatorLoc(); return PathDiagnosticLocation::createOperatorLoc(B, SM);
return S->getLocStart(); return PathDiagnosticLocation::createBegin(S, SM, LC);
} }
} else { } else {
assert(Location.isValid()); assert(Location.isValid());
return Location; return Location;
} }
return FullSourceLoc(); return PathDiagnosticLocation();
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -1564,7 +1572,9 @@ static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) {
if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) { if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
// Create a new macro group and add it to the stack. // Create a new macro group and add it to the stack.
PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece(Loc); PathDiagnosticMacroPiece *NewGroup =
new PathDiagnosticMacroPiece(
PathDiagnosticLocation::createSingleLocation(I->getLocation()));
if (MacroGroup) if (MacroGroup)
MacroGroup->push_back(NewGroup); MacroGroup->push_back(NewGroup);
@ -1872,7 +1882,6 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
BugReport::ranges_iterator Beg, End; BugReport::ranges_iterator Beg, End;
llvm::tie(Beg, End) = exampleReport->getRanges(); llvm::tie(Beg, End) = exampleReport->getRanges();
Diagnostic &Diag = getDiagnostic(); Diagnostic &Diag = getDiagnostic();
FullSourceLoc L(exampleReport->getLocation(), getSourceManager());
// Search the description for '%', as that will be interpretted as a // Search the description for '%', as that will be interpretted as a
// format character by FormatDiagnostics. // format character by FormatDiagnostics.
@ -1892,7 +1901,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
} }
{ {
DiagnosticBuilder diagBuilder = Diag.Report(L, ErrorDiag); DiagnosticBuilder diagBuilder = Diag.Report(
exampleReport->getLocation(getSourceManager()).asLocation(), ErrorDiag);
for (BugReport::ranges_iterator I = Beg; I != End; ++I) for (BugReport::ranges_iterator I = Beg; I != End; ++I)
diagBuilder << *I; diagBuilder << *I;
} }
@ -1902,8 +1912,9 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
return; return;
if (D->empty()) { if (D->empty()) {
PathDiagnosticPiece *piece = PathDiagnosticPiece *piece = new PathDiagnosticEventPiece(
new PathDiagnosticEventPiece(L, exampleReport->getDescription()); exampleReport->getLocation(getSourceManager()),
exampleReport->getDescription());
for ( ; Beg != End; ++Beg) piece->addRange(*Beg); for ( ; Beg != End; ++Beg) piece->addRange(*Beg);
D->push_back(piece); D->push_back(piece);
@ -1913,20 +1924,19 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
} }
void BugReporter::EmitBasicReport(StringRef name, StringRef str, void BugReporter::EmitBasicReport(StringRef name, StringRef str,
SourceLocation Loc, PathDiagnosticLocation Loc,
SourceRange* RBeg, unsigned NumRanges) { SourceRange* RBeg, unsigned NumRanges) {
EmitBasicReport(name, "", str, Loc, RBeg, NumRanges); EmitBasicReport(name, "", str, Loc, RBeg, NumRanges);
} }
void BugReporter::EmitBasicReport(StringRef name, void BugReporter::EmitBasicReport(StringRef name,
StringRef category, StringRef category,
StringRef str, SourceLocation Loc, StringRef str, PathDiagnosticLocation Loc,
SourceRange* RBeg, unsigned NumRanges) { SourceRange* RBeg, unsigned NumRanges) {
// 'BT' is owned by BugReporter. // 'BT' is owned by BugReporter.
BugType *BT = getBugTypeForName(name, category); BugType *BT = getBugTypeForName(name, category);
FullSourceLoc L = getContext().getFullLoc(Loc); BugReport *R = new BugReport(*BT, str, Loc);
BugReport *R = new BugReport(*BT, str, L);
for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg); for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg);
EmitReport(R); EmitReport(R);
} }

View File

@ -156,9 +156,15 @@ static SourceLocation getValidSourceLocation(const Stmt* S,
} }
PathDiagnosticLocation PathDiagnosticLocation
PathDiagnosticLocation::createBeginStmt(const Stmt *S, PathDiagnosticLocation::createBegin(const Decl *D,
const SourceManager &SM, const SourceManager &SM) {
LocationOrAnalysisContext LAC) { return PathDiagnosticLocation(D->getLocStart(), SM, SingleLocK);
}
PathDiagnosticLocation
PathDiagnosticLocation::createBegin(const Stmt *S,
const SourceManager &SM,
LocationOrAnalysisContext LAC) {
return PathDiagnosticLocation(getValidSourceLocation(S, LAC), return PathDiagnosticLocation(getValidSourceLocation(S, LAC),
SM, SingleLocK); SM, SingleLocK);
} }
@ -169,6 +175,12 @@ PathDiagnosticLocation
return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK); return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK);
} }
PathDiagnosticLocation
PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,
const SourceManager &SM) {
return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);
}
PathDiagnosticLocation PathDiagnosticLocation
PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS, PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS,
const SourceManager &SM) { const SourceManager &SM) {