/// getImplicitObjectArgument - Retrieves the implicit object
/// argument for the member call. For example, in "x.f(5)", this
/// operation would return "x".
- Expr *getImplicitObjectArgument();
+ Expr *getImplicitObjectArgument() const;
+
+ /// Retrieves the declaration of the called method.
+ CXXMethodDecl *getMethodDecl() const;
/// getRecordDecl - Retrieves the CXXRecordDecl for the underlying type of
/// the implicit object argument. Note that this is may not be the same
/// VisitCall - Transfer function for function calls.
- void VisitCall(const CallExpr* CE, ExplodedNode* Pred,
- CallExpr::const_arg_iterator AI,
- CallExpr::const_arg_iterator AE,
- ExplodedNodeSet& Dst);
+ void VisitCallExpr(const CallExpr* CE, ExplodedNode* Pred,
+ ExplodedNodeSet& Dst);
/// VisitCast - Transfer function logic for all casts (implicit and explicit).
void VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNode *Pred,
const MemRegion *Dest, const Stmt *S,
ExplodedNode *Pred, ExplodedNodeSet &Dst);
- void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE, ExplodedNode *Pred,
- ExplodedNodeSet &Dst);
-
- void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *C,
- ExplodedNode *Pred, ExplodedNodeSet &Dst);
-
void VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
ExplodedNodeSet &Dst);
const FunctionProtoType *FnType,
ExplodedNode *Pred, ExplodedNodeSet &Dst,
bool FstArgAsLValue = false);
-
- /// Evaluate method call itself. Used for CXXMethodCallExpr and
- /// CXXOperatorCallExpr.
- void evalMethodCall(const CallExpr *MCE, const CXXMethodDecl *MD,
- const Expr *ThisExpr, ExplodedNode *Pred,
- ExplodedNodeSet &Src, ExplodedNodeSet &Dst);
+
+ /// Evaluate callee expression (for a function call).
+ void evalCallee(const CallExpr *callExpr, const ExplodedNodeSet &src,
+ ExplodedNodeSet &dest);
/// evalEagerlyAssume - Given the nodes in 'Src', eagerly assume symbolic
/// expressions of the form 'x != 0' and generate new nodes (stored in Dst)
}
}
-Expr *CXXMemberCallExpr::getImplicitObjectArgument() {
- if (MemberExpr *MemExpr = dyn_cast<MemberExpr>(getCallee()->IgnoreParens()))
+Expr *CXXMemberCallExpr::getImplicitObjectArgument() const {
+ if (const MemberExpr *MemExpr =
+ dyn_cast<MemberExpr>(getCallee()->IgnoreParens()))
return MemExpr->getBase();
// FIXME: Will eventually need to cope with member pointers.
return 0;
}
+CXXMethodDecl *CXXMemberCallExpr::getMethodDecl() const {
+ if (const MemberExpr *MemExpr =
+ dyn_cast<MemberExpr>(getCallee()->IgnoreParens()))
+ return cast<CXXMethodDecl>(MemExpr->getMemberDecl());
+
+ // FIXME: Will eventually need to cope with member pointers.
+ return 0;
+}
+
+
CXXRecordDecl *CXXMemberCallExpr::getRecordDecl() {
Expr* ThisArg = getImplicitObjectArgument();
if (!ThisArg)
}
void AggExprVisitor::VisitCXXMemberCallExpr(CXXMemberCallExpr *E) {
- Eng.VisitCXXMemberCallExpr(E, Pred, DstSet);
+ Eng.Visit(E, Pred, DstSet);
}
void ExprEngine::VisitAggExpr(const Expr *E, const MemRegion *Dest,
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
// FIXME: We eventually should handle structs and other compound types
// that are returned by value.
- QualType T = callOrMsg.getResultType(Eng.getContext());
- if (Loc::isLocType(T) || (T->isIntegerType() && T->isScalarType())) {
+ // Use the result type from callOrMsg as it automatically adjusts
+ // for methods/functions that return references.
+ QualType resultTy = callOrMsg.getResultType(Eng.getContext());
+ if (Loc::isLocType(resultTy) ||
+ (resultTy->isIntegerType() && resultTy->isScalarType())) {
unsigned Count = Builder.getCurrentBlockCount();
SValBuilder &svalBuilder = Eng.getSValBuilder();
- SVal X = svalBuilder.getConjuredSymbolVal(NULL, Ex, T, Count);
+ SVal X = svalBuilder.getConjuredSymbolVal(NULL, Ex, resultTy, Count);
state = state->BindExpr(Ex, X, false);
}
unsigned Count = Builder.getCurrentBlockCount();
SValBuilder &svalBuilder = Eng.getSValBuilder();
SymbolRef Sym = svalBuilder.getConjuredSymbol(Ex, Count);
- QualType RetT = GetReturnType(Ex, svalBuilder.getContext());
+
+ // Use the result type from callOrMsg as it automatically adjusts
+ // for methods/functions that return references.
+ QualType resultTy = callOrMsg.getResultType(Eng.getContext());
state = state->set<RefBindings>(Sym, RefVal::makeOwned(RE.getObjKind(),
- RetT));
+ resultTy));
state = state->BindExpr(Ex, svalBuilder.makeLoc(Sym), false);
// FIXME: Add a flag to the checker where allocations are assumed to
if (dyn_cast_or_null<BlockDataRegion>(L.getAsRegion())) {
Summ = Summaries.getPersistentStopSummary();
}
- else {
- const FunctionDecl* FD = L.getAsFunctionDecl();
- Summ = !FD ? Summaries.getDefaultSummary() :
- Summaries.getSummary(FD);
+ else if (const FunctionDecl* FD = L.getAsFunctionDecl()) {
+ Summ = Summaries.getSummary(FD);
}
+ else if (const CXXMemberCallExpr *me = dyn_cast<CXXMemberCallExpr>(CE)) {
+ if (const CXXMethodDecl *MD = me->getMethodDecl())
+ Summ = Summaries.getSummary(MD);
+ else
+ Summ = Summaries.getDefaultSummary();
+ }
+ else
+ Summ = Summaries.getDefaultSummary();
assert(Summ);
evalSummary(Dst, Eng, Builder, CE,
}
}
+void ExprEngine::evalCallee(const CallExpr *callExpr,
+ const ExplodedNodeSet &src,
+ ExplodedNodeSet &dest) {
+
+ const Expr *callee = 0;
+
+ switch (callExpr->getStmtClass()) {
+ case Stmt::CXXMemberCallExprClass: {
+ // Evaluate the implicit object argument that is the recipient of the
+ // call.
+ callee = cast<CXXMemberCallExpr>(callExpr)->getImplicitObjectArgument();
+
+ // FIXME: handle member pointers.
+ if (!callee)
+ return;
+
+ break;
+ }
+ default: {
+ callee = callExpr->getCallee()->IgnoreParens();
+ break;
+ }
+ }
+
+ for (ExplodedNodeSet::iterator i = src.begin(), e = src.end(); i != e; ++i)
+ Visit(callee, *i, dest);
+}
+
const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXRecordDecl *D,
const StackFrameContext *SFC) {
const Type *T = D->getTypeForDecl();
Dst.Add(N);
}
-void ExprEngine::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE,
- ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
- // Get the method type.
- const FunctionProtoType *FnType =
- MCE->getCallee()->getType()->getAs<FunctionProtoType>();
- assert(FnType && "Method type not available");
-
- // Evaluate explicit arguments with a worklist.
- ExplodedNodeSet argsEvaluated;
- evalArguments(MCE->arg_begin(), MCE->arg_end(), FnType, Pred, argsEvaluated);
-
- // Evaluate the implicit object argument.
- ExplodedNodeSet AllargsEvaluated;
- const MemberExpr *ME = dyn_cast<MemberExpr>(MCE->getCallee()->IgnoreParens());
- if (!ME)
- return;
- Expr *ObjArgExpr = ME->getBase();
- for (ExplodedNodeSet::iterator I = argsEvaluated.begin(),
- E = argsEvaluated.end(); I != E; ++I) {
- Visit(ObjArgExpr, *I, AllargsEvaluated);
- }
-
- // Now evaluate the call itself.
- const CXXMethodDecl *MD = cast<CXXMethodDecl>(ME->getMemberDecl());
- assert(MD && "not a CXXMethodDecl?");
- evalMethodCall(MCE, MD, ObjArgExpr, Pred, AllargsEvaluated, Dst);
-}
-
-void ExprEngine::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *C,
- ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
- const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(C->getCalleeDecl());
- if (!MD) {
- // If the operator doesn't represent a method call treat as regular call.
- VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst);
- return;
- }
-
- // Determine the type of function we're calling (if available).
- const FunctionProtoType *Proto = NULL;
- QualType FnType = C->getCallee()->IgnoreParens()->getType();
- if (const PointerType *FnTypePtr = FnType->getAs<PointerType>())
- Proto = FnTypePtr->getPointeeType()->getAs<FunctionProtoType>();
-
- // Evaluate arguments treating the first one (object method is called on)
- // as alvalue.
- ExplodedNodeSet argsEvaluated;
- evalArguments(C->arg_begin(), C->arg_end(), Proto, Pred, argsEvaluated, true);
-
- // Now evaluate the call itself.
- evalMethodCall(C, MD, C->getArg(0), Pred, argsEvaluated, Dst);
-}
-
-void ExprEngine::evalMethodCall(const CallExpr *MCE, const CXXMethodDecl *MD,
- const Expr *ThisExpr, ExplodedNode *Pred,
- ExplodedNodeSet &Src, ExplodedNodeSet &Dst) {
- // Allow checkers to pre-visit the member call.
- ExplodedNodeSet PreVisitChecks;
- getCheckerManager().runCheckersForPreStmt(PreVisitChecks, Src, MCE, *this);
-
- if (!(MD->isThisDeclarationADefinition() && AMgr.shouldInlineCall())) {
- // FIXME: conservative method call evaluation.
- getCheckerManager().runCheckersForPostStmt(Dst, PreVisitChecks, MCE, *this);
- return;
- }
-
- const StackFrameContext *SFC = AMgr.getStackFrame(MD,
- Pred->getLocationContext(),
- MCE,
- Builder->getBlock(),
- Builder->getIndex());
- const CXXThisRegion *ThisR = getCXXThisRegion(MD, SFC);
- CallEnter Loc(MCE, SFC, Pred->getLocationContext());
- for (ExplodedNodeSet::iterator I = PreVisitChecks.begin(),
- E = PreVisitChecks.end(); I != E; ++I) {
- // Set up 'this' region.
- const GRState *state = GetState(*I);
- state = state->bindLoc(loc::MemRegionVal(ThisR), state->getSVal(ThisExpr));
- Dst.Add(Builder->generateNode(Loc, state, *I));
- }
-}
-
void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
if (CNE->isArray()) {
break;
}
- case Stmt::CallExprClass: {
- const CallExpr* C = cast<CallExpr>(S);
- VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst);
+ case Stmt::CallExprClass:
+ case Stmt::CXXOperatorCallExprClass:
+ case Stmt::CXXMemberCallExprClass: {
+ VisitCallExpr(cast<CallExpr>(S), Pred, Dst);
break;
}
break;
}
- case Stmt::CXXMemberCallExprClass: {
- const CXXMemberCallExpr *MCE = cast<CXXMemberCallExpr>(S);
- VisitCXXMemberCallExpr(MCE, Pred, Dst);
- break;
- }
-
- case Stmt::CXXOperatorCallExprClass: {
- const CXXOperatorCallExpr *C = cast<CXXOperatorCallExpr>(S);
- VisitCXXOperatorCallExpr(C, Pred, Dst);
- break;
- }
-
case Stmt::CXXNewExprClass: {
const CXXNewExpr *NE = cast<CXXNewExpr>(S);
VisitCXXNewExpr(NE, Pred, Dst);
bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE,
ExplodedNode *Pred) {
+ return false;
+
+ // Inlining isn't correct right now because we:
+ // (a) don't generate CallExit nodes.
+ // (b) we need a way to postpone doing post-visits of CallExprs until
+ // the CallExit. This means we need CallExits for the non-inline
+ // cases as well.
+
+#if 0
const GRState *state = GetState(Pred);
const Expr *Callee = CE->getCallee();
SVal L = state->getSVal(Callee);
if (!FD)
return false;
+ // Specially handle CXXMethods.
+ const CXXMethodDecl *methodDecl = 0;
+
+ switch (CE->getStmtClass()) {
+ default: break;
+ case Stmt::CXXOperatorCallExprClass: {
+ const CXXOperatorCallExpr *opCall = cast<CXXOperatorCallExpr>(CE);
+ methodDecl =
+ llvm::dyn_cast_or_null<CXXMethodDecl>(opCall->getCalleeDecl());
+ break;
+ }
+ case Stmt::CXXMemberCallExprClass: {
+ const CXXMemberCallExpr *memberCall = cast<CXXMemberCallExpr>(CE);
+ const MemberExpr *memberExpr =
+ cast<MemberExpr>(memberCall->getCallee()->IgnoreParens());
+ methodDecl = cast<CXXMethodDecl>(memberExpr->getMemberDecl());
+ break;
+ }
+ }
+
+
+
+
// Check if the function definition is in the same translation unit.
if (FD->hasBody(FD)) {
const StackFrameContext *stackFrame =
Dst.Add(N);
return true;
}
+
+ // Generate the CallExit node.
return false;
+#endif
}
-void ExprEngine::VisitCall(const CallExpr* CE, ExplodedNode* Pred,
- CallExpr::const_arg_iterator AI,
- CallExpr::const_arg_iterator AE,
- ExplodedNodeSet& Dst) {
+void ExprEngine::VisitCallExpr(const CallExpr* CE, ExplodedNode* Pred,
+ ExplodedNodeSet& dst) {
// Determine the type of function we're calling (if available).
const FunctionProtoType *Proto = NULL;
if (const PointerType *FnTypePtr = FnType->getAs<PointerType>())
Proto = FnTypePtr->getPointeeType()->getAs<FunctionProtoType>();
- // Evaluate the arguments.
- ExplodedNodeSet ArgsEvaluated;
- evalArguments(CE->arg_begin(), CE->arg_end(), Proto, Pred, ArgsEvaluated);
-
- // Now process the call itself.
- ExplodedNodeSet DstTmp;
- const Expr* Callee = CE->getCallee()->IgnoreParens();
-
- for (ExplodedNodeSet::iterator NI=ArgsEvaluated.begin(),
- NE=ArgsEvaluated.end(); NI != NE; ++NI) {
- // Evaluate the callee.
- ExplodedNodeSet DstTmp2;
- Visit(Callee, *NI, DstTmp2);
- // Perform the previsit of the CallExpr, storing the results in DstTmp.
- getCheckerManager().runCheckersForPreStmt(DstTmp, DstTmp2, CE, *this);
+ // Should the first argument be evaluated as an lvalue?
+ bool firstArgumentAsLvalue = false;
+ switch (CE->getStmtClass()) {
+ case Stmt::CXXOperatorCallExprClass:
+ firstArgumentAsLvalue = true;
+ break;
+ default:
+ break;
}
-
+
+ // Evaluate the arguments.
+ ExplodedNodeSet dstArgsEvaluated;
+ evalArguments(CE->arg_begin(), CE->arg_end(), Proto, Pred, dstArgsEvaluated,
+ firstArgumentAsLvalue);
+
+ // Evaluate the callee.
+ ExplodedNodeSet dstCalleeEvaluated;
+ evalCallee(CE, dstArgsEvaluated, dstCalleeEvaluated);
+
+ // Perform the previsit of the CallExpr.
+ ExplodedNodeSet dstPreVisit;
+ getCheckerManager().runCheckersForPreStmt(dstPreVisit, dstCalleeEvaluated,
+ CE, *this);
+
+ // Now evaluate the call itself.
class DefaultEval : public GraphExpander {
ExprEngine &Eng;
const CallExpr *CE;
public:
- bool Inlined;
DefaultEval(ExprEngine &eng, const CallExpr *ce)
- : Eng(eng), CE(ce), Inlined(false) { }
+ : Eng(eng), CE(ce) {}
virtual void expandGraph(ExplodedNodeSet &Dst, ExplodedNode *Pred) {
+ // Should we inline the call?
if (Eng.getAnalysisManager().shouldInlineCall() &&
Eng.InlineCall(Dst, CE, Pred)) {
- Inlined = true;
- } else {
- StmtNodeBuilder &Builder = Eng.getBuilder();
- assert(&Builder && "StmtNodeBuilder must be defined.");
-
- // Dispatch to the plug-in transfer function.
- unsigned oldSize = Dst.size();
- SaveOr OldHasGen(Builder.hasGeneratedNode);
-
- // Dispatch to transfer function logic to handle the call itself.
- const Expr* Callee = CE->getCallee()->IgnoreParens();
- const GRState* state = Eng.GetState(Pred);
- SVal L = state->getSVal(Callee);
- Eng.getTF().evalCall(Dst, Eng, Builder, CE, L, Pred);
-
- // Handle the case where no nodes where generated. Auto-generate that
- // contains the updated state if we aren't generating sinks.
- if (!Builder.BuildSinks && Dst.size() == oldSize &&
- !Builder.hasGeneratedNode)
- Eng.MakeNode(Dst, CE, Pred, state);
+ return;
}
+
+ StmtNodeBuilder &Builder = Eng.getBuilder();
+ assert(&Builder && "StmtNodeBuilder must be defined.");
+
+ // Dispatch to the plug-in transfer function.
+ unsigned oldSize = Dst.size();
+ SaveOr OldHasGen(Builder.hasGeneratedNode);
+
+ // Dispatch to transfer function logic to handle the call itself.
+ const Expr* Callee = CE->getCallee()->IgnoreParens();
+ const GRState* state = Eng.GetState(Pred);
+ SVal L = state->getSVal(Callee);
+ Eng.getTF().evalCall(Dst, Eng, Builder, CE, L, Pred);
+
+ // Handle the case where no nodes where generated. Auto-generate that
+ // contains the updated state if we aren't generating sinks.
+ if (!Builder.BuildSinks && Dst.size() == oldSize &&
+ !Builder.hasGeneratedNode)
+ Eng.MakeNode(Dst, CE, Pred, state);
}
};
// Finally, evaluate the function call. We try each of the checkers
// to see if the can evaluate the function call.
- ExplodedNodeSet DstTmp3;
+ ExplodedNodeSet dstCallEvaluated;
DefaultEval defEval(*this, CE);
-
- getCheckerManager().runCheckersForEvalCall(DstTmp3, DstTmp, CE,
- *this, &defEval);
-
- // Callee is inlined. We shouldn't do post call checking.
- if (defEval.Inlined)
- return;
+ getCheckerManager().runCheckersForEvalCall(dstCallEvaluated,
+ dstPreVisit,
+ CE, *this, &defEval);
// Finally, perform the post-condition check of the CallExpr and store
// the created nodes in 'Dst'.
- getCheckerManager().runCheckersForPostStmt(Dst, DstTmp3, CE, *this);
+ getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE,
+ *this);
}
//===----------------------------------------------------------------------===//
}
QualType CallOrObjCMessage::getResultType(ASTContext &ctx) const {
+ QualType resultTy;
+ bool isLVal = false;
+
if (CallE) {
+ isLVal = CallE->isLValue();
const Expr *Callee = CallE->getCallee();
if (const FunctionDecl *FD = State->getSVal(Callee).getAsFunctionDecl())
- return FD->getResultType();
- return CallE->getType();
+ resultTy = FD->getResultType();
+ else
+ resultTy = CallE->getType();
+ }
+ else {
+ isLVal = isa<ObjCMessageExpr>(Msg.getOriginExpr()) &&
+ Msg.getOriginExpr()->isLValue();
+ resultTy = Msg.getResultType(ctx);
}
- return Msg.getResultType(ctx);
+
+ if (isLVal)
+ resultTy = ctx.getPointerType(resultTy);
+
+ return resultTy;
}
SVal CallOrObjCMessage::getArgSValAsScalarOrLoc(unsigned i) const {
// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-store region -analyzer-inline-call -cfg-add-initializers -verify %s
+// XFAIL: *
class A {
int x;
// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-inline-call -analyzer-store region -verify %s
+// XFAIL: *
int test1_f1() {
int y = 1;
int x = i;
}
+// Test handling methods that accept references as parameters, and that
+// variables are properly invalidated.
+class RDar9203355 {
+ bool foo(unsigned valA, long long &result) const;
+ bool foo(unsigned valA, int &result) const;
+};
+bool RDar9203355::foo(unsigned valA, int &result) const {
+ long long val;
+ if (foo(valA, val) ||
+ (int)val != val) // no-warning
+ return true;
+ result = val; // no-warning
+ return false;
+}
+
+