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 { 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: 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::PreStmt> { + std::unique_ptr 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( + *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 V = S.castAs(); + 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(rhs)) { + DRE = dyn_cast(rhs); + V = C.getSVal(B->getRHS()); + } + } else if (isNullptr(B->getRHS(), C)) { + if (lhs && isa(lhs)) { + DRE = dyn_cast(lhs); + V = C.getSVal(B->getLHS()); + } + } else { + return; + } + if (DRE != nullptr) { + SymbolRef trySym = V.getAsLocSymbol(); + if (trySym && C.getState()->contains(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(Sym); + C.addTransition(State); + } +} + +void ento::registerDeadStatusChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} 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; +}