From 110eaaceddcec790f7e6a5e3bf1261c9aa1e73ab Mon Sep 17 00:00:00 2001
From: dim <dim@FreeBSD.org>
Date: Mon, 2 May 2011 19:39:53 +0000
Subject: Vendor import of clang trunk r130700:
 http://llvm.org/svn/llvm-project/cfe/trunk@130700

---
 lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp | 380 +++++++++++++++----------
 1 file changed, 235 insertions(+), 145 deletions(-)

(limited to 'lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp')

diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
index 7a1b978..63a5917 100644
--- a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -1,4 +1,4 @@
-//=- NSErrorCheckerer.cpp - Coding conventions for uses of NSError -*- C++ -*-==//
+//=- NSErrorChecker.cpp - Coding conventions for uses of NSError -*- C++ -*-==//
 //
 //                     The LLVM Compiler Infrastructure
 //
@@ -15,11 +15,12 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h"
-#include "BasicObjCFoundationChecks.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/Decl.h"
 #include "llvm/ADT/SmallVector.h"
@@ -27,142 +28,263 @@
 using namespace clang;
 using namespace ento;
 
-namespace {
-class NSErrorChecker : public BugType {
-  const Decl &CodeDecl;
-  const bool isNSErrorWarning;
-  IdentifierInfo * const II;
-  ExprEngine &Eng;
+static bool IsNSError(QualType T, IdentifierInfo *II);
+static bool IsCFError(QualType T, IdentifierInfo *II);
 
-  void CheckSignature(const ObjCMethodDecl& MD, QualType& ResultTy,
-                      llvm::SmallVectorImpl<VarDecl*>& ErrorParams);
+//===----------------------------------------------------------------------===//
+// NSErrorMethodChecker
+//===----------------------------------------------------------------------===//
 
-  void CheckSignature(const FunctionDecl& MD, QualType& ResultTy,
-                      llvm::SmallVectorImpl<VarDecl*>& ErrorParams);
+namespace {
+class NSErrorMethodChecker
+    : public Checker< check::ASTDecl<ObjCMethodDecl> > {
+  mutable IdentifierInfo *II;
 
-  bool CheckNSErrorArgument(QualType ArgTy);
-  bool CheckCFErrorArgument(QualType ArgTy);
+public:
+  NSErrorMethodChecker() : II(0) { }
 
-  void CheckParamDeref(const VarDecl *V, const LocationContext *LC,
-                       const GRState *state, BugReporter& BR);
+  void checkASTDecl(const ObjCMethodDecl *D,
+                    AnalysisManager &mgr, BugReporter &BR) const;
+};
+}
 
-  void EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl);
+void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D,
+                                        AnalysisManager &mgr,
+                                        BugReporter &BR) const {
+  if (!D->isThisDeclarationADefinition())
+    return;
+  if (!D->getResultType()->isVoidType())
+    return;
 
-public:
-  NSErrorChecker(const Decl &D, bool isNSError, ExprEngine& eng)
-    : BugType(isNSError ? "NSError** null dereference"
-                        : "CFErrorRef* null dereference",
-              "Coding conventions (Apple)"),
-    CodeDecl(D),
-    isNSErrorWarning(isNSError),
-    II(&eng.getContext().Idents.get(isNSErrorWarning ? "NSError":"CFErrorRef")),
-    Eng(eng) {}
-
-  void FlushReports(BugReporter& BR);
-};
+  if (!II)
+    II = &D->getASTContext().Idents.get("NSError"); 
 
-} // end anonymous namespace
+  bool hasNSError = false;
+  for (ObjCMethodDecl::param_iterator
+         I = D->param_begin(), E = D->param_end(); I != E; ++I)  {
+    if (IsNSError((*I)->getType(), II)) {
+      hasNSError = true;
+      break;
+    }
+  }
 
-void ento::RegisterNSErrorChecks(BugReporter& BR, ExprEngine &Eng,
-                                  const Decl &D) {
-  BR.Register(new NSErrorChecker(D, true, Eng));
-  BR.Register(new NSErrorChecker(D, false, Eng));
+  if (hasNSError) {
+    const char *err = "Method accepting NSError** "
+        "should have a non-void return value to indicate whether or not an "
+        "error occurred";
+    BR.EmitBasicReport("Bad return type when passing NSError**",
+                       "Coding conventions (Apple)", err, D->getLocation());
+  }
 }
 
-void NSErrorChecker::FlushReports(BugReporter& BR) {
-  // Get the analysis engine and the exploded analysis graph.
-  ExplodedGraph& G = Eng.getGraph();
+//===----------------------------------------------------------------------===//
+// CFErrorFunctionChecker
+//===----------------------------------------------------------------------===//
 
-  // Get the ASTContext, which is useful for querying type information.
-  ASTContext &Ctx = BR.getContext();
+namespace {
+class CFErrorFunctionChecker
+    : public Checker< check::ASTDecl<FunctionDecl> > {
+  mutable IdentifierInfo *II;
 
-  QualType ResultTy;
-  llvm::SmallVector<VarDecl*, 5> ErrorParams;
+public:
+  CFErrorFunctionChecker() : II(0) { }
 
-  if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CodeDecl))
-    CheckSignature(*MD, ResultTy, ErrorParams);
-  else if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(&CodeDecl))
-    CheckSignature(*FD, ResultTy, ErrorParams);
-  else
-    return;
+  void checkASTDecl(const FunctionDecl *D,
+                    AnalysisManager &mgr, BugReporter &BR) const;
+};
+}
 
-  if (ErrorParams.empty())
+void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
+                                        AnalysisManager &mgr,
+                                        BugReporter &BR) const {
+  if (!D->isThisDeclarationADefinition())
     return;
+  if (!D->getResultType()->isVoidType())
+    return;
+
+  if (!II)
+    II = &D->getASTContext().Idents.get("CFErrorRef"); 
 
-  if (ResultTy == Ctx.VoidTy) EmitRetTyWarning(BR, CodeDecl);
+  bool hasCFError = false;
+  for (FunctionDecl::param_const_iterator
+         I = D->param_begin(), E = D->param_end(); I != E; ++I)  {
+    if (IsCFError((*I)->getType(), II)) {
+      hasCFError = true;
+      break;
+    }
+  }
 
-  for (ExplodedGraph::roots_iterator RI=G.roots_begin(), RE=G.roots_end();
-       RI!=RE; ++RI) {
-    // Scan the parameters for an implicit null dereference.
-    for (llvm::SmallVectorImpl<VarDecl*>::iterator I=ErrorParams.begin(),
-          E=ErrorParams.end(); I!=E; ++I)
-        CheckParamDeref(*I, (*RI)->getLocationContext(), (*RI)->getState(), BR);
+  if (hasCFError) {
+    const char *err = "Function accepting CFErrorRef* "
+        "should have a non-void return value to indicate whether or not an "
+        "error occurred";
+    BR.EmitBasicReport("Bad return type when passing CFErrorRef*",
+                       "Coding conventions (Apple)", err, D->getLocation());
   }
 }
 
-void NSErrorChecker::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) {
-  std::string sbuf;
-  llvm::raw_string_ostream os(sbuf);
+//===----------------------------------------------------------------------===//
+// NSOrCFErrorDerefChecker
+//===----------------------------------------------------------------------===//
 
-  if (isa<ObjCMethodDecl>(CodeDecl))
-    os << "Method";
-  else
-    os << "Function";
+namespace {
 
-  os << " accepting ";
-  os << (isNSErrorWarning ? "NSError**" : "CFErrorRef*");
-  os << " should have a non-void return value to indicate whether or not an "
-        "error occurred";
+class NSErrorDerefBug : public BugType {
+public:
+  NSErrorDerefBug() : BugType("NSError** null dereference",
+                              "Coding conventions (Apple)") {}
+};
+
+class CFErrorDerefBug : public BugType {
+public:
+  CFErrorDerefBug() : BugType("CFErrorRef* null dereference",
+                              "Coding conventions (Apple)") {}
+};
 
-  BR.EmitBasicReport(isNSErrorWarning
-                     ? "Bad return type when passing NSError**"
-                     : "Bad return type when passing CFError*",
-                     getCategory(), os.str(),
-                     CodeDecl.getLocation());
 }
 
-void
-NSErrorChecker::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy,
-                             llvm::SmallVectorImpl<VarDecl*>& ErrorParams) {
+namespace {
+class NSOrCFErrorDerefChecker
+    : public Checker< check::Location,
+                        check::Event<ImplicitNullDerefEvent> > {
+  mutable IdentifierInfo *NSErrorII, *CFErrorII;
+public:
+  bool ShouldCheckNSError, ShouldCheckCFError;
+  NSOrCFErrorDerefChecker() : NSErrorII(0), CFErrorII(0),
+                              ShouldCheckNSError(0), ShouldCheckCFError(0) { }
+
+  void checkLocation(SVal loc, bool isLoad, CheckerContext &C) const;
+  void checkEvent(ImplicitNullDerefEvent event) const;
+};
+}
 
-  ResultTy = M.getResultType();
+namespace { struct NSErrorOut {}; }
+namespace { struct CFErrorOut {}; }
+
+typedef llvm::ImmutableMap<SymbolRef, unsigned> ErrorOutFlag;
+
+namespace clang {
+namespace ento {
+  template <>
+  struct GRStateTrait<NSErrorOut> : public GRStatePartialTrait<ErrorOutFlag> {  
+    static void *GDMIndex() { static int index = 0; return &index; }
+  };
+  template <>
+  struct GRStateTrait<CFErrorOut> : public GRStatePartialTrait<ErrorOutFlag> {  
+    static void *GDMIndex() { static int index = 0; return &index; }
+  };
+}
+}
 
-  for (ObjCMethodDecl::param_iterator I=M.param_begin(),
-       E=M.param_end(); I!=E; ++I)  {
+template <typename T>
+static bool hasFlag(SVal val, const GRState *state) {
+  if (SymbolRef sym = val.getAsSymbol())
+    if (const unsigned *attachedFlags = state->get<T>(sym))
+      return *attachedFlags;
+  return false;
+}
 
-    QualType T = (*I)->getType();
+template <typename T>
+static void setFlag(const GRState *state, SVal val, CheckerContext &C) {
+  // We tag the symbol that the SVal wraps.
+  if (SymbolRef sym = val.getAsSymbol())
+    C.addTransition(state->set<T>(sym, true));
+}
 
-    if (isNSErrorWarning) {
-      if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I);
-    }
-    else if (CheckCFErrorArgument(T))
-      ErrorParams.push_back(*I);
+static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) {
+  const StackFrameContext *
+    SFC = C.getPredecessor()->getLocationContext()->getCurrentStackFrame();
+  if (const loc::MemRegionVal* X = dyn_cast<loc::MemRegionVal>(&val)) {
+    const MemRegion* R = X->getRegion();
+    if (const VarRegion *VR = R->getAs<VarRegion>())
+      if (const StackArgumentsSpaceRegion *
+          stackReg = dyn_cast<StackArgumentsSpaceRegion>(VR->getMemorySpace()))
+        if (stackReg->getStackFrame() == SFC)
+          return VR->getValueType();
   }
+
+  return QualType();
 }
 
