trixirt created this revision.
trixirt added a reviewer: dcoughlin.
Herald added subscribers: cfe-commits, a.sidorin, szepet, xazax.hun, mgorny.
Herald added a reviewer: george.karpenkov.

The c++ 'new' function has several variants.
Only the no-throw versions of these return a nullptr on failure.
Error handling based on tha nullptr check for the throw version
is dead code.

ex/

char *n = new char[x];
if (n == nullptr)  // this is dead/impossible code

  do_something();


Repository:
  rC Clang

https://reviews.llvm.org/D47554

Files:
  include/clang/StaticAnalyzer/Checkers/Checkers.td
  lib/StaticAnalyzer/Checkers/CMakeLists.txt
  lib/StaticAnalyzer/Checkers/DeadStatusChecker.cpp
  test/Analysis/dead-status-new-nullptr.cpp

Index: test/Analysis/dead-status-new-nullptr.cpp
===================================================================
--- /dev/null
+++ test/Analysis/dead-status-new-nullptr.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=deadcode.DeadStatus -verify %s
+
+void *t1(unsigned n, bool *fail);
+void *t1(unsigned n, bool *fail) {
+  char *m = new char[n];
+  if (nullptr == m) // expected-warning{{This variable can never be a nullptr}}
+    *fail = true;
+  else
+    *fail = false;
+  return m;
+}
+
+void *t2(unsigned n, bool *fail);
+void *t2(unsigned n, bool *fail) {
+  char *m = new char[n];
+  if (m == nullptr) // expected-warning{{This variable can never be a nullptr}}
+    *fail = true;
+  else
+    *fail = false;
+  return m;
+}
+
+// a variant of nullptr
+void *t3(unsigned n, bool *fail);
+void *t3(unsigned n, bool *fail) {
+  char *m = new char[n];
+  if (m == 0) // expected-warning{{This variable can never be a nullptr}}
+    *fail = true;
+  else
+    *fail = false;
+  return m;
+}
+
+// a variant of nullptr
+#define NULL __null
+void *t4(unsigned n, bool *fail);
+void *t4(unsigned n, bool *fail) {
+  char *m = new char[n];
+  if (m == NULL) // expected-warning{{This variable can never be a nullptr}}
+    *fail = true;
+  else
+    *fail = false;
+  return m;
+}
Index: lib/StaticAnalyzer/Checkers/DeadStatusChecker.cpp
===================================================================
--- /dev/null
+++ lib/StaticAnalyzer/Checkers/DeadStatusChecker.cpp
@@ -0,0 +1,117 @@
+//==- DeadStatusChecker.cpp - Check for impossible status -*- 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 DeadStatus, a checker that looks for impossible
+//  status checks.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class DeadStatusChecker : public Checker<check::PostStmt<CXXNewExpr>,
+                                         check::PreStmt<BinaryOperator>> {
+  std::unique_ptr<BugType> NullptrCheckBug;
+  void reportNullptrCheck(SymbolRef Sym, const BinaryOperator *B,
+                          CheckerContext &C) const;
+  bool isNullptr(const Expr *E, CheckerContext &C) const;
+
+public:
+  DeadStatusChecker();
+  void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
+  void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
+};
+} // namespace
+
+REGISTER_SET_WITH_PROGRAMSTATE(NewThrowSymbols, SymbolRef)
+
+void DeadStatusChecker::reportNullptrCheck(SymbolRef Sym,
+                                           const BinaryOperator *B,
+                                           CheckerContext &C) const {
+  ExplodedNode *N = C.generateErrorNode();
+  if (!N)
+    return;
+
+  auto report = llvm::make_unique<BugReport>(
+      *NullptrCheckBug, "This variable can never be a nullptr", N);
+  report->addRange(B->getSourceRange());
+  C.emitReport(std::move(report));
+}
+
+bool DeadStatusChecker::isNullptr(const Expr *E, CheckerContext &C) const {
+  bool ret = false;
+  if (E) {
+    E = E->IgnoreParenImpCasts();
+    SVal S = C.getSVal(E);
+    if (S.getAs<DefinedOrUnknownSVal>()) {
+      DefinedOrUnknownSVal V = S.castAs<DefinedOrUnknownSVal>();
+      if (V.isZeroConstant())
+        ret = true;
+    }
+  }
+  return ret;
+}
+
+DeadStatusChecker::DeadStatusChecker() {
+  NullptrCheckBug.reset(
+      new BugType(this, "Dead nullptr check", "C++ API Error"));
+}
+
+void DeadStatusChecker::checkPreStmt(const BinaryOperator *B,
+                                     CheckerContext &C) const {
+  if (!B->isEqualityOp())
+    return;
+
+  const Expr *lhs = B->getLHS()->IgnoreParenImpCasts();
+  const Expr *rhs = B->getRHS()->IgnoreParenImpCasts();
+  const DeclRefExpr *DRE = nullptr;
+  SVal V;
+
+  if (isNullptr(B->getLHS(), C)) {
+    if (rhs && isa<DeclRefExpr>(rhs)) {
+      DRE = dyn_cast<DeclRefExpr>(rhs);
+      V = C.getSVal(B->getRHS());
+    }
+  } else if (isNullptr(B->getRHS(), C)) {
+    if (lhs && isa<DeclRefExpr>(lhs)) {
+      DRE = dyn_cast<DeclRefExpr>(lhs);
+      V = C.getSVal(B->getLHS());
+    }
+  } else {
+    return;
+  }
+  if (DRE != nullptr) {
+    SymbolRef trySym = V.getAsLocSymbol();
+    if (trySym && C.getState()->contains<NewThrowSymbols>(trySym))
+      reportNullptrCheck(trySym, B, C);
+  }
+}
+
+void DeadStatusChecker::checkPostStmt(const CXXNewExpr *NE,
+                                      CheckerContext &C) const {
+  ASTContext &Ctx = C.getASTContext();
+  if (!NE->shouldNullCheckAllocation(Ctx)) {
+    ProgramStateRef State = C.getState();
+    SVal V = C.getSVal(NE);
+    SymbolRef Sym = V.getAsLocSymbol();
+    State = State->add<NewThrowSymbols>(Sym);
+    C.addTransition(State);
+  }
+}
+
+void ento::registerDeadStatusChecker(CheckerManager &mgr) {
+  mgr.registerChecker<DeadStatusChecker>();
+}
Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -27,6 +27,7 @@
   CloneChecker.cpp
   ConversionChecker.cpp
   CXXSelfAssignmentChecker.cpp
+  DeadStatusChecker.cpp
   DeadStoresChecker.cpp
   DebugCheckers.cpp
   DeleteWithNonVirtualDtorChecker.cpp
Index: include/clang/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -346,6 +346,11 @@
 def DeadStoresChecker : Checker<"DeadStores">,
   HelpText<"Check for values stored to variables that are never read afterwards">,
   DescFile<"DeadStoresChecker.cpp">;
+
+def DeadStatusChecker : Checker<"DeadStatus">,
+  HelpText<"Check for impossible status">,
+  DescFile<"DeadStatusChecker.cpp">;
+
 } // end DeadCode
 
 let ParentPackage = DeadCodeAlpha in {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to