aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Analysis
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-01-19 10:04:05 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-01-19 10:04:05 +0000
commit676fbe8105eeb6ff4bb2ed261cb212fcfdbe7b63 (patch)
tree02a1ac369cb734d0abfa5000dd86e5b7797e6a74 /lib/Analysis
parentc7e70c433efc6953dc3888b9fbf9f3512d7da2b0 (diff)
downloadsrc-676fbe8105eeb6ff4bb2ed261cb212fcfdbe7b63.tar.gz
src-676fbe8105eeb6ff4bb2ed261cb212fcfdbe7b63.zip
Vendor import of clang trunk r351319 (just before the release_80 branchvendor/clang/clang-trunk-r351319
Notes
Notes: svn path=/vendor/clang/dist/; revision=343173 svn path=/vendor/clang/clang-trunk-r351319/; revision=343174; tag=vendor/clang/clang-trunk-r351319
Diffstat (limited to 'lib/Analysis')
-rw-r--r--lib/Analysis/AnalysisDeclContext.cpp19
-rw-r--r--lib/Analysis/BodyFarm.cpp60
-rw-r--r--lib/Analysis/CFG.cpp83
-rw-r--r--lib/Analysis/CMakeLists.txt7
-rw-r--r--lib/Analysis/CallGraph.cpp2
-rw-r--r--lib/Analysis/CloneDetection.cpp19
-rw-r--r--lib/Analysis/Consumed.cpp26
-rw-r--r--lib/Analysis/ExprMutationAnalyzer.cpp445
-rw-r--r--lib/Analysis/FormatString.cpp953
-rw-r--r--lib/Analysis/FormatStringParsing.h79
-rw-r--r--lib/Analysis/LiveVariables.cpp61
-rw-r--r--lib/Analysis/OSLog.cpp203
-rw-r--r--lib/Analysis/PrintfFormatString.cpp1029
-rw-r--r--lib/Analysis/ProgramPoint.cpp175
-rw-r--r--lib/Analysis/PseudoConstantAnalysis.cpp226
-rw-r--r--lib/Analysis/ReachableCode.cpp22
-rw-r--r--lib/Analysis/ScanfFormatString.cpp563
-rw-r--r--lib/Analysis/ThreadSafety.cpp410
-rw-r--r--lib/Analysis/ThreadSafetyCommon.cpp59
-rw-r--r--lib/Analysis/ThreadSafetyTIL.cpp18
20 files changed, 1104 insertions, 3355 deletions
diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp
index 9557f68452ff..30160bc239ae 100644
--- a/lib/Analysis/AnalysisDeclContext.cpp
+++ b/lib/Analysis/AnalysisDeclContext.cpp
@@ -27,7 +27,6 @@
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
-#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
#include "clang/Analysis/BodyFarm.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
@@ -137,7 +136,7 @@ bool AnalysisDeclContext::isBodyAutosynthesized() const {
bool AnalysisDeclContext::isBodyAutosynthesizedFromModelFile() const {
bool Tmp;
Stmt *Body = getBody(Tmp);
- return Tmp && Body->getLocStart().isValid();
+ return Tmp && Body->getBeginLoc().isValid();
}
/// Returns true if \param VD is an Objective-C implicit 'self' parameter.
@@ -292,12 +291,6 @@ ParentMap &AnalysisDeclContext::getParentMap() {
return *PM;
}
-PseudoConstantAnalysis *AnalysisDeclContext::getPseudoConstantAnalysis() {
- if (!PCA)
- PCA.reset(new PseudoConstantAnalysis(getBody()));
- return PCA.get();
-}
-
AnalysisDeclContext *AnalysisDeclContextManager::getContext(const Decl *D) {
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
// Calling 'hasBody' replaces 'FD' in place with the FunctionDecl
@@ -392,7 +385,7 @@ LocationContextManager::getLocationContext(AnalysisDeclContext *ctx,
LOC *L = cast_or_null<LOC>(Contexts.FindNodeOrInsertPos(ID, InsertPos));
if (!L) {
- L = new LOC(ctx, parent, d);
+ L = new LOC(ctx, parent, d, ++NewID);
Contexts.InsertNode(L, InsertPos);
}
return L;
@@ -409,7 +402,7 @@ LocationContextManager::getStackFrame(AnalysisDeclContext *ctx,
auto *L =
cast_or_null<StackFrameContext>(Contexts.FindNodeOrInsertPos(ID, InsertPos));
if (!L) {
- L = new StackFrameContext(ctx, parent, s, blk, idx);
+ L = new StackFrameContext(ctx, parent, s, blk, idx, ++NewID);
Contexts.InsertNode(L, InsertPos);
}
return L;
@@ -434,7 +427,7 @@ LocationContextManager::getBlockInvocationContext(AnalysisDeclContext *ctx,
cast_or_null<BlockInvocationContext>(Contexts.FindNodeOrInsertPos(ID,
InsertPos));
if (!L) {
- L = new BlockInvocationContext(ctx, parent, BD, ContextData);
+ L = new BlockInvocationContext(ctx, parent, BD, ContextData, ++NewID);
Contexts.InsertNode(L, InsertPos);
}
return L;
@@ -500,7 +493,7 @@ void LocationContext::dumpStack(
OS << "Calling anonymous code";
if (const Stmt *S = cast<StackFrameContext>(LCtx)->getCallSite()) {
OS << " at ";
- printLocation(OS, SM, S->getLocStart());
+ printLocation(OS, SM, S->getBeginLoc());
}
break;
case Scope:
@@ -510,7 +503,7 @@ void LocationContext::dumpStack(
OS << "Invoking block";
if (const Decl *D = cast<BlockInvocationContext>(LCtx)->getDecl()) {
OS << " defined at ";
- printLocation(OS, SM, D->getLocStart());
+ printLocation(OS, SM, D->getBeginLoc());
}
break;
}
diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp
index ac8fcdc912a0..35f046406763 100644
--- a/lib/Analysis/BodyFarm.cpp
+++ b/lib/Analysis/BodyFarm.cpp
@@ -201,10 +201,9 @@ ObjCIvarRefExpr *ASTMaker::makeObjCIvarRef(const Expr *Base,
/*arrow=*/true, /*free=*/false);
}
-
ReturnStmt *ASTMaker::makeReturn(const Expr *RetVal) {
- return new (C) ReturnStmt(SourceLocation(), const_cast<Expr*>(RetVal),
- nullptr);
+ return ReturnStmt::Create(C, SourceLocation(), const_cast<Expr *>(RetVal),
+ /* NRVOCandidate=*/nullptr);
}
IntegerLiteral *ASTMaker::makeIntegerLiteral(uint64_t Value, QualType Ty) {
@@ -270,8 +269,8 @@ static CallExpr *create_call_once_funcptr_call(ASTContext &C, ASTMaker M,
llvm_unreachable("Unexpected state");
}
- return new (C)
- CallExpr(C, SubExpr, CallArgs, C.VoidTy, VK_RValue, SourceLocation());
+ return CallExpr::Create(C, SubExpr, CallArgs, C.VoidTy, VK_RValue,
+ SourceLocation());
}
static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M,
@@ -293,12 +292,12 @@ static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M,
/* T =*/ callOperatorDecl->getType(),
/* VK =*/ VK_LValue);
- return new (C)
- CXXOperatorCallExpr(/*AstContext=*/C, OO_Call, callOperatorDeclRef,
- /*args=*/CallArgs,
- /*QualType=*/C.VoidTy,
- /*ExprValueType=*/VK_RValue,
- /*SourceLocation=*/SourceLocation(), FPOptions());
+ return CXXOperatorCallExpr::Create(
+ /*AstContext=*/C, OO_Call, callOperatorDeclRef,
+ /*args=*/CallArgs,
+ /*QualType=*/C.VoidTy,
+ /*ExprValueType=*/VK_RValue,
+ /*SourceLocation=*/SourceLocation(), FPOptions());
}
/// Create a fake body for std::call_once.
@@ -464,13 +463,13 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
Deref, M.makeIntegralCast(M.makeIntegerLiteral(1, C.IntTy), DerefType),
DerefType);
- IfStmt *Out = new (C)
- IfStmt(C, SourceLocation(),
- /* IsConstexpr=*/ false,
- /* init=*/ nullptr,
- /* var=*/ nullptr,
- /* cond=*/ FlagCheck,
- /* then=*/ M.makeCompound({CallbackCall, FlagAssignment}));
+ auto *Out =
+ IfStmt::Create(C, SourceLocation(),
+ /* IsConstexpr=*/false,
+ /* init=*/nullptr,
+ /* var=*/nullptr,
+ /* cond=*/FlagCheck,
+ /* then=*/M.makeCompound({CallbackCall, FlagAssignment}));
return Out;
}
@@ -510,7 +509,7 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
ASTMaker M(C);
// (1) Create the call.
- CallExpr *CE = new (C) CallExpr(
+ CallExpr *CE = CallExpr::Create(
/*ASTContext=*/C,
/*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Block),
/*args=*/None,
@@ -549,12 +548,12 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
Expr *GuardCondition = M.makeComparison(LValToRval, DoneValue, BO_NE);
// (5) Create the 'if' statement.
- IfStmt *If = new (C) IfStmt(C, SourceLocation(),
- /* IsConstexpr=*/ false,
- /* init=*/ nullptr,
- /* var=*/ nullptr,
- /* cond=*/ GuardCondition,
- /* then=*/ CS);
+ auto *If = IfStmt::Create(C, SourceLocation(),
+ /* IsConstexpr=*/false,
+ /* init=*/nullptr,
+ /* var=*/nullptr,
+ /* cond=*/GuardCondition,
+ /* then=*/CS);
return If;
}
@@ -580,8 +579,8 @@ static Stmt *create_dispatch_sync(ASTContext &C, const FunctionDecl *D) {
ASTMaker M(C);
DeclRefExpr *DR = M.makeDeclRefExpr(PV);
ImplicitCastExpr *ICE = M.makeLvalueToRvalue(DR, Ty);
- CallExpr *CE = new (C) CallExpr(C, ICE, None, C.VoidTy, VK_RValue,
- SourceLocation());
+ CallExpr *CE =
+ CallExpr::Create(C, ICE, None, C.VoidTy, VK_RValue, SourceLocation());
return CE;
}
@@ -657,8 +656,11 @@ static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D)
Stmt *Else = M.makeReturn(RetVal);
/// Construct the If.
- Stmt *If = new (C) IfStmt(C, SourceLocation(), false, nullptr, nullptr,
- Comparison, Body, SourceLocation(), Else);
+ auto *If = IfStmt::Create(C, SourceLocation(),
+ /* IsConstexpr=*/false,
+ /* init=*/nullptr,
+ /* var=*/nullptr, Comparison, Body,
+ SourceLocation(), Else);
return If;
}
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index 97829de7ace3..96130c25be8a 100644
--- a/lib/Analysis/CFG.cpp
+++ b/lib/Analysis/CFG.cpp
@@ -551,6 +551,7 @@ private:
CFGBlock *VisitGotoStmt(GotoStmt *G);
CFGBlock *VisitIfStmt(IfStmt *I);
CFGBlock *VisitImplicitCastExpr(ImplicitCastExpr *E, AddStmtChoice asc);
+ CFGBlock *VisitConstantExpr(ConstantExpr *E, AddStmtChoice asc);
CFGBlock *VisitIndirectGotoStmt(IndirectGotoStmt *I);
CFGBlock *VisitLabelStmt(LabelStmt *L);
CFGBlock *VisitBlockExpr(BlockExpr *E, AddStmtChoice asc);
@@ -571,7 +572,7 @@ private:
CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S);
CFGBlock *VisitObjCMessageExpr(ObjCMessageExpr *E, AddStmtChoice asc);
CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E);
- CFGBlock *VisitReturnStmt(ReturnStmt *R);
+ CFGBlock *VisitReturnStmt(Stmt *S);
CFGBlock *VisitSEHExceptStmt(SEHExceptStmt *S);
CFGBlock *VisitSEHFinallyStmt(SEHFinallyStmt *S);
CFGBlock *VisitSEHLeaveStmt(SEHLeaveStmt *S);
@@ -1038,12 +1039,14 @@ private:
if (!areExprTypesCompatible(Expr1, Expr2))
return {};
- llvm::APSInt L1, L2;
-
- if (!Expr1->EvaluateAsInt(L1, *Context) ||
- !Expr2->EvaluateAsInt(L2, *Context))
+ Expr::EvalResult L1Result, L2Result;
+ if (!Expr1->EvaluateAsInt(L1Result, *Context) ||
+ !Expr2->EvaluateAsInt(L2Result, *Context))
return {};
+ llvm::APSInt L1 = L1Result.Val.getInt();
+ llvm::APSInt L2 = L2Result.Val.getInt();
+
// Can't compare signed with unsigned or with different bit width.
if (L1.isSigned() != L2.isSigned() || L1.getBitWidth() != L2.getBitWidth())
return {};
@@ -1133,13 +1136,16 @@ private:
case BO_And: {
// If either operand is zero, we know the value
// must be false.
- llvm::APSInt IntVal;
- if (Bop->getLHS()->EvaluateAsInt(IntVal, *Context)) {
+ Expr::EvalResult LHSResult;
+ if (Bop->getLHS()->EvaluateAsInt(LHSResult, *Context)) {
+ llvm::APSInt IntVal = LHSResult.Val.getInt();
if (!IntVal.getBoolValue()) {
return TryResult(false);
}
}
- if (Bop->getRHS()->EvaluateAsInt(IntVal, *Context)) {
+ Expr::EvalResult RHSResult;
+ if (Bop->getRHS()->EvaluateAsInt(RHSResult, *Context)) {
+ llvm::APSInt IntVal = RHSResult.Val.getInt();
if (!IntVal.getBoolValue()) {
return TryResult(false);
}
@@ -1334,6 +1340,7 @@ void CFGBuilder::findConstructionContexts(
case CK_NoOp:
case CK_ConstructorConversion:
findConstructionContexts(Layer, Cast->getSubExpr());
+ break;
default:
break;
}
@@ -2099,6 +2106,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
case Stmt::ImplicitCastExprClass:
return VisitImplicitCastExpr(cast<ImplicitCastExpr>(S), asc);
+ case Stmt::ConstantExprClass:
+ return VisitConstantExpr(cast<ConstantExpr>(S), asc);
+
case Stmt::IndirectGotoStmtClass:
return VisitIndirectGotoStmt(cast<IndirectGotoStmt>(S));
@@ -2146,7 +2156,8 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
return VisitPseudoObjectExpr(cast<PseudoObjectExpr>(S));
case Stmt::ReturnStmtClass:
- return VisitReturnStmt(cast<ReturnStmt>(S));
+ case Stmt::CoreturnStmtClass:
+ return VisitReturnStmt(S);
case Stmt::SEHExceptStmtClass:
return VisitSEHExceptStmt(cast<SEHExceptStmt>(S));
@@ -2421,8 +2432,6 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
if (!boundType.isNull()) calleeType = boundType;
}
- findConstructionContextsForArguments(C);
-
// If this is a call to a no-return function, this stops the block here.
bool NoReturn = getFunctionExtInfo(*calleeType).getNoReturn();
@@ -2439,6 +2448,13 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
bool OmitArguments = false;
if (FunctionDecl *FD = C->getDirectCallee()) {
+ // TODO: Support construction contexts for variadic function arguments.
+ // These are a bit problematic and not very useful because passing
+ // C++ objects as C-style variadic arguments doesn't work in general
+ // (see [expr.call]).
+ if (!FD->isVariadic())
+ findConstructionContextsForArguments(C);
+
if (FD->isNoReturn() || C->isBuiltinAssumeFalse(*Context))
NoReturn = true;
if (FD->hasAttr<NoThrowAttr>())
@@ -2627,15 +2643,12 @@ CFGBlock *CFGBuilder::VisitDeclStmt(DeclStmt *DS) {
for (DeclStmt::reverse_decl_iterator I = DS->decl_rbegin(),
E = DS->decl_rend();
I != E; ++I) {
- // Get the alignment of the new DeclStmt, padding out to >=8 bytes.
- unsigned A = alignof(DeclStmt) < 8 ? 8 : alignof(DeclStmt);
// Allocate the DeclStmt using the BumpPtrAllocator. It will get
// automatically freed with the CFG.
DeclGroupRef DG(*I);
Decl *D = *I;
- void *Mem = cfg->getAllocator().Allocate(sizeof(DeclStmt), A);
- DeclStmt *DSNew = new (Mem) DeclStmt(DG, D->getLocation(), GetEndLoc(D));
+ DeclStmt *DSNew = new (Context) DeclStmt(DG, D->getLocation(), GetEndLoc(D));
cfg->addSyntheticDeclStmt(DSNew, DS);
// Append the fake DeclStmt to block.
@@ -2874,22 +2887,24 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {
return LastBlock;
}
-CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) {
+CFGBlock *CFGBuilder::VisitReturnStmt(Stmt *S) {
// If we were in the middle of a block we stop processing that block.
//
- // NOTE: If a "return" appears in the middle of a block, this means that the
- // code afterwards is DEAD (unreachable). We still keep a basic block
- // for that code; a simple "mark-and-sweep" from the entry block will be
- // able to report such dead blocks.
+ // NOTE: If a "return" or "co_return" appears in the middle of a block, this
+ // means that the code afterwards is DEAD (unreachable). We still keep
+ // a basic block for that code; a simple "mark-and-sweep" from the entry
+ // block will be able to report such dead blocks.
+ assert(isa<ReturnStmt>(S) || isa<CoreturnStmt>(S));
// Create the new block.
Block = createBlock(false);
- addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R);
+ addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), S);
- findConstructionContexts(
- ConstructionContextLayer::create(cfg->getBumpVectorContext(), R),
- R->getRetValue());
+ if (auto *R = dyn_cast<ReturnStmt>(S))
+ findConstructionContexts(
+ ConstructionContextLayer::create(cfg->getBumpVectorContext(), R),
+ R->getRetValue());
// If the one of the destructors does not return, we already have the Exit
// block as a successor.
@@ -2898,7 +2913,7 @@ CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) {
// Add the return statement to the block. This may create new blocks if R
// contains control-flow (short-circuit operations).
- return VisitStmt(R, AddStmtChoice::AlwaysAdd);
+ return VisitStmt(S, AddStmtChoice::AlwaysAdd);
}
CFGBlock *CFGBuilder::VisitSEHExceptStmt(SEHExceptStmt *ES) {
@@ -4250,7 +4265,10 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
Block = createBlock();
addStmt(S->getBeginStmt());
addStmt(S->getEndStmt());
- return addStmt(S->getRangeStmt());
+ CFGBlock *Head = addStmt(S->getRangeStmt());
+ if (S->getInit())
+ Head = addStmt(S->getInit());
+ return Head;
}
CFGBlock *CFGBuilder::VisitExprWithCleanups(ExprWithCleanups *E,
@@ -4352,6 +4370,11 @@ CFGBlock *CFGBuilder::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E,
CFGBlock *CFGBuilder::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *C,
AddStmtChoice asc) {
+ // If the constructor takes objects as arguments by value, we need to properly
+ // construct these objects. Construction contexts we find here aren't for the
+ // constructor C, they're for its arguments only.
+ findConstructionContextsForArguments(C);
+
autoCreateBlock();
appendConstructor(Block, C);
return VisitChildren(C);
@@ -4366,6 +4389,10 @@ CFGBlock *CFGBuilder::VisitImplicitCastExpr(ImplicitCastExpr *E,
return Visit(E->getSubExpr(), AddStmtChoice());
}
+CFGBlock *CFGBuilder::VisitConstantExpr(ConstantExpr *E, AddStmtChoice asc) {
+ return Visit(E->getSubExpr(), AddStmtChoice());
+}
+
CFGBlock *CFGBuilder::VisitIndirectGotoStmt(IndirectGotoStmt *I) {
// Lazily create the indirect-goto dispatch block if there isn't one already.
CFGBlock *IBlock = cfg->getIndirectGotoBlock();
@@ -4422,6 +4449,10 @@ tryAgain:
E = cast<CXXFunctionalCastExpr>(E)->getSubExpr();
goto tryAgain;
+ case Stmt::ConstantExprClass:
+ E = cast<ConstantExpr>(E)->getSubExpr();
+ goto tryAgain;
+
case Stmt::ParenExprClass:
E = cast<ParenExpr>(E)->getSubExpr();
goto tryAgain;
diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt
index 432067d98157..5345a56f2002 100644
--- a/lib/Analysis/CMakeLists.txt
+++ b/lib/Analysis/CMakeLists.txt
@@ -15,16 +15,12 @@ add_clang_library(clangAnalysis
Consumed.cpp
CodeInjector.cpp
Dominators.cpp
- FormatString.cpp
+ ExprMutationAnalyzer.cpp
LiveVariables.cpp
- OSLog.cpp
ObjCNoReturn.cpp
PostOrderCFGView.cpp
- PrintfFormatString.cpp
ProgramPoint.cpp
- PseudoConstantAnalysis.cpp
ReachableCode.cpp
- ScanfFormatString.cpp
ThreadSafety.cpp
ThreadSafetyCommon.cpp
ThreadSafetyLogical.cpp
@@ -33,6 +29,7 @@ add_clang_library(clangAnalysis
LINK_LIBS
clangAST
+ clangASTMatchers
clangBasic
clangLex
)
diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp
index bac00680ffda..66a6f1a9bcea 100644
--- a/lib/Analysis/CallGraph.cpp
+++ b/lib/Analysis/CallGraph.cpp
@@ -212,7 +212,7 @@ void CallGraph::viewGraph() const {
void CallGraphNode::print(raw_ostream &os) const {
if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(FD))
- return ND->printName(os);
+ return ND->printQualifiedName(os);
os << "< >";
}
diff --git a/lib/Analysis/CloneDetection.cpp b/lib/Analysis/CloneDetection.cpp
index 8912b3b76751..88402e2adaa7 100644
--- a/lib/Analysis/CloneDetection.cpp
+++ b/lib/Analysis/CloneDetection.cpp
@@ -7,7 +7,7 @@
//
//===----------------------------------------------------------------------===//
///
-/// This file implements classes for searching and anlyzing source code clones.
+/// This file implements classes for searching and analyzing source code clones.
///
//===----------------------------------------------------------------------===//
@@ -45,8 +45,8 @@ bool StmtSequence::contains(const StmtSequence &Other) const {
// Otherwise check if the start and end locations of the current sequence
// surround the other sequence.
bool StartIsInBounds =
- SM.isBeforeInTranslationUnit(getStartLoc(), Other.getStartLoc()) ||
- getStartLoc() == Other.getStartLoc();
+ SM.isBeforeInTranslationUnit(getBeginLoc(), Other.getBeginLoc()) ||
+ getBeginLoc() == Other.getBeginLoc();
if (!StartIsInBounds)
return false;
@@ -77,14 +77,14 @@ ASTContext &StmtSequence::getASTContext() const {
return D->getASTContext();
}
-SourceLocation StmtSequence::getStartLoc() const {
- return front()->getLocStart();
+SourceLocation StmtSequence::getBeginLoc() const {
+ return front()->getBeginLoc();
}
-SourceLocation StmtSequence::getEndLoc() const { return back()->getLocEnd(); }
+SourceLocation StmtSequence::getEndLoc() const { return back()->getEndLoc(); }
SourceRange StmtSequence::getSourceRange() const {
- return SourceRange(getStartLoc(), getEndLoc());
+ return SourceRange(getBeginLoc(), getEndLoc());
}
void CloneDetector::analyzeCodeBody(const Decl *D) {
@@ -433,7 +433,7 @@ size_t MinComplexityConstraint::calculateStmtComplexity(
// Look up what macros expanded into the current statement.
std::string MacroStack =
- data_collection::getMacroStack(Seq.getStartLoc(), Context);
+ data_collection::getMacroStack(Seq.getBeginLoc(), Context);
// First, check if ParentMacroStack is not empty which means we are currently
// dealing with a parent statement which was expanded from a macro.
@@ -523,8 +523,7 @@ void CloneConstraint::splitCloneGroups(
Result.push_back(PotentialGroup);
}
- assert(std::all_of(Indexes.begin(), Indexes.end(),
- [](char c) { return c == 1; }));
+ assert(llvm::all_of(Indexes, [](char c) { return c == 1; }));
}
CloneGroups = Result;
}
diff --git a/lib/Analysis/Consumed.cpp b/lib/Analysis/Consumed.cpp
index bc81a71b3d91..16eeaba2f61b 100644
--- a/lib/Analysis/Consumed.cpp
+++ b/lib/Analysis/Consumed.cpp
@@ -64,7 +64,7 @@ static SourceLocation getFirstStmtLoc(const CFGBlock *Block) {
// is not empty.
for (const auto &B : *Block)
if (Optional<CFGStmt> CS = B.getAs<CFGStmt>())
- return CS->getStmt()->getLocStart();
+ return CS->getStmt()->getBeginLoc();
// Block is empty.
// If we have one successor, return the first statement in that block
@@ -78,12 +78,12 @@ static SourceLocation getLastStmtLoc(const CFGBlock *Block) {
// Find the source location of the last statement in the block, if the block
// is not empty.
if (const Stmt *StmtNode = Block->getTerminator()) {
- return StmtNode->getLocStart();
+ return StmtNode->getBeginLoc();
} else {
for (CFGBlock::const_reverse_iterator BI = Block->rbegin(),
BE = Block->rend(); BI != BE; ++BI) {
if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>())
- return CS->getStmt()->getLocStart();
+ return CS->getStmt()->getBeginLoc();
}
}
@@ -463,7 +463,6 @@ class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
using InfoEntry = MapType::iterator;
using ConstInfoEntry = MapType::const_iterator;
- AnalysisDeclContext &AC;
ConsumedAnalyzer &Analyzer;
ConsumedStateMap *StateMap;
MapType PropagationMap;
@@ -515,9 +514,8 @@ public:
void VisitUnaryOperator(const UnaryOperator *UOp);
void VisitVarDecl(const VarDecl *Var);
- ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer,
- ConsumedStateMap *StateMap)
- : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {}
+ ConsumedStmtVisitor(ConsumedAnalyzer &Analyzer, ConsumedStateMap *StateMap)
+ : Analyzer(Analyzer), StateMap(StateMap) {}
PropagationInfo getInfo(const Expr *StmtNode) const {
ConstInfoEntry Entry = findInfo(StmtNode);
@@ -774,8 +772,7 @@ void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr(
void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
CXXConstructorDecl *Constructor = Call->getConstructor();
- ASTContext &CurrContext = AC.getASTContext();
- QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
+ QualType ThisType = Constructor->getThisType()->getPointeeType();
if (!isConsumableType(ThisType))
return;
@@ -793,7 +790,7 @@ void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
} else if (Constructor->isCopyConstructor()) {
// Copy state from arg. If setStateOnRead then set arg to CS_Unknown.
ConsumedState NS =
- isSetOnReadPtrType(Constructor->getThisType(CurrContext)) ?
+ isSetOnReadPtrType(Constructor->getThisType()) ?
CS_Unknown : CS_None;
copyInfo(Call->getArg(0), Call, NS);
} else {
@@ -893,7 +890,7 @@ void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) {
}
}
- StateMap->checkParamsForReturnTypestate(Ret->getLocStart(),
+ StateMap->checkParamsForReturnTypestate(Ret->getBeginLoc(),
Analyzer.WarningsHandler);
}
@@ -1203,8 +1200,7 @@ void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC,
const FunctionDecl *D) {
QualType ReturnType;
if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
- ASTContext &CurrContext = AC.getASTContext();
- ReturnType = Constructor->getThisType(CurrContext)->getPointeeType();
+ ReturnType = Constructor->getThisType()->getPointeeType();
} else
ReturnType = D->getCallResultType();
@@ -1323,7 +1319,7 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
BlockInfo = ConsumedBlockInfo(CFGraph->getNumBlockIDs(), SortedGraph);
CurrStates = llvm::make_unique<ConsumedStateMap>();
- ConsumedStmtVisitor Visitor(AC, *this, CurrStates.get());
+ ConsumedStmtVisitor Visitor(*this, CurrStates.get());
// Add all trackable parameters to the state map.
for (const auto *PI : D->parameters())
@@ -1363,7 +1359,7 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
case CFGElement::AutomaticObjectDtor: {
const CFGAutomaticObjDtor &DTor = B.castAs<CFGAutomaticObjDtor>();
- SourceLocation Loc = DTor.getTriggerStmt()->getLocEnd();
+ SourceLocation Loc = DTor.getTriggerStmt()->getEndLoc();
const VarDecl *Var = DTor.getVarDecl();
Visitor.checkCallability(PropagationInfo(Var),
diff --git a/lib/Analysis/ExprMutationAnalyzer.cpp b/lib/Analysis/ExprMutationAnalyzer.cpp
new file mode 100644
index 000000000000..8414cb5c726a
--- /dev/null
+++ b/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -0,0 +1,445 @@
+//===---------- ExprMutationAnalyzer.cpp ----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace clang {
+using namespace ast_matchers;
+
+namespace {
+
+AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) {
+ return llvm::is_contained(Node.capture_inits(), E);
+}
+
+AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
+ ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) {
+ const DeclStmt *const Range = Node.getRangeStmt();
+ return InnerMatcher.matches(*Range, Finder, Builder);
+}
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
+ cxxTypeidExpr;
+
+AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
+ return Node.isPotentiallyEvaluated();
+}
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXNoexceptExpr>
+ cxxNoexceptExpr;
+
+const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt,
+ GenericSelectionExpr>
+ genericSelectionExpr;
+
+AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
+ ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+ return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder);
+}
+
+const auto nonConstReferenceType = [] {
+ return hasUnqualifiedDesugaredType(
+ referenceType(pointee(unless(isConstQualified()))));
+};
+
+const auto nonConstPointerType = [] {
+ return hasUnqualifiedDesugaredType(
+ pointerType(pointee(unless(isConstQualified()))));
+};
+
+const auto isMoveOnly = [] {
+ return cxxRecordDecl(
+ hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))),
+ hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))),
+ unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(),
+ unless(isDeleted()))),
+ hasMethod(cxxMethodDecl(isCopyAssignmentOperator(),
+ unless(isDeleted()))))));
+};
+
+template <class T> struct NodeID;
+template <> struct NodeID<Expr> { static const std::string value; };
+template <> struct NodeID<Decl> { static const std::string value; };
+const std::string NodeID<Expr>::value = "expr";
+const std::string NodeID<Decl>::value = "decl";
+
+template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)>
+const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches,
+ ExprMutationAnalyzer *Analyzer, F Finder) {
+ const StringRef ID = NodeID<T>::value;
+ for (const auto &Nodes : Matches) {
+ if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID)))
+ return S;
+ }
+ return nullptr;
+}
+
+} // namespace
+
+const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) {
+ return findMutationMemoized(Exp,
+ {&ExprMutationAnalyzer::findDirectMutation,
+ &ExprMutationAnalyzer::findMemberMutation,
+ &ExprMutationAnalyzer::findArrayElementMutation,
+ &ExprMutationAnalyzer::findCastMutation,
+ &ExprMutationAnalyzer::findRangeLoopMutation,
+ &ExprMutationAnalyzer::findReferenceMutation,
+ &ExprMutationAnalyzer::findFunctionArgMutation},
+ Results);
+}
+
+const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) {
+ return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation);
+}
+
+const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) {
+ return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults);
+}
+
+const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) {
+ return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation);
+}
+
+const Stmt *ExprMutationAnalyzer::findMutationMemoized(
+ const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders,
+ ResultMap &MemoizedResults) {
+ const auto Memoized = MemoizedResults.find(Exp);
+ if (Memoized != MemoizedResults.end())
+ return Memoized->second;
+
+ if (isUnevaluated(Exp))
+ return MemoizedResults[Exp] = nullptr;
+
+ for (const auto &Finder : Finders) {
+ if (const Stmt *S = (this->*Finder)(Exp))
+ return MemoizedResults[Exp] = S;
+ }
+
+ return MemoizedResults[Exp] = nullptr;
+}
+
+const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec,
+ MutationFinder Finder) {
+ const auto Refs =
+ match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)),
+ Stm, Context);
+ for (const auto &RefNodes : Refs) {
+ const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value);
+ if ((this->*Finder)(E))
+ return E;
+ }
+ return nullptr;
+}
+
+bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) {
+ return selectFirst<Expr>(
+ NodeID<Expr>::value,
+ match(
+ findAll(
+ expr(equalsNode(Exp),
+ anyOf(
+ // `Exp` is part of the underlying expression of
+ // decltype/typeof if it has an ancestor of
+ // typeLoc.
+ hasAncestor(typeLoc(unless(
+ hasAncestor(unaryExprOrTypeTraitExpr())))),
+ hasAncestor(expr(anyOf(
+ // `UnaryExprOrTypeTraitExpr` is unevaluated
+ // unless it's sizeof on VLA.
+ unaryExprOrTypeTraitExpr(unless(sizeOfExpr(
+ hasArgumentOfType(variableArrayType())))),
+ // `CXXTypeidExpr` is unevaluated unless it's
+ // applied to an expression of glvalue of
+ // polymorphic class type.
+ cxxTypeidExpr(
+ unless(isPotentiallyEvaluated())),
+ // The controlling expression of
+ // `GenericSelectionExpr` is unevaluated.
+ genericSelectionExpr(hasControllingExpr(
+ hasDescendant(equalsNode(Exp)))),
+ cxxNoexceptExpr())))))
+ .bind(NodeID<Expr>::value)),
+ Stm, Context)) != nullptr;
+}
+
+const Stmt *
+ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) {
+ return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation);
+}
+
+const Stmt *
+ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) {
+ return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation);
+}
+
+const Stmt *ExprMutationAnalyzer::findExprPointeeMutation(
+ ArrayRef<ast_matchers::BoundNodes> Matches) {
+ return tryEachMatch<Expr>(Matches, this,
+ &ExprMutationAnalyzer::findPointeeMutation);
+}
+
+const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation(
+ ArrayRef<ast_matchers::BoundNodes> Matches) {
+ return tryEachMatch<Decl>(Matches, this,
+ &ExprMutationAnalyzer::findPointeeMutation);
+}
+
+const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
+ // LHS of any assignment operators.
+ const auto AsAssignmentLhs =
+ binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(Exp)));
+
+ // Operand of increment/decrement operators.
+ const auto AsIncDecOperand =
+ unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
+ hasUnaryOperand(equalsNode(Exp)));
+
+ // Invoking non-const member function.
+ // A member function is assumed to be non-const when it is unresolved.
+ const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
+ const auto AsNonConstThis =
+ expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))),
+ cxxOperatorCallExpr(callee(NonConstMethod),
+ hasArgument(0, equalsNode(Exp))),
+ callExpr(callee(expr(anyOf(
+ unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))),
+ cxxDependentScopeMemberExpr(
+ hasObjectExpression(equalsNode(Exp)))))))));
+
+ // Taking address of 'Exp'.
+ // We're assuming 'Exp' is mutated as soon as its address is taken, though in
+ // theory we can follow the pointer and see whether it escaped `Stm` or is
+ // dereferenced and then mutated. This is left for future improvements.
+ const auto AsAmpersandOperand =
+ unaryOperator(hasOperatorName("&"),
+ // A NoOp implicit cast is adding const.
+ unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
+ hasUnaryOperand(equalsNode(Exp)));
+ const auto AsPointerFromArrayDecay =
+ castExpr(hasCastKind(CK_ArrayToPointerDecay),
+ unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp)));
+ // Treat calling `operator->()` of move-only classes as taking address.
+ // These are typically smart pointers with unique ownership so we treat
+ // mutation of pointee as mutation of the smart pointer itself.
+ const auto AsOperatorArrowThis =
+ cxxOperatorCallExpr(hasOverloadedOperatorName("->"),
+ callee(cxxMethodDecl(ofClass(isMoveOnly()),
+ returns(nonConstPointerType()))),
+ argumentCountIs(1), hasArgument(0, equalsNode(Exp)));
+
+ // Used as non-const-ref argument when calling a function.
+ // An argument is assumed to be non-const-ref when the function is unresolved.
+ // Instantiated template functions are not handled here but in
+ // findFunctionArgMutation which has additional smarts for handling forwarding
+ // references.
+ const auto NonConstRefParam = forEachArgumentWithParam(
+ equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType())));
+ const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
+ const auto AsNonConstRefArg = anyOf(
+ callExpr(NonConstRefParam, NotInstantiated),
+ cxxConstructExpr(NonConstRefParam, NotInstantiated),
+ callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
+ cxxDependentScopeMemberExpr(),
+ hasType(templateTypeParmType())))),
+ hasAnyArgument(equalsNode(Exp))),
+ cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp))));
+
+ // Captured by a lambda by reference.
+ // If we're initializing a capture with 'Exp' directly then we're initializing
+ // a reference capture.
+ // For value captures there will be an ImplicitCastExpr <LValueToRValue>.
+ const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp));
+
+ // Returned as non-const-ref.
+ // If we're returning 'Exp' directly then it's returned as non-const-ref.
+ // For returning by value there will be an ImplicitCastExpr <LValueToRValue>.
+ // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for
+ // adding const.)
+ const auto AsNonConstRefReturn = returnStmt(hasReturnValue(equalsNode(Exp)));
+
+ const auto Matches =
+ match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
+ AsAmpersandOperand, AsPointerFromArrayDecay,
+ AsOperatorArrowThis, AsNonConstRefArg,
+ AsLambdaRefCaptureInit, AsNonConstRefReturn))
+ .bind("stmt")),
+ Stm, Context);
+ return selectFirst<Stmt>("stmt", Matches);
+}
+
+const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) {
+ // Check whether any member of 'Exp' is mutated.
+ const auto MemberExprs =
+ match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))),
+ cxxDependentScopeMemberExpr(
+ hasObjectExpression(equalsNode(Exp)))))
+ .bind(NodeID<Expr>::value)),
+ Stm, Context);
+ return findExprMutation(MemberExprs);
+}
+
+const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) {
+ // Check whether any element of an array is mutated.
+ const auto SubscriptExprs = match(
+ findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp))))
+ .bind(NodeID<Expr>::value)),
+ Stm, Context);
+ return findExprMutation(SubscriptExprs);
+}
+
+const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) {
+ // If 'Exp' is casted to any non-const reference type, check the castExpr.
+ const auto Casts =
+ match(findAll(castExpr(hasSourceExpression(equalsNode(Exp)),
+ anyOf(explicitCastExpr(hasDestinationType(
+ nonConstReferenceType())),
+ implicitCastExpr(hasImplicitDestinationType(
+ nonConstReferenceType()))))
+ .bind(NodeID<Expr>::value)),
+ Stm, Context);
+ if (const Stmt *S = findExprMutation(Casts))
+ return S;
+ // Treat std::{move,forward} as cast.
+ const auto Calls =
+ match(findAll(callExpr(callee(namedDecl(
+ hasAnyName("::std::move", "::std::forward"))),
+ hasArgument(0, equalsNode(Exp)))
+ .bind("expr")),
+ Stm, Context);
+ return findExprMutation(Calls);
+}
+
+const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) {
+ // If range for looping over 'Exp' with a non-const reference loop variable,
+ // check all declRefExpr of the loop variable.
+ const auto LoopVars =
+ match(findAll(cxxForRangeStmt(
+ hasLoopVariable(varDecl(hasType(nonConstReferenceType()))
+ .bind(NodeID<Decl>::value)),
+ hasRangeInit(equalsNode(Exp)))),
+ Stm, Context);
+ return findDeclMutation(LoopVars);
+}
+
+const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) {
+ // Follow non-const reference returned by `operator*()` of move-only classes.
+ // These are typically smart pointers with unique ownership so we treat
+ // mutation of pointee as mutation of the smart pointer itself.
+ const auto Ref =
+ match(findAll(cxxOperatorCallExpr(
+ hasOverloadedOperatorName("*"),
+ callee(cxxMethodDecl(ofClass(isMoveOnly()),
+ returns(nonConstReferenceType()))),
+ argumentCountIs(1), hasArgument(0, equalsNode(Exp)))
+ .bind(NodeID<Expr>::value)),
+ Stm, Context);
+ if (const Stmt *S = findExprMutation(Ref))
+ return S;
+
+ // If 'Exp' is bound to a non-const reference, check all declRefExpr to that.
+ const auto Refs = match(
+ stmt(forEachDescendant(
+ varDecl(
+ hasType(nonConstReferenceType()),
+ hasInitializer(anyOf(equalsNode(Exp),
+ conditionalOperator(anyOf(
+ hasTrueExpression(equalsNode(Exp)),
+ hasFalseExpression(equalsNode(Exp)))))),
+ hasParent(declStmt().bind("stmt")),
+ // Don't follow the reference in range statement, we've handled
+ // that separately.
+ unless(hasParent(declStmt(hasParent(
+ cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt"))))))))
+ .bind(NodeID<Decl>::value))),
+ Stm, Context);
+ return findDeclMutation(Refs);
+}
+
+const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) {
+ const auto NonConstRefParam = forEachArgumentWithParam(
+ equalsNode(Exp),
+ parmVarDecl(hasType(nonConstReferenceType())).bind("parm"));
+ const auto IsInstantiated = hasDeclaration(isInstantiated());
+ const auto FuncDecl = hasDeclaration(functionDecl().bind("func"));
+ const auto Matches = match(
+ findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl,
+ unless(callee(namedDecl(hasAnyName(
+ "::std::move", "::std::forward"))))),
+ cxxConstructExpr(NonConstRefParam, IsInstantiated,
+ FuncDecl)))
+ .bind(NodeID<Expr>::value)),
+ Stm, Context);
+ for (const auto &Nodes : Matches) {
+ const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value);
+ const auto *Func = Nodes.getNodeAs<FunctionDecl>("func");
+ if (!Func->getBody() || !Func->getPrimaryTemplate())
+ return Exp;
+
+ const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm");
+ const ArrayRef<ParmVarDecl *> AllParams =
+ Func->getPrimaryTemplate()->getTemplatedDecl()->parameters();
+ QualType ParmType =
+ AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(),
+ AllParams.size() - 1)]
+ ->getType();
+ if (const auto *T = ParmType->getAs<PackExpansionType>())
+ ParmType = T->getPattern();
+
+ // If param type is forwarding reference, follow into the function
+ // definition and see whether the param is mutated inside.
+ if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) {
+ if (!RefType->getPointeeType().getQualifiers() &&
+ RefType->getPointeeType()->getAs<TemplateTypeParmType>()) {
+ std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer =
+ FuncParmAnalyzer[Func];
+ if (!Analyzer)
+ Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context));
+ if (Analyzer->findMutation(Parm))
+ return Exp;
+ continue;
+ }
+ }
+ // Not forwarding reference.
+ return Exp;
+ }
+ return nullptr;
+}
+
+FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer(
+ const FunctionDecl &Func, ASTContext &Context)
+ : BodyAnalyzer(*Func.getBody(), Context) {
+ if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) {
+ // CXXCtorInitializer might also mutate Param but they're not part of
+ // function body, check them eagerly here since they're typically trivial.
+ for (const CXXCtorInitializer *Init : Ctor->inits()) {
+ ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context);
+ for (const ParmVarDecl *Parm : Ctor->parameters()) {
+ if (Results.find(Parm) != Results.end())
+ continue;
+ if (const Stmt *S = InitAnalyzer.findMutation(Parm))
+ Results[Parm] = S;
+ }
+ }
+ }
+}
+
+const Stmt *
+FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) {
+ const auto Memoized = Results.find(Parm);
+ if (Memoized != Results.end())
+ return Memoized->second;
+
+ if (const Stmt *S = BodyAnalyzer.findMutation(Parm))
+ return Results[Parm] = S;
+
+ return Results[Parm] = nullptr;
+}
+
+} // namespace clang
diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp
deleted file mode 100644
index f37e4affae3f..000000000000
--- a/lib/Analysis/FormatString.cpp
+++ /dev/null
@@ -1,953 +0,0 @@
-// FormatString.cpp - Common stuff for handling printf/scanf formats -*- C++ -*-
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Shared details for processing format strings of printf and scanf
-// (and friends).
-//
-//===----------------------------------------------------------------------===//
-
-#include "FormatStringParsing.h"
-#include "clang/Basic/LangOptions.h"
-#include "clang/Basic/TargetInfo.h"
-#include "llvm/Support/ConvertUTF.h"
-
-using clang::analyze_format_string::ArgType;
-using clang::analyze_format_string::FormatStringHandler;
-using clang::analyze_format_string::FormatSpecifier;
-using clang::analyze_format_string::LengthModifier;
-using clang::analyze_format_string::OptionalAmount;
-using clang::analyze_format_string::PositionContext;
-using clang::analyze_format_string::ConversionSpecifier;
-using namespace clang;
-
-// Key function to FormatStringHandler.
-FormatStringHandler::~FormatStringHandler() {}
-
-//===----------------------------------------------------------------------===//
-// Functions for parsing format strings components in both printf and
-// scanf format strings.
-//===----------------------------------------------------------------------===//
-
-OptionalAmount
-clang::analyze_format_string::ParseAmount(const char *&Beg, const char *E) {
- const char *I = Beg;
- UpdateOnReturn <const char*> UpdateBeg(Beg, I);
-
- unsigned accumulator = 0;
- bool hasDigits = false;
-
- for ( ; I != E; ++I) {
- char c = *I;
- if (c >= '0' && c <= '9') {
- hasDigits = true;
- accumulator = (accumulator * 10) + (c - '0');
- continue;
- }
-
- if (hasDigits)
- return OptionalAmount(OptionalAmount::Constant, accumulator, Beg, I - Beg,
- false);
-
- break;
- }
-
- return OptionalAmount();
-}
-
-OptionalAmount
-clang::analyze_format_string::ParseNonPositionAmount(const char *&Beg,
- const char *E,
- unsigned &argIndex) {
- if (*Beg == '*') {
- ++Beg;
- return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false);
- }
-
- return ParseAmount(Beg, E);
-}
-
-OptionalAmount
-clang::analyze_format_string::ParsePositionAmount(FormatStringHandler &H,
- const char *Start,
- const char *&Beg,
- const char *E,
- PositionContext p) {
- if (*Beg == '*') {
- const char *I = Beg + 1;
- const OptionalAmount &Amt = ParseAmount(I, E);
-
- if (Amt.getHowSpecified() == OptionalAmount::NotSpecified) {
- H.HandleInvalidPosition(Beg, I - Beg, p);
- return OptionalAmount(false);
- }
-
- if (I == E) {
- // No more characters left?
- H.HandleIncompleteSpecifier(Start, E - Start);
- return OptionalAmount(false);
- }
-
- assert(Amt.getHowSpecified() == OptionalAmount::Constant);
-
- if (*I == '$') {
- // Handle positional arguments
-
- // Special case: '*0$', since this is an easy mistake.
- if (Amt.getConstantAmount() == 0) {
- H.HandleZeroPosition(Beg, I - Beg + 1);
- return OptionalAmount(false);
- }
-
- const char *Tmp = Beg;
- Beg = ++I;
-
- return OptionalAmount(OptionalAmount::Arg, Amt.getConstantAmount() - 1,
- Tmp, 0, true);
- }
-
- H.HandleInvalidPosition(Beg, I - Beg, p);
- return OptionalAmount(false);
- }
-
- return ParseAmount(Beg, E);
-}
-
-
-bool
-clang::analyze_format_string::ParseFieldWidth(FormatStringHandler &H,
- FormatSpecifier &CS,
- const char *Start,
- const char *&Beg, const char *E,
- unsigned *argIndex) {
- // FIXME: Support negative field widths.
- if (argIndex) {
- CS.setFieldWidth(ParseNonPositionAmount(Beg, E, *argIndex));
- }
- else {
- const OptionalAmount Amt =
- ParsePositionAmount(H, Start, Beg, E,
- analyze_format_string::FieldWidthPos);
-
- if (Amt.isInvalid())
- return true;
- CS.setFieldWidth(Amt);
- }
- return false;
-}
-
-bool
-clang::analyze_format_string::ParseArgPosition(FormatStringHandler &H,
- FormatSpecifier &FS,
- const char *Start,
- const char *&Beg,
- const char *E) {
- const char *I = Beg;
-
- const OptionalAmount &Amt = ParseAmount(I, E);
-
- if (I == E) {
- // No more characters left?
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
-
- if (Amt.getHowSpecified() == OptionalAmount::Constant && *(I++) == '$') {
- // Warn that positional arguments are non-standard.
- H.HandlePosition(Start, I - Start);
-
- // Special case: '%0$', since this is an easy mistake.
- if (Amt.getConstantAmount() == 0) {
- H.HandleZeroPosition(Start, I - Start);
- return true;
- }
-
- FS.setArgIndex(Amt.getConstantAmount() - 1);
- FS.setUsesPositionalArg();
- // Update the caller's pointer if we decided to consume
- // these characters.
- Beg = I;
- return false;
- }
-
- return false;
-}
-
-bool
-clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS,
- const char *&I,
- const char *E,
- const LangOptions &LO,
- bool IsScanf) {
- LengthModifier::Kind lmKind = LengthModifier::None;
- const char *lmPosition = I;
- switch (*I) {
- default:
- return false;
- case 'h':
- ++I;
- if (I != E && *I == 'h') {
- ++I;
- lmKind = LengthModifier::AsChar;
- } else {
- lmKind = LengthModifier::AsShort;
- }
- break;
- case 'l':
- ++I;
- if (I != E && *I == 'l') {
- ++I;
- lmKind = LengthModifier::AsLongLong;
- } else {
- lmKind = LengthModifier::AsLong;
- }
- break;
- case 'j': lmKind = LengthModifier::AsIntMax; ++I; break;
- case 'z': lmKind = LengthModifier::AsSizeT; ++I; break;
- case 't': lmKind = LengthModifier::AsPtrDiff; ++I; break;
- case 'L': lmKind = LengthModifier::AsLongDouble; ++I; break;
- case 'q': lmKind = LengthModifier::AsQuad; ++I; break;
- case 'a':
- if (IsScanf && !LO.C99 && !LO.CPlusPlus11) {
- // For scanf in C90, look at the next character to see if this should
- // be parsed as the GNU extension 'a' length modifier. If not, this
- // will be parsed as a conversion specifier.
- ++I;
- if (I != E && (*I == 's' || *I == 'S' || *I == '[')) {
- lmKind = LengthModifier::AsAllocate;
- break;
- }
- --I;
- }
- return false;
- case 'm':
- if (IsScanf) {
- lmKind = LengthModifier::AsMAllocate;
- ++I;
- break;
- }
- return false;
- // printf: AsInt64, AsInt32, AsInt3264
- // scanf: AsInt64
- case 'I':
- if (I + 1 != E && I + 2 != E) {
- if (I[1] == '6' && I[2] == '4') {
- I += 3;
- lmKind = LengthModifier::AsInt64;
- break;
- }
- if (IsScanf)
- return false;
-
- if (I[1] == '3' && I[2] == '2') {
- I += 3;
- lmKind = LengthModifier::AsInt32;
- break;
- }
- }
- ++I;
- lmKind = LengthModifier::AsInt3264;
- break;
- case 'w':
- lmKind = LengthModifier::AsWide; ++I; break;
- }
- LengthModifier lm(lmPosition, lmKind);
- FS.setLengthModifier(lm);
- return true;
-}
-
-bool clang::analyze_format_string::ParseUTF8InvalidSpecifier(
- const char *SpecifierBegin, const char *FmtStrEnd, unsigned &Len) {
- if (SpecifierBegin + 1 >= FmtStrEnd)
- return false;
-
- const llvm::UTF8 *SB =
- reinterpret_cast<const llvm::UTF8 *>(SpecifierBegin + 1);
- const llvm::UTF8 *SE = reinterpret_cast<const llvm::UTF8 *>(FmtStrEnd);
- const char FirstByte = *SB;
-
- // If the invalid specifier is a multibyte UTF-8 string, return the
- // total length accordingly so that the conversion specifier can be
- // properly updated to reflect a complete UTF-8 specifier.
- unsigned NumBytes = llvm::getNumBytesForUTF8(FirstByte);
- if (NumBytes == 1)
- return false;
- if (SB + NumBytes > SE)
- return false;
-
- Len = NumBytes + 1;
- return true;
-}
-
-//===----------------------------------------------------------------------===//
-// Methods on ArgType.
-//===----------------------------------------------------------------------===//
-
-clang::analyze_format_string::ArgType::MatchKind
-ArgType::matchesType(ASTContext &C, QualType argTy) const {
- if (Ptr) {
- // It has to be a pointer.
- const PointerType *PT = argTy->getAs<PointerType>();
- if (!PT)
- return NoMatch;
-
- // We cannot write through a const qualified pointer.
- if (PT->getPointeeType().isConstQualified())
- return NoMatch;
-
- argTy = PT->getPointeeType();
- }
-
- switch (K) {
- case InvalidTy:
- llvm_unreachable("ArgType must be valid");
-
- case UnknownTy:
- return Match;
-
- case AnyCharTy: {
- if (const EnumType *ETy = argTy->getAs<EnumType>()) {
- // If the enum is incomplete we know nothing about the underlying type.
- // Assume that it's 'int'.
- if (!ETy->getDecl()->isComplete())
- return NoMatch;
- argTy = ETy->getDecl()->getIntegerType();
- }
-
- if (const BuiltinType *BT = argTy->getAs<BuiltinType>())
- switch (BT->getKind()) {
- default:
- break;
- case BuiltinType::Char_S:
- case BuiltinType::SChar:
- case BuiltinType::UChar:
- case BuiltinType::Char_U:
- return Match;
- }
- return NoMatch;
- }
-
- case SpecificTy: {
- if (const EnumType *ETy = argTy->getAs<EnumType>()) {
- // If the enum is incomplete we know nothing about the underlying type.
- // Assume that it's 'int'.
- if (!ETy->getDecl()->isComplete())
- argTy = C.IntTy;
- else
- argTy = ETy->getDecl()->getIntegerType();
- }
- argTy = C.getCanonicalType(argTy).getUnqualifiedType();
-
- if (T == argTy)
- return Match;
- // Check for "compatible types".
- if (const BuiltinType *BT = argTy->getAs<BuiltinType>())
- switch (BT->getKind()) {
- default:
- break;
- case BuiltinType::Char_S:
- case BuiltinType::SChar:
- case BuiltinType::Char_U:
- case BuiltinType::UChar:
- return T == C.UnsignedCharTy || T == C.SignedCharTy ? Match
- : NoMatch;
- case BuiltinType::Short:
- return T == C.UnsignedShortTy ? Match : NoMatch;
- case BuiltinType::UShort:
- return T == C.ShortTy ? Match : NoMatch;
- case BuiltinType::Int:
- return T == C.UnsignedIntTy ? Match : NoMatch;
- case BuiltinType::UInt:
- return T == C.IntTy ? Match : NoMatch;
- case BuiltinType::Long:
- return T == C.UnsignedLongTy ? Match : NoMatch;
- case BuiltinType::ULong:
- return T == C.LongTy ? Match : NoMatch;
- case BuiltinType::LongLong:
- return T == C.UnsignedLongLongTy ? Match : NoMatch;
- case BuiltinType::ULongLong:
- return T == C.LongLongTy ? Match : NoMatch;
- }
- return NoMatch;
- }
-
- case CStrTy: {
- const PointerType *PT = argTy->getAs<PointerType>();
- if (!PT)
- return NoMatch;
- QualType pointeeTy = PT->getPointeeType();
- if (const BuiltinType *BT = pointeeTy->getAs<BuiltinType>())
- switch (BT->getKind()) {
- case BuiltinType::Void:
- case BuiltinType::Char_U:
- case BuiltinType::UChar:
- case BuiltinType::Char_S:
- case BuiltinType::SChar:
- return Match;
- default:
- break;
- }
-
- return NoMatch;
- }
-
- case WCStrTy: {
- const PointerType *PT = argTy->getAs<PointerType>();
- if (!PT)
- return NoMatch;
- QualType pointeeTy =
- C.getCanonicalType(PT->getPointeeType()).getUnqualifiedType();
- return pointeeTy == C.getWideCharType() ? Match : NoMatch;
- }
-
- case WIntTy: {
-
- QualType PromoArg =
- argTy->isPromotableIntegerType()
- ? C.getPromotedIntegerType(argTy) : argTy;
-
- QualType WInt = C.getCanonicalType(C.getWIntType()).getUnqualifiedType();
- PromoArg = C.getCanonicalType(PromoArg).getUnqualifiedType();
-
- // If the promoted argument is the corresponding signed type of the
- // wint_t type, then it should match.
- if (PromoArg->hasSignedIntegerRepresentation() &&
- C.getCorrespondingUnsignedType(PromoArg) == WInt)
- return Match;
-
- return WInt == PromoArg ? Match : NoMatch;
- }
-
- case CPointerTy:
- if (argTy->isVoidPointerType()) {
- return Match;
- } if (argTy->isPointerType() || argTy->isObjCObjectPointerType() ||
- argTy->isBlockPointerType() || argTy->isNullPtrType()) {
- return NoMatchPedantic;
- } else {
- return NoMatch;
- }
-
- case ObjCPointerTy: {
- if (argTy->getAs<ObjCObjectPointerType>() ||
- argTy->getAs<BlockPointerType>())
- return Match;
-
- // Handle implicit toll-free bridging.
- if (const PointerType *PT = argTy->getAs<PointerType>()) {
- // Things such as CFTypeRef are really just opaque pointers
- // to C structs representing CF types that can often be bridged
- // to Objective-C objects. Since the compiler doesn't know which
- // structs can be toll-free bridged, we just accept them all.
- QualType pointee = PT->getPointeeType();
- if (pointee->getAsStructureType() || pointee->isVoidType())
- return Match;
- }
- return NoMatch;
- }
- }
-
- llvm_unreachable("Invalid ArgType Kind!");
-}
-
-QualType ArgType::getRepresentativeType(ASTContext &C) const {
- QualType Res;
- switch (K) {
- case InvalidTy:
- llvm_unreachable("No representative type for Invalid ArgType");
- case UnknownTy:
- llvm_unreachable("No representative type for Unknown ArgType");
- case AnyCharTy:
- Res = C.CharTy;
- break;
- case SpecificTy:
- Res = T;
- break;
- case CStrTy:
- Res = C.getPointerType(C.CharTy);
- break;
- case WCStrTy:
- Res = C.getPointerType(C.getWideCharType());
- break;
- case ObjCPointerTy:
- Res = C.ObjCBuiltinIdTy;
- break;
- case CPointerTy:
- Res = C.VoidPtrTy;
- break;
- case WIntTy: {
- Res = C.getWIntType();
- break;
- }
- }
-
- if (Ptr)
- Res = C.getPointerType(Res);
- return Res;
-}
-
-std::string ArgType::getRepresentativeTypeName(ASTContext &C) const {
- std::string S = getRepresentativeType(C).getAsString();
-
- std::string Alias;
- if (Name) {
- // Use a specific name for this type, e.g. "size_t".
- Alias = Name;
- if (Ptr) {
- // If ArgType is actually a pointer to T, append an asterisk.
- Alias += (Alias[Alias.size()-1] == '*') ? "*" : " *";
- }
- // If Alias is the same as the underlying type, e.g. wchar_t, then drop it.
- if (S == Alias)
- Alias.clear();
- }
-
- if (!Alias.empty())
- return std::string("'") + Alias + "' (aka '" + S + "')";
- return std::string("'") + S + "'";
-}
-
-
-//===----------------------------------------------------------------------===//
-// Methods on OptionalAmount.
-//===----------------------------------------------------------------------===//
-
-ArgType
-analyze_format_string::OptionalAmount::getArgType(ASTContext &Ctx) const {
- return Ctx.IntTy;
-}
-
-//===----------------------------------------------------------------------===//
-// Methods on LengthModifier.
-//===----------------------------------------------------------------------===//
-
-const char *
-analyze_format_string::LengthModifier::toString() const {
- switch (kind) {
- case AsChar:
- return "hh";
- case AsShort:
- return "h";
- case AsLong: // or AsWideChar
- return "l";
- case AsLongLong:
- return "ll";
- case AsQuad:
- return "q";
- case AsIntMax:
- return "j";
- case AsSizeT:
- return "z";
- case AsPtrDiff:
- return "t";
- case AsInt32:
- return "I32";
- case AsInt3264:
- return "I";
- case AsInt64:
- return "I64";
- case AsLongDouble:
- return "L";
- case AsAllocate:
- return "a";
- case AsMAllocate:
- return "m";
- case AsWide:
- return "w";
- case None:
- return "";
- }
- return nullptr;
-}
-
-//===----------------------------------------------------------------------===//
-// Methods on ConversionSpecifier.
-//===----------------------------------------------------------------------===//
-
-const char *ConversionSpecifier::toString() const {
- switch (kind) {
- case dArg: return "d";
- case DArg: return "D";
- case iArg: return "i";
- case oArg: return "o";
- case OArg: return "O";
- case uArg: return "u";
- case UArg: return "U";
- case xArg: return "x";
- case XArg: return "X";
- case fArg: return "f";
- case FArg: return "F";
- case eArg: return "e";
- case EArg: return "E";
- case gArg: return "g";
- case GArg: return "G";
- case aArg: return "a";
- case AArg: return "A";
- case cArg: return "c";
- case sArg: return "s";
- case pArg: return "p";
- case PArg:
- return "P";
- case nArg: return "n";
- case PercentArg: return "%";
- case ScanListArg: return "[";
- case InvalidSpecifier: return nullptr;
-
- // POSIX unicode extensions.
- case CArg: return "C";
- case SArg: return "S";
-
- // Objective-C specific specifiers.
- case ObjCObjArg: return "@";
-
- // FreeBSD kernel specific specifiers.
- case FreeBSDbArg: return "b";
- case FreeBSDDArg: return "D";
- case FreeBSDrArg: return "r";
- case FreeBSDyArg: return "y";
-
- // GlibC specific specifiers.
- case PrintErrno: return "m";
-
- // MS specific specifiers.
- case ZArg: return "Z";
- }
- return nullptr;
-}
-
-Optional<ConversionSpecifier>
-ConversionSpecifier::getStandardSpecifier() const {
- ConversionSpecifier::Kind NewKind;
-
- switch (getKind()) {
- default:
- return None;
- case DArg:
- NewKind = dArg;
- break;
- case UArg:
- NewKind = uArg;
- break;
- case OArg:
- NewKind = oArg;
- break;
- }
-
- ConversionSpecifier FixedCS(*this);
- FixedCS.setKind(NewKind);
- return FixedCS;
-}
-
-//===----------------------------------------------------------------------===//
-// Methods on OptionalAmount.
-//===----------------------------------------------------------------------===//
-
-void OptionalAmount::toString(raw_ostream &os) const {
- switch (hs) {
- case Invalid:
- case NotSpecified:
- return;
- case Arg:
- if (UsesDotPrefix)
- os << ".";
- if (usesPositionalArg())
- os << "*" << getPositionalArgIndex() << "$";
- else
- os << "*";
- break;
- case Constant:
- if (UsesDotPrefix)
- os << ".";
- os << amt;
- break;
- }
-}
-
-bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const {
- switch (LM.getKind()) {
- case LengthModifier::None:
- return true;
-
- // Handle most integer flags
- case LengthModifier::AsShort:
- if (Target.getTriple().isOSMSVCRT()) {
- switch (CS.getKind()) {
- case ConversionSpecifier::cArg:
- case ConversionSpecifier::CArg:
- case ConversionSpecifier::sArg:
- case ConversionSpecifier::SArg:
- case ConversionSpecifier::ZArg:
- return true;
- default:
- break;
- }
- }
- // Fall through.
- case LengthModifier::AsChar:
- case LengthModifier::AsLongLong:
- case LengthModifier::AsQuad:
- case LengthModifier::AsIntMax:
- case LengthModifier::AsSizeT:
- case LengthModifier::AsPtrDiff:
- switch (CS.getKind()) {
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::DArg:
- case ConversionSpecifier::iArg:
- case ConversionSpecifier::oArg:
- case ConversionSpecifier::OArg:
- case ConversionSpecifier::uArg:
- case ConversionSpecifier::UArg:
- case ConversionSpecifier::xArg:
- case ConversionSpecifier::XArg:
- case ConversionSpecifier::nArg:
- return true;
- case ConversionSpecifier::FreeBSDrArg:
- case ConversionSpecifier::FreeBSDyArg:
- return Target.getTriple().isOSFreeBSD() || Target.getTriple().isPS4();
- default:
- return false;
- }
-
- // Handle 'l' flag
- case LengthModifier::AsLong: // or AsWideChar
- switch (CS.getKind()) {
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::DArg:
- case ConversionSpecifier::iArg:
- case ConversionSpecifier::oArg:
- case ConversionSpecifier::OArg:
- case ConversionSpecifier::uArg:
- case ConversionSpecifier::UArg:
- case ConversionSpecifier::xArg:
- case ConversionSpecifier::XArg:
- case ConversionSpecifier::aArg:
- case ConversionSpecifier::AArg:
- case ConversionSpecifier::fArg:
- case ConversionSpecifier::FArg:
- case ConversionSpecifier::eArg:
- case ConversionSpecifier::EArg:
- case ConversionSpecifier::gArg:
- case ConversionSpecifier::GArg:
- case ConversionSpecifier::nArg:
- case ConversionSpecifier::cArg:
- case ConversionSpecifier::sArg:
- case ConversionSpecifier::ScanListArg:
- case ConversionSpecifier::ZArg:
- return true;
- case ConversionSpecifier::FreeBSDrArg:
- case ConversionSpecifier::FreeBSDyArg:
- return Target.getTriple().isOSFreeBSD() || Target.getTriple().isPS4();
- default:
- return false;
- }
-
- case LengthModifier::AsLongDouble:
- switch (CS.getKind()) {
- case ConversionSpecifier::aArg:
- case ConversionSpecifier::AArg:
- case ConversionSpecifier::fArg:
- case ConversionSpecifier::FArg:
- case ConversionSpecifier::eArg:
- case ConversionSpecifier::EArg:
- case ConversionSpecifier::gArg:
- case ConversionSpecifier::GArg:
- return true;
- // GNU libc extension.
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::iArg:
- case ConversionSpecifier::oArg:
- case ConversionSpecifier::uArg:
- case ConversionSpecifier::xArg:
- case ConversionSpecifier::XArg:
- return !Target.getTriple().isOSDarwin() &&
- !Target.getTriple().isOSWindows();
- default:
- return false;
- }
-
- case LengthModifier::AsAllocate:
- switch (CS.getKind()) {
- case ConversionSpecifier::sArg:
- case ConversionSpecifier::SArg:
- case ConversionSpecifier::ScanListArg:
- return true;
- default:
- return false;
- }
-
- case LengthModifier::AsMAllocate:
- switch (CS.getKind()) {
- case ConversionSpecifier::cArg:
- case ConversionSpecifier::CArg:
- case ConversionSpecifier::sArg:
- case ConversionSpecifier::SArg:
- case ConversionSpecifier::ScanListArg:
- return true;
- default:
- return false;
- }
- case LengthModifier::AsInt32:
- case LengthModifier::AsInt3264:
- case LengthModifier::AsInt64:
- switch (CS.getKind()) {
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::iArg:
- case ConversionSpecifier::oArg:
- case ConversionSpecifier::uArg:
- case ConversionSpecifier::xArg:
- case ConversionSpecifier::XArg:
- return Target.getTriple().isOSMSVCRT();
- default:
- return false;
- }
- case LengthModifier::AsWide:
- switch (CS.getKind()) {
- case ConversionSpecifier::cArg:
- case ConversionSpecifier::CArg:
- case ConversionSpecifier::sArg:
- case ConversionSpecifier::SArg:
- case ConversionSpecifier::ZArg:
- return Target.getTriple().isOSMSVCRT();
- default:
- return false;
- }
- }
- llvm_unreachable("Invalid LengthModifier Kind!");
-}
-
-bool FormatSpecifier::hasStandardLengthModifier() const {
- switch (LM.getKind()) {
- case LengthModifier::None:
- case LengthModifier::AsChar:
- case LengthModifier::AsShort:
- case LengthModifier::AsLong:
- case LengthModifier::AsLongLong:
- case LengthModifier::AsIntMax:
- case LengthModifier::AsSizeT:
- case LengthModifier::AsPtrDiff:
- case LengthModifier::AsLongDouble:
- return true;
- case LengthModifier::AsAllocate:
- case LengthModifier::AsMAllocate:
- case LengthModifier::AsQuad:
- case LengthModifier::AsInt32:
- case LengthModifier::AsInt3264:
- case LengthModifier::AsInt64:
- case LengthModifier::AsWide:
- return false;
- }
- llvm_unreachable("Invalid LengthModifier Kind!");
-}
-
-bool FormatSpecifier::hasStandardConversionSpecifier(
- const LangOptions &LangOpt) const {
- switch (CS.getKind()) {
- case ConversionSpecifier::cArg:
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::iArg:
- case ConversionSpecifier::oArg:
- case ConversionSpecifier::uArg:
- case ConversionSpecifier::xArg:
- case ConversionSpecifier::XArg:
- case ConversionSpecifier::fArg:
- case ConversionSpecifier::FArg:
- case ConversionSpecifier::eArg:
- case ConversionSpecifier::EArg:
- case ConversionSpecifier::gArg:
- case ConversionSpecifier::GArg:
- case ConversionSpecifier::aArg:
- case ConversionSpecifier::AArg:
- case ConversionSpecifier::sArg:
- case ConversionSpecifier::pArg:
- case ConversionSpecifier::nArg:
- case ConversionSpecifier::ObjCObjArg:
- case ConversionSpecifier::ScanListArg:
- case ConversionSpecifier::PercentArg:
- case ConversionSpecifier::PArg:
- return true;
- case ConversionSpecifier::CArg:
- case ConversionSpecifier::SArg:
- return LangOpt.ObjC1 || LangOpt.ObjC2;
- case ConversionSpecifier::InvalidSpecifier:
- case ConversionSpecifier::FreeBSDbArg:
- case ConversionSpecifier::FreeBSDDArg:
- case ConversionSpecifier::FreeBSDrArg:
- case ConversionSpecifier::FreeBSDyArg:
- case ConversionSpecifier::PrintErrno:
- case ConversionSpecifier::DArg:
- case ConversionSpecifier::OArg:
- case ConversionSpecifier::UArg:
- case ConversionSpecifier::ZArg:
- return false;
- }
- llvm_unreachable("Invalid ConversionSpecifier Kind!");
-}
-
-bool FormatSpecifier::hasStandardLengthConversionCombination() const {
- if (LM.getKind() == LengthModifier::AsLongDouble) {
- switch(CS.getKind()) {
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::iArg:
- case ConversionSpecifier::oArg:
- case ConversionSpecifier::uArg:
- case ConversionSpecifier::xArg:
- case ConversionSpecifier::XArg:
- return false;
- default:
- return true;
- }
- }
- return true;
-}
-
-Optional<LengthModifier> FormatSpecifier::getCorrectedLengthModifier() const {
- if (CS.isAnyIntArg() || CS.getKind() == ConversionSpecifier::nArg) {
- if (LM.getKind() == LengthModifier::AsLongDouble ||
- LM.getKind() == LengthModifier::AsQuad) {
- LengthModifier FixedLM(LM);
- FixedLM.setKind(LengthModifier::AsLongLong);
- return FixedLM;
- }
- }
-
- return None;
-}
-
-bool FormatSpecifier::namedTypeToLengthModifier(QualType QT,
- LengthModifier &LM) {
- assert(isa<TypedefType>(QT) && "Expected a TypedefType");
- const TypedefNameDecl *Typedef = cast<TypedefType>(QT)->getDecl();
-
- for (;;) {
- const IdentifierInfo *Identifier = Typedef->getIdentifier();
- if (Identifier->getName() == "size_t") {
- LM.setKind(LengthModifier::AsSizeT);
- return true;
- } else if (Identifier->getName() == "ssize_t") {
- // Not C99, but common in Unix.
- LM.setKind(LengthModifier::AsSizeT);
- return true;
- } else if (Identifier->getName() == "intmax_t") {
- LM.setKind(LengthModifier::AsIntMax);
- return true;
- } else if (Identifier->getName() == "uintmax_t") {
- LM.setKind(LengthModifier::AsIntMax);
- return true;
- } else if (Identifier->getName() == "ptrdiff_t") {
- LM.setKind(LengthModifier::AsPtrDiff);
- return true;
- }
-
- QualType T = Typedef->getUnderlyingType();
- if (!isa<TypedefType>(T))
- break;
-
- Typedef = cast<TypedefType>(T)->getDecl();
- }
- return false;
-}
diff --git a/lib/Analysis/FormatStringParsing.h b/lib/Analysis/FormatStringParsing.h
deleted file mode 100644
index a63140b366cd..000000000000
--- a/lib/Analysis/FormatStringParsing.h
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef LLVM_CLANG_LIB_ANALYSIS_FORMATSTRINGPARSING_H
-#define LLVM_CLANG_LIB_ANALYSIS_FORMATSTRINGPARSING_H
-
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Type.h"
-#include "clang/Analysis/Analyses/FormatString.h"
-
-namespace clang {
-
-class LangOptions;
-
-template <typename T>
-class UpdateOnReturn {
- T &ValueToUpdate;
- const T &ValueToCopy;
-public:
- UpdateOnReturn(T &valueToUpdate, const T &valueToCopy)
- : ValueToUpdate(valueToUpdate), ValueToCopy(valueToCopy) {}
-
- ~UpdateOnReturn() {
- ValueToUpdate = ValueToCopy;
- }
-};
-
-namespace analyze_format_string {
-
-OptionalAmount ParseAmount(const char *&Beg, const char *E);
-OptionalAmount ParseNonPositionAmount(const char *&Beg, const char *E,
- unsigned &argIndex);
-
-OptionalAmount ParsePositionAmount(FormatStringHandler &H,
- const char *Start, const char *&Beg,
- const char *E, PositionContext p);
-
-bool ParseFieldWidth(FormatStringHandler &H,
- FormatSpecifier &CS,
- const char *Start, const char *&Beg, const char *E,
- unsigned *argIndex);
-
-bool ParseArgPosition(FormatStringHandler &H,
- FormatSpecifier &CS, const char *Start,
- const char *&Beg, const char *E);
-
-/// Returns true if a LengthModifier was parsed and installed in the
-/// FormatSpecifier& argument, and false otherwise.
-bool ParseLengthModifier(FormatSpecifier &FS, const char *&Beg, const char *E,
- const LangOptions &LO, bool IsScanf = false);
-
-/// Returns true if the invalid specifier in \p SpecifierBegin is a UTF-8
-/// string; check that it won't go further than \p FmtStrEnd and write
-/// up the total size in \p Len.
-bool ParseUTF8InvalidSpecifier(const char *SpecifierBegin,
- const char *FmtStrEnd, unsigned &Len);
-
-template <typename T> class SpecifierResult {
- T FS;
- const char *Start;
- bool Stop;
-public:
- SpecifierResult(bool stop = false)
- : Start(nullptr), Stop(stop) {}
- SpecifierResult(const char *start,
- const T &fs)
- : FS(fs), Start(start), Stop(false) {}
-
- const char *getStart() const { return Start; }
- bool shouldStop() const { return Stop; }
- bool hasValue() const { return Start != nullptr; }
- const T &getValue() const {
- assert(hasValue());
- return FS;
- }
- const T &getValue() { return FS; }
-};
-
-} // end analyze_format_string namespace
-} // end clang namespace
-
-#endif
diff --git a/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp
index 05bc1a5d102c..afe2d264907f 100644
--- a/lib/Analysis/LiveVariables.cpp
+++ b/lib/Analysis/LiveVariables.cpp
@@ -93,6 +93,7 @@ public:
LiveVariables::Observer *obs = nullptr);
void dumpBlockLiveness(const SourceManager& M);
+ void dumpStmtLiveness(const SourceManager& M);
LiveVariablesImpl(AnalysisDeclContext &ac, bool KillAtAssign)
: analysisContext(ac),
@@ -237,8 +238,8 @@ static const Stmt *LookThroughStmt(const Stmt *S) {
while (S) {
if (const Expr *Ex = dyn_cast<Expr>(S))
S = Ex->IgnoreParens();
- if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(S)) {
- S = EWC->getSubExpr();
+ if (const FullExpr *FE = dyn_cast<FullExpr>(S)) {
+ S = FE->getSubExpr();
continue;
}
if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(S)) {
@@ -327,6 +328,35 @@ void TransferFunctions::Visit(Stmt *S) {
// No need to unconditionally visit subexpressions.
return;
}
+ case Stmt::IfStmtClass: {
+ // If one of the branches is an expression rather than a compound
+ // statement, it will be bad if we mark it as live at the terminator
+ // of the if-statement (i.e., immediately after the condition expression).
+ AddLiveStmt(val.liveStmts, LV.SSetFact, cast<IfStmt>(S)->getCond());
+ return;
+ }
+ case Stmt::WhileStmtClass: {
+ // If the loop body is an expression rather than a compound statement,
+ // it will be bad if we mark it as live at the terminator of the loop
+ // (i.e., immediately after the condition expression).
+ AddLiveStmt(val.liveStmts, LV.SSetFact, cast<WhileStmt>(S)->getCond());
+ return;
+ }
+ case Stmt::DoStmtClass: {
+ // If the loop body is an expression rather than a compound statement,
+ // it will be bad if we mark it as live at the terminator of the loop
+ // (i.e., immediately after the condition expression).
+ AddLiveStmt(val.liveStmts, LV.SSetFact, cast<DoStmt>(S)->getCond());
+ return;
+ }
+ case Stmt::ForStmtClass: {
+ // If the loop body is an expression rather than a compound statement,
+ // it will be bad if we mark it as live at the terminator of the loop
+ // (i.e., immediately after the condition expression).
+ AddLiveStmt(val.liveStmts, LV.SSetFact, cast<ForStmt>(S)->getCond());
+ return;
+ }
+
}
for (Stmt *Child : S->children()) {
@@ -597,7 +627,7 @@ void LiveVariablesImpl::dumpBlockLiveness(const SourceManager &M) {
it != ei; ++it) {
vec.push_back(it->first);
}
- llvm::sort(vec.begin(), vec.end(), [](const CFGBlock *A, const CFGBlock *B) {
+ llvm::sort(vec, [](const CFGBlock *A, const CFGBlock *B) {
return A->getBlockID() < B->getBlockID();
});
@@ -617,21 +647,38 @@ void LiveVariablesImpl::dumpBlockLiveness(const SourceManager &M) {
declVec.push_back(*si);
}
- llvm::sort(declVec.begin(), declVec.end(),
- [](const Decl *A, const Decl *B) {
- return A->getLocStart() < B->getLocStart();
+ llvm::sort(declVec, [](const Decl *A, const Decl *B) {
+ return A->getBeginLoc() < B->getBeginLoc();
});
for (std::vector<const VarDecl*>::iterator di = declVec.begin(),
de = declVec.end(); di != de; ++di) {
llvm::errs() << " " << (*di)->getDeclName().getAsString()
<< " <";
- (*di)->getLocation().dump(M);
+ (*di)->getLocation().print(llvm::errs(), M);
llvm::errs() << ">\n";
}
}
llvm::errs() << "\n";
}
+void LiveVariables::dumpStmtLiveness(const SourceManager &M) {
+ getImpl(impl).dumpStmtLiveness(M);
+}
+
+void LiveVariablesImpl::dumpStmtLiveness(const SourceManager &M) {
+ // Don't iterate over blockEndsToLiveness directly because it's not sorted.
+ for (auto I : *analysisContext.getCFG()) {
+
+ llvm::errs() << "\n[ B" << I->getBlockID()
+ << " (live statements at block exit) ]\n";
+ for (auto S : blocksEndToLiveness[I].liveStmts) {
+ llvm::errs() << "\n";
+ S->dump();
+ }
+ llvm::errs() << "\n";
+ }
+}
+
const void *LiveVariables::getTag() { static int x; return &x; }
const void *RelaxedLiveVariables::getTag() { static int x; return &x; }
diff --git a/lib/Analysis/OSLog.cpp b/lib/Analysis/OSLog.cpp
deleted file mode 100644
index b2983932ea22..000000000000
--- a/lib/Analysis/OSLog.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-// TODO: header template
-
-#include "clang/Analysis/Analyses/OSLog.h"
-#include "clang/AST/Attr.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/ExprObjC.h"
-#include "clang/Analysis/Analyses/FormatString.h"
-#include "clang/Basic/Builtins.h"
-#include "llvm/ADT/SmallBitVector.h"
-
-using namespace clang;
-
-using clang::analyze_os_log::OSLogBufferItem;
-using clang::analyze_os_log::OSLogBufferLayout;
-
-namespace {
-class OSLogFormatStringHandler
- : public analyze_format_string::FormatStringHandler {
-private:
- struct ArgData {
- const Expr *E = nullptr;
- Optional<OSLogBufferItem::Kind> Kind;
- Optional<unsigned> Size;
- Optional<const Expr *> Count;
- Optional<const Expr *> Precision;
- Optional<const Expr *> FieldWidth;
- unsigned char Flags = 0;
- };
- SmallVector<ArgData, 4> ArgsData;
- ArrayRef<const Expr *> Args;
-
- OSLogBufferItem::Kind
- getKind(analyze_format_string::ConversionSpecifier::Kind K) {
- switch (K) {
- case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s"
- return OSLogBufferItem::StringKind;
- case clang::analyze_format_string::ConversionSpecifier::SArg: // "%S"
- return OSLogBufferItem::WideStringKind;
- case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
- return OSLogBufferItem::PointerKind;
- case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg: // "%@"
- return OSLogBufferItem::ObjCObjKind;
- case clang::analyze_format_string::ConversionSpecifier::PrintErrno: // "%m"
- return OSLogBufferItem::ErrnoKind;
- default:
- return OSLogBufferItem::ScalarKind;
- }
- }
- }
-
-public:
- OSLogFormatStringHandler(ArrayRef<const Expr *> Args) : Args(Args) {
- ArgsData.reserve(Args.size());
- }
-
- virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
- const char *StartSpecifier,
- unsigned SpecifierLen) {
- if (!FS.consumesDataArgument() &&
- FS.getConversionSpecifier().getKind() !=
- clang::analyze_format_string::ConversionSpecifier::PrintErrno)
- return true;
-
- ArgsData.emplace_back();
- unsigned ArgIndex = FS.getArgIndex();
- if (ArgIndex < Args.size())
- ArgsData.back().E = Args[ArgIndex];
-
- // First get the Kind
- ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind());
- if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind &&
- !ArgsData.back().E) {
- // missing argument
- ArgsData.pop_back();
- return false;
- }
-
- switch (FS.getConversionSpecifier().getKind()) {
- case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s"
- case clang::analyze_format_string::ConversionSpecifier::SArg: { // "%S"
- auto &precision = FS.getPrecision();
- switch (precision.getHowSpecified()) {
- case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%s"
- break;
- case clang::analyze_format_string::OptionalAmount::Constant: // "%.16s"
- ArgsData.back().Size = precision.getConstantAmount();
- break;
- case clang::analyze_format_string::OptionalAmount::Arg: // "%.*s"
- ArgsData.back().Count = Args[precision.getArgIndex()];
- break;
- case clang::analyze_format_string::OptionalAmount::Invalid:
- return false;
- }
- break;
- }
- case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
- auto &precision = FS.getPrecision();
- switch (precision.getHowSpecified()) {
- case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%P"
- return false; // length must be supplied with pointer format specifier
- case clang::analyze_format_string::OptionalAmount::Constant: // "%.16P"
- ArgsData.back().Size = precision.getConstantAmount();
- break;
- case clang::analyze_format_string::OptionalAmount::Arg: // "%.*P"
- ArgsData.back().Count = Args[precision.getArgIndex()];
- break;
- case clang::analyze_format_string::OptionalAmount::Invalid:
- return false;
- }
- break;
- }
- default:
- if (FS.getPrecision().hasDataArgument()) {
- ArgsData.back().Precision = Args[FS.getPrecision().getArgIndex()];
- }
- break;
- }
- if (FS.getFieldWidth().hasDataArgument()) {
- ArgsData.back().FieldWidth = Args[FS.getFieldWidth().getArgIndex()];
- }
-
- if (FS.isPrivate()) {
- ArgsData.back().Flags |= OSLogBufferItem::IsPrivate;
- }
- if (FS.isPublic()) {
- ArgsData.back().Flags |= OSLogBufferItem::IsPublic;
- }
- return true;
- }
-
- void computeLayout(ASTContext &Ctx, OSLogBufferLayout &Layout) const {
- Layout.Items.clear();
- for (auto &Data : ArgsData) {
- if (Data.FieldWidth) {
- CharUnits Size = Ctx.getTypeSizeInChars((*Data.FieldWidth)->getType());
- Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, *Data.FieldWidth,
- Size, 0);
- }
- if (Data.Precision) {
- CharUnits Size = Ctx.getTypeSizeInChars((*Data.Precision)->getType());
- Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, *Data.Precision,
- Size, 0);
- }
- if (Data.Count) {
- // "%.*P" has an extra "count" that we insert before the argument.
- CharUnits Size = Ctx.getTypeSizeInChars((*Data.Count)->getType());
- Layout.Items.emplace_back(OSLogBufferItem::CountKind, *Data.Count, Size,
- 0);
- }
- if (Data.Size)
- Layout.Items.emplace_back(Ctx, CharUnits::fromQuantity(*Data.Size),
- Data.Flags);
- if (Data.Kind) {
- CharUnits Size;
- if (*Data.Kind == OSLogBufferItem::ErrnoKind)
- Size = CharUnits::Zero();
- else
- Size = Ctx.getTypeSizeInChars(Data.E->getType());
- Layout.Items.emplace_back(*Data.Kind, Data.E, Size, Data.Flags);
- } else {
- auto Size = Ctx.getTypeSizeInChars(Data.E->getType());
- Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, Data.E, Size,
- Data.Flags);
- }
- }
- }
-};
-} // end anonymous namespace
-
-bool clang::analyze_os_log::computeOSLogBufferLayout(
- ASTContext &Ctx, const CallExpr *E, OSLogBufferLayout &Layout) {
- ArrayRef<const Expr *> Args(E->getArgs(), E->getArgs() + E->getNumArgs());
-
- const Expr *StringArg;
- ArrayRef<const Expr *> VarArgs;
- switch (E->getBuiltinCallee()) {
- case Builtin::BI__builtin_os_log_format_buffer_size:
- assert(E->getNumArgs() >= 1 &&
- "__builtin_os_log_format_buffer_size takes at least 1 argument");
- StringArg = E->getArg(0);
- VarArgs = Args.slice(1);
- break;
- case Builtin::BI__builtin_os_log_format:
- assert(E->getNumArgs() >= 2 &&
- "__builtin_os_log_format takes at least 2 arguments");
- StringArg = E->getArg(1);
- VarArgs = Args.slice(2);
- break;
- default:
- llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout");
- }
-
- const StringLiteral *Lit = cast<StringLiteral>(StringArg->IgnoreParenCasts());
- assert(Lit && (Lit->isAscii() || Lit->isUTF8()));
- StringRef Data = Lit->getString();
- OSLogFormatStringHandler H(VarArgs);
- ParsePrintfString(H, Data.begin(), Data.end(), Ctx.getLangOpts(),
- Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/ false);
-
- H.computeLayout(Ctx, Layout);
- return true;
-}
diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp
deleted file mode 100644
index dcb15c5e3758..000000000000
--- a/lib/Analysis/PrintfFormatString.cpp
+++ /dev/null
@@ -1,1029 +0,0 @@
-//== PrintfFormatString.cpp - Analysis of printf format strings --*- C++ -*-==//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Handling of format string in printf and friends. The structure of format
-// strings for fprintf() are described in C99 7.19.6.1.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Analysis/Analyses/FormatString.h"
-#include "clang/Analysis/Analyses/OSLog.h"
-#include "FormatStringParsing.h"
-#include "clang/Basic/TargetInfo.h"
-
-using clang::analyze_format_string::ArgType;
-using clang::analyze_format_string::FormatStringHandler;
-using clang::analyze_format_string::LengthModifier;
-using clang::analyze_format_string::OptionalAmount;
-using clang::analyze_format_string::ConversionSpecifier;
-using clang::analyze_printf::PrintfSpecifier;
-
-using namespace clang;
-
-typedef clang::analyze_format_string::SpecifierResult<PrintfSpecifier>
- PrintfSpecifierResult;
-
-//===----------------------------------------------------------------------===//
-// Methods for parsing format strings.
-//===----------------------------------------------------------------------===//
-
-using analyze_format_string::ParseNonPositionAmount;
-
-static bool ParsePrecision(FormatStringHandler &H, PrintfSpecifier &FS,
- const char *Start, const char *&Beg, const char *E,
- unsigned *argIndex) {
- if (argIndex) {
- FS.setPrecision(ParseNonPositionAmount(Beg, E, *argIndex));
- } else {
- const OptionalAmount Amt = ParsePositionAmount(H, Start, Beg, E,
- analyze_format_string::PrecisionPos);
- if (Amt.isInvalid())
- return true;
- FS.setPrecision(Amt);
- }
- return false;
-}
-
-static bool ParseObjCFlags(FormatStringHandler &H, PrintfSpecifier &FS,
- const char *FlagBeg, const char *E, bool Warn) {
- StringRef Flag(FlagBeg, E - FlagBeg);
- // Currently there is only one flag.
- if (Flag == "tt") {
- FS.setHasObjCTechnicalTerm(FlagBeg);
- return false;
- }
- // Handle either the case of no flag or an invalid flag.
- if (Warn) {
- if (Flag == "")
- H.HandleEmptyObjCModifierFlag(FlagBeg, E - FlagBeg);
- else
- H.HandleInvalidObjCModifierFlag(FlagBeg, E - FlagBeg);
- }
- return true;
-}
-
-static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
- const char *&Beg,
- const char *E,
- unsigned &argIndex,
- const LangOptions &LO,
- const TargetInfo &Target,
- bool Warn,
- bool isFreeBSDKPrintf) {
-
- using namespace clang::analyze_format_string;
- using namespace clang::analyze_printf;
-
- const char *I = Beg;
- const char *Start = nullptr;
- UpdateOnReturn <const char*> UpdateBeg(Beg, I);
-
- // Look for a '%' character that indicates the start of a format specifier.
- for ( ; I != E ; ++I) {
- char c = *I;
- if (c == '\0') {
- // Detect spurious null characters, which are likely errors.
- H.HandleNullChar(I);
- return true;
- }
- if (c == '%') {
- Start = I++; // Record the start of the format specifier.
- break;
- }
- }
-
- // No format specifier found?
- if (!Start)
- return false;
-
- if (I == E) {
- // No more characters left?
- if (Warn)
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
-
- PrintfSpecifier FS;
- if (ParseArgPosition(H, FS, Start, I, E))
- return true;
-
- if (I == E) {
- // No more characters left?
- if (Warn)
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
-
- if (*I == '{') {
- ++I;
- unsigned char PrivacyFlags = 0;
- StringRef MatchedStr;
-
- do {
- StringRef Str(I, E - I);
- std::string Match = "^[\t\n\v\f\r ]*(private|public)[\t\n\v\f\r ]*(,|})";
- llvm::Regex R(Match);
- SmallVector<StringRef, 2> Matches;
-
- if (R.match(Str, &Matches)) {
- MatchedStr = Matches[1];
- I += Matches[0].size();
-
- // Set the privacy flag if the privacy annotation in the
- // comma-delimited segment is at least as strict as the privacy
- // annotations in previous comma-delimited segments.
- if (MatchedStr.equals("private"))
- PrivacyFlags = clang::analyze_os_log::OSLogBufferItem::IsPrivate;
- else if (PrivacyFlags == 0 && MatchedStr.equals("public"))
- PrivacyFlags = clang::analyze_os_log::OSLogBufferItem::IsPublic;
- } else {
- size_t CommaOrBracePos =
- Str.find_if([](char c) { return c == ',' || c == '}'; });
-
- if (CommaOrBracePos == StringRef::npos) {
- // Neither a comma nor the closing brace was found.
- if (Warn)
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
-
- I += CommaOrBracePos + 1;
- }
- // Continue until the closing brace is found.
- } while (*(I - 1) == ',');
-
- // Set the privacy flag.
- switch (PrivacyFlags) {
- case 0:
- break;
- case clang::analyze_os_log::OSLogBufferItem::IsPrivate:
- FS.setIsPrivate(MatchedStr.data());
- break;
- case clang::analyze_os_log::OSLogBufferItem::IsPublic:
- FS.setIsPublic(MatchedStr.data());
- break;
- default:
- llvm_unreachable("Unexpected privacy flag value");
- }
- }
-
- // Look for flags (if any).
- bool hasMore = true;
- for ( ; I != E; ++I) {
- switch (*I) {
- default: hasMore = false; break;
- case '\'':
- // FIXME: POSIX specific. Always accept?
- FS.setHasThousandsGrouping(I);
- break;
- case '-': FS.setIsLeftJustified(I); break;
- case '+': FS.setHasPlusPrefix(I); break;
- case ' ': FS.setHasSpacePrefix(I); break;
- case '#': FS.setHasAlternativeForm(I); break;
- case '0': FS.setHasLeadingZeros(I); break;
- }
- if (!hasMore)
- break;
- }
-
- if (I == E) {
- // No more characters left?
- if (Warn)
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
-
- // Look for the field width (if any).
- if (ParseFieldWidth(H, FS, Start, I, E,
- FS.usesPositionalArg() ? nullptr : &argIndex))
- return true;
-
- if (I == E) {
- // No more characters left?
- if (Warn)
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
-
- // Look for the precision (if any).
- if (*I == '.') {
- ++I;
- if (I == E) {
- if (Warn)
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
-
- if (ParsePrecision(H, FS, Start, I, E,
- FS.usesPositionalArg() ? nullptr : &argIndex))
- return true;
-
- if (I == E) {
- // No more characters left?
- if (Warn)
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
- }
-
- // Look for the length modifier.
- if (ParseLengthModifier(FS, I, E, LO) && I == E) {
- // No more characters left?
- if (Warn)
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
-
- // Look for the Objective-C modifier flags, if any.
- // We parse these here, even if they don't apply to
- // the conversion specifier, and then emit an error
- // later if the conversion specifier isn't '@'. This
- // enables better recovery, and we don't know if
- // these flags are applicable until later.
- const char *ObjCModifierFlagsStart = nullptr,
- *ObjCModifierFlagsEnd = nullptr;
- if (*I == '[') {
- ObjCModifierFlagsStart = I;
- ++I;
- auto flagStart = I;
- for (;; ++I) {
- ObjCModifierFlagsEnd = I;
- if (I == E) {
- if (Warn)
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
- // Did we find the closing ']'?
- if (*I == ']') {
- if (ParseObjCFlags(H, FS, flagStart, I, Warn))
- return true;
- ++I;
- break;
- }
- // There are no separators defined yet for multiple
- // Objective-C modifier flags. When those are
- // defined, this is the place to check.
- }
- }
-
- if (*I == '\0') {
- // Detect spurious null characters, which are likely errors.
- H.HandleNullChar(I);
- return true;
- }
-
- // Finally, look for the conversion specifier.
- const char *conversionPosition = I++;
- ConversionSpecifier::Kind k = ConversionSpecifier::InvalidSpecifier;
- switch (*conversionPosition) {
- default:
- break;
- // C99: 7.19.6.1 (section 8).
- case '%': k = ConversionSpecifier::PercentArg; break;
- case 'A': k = ConversionSpecifier::AArg; break;
- case 'E': k = ConversionSpecifier::EArg; break;
- case 'F': k = ConversionSpecifier::FArg; break;
- case 'G': k = ConversionSpecifier::GArg; break;
- case 'X': k = ConversionSpecifier::XArg; break;
- case 'a': k = ConversionSpecifier::aArg; break;
- case 'c': k = ConversionSpecifier::cArg; break;
- case 'd': k = ConversionSpecifier::dArg; break;
- case 'e': k = ConversionSpecifier::eArg; break;
- case 'f': k = ConversionSpecifier::fArg; break;
- case 'g': k = ConversionSpecifier::gArg; break;
- case 'i': k = ConversionSpecifier::iArg; break;
- case 'n': k = ConversionSpecifier::nArg; break;
- case 'o': k = ConversionSpecifier::oArg; break;
- case 'p': k = ConversionSpecifier::pArg; break;
- case 's': k = ConversionSpecifier::sArg; break;
- case 'u': k = ConversionSpecifier::uArg; break;
- case 'x': k = ConversionSpecifier::xArg; break;
- // POSIX specific.
- case 'C': k = ConversionSpecifier::CArg; break;
- case 'S': k = ConversionSpecifier::SArg; break;
- // Apple extension for os_log
- case 'P':
- k = ConversionSpecifier::PArg;
- break;
- // Objective-C.
- case '@': k = ConversionSpecifier::ObjCObjArg; break;
- // Glibc specific.
- case 'm': k = ConversionSpecifier::PrintErrno; break;
- // FreeBSD kernel specific.
- case 'b':
- if (isFreeBSDKPrintf)
- k = ConversionSpecifier::FreeBSDbArg; // int followed by char *
- break;
- case 'r':
- if (isFreeBSDKPrintf)
- k = ConversionSpecifier::FreeBSDrArg; // int
- break;
- case 'y':
- if (isFreeBSDKPrintf)
- k = ConversionSpecifier::FreeBSDyArg; // int
- break;
- // Apple-specific.
- case 'D':
- if (isFreeBSDKPrintf)
- k = ConversionSpecifier::FreeBSDDArg; // void * followed by char *
- else if (Target.getTriple().isOSDarwin())
- k = ConversionSpecifier::DArg;
- break;
- case 'O':
- if (Target.getTriple().isOSDarwin())
- k = ConversionSpecifier::OArg;
- break;
- case 'U':
- if (Target.getTriple().isOSDarwin())
- k = ConversionSpecifier::UArg;
- break;
- // MS specific.
- case 'Z':
- if (Target.getTriple().isOSMSVCRT())
- k = ConversionSpecifier::ZArg;
- }
-
- // Check to see if we used the Objective-C modifier flags with
- // a conversion specifier other than '@'.
- if (k != ConversionSpecifier::ObjCObjArg &&
- k != ConversionSpecifier::InvalidSpecifier &&
- ObjCModifierFlagsStart) {
- H.HandleObjCFlagsWithNonObjCConversion(ObjCModifierFlagsStart,
- ObjCModifierFlagsEnd + 1,
- conversionPosition);
- return true;
- }
-
- PrintfConversionSpecifier CS(conversionPosition, k);
- FS.setConversionSpecifier(CS);
- if (CS.consumesDataArgument() && !FS.usesPositionalArg())
- FS.setArgIndex(argIndex++);
- // FreeBSD kernel specific.
- if (k == ConversionSpecifier::FreeBSDbArg ||
- k == ConversionSpecifier::FreeBSDDArg)
- argIndex++;
-
- if (k == ConversionSpecifier::InvalidSpecifier) {
- unsigned Len = I - Start;
- if (ParseUTF8InvalidSpecifier(Start, E, Len)) {
- CS.setEndScanList(Start + Len);
- FS.setConversionSpecifier(CS);
- }
- // Assume the conversion takes one argument.
- return !H.HandleInvalidPrintfConversionSpecifier(FS, Start, Len);
- }
- return PrintfSpecifierResult(Start, FS);
-}
-
-bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H,
- const char *I,
- const char *E,
- const LangOptions &LO,
- const TargetInfo &Target,
- bool isFreeBSDKPrintf) {
-
- unsigned argIndex = 0;
-
- // Keep looking for a format specifier until we have exhausted the string.
- while (I != E) {
- const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex,
- LO, Target, true,
- isFreeBSDKPrintf);
- // Did a fail-stop error of any kind occur when parsing the specifier?
- // If so, don't do any more processing.
- if (FSR.shouldStop())
- return true;
- // Did we exhaust the string or encounter an error that
- // we can recover from?
- if (!FSR.hasValue())
- continue;
- // We have a format specifier. Pass it to the callback.
- if (!H.HandlePrintfSpecifier(FSR.getValue(), FSR.getStart(),
- I - FSR.getStart()))
- return true;
- }
- assert(I == E && "Format string not exhausted");
- return false;
-}
-
-bool clang::analyze_format_string::ParseFormatStringHasSArg(const char *I,
- const char *E,
- const LangOptions &LO,
- const TargetInfo &Target) {
-
- unsigned argIndex = 0;
-
- // Keep looking for a %s format specifier until we have exhausted the string.
- FormatStringHandler H;
- while (I != E) {
- const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex,
- LO, Target, false,
- false);
- // Did a fail-stop error of any kind occur when parsing the specifier?
- // If so, don't do any more processing.
- if (FSR.shouldStop())
- return false;
- // Did we exhaust the string or encounter an error that
- // we can recover from?
- if (!FSR.hasValue())
- continue;
- const analyze_printf::PrintfSpecifier &FS = FSR.getValue();
- // Return true if this a %s format specifier.
- if (FS.getConversionSpecifier().getKind() == ConversionSpecifier::Kind::sArg)
- return true;
- }
- return false;
-}
-
-//===----------------------------------------------------------------------===//
-// Methods on PrintfSpecifier.
-//===----------------------------------------------------------------------===//
-
-ArgType PrintfSpecifier::getArgType(ASTContext &Ctx,
- bool IsObjCLiteral) const {
- const PrintfConversionSpecifier &CS = getConversionSpecifier();
-
- if (!CS.consumesDataArgument())
- return ArgType::Invalid();
-
- if (CS.getKind() == ConversionSpecifier::cArg)
- switch (LM.getKind()) {
- case LengthModifier::None:
- return Ctx.IntTy;
- case LengthModifier::AsLong:
- case LengthModifier::AsWide:
- return ArgType(ArgType::WIntTy, "wint_t");
- case LengthModifier::AsShort:
- if (Ctx.getTargetInfo().getTriple().isOSMSVCRT())
- return Ctx.IntTy;
- LLVM_FALLTHROUGH;
- default:
- return ArgType::Invalid();
- }
-
- if (CS.isIntArg())
- switch (LM.getKind()) {
- case LengthModifier::AsLongDouble:
- // GNU extension.
- return Ctx.LongLongTy;
- case LengthModifier::None:
- return Ctx.IntTy;
- case LengthModifier::AsInt32:
- return ArgType(Ctx.IntTy, "__int32");
- case LengthModifier::AsChar: return ArgType::AnyCharTy;
- case LengthModifier::AsShort: return Ctx.ShortTy;
- case LengthModifier::AsLong: return Ctx.LongTy;
- case LengthModifier::AsLongLong:
- case LengthModifier::AsQuad:
- return Ctx.LongLongTy;
- case LengthModifier::AsInt64:
- return ArgType(Ctx.LongLongTy, "__int64");
- case LengthModifier::AsIntMax:
- return ArgType(Ctx.getIntMaxType(), "intmax_t");
- case LengthModifier::AsSizeT:
- return ArgType::makeSizeT(ArgType(Ctx.getSignedSizeType(), "ssize_t"));
- case LengthModifier::AsInt3264:
- return Ctx.getTargetInfo().getTriple().isArch64Bit()
- ? ArgType(Ctx.LongLongTy, "__int64")
- : ArgType(Ctx.IntTy, "__int32");
- case LengthModifier::AsPtrDiff:
- return ArgType::makePtrdiffT(
- ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"));
- case LengthModifier::AsAllocate:
- case LengthModifier::AsMAllocate:
- case LengthModifier::AsWide:
- return ArgType::Invalid();
- }
-
- if (CS.isUIntArg())
- switch (LM.getKind()) {
- case LengthModifier::AsLongDouble:
- // GNU extension.
- return Ctx.UnsignedLongLongTy;
- case LengthModifier::None:
- return Ctx.UnsignedIntTy;
- case LengthModifier::AsInt32:
- return ArgType(Ctx.UnsignedIntTy, "unsigned __int32");
- case LengthModifier::AsChar: return Ctx.UnsignedCharTy;
- case LengthModifier::AsShort: return Ctx.UnsignedShortTy;
- case LengthModifier::AsLong: return Ctx.UnsignedLongTy;
- case LengthModifier::AsLongLong:
- case LengthModifier::AsQuad:
- return Ctx.UnsignedLongLongTy;
- case LengthModifier::AsInt64:
- return ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64");
- case LengthModifier::AsIntMax:
- return ArgType(Ctx.getUIntMaxType(), "uintmax_t");
- case LengthModifier::AsSizeT:
- return ArgType::makeSizeT(ArgType(Ctx.getSizeType(), "size_t"));
- case LengthModifier::AsInt3264:
- return Ctx.getTargetInfo().getTriple().isArch64Bit()
- ? ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64")
- : ArgType(Ctx.UnsignedIntTy, "unsigned __int32");
- case LengthModifier::AsPtrDiff:
- return ArgType::makePtrdiffT(
- ArgType(Ctx.getUnsignedPointerDiffType(), "unsigned ptrdiff_t"));
- case LengthModifier::AsAllocate:
- case LengthModifier::AsMAllocate:
- case LengthModifier::AsWide:
- return ArgType::Invalid();
- }
-
- if (CS.isDoubleArg()) {
- if (LM.getKind() == LengthModifier::AsLongDouble)
- return Ctx.LongDoubleTy;
- return Ctx.DoubleTy;
- }
-
- if (CS.getKind() == ConversionSpecifier::nArg) {
- switch (LM.getKind()) {
- case LengthModifier::None:
- return ArgType::PtrTo(Ctx.IntTy);
- case LengthModifier::AsChar:
- return ArgType::PtrTo(Ctx.SignedCharTy);
- case LengthModifier::AsShort:
- return ArgType::PtrTo(Ctx.ShortTy);
- case LengthModifier::AsLong:
- return ArgType::PtrTo(Ctx.LongTy);
- case LengthModifier::AsLongLong:
- case LengthModifier::AsQuad:
- return ArgType::PtrTo(Ctx.LongLongTy);
- case LengthModifier::AsIntMax:
- return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t"));
- case LengthModifier::AsSizeT:
- return ArgType::PtrTo(ArgType(Ctx.getSignedSizeType(), "ssize_t"));
- case LengthModifier::AsPtrDiff:
- return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"));
- case LengthModifier::AsLongDouble:
- return ArgType(); // FIXME: Is this a known extension?
- case LengthModifier::AsAllocate:
- case LengthModifier::AsMAllocate:
- case LengthModifier::AsInt32:
- case LengthModifier::AsInt3264:
- case LengthModifier::AsInt64:
- case LengthModifier::AsWide:
- return ArgType::Invalid();
- }
- }
-
- switch (CS.getKind()) {
- case ConversionSpecifier::sArg:
- if (LM.getKind() == LengthModifier::AsWideChar) {
- if (IsObjCLiteral)
- return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()),
- "const unichar *");
- return ArgType(ArgType::WCStrTy, "wchar_t *");
- }
- if (LM.getKind() == LengthModifier::AsWide)
- return ArgType(ArgType::WCStrTy, "wchar_t *");
- return ArgType::CStrTy;
- case ConversionSpecifier::SArg:
- if (IsObjCLiteral)
- return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()),
- "const unichar *");
- if (Ctx.getTargetInfo().getTriple().isOSMSVCRT() &&
- LM.getKind() == LengthModifier::AsShort)
- return ArgType::CStrTy;
- return ArgType(ArgType::WCStrTy, "wchar_t *");
- case ConversionSpecifier::CArg:
- if (IsObjCLiteral)
- return ArgType(Ctx.UnsignedShortTy, "unichar");
- if (Ctx.getTargetInfo().getTriple().isOSMSVCRT() &&
- LM.getKind() == LengthModifier::AsShort)
- return Ctx.IntTy;
- return ArgType(Ctx.WideCharTy, "wchar_t");
- case ConversionSpecifier::pArg:
- case ConversionSpecifier::PArg:
- return ArgType::CPointerTy;
- case ConversionSpecifier::ObjCObjArg:
- return ArgType::ObjCPointerTy;
- default:
- break;
- }
-
- // FIXME: Handle other cases.
- return ArgType();
-}
-
-bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
- ASTContext &Ctx, bool IsObjCLiteral) {
- // %n is different from other conversion specifiers; don't try to fix it.
- if (CS.getKind() == ConversionSpecifier::nArg)
- return false;
-
- // Handle Objective-C objects first. Note that while the '%@' specifier will
- // not warn for structure pointer or void pointer arguments (because that's
- // how CoreFoundation objects are implemented), we only show a fixit for '%@'
- // if we know it's an object (block, id, class, or __attribute__((NSObject))).
- if (QT->isObjCRetainableType()) {
- if (!IsObjCLiteral)
- return false;
-
- CS.setKind(ConversionSpecifier::ObjCObjArg);
-
- // Disable irrelevant flags
- HasThousandsGrouping = false;
- HasPlusPrefix = false;
- HasSpacePrefix = false;
- HasAlternativeForm = false;
- HasLeadingZeroes = false;
- Precision.setHowSpecified(OptionalAmount::NotSpecified);
- LM.setKind(LengthModifier::None);
-
- return true;
- }
-
- // Handle strings next (char *, wchar_t *)
- if (QT->isPointerType() && (QT->getPointeeType()->isAnyCharacterType())) {
- CS.setKind(ConversionSpecifier::sArg);
-
- // Disable irrelevant flags
- HasAlternativeForm = 0;
- HasLeadingZeroes = 0;
-
- // Set the long length modifier for wide characters
- if (QT->getPointeeType()->isWideCharType())
- LM.setKind(LengthModifier::AsWideChar);
- else
- LM.setKind(LengthModifier::None);
-
- return true;
- }
-
- // If it's an enum, get its underlying type.
- if (const EnumType *ETy = QT->getAs<EnumType>())
- QT = ETy->getDecl()->getIntegerType();
-
- // We can only work with builtin types.
- const BuiltinType *BT = QT->getAs<BuiltinType>();
- if (!BT)
- return false;
-
- // Set length modifier
- switch (BT->getKind()) {
- case BuiltinType::Bool:
- case BuiltinType::WChar_U:
- case BuiltinType::WChar_S:
- case BuiltinType::Char8: // FIXME: Treat like 'char'?
- case BuiltinType::Char16:
- case BuiltinType::Char32:
- case BuiltinType::UInt128:
- case BuiltinType::Int128:
- case BuiltinType::Half:
- case BuiltinType::Float16:
- case BuiltinType::Float128:
- case BuiltinType::ShortAccum:
- case BuiltinType::Accum:
- case BuiltinType::LongAccum:
- case BuiltinType::UShortAccum:
- case BuiltinType::UAccum:
- case BuiltinType::ULongAccum:
- case BuiltinType::ShortFract:
- case BuiltinType::Fract:
- case BuiltinType::LongFract:
- case BuiltinType::UShortFract:
- case BuiltinType::UFract:
- case BuiltinType::ULongFract:
- case BuiltinType::SatShortAccum:
- case BuiltinType::SatAccum:
- case BuiltinType::SatLongAccum:
- case BuiltinType::SatUShortAccum:
- case BuiltinType::SatUAccum:
- case BuiltinType::SatULongAccum:
- case BuiltinType::SatShortFract:
- case BuiltinType::SatFract:
- case BuiltinType::SatLongFract:
- case BuiltinType::SatUShortFract:
- case BuiltinType::SatUFract:
- case BuiltinType::SatULongFract:
- // Various types which are non-trivial to correct.
- return false;
-
-#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
- case BuiltinType::Id:
-#include "clang/Basic/OpenCLImageTypes.def"
-#define SIGNED_TYPE(Id, SingletonId)
-#define UNSIGNED_TYPE(Id, SingletonId)
-#define FLOATING_TYPE(Id, SingletonId)
-#define BUILTIN_TYPE(Id, SingletonId) \
- case BuiltinType::Id:
-#include "clang/AST/BuiltinTypes.def"
- // Misc other stuff which doesn't make sense here.
- return false;
-
- case BuiltinType::UInt:
- case BuiltinType::Int:
- case BuiltinType::Float:
- case BuiltinType::Double:
- LM.setKind(LengthModifier::None);
- break;
-
- case BuiltinType::Char_U:
- case BuiltinType::UChar:
- case BuiltinType::Char_S:
- case BuiltinType::SChar:
- LM.setKind(LengthModifier::AsChar);
- break;
-
- case BuiltinType::Short:
- case BuiltinType::UShort:
- LM.setKind(LengthModifier::AsShort);
- break;
-
- case BuiltinType::Long:
- case BuiltinType::ULong:
- LM.setKind(LengthModifier::AsLong);
- break;
-
- case BuiltinType::LongLong:
- case BuiltinType::ULongLong:
- LM.setKind(LengthModifier::AsLongLong);
- break;
-
- case BuiltinType::LongDouble:
- LM.setKind(LengthModifier::AsLongDouble);
- break;
- }
-
- // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99.
- if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus11))
- namedTypeToLengthModifier(QT, LM);
-
- // If fixing the length modifier was enough, we might be done.
- if (hasValidLengthModifier(Ctx.getTargetInfo())) {
- // If we're going to offer a fix anyway, make sure the sign matches.
- switch (CS.getKind()) {
- case ConversionSpecifier::uArg:
- case ConversionSpecifier::UArg:
- if (QT->isSignedIntegerType())
- CS.setKind(clang::analyze_format_string::ConversionSpecifier::dArg);
- break;
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::DArg:
- case ConversionSpecifier::iArg:
- if (QT->isUnsignedIntegerType() && !HasPlusPrefix)
- CS.setKind(clang::analyze_format_string::ConversionSpecifier::uArg);
- break;
- default:
- // Other specifiers do not have signed/unsigned variants.
- break;
- }
-
- const analyze_printf::ArgType &ATR = getArgType(Ctx, IsObjCLiteral);
- if (ATR.isValid() && ATR.matchesType(Ctx, QT))
- return true;
- }
-
- // Set conversion specifier and disable any flags which do not apply to it.
- // Let typedefs to char fall through to int, as %c is silly for uint8_t.
- if (!isa<TypedefType>(QT) && QT->isCharType()) {
- CS.setKind(ConversionSpecifier::cArg);
- LM.setKind(LengthModifier::None);
- Precision.setHowSpecified(OptionalAmount::NotSpecified);
- HasAlternativeForm = 0;
- HasLeadingZeroes = 0;
- HasPlusPrefix = 0;
- }
- // Test for Floating type first as LongDouble can pass isUnsignedIntegerType
- else if (QT->isRealFloatingType()) {
- CS.setKind(ConversionSpecifier::fArg);
- }
- else if (QT->isSignedIntegerType()) {
- CS.setKind(ConversionSpecifier::dArg);
- HasAlternativeForm = 0;
- }
- else if (QT->isUnsignedIntegerType()) {
- CS.setKind(ConversionSpecifier::uArg);
- HasAlternativeForm = 0;
- HasPlusPrefix = 0;
- } else {
- llvm_unreachable("Unexpected type");
- }
-
- return true;
-}
-
-void PrintfSpecifier::toString(raw_ostream &os) const {
- // Whilst some features have no defined order, we are using the order
- // appearing in the C99 standard (ISO/IEC 9899:1999 (E) 7.19.6.1)
- os << "%";
-
- // Positional args
- if (usesPositionalArg()) {
- os << getPositionalArgIndex() << "$";
- }
-
- // Conversion flags
- if (IsLeftJustified) os << "-";
- if (HasPlusPrefix) os << "+";
- if (HasSpacePrefix) os << " ";
- if (HasAlternativeForm) os << "#";
- if (HasLeadingZeroes) os << "0";
-
- // Minimum field width
- FieldWidth.toString(os);
- // Precision
- Precision.toString(os);
- // Length modifier
- os << LM.toString();
- // Conversion specifier
- os << CS.toString();
-}
-
-bool PrintfSpecifier::hasValidPlusPrefix() const {
- if (!HasPlusPrefix)
- return true;
-
- // The plus prefix only makes sense for signed conversions
- switch (CS.getKind()) {
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::DArg:
- case ConversionSpecifier::iArg:
- case ConversionSpecifier::fArg:
- case ConversionSpecifier::FArg:
- case ConversionSpecifier::eArg:
- case ConversionSpecifier::EArg:
- case ConversionSpecifier::gArg:
- case ConversionSpecifier::GArg:
- case ConversionSpecifier::aArg:
- case ConversionSpecifier::AArg:
- case ConversionSpecifier::FreeBSDrArg:
- case ConversionSpecifier::FreeBSDyArg:
- return true;
-
- default:
- return false;
- }
-}
-
-bool PrintfSpecifier::hasValidAlternativeForm() const {
- if (!HasAlternativeForm)
- return true;
-
- // Alternate form flag only valid with the oxXaAeEfFgG conversions
- switch (CS.getKind()) {
- case ConversionSpecifier::oArg:
- case ConversionSpecifier::OArg:
- case ConversionSpecifier::xArg:
- case ConversionSpecifier::XArg:
- case ConversionSpecifier::aArg:
- case ConversionSpecifier::AArg:
- case ConversionSpecifier::eArg:
- case ConversionSpecifier::EArg:
- case ConversionSpecifier::fArg:
- case ConversionSpecifier::FArg:
- case ConversionSpecifier::gArg:
- case ConversionSpecifier::GArg:
- case ConversionSpecifier::FreeBSDrArg:
- case ConversionSpecifier::FreeBSDyArg:
- return true;
-
- default:
- return false;
- }
-}
-
-bool PrintfSpecifier::hasValidLeadingZeros() const {
- if (!HasLeadingZeroes)
- return true;
-
- // Leading zeroes flag only valid with the diouxXaAeEfFgG conversions
- switch (CS.getKind()) {
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::DArg:
- case ConversionSpecifier::iArg:
- case ConversionSpecifier::oArg:
- case ConversionSpecifier::OArg:
- case ConversionSpecifier::uArg:
- case ConversionSpecifier::UArg:
- case ConversionSpecifier::xArg:
- case ConversionSpecifier::XArg:
- case ConversionSpecifier::aArg:
- case ConversionSpecifier::AArg:
- case ConversionSpecifier::eArg:
- case ConversionSpecifier::EArg:
- case ConversionSpecifier::fArg:
- case ConversionSpecifier::FArg:
- case ConversionSpecifier::gArg:
- case ConversionSpecifier::GArg:
- case ConversionSpecifier::FreeBSDrArg:
- case ConversionSpecifier::FreeBSDyArg:
- return true;
-
- default:
- return false;
- }
-}
-
-bool PrintfSpecifier::hasValidSpacePrefix() const {
- if (!HasSpacePrefix)
- return true;
-
- // The space prefix only makes sense for signed conversions
- switch (CS.getKind()) {
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::DArg:
- case ConversionSpecifier::iArg:
- case ConversionSpecifier::fArg:
- case ConversionSpecifier::FArg:
- case ConversionSpecifier::eArg:
- case ConversionSpecifier::EArg:
- case ConversionSpecifier::gArg:
- case ConversionSpecifier::GArg:
- case ConversionSpecifier::aArg:
- case ConversionSpecifier::AArg:
- case ConversionSpecifier::FreeBSDrArg:
- case ConversionSpecifier::FreeBSDyArg:
- return true;
-
- default:
- return false;
- }
-}
-
-bool PrintfSpecifier::hasValidLeftJustified() const {
- if (!IsLeftJustified)
- return true;
-
- // The left justified flag is valid for all conversions except n
- switch (CS.getKind()) {
- case ConversionSpecifier::nArg:
- return false;
-
- default:
- return true;
- }
-}
-
-bool PrintfSpecifier::hasValidThousandsGroupingPrefix() const {
- if (!HasThousandsGrouping)
- return true;
-
- switch (CS.getKind()) {
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::DArg:
- case ConversionSpecifier::iArg:
- case ConversionSpecifier::uArg:
- case ConversionSpecifier::UArg:
- case ConversionSpecifier::fArg:
- case ConversionSpecifier::FArg:
- case ConversionSpecifier::gArg:
- case ConversionSpecifier::GArg:
- return true;
- default:
- return false;
- }
-}
-
-bool PrintfSpecifier::hasValidPrecision() const {
- if (Precision.getHowSpecified() == OptionalAmount::NotSpecified)
- return true;
-
- // Precision is only valid with the diouxXaAeEfFgGsP conversions
- switch (CS.getKind()) {
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::DArg:
- case ConversionSpecifier::iArg:
- case ConversionSpecifier::oArg:
- case ConversionSpecifier::OArg:
- case ConversionSpecifier::uArg:
- case ConversionSpecifier::UArg:
- case ConversionSpecifier::xArg:
- case ConversionSpecifier::XArg:
- case ConversionSpecifier::aArg:
- case ConversionSpecifier::AArg:
- case ConversionSpecifier::eArg:
- case ConversionSpecifier::EArg:
- case ConversionSpecifier::fArg:
- case ConversionSpecifier::FArg:
- case ConversionSpecifier::gArg:
- case ConversionSpecifier::GArg:
- case ConversionSpecifier::sArg:
- case ConversionSpecifier::FreeBSDrArg:
- case ConversionSpecifier::FreeBSDyArg:
- case ConversionSpecifier::PArg:
- return true;
-
- default:
- return false;
- }
-}
-bool PrintfSpecifier::hasValidFieldWidth() const {
- if (FieldWidth.getHowSpecified() == OptionalAmount::NotSpecified)
- return true;
-
- // The field width is valid for all conversions except n
- switch (CS.getKind()) {
- case ConversionSpecifier::nArg:
- return false;
-
- default:
- return true;
- }
-}
diff --git a/lib/Analysis/ProgramPoint.cpp b/lib/Analysis/ProgramPoint.cpp
index d9833659d7b3..2d016cb13353 100644
--- a/lib/Analysis/ProgramPoint.cpp
+++ b/lib/Analysis/ProgramPoint.cpp
@@ -43,6 +43,181 @@ ProgramPoint ProgramPoint::getProgramPoint(const Stmt *S, ProgramPoint::Kind K,
}
}
+LLVM_DUMP_METHOD void ProgramPoint::dump() const {
+ return print(/*CR=*/"\n", llvm::errs());
+}
+
+static void printLocation(raw_ostream &Out, SourceLocation SLoc,
+ const SourceManager &SM,
+ StringRef CR,
+ StringRef Postfix) {
+ if (SLoc.isFileID()) {
+ Out << CR << "line=" << SM.getExpansionLineNumber(SLoc)
+ << " col=" << SM.getExpansionColumnNumber(SLoc) << Postfix;
+ }
+}
+
+void ProgramPoint::print(StringRef CR, llvm::raw_ostream &Out) const {
+ const ASTContext &Context =
+ getLocationContext()->getAnalysisDeclContext()->getASTContext();
+ const SourceManager &SM = Context.getSourceManager();
+ switch (getKind()) {
+ case ProgramPoint::BlockEntranceKind:
+ Out << "Block Entrance: B"
+ << castAs<BlockEntrance>().getBlock()->getBlockID();
+ break;
+
+ case ProgramPoint::FunctionExitKind: {
+ auto FEP = getAs<FunctionExitPoint>();
+ Out << "Function Exit: B" << FEP->getBlock()->getBlockID();
+ if (const ReturnStmt *RS = FEP->getStmt()) {
+ Out << CR << " Return: S" << RS->getID(Context) << CR;
+ RS->printPretty(Out, /*helper=*/nullptr, Context.getPrintingPolicy(),
+ /*Indentation=*/2, /*NewlineSymbol=*/CR);
+ }
+ break;
+ }
+ case ProgramPoint::BlockExitKind:
+ assert(false);
+ break;
+
+ case ProgramPoint::CallEnterKind:
+ Out << "CallEnter";
+ break;
+
+ case ProgramPoint::CallExitBeginKind:
+ Out << "CallExitBegin";
+ break;
+
+ case ProgramPoint::CallExitEndKind:
+ Out << "CallExitEnd";
+ break;
+
+ case ProgramPoint::PostStmtPurgeDeadSymbolsKind:
+ Out << "PostStmtPurgeDeadSymbols";
+ break;
+
+ case ProgramPoint::PreStmtPurgeDeadSymbolsKind:
+ Out << "PreStmtPurgeDeadSymbols";
+ break;
+
+ case ProgramPoint::EpsilonKind:
+ Out << "Epsilon Point";
+ break;
+
+ case ProgramPoint::LoopExitKind: {
+ LoopExit LE = castAs<LoopExit>();
+ Out << "LoopExit: " << LE.getLoopStmt()->getStmtClassName();
+ break;
+ }
+
+ case ProgramPoint::PreImplicitCallKind: {
+ ImplicitCallPoint PC = castAs<ImplicitCallPoint>();
+ Out << "PreCall: ";
+ PC.getDecl()->print(Out, Context.getLangOpts());
+ printLocation(Out, PC.getLocation(), SM, CR, /*Postfix=*/CR);
+ break;
+ }
+
+ case ProgramPoint::PostImplicitCallKind: {
+ ImplicitCallPoint PC = castAs<ImplicitCallPoint>();
+ Out << "PostCall: ";
+ PC.getDecl()->print(Out, Context.getLangOpts());
+ printLocation(Out, PC.getLocation(), SM, CR, /*Postfix=*/CR);
+ break;
+ }
+
+ case ProgramPoint::PostInitializerKind: {
+ Out << "PostInitializer: ";
+ const CXXCtorInitializer *Init = castAs<PostInitializer>().getInitializer();
+ if (const FieldDecl *FD = Init->getAnyMember())
+ Out << *FD;
+ else {
+ QualType Ty = Init->getTypeSourceInfo()->getType();
+ Ty = Ty.getLocalUnqualifiedType();
+ Ty.print(Out, Context.getLangOpts());
+ }
+ break;
+ }
+
+ case ProgramPoint::BlockEdgeKind: {
+ const BlockEdge &E = castAs<BlockEdge>();
+ Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B"
+ << E.getDst()->getBlockID() << ')';
+
+ if (const Stmt *T = E.getSrc()->getTerminator()) {
+ SourceLocation SLoc = T->getBeginLoc();
+
+ Out << "\\|Terminator: ";
+ E.getSrc()->printTerminator(Out, Context.getLangOpts());
+ printLocation(Out, SLoc, SM, CR, /*Postfix=*/"");
+
+ if (isa<SwitchStmt>(T)) {
+ const Stmt *Label = E.getDst()->getLabel();
+
+ if (Label) {
+ if (const auto *C = dyn_cast<CaseStmt>(Label)) {
+ Out << CR << "case ";
+ if (C->getLHS())
+ C->getLHS()->printPretty(
+ Out, nullptr, Context.getPrintingPolicy(),
+ /*Indentation=*/0, /*NewlineSymbol=*/CR);
+
+ if (const Stmt *RHS = C->getRHS()) {
+ Out << " .. ";
+ RHS->printPretty(Out, nullptr, Context.getPrintingPolicy(),
+ /*Indetation=*/0, /*NewlineSymbol=*/CR);
+ }
+
+ Out << ":";
+ } else {
+ assert(isa<DefaultStmt>(Label));
+ Out << CR << "default:";
+ }
+ } else
+ Out << CR << "(implicit) default:";
+ } else if (isa<IndirectGotoStmt>(T)) {
+ // FIXME
+ } else {
+ Out << CR << "Condition: ";
+ if (*E.getSrc()->succ_begin() == E.getDst())
+ Out << "true";
+ else
+ Out << "false";
+ }
+
+ Out << CR;
+ }
+
+ break;
+ }
+
+ default: {
+ const Stmt *S = castAs<StmtPoint>().getStmt();
+ assert(S != nullptr && "Expecting non-null Stmt");
+
+ Out << S->getStmtClassName() << " S" << S->getID(Context) << " <"
+ << (const void *)S << "> ";
+ S->printPretty(Out, /*helper=*/nullptr, Context.getPrintingPolicy(),
+ /*Indentation=*/2, /*NewlineSymbol=*/CR);
+ printLocation(Out, S->getBeginLoc(), SM, CR, /*Postfix=*/"");
+
+ if (getAs<PreStmt>())
+ Out << CR << "PreStmt" << CR;
+ else if (getAs<PostLoad>())
+ Out << CR << "PostLoad" << CR;
+ else if (getAs<PostStore>())
+ Out << CR << "PostStore" << CR;
+ else if (getAs<PostLValue>())
+ Out << CR << "PostLValue" << CR;
+ else if (getAs<PostAllocatorCall>())
+ Out << CR << "PostAllocatorCall" << CR;
+
+ break;
+ }
+ }
+}
+
SimpleProgramPointTag::SimpleProgramPointTag(StringRef MsgProvider,
StringRef Msg)
: Desc((MsgProvider + " : " + Msg).str()) {}
diff --git a/lib/Analysis/PseudoConstantAnalysis.cpp b/lib/Analysis/PseudoConstantAnalysis.cpp
deleted file mode 100644
index 83b545a7be83..000000000000
--- a/lib/Analysis/PseudoConstantAnalysis.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-//== PseudoConstantAnalysis.cpp - Find Pseudoconstants in the AST-*- C++ -*-==//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file tracks the usage of variables in a Decl body to see if they are
-// never written to, implying that they constant. This is useful in static
-// analysis to see if a developer might have intended a variable to be const.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/Stmt.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include <deque>
-
-using namespace clang;
-
-typedef llvm::SmallPtrSet<const VarDecl*, 32> VarDeclSet;
-
-PseudoConstantAnalysis::PseudoConstantAnalysis(const Stmt *DeclBody) :
- DeclBody(DeclBody), Analyzed(false) {
- NonConstantsImpl = new VarDeclSet;
- UsedVarsImpl = new VarDeclSet;
-}
-
-PseudoConstantAnalysis::~PseudoConstantAnalysis() {
- delete (VarDeclSet*)NonConstantsImpl;
- delete (VarDeclSet*)UsedVarsImpl;
-}
-
-// Returns true if the given ValueDecl is never written to in the given DeclBody
-bool PseudoConstantAnalysis::isPseudoConstant(const VarDecl *VD) {
- // Only local and static variables can be pseudoconstants
- if (!VD->hasLocalStorage() && !VD->isStaticLocal())
- return false;
-
- if (!Analyzed) {
- RunAnalysis();
- Analyzed = true;
- }
-
- VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl;
-
- return !NonConstants->count(VD);
-}
-
-// Returns true if the variable was used (self assignments don't count)
-bool PseudoConstantAnalysis::wasReferenced(const VarDecl *VD) {
- if (!Analyzed) {
- RunAnalysis();
- Analyzed = true;
- }
-
- VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl;
-
- return UsedVars->count(VD);
-}
-
-// Returns a Decl from a (Block)DeclRefExpr (if any)
-const Decl *PseudoConstantAnalysis::getDecl(const Expr *E) {
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
- return DR->getDecl();
- else
- return nullptr;
-}
-
-void PseudoConstantAnalysis::RunAnalysis() {
- std::deque<const Stmt *> WorkList;
- VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl;
- VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl;
-
- // Start with the top level statement of the function
- WorkList.push_back(DeclBody);
-
- while (!WorkList.empty()) {
- const Stmt *Head = WorkList.front();
- WorkList.pop_front();
-
- if (const Expr *Ex = dyn_cast<Expr>(Head))
- Head = Ex->IgnoreParenCasts();
-
- switch (Head->getStmtClass()) {
- // Case 1: Assignment operators modifying VarDecls
- case Stmt::BinaryOperatorClass: {
- const BinaryOperator *BO = cast<BinaryOperator>(Head);
- // Look for a Decl on the LHS
- const Decl *LHSDecl = getDecl(BO->getLHS()->IgnoreParenCasts());
- if (!LHSDecl)
- break;
-
- // We found a binary operator with a DeclRefExpr on the LHS. We now check
- // for any of the assignment operators, implying that this Decl is being
- // written to.
- switch (BO->getOpcode()) {
- // Self-assignments don't count as use of a variable
- case BO_Assign: {
- // Look for a DeclRef on the RHS
- const Decl *RHSDecl = getDecl(BO->getRHS()->IgnoreParenCasts());
-
- // If the Decls match, we have self-assignment
- if (LHSDecl == RHSDecl)
- // Do not visit the children
- continue;
-
- LLVM_FALLTHROUGH;
- }
- case BO_AddAssign:
- case BO_SubAssign:
- case BO_MulAssign:
- case BO_DivAssign:
- case BO_AndAssign:
- case BO_OrAssign:
- case BO_XorAssign:
- case BO_ShlAssign:
- case BO_ShrAssign: {
- const VarDecl *VD = dyn_cast<VarDecl>(LHSDecl);
- // The DeclRefExpr is being assigned to - mark it as non-constant
- if (VD)
- NonConstants->insert(VD);
- break;
- }
-
- default:
- break;
- }
- break;
- }
-
- // Case 2: Pre/post increment/decrement and address of
- case Stmt::UnaryOperatorClass: {
- const UnaryOperator *UO = cast<UnaryOperator>(Head);
-
- // Look for a DeclRef in the subexpression
- const Decl *D = getDecl(UO->getSubExpr()->IgnoreParenCasts());
- if (!D)
- break;
-
- // We found a unary operator with a DeclRef as a subexpression. We now
- // check for any of the increment/decrement operators, as well as
- // addressOf.
- switch (UO->getOpcode()) {
- case UO_PostDec:
- case UO_PostInc:
- case UO_PreDec:
- case UO_PreInc:
- // The DeclRef is being changed - mark it as non-constant
- case UO_AddrOf: {
- // If we are taking the address of the DeclRefExpr, assume it is
- // non-constant.
- const VarDecl *VD = dyn_cast<VarDecl>(D);
- if (VD)
- NonConstants->insert(VD);
- break;
- }
-
- default:
- break;
- }
- break;
- }
-
- // Case 3: Reference Declarations
- case Stmt::DeclStmtClass: {
- const DeclStmt *DS = cast<DeclStmt>(Head);
- // Iterate over each decl and see if any of them contain reference decls
- for (const auto *I : DS->decls()) {
- // We only care about VarDecls
- const VarDecl *VD = dyn_cast<VarDecl>(I);
- if (!VD)
- continue;
-
- // We found a VarDecl; make sure it is a reference type
- if (!VD->getType().getTypePtr()->isReferenceType())
- continue;
-
- // Try to find a Decl in the initializer
- const Decl *D = getDecl(VD->getInit()->IgnoreParenCasts());
- if (!D)
- break;
-
- // If the reference is to another var, add the var to the non-constant
- // list
- if (const VarDecl *RefVD = dyn_cast<VarDecl>(D)) {
- NonConstants->insert(RefVD);
- continue;
- }
- }
- break;
- }
-
- // Case 4: Variable references
- case Stmt::DeclRefExprClass: {
- const DeclRefExpr *DR = cast<DeclRefExpr>(Head);
- if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
- // Add the Decl to the used list
- UsedVars->insert(VD);
- continue;
- }
- break;
- }
-
- // Case 5: Block expressions
- case Stmt::BlockExprClass: {
- const BlockExpr *B = cast<BlockExpr>(Head);
- // Add the body of the block to the list
- WorkList.push_back(B->getBody());
- continue;
- }
-
- default:
- break;
- } // switch (head->getStmtClass())
-
- // Add all substatements to the worklist
- for (const Stmt *SubStmt : Head->children())
- if (SubStmt)
- WorkList.push_back(SubStmt);
- } // while (!WorkList.empty())
-}
diff --git a/lib/Analysis/ReachableCode.cpp b/lib/Analysis/ReachableCode.cpp
index ed26a94f3d60..87f4f7010f98 100644
--- a/lib/Analysis/ReachableCode.cpp
+++ b/lib/Analysis/ReachableCode.cpp
@@ -153,7 +153,7 @@ static bool isExpandedFromConfigurationMacro(const Stmt *S,
// value comes from a macro, but we can do much better. This is likely
// to be over conservative. This logic is factored into a separate function
// so that we can refine it later.
- SourceLocation L = S->getLocStart();
+ SourceLocation L = S->getBeginLoc();
if (L.isMacroID()) {
SourceManager &SM = PP.getSourceManager();
if (IgnoreYES_NO) {
@@ -200,7 +200,7 @@ static bool isConfigurationValue(const Stmt *S,
// Special case looking for the sigil '()' around an integer literal.
if (const ParenExpr *PE = dyn_cast<ParenExpr>(S))
- if (!PE->getLocStart().isMacroID())
+ if (!PE->getBeginLoc().isMacroID())
return isConfigurationValue(PE->getSubExpr(), PP, SilenceableCondVal,
IncludeIntegers, true);
@@ -219,7 +219,7 @@ static bool isConfigurationValue(const Stmt *S,
return isConfigurationValue(cast<DeclRefExpr>(S)->getDecl(), PP);
case Stmt::ObjCBoolLiteralExprClass:
IgnoreYES_NO = true;
- // Fallthrough.
+ LLVM_FALLTHROUGH;
case Stmt::CXXBoolLiteralExprClass:
case Stmt::IntegerLiteralClass: {
const Expr *E = cast<Expr>(S);
@@ -446,7 +446,7 @@ bool DeadCodeScan::isDeadCodeRoot(const clang::CFGBlock *Block) {
}
static bool isValidDeadStmt(const Stmt *S) {
- if (S->getLocStart().isInvalid())
+ if (S->getBeginLoc().isInvalid())
return false;
if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S))
return BO->getOpcode() != BO_Comma;
@@ -474,9 +474,9 @@ const Stmt *DeadCodeScan::findDeadCode(const clang::CFGBlock *Block) {
static int SrcCmp(const std::pair<const CFGBlock *, const Stmt *> *p1,
const std::pair<const CFGBlock *, const Stmt *> *p2) {
- if (p1->second->getLocStart() < p2->second->getLocStart())
+ if (p1->second->getBeginLoc() < p2->second->getBeginLoc())
return -1;
- if (p2->second->getLocStart() < p1->second->getLocStart())
+ if (p2->second->getBeginLoc() < p1->second->getBeginLoc())
return 1;
return 0;
}
@@ -509,7 +509,7 @@ unsigned DeadCodeScan::scanBackwards(const clang::CFGBlock *Start,
}
// Specially handle macro-expanded code.
- if (S->getLocStart().isMacroID()) {
+ if (S->getBeginLoc().isMacroID()) {
count += scanMaybeReachableFromBlock(Block, PP, Reachable);
continue;
}
@@ -592,7 +592,7 @@ static SourceLocation GetUnreachableLoc(const Stmt *S,
case Expr::CXXFunctionalCastExprClass: {
const CXXFunctionalCastExpr *CE = cast <CXXFunctionalCastExpr>(S);
R1 = CE->getSubExpr()->getSourceRange();
- return CE->getLocStart();
+ return CE->getBeginLoc();
}
case Stmt::CXXTryStmtClass: {
return cast<CXXTryStmt>(S)->getHandler(0)->getCatchLoc();
@@ -605,7 +605,7 @@ static SourceLocation GetUnreachableLoc(const Stmt *S,
default: ;
}
R1 = S->getSourceRange();
- return S->getLocStart();
+ return S->getBeginLoc();
}
void DeadCodeScan::reportDeadCode(const CFGBlock *B,
@@ -631,12 +631,12 @@ void DeadCodeScan::reportDeadCode(const CFGBlock *B,
// a for/for-range loop. This is the block that contains
// the increment code.
if (const Stmt *LoopTarget = B->getLoopTarget()) {
- SourceLocation Loc = LoopTarget->getLocStart();
+ SourceLocation Loc = LoopTarget->getBeginLoc();
SourceRange R1(Loc, Loc), R2;
if (const ForStmt *FS = dyn_cast<ForStmt>(LoopTarget)) {
const Expr *Inc = FS->getInc();
- Loc = Inc->getLocStart();
+ Loc = Inc->getBeginLoc();
R2 = Inc->getSourceRange();
}
diff --git a/lib/Analysis/ScanfFormatString.cpp b/lib/Analysis/ScanfFormatString.cpp
deleted file mode 100644
index a9af0cdfdacd..000000000000
--- a/lib/Analysis/ScanfFormatString.cpp
+++ /dev/null
@@ -1,563 +0,0 @@
-//= ScanfFormatString.cpp - Analysis of printf format strings --*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Handling of format string in scanf and friends. The structure of format
-// strings for fscanf() are described in C99 7.19.6.2.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/Analysis/Analyses/FormatString.h"
-#include "FormatStringParsing.h"
-#include "clang/Basic/TargetInfo.h"
-
-using clang::analyze_format_string::ArgType;
-using clang::analyze_format_string::FormatStringHandler;
-using clang::analyze_format_string::LengthModifier;
-using clang::analyze_format_string::OptionalAmount;
-using clang::analyze_format_string::ConversionSpecifier;
-using clang::analyze_scanf::ScanfConversionSpecifier;
-using clang::analyze_scanf::ScanfSpecifier;
-using clang::UpdateOnReturn;
-using namespace clang;
-
-typedef clang::analyze_format_string::SpecifierResult<ScanfSpecifier>
- ScanfSpecifierResult;
-
-static bool ParseScanList(FormatStringHandler &H,
- ScanfConversionSpecifier &CS,
- const char *&Beg, const char *E) {
- const char *I = Beg;
- const char *start = I - 1;
- UpdateOnReturn <const char*> UpdateBeg(Beg, I);
-
- // No more characters?
- if (I == E) {
- H.HandleIncompleteScanList(start, I);
- return true;
- }
-
- // Special case: ']' is the first character.
- if (*I == ']') {
- if (++I == E) {
- H.HandleIncompleteScanList(start, I - 1);
- return true;
- }
- }
-
- // Special case: "^]" are the first characters.
- if (I + 1 != E && I[0] == '^' && I[1] == ']') {
- I += 2;
- if (I == E) {
- H.HandleIncompleteScanList(start, I - 1);
- return true;
- }
- }
-
- // Look for a ']' character which denotes the end of the scan list.
- while (*I != ']') {
- if (++I == E) {
- H.HandleIncompleteScanList(start, I - 1);
- return true;
- }
- }
-
- CS.setEndScanList(I);
- return false;
-}
-
-// FIXME: Much of this is copy-paste from ParsePrintfSpecifier.
-// We can possibly refactor.
-static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H,
- const char *&Beg,
- const char *E,
- unsigned &argIndex,
- const LangOptions &LO,
- const TargetInfo &Target) {
- using namespace clang::analyze_format_string;
- using namespace clang::analyze_scanf;
- const char *I = Beg;
- const char *Start = nullptr;
- UpdateOnReturn <const char*> UpdateBeg(Beg, I);
-
- // Look for a '%' character that indicates the start of a format specifier.
- for ( ; I != E ; ++I) {
- char c = *I;
- if (c == '\0') {
- // Detect spurious null characters, which are likely errors.
- H.HandleNullChar(I);
- return true;
- }
- if (c == '%') {
- Start = I++; // Record the start of the format specifier.
- break;
- }
- }
-
- // No format specifier found?
- if (!Start)
- return false;
-
- if (I == E) {
- // No more characters left?
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
-
- ScanfSpecifier FS;
- if (ParseArgPosition(H, FS, Start, I, E))
- return true;
-
- if (I == E) {
- // No more characters left?
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
-
- // Look for '*' flag if it is present.
- if (*I == '*') {
- FS.setSuppressAssignment(I);
- if (++I == E) {
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
- }
-
- // Look for the field width (if any). Unlike printf, this is either
- // a fixed integer or isn't present.
- const OptionalAmount &Amt = clang::analyze_format_string::ParseAmount(I, E);
- if (Amt.getHowSpecified() != OptionalAmount::NotSpecified) {
- assert(Amt.getHowSpecified() == OptionalAmount::Constant);
- FS.setFieldWidth(Amt);
-
- if (I == E) {
- // No more characters left?
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
- }
-
- // Look for the length modifier.
- if (ParseLengthModifier(FS, I, E, LO, /*scanf=*/true) && I == E) {
- // No more characters left?
- H.HandleIncompleteSpecifier(Start, E - Start);
- return true;
- }
-
- // Detect spurious null characters, which are likely errors.
- if (*I == '\0') {
- H.HandleNullChar(I);
- return true;
- }
-
- // Finally, look for the conversion specifier.
- const char *conversionPosition = I++;
- ScanfConversionSpecifier::Kind k = ScanfConversionSpecifier::InvalidSpecifier;
- switch (*conversionPosition) {
- default:
- break;
- case '%': k = ConversionSpecifier::PercentArg; break;
- case 'A': k = ConversionSpecifier::AArg; break;
- case 'E': k = ConversionSpecifier::EArg; break;
- case 'F': k = ConversionSpecifier::FArg; break;
- case 'G': k = ConversionSpecifier::GArg; break;
- case 'X': k = ConversionSpecifier::XArg; break;
- case 'a': k = ConversionSpecifier::aArg; break;
- case 'd': k = ConversionSpecifier::dArg; break;
- case 'e': k = ConversionSpecifier::eArg; break;
- case 'f': k = ConversionSpecifier::fArg; break;
- case 'g': k = ConversionSpecifier::gArg; break;
- case 'i': k = ConversionSpecifier::iArg; break;
- case 'n': k = ConversionSpecifier::nArg; break;
- case 'c': k = ConversionSpecifier::cArg; break;
- case 'C': k = ConversionSpecifier::CArg; break;
- case 'S': k = ConversionSpecifier::SArg; break;
- case '[': k = ConversionSpecifier::ScanListArg; break;
- case 'u': k = ConversionSpecifier::uArg; break;
- case 'x': k = ConversionSpecifier::xArg; break;
- case 'o': k = ConversionSpecifier::oArg; break;
- case 's': k = ConversionSpecifier::sArg; break;
- case 'p': k = ConversionSpecifier::pArg; break;
- // Apple extensions
- // Apple-specific
- case 'D':
- if (Target.getTriple().isOSDarwin())
- k = ConversionSpecifier::DArg;
- break;
- case 'O':
- if (Target.getTriple().isOSDarwin())
- k = ConversionSpecifier::OArg;
- break;
- case 'U':
- if (Target.getTriple().isOSDarwin())
- k = ConversionSpecifier::UArg;
- break;
- }
- ScanfConversionSpecifier CS(conversionPosition, k);
- if (k == ScanfConversionSpecifier::ScanListArg) {
- if (ParseScanList(H, CS, I, E))
- return true;
- }
- FS.setConversionSpecifier(CS);
- if (CS.consumesDataArgument() && !FS.getSuppressAssignment()
- && !FS.usesPositionalArg())
- FS.setArgIndex(argIndex++);
-
- // FIXME: '%' and '*' doesn't make sense. Issue a warning.
- // FIXME: 'ConsumedSoFar' and '*' doesn't make sense.
-
- if (k == ScanfConversionSpecifier::InvalidSpecifier) {
- unsigned Len = I - Beg;
- if (ParseUTF8InvalidSpecifier(Beg, E, Len)) {
- CS.setEndScanList(Beg + Len);
- FS.setConversionSpecifier(CS);
- }
- // Assume the conversion takes one argument.
- return !H.HandleInvalidScanfConversionSpecifier(FS, Beg, Len);
- }
- return ScanfSpecifierResult(Start, FS);
-}
-
-ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
- const ScanfConversionSpecifier &CS = getConversionSpecifier();
-
- if (!CS.consumesDataArgument())
- return ArgType::Invalid();
-
- switch(CS.getKind()) {
- // Signed int.
- case ConversionSpecifier::dArg:
- case ConversionSpecifier::DArg:
- case ConversionSpecifier::iArg:
- switch (LM.getKind()) {
- case LengthModifier::None:
- return ArgType::PtrTo(Ctx.IntTy);
- case LengthModifier::AsChar:
- return ArgType::PtrTo(ArgType::AnyCharTy);
- case LengthModifier::AsShort:
- return ArgType::PtrTo(Ctx.ShortTy);
- case LengthModifier::AsLong:
- return ArgType::PtrTo(Ctx.LongTy);
- case LengthModifier::AsLongLong:
- case LengthModifier::AsQuad:
- return ArgType::PtrTo(Ctx.LongLongTy);
- case LengthModifier::AsInt64:
- return ArgType::PtrTo(ArgType(Ctx.LongLongTy, "__int64"));
- case LengthModifier::AsIntMax:
- return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t"));
- case LengthModifier::AsSizeT:
- return ArgType::PtrTo(ArgType(Ctx.getSignedSizeType(), "ssize_t"));
- case LengthModifier::AsPtrDiff:
- return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"));
- case LengthModifier::AsLongDouble:
- // GNU extension.
- return ArgType::PtrTo(Ctx.LongLongTy);
- case LengthModifier::AsAllocate:
- case LengthModifier::AsMAllocate:
- case LengthModifier::AsInt32:
- case LengthModifier::AsInt3264:
- case LengthModifier::AsWide:
- return ArgType::Invalid();
- }
-
- // Unsigned int.
- case ConversionSpecifier::oArg:
- case ConversionSpecifier::OArg:
- case ConversionSpecifier::uArg:
- case ConversionSpecifier::UArg:
- case ConversionSpecifier::xArg:
- case ConversionSpecifier::XArg:
- switch (LM.getKind()) {
- case LengthModifier::None:
- return ArgType::PtrTo(Ctx.UnsignedIntTy);
- case LengthModifier::AsChar:
- return ArgType::PtrTo(Ctx.UnsignedCharTy);
- case LengthModifier::AsShort:
- return ArgType::PtrTo(Ctx.UnsignedShortTy);
- case LengthModifier::AsLong:
- return ArgType::PtrTo(Ctx.UnsignedLongTy);
- case LengthModifier::AsLongLong:
- case LengthModifier::AsQuad:
- return ArgType::PtrTo(Ctx.UnsignedLongLongTy);
- case LengthModifier::AsInt64:
- return ArgType::PtrTo(ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64"));
- case LengthModifier::AsIntMax:
- return ArgType::PtrTo(ArgType(Ctx.getUIntMaxType(), "uintmax_t"));
- case LengthModifier::AsSizeT:
- return ArgType::PtrTo(ArgType(Ctx.getSizeType(), "size_t"));
- case LengthModifier::AsPtrDiff:
- return ArgType::PtrTo(
- ArgType(Ctx.getUnsignedPointerDiffType(), "unsigned ptrdiff_t"));
- case LengthModifier::AsLongDouble:
- // GNU extension.
- return ArgType::PtrTo(Ctx.UnsignedLongLongTy);
- case LengthModifier::AsAllocate:
- case LengthModifier::AsMAllocate:
- case LengthModifier::AsInt32:
- case LengthModifier::AsInt3264:
- case LengthModifier::AsWide:
- return ArgType::Invalid();
- }
-
- // Float.
- case ConversionSpecifier::aArg:
- case ConversionSpecifier::AArg:
- case ConversionSpecifier::eArg:
- case ConversionSpecifier::EArg:
- case ConversionSpecifier::fArg:
- case ConversionSpecifier::FArg:
- case ConversionSpecifier::gArg:
- case ConversionSpecifier::GArg:
- switch (LM.getKind()) {
- case LengthModifier::None:
- return ArgType::PtrTo(Ctx.FloatTy);
- case LengthModifier::AsLong:
- return ArgType::PtrTo(Ctx.DoubleTy);
- case LengthModifier::AsLongDouble:
- return ArgType::PtrTo(Ctx.LongDoubleTy);
- default:
- return ArgType::Invalid();
- }
-
- // Char, string and scanlist.
- case ConversionSpecifier::cArg:
- case ConversionSpecifier::sArg:
- case ConversionSpecifier::ScanListArg:
- switch (LM.getKind()) {
- case LengthModifier::None:
- return ArgType::PtrTo(ArgType::AnyCharTy);
- case LengthModifier::AsLong:
- case LengthModifier::AsWide:
- return ArgType::PtrTo(ArgType(Ctx.getWideCharType(), "wchar_t"));
- case LengthModifier::AsAllocate:
- case LengthModifier::AsMAllocate:
- return ArgType::PtrTo(ArgType::CStrTy);
- case LengthModifier::AsShort:
- if (Ctx.getTargetInfo().getTriple().isOSMSVCRT())
- return ArgType::PtrTo(ArgType::AnyCharTy);
- LLVM_FALLTHROUGH;
- default:
- return ArgType::Invalid();
- }
- case ConversionSpecifier::CArg:
- case ConversionSpecifier::SArg:
- // FIXME: Mac OS X specific?
- switch (LM.getKind()) {
- case LengthModifier::None:
- case LengthModifier::AsWide:
- return ArgType::PtrTo(ArgType(Ctx.getWideCharType(), "wchar_t"));
- case LengthModifier::AsAllocate:
- case LengthModifier::AsMAllocate:
- return ArgType::PtrTo(ArgType(ArgType::WCStrTy, "wchar_t *"));
- case LengthModifier::AsShort:
- if (Ctx.getTargetInfo().getTriple().isOSMSVCRT())
- return ArgType::PtrTo(ArgType::AnyCharTy);
- LLVM_FALLTHROUGH;
- default:
- return ArgType::Invalid();
- }
-
- // Pointer.
- case ConversionSpecifier::pArg:
- return ArgType::PtrTo(ArgType::CPointerTy);
-
- // Write-back.
- case ConversionSpecifier::nArg:
- switch (LM.getKind()) {
- case LengthModifier::None:
- return ArgType::PtrTo(Ctx.IntTy);
- case LengthModifier::AsChar:
- return ArgType::PtrTo(Ctx.SignedCharTy);
- case LengthModifier::AsShort:
- return ArgType::PtrTo(Ctx.ShortTy);
- case LengthModifier::AsLong:
- return ArgType::PtrTo(Ctx.LongTy);
- case LengthModifier::AsLongLong:
- case LengthModifier::AsQuad:
- return ArgType::PtrTo(Ctx.LongLongTy);
- case LengthModifier::AsInt64:
- return ArgType::PtrTo(ArgType(Ctx.LongLongTy, "__int64"));
- case LengthModifier::AsIntMax:
- return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t"));
- case LengthModifier::AsSizeT:
- return ArgType::PtrTo(ArgType(Ctx.getSignedSizeType(), "ssize_t"));
- case LengthModifier::AsPtrDiff:
- return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"));
- case LengthModifier::AsLongDouble:
- return ArgType(); // FIXME: Is this a known extension?
- case LengthModifier::AsAllocate:
- case LengthModifier::AsMAllocate:
- case LengthModifier::AsInt32:
- case LengthModifier::AsInt3264:
- case LengthModifier::AsWide:
- return ArgType::Invalid();
- }
-
- default:
- break;
- }
-
- return ArgType();
-}
-
-bool ScanfSpecifier::fixType(QualType QT, QualType RawQT,
- const LangOptions &LangOpt,
- ASTContext &Ctx) {
-
- // %n is different from other conversion specifiers; don't try to fix it.
- if (CS.getKind() == ConversionSpecifier::nArg)
- return false;
-
- if (!QT->isPointerType())
- return false;
-
- QualType PT = QT->getPointeeType();
-
- // If it's an enum, get its underlying type.
- if (const EnumType *ETy = PT->getAs<EnumType>()) {
- // Don't try to fix incomplete enums.
- if (!ETy->getDecl()->isComplete())
- return false;
- PT = ETy->getDecl()->getIntegerType();
- }
-
- const BuiltinType *BT = PT->getAs<BuiltinType>();
- if (!BT)
- return false;
-
- // Pointer to a character.
- if (PT->isAnyCharacterType()) {
- CS.setKind(ConversionSpecifier::sArg);
- if (PT->isWideCharType())
- LM.setKind(LengthModifier::AsWideChar);
- else
- LM.setKind(LengthModifier::None);
-
- // If we know the target array length, we can use it as a field width.
- if (const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(RawQT)) {
- if (CAT->getSizeModifier() == ArrayType::Normal)
- FieldWidth = OptionalAmount(OptionalAmount::Constant,
- CAT->getSize().getZExtValue() - 1,
- "", 0, false);
-
- }
- return true;
- }
-
- // Figure out the length modifier.
- switch (BT->getKind()) {
- // no modifier
- case BuiltinType::UInt:
- case BuiltinType::Int:
- case BuiltinType::Float:
- LM.setKind(LengthModifier::None);
- break;
-
- // hh
- case BuiltinType::Char_U:
- case BuiltinType::UChar:
- case BuiltinType::Char_S:
- case BuiltinType::SChar:
- LM.setKind(LengthModifier::AsChar);
- break;
-
- // h
- case BuiltinType::Short:
- case BuiltinType::UShort:
- LM.setKind(LengthModifier::AsShort);
- break;
-
- // l
- case BuiltinType::Long:
- case BuiltinType::ULong:
- case BuiltinType::Double:
- LM.setKind(LengthModifier::AsLong);
- break;
-
- // ll
- case BuiltinType::LongLong:
- case BuiltinType::ULongLong:
- LM.setKind(LengthModifier::AsLongLong);
- break;
-
- // L
- case BuiltinType::LongDouble:
- LM.setKind(LengthModifier::AsLongDouble);
- break;
-
- // Don't know.
- default:
- return false;
- }
-
- // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99.
- if (isa<TypedefType>(PT) && (LangOpt.C99 || LangOpt.CPlusPlus11))
- namedTypeToLengthModifier(PT, LM);
-
- // If fixing the length modifier was enough, we are done.
- if (hasValidLengthModifier(Ctx.getTargetInfo())) {
- const analyze_scanf::ArgType &AT = getArgType(Ctx);
- if (AT.isValid() && AT.matchesType(Ctx, QT))
- return true;
- }
-
- // Figure out the conversion specifier.
- if (PT->isRealFloatingType())
- CS.setKind(ConversionSpecifier::fArg);
- else if (PT->isSignedIntegerType())
- CS.setKind(ConversionSpecifier::dArg);
- else if (PT->isUnsignedIntegerType())
- CS.setKind(ConversionSpecifier::uArg);
- else
- llvm_unreachable("Unexpected type");
-
- return true;
-}
-
-void ScanfSpecifier::toString(raw_ostream &os) const {
- os << "%";
-
- if (usesPositionalArg())
- os << getPositionalArgIndex() << "$";
- if (SuppressAssignment)
- os << "*";
-
- FieldWidth.toString(os);
- os << LM.toString();
- os << CS.toString();
-}
-
-bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H,
- const char *I,
- const char *E,
- const LangOptions &LO,
- const TargetInfo &Target) {
-
- unsigned argIndex = 0;
-
- // Keep looking for a format specifier until we have exhausted the string.
- while (I != E) {
- const ScanfSpecifierResult &FSR = ParseScanfSpecifier(H, I, E, argIndex,
- LO, Target);
- // Did a fail-stop error of any kind occur when parsing the specifier?
- // If so, don't do any more processing.
- if (FSR.shouldStop())
- return true;
- // Did we exhaust the string or encounter an error that
- // we can recover from?
- if (!FSR.hasValue())
- continue;
- // We have a format specifier. Pass it to the callback.
- if (!H.HandleScanfSpecifier(FSR.getValue(), FSR.getStart(),
- I - FSR.getStart())) {
- return true;
- }
- }
- assert(I == E && "Format string not exhausted");
- return false;
-}
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index 03cc234dce5c..78e1b056e1d7 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/lib/Analysis/ThreadSafety.cpp
@@ -33,6 +33,7 @@
#include "clang/Analysis/Analyses/ThreadSafetyUtil.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
+#include "clang/Basic/Builtins.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
@@ -41,6 +42,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
@@ -64,13 +66,6 @@ using namespace threadSafety;
// Key method definition
ThreadSafetyHandler::~ThreadSafetyHandler() = default;
-namespace {
-
-class TILPrinter :
- public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {};
-
-} // namespace
-
/// Issue a warning about an invalid lock expression
static void warnInvalidLock(ThreadSafetyHandler &Handler,
const Expr *MutexExp, const NamedDecl *D,
@@ -86,8 +81,8 @@ static void warnInvalidLock(ThreadSafetyHandler &Handler,
namespace {
-/// A set of CapabilityInfo objects, which are compiled from the
-/// requires attributes on a function.
+/// A set of CapabilityExpr objects, which are compiled from thread safety
+/// attributes on a function.
class CapExprSet : public SmallVector<CapabilityExpr, 4> {
public:
/// Push M onto list, but discard duplicates.
@@ -142,13 +137,16 @@ public:
handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan,
SourceLocation JoinLoc, LockErrorKind LEK,
ThreadSafetyHandler &Handler) const = 0;
+ virtual void handleLock(FactSet &FSet, FactManager &FactMan,
+ const FactEntry &entry, ThreadSafetyHandler &Handler,
+ StringRef DiagKind) const = 0;
virtual void handleUnlock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp, SourceLocation UnlockLoc,
bool FullyRemove, ThreadSafetyHandler &Handler,
StringRef DiagKind) const = 0;
// Return true if LKind >= LK, where exclusive > shared
- bool isAtLeast(LockKind LK) {
+ bool isAtLeast(LockKind LK) const {
return (LKind == LK_Exclusive) || (LK == LK_Shared);
}
};
@@ -159,7 +157,7 @@ using FactID = unsigned short;
/// the analysis of a single routine.
class FactManager {
private:
- std::vector<std::unique_ptr<FactEntry>> Facts;
+ std::vector<std::unique_ptr<const FactEntry>> Facts;
public:
FactID newFact(std::unique_ptr<FactEntry> Entry) {
@@ -168,7 +166,6 @@ public:
}
const FactEntry &operator[](FactID F) const { return *Facts[F]; }
- FactEntry &operator[](FactID F) { return *Facts[F]; }
};
/// A FactSet is the set of facts that are known to be true at a
@@ -238,22 +235,23 @@ public:
});
}
- FactEntry *findLock(FactManager &FM, const CapabilityExpr &CapE) const {
+ const FactEntry *findLock(FactManager &FM, const CapabilityExpr &CapE) const {
auto I = std::find_if(begin(), end(), [&](FactID ID) {
return FM[ID].matches(CapE);
});
return I != end() ? &FM[*I] : nullptr;
}
- FactEntry *findLockUniv(FactManager &FM, const CapabilityExpr &CapE) const {
+ const FactEntry *findLockUniv(FactManager &FM,
+ const CapabilityExpr &CapE) const {
auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {
return FM[ID].matchesUniv(CapE);
});
return I != end() ? &FM[*I] : nullptr;
}
- FactEntry *findPartialMatch(FactManager &FM,
- const CapabilityExpr &CapE) const {
+ const FactEntry *findPartialMatch(FactManager &FM,
+ const CapabilityExpr &CapE) const {
auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {
return FM[ID].partiallyMatches(CapE);
});
@@ -419,7 +417,7 @@ private:
Context::Factory ContextFactory;
std::vector<VarDefinition> VarDefinitions;
std::vector<unsigned> CtxIndices;
- std::vector<std::pair<Stmt *, Context>> SavedContexts;
+ std::vector<std::pair<const Stmt *, Context>> SavedContexts;
public:
LocalVariableMap() {
@@ -460,7 +458,7 @@ public:
/// Return the next context after processing S. This function is used by
/// clients of the class to get the appropriate context when traversing the
/// CFG. It must be called for every assignment or DeclStmt.
- Context getNextContext(unsigned &CtxIndex, Stmt *S, Context C) {
+ Context getNextContext(unsigned &CtxIndex, const Stmt *S, Context C) {
if (SavedContexts[CtxIndex+1].first == S) {
CtxIndex++;
Context Result = SavedContexts[CtxIndex].second;
@@ -522,7 +520,7 @@ protected:
unsigned getContextIndex() { return SavedContexts.size()-1; }
// Save the current context for later replay
- void saveContext(Stmt *S, Context C) {
+ void saveContext(const Stmt *S, Context C) {
SavedContexts.push_back(std::make_pair(S, C));
}
@@ -592,7 +590,7 @@ CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
namespace {
/// Visitor which builds a LocalVariableMap
-class VarMapBuilder : public StmtVisitor<VarMapBuilder> {
+class VarMapBuilder : public ConstStmtVisitor<VarMapBuilder> {
public:
LocalVariableMap* VMap;
LocalVariableMap::Context Ctx;
@@ -600,16 +598,16 @@ public:
VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context C)
: VMap(VM), Ctx(C) {}
- void VisitDeclStmt(DeclStmt *S);
- void VisitBinaryOperator(BinaryOperator *BO);
+ void VisitDeclStmt(const DeclStmt *S);
+ void VisitBinaryOperator(const BinaryOperator *BO);
};
} // namespace
// Add new local variables to the variable map
-void VarMapBuilder::VisitDeclStmt(DeclStmt *S) {
+void VarMapBuilder::VisitDeclStmt(const DeclStmt *S) {
bool modifiedCtx = false;
- DeclGroupRef DGrp = S->getDeclGroup();
+ const DeclGroupRef DGrp = S->getDeclGroup();
for (const auto *D : DGrp) {
if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) {
const Expr *E = VD->getInit();
@@ -627,7 +625,7 @@ void VarMapBuilder::VisitDeclStmt(DeclStmt *S) {
}
// Update local variable definitions in variable map
-void VarMapBuilder::VisitBinaryOperator(BinaryOperator *BO) {
+void VarMapBuilder::VisitBinaryOperator(const BinaryOperator *BO) {
if (!BO->isAssignmentOp())
return;
@@ -734,7 +732,7 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph,
CtxIndices.resize(CFGraph->getNumBlockIDs());
for (const auto *CurrBlock : *SortedGraph) {
- int CurrBlockID = CurrBlock->getBlockID();
+ unsigned CurrBlockID = CurrBlock->getBlockID();
CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
VisitedBlocks.insert(CurrBlock);
@@ -750,7 +748,7 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph,
continue;
}
- int PrevBlockID = (*PI)->getBlockID();
+ unsigned PrevBlockID = (*PI)->getBlockID();
CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
if (CtxInit) {
@@ -780,7 +778,7 @@ void LocalVariableMap::traverseCFG(CFG *CFGraph,
switch (BI.getKind()) {
case CFGElement::Statement: {
CFGStmt CS = BI.castAs<CFGStmt>();
- VMapBuilder.Visit(const_cast<Stmt *>(CS.getStmt()));
+ VMapBuilder.Visit(CS.getStmt());
break;
}
default:
@@ -819,13 +817,13 @@ static void findBlockLocations(CFG *CFGraph,
// Find the source location of the last statement in the block, if the
// block is not empty.
if (const Stmt *S = CurrBlock->getTerminator()) {
- CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getLocStart();
+ CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getBeginLoc();
} else {
for (CFGBlock::const_reverse_iterator BI = CurrBlock->rbegin(),
BE = CurrBlock->rend(); BI != BE; ++BI) {
// FIXME: Handle other CFGElement kinds.
if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) {
- CurrBlockInfo->ExitLoc = CS->getStmt()->getLocStart();
+ CurrBlockInfo->ExitLoc = CS->getStmt()->getBeginLoc();
break;
}
}
@@ -837,7 +835,7 @@ static void findBlockLocations(CFG *CFGraph,
for (const auto &BI : *CurrBlock) {
// FIXME: Handle other CFGElement kinds.
if (Optional<CFGStmt> CS = BI.getAs<CFGStmt>()) {
- CurrBlockInfo->EntryLoc = CS->getStmt()->getLocStart();
+ CurrBlockInfo->EntryLoc = CS->getStmt()->getBeginLoc();
break;
}
}
@@ -873,6 +871,12 @@ public:
}
}
+ void handleLock(FactSet &FSet, FactManager &FactMan, const FactEntry &entry,
+ ThreadSafetyHandler &Handler,
+ StringRef DiagKind) const override {
+ Handler.handleDoubleLock(DiagKind, entry.toString(), entry.loc());
+ }
+
void handleUnlock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp, SourceLocation UnlockLoc,
bool FullyRemove, ThreadSafetyHandler &Handler,
@@ -887,63 +891,117 @@ public:
class ScopedLockableFactEntry : public FactEntry {
private:
- SmallVector<const til::SExpr *, 4> UnderlyingMutexes;
+ enum UnderlyingCapabilityKind {
+ UCK_Acquired, ///< Any kind of acquired capability.
+ UCK_ReleasedShared, ///< Shared capability that was released.
+ UCK_ReleasedExclusive, ///< Exclusive capability that was released.
+ };
+
+ using UnderlyingCapability =
+ llvm::PointerIntPair<const til::SExpr *, 2, UnderlyingCapabilityKind>;
+
+ SmallVector<UnderlyingCapability, 4> UnderlyingMutexes;
public:
- ScopedLockableFactEntry(const CapabilityExpr &CE, SourceLocation Loc,
- const CapExprSet &Excl, const CapExprSet &Shrd)
- : FactEntry(CE, LK_Exclusive, Loc, false) {
- for (const auto &M : Excl)
- UnderlyingMutexes.push_back(M.sexpr());
- for (const auto &M : Shrd)
- UnderlyingMutexes.push_back(M.sexpr());
+ ScopedLockableFactEntry(const CapabilityExpr &CE, SourceLocation Loc)
+ : FactEntry(CE, LK_Exclusive, Loc, false) {}
+
+ void addExclusiveLock(const CapabilityExpr &M) {
+ UnderlyingMutexes.emplace_back(M.sexpr(), UCK_Acquired);
+ }
+
+ void addSharedLock(const CapabilityExpr &M) {
+ UnderlyingMutexes.emplace_back(M.sexpr(), UCK_Acquired);
+ }
+
+ void addExclusiveUnlock(const CapabilityExpr &M) {
+ UnderlyingMutexes.emplace_back(M.sexpr(), UCK_ReleasedExclusive);
+ }
+
+ void addSharedUnlock(const CapabilityExpr &M) {
+ UnderlyingMutexes.emplace_back(M.sexpr(), UCK_ReleasedShared);
}
void
handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan,
SourceLocation JoinLoc, LockErrorKind LEK,
ThreadSafetyHandler &Handler) const override {
- for (const auto *UnderlyingMutex : UnderlyingMutexes) {
- if (FSet.findLock(FactMan, CapabilityExpr(UnderlyingMutex, false))) {
+ for (const auto &UnderlyingMutex : UnderlyingMutexes) {
+ const auto *Entry = FSet.findLock(
+ FactMan, CapabilityExpr(UnderlyingMutex.getPointer(), false));
+ if ((UnderlyingMutex.getInt() == UCK_Acquired && Entry) ||
+ (UnderlyingMutex.getInt() != UCK_Acquired && !Entry)) {
// If this scoped lock manages another mutex, and if the underlying
- // mutex is still held, then warn about the underlying mutex.
+ // mutex is still/not held, then warn about the underlying mutex.
Handler.handleMutexHeldEndOfScope(
- "mutex", sx::toString(UnderlyingMutex), loc(), JoinLoc, LEK);
+ "mutex", sx::toString(UnderlyingMutex.getPointer()), loc(), JoinLoc,
+ LEK);
}
}
}
+ void handleLock(FactSet &FSet, FactManager &FactMan, const FactEntry &entry,
+ ThreadSafetyHandler &Handler,
+ StringRef DiagKind) const override {
+ for (const auto &UnderlyingMutex : UnderlyingMutexes) {
+ CapabilityExpr UnderCp(UnderlyingMutex.getPointer(), false);
+
+ if (UnderlyingMutex.getInt() == UCK_Acquired)
+ lock(FSet, FactMan, UnderCp, entry.kind(), entry.loc(), &Handler,
+ DiagKind);
+ else
+ unlock(FSet, FactMan, UnderCp, entry.loc(), &Handler, DiagKind);
+ }
+ }
+
void handleUnlock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp, SourceLocation UnlockLoc,
bool FullyRemove, ThreadSafetyHandler &Handler,
StringRef DiagKind) const override {
assert(!Cp.negative() && "Managing object cannot be negative.");
- for (const auto *UnderlyingMutex : UnderlyingMutexes) {
- CapabilityExpr UnderCp(UnderlyingMutex, false);
- auto UnderEntry = llvm::make_unique<LockableFactEntry>(
- !UnderCp, LK_Exclusive, UnlockLoc);
-
- if (FullyRemove) {
- // We're destroying the managing object.
- // Remove the underlying mutex if it exists; but don't warn.
- if (FSet.findLock(FactMan, UnderCp)) {
- FSet.removeLock(FactMan, UnderCp);
- FSet.addLock(FactMan, std::move(UnderEntry));
- }
+ for (const auto &UnderlyingMutex : UnderlyingMutexes) {
+ CapabilityExpr UnderCp(UnderlyingMutex.getPointer(), false);
+
+ // Remove/lock the underlying mutex if it exists/is still unlocked; warn
+ // on double unlocking/locking if we're not destroying the scoped object.
+ ThreadSafetyHandler *TSHandler = FullyRemove ? nullptr : &Handler;
+ if (UnderlyingMutex.getInt() == UCK_Acquired) {
+ unlock(FSet, FactMan, UnderCp, UnlockLoc, TSHandler, DiagKind);
} else {
- // We're releasing the underlying mutex, but not destroying the
- // managing object. Warn on dual release.
- if (!FSet.findLock(FactMan, UnderCp)) {
- Handler.handleUnmatchedUnlock(DiagKind, UnderCp.toString(),
- UnlockLoc);
- }
- FSet.removeLock(FactMan, UnderCp);
- FSet.addLock(FactMan, std::move(UnderEntry));
+ LockKind kind = UnderlyingMutex.getInt() == UCK_ReleasedShared
+ ? LK_Shared
+ : LK_Exclusive;
+ lock(FSet, FactMan, UnderCp, kind, UnlockLoc, TSHandler, DiagKind);
}
}
if (FullyRemove)
FSet.removeLock(FactMan, Cp);
}
+
+private:
+ void lock(FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp,
+ LockKind kind, SourceLocation loc, ThreadSafetyHandler *Handler,
+ StringRef DiagKind) const {
+ if (!FSet.findLock(FactMan, Cp)) {
+ FSet.removeLock(FactMan, !Cp);
+ FSet.addLock(FactMan,
+ llvm::make_unique<LockableFactEntry>(Cp, kind, loc));
+ } else if (Handler) {
+ Handler->handleDoubleLock(DiagKind, Cp.toString(), loc);
+ }
+ }
+
+ void unlock(FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp,
+ SourceLocation loc, ThreadSafetyHandler *Handler,
+ StringRef DiagKind) const {
+ if (FSet.findLock(FactMan, Cp)) {
+ FSet.removeLock(FactMan, Cp);
+ FSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(
+ !Cp, LK_Exclusive, loc));
+ } else if (Handler) {
+ Handler->handleUnmatchedUnlock(DiagKind, Cp.toString(), loc);
+ }
+ }
};
/// Class which implements the core thread safety analysis routines.
@@ -976,11 +1034,11 @@ public:
StringRef DiagKind);
template <typename AttrType>
- void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, Expr *Exp,
+ void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, const Expr *Exp,
const NamedDecl *D, VarDecl *SelfDecl = nullptr);
template <class AttrType>
- void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, Expr *Exp,
+ void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, const Expr *Exp,
const NamedDecl *D,
const CFGBlock *PredBlock, const CFGBlock *CurrBlock,
Expr *BrE, bool Neg);
@@ -1232,7 +1290,7 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet,
if (!ReqAttr && !Entry->negative()) {
// look for the negative capability, and remove it from the fact set.
CapabilityExpr NegC = !*Entry;
- FactEntry *Nen = FSet.findLock(FactMan, NegC);
+ const FactEntry *Nen = FSet.findLock(FactMan, NegC);
if (Nen) {
FSet.removeLock(FactMan, NegC);
}
@@ -1251,9 +1309,9 @@ void ThreadSafetyAnalyzer::addLock(FactSet &FSet,
}
// FIXME: Don't always warn when we have support for reentrant locks.
- if (FSet.findLock(FactMan, *Entry)) {
+ if (const FactEntry *Cp = FSet.findLock(FactMan, *Entry)) {
if (!Entry->asserted())
- Handler.handleDoubleLock(DiagKind, Entry->toString(), Entry->loc());
+ Cp->handleLock(FSet, FactMan, *Entry, Handler, DiagKind);
} else {
FSet.addLock(FactMan, std::move(Entry));
}
@@ -1289,7 +1347,7 @@ void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const CapabilityExpr &Cp,
/// and push them onto Mtxs, discarding any duplicates.
template <typename AttrType>
void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
- Expr *Exp, const NamedDecl *D,
+ const Expr *Exp, const NamedDecl *D,
VarDecl *SelfDecl) {
if (Attr->args_size() == 0) {
// The mutex held is the "this" object.
@@ -1321,7 +1379,7 @@ void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
/// any duplicates.
template <class AttrType>
void ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *Attr,
- Expr *Exp, const NamedDecl *D,
+ const Expr *Exp, const NamedDecl *D,
const CFGBlock *PredBlock,
const CFGBlock *CurrBlock,
Expr *BrE, bool Neg) {
@@ -1369,14 +1427,17 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,
if (!Cond)
return nullptr;
- if (const auto *CallExp = dyn_cast<CallExpr>(Cond))
+ if (const auto *CallExp = dyn_cast<CallExpr>(Cond)) {
+ if (CallExp->getBuiltinCallee() == Builtin::BI__builtin_expect)
+ return getTrylockCallExpr(CallExp->getArg(0), C, Negate);
return CallExp;
+ }
else if (const auto *PE = dyn_cast<ParenExpr>(Cond))
return getTrylockCallExpr(PE->getSubExpr(), C, Negate);
else if (const auto *CE = dyn_cast<ImplicitCastExpr>(Cond))
return getTrylockCallExpr(CE->getSubExpr(), C, Negate);
- else if (const auto *EWC = dyn_cast<ExprWithCleanups>(Cond))
- return getTrylockCallExpr(EWC->getSubExpr(), C, Negate);
+ else if (const auto *FE = dyn_cast<FullExpr>(Cond))
+ return getTrylockCallExpr(FE->getSubExpr(), C, Negate);
else if (const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
const Expr *E = LocalVarMap.lookupExpr(DRE->getDecl(), C);
return getTrylockCallExpr(E, C, Negate);
@@ -1412,6 +1473,17 @@ const CallExpr* ThreadSafetyAnalyzer::getTrylockCallExpr(const Stmt *Cond,
if (BOP->getOpcode() == BO_LOr)
return getTrylockCallExpr(BOP->getRHS(), C, Negate);
return nullptr;
+ } else if (const auto *COP = dyn_cast<ConditionalOperator>(Cond)) {
+ bool TCond, FCond;
+ if (getStaticBooleanValue(COP->getTrueExpr(), TCond) &&
+ getStaticBooleanValue(COP->getFalseExpr(), FCond)) {
+ if (TCond && !FCond)
+ return getTrylockCallExpr(COP->getCond(), C, Negate);
+ if (!TCond && FCond) {
+ Negate = !Negate;
+ return getTrylockCallExpr(COP->getCond(), C, Negate);
+ }
+ }
}
return nullptr;
}
@@ -1426,7 +1498,8 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
Result = ExitSet;
const Stmt *Cond = PredBlock->getTerminatorCondition();
- if (!Cond)
+ // We don't acquire try-locks on ?: branches, only when its result is used.
+ if (!Cond || isa<ConditionalOperator>(PredBlock->getTerminator()))
return;
bool Negate = false;
@@ -1434,7 +1507,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
const LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;
StringRef CapDiagKind = "mutex";
- auto *Exp = const_cast<CallExpr *>(getTrylockCallExpr(Cond, LVarCtx, Negate));
+ const auto *Exp = getTrylockCallExpr(Cond, LVarCtx, Negate);
if (!Exp)
return;
@@ -1494,7 +1567,7 @@ namespace {
/// An expression may cause us to add or remove locks from the lockset, or else
/// output error messages related to missing locks.
/// FIXME: In future, we may be able to not inherit from a visitor.
-class BuildLockset : public StmtVisitor<BuildLockset> {
+class BuildLockset : public ConstStmtVisitor<BuildLockset> {
friend class ThreadSafetyAnalyzer;
ThreadSafetyAnalyzer *Analyzer;
@@ -1514,19 +1587,23 @@ class BuildLockset : public StmtVisitor<BuildLockset> {
void checkPtAccess(const Expr *Exp, AccessKind AK,
ProtectedOperationKind POK = POK_VarAccess);
- void handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD = nullptr);
+ void handleCall(const Expr *Exp, const NamedDecl *D, VarDecl *VD = nullptr);
+ void examineArguments(const FunctionDecl *FD,
+ CallExpr::const_arg_iterator ArgBegin,
+ CallExpr::const_arg_iterator ArgEnd,
+ bool SkipFirstParam = false);
public:
BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info)
- : StmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet),
+ : ConstStmtVisitor<BuildLockset>(), Analyzer(Anlzr), FSet(Info.EntrySet),
LVarCtx(Info.EntryContext), CtxIndex(Info.EntryIndex) {}
- void VisitUnaryOperator(UnaryOperator *UO);
- void VisitBinaryOperator(BinaryOperator *BO);
- void VisitCastExpr(CastExpr *CE);
- void VisitCallExpr(CallExpr *Exp);
- void VisitCXXConstructExpr(CXXConstructExpr *Exp);
- void VisitDeclStmt(DeclStmt *S);
+ void VisitUnaryOperator(const UnaryOperator *UO);
+ void VisitBinaryOperator(const BinaryOperator *BO);
+ void VisitCastExpr(const CastExpr *CE);
+ void VisitCallExpr(const CallExpr *Exp);
+ void VisitCXXConstructExpr(const CXXConstructExpr *Exp);
+ void VisitDeclStmt(const DeclStmt *S);
};
} // namespace
@@ -1549,7 +1626,7 @@ void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
if (Cp.negative()) {
// Negative capabilities act like locks excluded
- FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp);
+ const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, !Cp);
if (LDat) {
Analyzer->Handler.handleFunExcludesLock(
DiagKind, D->getNameAsString(), (!Cp).toString(), Loc);
@@ -1570,7 +1647,7 @@ void BuildLockset::warnIfMutexNotHeld(const NamedDecl *D, const Expr *Exp,
return;
}
- FactEntry* LDat = FSet.findLockUniv(Analyzer->FactMan, Cp);
+ const FactEntry *LDat = FSet.findLockUniv(Analyzer->FactMan, Cp);
bool NoError = true;
if (!LDat) {
// No exact match found. Look for a partial match.
@@ -1606,7 +1683,7 @@ void BuildLockset::warnIfMutexHeld(const NamedDecl *D, const Expr *Exp,
return;
}
- FactEntry* LDat = FSet.findLock(Analyzer->FactMan, Cp);
+ const FactEntry *LDat = FSet.findLock(Analyzer->FactMan, Cp);
if (LDat) {
Analyzer->Handler.handleFunExcludesLock(
DiagKind, D->getNameAsString(), Cp.toString(), Exp->getExprLoc());
@@ -1630,6 +1707,9 @@ void BuildLockset::checkAccess(const Expr *Exp, AccessKind AK,
const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()->getCanonicalDecl());
if (VD && VD->isLocalVarDecl() && VD->getType()->isReferenceType()) {
if (const auto *E = VD->getInit()) {
+ // Guard against self-initialization. e.g., int &i = i;
+ if (E == Exp)
+ break;
Exp = E;
continue;
}
@@ -1718,7 +1798,8 @@ void BuildLockset::checkPtAccess(const Expr *Exp, AccessKind AK,
/// and check that the appropriate locks are held. Non-const method calls with
/// the same signature as const method calls can be also treated as reads.
///
-void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
+void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
+ VarDecl *VD) {
SourceLocation Loc = Exp->getExprLoc();
CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;
CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;
@@ -1858,25 +1939,32 @@ void BuildLockset::handleCall(Expr *Exp, const NamedDecl *D, VarDecl *VD) {
if (isScopedVar) {
// Add the managing object as a dummy mutex, mapped to the underlying mutex.
SourceLocation MLoc = VD->getLocation();
- DeclRefExpr DRE(VD, false, VD->getType(), VK_LValue, VD->getLocation());
+ DeclRefExpr DRE(VD->getASTContext(), VD, false, VD->getType(), VK_LValue,
+ VD->getLocation());
// FIXME: does this store a pointer to DRE?
CapabilityExpr Scp = Analyzer->SxBuilder.translateAttrExpr(&DRE, nullptr);
- std::copy(ScopedExclusiveReqs.begin(), ScopedExclusiveReqs.end(),
- std::back_inserter(ExclusiveLocksToAdd));
- std::copy(ScopedSharedReqs.begin(), ScopedSharedReqs.end(),
- std::back_inserter(SharedLocksToAdd));
- Analyzer->addLock(FSet,
- llvm::make_unique<ScopedLockableFactEntry>(
- Scp, MLoc, ExclusiveLocksToAdd, SharedLocksToAdd),
- CapDiagKind);
+ auto ScopedEntry = llvm::make_unique<ScopedLockableFactEntry>(Scp, MLoc);
+ for (const auto &M : ExclusiveLocksToAdd)
+ ScopedEntry->addExclusiveLock(M);
+ for (const auto &M : ScopedExclusiveReqs)
+ ScopedEntry->addExclusiveLock(M);
+ for (const auto &M : SharedLocksToAdd)
+ ScopedEntry->addSharedLock(M);
+ for (const auto &M : ScopedSharedReqs)
+ ScopedEntry->addSharedLock(M);
+ for (const auto &M : ExclusiveLocksToRemove)
+ ScopedEntry->addExclusiveUnlock(M);
+ for (const auto &M : SharedLocksToRemove)
+ ScopedEntry->addSharedUnlock(M);
+ Analyzer->addLock(FSet, std::move(ScopedEntry), CapDiagKind);
}
}
/// For unary operations which read and write a variable, we need to
/// check whether we hold any required mutexes. Reads are checked in
/// VisitCastExpr.
-void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) {
+void BuildLockset::VisitUnaryOperator(const UnaryOperator *UO) {
switch (UO->getOpcode()) {
case UO_PostDec:
case UO_PostInc:
@@ -1892,7 +1980,7 @@ void BuildLockset::VisitUnaryOperator(UnaryOperator *UO) {
/// For binary operations which assign to a variable (writes), we need to check
/// whether we hold any required mutexes.
/// FIXME: Deal with non-primitive types.
-void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
+void BuildLockset::VisitBinaryOperator(const BinaryOperator *BO) {
if (!BO->isAssignmentOp())
return;
@@ -1905,16 +1993,43 @@ void BuildLockset::VisitBinaryOperator(BinaryOperator *BO) {
/// Whenever we do an LValue to Rvalue cast, we are reading a variable and
/// need to ensure we hold any required mutexes.
/// FIXME: Deal with non-primitive types.
-void BuildLockset::VisitCastExpr(CastExpr *CE) {
+void BuildLockset::VisitCastExpr(const CastExpr *CE) {
if (CE->getCastKind() != CK_LValueToRValue)
return;
checkAccess(CE->getSubExpr(), AK_Read);
}
-void BuildLockset::VisitCallExpr(CallExpr *Exp) {
- bool ExamineArgs = true;
- bool OperatorFun = false;
+void BuildLockset::examineArguments(const FunctionDecl *FD,
+ CallExpr::const_arg_iterator ArgBegin,
+ CallExpr::const_arg_iterator ArgEnd,
+ bool SkipFirstParam) {
+ // Currently we can't do anything if we don't know the function declaration.
+ if (!FD)
+ return;
+
+ // NO_THREAD_SAFETY_ANALYSIS does double duty here. Normally it
+ // only turns off checking within the body of a function, but we also
+ // use it to turn off checking in arguments to the function. This
+ // could result in some false negatives, but the alternative is to
+ // create yet another attribute.
+ if (FD->hasAttr<NoThreadSafetyAnalysisAttr>())
+ return;
+
+ const ArrayRef<ParmVarDecl *> Params = FD->parameters();
+ auto Param = Params.begin();
+ if (SkipFirstParam)
+ ++Param;
+ // There can be default arguments, so we stop when one iterator is at end().
+ for (auto Arg = ArgBegin; Param != Params.end() && Arg != ArgEnd;
+ ++Param, ++Arg) {
+ QualType Qt = (*Param)->getType();
+ if (Qt->isReferenceType())
+ checkAccess(*Arg, AK_Read, POK_PassByRef);
+ }
+}
+
+void BuildLockset::VisitCallExpr(const CallExpr *Exp) {
if (const auto *CE = dyn_cast<CXXMemberCallExpr>(Exp)) {
const auto *ME = dyn_cast<MemberExpr>(CE->getCallee());
// ME can be null when calling a method pointer
@@ -1933,13 +2048,12 @@ void BuildLockset::VisitCallExpr(CallExpr *Exp) {
checkAccess(CE->getImplicitObjectArgument(), AK_Read);
}
}
- } else if (const auto *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
- OperatorFun = true;
+ examineArguments(CE->getDirectCallee(), CE->arg_begin(), CE->arg_end());
+ } else if (const auto *OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {
auto OEop = OE->getOperator();
switch (OEop) {
case OO_Equal: {
- ExamineArgs = false;
const Expr *Target = OE->getArg(0);
const Expr *Source = OE->getArg(1);
checkAccess(Target, AK_Written);
@@ -1948,60 +2062,27 @@ void BuildLockset::VisitCallExpr(CallExpr *Exp) {
}
case OO_Star:
case OO_Arrow:
- case OO_Subscript: {
- const Expr *Obj = OE->getArg(0);
- checkAccess(Obj, AK_Read);
+ case OO_Subscript:
if (!(OEop == OO_Star && OE->getNumArgs() > 1)) {
// Grrr. operator* can be multiplication...
- checkPtAccess(Obj, AK_Read);
+ checkPtAccess(OE->getArg(0), AK_Read);
}
- break;
- }
+ LLVM_FALLTHROUGH;
default: {
// TODO: get rid of this, and rely on pass-by-ref instead.
const Expr *Obj = OE->getArg(0);
checkAccess(Obj, AK_Read);
+ // Check the remaining arguments. For method operators, the first
+ // argument is the implicit self argument, and doesn't appear in the
+ // FunctionDecl, but for non-methods it does.
+ const FunctionDecl *FD = OE->getDirectCallee();
+ examineArguments(FD, std::next(OE->arg_begin()), OE->arg_end(),
+ /*SkipFirstParam*/ !isa<CXXMethodDecl>(FD));
break;
}
}
- }
-
- if (ExamineArgs) {
- if (FunctionDecl *FD = Exp->getDirectCallee()) {
- // NO_THREAD_SAFETY_ANALYSIS does double duty here. Normally it
- // only turns off checking within the body of a function, but we also
- // use it to turn off checking in arguments to the function. This
- // could result in some false negatives, but the alternative is to
- // create yet another attribute.
- if (!FD->hasAttr<NoThreadSafetyAnalysisAttr>()) {
- unsigned Fn = FD->getNumParams();
- unsigned Cn = Exp->getNumArgs();
- unsigned Skip = 0;
-
- unsigned i = 0;
- if (OperatorFun) {
- if (isa<CXXMethodDecl>(FD)) {
- // First arg in operator call is implicit self argument,
- // and doesn't appear in the FunctionDecl.
- Skip = 1;
- Cn--;
- } else {
- // Ignore the first argument of operators; it's been checked above.
- i = 1;
- }
- }
- // Ignore default arguments
- unsigned n = (Fn < Cn) ? Fn : Cn;
-
- for (; i < n; ++i) {
- ParmVarDecl* Pvd = FD->getParamDecl(i);
- Expr* Arg = Exp->getArg(i+Skip);
- QualType Qt = Pvd->getType();
- if (Qt->isReferenceType())
- checkAccess(Arg, AK_Read, POK_PassByRef);
- }
- }
- }
+ } else {
+ examineArguments(Exp->getDirectCallee(), Exp->arg_begin(), Exp->arg_end());
}
auto *D = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());
@@ -2010,13 +2091,14 @@ void BuildLockset::VisitCallExpr(CallExpr *Exp) {
handleCall(Exp, D);
}
-void BuildLockset::VisitCXXConstructExpr(CXXConstructExpr *Exp) {
+void BuildLockset::VisitCXXConstructExpr(const CXXConstructExpr *Exp) {
const CXXConstructorDecl *D = Exp->getConstructor();
if (D && D->isCopyConstructor()) {
const Expr* Source = Exp->getArg(0);
checkAccess(Source, AK_Read);
+ } else {
+ examineArguments(D, Exp->arg_begin(), Exp->arg_end());
}
- // FIXME -- only handles constructors in DeclStmt below.
}
static CXXConstructorDecl *
@@ -2046,7 +2128,7 @@ static Expr *buildFakeCtorCall(CXXConstructorDecl *CD, ArrayRef<Expr *> Args,
SourceRange(Loc, Loc));
}
-void BuildLockset::VisitDeclStmt(DeclStmt *S) {
+void BuildLockset::VisitDeclStmt(const DeclStmt *S) {
// adjust the context
LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);
@@ -2080,7 +2162,7 @@ void BuildLockset::VisitDeclStmt(DeclStmt *S) {
CXXConstructorDecl *CtorD = findConstructorForByValueReturn(RD);
if (!CtorD || !CtorD->hasAttrs())
continue;
- handleCall(buildFakeCtorCall(CtorD, {E}, E->getLocStart()), CtorD, VD);
+ handleCall(buildFakeCtorCall(CtorD, {E}, E->getBeginLoc()), CtorD, VD);
}
}
}
@@ -2242,8 +2324,8 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
// We must ignore such methods.
if (A->args_size() == 0)
return;
- // FIXME -- deal with exclusive vs. shared unlock functions?
- getMutexIDs(ExclusiveLocksToAdd, A, nullptr, D);
+ getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
+ nullptr, D);
getMutexIDs(LocksReleased, A, nullptr, D);
CapDiagKind = ClassifyDiagnostic(A);
} else if (const auto *A = dyn_cast<AcquireCapabilityAttr>(Attr)) {
@@ -2279,7 +2361,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
}
for (const auto *CurrBlock : *SortedGraph) {
- int CurrBlockID = CurrBlock->getBlockID();
+ unsigned CurrBlockID = CurrBlock->getBlockID();
CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
// Use the default initial lockset in case there are no predecessors.
@@ -2306,7 +2388,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
if (*PI == nullptr || !VisitedBlocks.alreadySet(*PI))
continue;
- int PrevBlockID = (*PI)->getBlockID();
+ unsigned PrevBlockID = (*PI)->getBlockID();
CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
// Ignore edges from blocks that can't return.
@@ -2347,7 +2429,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
// Process continue and break blocks. Assume that the lockset for the
// resulting block is unaffected by any discrepancies in them.
for (const auto *PrevBlock : SpecialBlocks) {
- int PrevBlockID = PrevBlock->getBlockID();
+ unsigned PrevBlockID = PrevBlock->getBlockID();
CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
if (!LocksetInitialized) {
@@ -2382,21 +2464,21 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
switch (BI.getKind()) {
case CFGElement::Statement: {
CFGStmt CS = BI.castAs<CFGStmt>();
- LocksetBuilder.Visit(const_cast<Stmt *>(CS.getStmt()));
+ LocksetBuilder.Visit(CS.getStmt());
break;
}
// Ignore BaseDtor, MemberDtor, and TemporaryDtor for now.
case CFGElement::AutomaticObjectDtor: {
CFGAutomaticObjDtor AD = BI.castAs<CFGAutomaticObjDtor>();
- auto *DD = const_cast<CXXDestructorDecl *>(
- AD.getDestructorDecl(AC.getASTContext()));
+ const auto *DD = AD.getDestructorDecl(AC.getASTContext());
if (!DD->hasAttrs())
break;
// Create a dummy expression,
auto *VD = const_cast<VarDecl *>(AD.getVarDecl());
- DeclRefExpr DRE(VD, false, VD->getType().getNonReferenceType(),
- VK_LValue, AD.getTriggerStmt()->getLocEnd());
+ DeclRefExpr DRE(VD->getASTContext(), VD, false,
+ VD->getType().getNonReferenceType(), VK_LValue,
+ AD.getTriggerStmt()->getEndLoc());
LocksetBuilder.handleCall(&DRE, DD);
break;
}
diff --git a/lib/Analysis/ThreadSafetyCommon.cpp b/lib/Analysis/ThreadSafetyCommon.cpp
index fced17ff9197..14d1d9c7a8f7 100644
--- a/lib/Analysis/ThreadSafetyCommon.cpp
+++ b/lib/Analysis/ThreadSafetyCommon.cpp
@@ -128,7 +128,8 @@ CapabilityExpr SExprBuilder::translateAttrExpr(const Expr *AttrExp,
// Hack to handle constructors, where self cannot be recovered from
// the expression.
if (SelfDecl && !Ctx.SelfArg) {
- DeclRefExpr SelfDRE(SelfDecl, false, SelfDecl->getType(), VK_LValue,
+ DeclRefExpr SelfDRE(SelfDecl->getASTContext(), SelfDecl, false,
+ SelfDecl->getType(), VK_LValue,
SelfDecl->getLocation());
Ctx.SelfArg = &SelfDRE;
@@ -211,6 +212,8 @@ til::SExpr *SExprBuilder::translate(const Stmt *S, CallingContext *Ctx) {
return translateCXXThisExpr(cast<CXXThisExpr>(S), Ctx);
case Stmt::MemberExprClass:
return translateMemberExpr(cast<MemberExpr>(S), Ctx);
+ case Stmt::ObjCIvarRefExprClass:
+ return translateObjCIVarRefExpr(cast<ObjCIvarRefExpr>(S), Ctx);
case Stmt::CallExprClass:
return translateCallExpr(cast<CallExpr>(S), Ctx);
case Stmt::CXXMemberCallExprClass:
@@ -233,6 +236,8 @@ til::SExpr *SExprBuilder::translate(const Stmt *S, CallingContext *Ctx) {
cast<BinaryConditionalOperator>(S), Ctx);
// We treat these as no-ops
+ case Stmt::ConstantExprClass:
+ return translate(cast<ConstantExpr>(S)->getSubExpr(), Ctx);
case Stmt::ParenExprClass:
return translate(cast<ParenExpr>(S)->getSubExpr(), Ctx);
case Stmt::ExprWithCleanupsClass:
@@ -311,9 +316,9 @@ static const ValueDecl *getValueDeclFromSExpr(const til::SExpr *E) {
return nullptr;
}
-static bool hasCppPointerType(const til::SExpr *E) {
+static bool hasAnyPointerType(const til::SExpr *E) {
auto *VD = getValueDeclFromSExpr(E);
- if (VD && VD->getType()->isPointerType())
+ if (VD && VD->getType()->isAnyPointerType())
return true;
if (const auto *C = dyn_cast<til::Cast>(E))
return C->castOpcode() == til::CAST_objToPtr;
@@ -344,7 +349,20 @@ til::SExpr *SExprBuilder::translateMemberExpr(const MemberExpr *ME,
D = getFirstVirtualDecl(VD);
til::Project *P = new (Arena) til::Project(E, D);
- if (hasCppPointerType(BE))
+ if (hasAnyPointerType(BE))
+ P->setArrow(true);
+ return P;
+}
+
+til::SExpr *SExprBuilder::translateObjCIVarRefExpr(const ObjCIvarRefExpr *IVRE,
+ CallingContext *Ctx) {
+ til::SExpr *BE = translate(IVRE->getBase(), Ctx);
+ til::SExpr *E = new (Arena) til::SApply(BE);
+
+ const auto *D = cast<ObjCIvarDecl>(IVRE->getDecl()->getCanonicalDecl());
+
+ til::Project *P = new (Arena) til::Project(E, D);
+ if (hasAnyPointerType(BE))
P->setArrow(true);
return P;
}
@@ -354,15 +372,17 @@ til::SExpr *SExprBuilder::translateCallExpr(const CallExpr *CE,
const Expr *SelfE) {
if (CapabilityExprMode) {
// Handle LOCK_RETURNED
- const FunctionDecl *FD = CE->getDirectCallee()->getMostRecentDecl();
- if (LockReturnedAttr* At = FD->getAttr<LockReturnedAttr>()) {
- CallingContext LRCallCtx(Ctx);
- LRCallCtx.AttrDecl = CE->getDirectCallee();
- LRCallCtx.SelfArg = SelfE;
- LRCallCtx.NumArgs = CE->getNumArgs();
- LRCallCtx.FunArgs = CE->getArgs();
- return const_cast<til::SExpr *>(
- translateAttrExpr(At->getArg(), &LRCallCtx).sexpr());
+ if (const FunctionDecl *FD = CE->getDirectCallee()) {
+ FD = FD->getMostRecentDecl();
+ if (LockReturnedAttr *At = FD->getAttr<LockReturnedAttr>()) {
+ CallingContext LRCallCtx(Ctx);
+ LRCallCtx.AttrDecl = CE->getDirectCallee();
+ LRCallCtx.SelfArg = SelfE;
+ LRCallCtx.NumArgs = CE->getNumArgs();
+ LRCallCtx.FunArgs = CE->getArgs();
+ return const_cast<til::SExpr *>(
+ translateAttrExpr(At->getArg(), &LRCallCtx).sexpr());
+ }
}
}
@@ -927,6 +947,16 @@ void SExprBuilder::exitCFG(const CFGBlock *Last) {
}
/*
+namespace {
+
+class TILPrinter :
+ public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {};
+
+} // namespace
+
+namespace clang {
+namespace threadSafety {
+
void printSCFG(CFGWalker &Walker) {
llvm::BumpPtrAllocator Bpa;
til::MemRegionRef Arena(&Bpa);
@@ -934,4 +964,7 @@ void printSCFG(CFGWalker &Walker) {
til::SCFG *Scfg = SxBuilder.buildCFG(Walker);
TILPrinter::print(Scfg, llvm::errs());
}
+
+} // namespace threadSafety
+} // namespace clang
*/
diff --git a/lib/Analysis/ThreadSafetyTIL.cpp b/lib/Analysis/ThreadSafetyTIL.cpp
index 798bbfb29d7b..11f7afbd229c 100644
--- a/lib/Analysis/ThreadSafetyTIL.cpp
+++ b/lib/Analysis/ThreadSafetyTIL.cpp
@@ -150,7 +150,7 @@ void til::simplifyIncompleteArg(til::Phi *Ph) {
}
// Renumbers the arguments and instructions to have unique, sequential IDs.
-int BasicBlock::renumberInstrs(int ID) {
+unsigned BasicBlock::renumberInstrs(unsigned ID) {
for (auto *Arg : Args)
Arg->setID(this, ID++);
for (auto *Instr : Instrs)
@@ -163,7 +163,8 @@ int BasicBlock::renumberInstrs(int ID) {
// Each block will be written into the Blocks array in order, and its BlockID
// will be set to the index in the array. Sorting should start from the entry
// block, and ID should be the total number of blocks.
-int BasicBlock::topologicalSort(SimpleArray<BasicBlock *> &Blocks, int ID) {
+unsigned BasicBlock::topologicalSort(SimpleArray<BasicBlock *> &Blocks,
+ unsigned ID) {
if (Visited) return ID;
Visited = true;
for (auto *Block : successors())
@@ -186,7 +187,8 @@ int BasicBlock::topologicalSort(SimpleArray<BasicBlock *> &Blocks, int ID) {
// critical edges, and (3) the entry block is reachable from the exit block
// and no blocks are accessible via traversal of back-edges from the exit that
// weren't accessible via forward edges from the entry.
-int BasicBlock::topologicalFinalSort(SimpleArray<BasicBlock*>& Blocks, int ID) {
+unsigned BasicBlock::topologicalFinalSort(SimpleArray<BasicBlock *> &Blocks,
+ unsigned ID) {
// Visited is assumed to have been set by the topologicalSort. This pass
// assumes !Visited means that we've visited this node before.
if (!Visited) return ID;
@@ -257,7 +259,7 @@ void BasicBlock::computePostDominator() {
// Renumber instructions in all blocks
void SCFG::renumberInstrs() {
- int InstrID = 0;
+ unsigned InstrID = 0;
for (auto *Block : Blocks)
InstrID = Block->renumberInstrs(InstrID);
}
@@ -288,11 +290,11 @@ static inline void computeNodeID(BasicBlock *B,
// 3) Topologically sorting the blocks into the "Blocks" array.
void SCFG::computeNormalForm() {
// Topologically sort the blocks starting from the entry block.
- int NumUnreachableBlocks = Entry->topologicalSort(Blocks, Blocks.size());
+ unsigned NumUnreachableBlocks = Entry->topologicalSort(Blocks, Blocks.size());
if (NumUnreachableBlocks > 0) {
// If there were unreachable blocks shift everything down, and delete them.
- for (size_t I = NumUnreachableBlocks, E = Blocks.size(); I < E; ++I) {
- size_t NI = I - NumUnreachableBlocks;
+ for (unsigned I = NumUnreachableBlocks, E = Blocks.size(); I < E; ++I) {
+ unsigned NI = I - NumUnreachableBlocks;
Blocks[NI] = Blocks[I];
Blocks[NI]->BlockID = NI;
// FIXME: clean up predecessor pointers to unreachable blocks?
@@ -305,7 +307,7 @@ void SCFG::computeNormalForm() {
Block->computeDominator();
// Once dominators have been computed, the final sort may be performed.
- int NumBlocks = Exit->topologicalFinalSort(Blocks, 0);
+ unsigned NumBlocks = Exit->topologicalFinalSort(Blocks, 0);
assert(static_cast<size_t>(NumBlocks) == Blocks.size());
(void) NumBlocks;