From: Ted Kremenek Date: Tue, 3 Nov 2009 08:03:59 +0000 (+0000) Subject: Implement: Warn against using -[NSAutoreleasePool release... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=54cb7ccc769a5e81a13812e08c21daf52a781262;p=clang Implement: Warn against using -[NSAutoreleasePool release] in GC mode git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@85887 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Analysis/PathSensitive/Checker.h b/include/clang/Analysis/PathSensitive/Checker.h index 59ce71e616..ac856d370f 100644 --- a/include/clang/Analysis/PathSensitive/Checker.h +++ b/include/clang/Analysis/PathSensitive/Checker.h @@ -72,6 +72,10 @@ public: ASTContext &getASTContext() { return Eng.getContext(); } + + BugReporter &getBugReporter() { + return Eng.getBugReporter(); + } ExplodedNode *GenerateNode(const Stmt *S, bool markAsSink = false) { return GenerateNode(S, getState(), markAsSink); diff --git a/lib/Analysis/BasicObjCFoundationChecks.cpp b/lib/Analysis/BasicObjCFoundationChecks.cpp index aa2d0ab5a7..4781d5ec24 100644 --- a/lib/Analysis/BasicObjCFoundationChecks.cpp +++ b/lib/Analysis/BasicObjCFoundationChecks.cpp @@ -535,4 +535,5 @@ void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) { Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass); RegisterNSErrorChecks(BR, Eng, D); + RegisterNSAutoreleasePoolChecks(Eng); } diff --git a/lib/Analysis/BasicObjCFoundationChecks.h b/lib/Analysis/BasicObjCFoundationChecks.h index 1271ae4ab1..ea4d3ecfca 100644 --- a/lib/Analysis/BasicObjCFoundationChecks.h +++ b/lib/Analysis/BasicObjCFoundationChecks.h @@ -42,6 +42,7 @@ GRSimpleAPICheck *CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR); void RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng, const Decl &D); +void RegisterNSAutoreleasePoolChecks(GRExprEngine &Eng); } // end clang namespace diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 9830d79526..a34e274dff 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -30,6 +30,7 @@ add_clang_library(clangAnalysis GRState.cpp LiveVariables.cpp MemRegion.cpp + NSAutoreleasePoolChecker.cpp NSErrorChecker.cpp NullDerefChecker.cpp PathDiagnostic.cpp diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp index f9020eea21..d0710e5880 100644 --- a/lib/Analysis/GRExprEngine.cpp +++ b/lib/Analysis/GRExprEngine.cpp @@ -1956,24 +1956,27 @@ void GRExprEngine::VisitObjCMessageExprDispatchHelper(ObjCMessageExpr* ME, } } + // Handle previsits checks. + ExplodedNodeSet Src, DstTmp; + Src.Add(Pred); + CheckerVisit(ME, DstTmp, Src, true); + // Check if we raise an exception. For now treat these as sinks. Eventually // we will want to handle exceptions properly. - SaveAndRestore OldSink(Builder->BuildSinks); - if (RaisesException) Builder->BuildSinks = true; // Dispatch to plug-in transfer function. - unsigned size = Dst.size(); SaveOr OldHasGen(Builder->HasGeneratedNode); - - EvalObjCMessageExpr(Dst, ME, Pred); + + for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); + DI!=DE; ++DI) + EvalObjCMessageExpr(Dst, ME, *DI); // Handle the case where no nodes where generated. Auto-generate that // contains the updated state if we aren't generating sinks. - if (!Builder->BuildSinks && Dst.size() == size && !Builder->HasGeneratedNode) MakeNode(Dst, ME, Pred, state); } diff --git a/lib/Analysis/NSAutoreleasePoolChecker.cpp b/lib/Analysis/NSAutoreleasePoolChecker.cpp new file mode 100644 index 0000000000..e0a8d0dc5f --- /dev/null +++ b/lib/Analysis/NSAutoreleasePoolChecker.cpp @@ -0,0 +1,84 @@ +//=- NSAutoreleasePoolChecker.cpp --------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a NSAutoreleasePoolChecker, a small checker that warns +// about subpar uses of NSAutoreleasePool. Note that while the check itself +// (in it's current form) could be written as a flow-insensitive check, in +// can be potentially enhanced in the future with flow-sensitive information. +// It is also a good example of the CheckerVisitor interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/BugReporter.h" +#include "clang/Analysis/PathSensitive/GRExprEngine.h" +#include "clang/Analysis/PathSensitive/CheckerVisitor.h" +#include "BasicObjCFoundationChecks.h" +#include "llvm/Support/Compiler.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Decl.h" + +using namespace clang; + +namespace { +class VISIBILITY_HIDDEN NSAutoreleasePoolChecker + : public CheckerVisitor { + + Selector releaseS; + +public: + NSAutoreleasePoolChecker(Selector release_s) : releaseS(release_s) {} + + static void *getTag() { + static int x = 0; + return &x; + } + + void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); +}; + +} // end anonymous namespace + + +void clang::RegisterNSAutoreleasePoolChecks(GRExprEngine &Eng) { + ASTContext &Ctx = Eng.getContext(); + if (Ctx.getLangOptions().getGCMode() != LangOptions::NonGC) { + Eng.registerCheck(new NSAutoreleasePoolChecker(GetNullarySelector("release", + Ctx))); + } +} + +void +NSAutoreleasePoolChecker::PreVisitObjCMessageExpr(CheckerContext &C, + const ObjCMessageExpr *ME) { + + const Expr *receiver = ME->getReceiver(); + if (!receiver) + return; + + // FIXME: Enhance with value-tracking information instead of consulting + // the type of the expression. + const ObjCObjectPointerType* PT = + receiver->getType()->getAs(); + const ObjCInterfaceDecl* OD = PT->getInterfaceDecl(); + if (!OD) + return; + if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) + return; + + // Sending 'release' message? + if (ME->getSelector() != releaseS) + return; + + SourceRange R = ME->getSourceRange(); + + C.getBugReporter().EmitBasicReport("Use -drain instead of -release", + "API Upgrade (Apple)", + "Use -drain instead of -release when using NSAutoreleasePool " + "and garbage collection", ME->getLocStart(), &R, 1); +} diff --git a/test/Analysis/retain-release-gc-only.m b/test/Analysis/retain-release-gc-only.m index 2833b02f07..e27cfe758a 100644 --- a/test/Analysis/retain-release-gc-only.m +++ b/test/Analysis/retain-release-gc-only.m @@ -92,6 +92,7 @@ typedef struct _NSZone NSZone; + (id)allocWithZone:(NSZone *)zone; + (id)alloc; - (void)dealloc; +- (void)release; @end @interface NSObject (NSCoderMethods) - (id)awakeAfterUsingCoder:(NSCoder *)aDecoder; @@ -321,6 +322,16 @@ void rdar_7174400(QCView *view, QCRenderer *renderer, CIContext *context, [context createCGImage:img fromRect:rect format:form colorSpace:cs]; // expected-warning{{leak}} } +//===----------------------------------------------------------------------===// +// Warn against using -[NSAutoreleasePool release] in +// GC mode +//===----------------------------------------------------------------------===// + +void rdar_6250216(void) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + [pool release]; // expected-warning{{Use -drain instead of -release when using NSAutoreleasePool and garbage collection}} +} + //===----------------------------------------------------------------------===// // Tests of ownership attributes. //===----------------------------------------------------------------------===//