-void
-NSErrorChecker::CheckSignature(const FunctionDecl& F, QualType& ResultTy,
-                             llvm::SmallVectorImpl<VarDecl*>& ErrorParams) {
+void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,
+                                            CheckerContext &C) const {
+  if (!isLoad)
+    return;
+  if (loc.isUndef() || !isa<Loc>(loc))
+    return;
 
-  ResultTy = F.getResultType();
+  ASTContext &Ctx = C.getASTContext();
+  const GRState *state = C.getState();
 
-  for (FunctionDecl::param_const_iterator I = F.param_begin(),
-                                          E = F.param_end(); I != E; ++I)  {
+  // If we are loading from NSError**/CFErrorRef* parameter, mark the resulting
+  // SVal so that we can later check it when handling the
+  // ImplicitNullDerefEvent event.
+  // FIXME: Cumbersome! Maybe add hook at construction of SVals at start of
+  // function ?
 
-    QualType T = (*I)->getType();
+  QualType parmT = parameterTypeFromSVal(loc, C);
+  if (parmT.isNull())
+    return;
 
-    if (isNSErrorWarning) {
-      if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I);
-    }
-    else if (CheckCFErrorArgument(T))
-      ErrorParams.push_back(*I);
+  if (!NSErrorII)
+    NSErrorII = &Ctx.Idents.get("NSError");
+  if (!CFErrorII)
+    CFErrorII = &Ctx.Idents.get("CFErrorRef");
+
+  if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) {
+    setFlag<NSErrorOut>(state, state->getSVal(cast<Loc>(loc)), C);
+    return;
+  }
+
+  if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) {
+    setFlag<CFErrorOut>(state, state->getSVal(cast<Loc>(loc)), C);
+    return;
   }
 }
 
