forked from OSchip/llvm-project
Move NullDeref and UndefDeref into their own checker.
Add a CheckLocation() interface to Checker. Now ImplicitNullDeref nodes are cached in NullDerefChecker. More cleanups follow. llvm-svn: 85471
This commit is contained in:
parent
8107289624
commit
6b8bfb376b
|
@ -19,6 +19,7 @@
|
||||||
#include "clang/Basic/SourceLocation.h"
|
#include "clang/Basic/SourceLocation.h"
|
||||||
#include "clang/Analysis/PathSensitive/GRState.h"
|
#include "clang/Analysis/PathSensitive/GRState.h"
|
||||||
#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
|
#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
|
||||||
|
#include "clang/Analysis/PathSensitive/BugType.h"
|
||||||
#include "llvm/ADT/SmallPtrSet.h"
|
#include "llvm/ADT/SmallPtrSet.h"
|
||||||
#include "llvm/ADT/SmallSet.h"
|
#include "llvm/ADT/SmallSet.h"
|
||||||
#include "llvm/ADT/SmallString.h"
|
#include "llvm/ADT/SmallString.h"
|
||||||
|
@ -183,38 +184,6 @@ public:
|
||||||
const_iterator end() const { return const_iterator(Reports.end()); }
|
const_iterator end() const { return const_iterator(Reports.end()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class BugType {
|
|
||||||
private:
|
|
||||||
const std::string Name;
|
|
||||||
const std::string Category;
|
|
||||||
llvm::FoldingSet<BugReportEquivClass> EQClasses;
|
|
||||||
friend class BugReporter;
|
|
||||||
bool SuppressonSink;
|
|
||||||
public:
|
|
||||||
BugType(const char *name, const char* cat)
|
|
||||||
: Name(name), Category(cat), SuppressonSink(false) {}
|
|
||||||
virtual ~BugType();
|
|
||||||
|
|
||||||
// FIXME: Should these be made strings as well?
|
|
||||||
const std::string& getName() const { return Name; }
|
|
||||||
const std::string& getCategory() const { return Category; }
|
|
||||||
|
|
||||||
/// isSuppressOnSink - Returns true if bug reports associated with this bug
|
|
||||||
/// type should be suppressed if the end node of the report is post-dominated
|
|
||||||
/// by a sink node.
|
|
||||||
bool isSuppressOnSink() const { return SuppressonSink; }
|
|
||||||
void setSuppressOnSink(bool x) { SuppressonSink = x; }
|
|
||||||
|
|
||||||
virtual void FlushReports(BugReporter& BR);
|
|
||||||
|
|
||||||
typedef llvm::FoldingSet<BugReportEquivClass>::iterator iterator;
|
|
||||||
iterator begin() { return EQClasses.begin(); }
|
|
||||||
iterator end() { return EQClasses.end(); }
|
|
||||||
|
|
||||||
typedef llvm::FoldingSet<BugReportEquivClass>::const_iterator const_iterator;
|
|
||||||
const_iterator begin() const { return EQClasses.begin(); }
|
|
||||||
const_iterator end() const { return EQClasses.end(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Specialized subclasses of BugReport.
|
// Specialized subclasses of BugReport.
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
#ifndef LLVM_CLANG_BUGTYPE
|
||||||
|
#define LLVM_CLANG_BUGTYPE
|
||||||
|
|
||||||
|
#include <llvm/ADT/FoldingSet.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
|
||||||
|
class BugReportEquivClass;
|
||||||
|
class BugReporter;
|
||||||
|
class BuiltinBugReport;
|
||||||
|
class BugReporterContext;
|
||||||
|
class GRExprEngine;
|
||||||
|
|
||||||
|
class BugType {
|
||||||
|
private:
|
||||||
|
const std::string Name;
|
||||||
|
const std::string Category;
|
||||||
|
llvm::FoldingSet<BugReportEquivClass> EQClasses;
|
||||||
|
friend class BugReporter;
|
||||||
|
bool SuppressonSink;
|
||||||
|
public:
|
||||||
|
BugType(const char *name, const char* cat)
|
||||||
|
: Name(name), Category(cat), SuppressonSink(false) {}
|
||||||
|
virtual ~BugType();
|
||||||
|
|
||||||
|
// FIXME: Should these be made strings as well?
|
||||||
|
const std::string& getName() const { return Name; }
|
||||||
|
const std::string& getCategory() const { return Category; }
|
||||||
|
|
||||||
|
/// isSuppressOnSink - Returns true if bug reports associated with this bug
|
||||||
|
/// type should be suppressed if the end node of the report is post-dominated
|
||||||
|
/// by a sink node.
|
||||||
|
bool isSuppressOnSink() const { return SuppressonSink; }
|
||||||
|
void setSuppressOnSink(bool x) { SuppressonSink = x; }
|
||||||
|
|
||||||
|
virtual void FlushReports(BugReporter& BR);
|
||||||
|
|
||||||
|
typedef llvm::FoldingSet<BugReportEquivClass>::iterator iterator;
|
||||||
|
iterator begin() { return EQClasses.begin(); }
|
||||||
|
iterator end() { return EQClasses.end(); }
|
||||||
|
|
||||||
|
typedef llvm::FoldingSet<BugReportEquivClass>::const_iterator const_iterator;
|
||||||
|
const_iterator begin() const { return EQClasses.begin(); }
|
||||||
|
const_iterator end() const { return EQClasses.end(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class BuiltinBug : public BugType {
|
||||||
|
GRExprEngine &Eng;
|
||||||
|
protected:
|
||||||
|
const std::string desc;
|
||||||
|
public:
|
||||||
|
BuiltinBug(GRExprEngine *eng, const char* n, const char* d)
|
||||||
|
: BugType(n, "Logic errors"), Eng(*eng), desc(d) {}
|
||||||
|
|
||||||
|
BuiltinBug(GRExprEngine *eng, const char* n)
|
||||||
|
: BugType(n, "Logic errors"), Eng(*eng), desc(n) {}
|
||||||
|
|
||||||
|
const std::string &getDescription() const { return desc; }
|
||||||
|
|
||||||
|
virtual void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) {}
|
||||||
|
|
||||||
|
void FlushReports(BugReporter& BR) { FlushReportsImpl(BR, Eng); }
|
||||||
|
|
||||||
|
virtual void registerInitialVisitors(BugReporterContext& BRC,
|
||||||
|
const ExplodedNode* N,
|
||||||
|
BuiltinBugReport *R) {}
|
||||||
|
|
||||||
|
template <typename ITER> void Emit(BugReporter& BR, ITER I, ITER E);
|
||||||
|
};
|
||||||
|
} // end clang namespace
|
||||||
|
#endif
|
|
@ -104,16 +104,24 @@ private:
|
||||||
GRStmtNodeBuilder &Builder,
|
GRStmtNodeBuilder &Builder,
|
||||||
GRExprEngine &Eng,
|
GRExprEngine &Eng,
|
||||||
const Stmt *stmt,
|
const Stmt *stmt,
|
||||||
ExplodedNode *Pred, bool isPrevisit) {
|
ExplodedNode *Pred, void *tag, bool isPrevisit) {
|
||||||
CheckerContext C(Dst, Builder, Eng, Pred, getTag(), isPrevisit);
|
CheckerContext C(Dst, Builder, Eng, Pred, tag, isPrevisit);
|
||||||
assert(isPrevisit && "Only previsit supported for now.");
|
assert(isPrevisit && "Only previsit supported for now.");
|
||||||
_PreVisit(C, stmt);
|
_PreVisit(C, stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~Checker() {}
|
virtual ~Checker() {}
|
||||||
virtual void _PreVisit(CheckerContext &C, const Stmt *stmt) = 0;
|
virtual void _PreVisit(CheckerContext &C, const Stmt *stmt) {}
|
||||||
virtual const void *getTag() = 0;
|
|
||||||
|
// This is a previsit which takes a node returns a node.
|
||||||
|
virtual ExplodedNode *CheckLocation(const Stmt *S, ExplodedNode *Pred,
|
||||||
|
const GRState *state, SVal V,
|
||||||
|
GRExprEngine &Eng) {
|
||||||
|
return Pred;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end clang namespace
|
} // end clang namespace
|
||||||
|
|
|
@ -75,7 +75,8 @@ class GRExprEngine : public GRSubEngine {
|
||||||
Selector RaiseSel;
|
Selector RaiseSel;
|
||||||
|
|
||||||
llvm::OwningPtr<GRSimpleAPICheck> BatchAuditor;
|
llvm::OwningPtr<GRSimpleAPICheck> BatchAuditor;
|
||||||
std::vector<Checker*> Checkers;
|
|
||||||
|
llvm::DenseMap<void *, Checker*> Checkers;
|
||||||
|
|
||||||
/// BR - The BugReporter associated with this engine. It is important that
|
/// BR - The BugReporter associated with this engine. It is important that
|
||||||
// this object be placed at the very end of member variables so that its
|
// this object be placed at the very end of member variables so that its
|
||||||
|
@ -126,18 +127,6 @@ public:
|
||||||
// calling a function with the attribute "noreturn".
|
// calling a function with the attribute "noreturn".
|
||||||
ErrorNodes NoReturnCalls;
|
ErrorNodes NoReturnCalls;
|
||||||
|
|
||||||
/// ImplicitNullDeref - Nodes in the ExplodedGraph that result from
|
|
||||||
/// taking a dereference on a symbolic pointer that MAY be NULL.
|
|
||||||
ErrorNodes ImplicitNullDeref;
|
|
||||||
|
|
||||||
/// ExplicitNullDeref - Nodes in the ExplodedGraph that result from
|
|
||||||
/// taking a dereference on a symbolic pointer that MUST be NULL.
|
|
||||||
ErrorNodes ExplicitNullDeref;
|
|
||||||
|
|
||||||
/// UndefDeref - Nodes in the ExplodedGraph that result from
|
|
||||||
/// taking a dereference on an undefined value.
|
|
||||||
ErrorNodes UndefDeref;
|
|
||||||
|
|
||||||
/// ImplicitBadSizedVLA - Nodes in the ExplodedGraph that result from
|
/// ImplicitBadSizedVLA - Nodes in the ExplodedGraph that result from
|
||||||
/// constructing a zero-sized VLA where the size may be zero.
|
/// constructing a zero-sized VLA where the size may be zero.
|
||||||
ErrorNodes ImplicitBadSizedVLA;
|
ErrorNodes ImplicitBadSizedVLA;
|
||||||
|
@ -158,10 +147,6 @@ public:
|
||||||
/// ObjC message expressions where the receiver is undefined (uninitialized).
|
/// ObjC message expressions where the receiver is undefined (uninitialized).
|
||||||
ErrorNodes UndefReceivers;
|
ErrorNodes UndefReceivers;
|
||||||
|
|
||||||
/// UndefArg - Nodes in the ExplodedGraph resulting from calls to functions
|
|
||||||
/// where a pass-by-value argument has an undefined value.
|
|
||||||
UndefArgsTy UndefArgs;
|
|
||||||
|
|
||||||
/// MsgExprUndefArgs - Nodes in the ExplodedGraph resulting from
|
/// MsgExprUndefArgs - Nodes in the ExplodedGraph resulting from
|
||||||
/// message expressions where a pass-by-value argument has an undefined
|
/// message expressions where a pass-by-value argument has an undefined
|
||||||
/// value.
|
/// value.
|
||||||
|
@ -195,6 +180,8 @@ public:
|
||||||
|
|
||||||
BugReporter& getBugReporter() { return BR; }
|
BugReporter& getBugReporter() { return BR; }
|
||||||
|
|
||||||
|
GRStmtNodeBuilder &getBuilder() { assert(Builder); return *Builder; }
|
||||||
|
|
||||||
/// setTransferFunctions
|
/// setTransferFunctions
|
||||||
void setTransferFunctions(GRTransferFuncs* tf);
|
void setTransferFunctions(GRTransferFuncs* tf);
|
||||||
|
|
||||||
|
@ -217,8 +204,14 @@ public:
|
||||||
|
|
||||||
void RegisterInternalChecks();
|
void RegisterInternalChecks();
|
||||||
|
|
||||||
|
template <typename CHECKER>
|
||||||
void registerCheck(Checker *check) {
|
void registerCheck(Checker *check) {
|
||||||
Checkers.push_back(check);
|
Checkers[CHECKER::getTag()] = check;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CHECKER>
|
||||||
|
CHECKER *getChecker() {
|
||||||
|
return static_cast<CHECKER*>(Checkers[CHECKER::getTag()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isRetStackAddr(const ExplodedNode* N) const {
|
bool isRetStackAddr(const ExplodedNode* N) const {
|
||||||
|
@ -234,15 +227,15 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isImplicitNullDeref(const ExplodedNode* N) const {
|
bool isImplicitNullDeref(const ExplodedNode* N) const {
|
||||||
return N->isSink() && ImplicitNullDeref.count(const_cast<ExplodedNode*>(N)) != 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isExplicitNullDeref(const ExplodedNode* N) const {
|
bool isExplicitNullDeref(const ExplodedNode* N) const {
|
||||||
return N->isSink() && ExplicitNullDeref.count(const_cast<ExplodedNode*>(N)) != 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isUndefDeref(const ExplodedNode* N) const {
|
bool isUndefDeref(const ExplodedNode* N) const {
|
||||||
return N->isSink() && UndefDeref.count(const_cast<ExplodedNode*>(N)) != 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isNoReturnCall(const ExplodedNode* N) const {
|
bool isNoReturnCall(const ExplodedNode* N) const {
|
||||||
|
@ -254,13 +247,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isBadCall(const ExplodedNode* N) const {
|
bool isBadCall(const ExplodedNode* N) const {
|
||||||
return N->isSink() && BadCalls.count(const_cast<ExplodedNode*>(N)) != 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isUndefArg(const ExplodedNode* N) const {
|
bool isUndefArg(const ExplodedNode* N) const {
|
||||||
return N->isSink() &&
|
return false;
|
||||||
(UndefArgs.find(const_cast<ExplodedNode*>(N)) != UndefArgs.end() ||
|
|
||||||
MsgExprUndefArgs.find(const_cast<ExplodedNode*>(N)) != MsgExprUndefArgs.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isUndefReceiver(const ExplodedNode* N) const {
|
bool isUndefReceiver(const ExplodedNode* N) const {
|
||||||
|
@ -279,17 +270,6 @@ public:
|
||||||
undef_branch_iterator undef_branches_begin() { return UndefBranches.begin(); }
|
undef_branch_iterator undef_branches_begin() { return UndefBranches.begin(); }
|
||||||
undef_branch_iterator undef_branches_end() { return UndefBranches.end(); }
|
undef_branch_iterator undef_branches_end() { return UndefBranches.end(); }
|
||||||
|
|
||||||
typedef ErrorNodes::iterator null_deref_iterator;
|
|
||||||
null_deref_iterator null_derefs_begin() { return ExplicitNullDeref.begin(); }
|
|
||||||
null_deref_iterator null_derefs_end() { return ExplicitNullDeref.end(); }
|
|
||||||
|
|
||||||
null_deref_iterator implicit_null_derefs_begin() {
|
|
||||||
return ImplicitNullDeref.begin();
|
|
||||||
}
|
|
||||||
null_deref_iterator implicit_null_derefs_end() {
|
|
||||||
return ImplicitNullDeref.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef ErrorNodes::iterator nil_receiver_struct_ret_iterator;
|
typedef ErrorNodes::iterator nil_receiver_struct_ret_iterator;
|
||||||
|
|
||||||
nil_receiver_struct_ret_iterator nil_receiver_struct_ret_begin() {
|
nil_receiver_struct_ret_iterator nil_receiver_struct_ret_begin() {
|
||||||
|
@ -312,10 +292,6 @@ public:
|
||||||
return NilReceiverLargerThanVoidPtrRetExplicit.end();
|
return NilReceiverLargerThanVoidPtrRetExplicit.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef ErrorNodes::iterator undef_deref_iterator;
|
|
||||||
undef_deref_iterator undef_derefs_begin() { return UndefDeref.begin(); }
|
|
||||||
undef_deref_iterator undef_derefs_end() { return UndefDeref.end(); }
|
|
||||||
|
|
||||||
typedef ErrorNodes::iterator undef_result_iterator;
|
typedef ErrorNodes::iterator undef_result_iterator;
|
||||||
undef_result_iterator undef_results_begin() { return UndefResults.begin(); }
|
undef_result_iterator undef_results_begin() { return UndefResults.begin(); }
|
||||||
undef_result_iterator undef_results_end() { return UndefResults.end(); }
|
undef_result_iterator undef_results_end() { return UndefResults.end(); }
|
||||||
|
@ -325,9 +301,6 @@ public:
|
||||||
bad_calls_iterator bad_calls_end() { return BadCalls.end(); }
|
bad_calls_iterator bad_calls_end() { return BadCalls.end(); }
|
||||||
|
|
||||||
typedef UndefArgsTy::iterator undef_arg_iterator;
|
typedef UndefArgsTy::iterator undef_arg_iterator;
|
||||||
undef_arg_iterator undef_arg_begin() { return UndefArgs.begin(); }
|
|
||||||
undef_arg_iterator undef_arg_end() { return UndefArgs.end(); }
|
|
||||||
|
|
||||||
undef_arg_iterator msg_expr_undef_arg_begin() {
|
undef_arg_iterator msg_expr_undef_arg_begin() {
|
||||||
return MsgExprUndefArgs.begin();
|
return MsgExprUndefArgs.begin();
|
||||||
}
|
}
|
||||||
|
@ -427,7 +400,11 @@ public:
|
||||||
protected:
|
protected:
|
||||||
/// CheckerVisit - Dispatcher for performing checker-specific logic
|
/// CheckerVisit - Dispatcher for performing checker-specific logic
|
||||||
/// at specific statements.
|
/// at specific statements.
|
||||||
void CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src, bool isPrevisit);
|
void CheckerVisit(Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
|
||||||
|
bool isPrevisit);
|
||||||
|
|
||||||
|
ExplodedNode *CheckerVisitLocation(Stmt *S, ExplodedNode *Pred,
|
||||||
|
const GRState *state, SVal V);
|
||||||
|
|
||||||
/// Visit - Transfer function logic for all statements. Dispatches to
|
/// Visit - Transfer function logic for all statements. Dispatches to
|
||||||
/// other functions that handle specific kinds of statements.
|
/// other functions that handle specific kinds of statements.
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
//== NullDerefChecker - Null dereference checker ----------------*- C++ -*--==//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLVM_CLANG_NULLDEREFCHECKER
|
||||||
|
#define LLVM_CLANG_NULLDEREFCHECKER
|
||||||
|
|
||||||
|
#include "clang/Analysis/PathSensitive/Checker.h"
|
||||||
|
#include "clang/Analysis/PathSensitive/BugType.h"
|
||||||
|
|
||||||
|
namespace clang {
|
||||||
|
|
||||||
|
class ExplodedNode;
|
||||||
|
|
||||||
|
class NullDeref : public BuiltinBug {
|
||||||
|
public:
|
||||||
|
NullDeref()
|
||||||
|
: BuiltinBug(0, "Null dereference", "Dereference of null pointer") {}
|
||||||
|
|
||||||
|
void registerInitialVisitors(BugReporterContext& BRC,
|
||||||
|
const ExplodedNode* N,
|
||||||
|
BuiltinBugReport *R);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NullDerefChecker : public Checker {
|
||||||
|
NullDeref *BT;
|
||||||
|
llvm::SmallVector<ExplodedNode*, 2> ImplicitNullDerefNodes;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
NullDerefChecker() : BT(0) {}
|
||||||
|
ExplodedNode *CheckLocation(const Stmt *S, ExplodedNode *Pred,
|
||||||
|
const GRState *state, SVal V,GRExprEngine &Eng);
|
||||||
|
|
||||||
|
static void *getTag() {
|
||||||
|
static int x = 0;
|
||||||
|
return &x;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef llvm::SmallVectorImpl<ExplodedNode*>::iterator iterator;
|
||||||
|
iterator implicit_nodes_begin() { return ImplicitNullDerefNodes.begin(); }
|
||||||
|
iterator implicit_nodes_end() { return ImplicitNullDerefNodes.end(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end clang namespace
|
||||||
|
#endif
|
|
@ -18,6 +18,7 @@
|
||||||
#include "clang/Analysis/LocalCheckers.h"
|
#include "clang/Analysis/LocalCheckers.h"
|
||||||
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
||||||
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
|
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
|
||||||
|
#include "clang/Analysis/PathSensitive/NullDerefChecker.h"
|
||||||
#include "BasicObjCFoundationChecks.h"
|
#include "BasicObjCFoundationChecks.h"
|
||||||
#include "llvm/Support/Compiler.h"
|
#include "llvm/Support/Compiler.h"
|
||||||
#include "clang/AST/DeclObjC.h"
|
#include "clang/AST/DeclObjC.h"
|
||||||
|
@ -208,8 +209,9 @@ void NSErrorCheck::CheckParamDeref(const VarDecl *Param,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Iterate over the implicit-null dereferences.
|
// Iterate over the implicit-null dereferences.
|
||||||
for (GRExprEngine::null_deref_iterator I=Eng.implicit_null_derefs_begin(),
|
NullDerefChecker *Checker = Eng.getChecker<NullDerefChecker>();
|
||||||
E=Eng.implicit_null_derefs_end(); I!=E; ++I) {
|
for (NullDerefChecker::iterator I = Checker->implicit_nodes_begin(),
|
||||||
|
E = Checker->implicit_nodes_end(); I != E; ++I) {
|
||||||
|
|
||||||
const GRState *state = (*I)->getState();
|
const GRState *state = (*I)->getState();
|
||||||
const SVal* X = state->get<GRState::NullDerefTag>();
|
const SVal* X = state->get<GRState::NullDerefTag>();
|
||||||
|
|
|
@ -118,17 +118,20 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
|
||||||
ExplodedNodeSet Tmp;
|
ExplodedNodeSet Tmp;
|
||||||
ExplodedNodeSet *PrevSet = &Src;
|
ExplodedNodeSet *PrevSet = &Src;
|
||||||
|
|
||||||
for (std::vector<Checker*>::iterator I = Checkers.begin(), E = Checkers.end();
|
for (llvm::DenseMap<void*, Checker*>::iterator I = Checkers.begin(),
|
||||||
I != E; ++I) {
|
E = Checkers.end(); I != E; ++I) {
|
||||||
|
|
||||||
ExplodedNodeSet *CurrSet = (I+1 == E) ? &Dst
|
llvm::DenseMap<void*, Checker*>::iterator X = I;
|
||||||
|
|
||||||
|
ExplodedNodeSet *CurrSet = (++X == E) ? &Dst
|
||||||
: (PrevSet == &Tmp) ? &Src : &Tmp;
|
: (PrevSet == &Tmp) ? &Src : &Tmp;
|
||||||
CurrSet->clear();
|
CurrSet->clear();
|
||||||
Checker *checker = *I;
|
void *tag = I->first;
|
||||||
|
Checker *checker = I->second;
|
||||||
|
|
||||||
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
|
for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
|
||||||
NI != NE; ++NI)
|
NI != NE; ++NI)
|
||||||
checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, isPrevisit);
|
checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, isPrevisit);
|
||||||
|
|
||||||
// Update which NodeSet is the current one.
|
// Update which NodeSet is the current one.
|
||||||
PrevSet = CurrSet;
|
PrevSet = CurrSet;
|
||||||
|
@ -138,6 +141,21 @@ void GRExprEngine::CheckerVisit(Stmt *S, ExplodedNodeSet &Dst,
|
||||||
// automatically.
|
// automatically.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExplodedNode *GRExprEngine::CheckerVisitLocation(Stmt *S, ExplodedNode *Pred,
|
||||||
|
const GRState *state, SVal V) {
|
||||||
|
if (Checkers.empty())
|
||||||
|
return Pred;
|
||||||
|
|
||||||
|
for (llvm::DenseMap<void*, Checker*>::iterator I = Checkers.begin(),
|
||||||
|
E = Checkers.end(); I != E; ++I) {
|
||||||
|
Pred = I->second->CheckLocation(S, Pred, state, V, *this);
|
||||||
|
if (!Pred)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pred;
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Engine construction and deletion.
|
// Engine construction and deletion.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -166,9 +184,9 @@ GRExprEngine::GRExprEngine(AnalysisManager &mgr)
|
||||||
GRExprEngine::~GRExprEngine() {
|
GRExprEngine::~GRExprEngine() {
|
||||||
BR.FlushReports();
|
BR.FlushReports();
|
||||||
delete [] NSExceptionInstanceRaiseSelectors;
|
delete [] NSExceptionInstanceRaiseSelectors;
|
||||||
for (std::vector<Checker*>::iterator I=Checkers.begin(), E=Checkers.end();
|
for (llvm::DenseMap<void*, Checker*>::iterator I=Checkers.begin(),
|
||||||
I!=E; ++I)
|
E=Checkers.end(); I!=E; ++I)
|
||||||
delete *I;
|
delete I->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -1188,58 +1206,11 @@ ExplodedNode* GRExprEngine::EvalLocation(Stmt* Ex, ExplodedNode* Pred,
|
||||||
SaveAndRestore<const void*> OldTag(Builder->Tag);
|
SaveAndRestore<const void*> OldTag(Builder->Tag);
|
||||||
Builder->Tag = tag;
|
Builder->Tag = tag;
|
||||||
|
|
||||||
// Check for loads/stores from/to undefined values.
|
|
||||||
if (location.isUndef()) {
|
|
||||||
ExplodedNode* N =
|
|
||||||
Builder->generateNode(Ex, state, Pred,
|
|
||||||
ProgramPoint::PostUndefLocationCheckFailedKind);
|
|
||||||
|
|
||||||
if (N) {
|
|
||||||
N->markAsSink();
|
|
||||||
UndefDeref.insert(N);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for loads/stores from/to unknown locations. Treat as No-Ops.
|
|
||||||
if (location.isUnknown())
|
if (location.isUnknown())
|
||||||
return Pred;
|
return Pred;
|
||||||
|
|
||||||
// During a load, one of two possible situations arise:
|
return CheckerVisitLocation(Ex, Pred, state, location);
|
||||||
// (1) A crash, because the location (pointer) was NULL.
|
|
||||||
// (2) The location (pointer) is not NULL, and the dereference works.
|
|
||||||
//
|
|
||||||
// We add these assumptions.
|
|
||||||
|
|
||||||
Loc LV = cast<Loc>(location);
|
|
||||||
|
|
||||||
// "Assume" that the pointer is not NULL.
|
|
||||||
const GRState *StNotNull = state->Assume(LV, true);
|
|
||||||
|
|
||||||
// "Assume" that the pointer is NULL.
|
|
||||||
const GRState *StNull = state->Assume(LV, false);
|
|
||||||
|
|
||||||
if (StNull) {
|
|
||||||
// Use the Generic Data Map to mark in the state what lval was null.
|
|
||||||
const SVal* PersistentLV = getBasicVals().getPersistentSVal(LV);
|
|
||||||
StNull = StNull->set<GRState::NullDerefTag>(PersistentLV);
|
|
||||||
|
|
||||||
// We don't use "MakeNode" here because the node will be a sink
|
|
||||||
// and we have no intention of processing it later.
|
|
||||||
ExplodedNode* NullNode =
|
|
||||||
Builder->generateNode(Ex, StNull, Pred,
|
|
||||||
ProgramPoint::PostNullCheckFailedKind);
|
|
||||||
|
|
||||||
if (NullNode) {
|
|
||||||
NullNode->markAsSink();
|
|
||||||
if (StNotNull) ImplicitNullDeref.insert(NullNode);
|
|
||||||
else ExplicitNullDeref.insert(NullNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!StNotNull)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// FIXME: Temporarily disable out-of-bounds checking until we make
|
// FIXME: Temporarily disable out-of-bounds checking until we make
|
||||||
// the logic reflect recent changes to CastRegion and friends.
|
// the logic reflect recent changes to CastRegion and friends.
|
||||||
|
@ -1282,10 +1253,6 @@ ExplodedNode* GRExprEngine::EvalLocation(Stmt* Ex, ExplodedNode* Pred,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Generate a new node indicating the checks succeed.
|
|
||||||
return Builder->generateNode(Ex, StNotNull, Pred,
|
|
||||||
ProgramPoint::PostLocationChecksSucceedKind);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -2895,7 +2862,8 @@ namespace llvm {
|
||||||
template<>
|
template<>
|
||||||
struct VISIBILITY_HIDDEN DOTGraphTraits<ExplodedNode*> :
|
struct VISIBILITY_HIDDEN DOTGraphTraits<ExplodedNode*> :
|
||||||
public DefaultDOTGraphTraits {
|
public DefaultDOTGraphTraits {
|
||||||
|
// FIXME: Since we do not cache error nodes in GRExprEngine now, this does not
|
||||||
|
// work.
|
||||||
static std::string getNodeAttributes(const ExplodedNode* N, void*) {
|
static std::string getNodeAttributes(const ExplodedNode* N, void*) {
|
||||||
|
|
||||||
if (GraphPrintCheckerState->isImplicitNullDeref(N) ||
|
if (GraphPrintCheckerState->isImplicitNullDeref(N) ||
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
||||||
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
|
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
|
||||||
#include "clang/Analysis/PathSensitive/CheckerVisitor.h"
|
#include "clang/Analysis/PathSensitive/CheckerVisitor.h"
|
||||||
|
#include "clang/Analysis/PathSensitive/NullDerefChecker.h"
|
||||||
#include "clang/Analysis/PathDiagnostic.h"
|
#include "clang/Analysis/PathDiagnostic.h"
|
||||||
#include "clang/Basic/SourceManager.h"
|
#include "clang/Basic/SourceManager.h"
|
||||||
#include "llvm/Support/Compiler.h"
|
#include "llvm/Support/Compiler.h"
|
||||||
|
@ -40,10 +41,8 @@ ExplodedNode* GetNode(GRExprEngine::undef_arg_iterator I) {
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Bug Descriptions.
|
// Bug Descriptions.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
namespace clang {
|
||||||
namespace {
|
class BuiltinBugReport : public RangedBugReport {
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN BuiltinBugReport : public RangedBugReport {
|
|
||||||
public:
|
public:
|
||||||
BuiltinBugReport(BugType& bt, const char* desc,
|
BuiltinBugReport(BugType& bt, const char* desc,
|
||||||
ExplodedNode *n)
|
ExplodedNode *n)
|
||||||
|
@ -57,57 +56,21 @@ public:
|
||||||
const ExplodedNode* N);
|
const ExplodedNode* N);
|
||||||
};
|
};
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN BuiltinBug : public BugType {
|
void BuiltinBugReport::registerInitialVisitors(BugReporterContext& BRC,
|
||||||
GRExprEngine &Eng;
|
const ExplodedNode* N) {
|
||||||
protected:
|
static_cast<BuiltinBug&>(getBugType()).registerInitialVisitors(BRC, N, this);
|
||||||
const std::string desc;
|
}
|
||||||
public:
|
|
||||||
BuiltinBug(GRExprEngine *eng, const char* n, const char* d)
|
|
||||||
: BugType(n, "Logic errors"), Eng(*eng), desc(d) {}
|
|
||||||
|
|
||||||
BuiltinBug(GRExprEngine *eng, const char* n)
|
|
||||||
: BugType(n, "Logic errors"), Eng(*eng), desc(n) {}
|
|
||||||
|
|
||||||
const std::string &getDescription() const { return desc; }
|
|
||||||
|
|
||||||
virtual void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) {}
|
|
||||||
|
|
||||||
void FlushReports(BugReporter& BR) { FlushReportsImpl(BR, Eng); }
|
|
||||||
|
|
||||||
virtual void registerInitialVisitors(BugReporterContext& BRC,
|
|
||||||
const ExplodedNode* N,
|
|
||||||
BuiltinBugReport *R) {}
|
|
||||||
|
|
||||||
template <typename ITER> void Emit(BugReporter& BR, ITER I, ITER E);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <typename ITER>
|
template <typename ITER>
|
||||||
void BuiltinBug::Emit(BugReporter& BR, ITER I, ITER E) {
|
void BuiltinBug::Emit(BugReporter& BR, ITER I, ITER E) {
|
||||||
for (; I != E; ++I) BR.EmitReport(new BuiltinBugReport(*this, desc.c_str(),
|
for (; I != E; ++I) BR.EmitReport(new BuiltinBugReport(*this, desc.c_str(),
|
||||||
GetNode(I)));
|
GetNode(I)));
|
||||||
}
|
}
|
||||||
|
void NullDeref::registerInitialVisitors(BugReporterContext& BRC,
|
||||||
void BuiltinBugReport::registerInitialVisitors(BugReporterContext& BRC,
|
|
||||||
const ExplodedNode* N) {
|
|
||||||
static_cast<BuiltinBug&>(getBugType()).registerInitialVisitors(BRC, N, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN NullDeref : public BuiltinBug {
|
|
||||||
public:
|
|
||||||
NullDeref(GRExprEngine* eng)
|
|
||||||
: BuiltinBug(eng,"Null dereference", "Dereference of null pointer") {}
|
|
||||||
|
|
||||||
void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) {
|
|
||||||
Emit(BR, Eng.null_derefs_begin(), Eng.null_derefs_end());
|
|
||||||
}
|
|
||||||
|
|
||||||
void registerInitialVisitors(BugReporterContext& BRC,
|
|
||||||
const ExplodedNode* N,
|
const ExplodedNode* N,
|
||||||
BuiltinBugReport *R) {
|
BuiltinBugReport *R) {
|
||||||
registerTrackNullOrUndefValue(BRC, GetDerefExpr(N), N);
|
registerTrackNullOrUndefValue(BRC, bugreporter::GetDerefExpr(N), N);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN NilReceiverStructRet : public BuiltinBug {
|
class VISIBILITY_HIDDEN NilReceiverStructRet : public BuiltinBug {
|
||||||
public:
|
public:
|
||||||
|
@ -175,14 +138,12 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class VISIBILITY_HIDDEN UndefinedDeref : public BuiltinBug {
|
class VISIBILITY_HIDDEN UndefinedDeref : public BuiltinBug {
|
||||||
public:
|
public:
|
||||||
UndefinedDeref(GRExprEngine* eng)
|
UndefinedDeref()
|
||||||
: BuiltinBug(eng,"Dereference of undefined pointer value") {}
|
: BuiltinBug(0, "Dereference of undefined pointer value") {}
|
||||||
|
|
||||||
void FlushReportsImpl(BugReporter& BR, GRExprEngine& Eng) {
|
|
||||||
Emit(BR, Eng.undef_derefs_begin(), Eng.undef_derefs_end());
|
|
||||||
}
|
|
||||||
|
|
||||||
void registerInitialVisitors(BugReporterContext& BRC,
|
void registerInitialVisitors(BugReporterContext& BRC,
|
||||||
const ExplodedNode* N,
|
const ExplodedNode* N,
|
||||||
|
@ -595,7 +556,7 @@ public:
|
||||||
CheckAttrNonNull() : BT(0) {}
|
CheckAttrNonNull() : BT(0) {}
|
||||||
~CheckAttrNonNull() {}
|
~CheckAttrNonNull() {}
|
||||||
|
|
||||||
const void *getTag() {
|
static void *getTag() {
|
||||||
static int x = 0;
|
static int x = 0;
|
||||||
return &x;
|
return &x;
|
||||||
}
|
}
|
||||||
|
@ -676,10 +637,9 @@ public:
|
||||||
C.addTransition(C.GenerateNode(CE, state));
|
C.addTransition(C.GenerateNode(CE, state));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // end anonymous namespace
|
|
||||||
|
|
||||||
// Undefined arguments checking.
|
// Undefined arguments checking.
|
||||||
namespace {
|
|
||||||
class VISIBILITY_HIDDEN CheckUndefinedArg
|
class VISIBILITY_HIDDEN CheckUndefinedArg
|
||||||
: public CheckerVisitor<CheckUndefinedArg> {
|
: public CheckerVisitor<CheckUndefinedArg> {
|
||||||
|
|
||||||
|
@ -689,7 +649,7 @@ public:
|
||||||
CheckUndefinedArg() : BT(0) {}
|
CheckUndefinedArg() : BT(0) {}
|
||||||
~CheckUndefinedArg() {}
|
~CheckUndefinedArg() {}
|
||||||
|
|
||||||
const void *getTag() {
|
static void *getTag() {
|
||||||
static int x = 0;
|
static int x = 0;
|
||||||
return &x;
|
return &x;
|
||||||
}
|
}
|
||||||
|
@ -721,7 +681,7 @@ public:
|
||||||
CheckBadCall() : BT(0) {}
|
CheckBadCall() : BT(0) {}
|
||||||
~CheckBadCall() {}
|
~CheckBadCall() {}
|
||||||
|
|
||||||
const void *getTag() {
|
static void *getTag() {
|
||||||
static int x = 0;
|
static int x = 0;
|
||||||
return &x;
|
return &x;
|
||||||
}
|
}
|
||||||
|
@ -748,7 +708,7 @@ public:
|
||||||
CheckDivZero() : BT(0) {}
|
CheckDivZero() : BT(0) {}
|
||||||
~CheckDivZero() {}
|
~CheckDivZero() {}
|
||||||
|
|
||||||
const void *getTag() {
|
static void *getTag() {
|
||||||
static int x;
|
static int x;
|
||||||
return &x;
|
return &x;
|
||||||
}
|
}
|
||||||
|
@ -797,7 +757,85 @@ void CheckDivZero::PreVisitBinaryOperator(CheckerContext &C,
|
||||||
if (stateNotZero != C.getState())
|
if (stateNotZero != C.getState())
|
||||||
C.addTransition(C.GenerateNode(B, stateNotZero));
|
C.addTransition(C.GenerateNode(B, stateNotZero));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class VISIBILITY_HIDDEN CheckUndefDeref : public Checker {
|
||||||
|
UndefinedDeref *BT;
|
||||||
|
public:
|
||||||
|
CheckUndefDeref() : BT(0) {}
|
||||||
|
|
||||||
|
ExplodedNode *CheckSVal(const Stmt *S, ExplodedNode *Pred,
|
||||||
|
const GRState *state, SVal V, GRExprEngine &Eng);
|
||||||
|
|
||||||
|
static void *getTag() {
|
||||||
|
static int x = 0;
|
||||||
|
return &x;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ExplodedNode *CheckUndefDeref::CheckSVal(const Stmt *S, ExplodedNode *Pred,
|
||||||
|
const GRState *state, SVal V,
|
||||||
|
GRExprEngine &Eng) {
|
||||||
|
GRStmtNodeBuilder &Builder = Eng.getBuilder();
|
||||||
|
BugReporter &BR = Eng.getBugReporter();
|
||||||
|
|
||||||
|
if (V.isUndef()) {
|
||||||
|
ExplodedNode *N = Builder.generateNode(S, state, Pred,
|
||||||
|
ProgramPoint::PostUndefLocationCheckFailedKind);
|
||||||
|
if (N) {
|
||||||
|
if (!BT)
|
||||||
|
BT = new UndefinedDeref();
|
||||||
|
|
||||||
|
N->markAsSink();
|
||||||
|
BR.EmitReport(new BuiltinBugReport(*BT, BT->getDescription().c_str(), N));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pred;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExplodedNode *NullDerefChecker::CheckLocation(const Stmt *S, ExplodedNode *Pred,
|
||||||
|
const GRState *state, SVal V,
|
||||||
|
GRExprEngine &Eng) {
|
||||||
|
Loc *LV = dyn_cast<Loc>(&V);
|
||||||
|
|
||||||
|
// If the value is not a location, don't touch the node.
|
||||||
|
if (!LV)
|
||||||
|
return Pred;
|
||||||
|
|
||||||
|
const GRState *NotNullState = state->Assume(*LV, true);
|
||||||
|
const GRState *NullState = state->Assume(*LV, false);
|
||||||
|
|
||||||
|
GRStmtNodeBuilder &Builder = Eng.getBuilder();
|
||||||
|
BugReporter &BR = Eng.getBugReporter();
|
||||||
|
|
||||||
|
// The explicit NULL case.
|
||||||
|
if (NullState) {
|
||||||
|
// Use the GDM to mark in the state what lval was null.
|
||||||
|
const SVal *PersistentLV = Eng.getBasicVals().getPersistentSVal(*LV);
|
||||||
|
NullState = NullState->set<GRState::NullDerefTag>(PersistentLV);
|
||||||
|
|
||||||
|
ExplodedNode *N = Builder.generateNode(S, NullState, Pred,
|
||||||
|
ProgramPoint::PostNullCheckFailedKind);
|
||||||
|
if (N) {
|
||||||
|
N->markAsSink();
|
||||||
|
|
||||||
|
if (!NotNullState) { // Explicit null case.
|
||||||
|
if (!BT)
|
||||||
|
BT = new NullDeref();
|
||||||
|
BR.EmitReport(new BuiltinBugReport(*BT,BT->getDescription().c_str(),N));
|
||||||
|
return 0;
|
||||||
|
} else // Implicit null case.
|
||||||
|
ImplicitNullDerefNodes.push_back(N);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NotNullState)
|
||||||
|
return 0;
|
||||||
|
return Builder.generateNode(S, NotNullState, Pred,
|
||||||
|
ProgramPoint::PostLocationChecksSucceedKind);
|
||||||
|
}
|
||||||
|
} // end clang namespace
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Check registration.
|
// Check registration.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -808,8 +846,6 @@ void GRExprEngine::RegisterInternalChecks() {
|
||||||
// create BugReports on-the-fly but instead wait until GRExprEngine finishes
|
// create BugReports on-the-fly but instead wait until GRExprEngine finishes
|
||||||
// analyzing a function. Generation of BugReport objects is done via a call
|
// analyzing a function. Generation of BugReport objects is done via a call
|
||||||
// to 'FlushReports' from BugReporter.
|
// to 'FlushReports' from BugReporter.
|
||||||
BR.Register(new NullDeref(this));
|
|
||||||
BR.Register(new UndefinedDeref(this));
|
|
||||||
BR.Register(new UndefBranch(this));
|
BR.Register(new UndefBranch(this));
|
||||||
BR.Register(new UndefResult(this));
|
BR.Register(new UndefResult(this));
|
||||||
BR.Register(new RetStack(this));
|
BR.Register(new RetStack(this));
|
||||||
|
@ -826,8 +862,10 @@ void GRExprEngine::RegisterInternalChecks() {
|
||||||
// their associated BugType will get registered with the BugReporter
|
// their associated BugType will get registered with the BugReporter
|
||||||
// automatically. Note that the check itself is owned by the GRExprEngine
|
// automatically. Note that the check itself is owned by the GRExprEngine
|
||||||
// object.
|
// object.
|
||||||
registerCheck(new CheckAttrNonNull());
|
registerCheck<CheckAttrNonNull>(new CheckAttrNonNull());
|
||||||
registerCheck(new CheckUndefinedArg());
|
registerCheck<CheckUndefinedArg>(new CheckUndefinedArg());
|
||||||
registerCheck(new CheckBadCall());
|
registerCheck<CheckBadCall>(new CheckBadCall());
|
||||||
registerCheck(new CheckDivZero());
|
registerCheck<CheckDivZero>(new CheckDivZero());
|
||||||
|
registerCheck<CheckUndefDeref>(new CheckUndefDeref());
|
||||||
|
registerCheck<NullDerefChecker>(new NullDerefChecker());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue