Added "GRAuditor" and "GRSimpleAPICheck" interface to allow simple stateless checkers to be injected into the analyzer.
Added "AnnotatedPath" class to record an annotated path that will be useful for inspecting paths.
Added some boilerplate code for simple checks of Apple's Foundation API.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@48867 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Analysis/PathSensitive/AnnotatedPath.h b/include/clang/Analysis/PathSensitive/AnnotatedPath.h
new file mode 100644
index 0000000..299634b
--- /dev/null
+++ b/include/clang/Analysis/PathSensitive/AnnotatedPath.h
@@ -0,0 +1,65 @@
+//=-- AnnotatedPath.h - An annotated list of ExplodedNodes -*- 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 AnnotatedPath, which represents a collection of
+// annotated ExplodedNodes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_ANNOTPATH
+#define LLVM_CLANG_ANALYSIS_ANNOTPATH
+
+#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
+#include <string>
+#include <list>
+
+namespace clang {
+
+ class Expr;
+
+template <typename STATE>
+class AnnotatedNode {
+ ExplodedNode<STATE> *Node;
+ std::string annotation;
+ Expr* E;
+
+public:
+ AnnotatedNode(ExplodedNode<STATE>* N, const std::string& annot,
+ Expr* e = NULL)
+ : Node(N), annotation(annot), E(e) {}
+
+ ExplodedNode<STATE> getNode() const { return Node; }
+
+ const std::string& getString() const { return annotation; }
+
+ Expr* getExpr() const { return E; }
+};
+
+
+template <typename STATE>
+class AnnotatedPath {
+ typedef std::list<AnnotatedNode<STATE> > impl;
+ impl path;
+public:
+ AnnotatedPath();
+
+ void push_back(ExplodedNode<STATE>* N, const std::string& s, Expr* E = NULL) {
+ path.push_back(AnnotatedNode<STATE>(N, s, E));
+ }
+
+ typedef typename impl::iterator iterator;
+
+ iterator begin() { return path.begin(); }
+ iterator end() { return path.end(); }
+
+};
+
+} // end clang namespace
+
+#endif
diff --git a/include/clang/Analysis/PathSensitive/GRAuditor.h b/include/clang/Analysis/PathSensitive/GRAuditor.h
new file mode 100644
index 0000000..29fb3bc
--- /dev/null
+++ b/include/clang/Analysis/PathSensitive/GRAuditor.h
@@ -0,0 +1,38 @@
+//==- GRAuditor.h - Observers of the creation of ExplodedNodes------*- 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 GRAuditor and its primary subclasses, an interface
+// to audit the creation of ExplodedNodes. This interface can be used
+// to implement simple checkers that do not mutate analysis state but
+// instead operate by perfoming simple logical checks at key monitoring
+// locations (e.g., function calls).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_GRAUDITOR
+#define LLVM_CLANG_ANALYSIS_GRAUDITOR
+
+#include "clang/AST/Expr.h"
+#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
+
+namespace clang {
+
+template <typename STATE>
+class GRAuditor {
+public:
+ typedef ExplodedNode<STATE> NodeTy;
+
+ virtual ~GRAuditor() {}
+ virtual bool Audit(NodeTy* N) = 0;
+};
+
+
+} // end clang namespace
+
+#endif
diff --git a/include/clang/Analysis/PathSensitive/GRCoreEngine.h b/include/clang/Analysis/PathSensitive/GRCoreEngine.h
index a00292b..e22df35 100644
--- a/include/clang/Analysis/PathSensitive/GRCoreEngine.h
+++ b/include/clang/Analysis/PathSensitive/GRCoreEngine.h
@@ -15,7 +15,7 @@
#ifndef LLVM_CLANG_ANALYSIS_GRENGINE
#define LLVM_CLANG_ANALYSIS_GRENGINE
-#include "clang/AST/Stmt.h"
+#include "clang/AST/Expr.h"
#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
#include "clang/Analysis/PathSensitive/GRWorkList.h"
#include "clang/Analysis/PathSensitive/GRBlockCounter.h"
@@ -163,6 +163,16 @@
CFGBlock* getBlock() const { return &B; }
};
+template <typename STATE>
+class GRNodeAuditor {
+public:
+ typedef ExplodedNode<STATE> NodeTy;
+
+ virtual ~GRNodeAuditor() {}
+ virtual bool Audit(NodeTy* N) = 0;
+};
+
+
template<typename STATE>
class GRStmtNodeBuilder {
typedef STATE StateTy;
@@ -171,10 +181,28 @@
GRStmtNodeBuilderImpl& NB;
StateTy* CleanedState;
+ GRNodeAuditor<StateTy> **CallExprAuditBeg, **CallExprAuditEnd;
+ GRNodeAuditor<StateTy> **ObjCMsgExprAuditBeg, **ObjCMsgExprAuditEnd;
+
public:
- GRStmtNodeBuilder(GRStmtNodeBuilderImpl& nb) : NB(nb), BuildSinks(false) {
+ GRStmtNodeBuilder(GRStmtNodeBuilderImpl& nb) : NB(nb),
+ CallExprAuditBeg(0), CallExprAuditEnd(0),
+ ObjCMsgExprAuditBeg(0), ObjCMsgExprAuditEnd(0), BuildSinks(false) {
+
CleanedState = getLastNode()->getState();
}
+
+ void setObjCMsgExprAuditors(GRNodeAuditor<StateTy> **B,
+ GRNodeAuditor<StateTy> **E) {
+ ObjCMsgExprAuditBeg = B;
+ ObjCMsgExprAuditEnd = E;
+ }
+
+ void setCallExprAuditors(GRNodeAuditor<StateTy> **B,
+ GRNodeAuditor<StateTy> **E) {
+ CallExprAuditBeg = B;
+ CallExprAuditEnd = E;
+ }
NodeTy* getLastNode() const {
return static_cast<NodeTy*>(NB.getLastNode());
@@ -223,8 +251,18 @@
if (N) {
if (BuildSinks)
N->markAsSink();
- else
+ else {
Dst.Add(N);
+
+ if (isa<CallExpr>(S))
+ for (GRNodeAuditor<StateTy>** I = CallExprAuditBeg;
+ I != CallExprAuditEnd; ++I)
+ (*I)->Audit(N);
+ else if (isa<ObjCMessageExpr>(S))
+ for (GRNodeAuditor<StateTy>** I = ObjCMsgExprAuditBeg;
+ I != ObjCMsgExprAuditEnd; ++I)
+ (*I)->Audit(N);
+ }
}
return N;
diff --git a/include/clang/Analysis/PathSensitive/GRExprEngine.h b/include/clang/Analysis/PathSensitive/GRExprEngine.h
index 96a6d4a..b04469e 100644
--- a/include/clang/Analysis/PathSensitive/GRExprEngine.h
+++ b/include/clang/Analysis/PathSensitive/GRExprEngine.h
@@ -15,10 +15,13 @@
#include "clang/Analysis/PathSensitive/GRCoreEngine.h"
#include "clang/Analysis/PathSensitive/ValueState.h"
+#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
#include "clang/Analysis/PathSensitive/GRTransferFuncs.h"
namespace clang {
+ class StatelessChecks;
+
class GRExprEngine {
public:
@@ -32,6 +35,7 @@
typedef GRIndirectGotoNodeBuilder<GRExprEngine> IndirectGotoNodeBuilder;
typedef GRSwitchNodeBuilder<GRExprEngine> SwitchNodeBuilder;
typedef ExplodedNodeSet<StateTy> NodeSet;
+
protected:
/// G - the simulation graph.
@@ -67,6 +71,12 @@
/// CurrentStmt - The current block-level statement.
Stmt* CurrentStmt;
+
+ typedef llvm::SmallVector<GRSimpleAPICheck*,2> SimpleChecksTy;
+
+ SimpleChecksTy CallChecks;
+ SimpleChecksTy MsgExprChecks;
+
typedef llvm::SmallPtrSet<NodeTy*,2> UndefBranchesTy;
typedef llvm::SmallPtrSet<NodeTy*,2> UndefStoresTy;
@@ -278,6 +288,26 @@
return UndefReceivers.end();
}
+ typedef SimpleChecksTy::iterator simple_checks_iterator;
+
+ simple_checks_iterator call_auditors_begin() { return CallChecks.begin(); }
+ simple_checks_iterator call_auditors_end() { return CallChecks.end(); }
+
+ simple_checks_iterator msgexpr_auditors_begin() {
+ return MsgExprChecks.begin();
+ }
+ simple_checks_iterator msgexpr_auditors_end() {
+ return MsgExprChecks.end();
+ }
+
+ void AddCallCheck(GRSimpleAPICheck* A) {
+ CallChecks.push_back(A);
+ }
+
+ void AddObjCMessageExprCheck(GRSimpleAPICheck* A) {
+ MsgExprChecks.push_back(A);
+ }
+
/// ProcessStmt - Called by GRCoreEngine. Used to generate new successor
/// nodes by processing the 'effects' of a block-level statement.
void ProcessStmt(Stmt* S, StmtNodeBuilder& builder);
diff --git a/include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h b/include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h
new file mode 100644
index 0000000..2da2cb4
--- /dev/null
+++ b/include/clang/Analysis/PathSensitive/GRSimpleAPICheck.h
@@ -0,0 +1,35 @@
+// GRCheckAPI.h - Simple API checks based on GRAuditor ------------*- 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 the interface for building simple, path-sensitive checks
+// that are stateless and only emit warnings at errors that occur at
+// CallExpr or ObjCMessageExpr.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_GRAPICHECKS
+#define LLVM_CLANG_ANALYSIS_GRAPICHECKS
+
+#include "clang/Analysis/PathSensitive/GRAuditor.h"
+
+namespace clang {
+
+class ValueState;
+
+class GRSimpleAPICheck : public GRAuditor<ValueState> {
+public:
+ GRSimpleAPICheck() {}
+ virtual ~GRSimpleAPICheck() {}
+
+
+};
+
+} // end namespace clang
+
+#endif
diff --git a/lib/Analysis/BasicObjCFoundationChecks.cpp b/lib/Analysis/BasicObjCFoundationChecks.cpp
new file mode 100644
index 0000000..0fae7dd
--- /dev/null
+++ b/lib/Analysis/BasicObjCFoundationChecks.cpp
@@ -0,0 +1,139 @@
+//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- 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 BasicObjCFoundationChecks, a class that encapsulates
+// a set of simple checks to run on Objective-C code using Apple's Foundation
+// classes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
+#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
+#include "clang/Analysis/PathSensitive/ValueState.h"
+#include "clang/Analysis/PathSensitive/AnnotatedPath.h"
+#include "clang/Analysis/PathDiagnostic.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ASTContext.h"
+#include "llvm/Support/Compiler.h"
+
+#include <vector>
+
+using namespace clang;
+
+namespace {
+
+class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
+
+ ASTContext &Ctx;
+ ValueStateManager* VMgr;
+ std::list<AnnotatedPath<ValueState> > Errors;
+
+ RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
+
+ bool isNSString(ObjCInterfaceType* T, const char* suffix);
+ bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
+
+ void RegisterError(NodeTy* N, Expr* E, const char *msg);
+
+public:
+ BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
+ : Ctx(ctx), VMgr(vmgr) {}
+
+ virtual ~BasicObjCFoundationChecks() {}
+
+ virtual bool Audit(ExplodedNode<ValueState>* N);
+};
+
+} // end anonymous namespace
+
+
+bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
+
+ ObjCMessageExpr* ME =
+ cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
+
+ Expr* Receiver = ME->getReceiver();
+
+ if (!Receiver)
+ return false;
+
+ assert (Receiver->getType()->isPointerType());
+
+ const PointerType* T = Receiver->getType()->getAsPointerType();
+
+ ObjCInterfaceType* ReceiverType =
+ dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
+
+ if (!ReceiverType)
+ return false;
+
+ const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
+
+ if (name[0] != 'N' || name[1] != 'S')
+ return false;
+
+ name += 2;
+
+ // FIXME: Make all of this faster.
+
+ if (isNSString(ReceiverType, name))
+ return AuditNSString(N, ME);
+
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// Error reporting.
+//===----------------------------------------------------------------------===//
+
+
+void BasicObjCFoundationChecks::RegisterError(NodeTy* N,
+ Expr* E, const char *msg) {
+
+ Errors.push_back(AnnotatedPath<ValueState>());
+ Errors.back().push_back(N, msg, E);
+}
+
+//===----------------------------------------------------------------------===//
+// NSString checking.
+//===----------------------------------------------------------------------===//
+
+bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
+ const char* suffix) {
+
+ return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
+}
+
+bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
+ ObjCMessageExpr* ME) {
+
+ Selector S = ME->getSelector();
+
+ if (S.isUnarySelector())
+ return false;
+
+ // FIXME: This is going to be really slow doing these checks with
+ // lexical comparisons.
+
+ std::string name = S.getName();
+ ValueState* St = N->getState();
+
+ if (name == "compare:") {
+ // Check if the compared NSString is nil.
+ Expr * E = ME->getArg(0);
+ RVal X = GetRVal(St, E);
+
+ if (isa<lval::ConcreteInt>(X)) {
+ RegisterError(N, E,
+ "Argument to NSString method 'compare:' cannot be nil.");
+ }
+ }
+
+ return false;
+}
diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp
index bce1cbd..b6f164a 100644
--- a/lib/Analysis/GRExprEngine.cpp
+++ b/lib/Analysis/GRExprEngine.cpp
@@ -407,6 +407,19 @@
CurrentStmt = S;
NodeSet Dst;
+ // Set up our simple checks.
+
+ if (!MsgExprChecks.empty())
+ Builder->setObjCMsgExprAuditors(
+ (GRNodeAuditor<ValueState>**) &MsgExprChecks[0],
+ (GRNodeAuditor<ValueState>**) (&MsgExprChecks[0] + MsgExprChecks.size()));
+
+
+ if (!CallChecks.empty())
+ Builder->setCallExprAuditors(
+ (GRNodeAuditor<ValueState>**) &CallChecks[0],
+ (GRNodeAuditor<ValueState>**) (&CallChecks[0] + CallChecks.size()));
+
// Create the cleaned state.
CleanedState = StateMgr.RemoveDeadBindings(StmtEntryNode->getState(),