+void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
+  if (event.IsLoad)
+    return;
+
+  SVal loc = event.Location;
+  const GRState *state = event.SinkNode->getState();
+  BugReporter &BR = *event.BR;
+
+  bool isNSError = hasFlag<NSErrorOut>(loc, state);
+  bool isCFError = false;
+  if (!isNSError)
+    isCFError = hasFlag<CFErrorOut>(loc, state);
+
+  if (!(isNSError || isCFError))
+    return;
 
-bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) {
+  // Storing to possible null NSError/CFErrorRef out parameter.
 
-  const PointerType* PPT = ArgTy->getAs<PointerType>();
+  // Emit an error.
+  std::string err;
+  llvm::raw_string_ostream os(err);
+    os << "Potential null dereference.  According to coding standards ";
+
+  if (isNSError)
+    os << "in 'Creating and Returning NSError Objects' the parameter '";
+  else
+    os << "documented in CoreFoundation/CFError.h the parameter '";
+
+  os  << "' may be null.";
+
+  BugType *bug = 0;
+  if (isNSError)
+    bug = new NSErrorDerefBug();
+  else
+    bug = new CFErrorDerefBug();
+  EnhancedBugReport *report = new EnhancedBugReport(*bug, os.str(),
+                                                    event.SinkNode);
+  BR.EmitReport(report);
+}
+
+static bool IsNSError(QualType T, IdentifierInfo *II) {
+
+  const PointerType* PPT = T->getAs<PointerType>();
   if (!PPT)
     return false;
 
@@ -181,9 +303,8 @@ bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) {
   return false;
 }
 
-bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) {
-
-  const PointerType* PPT = ArgTy->getAs<PointerType>();
+static bool IsCFError(QualType T, IdentifierInfo *II) {
+  const PointerType* PPT = T->getAs<PointerType>();
   if (!PPT) return false;
 
   const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>();
@@ -192,47 +313,16 @@ bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) {
   return TT->getDecl()->getIdentifier() == II;
 }
 
-void NSErrorChecker::CheckParamDeref(const VarDecl *Param,
-                                   const LocationContext *LC,
-                                   const GRState *rootState,
-                                   BugReporter& BR) {
-
-  SVal ParamL = rootState->getLValue(Param, LC);
-  const MemRegion* ParamR = cast<loc::MemRegionVal>(ParamL).getRegionAs<VarRegion>();
-  assert (ParamR && "Parameters always have VarRegions.");
-  SVal ParamSVal = rootState->getSVal(ParamR);
-
-  // FIXME: For now assume that ParamSVal is symbolic.  We need to generalize
-  // this later.
-  SymbolRef ParamSym = ParamSVal.getAsLocSymbol();
-  if (!ParamSym)
-    return;
+void ento::registerNSErrorChecker(CheckerManager &mgr) {
+  mgr.registerChecker<NSErrorMethodChecker>();
+  NSOrCFErrorDerefChecker *
+    checker = mgr.registerChecker<NSOrCFErrorDerefChecker>();
+  checker->ShouldCheckNSError = true;
+}
 
-  // Iterate over the implicit-null dereferences.
-  ExplodedNode *const* I,  *const* E;
-  llvm::tie(I, E) = GetImplicitNullDereferences(Eng);
-  for ( ; I != E; ++I) {
-    const GRState *state = (*I)->getState();
-    SVal location = state->getSVal((*I)->getLocationAs<StmtPoint>()->getStmt());
-    if (location.getAsSymbol() != ParamSym)
-      continue;
-
-    // Emit an error.
-    std::string sbuf;
-    llvm::raw_string_ostream os(sbuf);
-      os << "Potential null dereference.  According to coding standards ";
-
-    if (isNSErrorWarning)
-      os << "in 'Creating and Returning NSError Objects' the parameter '";
-    else
-      os << "documented in CoreFoundation/CFError.h the parameter '";
-
-    os << Param << "' may be null.";
-
-    BugReport *report = new BugReport(*this, os.str(), *I);
-    // FIXME: Notable symbols are now part of the report.  We should
-    //  add support for notable symbols in BugReport.
-    //    BR.addNotableSymbol(SV->getSymbol());
-    BR.EmitReport(report);
-  }
+void ento::registerCFErrorChecker(CheckerManager &mgr) {
+  mgr.registerChecker<CFErrorFunctionChecker>();
+  NSOrCFErrorDerefChecker *
+    checker = mgr.registerChecker<NSOrCFErrorDerefChecker>();
+  checker->ShouldCheckCFError = true;
 }
-- 
cgit v1.1