diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:49 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:49 +0000 |
commit | 2298981669bf3bd63335a4be179bc0f96823a8f4 (patch) | |
tree | 1cbe2eb27f030d2d70b80ee5ca3c86bee7326a9f /lib/Analysis | |
parent | 9a83721404652cea39e9f02ae3e3b5c964602a5c (diff) | |
download | src-2298981669bf3bd63335a4be179bc0f96823a8f4.tar.gz src-2298981669bf3bd63335a4be179bc0f96823a8f4.zip |
Vendor import of stripped clang trunk r366426 (just before thevendor/clang/clang-trunk-r366426
release_90 branch point):
https://llvm.org/svn/llvm-project/cfe/trunk@366426
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')
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 ®istry) { + 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 ®istry) { + 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 ®istry) { + 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 |