r284473 - [analyzer] Add NumberObjectConversion checker.

Artem Dergachev via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 18 04:06:29 PDT 2016


Author: dergachev
Date: Tue Oct 18 06:06:28 2016
New Revision: 284473

URL: http://llvm.org/viewvc/llvm-project?rev=284473&view=rev
Log:
[analyzer] Add NumberObjectConversion checker.

When dealing with objects that represent numbers, such as Objective-C NSNumber,
the language provides little protection from accidentally interpreting
the value of a pointer to such object as the value of the number represented
by the object. Results of such mis-interpretation may be unexpected.

The checker attempts to fill this gap in cases when the code is obviously
incorrect.

With "Pedantic" option enabled, this checker enforces a coding style to
completely prevent errors of this kind (off by default).

Differential Revision: https://reviews.llvm.org/D22968

Added:
    cfe/trunk/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
    cfe/trunk/test/Analysis/number-object-conversion.cpp
    cfe/trunk/test/Analysis/number-object-conversion.m
Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td
    cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
    cfe/trunk/test/Analysis/Inputs/system-header-simulator-objc.h

Modified: cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td?rev=284473&r1=284472&r2=284473&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Checkers/Checkers.td Tue Oct 18 06:06:28 2016
@@ -474,6 +474,11 @@ def CStringNotNullTerm : Checker<"NotNul
 
 let ParentPackage = OSX in {
 
+def NumberObjectConversionChecker : Checker<"NumberObjectConversion">,
+  InPackage<OSX>,
+  HelpText<"Check for erroneous conversions of objects representing numbers into numbers">,
+  DescFile<"NumberObjectConversionChecker.cpp">;
+
 def MacOSXAPIChecker : Checker<"API">,
   InPackage<OSX>,
   HelpText<"Check for proper uses of various Apple APIs">,

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt?rev=284473&r1=284472&r2=284473&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt Tue Oct 18 06:06:28 2016
@@ -54,6 +54,7 @@ add_clang_library(clangStaticAnalyzerChe
   NoReturnFunctionChecker.cpp
   NonNullParamChecker.cpp
   NullabilityChecker.cpp
+  NumberObjectConversionChecker.cpp
   ObjCAtSyncChecker.cpp
   ObjCContainersASTChecker.cpp
   ObjCContainersChecker.cpp

Added: cfe/trunk/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp?rev=284473&view=auto
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp (added)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp Tue Oct 18 06:06:28 2016
@@ -0,0 +1,267 @@
+//===- NumberObjectConversionChecker.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 NumberObjectConversionChecker, which checks for a
+// particular common mistake when dealing with numbers represented as objects
+// passed around by pointers. Namely, the language allows to reinterpret the
+// pointer as a number directly, often without throwing any warnings,
+// but in most cases the result of such conversion is clearly unexpected,
+// as pointer value, rather than number value represented by the pointee object,
+// becomes the result of such operation.
+//
+// Currently the checker supports the Objective-C NSNumber class,
+// and the OSBoolean class found in macOS low-level code; the latter
+// can only hold boolean values.
+//
+// This checker has an option "Pedantic" (boolean), which enables detection of
+// more conversion patterns (which are most likely more harmless, and therefore
+// are more likely to produce false positives) - disabled by default,
+// enabled with `-analyzer-config osx.NumberObjectConversion:Pedantic=true'.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/APSInt.h"
+
+using namespace clang;
+using namespace ento;
+using namespace ast_matchers;
+
+namespace {
+
+class NumberObjectConversionChecker : public Checker<check::ASTCodeBody> {
+public:
+  bool Pedantic;
+
+  void checkASTCodeBody(const Decl *D, AnalysisManager &AM,
+                        BugReporter &BR) const;
+};
+
+class Callback : public MatchFinder::MatchCallback {
+  const NumberObjectConversionChecker *C;
+  BugReporter &BR;
+  AnalysisDeclContext *ADC;
+
+public:
+  Callback(const NumberObjectConversionChecker *C,
+           BugReporter &BR, AnalysisDeclContext *ADC)
+      : C(C), BR(BR), ADC(ADC) {}
+  virtual void run(const MatchFinder::MatchResult &Result);
+};
+} // end of anonymous namespace
+
+void Callback::run(const MatchFinder::MatchResult &Result) {
+  bool IsPedanticMatch = (Result.Nodes.getNodeAs<Stmt>("pedantic") != nullptr);
+  if (IsPedanticMatch && !C->Pedantic)
+    return;
+
+  const Stmt *Conv = Result.Nodes.getNodeAs<Stmt>("conv");
+  assert(Conv);
+  const Expr *Osboolean = Result.Nodes.getNodeAs<Expr>("osboolean");
+  const Expr *Nsnumber = Result.Nodes.getNodeAs<Expr>("nsnumber");
+  bool IsObjC = (bool)Nsnumber;
+  const Expr *Obj = IsObjC ? Nsnumber : Osboolean;
+  assert(Obj);
+
+  ASTContext &ACtx = ADC->getASTContext();
+
+  if (const Expr *CheckIfNull =
+          Result.Nodes.getNodeAs<Expr>("check_if_null")) {
+    // We consider NULL to be a pointer, even if it is defined as a plain 0.
+    // FIXME: Introduce a matcher to implement this logic?
+    SourceLocation Loc = CheckIfNull->getLocStart();
+    if (Loc.isMacroID()) {
+      StringRef MacroName = Lexer::getImmediateMacroName(
+          Loc, ACtx.getSourceManager(), ACtx.getLangOpts());
+      if (MacroName != "YES" && MacroName != "NO")
+        return;
+    } else {
+      // Otherwise, comparison of pointers to 0 might still be intentional.
+      // See if this is the case.
+      llvm::APSInt Result;
+      if (CheckIfNull->IgnoreParenCasts()->EvaluateAsInt(
+              Result, ACtx, Expr::SE_AllowSideEffects)) {
+        if (Result == 0) {
+          if (!C->Pedantic)
+            return;
+          IsPedanticMatch = true;
+        }
+      }
+    }
+  }
+
+  llvm::SmallString<64> Msg;
+  llvm::raw_svector_ostream OS(Msg);
+  OS << "Converting '"
+     << Obj->getType().getCanonicalType().getUnqualifiedType().getAsString()
+     << "' to a plain ";
+
+  if (Result.Nodes.getNodeAs<QualType>("int_type") != nullptr)
+    OS << "integer value";
+  else if (Result.Nodes.getNodeAs<QualType>("objc_bool_type") != nullptr)
+    OS << "BOOL value";
+  else if (Result.Nodes.getNodeAs<QualType>("cpp_bool_type") != nullptr)
+    OS << "bool value";
+  else
+    OS << "boolean value for branching";
+
+  if (IsPedanticMatch) {
+    if (IsObjC) {
+      OS << "; please compare the pointer to nil instead "
+            "to suppress this warning";
+    } else {
+      OS << "; please compare the pointer to NULL or nullptr instead "
+            "to suppress this warning";
+    }
+  } else {
+    OS << "; pointer value is being used instead";
+  }
+
+  BR.EmitBasicReport(
+      ADC->getDecl(), C, "Suspicious number object conversion", "Logic error",
+      OS.str(),
+      PathDiagnosticLocation::createBegin(Obj, BR.getSourceManager(), ADC),
+      Conv->getSourceRange());
+}
+
+void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D,
+                                                     AnalysisManager &AM,
+                                                     BugReporter &BR) const {
+  MatchFinder F;
+  Callback CB(this, BR, AM.getAnalysisDeclContext(D));
+
+  auto OSBooleanExprM =
+      expr(ignoringParenImpCasts(
+          expr(hasType(hasCanonicalType(
+              pointerType(pointee(hasCanonicalType(
+                  recordType(hasDeclaration(
+                      cxxRecordDecl(hasName(
+                          "OSBoolean")))))))))).bind("osboolean")));
+
+  auto NSNumberExprM =
+      expr(ignoringParenImpCasts(expr(hasType(hasCanonicalType(
+          objcObjectPointerType(pointee(
+              qualType(hasCanonicalType(
+                  qualType(hasDeclaration(
+                      objcInterfaceDecl(hasName(
+                          "NSNumber"))))))))))).bind("nsnumber")));
+
+  auto SuspiciousExprM =
+      anyOf(OSBooleanExprM, NSNumberExprM);
+
+  auto AnotherNSNumberExprM =
+      expr(equalsBoundNode("nsnumber"));
+
+  // The .bind here is in order to compose the error message more accurately.
+  auto ObjCBooleanTypeM =
+      qualType(typedefType(hasDeclaration(
+                   typedefDecl(hasName("BOOL"))))).bind("objc_bool_type");
+
+  // The .bind here is in order to compose the error message more accurately.
+  auto AnyBooleanTypeM =
+      qualType(anyOf(qualType(booleanType()).bind("cpp_bool_type"),
+                     ObjCBooleanTypeM));
+
+
+  // The .bind here is in order to compose the error message more accurately.
+  auto AnyNumberTypeM =
+      qualType(hasCanonicalType(isInteger()),
+               unless(typedefType(hasDeclaration(
+                   typedefDecl(matchesName("^::u?intptr_t$"))))))
+      .bind("int_type");
+
+  auto AnyBooleanOrNumberTypeM =
+      qualType(anyOf(AnyBooleanTypeM, AnyNumberTypeM));
+
+  auto AnyBooleanOrNumberExprM =
+      expr(ignoringParenImpCasts(expr(hasType(AnyBooleanOrNumberTypeM))));
+
+  auto ConversionThroughAssignmentM =
+      binaryOperator(hasOperatorName("="),
+                     hasLHS(AnyBooleanOrNumberExprM),
+                     hasRHS(SuspiciousExprM));
+
+  auto ConversionThroughBranchingM =
+      ifStmt(hasCondition(SuspiciousExprM))
+      .bind("pedantic");
+
+  auto ConversionThroughCallM =
+      callExpr(hasAnyArgument(allOf(hasType(AnyBooleanOrNumberTypeM),
+                                    ignoringParenImpCasts(SuspiciousExprM))));
+
+  // We bind "check_if_null" to modify the warning message
+  // in case it was intended to compare a pointer to 0 with a relatively-ok
+  // construct "x == 0" or "x != 0".
+  auto ConversionThroughEquivalenceM =
+      binaryOperator(anyOf(hasOperatorName("=="), hasOperatorName("!=")),
+                     hasEitherOperand(SuspiciousExprM),
+                     hasEitherOperand(AnyBooleanOrNumberExprM
+                                      .bind("check_if_null")));
+
+  auto ConversionThroughComparisonM =
+      binaryOperator(anyOf(hasOperatorName(">="), hasOperatorName(">"),
+                           hasOperatorName("<="), hasOperatorName("<")),
+                     hasEitherOperand(SuspiciousExprM),
+                     hasEitherOperand(AnyBooleanOrNumberExprM));
+
+  auto ConversionThroughConditionalOperatorM =
+      conditionalOperator(
+          hasCondition(SuspiciousExprM),
+          unless(hasTrueExpression(hasDescendant(AnotherNSNumberExprM))),
+          unless(hasFalseExpression(hasDescendant(AnotherNSNumberExprM))))
+      .bind("pedantic");
+
+  auto ConversionThroughExclamationMarkM =
+      unaryOperator(hasOperatorName("!"), has(expr(SuspiciousExprM)))
+      .bind("pedantic");
+
+  auto ConversionThroughExplicitBooleanCastM =
+      explicitCastExpr(hasType(AnyBooleanTypeM),
+                       has(expr(SuspiciousExprM)))
+      .bind("pedantic");
+
+  auto ConversionThroughExplicitNumberCastM =
+      explicitCastExpr(hasType(AnyNumberTypeM),
+                       has(expr(SuspiciousExprM)));
+
+  auto ConversionThroughInitializerM =
+      declStmt(hasSingleDecl(
+          varDecl(hasType(AnyBooleanOrNumberTypeM),
+                  hasInitializer(SuspiciousExprM))));
+
+  auto FinalM = stmt(anyOf(ConversionThroughAssignmentM,
+                           ConversionThroughBranchingM,
+                           ConversionThroughCallM,
+                           ConversionThroughComparisonM,
+                           ConversionThroughConditionalOperatorM,
+                           ConversionThroughEquivalenceM,
+                           ConversionThroughExclamationMarkM,
+                           ConversionThroughExplicitBooleanCastM,
+                           ConversionThroughExplicitNumberCastM,
+                           ConversionThroughInitializerM)).bind("conv");
+
+  F.addMatcher(stmt(forEachDescendant(FinalM)), &CB);
+  F.match(*D->getBody(), AM.getASTContext());
+}
+
+void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) {
+  const LangOptions &LO = Mgr.getLangOpts();
+  if (LO.CPlusPlus || LO.ObjC2) {
+    NumberObjectConversionChecker *Chk =
+        Mgr.registerChecker<NumberObjectConversionChecker>();
+    Chk->Pedantic =
+        Mgr.getAnalyzerOptions().getBooleanOption("Pedantic", false, Chk);
+  }
+}

