aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Analysis
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-08-20 20:50:49 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-08-20 20:50:49 +0000
commit2298981669bf3bd63335a4be179bc0f96823a8f4 (patch)
tree1cbe2eb27f030d2d70b80ee5ca3c86bee7326a9f /lib/Analysis
parent9a83721404652cea39e9f02ae3e3b5c964602a5c (diff)
downloadsrc-2298981669bf3bd63335a4be179bc0f96823a8f4.tar.gz
src-2298981669bf3bd63335a4be179bc0f96823a8f4.zip
Vendor import of stripped clang trunk r366426 (just before thevendor/clang/clang-trunk-r366426
Notes
Notes: svn path=/vendor/clang/dist/; revision=351280 svn path=/vendor/clang/clang-trunk-r366426/; revision=351281; tag=vendor/clang/clang-trunk-r366426
Diffstat (limited to 'lib/Analysis')
-rw-r--r--lib/Analysis/AnalysisDeclContext.cpp104
-rw-r--r--lib/Analysis/BodyFarm.cpp40
-rw-r--r--lib/Analysis/CFG.cpp288
-rw-r--r--lib/Analysis/CFGReachabilityAnalysis.cpp7
-rw-r--r--lib/Analysis/CFGStmtMap.cpp9
-rw-r--r--lib/Analysis/CallGraph.cpp7
-rw-r--r--lib/Analysis/CloneDetection.cpp13
-rw-r--r--lib/Analysis/CocoaConventions.cpp7
-rw-r--r--lib/Analysis/CodeInjector.cpp7
-rw-r--r--lib/Analysis/ConstructionContext.cpp7
-rw-r--r--lib/Analysis/Consumed.cpp9
-rw-r--r--lib/Analysis/Dominators.cpp17
-rw-r--r--lib/Analysis/ExprMutationAnalyzer.cpp53
-rw-r--r--lib/Analysis/LiveVariables.cpp9
-rw-r--r--lib/Analysis/ObjCNoReturn.cpp7
-rw-r--r--lib/Analysis/PostOrderCFGView.cpp7
-rw-r--r--lib/Analysis/ProgramPoint.cpp225
-rw-r--r--lib/Analysis/ReachableCode.cpp31
-rw-r--r--lib/Analysis/RetainSummaryManager.cpp1287
-rw-r--r--lib/Analysis/ThreadSafety.cpp28
-rw-r--r--lib/Analysis/ThreadSafetyCommon.cpp28
-rw-r--r--lib/Analysis/ThreadSafetyLogical.cpp7
-rw-r--r--lib/Analysis/ThreadSafetyTIL.cpp7
-rw-r--r--lib/Analysis/UninitializedValues.cpp26
-rw-r--r--lib/Analysis/plugins/CheckerDependencyHandling/CheckerDependencyHandling.cpp28
-rw-r--r--lib/Analysis/plugins/CheckerDependencyHandling/CheckerDependencyHandlingAnalyzerPlugin.exports2
-rw-r--r--lib/Analysis/plugins/CheckerOptionHandling/CheckerOptionHandling.cpp44
-rw-r--r--lib/Analysis/plugins/CheckerOptionHandling/CheckerOptionHandlingAnalyzerPlugin.exports2
-rw-r--r--lib/Analysis/plugins/SampleAnalyzer/MainCallChecker.cpp54
-rw-r--r--lib/Analysis/plugins/SampleAnalyzer/SampleAnalyzerPlugin.exports2
30 files changed, 2043 insertions, 319 deletions
diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp
index 30160bc239ae..b6a429ff49eb 100644
--- a/lib/Analysis/AnalysisDeclContext.cpp
+++ b/lib/Analysis/AnalysisDeclContext.cpp
@@ -1,9 +1,8 @@
//===- AnalysisDeclContext.cpp - Analysis context for Path Sens analysis --===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -31,6 +30,7 @@
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Analysis/Support/BumpVector.h"
+#include "clang/Basic/JsonSupport.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
@@ -71,7 +71,7 @@ AnalysisDeclContextManager::AnalysisDeclContextManager(
bool addLoopExit, bool addScopes, bool synthesizeBodies,
bool addStaticInitBranch, bool addCXXNewAllocator,
bool addRichCXXConstructors, bool markElidedCXXConstructors,
- CodeInjector *injector)
+ bool addVirtualBaseBranches, CodeInjector *injector)
: Injector(injector), FunctionBodyFarm(ASTCtx, injector),
SynthesizeBodies(synthesizeBodies) {
cfgBuildOptions.PruneTriviallyFalseEdges = !useUnoptimizedCFG;
@@ -85,6 +85,7 @@ AnalysisDeclContextManager::AnalysisDeclContextManager(
cfgBuildOptions.AddCXXNewAllocator = addCXXNewAllocator;
cfgBuildOptions.AddRichCXXConstructors = addRichCXXConstructors;
cfgBuildOptions.MarkElidedCXXConstructors = markElidedCXXConstructors;
+ cfgBuildOptions.AddVirtualBaseBranches = addVirtualBaseBranches;
}
void AnalysisDeclContextManager::clear() { Contexts.clear(); }
@@ -463,17 +464,17 @@ bool LocationContext::isParentOf(const LocationContext *LC) const {
return false;
}
-static void printLocation(raw_ostream &OS, const SourceManager &SM,
- SourceLocation SLoc) {
- if (SLoc.isFileID() && SM.isInMainFile(SLoc))
- OS << "line " << SM.getExpansionLineNumber(SLoc);
+static void printLocation(raw_ostream &Out, const SourceManager &SM,
+ SourceLocation Loc) {
+ if (Loc.isFileID() && SM.isInMainFile(Loc))
+ Out << SM.getExpansionLineNumber(Loc);
else
- SLoc.print(OS, SM);
+ Loc.print(Out, SM);
}
-void LocationContext::dumpStack(
- raw_ostream &OS, StringRef Indent, const char *NL, const char *Sep,
- std::function<void(const LocationContext *)> printMoreInfoPerContext) const {
+void LocationContext::dumpStack(raw_ostream &Out, const char *NL,
+ std::function<void(const LocationContext *)>
+ printMoreInfoPerContext) const {
ASTContext &Ctx = getAnalysisDeclContext()->getASTContext();
PrintingPolicy PP(Ctx.getLangOpts());
PP.TerseOutput = 1;
@@ -485,38 +486,91 @@ void LocationContext::dumpStack(
for (const LocationContext *LCtx = this; LCtx; LCtx = LCtx->getParent()) {
switch (LCtx->getKind()) {
case StackFrame:
- OS << Indent << '#' << Frame << ' ';
+ Out << "\t#" << Frame << ' ';
++Frame;
if (const auto *D = dyn_cast<NamedDecl>(LCtx->getDecl()))
- OS << "Calling " << D->getQualifiedNameAsString();
+ Out << "Calling " << D->getQualifiedNameAsString();
else
- OS << "Calling anonymous code";
+ Out << "Calling anonymous code";
if (const Stmt *S = cast<StackFrameContext>(LCtx)->getCallSite()) {
- OS << " at ";
- printLocation(OS, SM, S->getBeginLoc());
+ Out << " at line ";
+ printLocation(Out, SM, S->getBeginLoc());
}
break;
case Scope:
- OS << "Entering scope";
+ Out << "Entering scope";
break;
case Block:
- OS << "Invoking block";
+ Out << "Invoking block";
if (const Decl *D = cast<BlockInvocationContext>(LCtx)->getDecl()) {
- OS << " defined at ";
- printLocation(OS, SM, D->getBeginLoc());
+ Out << " defined at line ";
+ printLocation(Out, SM, D->getBeginLoc());
}
break;
}
- OS << NL;
+ Out << NL;
printMoreInfoPerContext(LCtx);
}
}
-LLVM_DUMP_METHOD void LocationContext::dumpStack() const {
- dumpStack(llvm::errs());
+void LocationContext::printJson(raw_ostream &Out, const char *NL,
+ unsigned int Space, bool IsDot,
+ std::function<void(const LocationContext *)>
+ printMoreInfoPerContext) const {
+ ASTContext &Ctx = getAnalysisDeclContext()->getASTContext();
+ PrintingPolicy PP(Ctx.getLangOpts());
+ PP.TerseOutput = 1;
+
+ const SourceManager &SM =
+ getAnalysisDeclContext()->getASTContext().getSourceManager();
+
+ unsigned Frame = 0;
+ for (const LocationContext *LCtx = this; LCtx; LCtx = LCtx->getParent()) {
+ Indent(Out, Space, IsDot)
+ << "{ \"lctx_id\": " << LCtx->getID() << ", \"location_context\": \"";
+ switch (LCtx->getKind()) {
+ case StackFrame:
+ Out << '#' << Frame << " Call\", \"calling\": \"";
+ ++Frame;
+ if (const auto *D = dyn_cast<NamedDecl>(LCtx->getDecl()))
+ Out << D->getQualifiedNameAsString();
+ else
+ Out << "anonymous code";
+
+ Out << "\", \"location\": ";
+ if (const Stmt *S = cast<StackFrameContext>(LCtx)->getCallSite()) {
+ printSourceLocationAsJson(Out, S->getBeginLoc(), SM);
+ } else {
+ Out << "null";
+ }
+
+ Out << ", \"items\": ";
+ break;
+ case Scope:
+ Out << "Entering scope\" ";
+ break;
+ case Block:
+ Out << "Invoking block\" ";
+ if (const Decl *D = cast<BlockInvocationContext>(LCtx)->getDecl()) {
+ Out << ", \"location\": ";
+ printSourceLocationAsJson(Out, D->getBeginLoc(), SM);
+ Out << ' ';
+ }
+ break;
+ }
+
+ printMoreInfoPerContext(LCtx);
+
+ Out << '}';
+ if (LCtx->getParent())
+ Out << ',';
+ Out << NL;
+ }
}
+LLVM_DUMP_METHOD void LocationContext::dump() const { printJson(llvm::errs()); }
+
//===----------------------------------------------------------------------===//
// Lazily generated map to query the external variables referenced by a Block.
//===----------------------------------------------------------------------===//
diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp
index 35f046406763..576f86516017 100644
--- a/lib/Analysis/BodyFarm.cpp
+++ b/lib/Analysis/BodyFarm.cpp
@@ -1,9 +1,8 @@
//== BodyFarm.cpp - Factory for conjuring up fake bodies ----------*- C++ -*-//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -221,7 +220,7 @@ MemberExpr *ASTMaker::makeMemberExpression(Expr *base, ValueDecl *MemberDecl,
SourceLocation(), MemberDecl, FoundDecl,
DeclarationNameInfo(MemberDecl->getDeclName(), SourceLocation()),
/* TemplateArgumentListInfo=*/ nullptr, MemberDecl->getType(), ValueKind,
- OK_Ordinary);
+ OK_Ordinary, NOUR_None);
}
ValueDecl *ASTMaker::findMemberField(const RecordDecl *RD, StringRef Name) {
@@ -294,7 +293,7 @@ static CallExpr *create_call_once_lambda_call(ASTContext &C, ASTMaker M,
return CXXOperatorCallExpr::Create(
/*AstContext=*/C, OO_Call, callOperatorDeclRef,
- /*args=*/CallArgs,
+ /*Args=*/CallArgs,
/*QualType=*/C.VoidTy,
/*ExprValueType=*/VK_RValue,
/*SourceLocation=*/SourceLocation(), FPOptions());
@@ -466,10 +465,10 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
auto *Out =
IfStmt::Create(C, SourceLocation(),
/* IsConstexpr=*/false,
- /* init=*/nullptr,
- /* var=*/nullptr,
- /* cond=*/FlagCheck,
- /* then=*/M.makeCompound({CallbackCall, FlagAssignment}));
+ /* Init=*/nullptr,
+ /* Var=*/nullptr,
+ /* Cond=*/FlagCheck,
+ /* Then=*/M.makeCompound({CallbackCall, FlagAssignment}));
return Out;
}
@@ -512,7 +511,7 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
CallExpr *CE = CallExpr::Create(
/*ASTContext=*/C,
/*StmtClass=*/M.makeLvalueToRvalue(/*Expr=*/Block),
- /*args=*/None,
+ /*Args=*/None,
/*QualType=*/C.VoidTy,
/*ExprValueType=*/VK_RValue,
/*SourceLocation=*/SourceLocation());
@@ -550,10 +549,10 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
// (5) Create the 'if' statement.
auto *If = IfStmt::Create(C, SourceLocation(),
/* IsConstexpr=*/false,
- /* init=*/nullptr,
- /* var=*/nullptr,
- /* cond=*/GuardCondition,
- /* then=*/CS);
+ /* Init=*/nullptr,
+ /* Var=*/nullptr,
+ /* Cond=*/GuardCondition,
+ /* Then=*/CS);
return If;
}
@@ -658,16 +657,14 @@ static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D)
/// Construct the If.
auto *If = IfStmt::Create(C, SourceLocation(),
/* IsConstexpr=*/false,
- /* init=*/nullptr,
- /* var=*/nullptr, Comparison, Body,
+ /* Init=*/nullptr,
+ /* Var=*/nullptr, Comparison, Body,
SourceLocation(), Else);
return If;
}
Stmt *BodyFarm::getBody(const FunctionDecl *D) {
- D = D->getCanonicalDecl();
-
Optional<Stmt *> &Val = Bodies[D];
if (Val.hasValue())
return Val.getValue();
@@ -807,6 +804,11 @@ Stmt *BodyFarm::getBody(const ObjCMethodDecl *D) {
D = D->getCanonicalDecl();
+ // We should not try to synthesize explicitly redefined accessors.
+ // We do not know for sure how they behave.
+ if (!D->isImplicit())
+ return nullptr;
+
Optional<Stmt *> &Val = Bodies[D];
if (Val.hasValue())
return Val.getValue();
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index 96130c25be8a..0ed1e988a196 100644
--- a/lib/Analysis/CFG.cpp
+++ b/lib/Analysis/CFG.cpp
@@ -1,9 +1,8 @@
//===- CFG.cpp - Classes for representing and building CFGs ---------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -28,10 +27,11 @@
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/Type.h"
-#include "clang/Analysis/Support/BumpVector.h"
#include "clang/Analysis/ConstructionContext.h"
+#include "clang/Analysis/Support/BumpVector.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/ExceptionSpecificationType.h"
+#include "clang/Basic/JsonSupport.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
@@ -549,6 +549,7 @@ private:
CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E, AddStmtChoice asc);
CFGBlock *VisitForStmt(ForStmt *F);
CFGBlock *VisitGotoStmt(GotoStmt *G);
+ CFGBlock *VisitGCCAsmStmt(GCCAsmStmt *G, AddStmtChoice asc);
CFGBlock *VisitIfStmt(IfStmt *I);
CFGBlock *VisitImplicitCastExpr(ImplicitCastExpr *E, AddStmtChoice asc);
CFGBlock *VisitConstantExpr(ConstantExpr *E, AddStmtChoice asc);
@@ -588,6 +589,8 @@ private:
CFGBlock *VisitStmt(Stmt *S, AddStmtChoice asc);
CFGBlock *VisitChildren(Stmt *S);
CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc);
+ CFGBlock *VisitOMPExecutableDirective(OMPExecutableDirective *D,
+ AddStmtChoice asc);
void maybeAddScopeBeginForVarDecl(CFGBlock *B, const VarDecl *VD,
const Stmt *S) {
@@ -1379,6 +1382,15 @@ void CFGBuilder::findConstructionContexts(
findConstructionContexts(Layer, CO->getRHS());
break;
}
+ case Stmt::InitListExprClass: {
+ auto *ILE = cast<InitListExpr>(Child);
+ if (ILE->isTransparent()) {
+ findConstructionContexts(Layer, ILE->getInit(0));
+ break;
+ }
+ // TODO: Handle other cases. For now, fail to find construction contexts.
+ break;
+ }
default:
break;
}
@@ -1423,13 +1435,41 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) {
if (badCFG)
return nullptr;
- // For C++ constructor add initializers to CFG.
- if (const CXXConstructorDecl *CD = dyn_cast_or_null<CXXConstructorDecl>(D)) {
+ // For C++ constructor add initializers to CFG. Constructors of virtual bases
+ // are ignored unless the object is of the most derived class.
+ // class VBase { VBase() = default; VBase(int) {} };
+ // class A : virtual public VBase { A() : VBase(0) {} };
+ // class B : public A {};
+ // B b; // Constructor calls in order: VBase(), A(), B().
+ // // VBase(0) is ignored because A isn't the most derived class.
+ // This may result in the virtual base(s) being already initialized at this
+ // point, in which case we should jump right onto non-virtual bases and
+ // fields. To handle this, make a CFG branch. We only need to add one such
+ // branch per constructor, since the Standard states that all virtual bases
+ // shall be initialized before non-virtual bases and direct data members.
+ if (const auto *CD = dyn_cast_or_null<CXXConstructorDecl>(D)) {
+ CFGBlock *VBaseSucc = nullptr;
for (auto *I : llvm::reverse(CD->inits())) {
+ if (BuildOpts.AddVirtualBaseBranches && !VBaseSucc &&
+ I->isBaseInitializer() && I->isBaseVirtual()) {
+ // We've reached the first virtual base init while iterating in reverse
+ // order. Make a new block for virtual base initializers so that we
+ // could skip them.
+ VBaseSucc = Succ = B ? B : &cfg->getExit();
+ Block = createBlock();
+ }
B = addInitializer(I);
if (badCFG)
return nullptr;
}
+ if (VBaseSucc) {
+ // Make a branch block for potentially skipping virtual base initializers.
+ Succ = VBaseSucc;
+ B = createBlock();
+ B->setTerminator(
+ CFGTerminator(nullptr, CFGTerminator::VirtualBaseBranch));
+ addSuccessor(B, Block, true);
+ }
}
if (B)
@@ -1441,22 +1481,38 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) {
E = BackpatchBlocks.end(); I != E; ++I ) {
CFGBlock *B = I->block;
- const GotoStmt *G = cast<GotoStmt>(B->getTerminator());
- LabelMapTy::iterator LI = LabelMap.find(G->getLabel());
-
- // If there is no target for the goto, then we are looking at an
- // incomplete AST. Handle this by not registering a successor.
- if (LI == LabelMap.end()) continue;
-
- JumpTarget JT = LI->second;
- prependAutomaticObjLifetimeWithTerminator(B, I->scopePosition,
- JT.scopePosition);
- prependAutomaticObjDtorsWithTerminator(B, I->scopePosition,
- JT.scopePosition);
- const VarDecl *VD = prependAutomaticObjScopeEndWithTerminator(
- B, I->scopePosition, JT.scopePosition);
- appendScopeBegin(JT.block, VD, G);
- addSuccessor(B, JT.block);
+ if (auto *G = dyn_cast<GotoStmt>(B->getTerminator())) {
+ LabelMapTy::iterator LI = LabelMap.find(G->getLabel());
+ // If there is no target for the goto, then we are looking at an
+ // incomplete AST. Handle this by not registering a successor.
+ if (LI == LabelMap.end())
+ continue;
+ JumpTarget JT = LI->second;
+ prependAutomaticObjLifetimeWithTerminator(B, I->scopePosition,
+ JT.scopePosition);
+ prependAutomaticObjDtorsWithTerminator(B, I->scopePosition,
+ JT.scopePosition);
+ const VarDecl *VD = prependAutomaticObjScopeEndWithTerminator(
+ B, I->scopePosition, JT.scopePosition);
+ appendScopeBegin(JT.block, VD, G);
+ addSuccessor(B, JT.block);
+ };
+ if (auto *G = dyn_cast<GCCAsmStmt>(B->getTerminator())) {
+ CFGBlock *Successor = (I+1)->block;
+ for (auto *L : G->labels()) {
+ LabelMapTy::iterator LI = LabelMap.find(L->getLabel());
+ // If there is no target for the goto, then we are looking at an
+ // incomplete AST. Handle this by not registering a successor.
+ if (LI == LabelMap.end())
+ continue;
+ JumpTarget JT = LI->second;
+ // Successor has been added, so skip it.
+ if (JT.block == Successor)
+ continue;
+ addSuccessor(B, JT.block);
+ }
+ I++;
+ }
}
// Add successors to the Indirect Goto Dispatch block (if we have one).
@@ -1761,6 +1817,9 @@ void CFGBuilder::addImplicitDtorsForDestructor(const CXXDestructorDecl *DD) {
// At the end destroy virtual base objects.
for (const auto &VI : RD->vbases()) {
+ // TODO: Add a VirtualBaseBranch to see if the most derived class
+ // (which is different from the current class) is responsible for
+ // destroying them.
const CXXRecordDecl *CD = VI.getType()->getAsCXXRecordDecl();
if (!CD->hasTrivialDestructor()) {
autoCreateBlock();
@@ -1948,7 +2007,7 @@ void CFGBuilder::prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk,
= Blk->beginAutomaticObjDtorsInsert(Blk->end(), B.distance(E), C);
for (LocalScope::const_iterator I = B; I != E; ++I)
InsertPos = Blk->insertAutomaticObjDtor(InsertPos, *I,
- Blk->getTerminator());
+ Blk->getTerminatorStmt());
}
/// prependAutomaticObjLifetimeWithTerminator - Prepend lifetime CFGElements for
@@ -1963,8 +2022,10 @@ void CFGBuilder::prependAutomaticObjLifetimeWithTerminator(
BumpVectorContext &C = cfg->getBumpVectorContext();
CFGBlock::iterator InsertPos =
Blk->beginLifetimeEndsInsert(Blk->end(), B.distance(E), C);
- for (LocalScope::const_iterator I = B; I != E; ++I)
- InsertPos = Blk->insertLifetimeEnds(InsertPos, *I, Blk->getTerminator());
+ for (LocalScope::const_iterator I = B; I != E; ++I) {
+ InsertPos =
+ Blk->insertLifetimeEnds(InsertPos, *I, Blk->getTerminatorStmt());
+ }
}
/// prependAutomaticObjScopeEndWithTerminator - Prepend scope end CFGElements for
@@ -1983,7 +2044,7 @@ CFGBuilder::prependAutomaticObjScopeEndWithTerminator(
LocalScope::const_iterator PlaceToInsert = B;
for (LocalScope::const_iterator I = B; I != E; ++I)
PlaceToInsert = I;
- Blk->insertScopeEnd(InsertPos, *PlaceToInsert, Blk->getTerminator());
+ Blk->insertScopeEnd(InsertPos, *PlaceToInsert, Blk->getTerminatorStmt());
return *PlaceToInsert;
}
@@ -1999,6 +2060,10 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
if (Expr *E = dyn_cast<Expr>(S))
S = E->IgnoreParens();
+ if (Context->getLangOpts().OpenMP)
+ if (auto *D = dyn_cast<OMPExecutableDirective>(S))
+ return VisitOMPExecutableDirective(D, asc);
+
switch (S->getStmtClass()) {
default:
return VisitStmt(S, asc);
@@ -2100,6 +2165,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
case Stmt::GotoStmtClass:
return VisitGotoStmt(cast<GotoStmt>(S));
+ case Stmt::GCCAsmStmtClass:
+ return VisitGCCAsmStmt(cast<GCCAsmStmt>(S), asc);
+
case Stmt::IfStmtClass:
return VisitIfStmt(cast<IfStmt>(S));
@@ -2459,7 +2527,8 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
NoReturn = true;
if (FD->hasAttr<NoThrowAttr>())
AddEHEdge = false;
- if (FD->getBuiltinID() == Builtin::BI__builtin_object_size)
+ if (FD->getBuiltinID() == Builtin::BI__builtin_object_size ||
+ FD->getBuiltinID() == Builtin::BI__builtin_dynamic_object_size)
OmitArguments = true;
}
@@ -2862,8 +2931,8 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {
// Add the successors. If we know that specific branches are
// unreachable, inform addSuccessor() of that knowledge.
- addSuccessor(Block, ThenBlock, /* isReachable = */ !KnownVal.isFalse());
- addSuccessor(Block, ElseBlock, /* isReachable = */ !KnownVal.isTrue());
+ addSuccessor(Block, ThenBlock, /* IsReachable = */ !KnownVal.isFalse());
+ addSuccessor(Block, ElseBlock, /* IsReachable = */ !KnownVal.isTrue());
// Add the condition as the last statement in the new block. This may
// create new blocks as the condition may contain control-flow. Any newly
@@ -3103,6 +3172,28 @@ CFGBlock *CFGBuilder::VisitGotoStmt(GotoStmt *G) {
return Block;
}
+CFGBlock *CFGBuilder::VisitGCCAsmStmt(GCCAsmStmt *G, AddStmtChoice asc) {
+ // Goto is a control-flow statement. Thus we stop processing the current
+ // block and create a new one.
+
+ if (!G->isAsmGoto())
+ return VisitStmt(G, asc);
+
+ if (Block) {
+ Succ = Block;
+ if (badCFG)
+ return nullptr;
+ }
+ Block = createBlock();
+ Block->setTerminator(G);
+ // We will backpatch this block later for all the labels.
+ BackpatchBlocks.push_back(JumpSource(Block, ScopePos));
+ // Save "Succ" in BackpatchBlocks. In the backpatch processing, "Succ" is
+ // used to avoid adding "Succ" again.
+ BackpatchBlocks.push_back(JumpSource(Succ, ScopePos));
+ return Block;
+}
+
CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) {
CFGBlock *LoopSuccessor = nullptr;
@@ -4330,8 +4421,8 @@ CFGBlock *CFGBuilder::VisitCXXNewExpr(CXXNewExpr *NE,
if (BuildOpts.AddCXXNewAllocator)
appendNewAllocator(Block, NE);
- if (NE->isArray())
- Block = Visit(NE->getArraySize());
+ if (NE->isArray() && *NE->getArraySize())
+ Block = Visit(*NE->getArraySize());
for (CXXNewExpr::arg_iterator I = NE->placement_arg_begin(),
E = NE->placement_arg_end(); I != E; ++I)
@@ -4603,7 +4694,8 @@ void CFGBuilder::InsertTempDtorDecisionBlock(const TempDtorContext &Context,
}
assert(Context.TerminatorExpr);
CFGBlock *Decision = createBlock(false);
- Decision->setTerminator(CFGTerminator(Context.TerminatorExpr, true));
+ Decision->setTerminator(CFGTerminator(Context.TerminatorExpr,
+ CFGTerminator::TemporaryDtorsBranch));
addSuccessor(Decision, Block, !Context.KnownExecuted.isFalse());
addSuccessor(Decision, FalseSucc ? FalseSucc : Context.Succ,
!Context.KnownExecuted.isTrue());
@@ -4642,6 +4734,37 @@ CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
return Block;
}
+CFGBlock *CFGBuilder::VisitOMPExecutableDirective(OMPExecutableDirective *D,
+ AddStmtChoice asc) {
+ if (asc.alwaysAdd(*this, D)) {
+ autoCreateBlock();
+ appendStmt(Block, D);
+ }
+
+ // Iterate over all used expression in clauses.
+ CFGBlock *B = Block;
+
+ // Reverse the elements to process them in natural order. Iterators are not
+ // bidirectional, so we need to create temp vector.
+ SmallVector<Stmt *, 8> Used(
+ OMPExecutableDirective::used_clauses_children(D->clauses()));
+ for (Stmt *S : llvm::reverse(Used)) {
+ assert(S && "Expected non-null used-in-clause child.");
+ if (CFGBlock *R = Visit(S))
+ B = R;
+ }
+ // Visit associated structured block if any.
+ if (!D->isStandaloneDirective())
+ if (Stmt *S = D->getStructuredBlock()) {
+ if (!isa<CompoundStmt>(S))
+ addLocalScopeAndDtors(S);
+ if (CFGBlock *R = addStmt(S))
+ B = R;
+ }
+
+ return B;
+}
+
/// createBlock - Constructs and adds a new CFGBlock to the CFG. The block has
/// no successors or predecessors. If this is the first block created in the
/// CFG, it is automatically set to be the Entry and Exit of the CFG.
@@ -4668,6 +4791,51 @@ std::unique_ptr<CFG> CFG::buildCFG(const Decl *D, Stmt *Statement,
return Builder.buildCFG(D, Statement);
}
+bool CFG::isLinear() const {
+ // Quick path: if we only have the ENTRY block, the EXIT block, and some code
+ // in between, then we have no room for control flow.
+ if (size() <= 3)
+ return true;
+
+ // Traverse the CFG until we find a branch.
+ // TODO: While this should still be very fast,
+ // maybe we should cache the answer.
+ llvm::SmallPtrSet<const CFGBlock *, 4> Visited;
+ const CFGBlock *B = Entry;
+ while (B != Exit) {
+ auto IteratorAndFlag = Visited.insert(B);
+ if (!IteratorAndFlag.second) {
+ // We looped back to a block that we've already visited. Not linear.
+ return false;
+ }
+
+ // Iterate over reachable successors.
+ const CFGBlock *FirstReachableB = nullptr;
+ for (const CFGBlock::AdjacentBlock &AB : B->succs()) {
+ if (!AB.isReachable())
+ continue;
+
+ if (FirstReachableB == nullptr) {
+ FirstReachableB = &*AB;
+ } else {
+ // We've encountered a branch. It's not a linear CFG.
+ return false;
+ }
+ }
+
+ if (!FirstReachableB) {
+ // We reached a dead end. EXIT is unreachable. This is linear enough.
+ return true;
+ }
+
+ // There's only one way to move forward. Proceed.
+ B = FirstReachableB;
+ }
+
+ // We reached EXIT and found no branches.
+ return true;
+}
+
const CXXDestructorDecl *
CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const {
switch (getKind()) {
@@ -4766,7 +4934,7 @@ bool CFGBlock::FilterEdge(const CFGBlock::FilterOptions &F,
// If the 'To' has no label or is labeled but the label isn't a
// CaseStmt then filter this edge.
if (const SwitchStmt *S =
- dyn_cast_or_null<SwitchStmt>(From->getTerminator().getStmt())) {
+ dyn_cast_or_null<SwitchStmt>(From->getTerminatorStmt())) {
if (S->isAllEnumCasesCovered()) {
const Stmt *L = To->getLabel();
if (!L || !isa<CaseStmt>(L))
@@ -5001,9 +5169,18 @@ public:
public:
void print(CFGTerminator T) {
- if (T.isTemporaryDtorsBranch())
+ switch (T.getKind()) {
+ case CFGTerminator::StmtBranch:
+ Visit(T.getStmt());
+ break;
+ case CFGTerminator::TemporaryDtorsBranch:
OS << "(Temp Dtor) ";
- Visit(T.getStmt());
+ Visit(T.getStmt());
+ break;
+ case CFGTerminator::VirtualBaseBranch:
+ OS << "(See if most derived ctor has already initialized vbases)";
+ break;
+ }
}
};
@@ -5312,7 +5489,7 @@ static void print_block(raw_ostream &OS, const CFG* cfg,
}
// Print the terminator of this block.
- if (B.getTerminator()) {
+ if (B.getTerminator().isValid()) {
if (ShowColors)
OS.changeColor(raw_ostream::GREEN);
@@ -5464,8 +5641,43 @@ void CFGBlock::printTerminator(raw_ostream &OS,
TPrinter.print(getTerminator());
}
+/// printTerminatorJson - Pretty-prints the terminator in JSON format.
+void CFGBlock::printTerminatorJson(raw_ostream &Out, const LangOptions &LO,
+ bool AddQuotes) const {
+ std::string Buf;
+ llvm::raw_string_ostream TempOut(Buf);
+
+ printTerminator(TempOut, LO);
+
+ Out << JsonFormat(TempOut.str(), AddQuotes);
+}
+
+const Expr *CFGBlock::getLastCondition() const {
+ // If the terminator is a temporary dtor or a virtual base, etc, we can't
+ // retrieve a meaningful condition, bail out.
+ if (Terminator.getKind() != CFGTerminator::StmtBranch)
+ return nullptr;
+
+ // Also, if this method was called on a block that doesn't have 2 successors,
+ // this block doesn't have retrievable condition.
+ if (succ_size() < 2)
+ return nullptr;
+
+ auto StmtElem = rbegin()->getAs<CFGStmt>();
+ if (!StmtElem)
+ return nullptr;
+
+ const Stmt *Cond = StmtElem->getStmt();
+ if (isa<ObjCForCollectionStmt>(Cond))
+ return nullptr;
+
+ // Only ObjCForCollectionStmt is known not to be a non-Expr terminator, hence
+ // the cast<>.
+ return cast<Expr>(Cond)->IgnoreParens();
+}
+
Stmt *CFGBlock::getTerminatorCondition(bool StripParens) {
- Stmt *Terminator = this->Terminator;
+ Stmt *Terminator = getTerminatorStmt();
if (!Terminator)
return nullptr;
diff --git a/lib/Analysis/CFGReachabilityAnalysis.cpp b/lib/Analysis/CFGReachabilityAnalysis.cpp
index cdad5b57aee8..2b5d6c466cde 100644
--- a/lib/Analysis/CFGReachabilityAnalysis.cpp
+++ b/lib/Analysis/CFGReachabilityAnalysis.cpp
@@ -1,9 +1,8 @@
//===- CFGReachabilityAnalysis.cpp - Basic reachability analysis ----------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/Analysis/CFGStmtMap.cpp b/lib/Analysis/CFGStmtMap.cpp
index 3eed0d52f848..d1c23e3c879b 100644
--- a/lib/Analysis/CFGStmtMap.cpp
+++ b/lib/Analysis/CFGStmtMap.cpp
@@ -1,9 +1,8 @@
//===--- CFGStmtMap.h - Map from Stmt* to CFGBlock* -----------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -71,7 +70,7 @@ static void Accumulate(SMap &SM, CFGBlock *B) {
// Finally, look at the terminator. If the terminator was already added
// because it is a block-level expression in another block, overwrite
// that mapping.
- if (Stmt *Term = B->getTerminator())
+ if (Stmt *Term = B->getTerminatorStmt())
SM[Term] = B;
}
diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp
index 66a6f1a9bcea..7eda80ea0505 100644
--- a/lib/Analysis/CallGraph.cpp
+++ b/lib/Analysis/CallGraph.cpp
@@ -1,9 +1,8 @@
//===- CallGraph.cpp - AST-based Call graph -------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/Analysis/CloneDetection.cpp b/lib/Analysis/CloneDetection.cpp
index 88402e2adaa7..74328e8ae67f 100644
--- a/lib/Analysis/CloneDetection.cpp
+++ b/lib/Analysis/CloneDetection.cpp
@@ -1,9 +1,8 @@
//===--- CloneDetection.cpp - Finds code clones in an AST -------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
@@ -368,11 +367,7 @@ void RecursiveCloneTypeIIHashConstraint::constrain(
}
// Sort hash_codes in StmtsByHash.
- std::stable_sort(StmtsByHash.begin(), StmtsByHash.end(),
- [](std::pair<size_t, StmtSequence> LHS,
- std::pair<size_t, StmtSequence> RHS) {
- return LHS.first < RHS.first;
- });
+ llvm::stable_sort(StmtsByHash, llvm::less_first());
// Check for each StmtSequence if its successor has the same hash value.
// We don't check the last StmtSequence as it has no successor.
diff --git a/lib/Analysis/CocoaConventions.cpp b/lib/Analysis/CocoaConventions.cpp
index b2d416c171a1..b2ef426dead2 100644
--- a/lib/Analysis/CocoaConventions.cpp
+++ b/lib/Analysis/CocoaConventions.cpp
@@ -1,9 +1,8 @@
//===- CocoaConventions.h - Special handling of Cocoa conventions -*- C++ -*--//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/Analysis/CodeInjector.cpp b/lib/Analysis/CodeInjector.cpp
index 76bf364444d1..412de96a13b9 100644
--- a/lib/Analysis/CodeInjector.cpp
+++ b/lib/Analysis/CodeInjector.cpp
@@ -1,9 +1,8 @@
//===-- CodeInjector.cpp ----------------------------------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Analysis/ConstructionContext.cpp b/lib/Analysis/ConstructionContext.cpp
index 8169d4a93a6d..6ba1e2173d2c 100644
--- a/lib/Analysis/ConstructionContext.cpp
+++ b/lib/Analysis/ConstructionContext.cpp
@@ -1,9 +1,8 @@
//===- ConstructionContext.cpp - CFG constructor information --------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/Analysis/Consumed.cpp b/lib/Analysis/Consumed.cpp
index 16eeaba2f61b..eee36d9caf7f 100644
--- a/lib/Analysis/Consumed.cpp
+++ b/lib/Analysis/Consumed.cpp
@@ -1,9 +1,8 @@
//===- Consumed.cpp -------------------------------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -77,7 +76,7 @@ static SourceLocation getFirstStmtLoc(const CFGBlock *Block) {
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()) {
+ if (const Stmt *StmtNode = Block->getTerminatorStmt()) {
return StmtNode->getBeginLoc();
} else {
for (CFGBlock::const_reverse_iterator BI = Block->rbegin(),
diff --git a/lib/Analysis/Dominators.cpp b/lib/Analysis/Dominators.cpp
index 1b7dd8c804e1..f7ad68673d0f 100644
--- a/lib/Analysis/Dominators.cpp
+++ b/lib/Analysis/Dominators.cpp
@@ -1,14 +1,19 @@
//===- Dominators.cpp - Implementation of dominators tree for Clang CFG ---===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Analyses/Dominators.h"
-using namespace clang;
+namespace clang {
+
+template <>
+void CFGDominatorTreeImpl</*IsPostDom=*/true>::anchor() {}
+
+template <>
+void CFGDominatorTreeImpl</*IsPostDom=*/false>::anchor() {}
-void DominatorTree::anchor() {}
+} // end of namespace clang
diff --git a/lib/Analysis/ExprMutationAnalyzer.cpp b/lib/Analysis/ExprMutationAnalyzer.cpp
index 8414cb5c726a..fb5a139e82ab 100644
--- a/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -1,9 +1,8 @@
//===---------- ExprMutationAnalyzer.cpp ----------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Analyses/ExprMutationAnalyzer.h"
@@ -25,6 +24,18 @@ AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt,
return InnerMatcher.matches(*Range, Finder, Builder);
}
+AST_MATCHER_P(Expr, maybeEvalCommaExpr,
+ ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+ const Expr* Result = &Node;
+ while (const auto *BOComma =
+ dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) {
+ if (!BOComma->isCommaOp())
+ break;
+ Result = BOComma->getRHS();
+ }
+ return InnerMatcher.matches(*Result, Finder, Builder);
+}
+
const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr>
cxxTypeidExpr;
@@ -194,24 +205,28 @@ const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation(
const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
// LHS of any assignment operators.
const auto AsAssignmentLhs =
- binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(Exp)));
+ binaryOperator(isAssignmentOperator(),
+ hasLHS(maybeEvalCommaExpr(equalsNode(Exp))));
// Operand of increment/decrement operators.
const auto AsIncDecOperand =
unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")),
- hasUnaryOperand(equalsNode(Exp)));
+ hasUnaryOperand(maybeEvalCommaExpr(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))),
+ expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod),
+ on(maybeEvalCommaExpr(equalsNode(Exp)))),
cxxOperatorCallExpr(callee(NonConstMethod),
- hasArgument(0, equalsNode(Exp))),
+ hasArgument(0,
+ maybeEvalCommaExpr(equalsNode(Exp)))),
callExpr(callee(expr(anyOf(
- unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))),
+ unresolvedMemberExpr(
+ hasObjectExpression(maybeEvalCommaExpr(equalsNode(Exp)))),
cxxDependentScopeMemberExpr(
- hasObjectExpression(equalsNode(Exp)))))))));
+ hasObjectExpression(maybeEvalCommaExpr(equalsNode(Exp))))))))));
// Taking address of 'Exp'.
// We're assuming 'Exp' is mutated as soon as its address is taken, though in
@@ -221,10 +236,11 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
unaryOperator(hasOperatorName("&"),
// A NoOp implicit cast is adding const.
unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))),
- hasUnaryOperand(equalsNode(Exp)));
+ hasUnaryOperand(maybeEvalCommaExpr(equalsNode(Exp))));
const auto AsPointerFromArrayDecay =
castExpr(hasCastKind(CK_ArrayToPointerDecay),
- unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp)));
+ unless(hasParent(arraySubscriptExpr())),
+ has(maybeEvalCommaExpr(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.
@@ -232,7 +248,8 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
cxxOperatorCallExpr(hasOverloadedOperatorName("->"),
callee(cxxMethodDecl(ofClass(isMoveOnly()),
returns(nonConstPointerType()))),
- argumentCountIs(1), hasArgument(0, equalsNode(Exp)));
+ argumentCountIs(1),
+ hasArgument(0, maybeEvalCommaExpr(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.
@@ -240,7 +257,8 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
// findFunctionArgMutation which has additional smarts for handling forwarding
// references.
const auto NonConstRefParam = forEachArgumentWithParam(
- equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType())));
+ maybeEvalCommaExpr(equalsNode(Exp)),
+ parmVarDecl(hasType(nonConstReferenceType())));
const auto NotInstantiated = unless(hasDeclaration(isInstantiated()));
const auto AsNonConstRefArg = anyOf(
callExpr(NonConstRefParam, NotInstantiated),
@@ -248,8 +266,8 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(),
cxxDependentScopeMemberExpr(),
hasType(templateTypeParmType())))),
- hasAnyArgument(equalsNode(Exp))),
- cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp))));
+ hasAnyArgument(maybeEvalCommaExpr(equalsNode(Exp)))),
+ cxxUnresolvedConstructExpr(hasAnyArgument(maybeEvalCommaExpr(equalsNode(Exp)))));
// Captured by a lambda by reference.
// If we're initializing a capture with 'Exp' directly then we're initializing
@@ -262,7 +280,8 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) {
// 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 AsNonConstRefReturn = returnStmt(hasReturnValue(
+ maybeEvalCommaExpr(equalsNode(Exp))));
const auto Matches =
match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis,
diff --git a/lib/Analysis/LiveVariables.cpp b/lib/Analysis/LiveVariables.cpp
index afe2d264907f..2cd607d8a493 100644
--- a/lib/Analysis/LiveVariables.cpp
+++ b/lib/Analysis/LiveVariables.cpp
@@ -1,9 +1,8 @@
//=- LiveVariables.cpp - Live Variable Analysis for Source CFGs ----------*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -502,7 +501,7 @@ LiveVariablesImpl::runOnBlock(const CFGBlock *block,
TransferFunctions TF(*this, val, obs, block);
// Visit the terminator (if any).
- if (const Stmt *term = block->getTerminator())
+ if (const Stmt *term = block->getTerminatorStmt())
TF.Visit(const_cast<Stmt*>(term));
// Apply the transfer function for all Stmts in the block.
diff --git a/lib/Analysis/ObjCNoReturn.cpp b/lib/Analysis/ObjCNoReturn.cpp
index f27568c0c322..fe1edb496859 100644
--- a/lib/Analysis/ObjCNoReturn.cpp
+++ b/lib/Analysis/ObjCNoReturn.cpp
@@ -1,9 +1,8 @@
//= ObjCNoReturn.cpp - Handling of Cocoa APIs known not to return --*- C++ -*---
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/Analysis/PostOrderCFGView.cpp b/lib/Analysis/PostOrderCFGView.cpp
index d5d0bafe664c..f79d0007cb3d 100644
--- a/lib/Analysis/PostOrderCFGView.cpp
+++ b/lib/Analysis/PostOrderCFGView.cpp
@@ -1,9 +1,8 @@
//===- PostOrderCFGView.cpp - Post order view of CFG blocks ---------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/Analysis/ProgramPoint.cpp b/lib/Analysis/ProgramPoint.cpp
index 2d016cb13353..97e90965d007 100644
--- a/lib/Analysis/ProgramPoint.cpp
+++ b/lib/Analysis/ProgramPoint.cpp
@@ -1,9 +1,8 @@
//==- ProgramPoint.cpp - Program Points for Path-Sensitive Analysis -*- C++ -*-/
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -13,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/JsonSupport.h"
using namespace clang;
@@ -44,151 +44,141 @@ 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;
- }
+ return printJson(llvm::errs());
}
-void ProgramPoint::print(StringRef CR, llvm::raw_ostream &Out) const {
+void ProgramPoint::printJson(llvm::raw_ostream &Out, const char *NL) const {
const ASTContext &Context =
getLocationContext()->getAnalysisDeclContext()->getASTContext();
const SourceManager &SM = Context.getSourceManager();
+ const PrintingPolicy &PP = Context.getPrintingPolicy();
+ const bool AddQuotes = true;
+
+ Out << "\"kind\": \"";
switch (getKind()) {
case ProgramPoint::BlockEntranceKind:
- Out << "Block Entrance: B"
+ Out << "BlockEntrance\""
+ << ", \"block_id\": "
<< castAs<BlockEntrance>().getBlock()->getBlockID();
break;
case ProgramPoint::FunctionExitKind: {
auto FEP = getAs<FunctionExitPoint>();
- Out << "Function Exit: B" << FEP->getBlock()->getBlockID();
+ Out << "FunctionExit\""
+ << ", \"block_id\": " << FEP->getBlock()->getBlockID()
+ << ", \"stmt_id\": ";
+
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);
+ Out << RS->getID(Context) << ", \"stmt\": ";
+ RS->printJson(Out, nullptr, PP, AddQuotes);
+ } else {
+ Out << "null, \"stmt\": null";
}
break;
}
case ProgramPoint::BlockExitKind:
- assert(false);
+ llvm_unreachable("BlockExitKind");
break;
-
case ProgramPoint::CallEnterKind:
- Out << "CallEnter";
+ Out << "CallEnter\"";
break;
-
case ProgramPoint::CallExitBeginKind:
- Out << "CallExitBegin";
+ Out << "CallExitBegin\"";
break;
-
case ProgramPoint::CallExitEndKind:
- Out << "CallExitEnd";
- break;
-
- case ProgramPoint::PostStmtPurgeDeadSymbolsKind:
- Out << "PostStmtPurgeDeadSymbols";
- break;
-
- case ProgramPoint::PreStmtPurgeDeadSymbolsKind:
- Out << "PreStmtPurgeDeadSymbols";
+ Out << "CallExitEnd\"";
break;
-
case ProgramPoint::EpsilonKind:
- Out << "Epsilon Point";
+ Out << "EpsilonPoint\"";
break;
- case ProgramPoint::LoopExitKind: {
- LoopExit LE = castAs<LoopExit>();
- Out << "LoopExit: " << LE.getLoopStmt()->getStmtClassName();
+ case ProgramPoint::LoopExitKind:
+ Out << "LoopExit\", \"stmt\": \""
+ << castAs<LoopExit>().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);
+ Out << "PreCall\", \"decl\": \""
+ << PC.getDecl()->getAsFunction()->getQualifiedNameAsString()
+ << "\", \"location\": ";
+ printSourceLocationAsJson(Out, PC.getLocation(), SM);
break;
}
case ProgramPoint::PostImplicitCallKind: {
ImplicitCallPoint PC = castAs<ImplicitCallPoint>();
- Out << "PostCall: ";
- PC.getDecl()->print(Out, Context.getLangOpts());
- printLocation(Out, PC.getLocation(), SM, CR, /*Postfix=*/CR);
+ Out << "PostCall\", \"decl\": \""
+ << PC.getDecl()->getAsFunction()->getQualifiedNameAsString()
+ << "\", \"location\": ";
+ printSourceLocationAsJson(Out, PC.getLocation(), SM);
break;
}
case ProgramPoint::PostInitializerKind: {
- Out << "PostInitializer: ";
+ Out << "PostInitializer\", ";
const CXXCtorInitializer *Init = castAs<PostInitializer>().getInitializer();
- if (const FieldDecl *FD = Init->getAnyMember())
- Out << *FD;
- else {
+ if (const FieldDecl *FD = Init->getAnyMember()) {
+ Out << "\"field_decl\": \"" << *FD << '\"';
+ } else {
+ Out << "\"type\": \"";
QualType Ty = Init->getTypeSourceInfo()->getType();
Ty = Ty.getLocalUnqualifiedType();
Ty.print(Out, Context.getLangOpts());
+ Out << '\"';
}
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 << ":";
+ const Stmt *T = E.getSrc()->getTerminatorStmt();
+ Out << "Edge\", \"src_id\": " << E.getSrc()->getBlockID()
+ << ", \"dst_id\": " << E.getDst()->getBlockID() << ", \"terminator\": ";
+
+ if (!T) {
+ Out << "null, \"term_kind\": null";
+ break;
+ }
+
+ E.getSrc()->printTerminatorJson(Out, Context.getLangOpts(),
+ /*AddQuotes=*/true);
+ Out << ", \"location\": ";
+ printSourceLocationAsJson(Out, T->getBeginLoc(), SM);
+
+ Out << ", \"term_kind\": \"";
+ if (isa<SwitchStmt>(T)) {
+ Out << "SwitchStmt\", \"case\": ";
+ if (const Stmt *Label = E.getDst()->getLabel()) {
+ if (const auto *C = dyn_cast<CaseStmt>(Label)) {
+ Out << "{ \"lhs\": ";
+ if (const Stmt *LHS = C->getLHS()) {
+ LHS->printJson(Out, nullptr, PP, AddQuotes);
+ } else {
+ Out << "null";
+ }
+
+ Out << ", \"rhs\": ";
+ if (const Stmt *RHS = C->getRHS()) {
+ RHS->printJson(Out, nullptr, PP, AddQuotes);
} else {
- assert(isa<DefaultStmt>(Label));
- Out << CR << "default:";
+ Out << "null";
}
- } else
- Out << CR << "(implicit) default:";
- } else if (isa<IndirectGotoStmt>(T)) {
- // FIXME
+ Out << " }";
+ } else {
+ assert(isa<DefaultStmt>(Label));
+ Out << "\"default\"";
+ }
} else {
- Out << CR << "Condition: ";
- if (*E.getSrc()->succ_begin() == E.getDst())
- Out << "true";
- else
- Out << "false";
+ Out << "\"implicit default\"";
}
-
- Out << CR;
+ } else if (isa<IndirectGotoStmt>(T)) {
+ // FIXME: More info.
+ Out << "IndirectGotoStmt\"";
+ } else {
+ Out << "Condition\", \"value\": "
+ << (*E.getSrc()->succ_begin() == E.getDst() ? "true" : "false");
}
-
break;
}
@@ -196,23 +186,44 @@ void ProgramPoint::print(StringRef CR, llvm::raw_ostream &Out) const {
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=*/"");
+ Out << "Statement\", \"stmt_kind\": \"" << S->getStmtClassName()
+ << "\", \"stmt_id\": " << S->getID(Context)
+ << ", \"pointer\": \"" << (const void *)S << "\", \"pretty\": ";
+
+ S->printJson(Out, nullptr, PP, AddQuotes);
- if (getAs<PreStmt>())
- Out << CR << "PreStmt" << CR;
+ Out << ", \"location\": ";
+ printSourceLocationAsJson(Out, S->getBeginLoc(), SM);
+
+ Out << ", \"stmt_point_kind\": \"";
+ if (getAs<PreLoad>())
+ Out << "PreLoad";
+ else if (getAs<PreStore>())
+ Out << "PreStore";
+ else if (getAs<PostAllocatorCall>())
+ Out << "PostAllocatorCall";
+ else if (getAs<PostCondition>())
+ Out << "PostCondition";
else if (getAs<PostLoad>())
- Out << CR << "PostLoad" << CR;
- else if (getAs<PostStore>())
- Out << CR << "PostStore" << CR;
+ Out << "PostLoad";
else if (getAs<PostLValue>())
- Out << CR << "PostLValue" << CR;
- else if (getAs<PostAllocatorCall>())
- Out << CR << "PostAllocatorCall" << CR;
+ Out << "PostLValue";
+ else if (getAs<PostStore>())
+ Out << "PostStore";
+ else if (getAs<PostStmt>())
+ Out << "PostStmt";
+ else if (getAs<PostStmtPurgeDeadSymbols>())
+ Out << "PostStmtPurgeDeadSymbols";
+ else if (getAs<PreStmtPurgeDeadSymbols>())
+ Out << "PreStmtPurgeDeadSymbols";
+ else if (getAs<PreStmt>())
+ Out << "PreStmt";
+ else {
+ Out << "\nKind: '" << getKind();
+ llvm_unreachable("' is unhandled StmtPoint kind!");
+ }
+ Out << '\"';
break;
}
}
diff --git a/lib/Analysis/ReachableCode.cpp b/lib/Analysis/ReachableCode.cpp
index 87f4f7010f98..2fea88ea2eff 100644
--- a/lib/Analysis/ReachableCode.cpp
+++ b/lib/Analysis/ReachableCode.cpp
@@ -1,9 +1,8 @@
-//=- ReachableCodePathInsensitive.cpp ---------------------------*- C++ --*-==//
+//===-- ReachableCode.cpp - Code Reachability Analysis --------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -49,7 +48,7 @@ static bool isTrivialExpression(const Expr *Ex) {
static bool isTrivialDoWhile(const CFGBlock *B, const Stmt *S) {
// Check if the block ends with a do...while() and see if 'S' is the
// condition.
- if (const Stmt *Term = B->getTerminator()) {
+ if (const Stmt *Term = B->getTerminatorStmt()) {
if (const DoStmt *DS = dyn_cast<DoStmt>(Term)) {
const Expr *Cond = DS->getCond()->IgnoreParenCasts();
return Cond == S && isTrivialExpression(Cond);
@@ -117,7 +116,7 @@ static bool isDeadReturn(const CFGBlock *B, const Stmt *S) {
// the call to the destructor.
assert(Current->succ_size() == 2);
Current = *(Current->succ_begin() + 1);
- } else if (!Current->getTerminator() && Current->succ_size() == 1) {
+ } else if (!Current->getTerminatorStmt() && Current->succ_size() == 1) {
// If there is only one successor, we're not dealing with outgoing control
// flow. Thus, look into the next block.
Current = *Current->succ_begin();
@@ -193,9 +192,10 @@ static bool isConfigurationValue(const Stmt *S,
if (!S)
return false;
- S = S->IgnoreImplicit();
+ if (const auto *Ex = dyn_cast<Expr>(S))
+ S = Ex->IgnoreImplicit();
- if (const Expr *Ex = dyn_cast<Expr>(S))
+ if (const auto *Ex = dyn_cast<Expr>(S))
S = Ex->IgnoreCasts();
// Special case looking for the sigil '()' around an integer literal.
@@ -292,7 +292,7 @@ static bool isConfigurationValue(const ValueDecl *D, Preprocessor &PP) {
/// Returns true if we should always explore all successors of a block.
static bool shouldTreatSuccessorsAsReachable(const CFGBlock *B,
Preprocessor &PP) {
- if (const Stmt *Term = B->getTerminator()) {
+ if (const Stmt *Term = B->getTerminatorStmt()) {
if (isa<SwitchStmt>(Term))
return true;
// Specially handle '||' and '&&'.
@@ -461,12 +461,11 @@ const Stmt *DeadCodeScan::findDeadCode(const clang::CFGBlock *Block) {
return S;
}
- if (CFGTerminator T = Block->getTerminator()) {
- if (!T.isTemporaryDtorsBranch()) {
- const Stmt *S = T.getStmt();
- if (isValidDeadStmt(S))
- return S;
- }
+ CFGTerminator T = Block->getTerminator();
+ if (T.isStmtBranch()) {
+ const Stmt *S = T.getStmt();
+ if (S && isValidDeadStmt(S))
+ return S;
}
return nullptr;
diff --git a/lib/Analysis/RetainSummaryManager.cpp b/lib/Analysis/RetainSummaryManager.cpp
new file mode 100644
index 000000000000..132053fd2c24
--- /dev/null
+++ b/lib/Analysis/RetainSummaryManager.cpp
@@ -0,0 +1,1287 @@
+//== RetainSummaryManager.cpp - Summaries for reference counting --*- C++ -*--//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines summaries implementation for retain counting, which
+// implements a reference count checker for Core Foundation, Cocoa
+// and OSObject (on Mac OS X).
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
+#include "clang/Analysis/RetainSummaryManager.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang;
+using namespace ento;
+
+template <class T>
+constexpr static bool isOneOf() {
+ return false;
+}
+
+/// Helper function to check whether the class is one of the
+/// rest of varargs.
+template <class T, class P, class... ToCompare>
+constexpr static bool isOneOf() {
+ return std::is_same<T, P>::value || isOneOf<T, ToCompare...>();
+}
+
+namespace {
+
+/// Fake attribute class for RC* attributes.
+struct GeneralizedReturnsRetainedAttr {
+ static bool classof(const Attr *A) {
+ if (auto AA = dyn_cast<AnnotateAttr>(A))
+ return AA->getAnnotation() == "rc_ownership_returns_retained";
+ return false;
+ }
+};
+
+struct GeneralizedReturnsNotRetainedAttr {
+ static bool classof(const Attr *A) {
+ if (auto AA = dyn_cast<AnnotateAttr>(A))
+ return AA->getAnnotation() == "rc_ownership_returns_not_retained";
+ return false;
+ }
+};
+
+struct GeneralizedConsumedAttr {
+ static bool classof(const Attr *A) {
+ if (auto AA = dyn_cast<AnnotateAttr>(A))
+ return AA->getAnnotation() == "rc_ownership_consumed";
+ return false;
+ }
+};
+
+}
+
+template <class T>
+Optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D,
+ QualType QT) {
+ ObjKind K;
+ if (isOneOf<T, CFConsumedAttr, CFReturnsRetainedAttr,
+ CFReturnsNotRetainedAttr>()) {
+ if (!TrackObjCAndCFObjects)
+ return None;
+
+ K = ObjKind::CF;
+ } else if (isOneOf<T, NSConsumedAttr, NSConsumesSelfAttr,
+ NSReturnsAutoreleasedAttr, NSReturnsRetainedAttr,
+ NSReturnsNotRetainedAttr, NSConsumesSelfAttr>()) {
+
+ if (!TrackObjCAndCFObjects)
+ return None;
+
+ if (isOneOf<T, NSReturnsRetainedAttr, NSReturnsAutoreleasedAttr,
+ NSReturnsNotRetainedAttr>() &&
+ !cocoa::isCocoaObjectRef(QT))
+ return None;
+ K = ObjKind::ObjC;
+ } else if (isOneOf<T, OSConsumedAttr, OSConsumesThisAttr,
+ OSReturnsNotRetainedAttr, OSReturnsRetainedAttr,
+ OSReturnsRetainedOnZeroAttr,
+ OSReturnsRetainedOnNonZeroAttr>()) {
+ if (!TrackOSObjects)
+ return None;
+ K = ObjKind::OS;
+ } else if (isOneOf<T, GeneralizedReturnsNotRetainedAttr,
+ GeneralizedReturnsRetainedAttr,
+ GeneralizedConsumedAttr>()) {
+ K = ObjKind::Generalized;
+ } else {
+ llvm_unreachable("Unexpected attribute");
+ }
+ if (D->hasAttr<T>())
+ return K;
+ return None;
+}
+
+template <class T1, class T2, class... Others>
+Optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D,
+ QualType QT) {
+ if (auto Out = hasAnyEnabledAttrOf<T1>(D, QT))
+ return Out;
+ return hasAnyEnabledAttrOf<T2, Others...>(D, QT);
+}
+
+const RetainSummary *
+RetainSummaryManager::getPersistentSummary(const RetainSummary &OldSumm) {
+ // Unique "simple" summaries -- those without ArgEffects.
+ if (OldSumm.isSimple()) {
+ ::llvm::FoldingSetNodeID ID;
+ OldSumm.Profile(ID);
+
+ void *Pos;
+ CachedSummaryNode *N = SimpleSummaries.FindNodeOrInsertPos(ID, Pos);
+
+ if (!N) {
+ N = (CachedSummaryNode *) BPAlloc.Allocate<CachedSummaryNode>();
+ new (N) CachedSummaryNode(OldSumm);
+ SimpleSummaries.InsertNode(N, Pos);
+ }
+
+ return &N->getValue();
+ }
+
+ RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>();
+ new (Summ) RetainSummary(OldSumm);
+ return Summ;
+}
+
+static bool isSubclass(const Decl *D,
+ StringRef ClassName) {
+ using namespace ast_matchers;
+ DeclarationMatcher SubclassM = cxxRecordDecl(isSameOrDerivedFrom(ClassName));
+ return !(match(SubclassM, *D, D->getASTContext()).empty());
+}
+
+static bool isOSObjectSubclass(const Decl *D) {
+ return D && isSubclass(D, "OSMetaClassBase");
+}
+
+static bool isOSObjectDynamicCast(StringRef S) {
+ return S == "safeMetaCast";
+}
+
+static bool isOSObjectRequiredCast(StringRef S) {
+ return S == "requiredMetaCast";
+}
+
+static bool isOSObjectThisCast(StringRef S) {
+ return S == "metaCast";
+}
+
+
+static bool isOSObjectPtr(QualType QT) {
+ return isOSObjectSubclass(QT->getPointeeCXXRecordDecl());
+}
+
+static bool isISLObjectRef(QualType Ty) {
+ return StringRef(Ty.getAsString()).startswith("isl_");
+}
+
+static bool isOSIteratorSubclass(const Decl *D) {
+ return isSubclass(D, "OSIterator");
+}
+
+static bool hasRCAnnotation(const Decl *D, StringRef rcAnnotation) {
+ for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) {
+ if (Ann->getAnnotation() == rcAnnotation)
+ return true;
+ }
+ return false;
+}
+
+static bool isRetain(const FunctionDecl *FD, StringRef FName) {
+ return FName.startswith_lower("retain") || FName.endswith_lower("retain");
+}
+
+static bool isRelease(const FunctionDecl *FD, StringRef FName) {
+ return FName.startswith_lower("release") || FName.endswith_lower("release");
+}
+
+static bool isAutorelease(const FunctionDecl *FD, StringRef FName) {
+ return FName.startswith_lower("autorelease") ||
+ FName.endswith_lower("autorelease");
+}
+
+static bool isMakeCollectable(StringRef FName) {
+ return FName.contains_lower("MakeCollectable");
+}
+
+/// A function is OSObject related if it is declared on a subclass
+/// of OSObject, or any of the parameters is a subclass of an OSObject.
+static bool isOSObjectRelated(const CXXMethodDecl *MD) {
+ if (isOSObjectSubclass(MD->getParent()))
+ return true;
+
+ for (ParmVarDecl *Param : MD->parameters()) {
+ QualType PT = Param->getType()->getPointeeType();
+ if (!PT.isNull())
+ if (CXXRecordDecl *RD = PT->getAsCXXRecordDecl())
+ if (isOSObjectSubclass(RD))
+ return true;
+ }
+
+ return false;
+}
+
+bool
+RetainSummaryManager::isKnownSmartPointer(QualType QT) {
+ QT = QT.getCanonicalType();
+ const auto *RD = QT->getAsCXXRecordDecl();
+ if (!RD)
+ return false;
+ const IdentifierInfo *II = RD->getIdentifier();
+ if (II && II->getName() == "smart_ptr")
+ if (const auto *ND = dyn_cast<NamespaceDecl>(RD->getDeclContext()))
+ if (ND->getNameAsString() == "os")
+ return true;
+ return false;
+}
+
+const RetainSummary *
+RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD,
+ StringRef FName, QualType RetTy) {
+ assert(TrackOSObjects &&
+ "Requesting a summary for an OSObject but OSObjects are not tracked");
+
+ if (RetTy->isPointerType()) {
+ const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl();
+ if (PD && isOSObjectSubclass(PD)) {
+ if (isOSObjectDynamicCast(FName) || isOSObjectRequiredCast(FName) ||
+ isOSObjectThisCast(FName))
+ return getDefaultSummary();
+
+ // TODO: Add support for the slightly common *Matching(table) idiom.
+ // Cf. IOService::nameMatching() etc. - these function have an unusual
+ // contract of returning at +0 or +1 depending on their last argument.
+ if (FName.endswith("Matching")) {
+ return getPersistentStopSummary();
+ }
+
+ // All objects returned with functions *not* starting with 'get',
+ // or iterators, are returned at +1.
+ if ((!FName.startswith("get") && !FName.startswith("Get")) ||
+ isOSIteratorSubclass(PD)) {
+ return getOSSummaryCreateRule(FD);
+ } else {
+ return getOSSummaryGetRule(FD);
+ }
+ }
+ }
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ const CXXRecordDecl *Parent = MD->getParent();
+ if (Parent && isOSObjectSubclass(Parent)) {
+ if (FName == "release" || FName == "taggedRelease")
+ return getOSSummaryReleaseRule(FD);
+
+ if (FName == "retain" || FName == "taggedRetain")
+ return getOSSummaryRetainRule(FD);
+
+ if (FName == "free")
+ return getOSSummaryFreeRule(FD);
+
+ if (MD->getOverloadedOperator() == OO_New)
+ return getOSSummaryCreateRule(MD);
+ }
+ }
+
+ return nullptr;
+}
+
+const RetainSummary *RetainSummaryManager::getSummaryForObjCOrCFObject(
+ const FunctionDecl *FD,
+ StringRef FName,
+ QualType RetTy,
+ const FunctionType *FT,
+ bool &AllowAnnotations) {
+
+ ArgEffects ScratchArgs(AF.getEmptyMap());
+
+ std::string RetTyName = RetTy.getAsString();
+ if (FName == "pthread_create" || FName == "pthread_setspecific") {
+ // Part of: <rdar://problem/7299394> and <rdar://problem/11282706>.
+ // This will be addressed better with IPA.
+ return getPersistentStopSummary();
+ } else if(FName == "NSMakeCollectable") {
+ // Handle: id NSMakeCollectable(CFTypeRef)
+ AllowAnnotations = false;
+ return RetTy->isObjCIdType() ? getUnarySummary(FT, DoNothing)
+ : getPersistentStopSummary();
+ } else if (FName == "CMBufferQueueDequeueAndRetain" ||
+ FName == "CMBufferQueueDequeueIfDataReadyAndRetain") {
+ // Part of: <rdar://problem/39390714>.
+ return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF),
+ ScratchArgs,
+ ArgEffect(DoNothing),
+ ArgEffect(DoNothing));
+ } else if (FName == "CFPlugInInstanceCreate") {
+ return getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs);
+ } else if (FName == "IORegistryEntrySearchCFProperty" ||
+ (RetTyName == "CFMutableDictionaryRef" &&
+ (FName == "IOBSDNameMatching" || FName == "IOServiceMatching" ||
+ FName == "IOServiceNameMatching" ||
+ FName == "IORegistryEntryIDMatching" ||
+ FName == "IOOpenFirmwarePathMatching"))) {
+ // Part of <rdar://problem/6961230>. (IOKit)
+ // This should be addressed using a API table.
+ return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "IOServiceGetMatchingService" ||
+ FName == "IOServiceGetMatchingServices") {
+ // FIXES: <rdar://problem/6326900>
+ // This should be addressed using a API table. This strcmp is also
+ // a little gross, but there is no need to super optimize here.
+ ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(DecRef, ObjKind::CF));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "IOServiceAddNotification" ||
+ FName == "IOServiceAddMatchingNotification") {
+ // Part of <rdar://problem/6961230>. (IOKit)
+ // This should be addressed using a API table.
+ ScratchArgs = AF.add(ScratchArgs, 2, ArgEffect(DecRef, ObjKind::CF));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "CVPixelBufferCreateWithBytes") {
+ // FIXES: <rdar://problem/7283567>
+ // Eventually this can be improved by recognizing that the pixel
+ // buffer passed to CVPixelBufferCreateWithBytes is released via
+ // a callback and doing full IPA to make sure this is done correctly.
+ // FIXME: This function has an out parameter that returns an
+ // allocated object.
+ ScratchArgs = AF.add(ScratchArgs, 7, ArgEffect(StopTracking));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "CGBitmapContextCreateWithData") {
+ // FIXES: <rdar://problem/7358899>
+ // Eventually this can be improved by recognizing that 'releaseInfo'
+ // passed to CGBitmapContextCreateWithData is released via
+ // a callback and doing full IPA to make sure this is done correctly.
+ ScratchArgs = AF.add(ScratchArgs, 8, ArgEffect(ArgEffect(StopTracking)));
+ return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "CVPixelBufferCreateWithPlanarBytes") {
+ // FIXES: <rdar://problem/7283567>
+ // Eventually this can be improved by recognizing that the pixel
+ // buffer passed to CVPixelBufferCreateWithPlanarBytes is released
+ // via a callback and doing full IPA to make sure this is done
+ // correctly.
+ ScratchArgs = AF.add(ScratchArgs, 12, ArgEffect(StopTracking));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "VTCompressionSessionEncodeFrame") {
+ // The context argument passed to VTCompressionSessionEncodeFrame()
+ // is passed to the callback specified when creating the session
+ // (e.g. with VTCompressionSessionCreate()) which can release it.
+ // To account for this possibility, conservatively stop tracking
+ // the context.
+ ScratchArgs = AF.add(ScratchArgs, 5, ArgEffect(StopTracking));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "dispatch_set_context" ||
+ FName == "xpc_connection_set_context") {
+ // <rdar://problem/11059275> - The analyzer currently doesn't have
+ // a good way to reason about the finalizer function for libdispatch.
+ // If we pass a context object that is memory managed, stop tracking it.
+ // <rdar://problem/13783514> - Same problem, but for XPC.
+ // FIXME: this hack should possibly go away once we can handle
+ // libdispatch and XPC finalizers.
+ ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(StopTracking));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName.startswith("NSLog")) {
+ return getDoNothingSummary();
+ } else if (FName.startswith("NS") &&
+ (FName.find("Insert") != StringRef::npos)) {
+ // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
+ // be deallocated by NSMapRemove. (radar://11152419)
+ ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(StopTracking));
+ ScratchArgs = AF.add(ScratchArgs, 2, ArgEffect(StopTracking));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs, ArgEffect(DoNothing),
+ ArgEffect(DoNothing));
+ }
+
+ if (RetTy->isPointerType()) {
+
+ // For CoreFoundation ('CF') types.
+ if (cocoa::isRefType(RetTy, "CF", FName)) {
+ if (isRetain(FD, FName)) {
+ // CFRetain isn't supposed to be annotated. However, this may as
+ // well be a user-made "safe" CFRetain function that is incorrectly
+ // annotated as cf_returns_retained due to lack of better options.
+ // We want to ignore such annotation.
+ AllowAnnotations = false;
+
+ return getUnarySummary(FT, IncRef);
+ } else if (isAutorelease(FD, FName)) {
+ // The headers use cf_consumed, but we can fully model CFAutorelease
+ // ourselves.
+ AllowAnnotations = false;
+
+ return getUnarySummary(FT, Autorelease);
+ } else if (isMakeCollectable(FName)) {
+ AllowAnnotations = false;
+ return getUnarySummary(FT, DoNothing);
+ } else {
+ return getCFCreateGetRuleSummary(FD);
+ }
+ }
+
+ // For CoreGraphics ('CG') and CoreVideo ('CV') types.
+ if (cocoa::isRefType(RetTy, "CG", FName) ||
+ cocoa::isRefType(RetTy, "CV", FName)) {
+ if (isRetain(FD, FName))
+ return getUnarySummary(FT, IncRef);
+ else
+ return getCFCreateGetRuleSummary(FD);
+ }
+
+ // For all other CF-style types, use the Create/Get
+ // rule for summaries but don't support Retain functions
+ // with framework-specific prefixes.
+ if (coreFoundation::isCFObjectRef(RetTy)) {
+ return getCFCreateGetRuleSummary(FD);
+ }
+
+ if (FD->hasAttr<CFAuditedTransferAttr>()) {
+ return getCFCreateGetRuleSummary(FD);
+ }
+ }
+
+ // Check for release functions, the only kind of functions that we care
+ // about that don't return a pointer type.
+ if (FName.startswith("CG") || FName.startswith("CF")) {
+ // Test for 'CGCF'.
+ FName = FName.substr(FName.startswith("CGCF") ? 4 : 2);
+
+ if (isRelease(FD, FName))
+ return getUnarySummary(FT, DecRef);
+ else {
+ assert(ScratchArgs.isEmpty());
+ // Remaining CoreFoundation and CoreGraphics functions.
+ // We use to assume that they all strictly followed the ownership idiom
+ // and that ownership cannot be transferred. While this is technically
+ // correct, many methods allow a tracked object to escape. For example:
+ //
+ // CFMutableDictionaryRef x = CFDictionaryCreateMutable(...);
+ // CFDictionaryAddValue(y, key, x);
+ // CFRelease(x);
+ // ... it is okay to use 'x' since 'y' has a reference to it
+ //
+ // We handle this and similar cases with the follow heuristic. If the
+ // function name contains "InsertValue", "SetValue", "AddValue",
+ // "AppendValue", or "SetAttribute", then we assume that arguments may
+ // "escape." This means that something else holds on to the object,
+ // allowing it be used even after its local retain count drops to 0.
+ ArgEffectKind E =
+ (StrInStrNoCase(FName, "InsertValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "AppendValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "SetAttribute") != StringRef::npos)
+ ? MayEscape
+ : DoNothing;
+
+ return getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(E, ObjKind::CF));
+ }
+ }
+
+ return nullptr;
+}
+
+const RetainSummary *
+RetainSummaryManager::generateSummary(const FunctionDecl *FD,
+ bool &AllowAnnotations) {
+ // We generate "stop" summaries for implicitly defined functions.
+ if (FD->isImplicit())
+ return getPersistentStopSummary();
+
+ const IdentifierInfo *II = FD->getIdentifier();
+
+ StringRef FName = II ? II->getName() : "";
+
+ // Strip away preceding '_'. Doing this here will effect all the checks
+ // down below.
+ FName = FName.substr(FName.find_first_not_of('_'));
+
+ // Inspect the result type. Strip away any typedefs.
+ const auto *FT = FD->getType()->getAs<FunctionType>();
+ QualType RetTy = FT->getReturnType();
+
+ if (TrackOSObjects)
+ if (const RetainSummary *S = getSummaryForOSObject(FD, FName, RetTy))
+ return S;
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
+ if (!isOSObjectRelated(MD))
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ArgEffects(AF.getEmptyMap()),
+ ArgEffect(DoNothing),
+ ArgEffect(StopTracking),
+ ArgEffect(DoNothing));
+
+ if (TrackObjCAndCFObjects)
+ if (const RetainSummary *S =
+ getSummaryForObjCOrCFObject(FD, FName, RetTy, FT, AllowAnnotations))
+ return S;
+
+ return getDefaultSummary();
+}
+
+const RetainSummary *
+RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
+ // If we don't know what function we're calling, use our default summary.
+ if (!FD)
+ return getDefaultSummary();
+
+ // Look up a summary in our cache of FunctionDecls -> Summaries.
+ FuncSummariesTy::iterator I = FuncSummaries.find(FD);
+ if (I != FuncSummaries.end())
+ return I->second;
+
+ // No summary? Generate one.
+ bool AllowAnnotations = true;
+ const RetainSummary *S = generateSummary(FD, AllowAnnotations);
+
+ // Annotations override defaults.
+ if (AllowAnnotations)
+ updateSummaryFromAnnotations(S, FD);
+
+ FuncSummaries[FD] = S;
+ return S;
+}
+
+//===----------------------------------------------------------------------===//
+// Summary creation for functions (largely uses of Core Foundation).
+//===----------------------------------------------------------------------===//
+
+static ArgEffect getStopTrackingHardEquivalent(ArgEffect E) {
+ switch (E.getKind()) {
+ case DoNothing:
+ case Autorelease:
+ case DecRefBridgedTransferred:
+ case IncRef:
+ case UnretainedOutParameter:
+ case RetainedOutParameter:
+ case RetainedOutParameterOnZero:
+ case RetainedOutParameterOnNonZero:
+ case MayEscape:
+ case StopTracking:
+ case StopTrackingHard:
+ return E.withKind(StopTrackingHard);
+ case DecRef:
+ case DecRefAndStopTrackingHard:
+ return E.withKind(DecRefAndStopTrackingHard);
+ case Dealloc:
+ return E.withKind(Dealloc);
+ }
+
+ llvm_unreachable("Unknown ArgEffect kind");
+}
+
+const RetainSummary *
+RetainSummaryManager::updateSummaryForNonZeroCallbackArg(const RetainSummary *S,
+ AnyCall &C) {
+ ArgEffect RecEffect = getStopTrackingHardEquivalent(S->getReceiverEffect());
+ ArgEffect DefEffect = getStopTrackingHardEquivalent(S->getDefaultArgEffect());
+
+ ArgEffects ScratchArgs(AF.getEmptyMap());
+ ArgEffects CustomArgEffects = S->getArgEffects();
+ for (ArgEffects::iterator I = CustomArgEffects.begin(),
+ E = CustomArgEffects.end();
+ I != E; ++I) {
+ ArgEffect Translated = getStopTrackingHardEquivalent(I->second);
+ if (Translated.getKind() != DefEffect.getKind())
+ ScratchArgs = AF.add(ScratchArgs, I->first, Translated);
+ }
+
+ RetEffect RE = RetEffect::MakeNoRetHard();
+
+ // Special cases where the callback argument CANNOT free the return value.
+ // This can generally only happen if we know that the callback will only be
+ // called when the return value is already being deallocated.
+ if (const IdentifierInfo *Name = C.getIdentifier()) {
+ // When the CGBitmapContext is deallocated, the callback here will free
+ // the associated data buffer.
+ // The callback in dispatch_data_create frees the buffer, but not
+ // the data object.
+ if (Name->isStr("CGBitmapContextCreateWithData") ||
+ Name->isStr("dispatch_data_create"))
+ RE = S->getRetEffect();
+ }
+
+ return getPersistentSummary(RE, ScratchArgs, RecEffect, DefEffect);
+}
+
+void RetainSummaryManager::updateSummaryForReceiverUnconsumedSelf(
+ const RetainSummary *&S) {
+
+ RetainSummaryTemplate Template(S, *this);
+
+ Template->setReceiverEffect(ArgEffect(DoNothing));
+ Template->setRetEffect(RetEffect::MakeNoRet());
+}
+
+
+void RetainSummaryManager::updateSummaryForArgumentTypes(
+ const AnyCall &C, const RetainSummary *&RS) {
+ RetainSummaryTemplate Template(RS, *this);
+
+ unsigned parm_idx = 0;
+ for (auto pi = C.param_begin(), pe = C.param_end(); pi != pe;
+ ++pi, ++parm_idx) {
+ QualType QT = (*pi)->getType();
+
+ // Skip already created values.
+ if (RS->getArgEffects().contains(parm_idx))
+ continue;
+
+ ObjKind K = ObjKind::AnyObj;
+
+ if (isISLObjectRef(QT)) {
+ K = ObjKind::Generalized;
+ } else if (isOSObjectPtr(QT)) {
+ K = ObjKind::OS;
+ } else if (cocoa::isCocoaObjectRef(QT)) {
+ K = ObjKind::ObjC;
+ } else if (coreFoundation::isCFObjectRef(QT)) {
+ K = ObjKind::CF;
+ }
+
+ if (K != ObjKind::AnyObj)
+ Template->addArg(AF, parm_idx,
+ ArgEffect(RS->getDefaultArgEffect().getKind(), K));
+ }
+}
+
+const RetainSummary *
+RetainSummaryManager::getSummary(AnyCall C,
+ bool HasNonZeroCallbackArg,
+ bool IsReceiverUnconsumedSelf,
+ QualType ReceiverType) {
+ const RetainSummary *Summ;
+ switch (C.getKind()) {
+ case AnyCall::Function:
+ case AnyCall::Constructor:
+ case AnyCall::Allocator:
+ case AnyCall::Deallocator:
+ Summ = getFunctionSummary(cast_or_null<FunctionDecl>(C.getDecl()));
+ break;
+ case AnyCall::Block:
+ case AnyCall::Destructor:
+ // FIXME: These calls are currently unsupported.
+ return getPersistentStopSummary();
+ case AnyCall::ObjCMethod: {
+ const auto *ME = cast_or_null<ObjCMessageExpr>(C.getExpr());
+ if (!ME) {
+ Summ = getMethodSummary(cast<ObjCMethodDecl>(C.getDecl()));
+ } else if (ME->isInstanceMessage()) {
+ Summ = getInstanceMethodSummary(ME, ReceiverType);
+ } else {
+ Summ = getClassMethodSummary(ME);
+ }
+ break;
+ }
+ }
+
+ if (HasNonZeroCallbackArg)
+ Summ = updateSummaryForNonZeroCallbackArg(Summ, C);
+
+ if (IsReceiverUnconsumedSelf)
+ updateSummaryForReceiverUnconsumedSelf(Summ);
+
+ updateSummaryForArgumentTypes(C, Summ);
+
+ assert(Summ && "Unknown call type?");
+ return Summ;
+}
+
+
+const RetainSummary *
+RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) {
+ if (coreFoundation::followsCreateRule(FD))
+ return getCFSummaryCreateRule(FD);
+
+ return getCFSummaryGetRule(FD);
+}
+
+bool RetainSummaryManager::isTrustedReferenceCountImplementation(
+ const Decl *FD) {
+ return hasRCAnnotation(FD, "rc_ownership_trusted_implementation");
+}
+
+Optional<RetainSummaryManager::BehaviorSummary>
+RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD,
+ bool &hasTrustedImplementationAnnotation) {
+
+ IdentifierInfo *II = FD->getIdentifier();
+ if (!II)
+ return None;
+
+ StringRef FName = II->getName();
+ FName = FName.substr(FName.find_first_not_of('_'));
+
+ QualType ResultTy = CE->getCallReturnType(Ctx);
+ if (ResultTy->isObjCIdType()) {
+ if (II->isStr("NSMakeCollectable"))
+ return BehaviorSummary::Identity;
+ } else if (ResultTy->isPointerType()) {
+ // Handle: (CF|CG|CV)Retain
+ // CFAutorelease
+ // It's okay to be a little sloppy here.
+ if (FName == "CMBufferQueueDequeueAndRetain" ||
+ FName == "CMBufferQueueDequeueIfDataReadyAndRetain") {
+ // Part of: <rdar://problem/39390714>.
+ // These are not retain. They just return something and retain it.
+ return None;
+ }
+ if (CE->getNumArgs() == 1 &&
+ (cocoa::isRefType(ResultTy, "CF", FName) ||
+ cocoa::isRefType(ResultTy, "CG", FName) ||
+ cocoa::isRefType(ResultTy, "CV", FName)) &&
+ (isRetain(FD, FName) || isAutorelease(FD, FName) ||
+ isMakeCollectable(FName)))
+ return BehaviorSummary::Identity;
+
+ // safeMetaCast is called by OSDynamicCast.
+ // We assume that OSDynamicCast is either an identity (cast is OK,
+ // the input was non-zero),
+ // or that it returns zero (when the cast failed, or the input
+ // was zero).
+ if (TrackOSObjects) {
+ if (isOSObjectDynamicCast(FName) && FD->param_size() >= 1) {
+ return BehaviorSummary::IdentityOrZero;
+ } else if (isOSObjectRequiredCast(FName) && FD->param_size() >= 1) {
+ return BehaviorSummary::Identity;
+ } else if (isOSObjectThisCast(FName) && isa<CXXMethodDecl>(FD) &&
+ !cast<CXXMethodDecl>(FD)->isStatic()) {
+ return BehaviorSummary::IdentityThis;
+ }
+ }
+
+ const FunctionDecl* FDD = FD->getDefinition();
+ if (FDD && isTrustedReferenceCountImplementation(FDD)) {
+ hasTrustedImplementationAnnotation = true;
+ return BehaviorSummary::Identity;
+ }
+ }
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ const CXXRecordDecl *Parent = MD->getParent();
+ if (TrackOSObjects && Parent && isOSObjectSubclass(Parent))
+ if (FName == "release" || FName == "retain")
+ return BehaviorSummary::NoOp;
+ }
+
+ return None;
+}
+
+const RetainSummary *
+RetainSummaryManager::getUnarySummary(const FunctionType* FT,
+ ArgEffectKind AE) {
+
+ // Unary functions have no arg effects by definition.
+ ArgEffects ScratchArgs(AF.getEmptyMap());
+
+ // Sanity check that this is *really* a unary function. This can
+ // happen if people do weird things.
+ const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(FT);
+ if (!FTP || FTP->getNumParams() != 1)
+ return getPersistentStopSummary();
+
+ ArgEffect Effect(AE, ObjKind::CF);
+
+ ScratchArgs = AF.add(ScratchArgs, 0, Effect);
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryRetainRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ AF.getEmptyMap(),
+ /*ReceiverEff=*/ArgEffect(DoNothing),
+ /*DefaultEff=*/ArgEffect(DoNothing),
+ /*ThisEff=*/ArgEffect(IncRef, ObjKind::OS));
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryReleaseRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ AF.getEmptyMap(),
+ /*ReceiverEff=*/ArgEffect(DoNothing),
+ /*DefaultEff=*/ArgEffect(DoNothing),
+ /*ThisEff=*/ArgEffect(DecRef, ObjKind::OS));
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryFreeRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ AF.getEmptyMap(),
+ /*ReceiverEff=*/ArgEffect(DoNothing),
+ /*DefaultEff=*/ArgEffect(DoNothing),
+ /*ThisEff=*/ArgEffect(Dealloc, ObjKind::OS));
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryCreateRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeOwned(ObjKind::OS),
+ AF.getEmptyMap());
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryGetRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::OS),
+ AF.getEmptyMap());
+}
+
+const RetainSummary *
+RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF),
+ ArgEffects(AF.getEmptyMap()));
+}
+
+const RetainSummary *
+RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::CF),
+ ArgEffects(AF.getEmptyMap()),
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+}
+
+
+
+
+//===----------------------------------------------------------------------===//
+// Summary creation for Selectors.
+//===----------------------------------------------------------------------===//
+
+Optional<RetEffect>
+RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy,
+ const Decl *D) {
+ if (hasAnyEnabledAttrOf<NSReturnsRetainedAttr>(D, RetTy))
+ return ObjCAllocRetE;
+
+ if (auto K = hasAnyEnabledAttrOf<CFReturnsRetainedAttr, OSReturnsRetainedAttr,
+ GeneralizedReturnsRetainedAttr>(D, RetTy))
+ return RetEffect::MakeOwned(*K);
+
+ if (auto K = hasAnyEnabledAttrOf<
+ CFReturnsNotRetainedAttr, OSReturnsNotRetainedAttr,
+ GeneralizedReturnsNotRetainedAttr, NSReturnsNotRetainedAttr,
+ NSReturnsAutoreleasedAttr>(D, RetTy))
+ return RetEffect::MakeNotOwned(*K);
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D))
+ for (const auto *PD : MD->overridden_methods())
+ if (auto RE = getRetEffectFromAnnotations(RetTy, PD))
+ return RE;
+
+ return None;
+}
+
+/// \return Whether the chain of typedefs starting from {@code QT}
+/// has a typedef with a given name {@code Name}.
+static bool hasTypedefNamed(QualType QT,
+ StringRef Name) {
+ while (auto *T = dyn_cast<TypedefType>(QT)) {
+ const auto &Context = T->getDecl()->getASTContext();
+ if (T->getDecl()->getIdentifier() == &Context.Idents.get(Name))
+ return true;
+ QT = T->getDecl()->getUnderlyingType();
+ }
+ return false;
+}
+
+static QualType getCallableReturnType(const NamedDecl *ND) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
+ return FD->getReturnType();
+ } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(ND)) {
+ return MD->getReturnType();
+ } else {
+ llvm_unreachable("Unexpected decl");
+ }
+}
+
+bool RetainSummaryManager::applyParamAnnotationEffect(
+ const ParmVarDecl *pd, unsigned parm_idx, const NamedDecl *FD,
+ RetainSummaryTemplate &Template) {
+ QualType QT = pd->getType();
+ if (auto K =
+ hasAnyEnabledAttrOf<NSConsumedAttr, CFConsumedAttr, OSConsumedAttr,
+ GeneralizedConsumedAttr>(pd, QT)) {
+ Template->addArg(AF, parm_idx, ArgEffect(DecRef, *K));
+ return true;
+ } else if (auto K = hasAnyEnabledAttrOf<
+ CFReturnsRetainedAttr, OSReturnsRetainedAttr,
+ OSReturnsRetainedOnNonZeroAttr, OSReturnsRetainedOnZeroAttr,
+ GeneralizedReturnsRetainedAttr>(pd, QT)) {
+
+ // For OSObjects, we try to guess whether the object is created based
+ // on the return value.
+ if (K == ObjKind::OS) {
+ QualType QT = getCallableReturnType(FD);
+
+ bool HasRetainedOnZero = pd->hasAttr<OSReturnsRetainedOnZeroAttr>();
+ bool HasRetainedOnNonZero = pd->hasAttr<OSReturnsRetainedOnNonZeroAttr>();
+
+ // The usual convention is to create an object on non-zero return, but
+ // it's reverted if the typedef chain has a typedef kern_return_t,
+ // because kReturnSuccess constant is defined as zero.
+ // The convention can be overwritten by custom attributes.
+ bool SuccessOnZero =
+ HasRetainedOnZero ||
+ (hasTypedefNamed(QT, "kern_return_t") && !HasRetainedOnNonZero);
+ bool ShouldSplit = !QT.isNull() && !QT->isVoidType();
+ ArgEffectKind AK = RetainedOutParameter;
+ if (ShouldSplit && SuccessOnZero) {
+ AK = RetainedOutParameterOnZero;
+ } else if (ShouldSplit && (!SuccessOnZero || HasRetainedOnNonZero)) {
+ AK = RetainedOutParameterOnNonZero;
+ }
+ Template->addArg(AF, parm_idx, ArgEffect(AK, ObjKind::OS));
+ }
+
+ // For others:
+ // Do nothing. Retained out parameters will either point to a +1 reference
+ // or NULL, but the way you check for failure differs depending on the
+ // API. Consequently, we don't have a good way to track them yet.
+ return true;
+ } else if (auto K = hasAnyEnabledAttrOf<CFReturnsNotRetainedAttr,
+ OSReturnsNotRetainedAttr,
+ GeneralizedReturnsNotRetainedAttr>(
+ pd, QT)) {
+ Template->addArg(AF, parm_idx, ArgEffect(UnretainedOutParameter, *K));
+ return true;
+ }
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ for (const auto *OD : MD->overridden_methods()) {
+ const ParmVarDecl *OP = OD->parameters()[parm_idx];
+ if (applyParamAnnotationEffect(OP, parm_idx, OD, Template))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
+ const FunctionDecl *FD) {
+ if (!FD)
+ return;
+
+ assert(Summ && "Must have a summary to add annotations to.");
+ RetainSummaryTemplate Template(Summ, *this);
+
+ // Effects on the parameters.
+ unsigned parm_idx = 0;
+ for (auto pi = FD->param_begin(),
+ pe = FD->param_end(); pi != pe; ++pi, ++parm_idx)
+ applyParamAnnotationEffect(*pi, parm_idx, FD, Template);
+
+ QualType RetTy = FD->getReturnType();
+ if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, FD))
+ Template->setRetEffect(*RetE);
+
+ if (hasAnyEnabledAttrOf<OSConsumesThisAttr>(FD, RetTy))
+ Template->setThisEffect(ArgEffect(DecRef, ObjKind::OS));
+}
+
+void
+RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
+ const ObjCMethodDecl *MD) {
+ if (!MD)
+ return;
+
+ assert(Summ && "Must have a valid summary to add annotations to");
+ RetainSummaryTemplate Template(Summ, *this);
+
+ // Effects on the receiver.
+ if (hasAnyEnabledAttrOf<NSConsumesSelfAttr>(MD, MD->getReturnType()))
+ Template->setReceiverEffect(ArgEffect(DecRef, ObjKind::ObjC));
+
+ // Effects on the parameters.
+ unsigned parm_idx = 0;
+ for (auto pi = MD->param_begin(), pe = MD->param_end(); pi != pe;
+ ++pi, ++parm_idx)
+ applyParamAnnotationEffect(*pi, parm_idx, MD, Template);
+
+ QualType RetTy = MD->getReturnType();
+ if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, MD))
+ Template->setRetEffect(*RetE);
+}
+
+const RetainSummary *
+RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD,
+ Selector S, QualType RetTy) {
+ // Any special effects?
+ ArgEffect ReceiverEff = ArgEffect(DoNothing, ObjKind::ObjC);
+ RetEffect ResultEff = RetEffect::MakeNoRet();
+
+ // Check the method family, and apply any default annotations.
+ switch (MD ? MD->getMethodFamily() : S.getMethodFamily()) {
+ case OMF_None:
+ case OMF_initialize:
+ case OMF_performSelector:
+ // Assume all Objective-C methods follow Cocoa Memory Management rules.
+ // FIXME: Does the non-threaded performSelector family really belong here?
+ // The selector could be, say, @selector(copy).
+ if (cocoa::isCocoaObjectRef(RetTy))
+ ResultEff = RetEffect::MakeNotOwned(ObjKind::ObjC);
+ else if (coreFoundation::isCFObjectRef(RetTy)) {
+ // ObjCMethodDecl currently doesn't consider CF objects as valid return
+ // values for alloc, new, copy, or mutableCopy, so we have to
+ // double-check with the selector. This is ugly, but there aren't that
+ // many Objective-C methods that return CF objects, right?
+ if (MD) {
+ switch (S.getMethodFamily()) {
+ case OMF_alloc:
+ case OMF_new:
+ case OMF_copy:
+ case OMF_mutableCopy:
+ ResultEff = RetEffect::MakeOwned(ObjKind::CF);
+ break;
+ default:
+ ResultEff = RetEffect::MakeNotOwned(ObjKind::CF);
+ break;
+ }
+ } else {
+ ResultEff = RetEffect::MakeNotOwned(ObjKind::CF);
+ }
+ }
+ break;
+ case OMF_init:
+ ResultEff = ObjCInitRetE;
+ ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC);
+ break;
+ case OMF_alloc:
+ case OMF_new:
+ case OMF_copy:
+ case OMF_mutableCopy:
+ if (cocoa::isCocoaObjectRef(RetTy))
+ ResultEff = ObjCAllocRetE;
+ else if (coreFoundation::isCFObjectRef(RetTy))
+ ResultEff = RetEffect::MakeOwned(ObjKind::CF);
+ break;
+ case OMF_autorelease:
+ ReceiverEff = ArgEffect(Autorelease, ObjKind::ObjC);
+ break;
+ case OMF_retain:
+ ReceiverEff = ArgEffect(IncRef, ObjKind::ObjC);
+ break;
+ case OMF_release:
+ ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC);
+ break;
+ case OMF_dealloc:
+ ReceiverEff = ArgEffect(Dealloc, ObjKind::ObjC);
+ break;
+ case OMF_self:
+ // -self is handled specially by the ExprEngine to propagate the receiver.
+ break;
+ case OMF_retainCount:
+ case OMF_finalize:
+ // These methods don't return objects.
+ break;
+ }
+
+ // If one of the arguments in the selector has the keyword 'delegate' we
+ // should stop tracking the reference count for the receiver. This is
+ // because the reference count is quite possibly handled by a delegate
+ // method.
+ if (S.isKeywordSelector()) {
+ for (unsigned i = 0, e = S.getNumArgs(); i != e; ++i) {
+ StringRef Slot = S.getNameForSlot(i);
+ if (Slot.substr(Slot.size() - 8).equals_lower("delegate")) {
+ if (ResultEff == ObjCInitRetE)
+ ResultEff = RetEffect::MakeNoRetHard();
+ else
+ ReceiverEff = ArgEffect(StopTrackingHard, ObjKind::ObjC);
+ }
+ }
+ }
+
+ if (ReceiverEff.getKind() == DoNothing &&
+ ResultEff.getKind() == RetEffect::NoRet)
+ return getDefaultSummary();
+
+ return getPersistentSummary(ResultEff, ArgEffects(AF.getEmptyMap()),
+ ArgEffect(ReceiverEff), ArgEffect(MayEscape));
+}
+
+const RetainSummary *
+RetainSummaryManager::getClassMethodSummary(const ObjCMessageExpr *ME) {
+ assert(!ME->isInstanceMessage());
+ const ObjCInterfaceDecl *Class = ME->getReceiverInterface();
+
+ return getMethodSummary(ME->getSelector(), Class, ME->getMethodDecl(),
+ ME->getType(), ObjCClassMethodSummaries);
+}
+
+const RetainSummary *RetainSummaryManager::getInstanceMethodSummary(
+ const ObjCMessageExpr *ME,
+ QualType ReceiverType) {
+ const ObjCInterfaceDecl *ReceiverClass = nullptr;
+
+ // We do better tracking of the type of the object than the core ExprEngine.
+ // See if we have its type in our private state.
+ if (!ReceiverType.isNull())
+ if (const auto *PT = ReceiverType->getAs<ObjCObjectPointerType>())
+ ReceiverClass = PT->getInterfaceDecl();
+
+ // If we don't know what kind of object this is, fall back to its static type.
+ if (!ReceiverClass)
+ ReceiverClass = ME->getReceiverInterface();
+
+ // FIXME: The receiver could be a reference to a class, meaning that
+ // we should use the class method.
+ // id x = [NSObject class];
+ // [x performSelector:... withObject:... afterDelay:...];
+ Selector S = ME->getSelector();
+ const ObjCMethodDecl *Method = ME->getMethodDecl();
+ if (!Method && ReceiverClass)
+ Method = ReceiverClass->getInstanceMethod(S);
+
+ return getMethodSummary(S, ReceiverClass, Method, ME->getType(),
+ ObjCMethodSummaries);
+}
+
+const RetainSummary *
+RetainSummaryManager::getMethodSummary(Selector S,
+ const ObjCInterfaceDecl *ID,
+ const ObjCMethodDecl *MD, QualType RetTy,
+ ObjCMethodSummariesTy &CachedSummaries) {
+
+ // Objective-C method summaries are only applicable to ObjC and CF objects.
+ if (!TrackObjCAndCFObjects)
+ return getDefaultSummary();
+
+ // Look up a summary in our summary cache.
+ const RetainSummary *Summ = CachedSummaries.find(ID, S);
+
+ if (!Summ) {
+ Summ = getStandardMethodSummary(MD, S, RetTy);
+
+ // Annotations override defaults.
+ updateSummaryFromAnnotations(Summ, MD);
+
+ // Memoize the summary.
+ CachedSummaries[ObjCSummaryKey(ID, S)] = Summ;
+ }
+
+ return Summ;
+}
+
+void RetainSummaryManager::InitializeClassMethodSummaries() {
+ ArgEffects ScratchArgs = AF.getEmptyMap();
+
+ // Create the [NSAssertionHandler currentHander] summary.
+ addClassMethSummary("NSAssertionHandler", "currentHandler",
+ getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::ObjC),
+ ScratchArgs));
+
+ // Create the [NSAutoreleasePool addObject:] summary.
+ ScratchArgs = AF.add(ScratchArgs, 0, ArgEffect(Autorelease));
+ addClassMethSummary("NSAutoreleasePool", "addObject",
+ getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs,
+ ArgEffect(DoNothing),
+ ArgEffect(Autorelease)));
+}
+
+void RetainSummaryManager::InitializeMethodSummaries() {
+
+ ArgEffects ScratchArgs = AF.getEmptyMap();
+ // Create the "init" selector. It just acts as a pass-through for the
+ // receiver.
+ const RetainSummary *InitSumm = getPersistentSummary(
+ ObjCInitRetE, ScratchArgs, ArgEffect(DecRef, ObjKind::ObjC));
+ addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm);
+
+ // awakeAfterUsingCoder: behaves basically like an 'init' method. It
+ // claims the receiver and returns a retained object.
+ addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx),
+ InitSumm);
+
+ // The next methods are allocators.
+ const RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE,
+ ScratchArgs);
+ const RetainSummary *CFAllocSumm =
+ getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs);
+
+ // Create the "retain" selector.
+ RetEffect NoRet = RetEffect::MakeNoRet();
+ const RetainSummary *Summ = getPersistentSummary(
+ NoRet, ScratchArgs, ArgEffect(IncRef, ObjKind::ObjC));
+ addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ);
+
+ // Create the "release" selector.
+ Summ = getPersistentSummary(NoRet, ScratchArgs,
+ ArgEffect(DecRef, ObjKind::ObjC));
+ addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ);
+
+ // Create the -dealloc summary.
+ Summ = getPersistentSummary(NoRet, ScratchArgs, ArgEffect(Dealloc,
+ ObjKind::ObjC));
+ addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ);
+
+ // Create the "autorelease" selector.
+ Summ = getPersistentSummary(NoRet, ScratchArgs, ArgEffect(Autorelease,
+ ObjKind::ObjC));
+ addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ);
+
+ // For NSWindow, allocated objects are (initially) self-owned.
+ // FIXME: For now we opt for false negatives with NSWindow, as these objects
+ // self-own themselves. However, they only do this once they are displayed.
+ // Thus, we need to track an NSWindow's display status.
+ // This is tracked in <rdar://problem/6062711>.
+ // See also http://llvm.org/bugs/show_bug.cgi?id=3714.
+ const RetainSummary *NoTrackYet =
+ getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs,
+ ArgEffect(StopTracking), ArgEffect(StopTracking));
+
+ addClassMethSummary("NSWindow", "alloc", NoTrackYet);
+
+ // For NSPanel (which subclasses NSWindow), allocated objects are not
+ // self-owned.
+ // FIXME: For now we don't track NSPanels. object for the same reason
+ // as for NSWindow objects.
+ addClassMethSummary("NSPanel", "alloc", NoTrackYet);
+
+ // For NSNull, objects returned by +null are singletons that ignore
+ // retain/release semantics. Just don't track them.
+ // <rdar://problem/12858915>
+ addClassMethSummary("NSNull", "null", NoTrackYet);
+
+ // Don't track allocated autorelease pools, as it is okay to prematurely
+ // exit a method.
+ addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet);
+ addClassMethSummary("NSAutoreleasePool", "allocWithZone", NoTrackYet, false);
+ addClassMethSummary("NSAutoreleasePool", "new", NoTrackYet);
+
+ // Create summaries QCRenderer/QCView -createSnapShotImageOfType:
+ addInstMethSummary("QCRenderer", AllocSumm, "createSnapshotImageOfType");
+ addInstMethSummary("QCView", AllocSumm, "createSnapshotImageOfType");
+
+ // Create summaries for CIContext, 'createCGImage' and
+ // 'createCGLayerWithSize'. These objects are CF objects, and are not
+ // automatically garbage collected.
+ addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect");
+ addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect",
+ "format", "colorSpace");
+ addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", "info");
+}
+
+const RetainSummary *
+RetainSummaryManager::getMethodSummary(const ObjCMethodDecl *MD) {
+ const ObjCInterfaceDecl *ID = MD->getClassInterface();
+ Selector S = MD->getSelector();
+ QualType ResultTy = MD->getReturnType();
+
+ ObjCMethodSummariesTy *CachedSummaries;
+ if (MD->isInstanceMethod())
+ CachedSummaries = &ObjCMethodSummaries;
+ else
+ CachedSummaries = &ObjCClassMethodSummaries;
+
+ return getMethodSummary(S, ID, MD, ResultTy, *CachedSummaries);
+}
diff --git a/lib/Analysis/ThreadSafety.cpp b/lib/Analysis/ThreadSafety.cpp
index 78e1b056e1d7..c7b4c4455664 100644
--- a/lib/Analysis/ThreadSafety.cpp
+++ b/lib/Analysis/ThreadSafety.cpp
@@ -1,9 +1,8 @@
//===- ThreadSafety.cpp ---------------------------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -816,7 +815,7 @@ 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()) {
+ if (const Stmt *S = CurrBlock->getTerminatorStmt()) {
CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getBeginLoc();
} else {
for (CFGBlock::const_reverse_iterator BI = CurrBlock->rbegin(),
@@ -874,7 +873,7 @@ public:
void handleLock(FactSet &FSet, FactManager &FactMan, const FactEntry &entry,
ThreadSafetyHandler &Handler,
StringRef DiagKind) const override {
- Handler.handleDoubleLock(DiagKind, entry.toString(), entry.loc());
+ Handler.handleDoubleLock(DiagKind, entry.toString(), loc(), entry.loc());
}
void handleUnlock(FactSet &FSet, FactManager &FactMan,
@@ -982,12 +981,13 @@ private:
void lock(FactSet &FSet, FactManager &FactMan, const CapabilityExpr &Cp,
LockKind kind, SourceLocation loc, ThreadSafetyHandler *Handler,
StringRef DiagKind) const {
- if (!FSet.findLock(FactMan, Cp)) {
+ if (const FactEntry *Fact = FSet.findLock(FactMan, Cp)) {
+ if (Handler)
+ Handler->handleDoubleLock(DiagKind, Cp.toString(), Fact->loc(), loc);
+ } else {
FSet.removeLock(FactMan, !Cp);
FSet.addLock(FactMan,
llvm::make_unique<LockableFactEntry>(Cp, kind, loc));
- } else if (Handler) {
- Handler->handleDoubleLock(DiagKind, Cp.toString(), loc);
}
}
@@ -1335,8 +1335,8 @@ void ThreadSafetyAnalyzer::removeLock(FactSet &FSet, const CapabilityExpr &Cp,
// Generic lock removal doesn't care about lock kind mismatches, but
// otherwise diagnose when the lock kinds are mismatched.
if (ReceivedKind != LK_Generic && LDat->kind() != ReceivedKind) {
- Handler.handleIncorrectUnlockKind(DiagKind, Cp.toString(),
- LDat->kind(), ReceivedKind, UnlockLoc);
+ Handler.handleIncorrectUnlockKind(DiagKind, Cp.toString(), LDat->kind(),
+ ReceivedKind, LDat->loc(), UnlockLoc);
}
LDat->handleUnlock(FSet, FactMan, Cp, UnlockLoc, FullyRemove, Handler,
@@ -1499,7 +1499,7 @@ void ThreadSafetyAnalyzer::getEdgeLockset(FactSet& Result,
const Stmt *Cond = PredBlock->getTerminatorCondition();
// We don't acquire try-locks on ?: branches, only when its result is used.
- if (!Cond || isa<ConditionalOperator>(PredBlock->getTerminator()))
+ if (!Cond || isa<ConditionalOperator>(PredBlock->getTerminatorStmt()))
return;
bool Negate = false;
@@ -2402,7 +2402,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
// a difference in locksets is probably due to a bug in that block, rather
// than in some other predecessor. In that case, keep the other
// predecessor's lockset.
- if (const Stmt *Terminator = (*PI)->getTerminator()) {
+ if (const Stmt *Terminator = (*PI)->getTerminatorStmt()) {
if (isa<ContinueStmt>(Terminator) || isa<BreakStmt>(Terminator)) {
SpecialBlocks.push_back(*PI);
continue;
@@ -2441,7 +2441,7 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
// it might also be part of a switch. Also, a subsequent destructor
// might add to the lockset, in which case the real issue might be a
// double lock on the other path.
- const Stmt *Terminator = PrevBlock->getTerminator();
+ const Stmt *Terminator = PrevBlock->getTerminatorStmt();
bool IsLoop = Terminator && isa<ContinueStmt>(Terminator);
FactSet PrevLockset;
diff --git a/lib/Analysis/ThreadSafetyCommon.cpp b/lib/Analysis/ThreadSafetyCommon.cpp
index 14d1d9c7a8f7..373dfc77fa9b 100644
--- a/lib/Analysis/ThreadSafetyCommon.cpp
+++ b/lib/Analysis/ThreadSafetyCommon.cpp
@@ -1,9 +1,8 @@
//===- ThreadSafetyCommon.cpp ---------------------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -277,18 +276,23 @@ til::SExpr *SExprBuilder::translateDeclRefExpr(const DeclRefExpr *DRE,
// Function parameters require substitution and/or renaming.
if (const auto *PV = dyn_cast_or_null<ParmVarDecl>(VD)) {
- const auto *FD =
- cast<FunctionDecl>(PV->getDeclContext())->getCanonicalDecl();
unsigned I = PV->getFunctionScopeIndex();
-
- if (Ctx && Ctx->FunArgs && FD == Ctx->AttrDecl->getCanonicalDecl()) {
- // Substitute call arguments for references to function parameters
- assert(I < Ctx->NumArgs);
- return translate(Ctx->FunArgs[I], Ctx->Prev);
+ const DeclContext *D = PV->getDeclContext();
+ if (Ctx && Ctx->FunArgs) {
+ const Decl *Canonical = Ctx->AttrDecl->getCanonicalDecl();
+ if (isa<FunctionDecl>(D)
+ ? (cast<FunctionDecl>(D)->getCanonicalDecl() == Canonical)
+ : (cast<ObjCMethodDecl>(D)->getCanonicalDecl() == Canonical)) {
+ // Substitute call arguments for references to function parameters
+ assert(I < Ctx->NumArgs);
+ return translate(Ctx->FunArgs[I], Ctx->Prev);
+ }
}
// Map the param back to the param of the original function declaration
// for consistent comparisons.
- VD = FD->getParamDecl(I);
+ VD = isa<FunctionDecl>(D)
+ ? cast<FunctionDecl>(D)->getCanonicalDecl()->getParamDecl(I)
+ : cast<ObjCMethodDecl>(D)->getCanonicalDecl()->getParamDecl(I);
}
// For non-local variables, treat it as a reference to a named object.
diff --git a/lib/Analysis/ThreadSafetyLogical.cpp b/lib/Analysis/ThreadSafetyLogical.cpp
index facfa11a39ab..ac730770093e 100644
--- a/lib/Analysis/ThreadSafetyLogical.cpp
+++ b/lib/Analysis/ThreadSafetyLogical.cpp
@@ -1,9 +1,8 @@
//===- ThreadSafetyLogical.cpp ---------------------------------*- C++ --*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This file defines a representation for logical expressions with SExpr leaves
diff --git a/lib/Analysis/ThreadSafetyTIL.cpp b/lib/Analysis/ThreadSafetyTIL.cpp
index 11f7afbd229c..652f953d2a6d 100644
--- a/lib/Analysis/ThreadSafetyTIL.cpp
+++ b/lib/Analysis/ThreadSafetyTIL.cpp
@@ -1,9 +1,8 @@
//===- ThreadSafetyTIL.cpp ------------------------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT in the llvm repository for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp
index 31c88a109565..8a233d4a44f1 100644
--- a/lib/Analysis/UninitializedValues.cpp
+++ b/lib/Analysis/UninitializedValues.cpp
@@ -1,9 +1,8 @@
//===- UninitializedValues.cpp - Find Uninitialized Values ----------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -351,6 +350,7 @@ public:
void VisitBinaryOperator(BinaryOperator *BO);
void VisitCallExpr(CallExpr *CE);
void VisitCastExpr(CastExpr *CE);
+ void VisitOMPExecutableDirective(OMPExecutableDirective *ED);
void operator()(Stmt *S) { Visit(S); }
@@ -456,6 +456,11 @@ void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) {
classify(UO->getSubExpr(), Use);
}
+void ClassifyRefs::VisitOMPExecutableDirective(OMPExecutableDirective *ED) {
+ for (Stmt *S : OMPExecutableDirective::used_clauses_children(ED->clauses()))
+ classify(cast<Expr>(S), Use);
+}
+
static bool isPointerToConst(const QualType &QT) {
return QT->isAnyPointerType() && QT->getPointeeType().isConstQualified();
}
@@ -533,6 +538,7 @@ public:
void VisitDeclStmt(DeclStmt *ds);
void VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS);
void VisitObjCMessageExpr(ObjCMessageExpr *ME);
+ void VisitOMPExecutableDirective(OMPExecutableDirective *ED);
bool isTrackedVar(const VarDecl *vd) {
return ::isTrackedVar(vd, cast<DeclContext>(ac.getDecl()));
@@ -652,7 +658,7 @@ public:
// uninitialized.
for (const auto *Block : cfg) {
unsigned BlockID = Block->getBlockID();
- const Stmt *Term = Block->getTerminator();
+ const Stmt *Term = Block->getTerminatorStmt();
if (SuccsVisited[BlockID] && SuccsVisited[BlockID] < Block->succ_size() &&
Term) {
// This block inevitably leads to the use. If we have an edge from here
@@ -708,6 +714,16 @@ void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) {
}
}
+void TransferFunctions::VisitOMPExecutableDirective(
+ OMPExecutableDirective *ED) {
+ for (Stmt *S : OMPExecutableDirective::used_clauses_children(ED->clauses())) {
+ assert(S && "Expected non-null used-in-clause child.");
+ Visit(S);
+ }
+ if (!ED->isStandaloneDirective())
+ Visit(ED->getStructuredBlock());
+}
+
void TransferFunctions::VisitBlockExpr(BlockExpr *be) {
const BlockDecl *bd = be->getBlockDecl();
for (const auto &I : bd->captures()) {
diff --git a/lib/Analysis/plugins/CheckerDependencyHandling/CheckerDependencyHandling.cpp b/lib/Analysis/plugins/CheckerDependencyHandling/CheckerDependencyHandling.cpp
new file mode 100644
index 000000000000..be8e1200d0bf
--- /dev/null
+++ b/lib/Analysis/plugins/CheckerDependencyHandling/CheckerDependencyHandling.cpp
@@ -0,0 +1,28 @@
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+struct Dependency : public Checker<check::BeginFunction> {
+ void checkBeginFunction(CheckerContext &Ctx) const {}
+};
+struct DependendentChecker : public Checker<check::BeginFunction> {
+ void checkBeginFunction(CheckerContext &Ctx) const {}
+};
+} // end anonymous namespace
+
+// Register plugin!
+extern "C" void clang_registerCheckers(CheckerRegistry &registry) {
+ registry.addChecker<Dependency>("example.Dependency", "", "");
+ registry.addChecker<DependendentChecker>("example.DependendentChecker", "",
+ "");
+
+ registry.addDependency("example.DependendentChecker", "example.Dependency");
+}
+
+extern "C" const char clang_analyzerAPIVersionString[] =
+ CLANG_ANALYZER_API_VERSION_STRING;
diff --git a/lib/Analysis/plugins/CheckerDependencyHandling/CheckerDependencyHandlingAnalyzerPlugin.exports b/lib/Analysis/plugins/CheckerDependencyHandling/CheckerDependencyHandlingAnalyzerPlugin.exports
new file mode 100644
index 000000000000..8d9ff882cfb1
--- /dev/null
+++ b/lib/Analysis/plugins/CheckerDependencyHandling/CheckerDependencyHandlingAnalyzerPlugin.exports
@@ -0,0 +1,2 @@
+clang_registerCheckers
+clang_analyzerAPIVersionString
diff --git a/lib/Analysis/plugins/CheckerOptionHandling/CheckerOptionHandling.cpp b/lib/Analysis/plugins/CheckerOptionHandling/CheckerOptionHandling.cpp
new file mode 100644
index 000000000000..77de3630ae7e
--- /dev/null
+++ b/lib/Analysis/plugins/CheckerOptionHandling/CheckerOptionHandling.cpp
@@ -0,0 +1,44 @@
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+struct MyChecker : public Checker<check::BeginFunction> {
+ void checkBeginFunction(CheckerContext &Ctx) const {}
+};
+
+void registerMyChecker(CheckerManager &Mgr) {
+ MyChecker *Checker = Mgr.registerChecker<MyChecker>();
+ llvm::outs() << "Example option is set to "
+ << (Mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ Checker, "ExampleOption")
+ ? "true"
+ : "false")
+ << '\n';
+}
+
+bool shouldRegisterMyChecker(const LangOptions &LO) { return true; }
+
+} // end anonymous namespace
+
+// Register plugin!
+extern "C" void clang_registerCheckers(CheckerRegistry &registry) {
+ registry.addChecker(registerMyChecker, shouldRegisterMyChecker,
+ "example.MyChecker", "Example Description",
+ "example.mychecker.documentation.nonexistent.html",
+ /*isHidden*/false);
+
+ registry.addCheckerOption(/*OptionType*/ "bool",
+ /*CheckerFullName*/ "example.MyChecker",
+ /*OptionName*/ "ExampleOption",
+ /*DefaultValStr*/ "false",
+ /*Description*/ "This is an example checker opt.",
+ /*DevelopmentStage*/ "released");
+}
+
+extern "C" const char clang_analyzerAPIVersionString[] =
+ CLANG_ANALYZER_API_VERSION_STRING;
diff --git a/lib/Analysis/plugins/CheckerOptionHandling/CheckerOptionHandlingAnalyzerPlugin.exports b/lib/Analysis/plugins/CheckerOptionHandling/CheckerOptionHandlingAnalyzerPlugin.exports
new file mode 100644
index 000000000000..8d9ff882cfb1
--- /dev/null
+++ b/lib/Analysis/plugins/CheckerOptionHandling/CheckerOptionHandlingAnalyzerPlugin.exports
@@ -0,0 +1,2 @@
+clang_registerCheckers
+clang_analyzerAPIVersionString
diff --git a/lib/Analysis/plugins/SampleAnalyzer/MainCallChecker.cpp b/lib/Analysis/plugins/SampleAnalyzer/MainCallChecker.cpp
new file mode 100644
index 000000000000..8bd4085108e9
--- /dev/null
+++ b/lib/Analysis/plugins/SampleAnalyzer/MainCallChecker.cpp
@@ -0,0 +1,54 @@
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class MainCallChecker : public Checker<check::PreStmt<CallExpr>> {
+ mutable std::unique_ptr<BugType> BT;
+
+public:
+ void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+void MainCallChecker::checkPreStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ const Expr *Callee = CE->getCallee();
+ const FunctionDecl *FD = C.getSVal(Callee).getAsFunctionDecl();
+
+ if (!FD)
+ return;
+
+ // Get the name of the callee.
+ IdentifierInfo *II = FD->getIdentifier();
+ if (!II) // if no identifier, not a simple C function
+ return;
+
+ if (II->isStr("main")) {
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+
+ if (!BT)
+ BT.reset(new BugType(this, "call to main", "example analyzer plugin"));
+
+ std::unique_ptr<BugReport> report =
+ llvm::make_unique<BugReport>(*BT, BT->getName(), N);
+ report->addRange(Callee->getSourceRange());
+ C.emitReport(std::move(report));
+ }
+}
+
+// Register plugin!
+extern "C" void clang_registerCheckers(CheckerRegistry &registry) {
+ registry.addChecker<MainCallChecker>(
+ "example.MainCallChecker", "Disallows calls to functions called main",
+ "");
+}
+
+extern "C" const char clang_analyzerAPIVersionString[] =
+ CLANG_ANALYZER_API_VERSION_STRING;
diff --git a/lib/Analysis/plugins/SampleAnalyzer/SampleAnalyzerPlugin.exports b/lib/Analysis/plugins/SampleAnalyzer/SampleAnalyzerPlugin.exports
new file mode 100644
index 000000000000..8d9ff882cfb1
--- /dev/null
+++ b/lib/Analysis/plugins/SampleAnalyzer/SampleAnalyzerPlugin.exports
@@ -0,0 +1,2 @@
+clang_registerCheckers
+clang_analyzerAPIVersionString