1 //== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines BasicObjCFoundationChecks, a class that encapsulates
11 // a set of simple checks to run on Objective-C code using Apple's Foundation
14 //===----------------------------------------------------------------------===//
16 #include "BasicObjCFoundationChecks.h"
18 #include "clang/Checker/PathSensitive/ExplodedGraph.h"
19 #include "clang/Checker/PathSensitive/CheckerVisitor.h"
20 #include "clang/Checker/PathSensitive/GRExprEngine.h"
21 #include "clang/Checker/PathSensitive/GRState.h"
22 #include "clang/Checker/BugReporter/BugType.h"
23 #include "clang/Checker/PathSensitive/MemRegion.h"
24 #include "clang/Checker/PathSensitive/CheckerVisitor.h"
25 #include "clang/Checker/Checkers/LocalCheckers.h"
26 #include "clang/AST/DeclObjC.h"
27 #include "clang/AST/Expr.h"
28 #include "clang/AST/ExprObjC.h"
29 #include "clang/AST/ASTContext.h"
31 using namespace clang
;
34 class APIMisuse
: public BugType
{
36 APIMisuse(const char* name
) : BugType(name
, "API Misuse (Apple)") {}
38 } // end anonymous namespace
40 //===----------------------------------------------------------------------===//
42 //===----------------------------------------------------------------------===//
44 static const ObjCInterfaceType
* GetReceiverType(const ObjCMessageExpr
* ME
) {
46 switch (ME
->getReceiverKind()) {
47 case ObjCMessageExpr::Instance
:
48 T
= ME
->getInstanceReceiver()->getType();
51 case ObjCMessageExpr::SuperInstance
:
52 T
= ME
->getSuperType();
55 case ObjCMessageExpr::Class
:
56 case ObjCMessageExpr::SuperClass
:
60 if (const ObjCObjectPointerType
*PT
= T
->getAs
<ObjCObjectPointerType
>())
61 return PT
->getInterfaceType();
66 static const char* GetReceiverNameType(const ObjCMessageExpr
* ME
) {
67 if (const ObjCInterfaceType
*ReceiverType
= GetReceiverType(ME
))
68 return ReceiverType
->getDecl()->getIdentifier()->getNameStart();
72 static bool isNSString(llvm::StringRef ClassName
) {
73 return ClassName
== "NSString" || ClassName
== "NSMutableString";
76 static inline bool isNil(SVal X
) {
77 return isa
<loc::ConcreteInt
>(X
);
80 //===----------------------------------------------------------------------===//
81 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
82 //===----------------------------------------------------------------------===//
85 class NilArgChecker
: public CheckerVisitor
<NilArgChecker
> {
87 void WarnNilArg(CheckerContext
&C
, const ObjCMessageExpr
* ME
, unsigned Arg
);
89 NilArgChecker() : BT(0) {}
90 static void *getTag() { static int x
= 0; return &x
; }
91 void PreVisitObjCMessageExpr(CheckerContext
&C
, const ObjCMessageExpr
*ME
);
95 void NilArgChecker::WarnNilArg(CheckerContext
&C
,
96 const clang::ObjCMessageExpr
*ME
,
100 BT
= new APIMisuse("nil argument");
102 if (ExplodedNode
*N
= C
.GenerateSink()) {
103 llvm::SmallString
<128> sbuf
;
104 llvm::raw_svector_ostream
os(sbuf
);
105 os
<< "Argument to '" << GetReceiverNameType(ME
) << "' method '"
106 << ME
->getSelector().getAsString() << "' cannot be nil";
108 RangedBugReport
*R
= new RangedBugReport(*BT
, os
.str(), N
);
109 R
->addRange(ME
->getArg(Arg
)->getSourceRange());
114 void NilArgChecker::PreVisitObjCMessageExpr(CheckerContext
&C
,
115 const ObjCMessageExpr
*ME
)
117 const ObjCInterfaceType
*ReceiverType
= GetReceiverType(ME
);
121 if (isNSString(ReceiverType
->getDecl()->getIdentifier()->getName())) {
122 Selector S
= ME
->getSelector();
124 if (S
.isUnarySelector())
127 // FIXME: This is going to be really slow doing these checks with
128 // lexical comparisons.
130 std::string NameStr
= S
.getAsString();
131 llvm::StringRef
Name(NameStr
);
132 assert(!Name
.empty());
134 // FIXME: Checking for initWithFormat: will not work in most cases
135 // yet because [NSString alloc] returns id, not NSString*. We will
136 // need support for tracking expected-type information in the analyzer
137 // to find these errors.
138 if (Name
== "caseInsensitiveCompare:" ||
139 Name
== "compare:" ||
140 Name
== "compare:options:" ||
141 Name
== "compare:options:range:" ||
142 Name
== "compare:options:range:locale:" ||
143 Name
== "componentsSeparatedByCharactersInSet:" ||
144 Name
== "initWithFormat:") {
145 if (isNil(C
.getState()->getSVal(ME
->getArg(0))))
146 WarnNilArg(C
, ME
, 0);
151 //===----------------------------------------------------------------------===//
153 //===----------------------------------------------------------------------===//
156 class CFNumberCreateChecker
: public CheckerVisitor
<CFNumberCreateChecker
> {
160 CFNumberCreateChecker() : BT(0), II(0) {}
161 ~CFNumberCreateChecker() {}
162 static void *getTag() { static int x
= 0; return &x
; }
163 void PreVisitCallExpr(CheckerContext
&C
, const CallExpr
*CE
);
165 void EmitError(const TypedRegion
* R
, const Expr
* Ex
,
166 uint64_t SourceSize
, uint64_t TargetSize
, uint64_t NumberKind
);
168 } // end anonymous namespace
171 kCFNumberSInt8Type
= 1,
172 kCFNumberSInt16Type
= 2,
173 kCFNumberSInt32Type
= 3,
174 kCFNumberSInt64Type
= 4,
175 kCFNumberFloat32Type
= 5,
176 kCFNumberFloat64Type
= 6,
177 kCFNumberCharType
= 7,
178 kCFNumberShortType
= 8,
179 kCFNumberIntType
= 9,
180 kCFNumberLongType
= 10,
181 kCFNumberLongLongType
= 11,
182 kCFNumberFloatType
= 12,
183 kCFNumberDoubleType
= 13,
184 kCFNumberCFIndexType
= 14,
185 kCFNumberNSIntegerType
= 15,
186 kCFNumberCGFloatType
= 16
195 Optional() : IsKnown(false), Val(0) {}
196 Optional(const T
& val
) : IsKnown(true), Val(val
) {}
198 bool isKnown() const { return IsKnown
; }
200 const T
& getValue() const {
205 operator const T
&() const {
211 static Optional
<uint64_t> GetCFNumberSize(ASTContext
& Ctx
, uint64_t i
) {
212 static const unsigned char FixedSize
[] = { 8, 16, 32, 64, 32, 64 };
214 if (i
< kCFNumberCharType
)
215 return FixedSize
[i
-1];
220 case kCFNumberCharType
: T
= Ctx
.CharTy
; break;
221 case kCFNumberShortType
: T
= Ctx
.ShortTy
; break;
222 case kCFNumberIntType
: T
= Ctx
.IntTy
; break;
223 case kCFNumberLongType
: T
= Ctx
.LongTy
; break;
224 case kCFNumberLongLongType
: T
= Ctx
.LongLongTy
; break;
225 case kCFNumberFloatType
: T
= Ctx
.FloatTy
; break;
226 case kCFNumberDoubleType
: T
= Ctx
.DoubleTy
; break;
227 case kCFNumberCFIndexType
:
228 case kCFNumberNSIntegerType
:
229 case kCFNumberCGFloatType
:
230 // FIXME: We need a way to map from names to Type*.
232 return Optional
<uint64_t>();
235 return Ctx
.getTypeSize(T
);
239 static const char* GetCFNumberTypeStr(uint64_t i
) {
240 static const char* Names
[] = {
241 "kCFNumberSInt8Type",
242 "kCFNumberSInt16Type",
243 "kCFNumberSInt32Type",
244 "kCFNumberSInt64Type",
245 "kCFNumberFloat32Type",
246 "kCFNumberFloat64Type",
248 "kCFNumberShortType",
251 "kCFNumberLongLongType",
252 "kCFNumberFloatType",
253 "kCFNumberDoubleType",
254 "kCFNumberCFIndexType",
255 "kCFNumberNSIntegerType",
256 "kCFNumberCGFloatType"
259 return i
<= kCFNumberCGFloatType
? Names
[i
-1] : "Invalid CFNumberType";
263 void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext
&C
,
266 const Expr
* Callee
= CE
->getCallee();
267 const GRState
*state
= C
.getState();
268 SVal CallV
= state
->getSVal(Callee
);
269 const FunctionDecl
* FD
= CallV
.getAsFunctionDecl();
274 ASTContext
&Ctx
= C
.getASTContext();
276 II
= &Ctx
.Idents
.get("CFNumberCreate");
278 if (FD
->getIdentifier() != II
|| CE
->getNumArgs() != 3)
281 // Get the value of the "theType" argument.
282 SVal TheTypeVal
= state
->getSVal(CE
->getArg(1));
284 // FIXME: We really should allow ranges of valid theType values, and
285 // bifurcate the state appropriately.
286 nonloc::ConcreteInt
* V
= dyn_cast
<nonloc::ConcreteInt
>(&TheTypeVal
);
290 uint64_t NumberKind
= V
->getValue().getLimitedValue();
291 Optional
<uint64_t> TargetSize
= GetCFNumberSize(Ctx
, NumberKind
);
293 // FIXME: In some cases we can emit an error.
294 if (!TargetSize
.isKnown())
297 // Look at the value of the integer being passed by reference. Essentially
298 // we want to catch cases where the value passed in is not equal to the
299 // size of the type being created.
300 SVal TheValueExpr
= state
->getSVal(CE
->getArg(2));
302 // FIXME: Eventually we should handle arbitrary locations. We can do this
303 // by having an enhanced memory model that does low-level typing.
304 loc::MemRegionVal
* LV
= dyn_cast
<loc::MemRegionVal
>(&TheValueExpr
);
308 const TypedRegion
* R
= dyn_cast
<TypedRegion
>(LV
->StripCasts());
312 QualType T
= Ctx
.getCanonicalType(R
->getValueType());
314 // FIXME: If the pointee isn't an integer type, should we flag a warning?
315 // People can do weird stuff with pointers.
317 if (!T
->isIntegerType())
320 uint64_t SourceSize
= Ctx
.getTypeSize(T
);
322 // CHECK: is SourceSize == TargetSize
323 if (SourceSize
== TargetSize
)
326 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
327 // otherwise generate a regular node.
329 // FIXME: We can actually create an abstract "CFNumber" object that has
330 // the bits initialized to the provided values.
332 if (ExplodedNode
*N
= SourceSize
< TargetSize
? C
.GenerateSink()
333 : C
.GenerateNode()) {
334 llvm::SmallString
<128> sbuf
;
335 llvm::raw_svector_ostream
os(sbuf
);
337 os
<< (SourceSize
== 8 ? "An " : "A ")
338 << SourceSize
<< " bit integer is used to initialize a CFNumber "
339 "object that represents "
340 << (TargetSize
== 8 ? "an " : "a ")
341 << TargetSize
<< " bit integer. ";
343 if (SourceSize
< TargetSize
)
344 os
<< (TargetSize
- SourceSize
)
345 << " bits of the CFNumber value will be garbage." ;
347 os
<< (SourceSize
- TargetSize
)
348 << " bits of the input integer will be lost.";
351 BT
= new APIMisuse("Bad use of CFNumberCreate");
353 RangedBugReport
*report
= new RangedBugReport(*BT
, os
.str(), N
);
354 report
->addRange(CE
->getArg(2)->getSourceRange());
355 C
.EmitReport(report
);
359 //===----------------------------------------------------------------------===//
360 // CFRetain/CFRelease checking for null arguments.
361 //===----------------------------------------------------------------------===//
364 class CFRetainReleaseChecker
: public CheckerVisitor
<CFRetainReleaseChecker
> {
366 IdentifierInfo
*Retain
, *Release
;
368 CFRetainReleaseChecker(): BT(0), Retain(0), Release(0) {}
369 static void *getTag() { static int x
= 0; return &x
; }
370 void PreVisitCallExpr(CheckerContext
& C
, const CallExpr
* CE
);
372 } // end anonymous namespace
375 void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext
& C
,
376 const CallExpr
* CE
) {
377 // If the CallExpr doesn't have exactly 1 argument just give up checking.
378 if (CE
->getNumArgs() != 1)
381 // Get the function declaration of the callee.
382 const GRState
* state
= C
.getState();
383 SVal X
= state
->getSVal(CE
->getCallee());
384 const FunctionDecl
* FD
= X
.getAsFunctionDecl();
390 ASTContext
&Ctx
= C
.getASTContext();
391 Retain
= &Ctx
.Idents
.get("CFRetain");
392 Release
= &Ctx
.Idents
.get("CFRelease");
393 BT
= new APIMisuse("null passed to CFRetain/CFRelease");
396 // Check if we called CFRetain/CFRelease.
397 const IdentifierInfo
*FuncII
= FD
->getIdentifier();
398 if (!(FuncII
== Retain
|| FuncII
== Release
))
401 // FIXME: The rest of this just checks that the argument is non-null.
402 // It should probably be refactored and combined with AttrNonNullChecker.
404 // Get the argument's value.
405 const Expr
*Arg
= CE
->getArg(0);
406 SVal ArgVal
= state
->getSVal(Arg
);
407 DefinedSVal
*DefArgVal
= dyn_cast
<DefinedSVal
>(&ArgVal
);
412 ValueManager
&ValMgr
= C
.getValueManager();
413 DefinedSVal Zero
= cast
<DefinedSVal
>(ValMgr
.makeZeroVal(Arg
->getType()));
415 // Make an expression asserting that they're equal.
416 SValuator
&SVator
= ValMgr
.getSValuator();
417 DefinedOrUnknownSVal ArgIsNull
= SVator
.EvalEQ(state
, Zero
, *DefArgVal
);
420 const GRState
*stateTrue
, *stateFalse
;
421 llvm::tie(stateTrue
, stateFalse
) = state
->Assume(ArgIsNull
);
423 if (stateTrue
&& !stateFalse
) {
424 ExplodedNode
*N
= C
.GenerateSink(stateTrue
);
428 const char *description
= (FuncII
== Retain
)
429 ? "Null pointer argument in call to CFRetain"
430 : "Null pointer argument in call to CFRelease";
432 EnhancedBugReport
*report
= new EnhancedBugReport(*BT
, description
, N
);
433 report
->addRange(Arg
->getSourceRange());
434 report
->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue
, Arg
);
435 C
.EmitReport(report
);
439 // From here on, we know the argument is non-null.
440 C
.addTransition(stateFalse
);
443 //===----------------------------------------------------------------------===//
444 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
445 //===----------------------------------------------------------------------===//
448 class ClassReleaseChecker
: public CheckerVisitor
<ClassReleaseChecker
> {
451 Selector autoreleaseS
;
455 ClassReleaseChecker()
458 static void *getTag() { static int x
= 0; return &x
; }
460 void PreVisitObjCMessageExpr(CheckerContext
&C
, const ObjCMessageExpr
*ME
);
464 void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext
&C
,
465 const ObjCMessageExpr
*ME
) {
468 BT
= new APIMisuse("message incorrectly sent to class instead of class "
471 ASTContext
&Ctx
= C
.getASTContext();
472 releaseS
= GetNullarySelector("release", Ctx
);
473 retainS
= GetNullarySelector("retain", Ctx
);
474 autoreleaseS
= GetNullarySelector("autorelease", Ctx
);
475 drainS
= GetNullarySelector("drain", Ctx
);
478 ObjCInterfaceDecl
*Class
= 0;
480 switch (ME
->getReceiverKind()) {
481 case ObjCMessageExpr::Class
:
482 Class
= ME
->getClassReceiver()->getAs
<ObjCObjectType
>()->getInterface();
484 case ObjCMessageExpr::SuperClass
:
485 Class
= ME
->getSuperType()->getAs
<ObjCObjectType
>()->getInterface();
487 case ObjCMessageExpr::Instance
:
488 case ObjCMessageExpr::SuperInstance
:
492 Selector S
= ME
->getSelector();
493 if (!(S
== releaseS
|| S
== retainS
|| S
== autoreleaseS
|| S
== drainS
))
496 if (ExplodedNode
*N
= C
.GenerateNode()) {
497 llvm::SmallString
<200> buf
;
498 llvm::raw_svector_ostream
os(buf
);
500 os
<< "The '" << S
.getAsString() << "' message should be sent to instances "
501 "of class '" << Class
->getName()
502 << "' and not the class directly";
504 RangedBugReport
*report
= new RangedBugReport(*BT
, os
.str(), N
);
505 report
->addRange(ME
->getSourceRange());
506 C
.EmitReport(report
);
510 //===----------------------------------------------------------------------===//
511 // Check registration.
512 //===----------------------------------------------------------------------===//
514 void clang::RegisterAppleChecks(GRExprEngine
& Eng
, const Decl
&D
) {
515 Eng
.registerCheck(new NilArgChecker());
516 Eng
.registerCheck(new CFNumberCreateChecker());
517 RegisterNSErrorChecks(Eng
.getBugReporter(), Eng
, D
);
518 RegisterNSAutoreleasePoolChecks(Eng
);
519 Eng
.registerCheck(new CFRetainReleaseChecker());
520 Eng
.registerCheck(new ClassReleaseChecker());