Modified: cfe/trunk/test/Analysis/Inputs/system-header-simulator-objc.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/Inputs/system-header-simulator-objc.h?rev=284473&r1=284472&r2=284473&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/Inputs/system-header-simulator-objc.h (original)
+++ cfe/trunk/test/Analysis/Inputs/system-header-simulator-objc.h Tue Oct 18 06:06:28 2016
@@ -10,10 +10,16 @@ typedef unsigned short UInt16;
 
 typedef signed long CFIndex;
 typedef signed char BOOL;
+#define YES ((BOOL)1)
+#define NO ((BOOL)0)
+
 typedef unsigned long NSUInteger;
 typedef unsigned short unichar;
 typedef UInt16 UniChar;
 
+#define NULL ((void *)0)
+#define nil ((id)0)
+
 enum {
     NSASCIIStringEncoding = 1,
     NSNEXTSTEPStringEncoding = 2,
@@ -72,6 +78,7 @@ NSFastEnumerationState;
 @end
 @interface NSNumber : NSValue  - (char)charValue;
 - (id)initWithInt:(int)value;
+- (BOOL)boolValue;
 @end   @class NSString;
 @interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>  - (NSUInteger)count;
 @end  @interface NSArray (NSArrayCreation)  + (id)array;

Added: cfe/trunk/test/Analysis/number-object-conversion.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/number-object-conversion.cpp?rev=284473&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/number-object-conversion.cpp (added)
+++ cfe/trunk/test/Analysis/number-object-conversion.cpp Tue Oct 18 06:06:28 2016
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -w -std=c++11 -analyze -analyzer-checker=osx.NumberObjectConversion %s -verify
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -w -std=c++11 -analyze -analyzer-checker=osx.NumberObjectConversion -analyzer-config osx.NumberObjectConversion:Pedantic=true -DPEDANTIC %s -verify
+
+#define NULL ((void *)0)
+#include "Inputs/system-header-simulator-cxx.h" // for nullptr
+
+class OSBoolean {
+public:
+  virtual bool isTrue() const;
+  virtual bool isFalse() const;
+};
+
+extern const OSBoolean *const &kOSBooleanFalse;
+extern const OSBoolean *const &kOSBooleanTrue;
+
+void takes_bool(bool);
+
+void bad(const OSBoolean *p) {
+#ifdef PEDANTIC
+  if (p) {} // expected-warning{{Converting 'const class OSBoolean *' to a plain boolean value for branching; please compare the pointer to NULL or nullptr instead to suppress this warning}}
+  if (!p) {} // expected-warning{{Converting 'const class OSBoolean *' to a plain boolean value for branching; please compare the pointer to NULL or nullptr instead to suppress this warning}}
+  p ? 1 : 2; // expected-warning{{Converting 'const class OSBoolean *' to a plain boolean value for branching; please compare the pointer to NULL or nullptr instead to suppress this warning}}
+  (bool)p; // expected-warning{{Converting 'const class OSBoolean *' to a plain bool value; please compare the pointer to NULL or nullptr instead to suppress this warning}}
+#endif
+  bool x = p; // expected-warning{{Converting 'const class OSBoolean *' to a plain bool value; pointer value is being used instead}}
+  x = p; // expected-warning{{Converting 'const class OSBoolean *' to a plain bool value; pointer value is being used instead}}
+  takes_bool(p); // expected-warning{{Converting 'const class OSBoolean *' to a plain bool value; pointer value is being used instead}}
+  takes_bool(x); // no-warning
+}
+
+typedef bool sugared_bool;
+typedef const OSBoolean *sugared_OSBoolean;
+void bad_sugared(sugared_OSBoolean p) {
+  sugared_bool x = p; // expected-warning{{Converting 'const class OSBoolean *' to a plain bool value; pointer value is being used instead}}
+}
+
+void good(const OSBoolean *p) {
+  bool x = p->isTrue(); // no-warning
+  (bool)p->isFalse(); // no-warning
+  if (p == kOSBooleanTrue) {} // no-warning
+}
+
+void suppression(const OSBoolean *p) {
+  if (p == NULL) {} // no-warning
+  bool y = (p == nullptr); // no-warning
+}
+
+// Conversion of a pointer to an intptr_t is fine.
+typedef long intptr_t;
+typedef unsigned long uintptr_t;
+typedef long fintptr_t; // Fake, for testing the regex.
+void test_intptr_t(const OSBoolean *p) {
+  (long)p; // expected-warning{{Converting 'const class OSBoolean *' to a plain integer value; pointer value is being used instead}}
+  (intptr_t)p; // no-warning
+  (unsigned long)p; // expected-warning{{Converting 'const class OSBoolean *' to a plain integer value; pointer value is being used instead}}
+  (uintptr_t)p; // no-warning
+  (fintptr_t)p; // expected-warning{{Converting 'const class OSBoolean *' to a plain integer value; pointer value is being used instead}}
+}
+
+// Test a different definition of NULL.
+#undef NULL
+#define NULL 0
+void test_non_pointer_NULL(const OSBoolean *p) {
+  if (p == NULL) {} // no-warning
+}

Added: cfe/trunk/test/Analysis/number-object-conversion.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/number-object-conversion.m?rev=284473&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/number-object-conversion.m (added)
+++ cfe/trunk/test/Analysis/number-object-conversion.m Tue Oct 18 06:06:28 2016
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -fblocks -w -analyze -analyzer-checker=osx.NumberObjectConversion %s -verify
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -fblocks -w -analyze -analyzer-checker=osx.NumberObjectConversion -analyzer-config osx.NumberObjectConversion:Pedantic=true -DPEDANTIC %s -verify
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -fblocks -fobjc-arc -w -analyze -analyzer-checker=osx.NumberObjectConversion %s -verify
+// RUN: %clang_cc1 -triple i386-apple-darwin10 -fblocks -fobjc-arc -w -analyze -analyzer-checker=osx.NumberObjectConversion -analyzer-config osx.NumberObjectConversion:Pedantic=true -DPEDANTIC %s -verify
+
+#include "Inputs/system-header-simulator-objc.h"
+
+void takes_boolean(BOOL);
+void takes_integer(int);
+
+void bad(NSNumber *p) {
+#ifdef PEDANTIC
+  if (p) {} // expected-warning{{Converting 'NSNumber *' to a plain boolean value for branching; please compare the pointer to nil instead to suppress this warning}}
+  if (!p) {} // expected-warning{{Converting 'NSNumber *' to a plain boolean value for branching; please compare the pointer to nil instead to suppress this warning}}
+  (!p) ? 1 : 2; // expected-warning{{Converting 'NSNumber *' to a plain boolean value for branching; please compare the pointer to nil instead to suppress this warning}}
+  (BOOL)p; // expected-warning{{Converting 'NSNumber *' to a plain BOOL value; please compare the pointer to nil instead to suppress this warning}}
+  if (p == 0) {} // expected-warning{{Converting 'NSNumber *' to a plain integer value; please compare the pointer to nil instead to suppress this warning}}
+  if (p > 0) {} // expected-warning{{Converting 'NSNumber *' to a plain integer value; pointer value is being used instead}}
+#endif
+  if (p == YES) {} // expected-warning{{Converting 'NSNumber *' to a plain BOOL value; pointer value is being used instead}}
+  if (p == NO) {} // expected-warning{{Converting 'NSNumber *' to a plain BOOL value; pointer value is being used instead}}
+  BOOL x = p; // expected-warning{{Converting 'NSNumber *' to a plain BOOL value; pointer value is being used instead}}
+  x = p; // expected-warning{{Converting 'NSNumber *' to a plain BOOL value; pointer value is being used instead}}
+  x = (p == YES); // expected-warning{{Converting 'NSNumber *' to a plain BOOL value; pointer value is being used instead}}
+  if (p == 1) {} // expected-warning{{Converting 'NSNumber *' to a plain integer value; pointer value is being used instead}}
+  int y = p; // expected-warning{{Converting 'NSNumber *' to a plain integer value; pointer value is being used instead}}
+  y = p; // expected-warning{{Converting 'NSNumber *' to a plain integer value; pointer value is being used instead}}
+  takes_boolean(p); // expected-warning{{Converting 'NSNumber *' to a plain BOOL value; pointer value is being used instead}}
+  takes_integer(p); // expected-warning{{Converting 'NSNumber *' to a plain integer value; pointer value is being used instead}}
+  takes_boolean(x); // no-warning
+  takes_integer(y); // no-warning
+}
+
+typedef NSNumber *SugaredNumber;
+void bad_sugared(SugaredNumber p) {
+  p == YES; // expected-warning{{Converting 'NSNumber *' to a plain BOOL value; pointer value is being used instead}}
+}
+
+ at interface I : NSObject {
+ at public
+  NSNumber *ivar;
+  NSNumber *prop;
+}
+- (NSNumber *)foo;
+ at property(copy) NSNumber *prop;
+ at end
+
+ at implementation I
+ at synthesize prop;
+ at end
+
+void bad_ivar(I *i) {
+  i->ivar == YES; // expected-warning{{Converting 'NSNumber *' to a plain BOOL value; pointer value is being used instead}}
+  i->prop == YES; // expected-warning{{Converting 'NSNumber *' to a plain BOOL value; pointer value is being used instead}}
+  [i foo] == YES; // expected-warning{{Converting 'NSNumber *' to a plain BOOL value; pointer value is being used instead}}
+}
+
+void good(NSNumber *p) {
+  if ([p boolValue] == NO) {} // no-warning
+  if ([p boolValue] == YES) {} // no-warning
+  BOOL x = [p boolValue]; // no-warning
+}
+
+void suppression(NSNumber *p) {
+  if (p == NULL) {} // no-warning
+  if (p == nil) {} // no-warning
+}
+
+// Conversion of a pointer to an intptr_t is fine.
+typedef long intptr_t;
+typedef unsigned long uintptr_t;
+typedef long fintptr_t; // Fake, for testing the regex.
+void test_intptr_t(NSNumber *p) {
+  (long)p; // expected-warning{{Converting 'NSNumber *' to a plain integer value; pointer value is being used instead}}
+  (intptr_t)p; // no-warning
+  (unsigned long)p; // expected-warning{{Converting 'NSNumber *' to a plain integer value; pointer value is being used instead}}
+  (uintptr_t)p; // no-warning
+  (fintptr_t)p; // expected-warning{{Converting 'NSNumber *' to a plain integer value; pointer value is being used instead}}
+}
+
+// Test a different definition of NULL.
+#undef NULL
+#define NULL 0
+void test_non_pointer_NULL(NSNumber *p) {
+  if (p == NULL) {} // no-warning
+}




More information about the cfe-commits mailing list