diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Analysis/PseudoConstantAnalysis.cpp | 61 | ||||
-rw-r--r-- | lib/Checker/IdempotentOperationChecker.cpp | 40 |
2 files changed, 82 insertions, 19 deletions
diff --git a/lib/Analysis/PseudoConstantAnalysis.cpp b/lib/Analysis/PseudoConstantAnalysis.cpp index 7a2fa8a8bd..5deabacea4 100644 --- a/lib/Analysis/PseudoConstantAnalysis.cpp +++ b/lib/Analysis/PseudoConstantAnalysis.cpp @@ -28,10 +28,12 @@ typedef llvm::SmallPtrSet<const VarDecl*, VARDECL_SET_SIZE> VarDeclSet; PseudoConstantAnalysis::PseudoConstantAnalysis(const Stmt *DeclBody) : DeclBody(DeclBody), Analyzed(false) { NonConstantsImpl = new VarDeclSet; + UsedVarsImpl = new VarDeclSet; } PseudoConstantAnalysis::~PseudoConstantAnalysis() { delete (VarDeclSet*)NonConstantsImpl; + delete (VarDeclSet*)UsedVarsImpl; } // Returns true if the given ValueDecl is never written to in the given DeclBody @@ -50,9 +52,22 @@ bool PseudoConstantAnalysis::isPseudoConstant(const VarDecl *VD) { return !NonConstants->count(VD); } +// Returns true if the variable was used (self assignments don't count) +bool PseudoConstantAnalysis::wasReferenced(const VarDecl *VD) { + if (!Analyzed) { + RunAnalysis(); + Analyzed = true; + } + + VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl; + + return UsedVars->count(VD); +} + void PseudoConstantAnalysis::RunAnalysis() { std::deque<const Stmt *> WorkList; VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl; + VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl; // Start with the top level statement of the function WorkList.push_back(DeclBody); @@ -65,7 +80,7 @@ void PseudoConstantAnalysis::RunAnalysis() { // Case 1: Assignment operators modifying ValueDecl case Stmt::BinaryOperatorClass: { const BinaryOperator *BO = cast<BinaryOperator>(Head); - const Expr *LHS = BO->getLHS()->IgnoreParenImpCasts(); + const Expr *LHS = BO->getLHS()->IgnoreParenCasts(); const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(LHS); // We only care about DeclRefExprs on the LHS @@ -76,7 +91,16 @@ void PseudoConstantAnalysis::RunAnalysis() { // for any of the assignment operators, implying that this Decl is being // written to. switch (BO->getOpcode()) { - case BinaryOperator::Assign: + case BinaryOperator::Assign: { + const Expr *RHS = BO->getRHS()->IgnoreParenCasts(); + if (const DeclRefExpr *RHSDecl = dyn_cast<DeclRefExpr>(RHS)) { + // Self-assignments don't count as use of a variable + if (DR->getDecl() == RHSDecl->getDecl()) + // Do not visit the children + continue; + } + + } case BinaryOperator::AddAssign: case BinaryOperator::SubAssign: case BinaryOperator::MulAssign: @@ -148,16 +172,37 @@ void PseudoConstantAnalysis::RunAnalysis() { if (!VD->getType().getTypePtr()->isReferenceType()) continue; - // Ignore VarDecls without a body - if (!VD->getBody()) - continue; - // If the reference is to another var, add the var to the non-constant // list - if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(VD->getBody())) - if (const VarDecl *RefVD = dyn_cast<VarDecl>(DR->getDecl())) + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(VD->getInit())) + if (const VarDecl *RefVD = dyn_cast<VarDecl>(DR->getDecl())) { NonConstants->insert(RefVD); + continue; + } + } + break; + } + + // Case 4: Block variable references + case Stmt::BlockDeclRefExprClass: { + // Any block variables are assumed to be non-constant + const BlockDeclRefExpr *BDR = cast<BlockDeclRefExpr>(Head); + if (const VarDecl *VD = dyn_cast<VarDecl>(BDR->getDecl())) { + NonConstants->insert(VD); + UsedVars->insert(VD); + continue; } + break; + } + + // Case 5: Variable references + case Stmt::DeclRefExprClass: { + const DeclRefExpr *DR = cast<DeclRefExpr>(Head); + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + UsedVars->insert(VD); + continue; + } + break; } default: diff --git a/lib/Checker/IdempotentOperationChecker.cpp b/lib/Checker/IdempotentOperationChecker.cpp index 2cfcaa44d4..d8936203ea 100644 --- a/lib/Checker/IdempotentOperationChecker.cpp +++ b/lib/Checker/IdempotentOperationChecker.cpp @@ -74,7 +74,9 @@ class IdempotentOperationChecker void UpdateAssumption(Assumption &A, const Assumption &New); // False positive reduction methods - static bool isParameterSelfAssign(const Expr *LHS, const Expr *RHS); + static bool isUnusedSelfAssign(const Expr *LHS, + const Expr *RHS, + AnalysisContext *AC); static bool isTruncationExtensionAssignment(const Expr *LHS, const Expr *RHS); static bool PathWasCompletelyAnalyzed(const CFG *C, @@ -182,12 +184,19 @@ void IdempotentOperationChecker::PreVisitBinaryOperator( // Fall through intentional case BinaryOperator::Assign: - // x Assign x has a few more false positives we can check for - if (isParameterSelfAssign(RHS, LHS) - || isTruncationExtensionAssignment(RHS, LHS)) { + // x Assign x can be used to silence unused variable warnings intentionally, + // and has a slightly different definition for false positives. + if (isUnusedSelfAssign(RHS, LHS, AC) + || isTruncationExtensionAssignment(RHS, LHS) + || containsNonLocalVarDecl(RHS) + || containsNonLocalVarDecl(LHS)) { A = Impossible; return; } + if (LHSVal != RHSVal) + break; + UpdateAssumption(A, Equal); + return; case BinaryOperator::SubAssign: case BinaryOperator::DivAssign: @@ -397,10 +406,12 @@ inline void IdempotentOperationChecker::UpdateAssumption(Assumption &A, } } -// Check for a statement were a parameter is self assigned (to avoid an unused -// variable warning) -bool IdempotentOperationChecker::isParameterSelfAssign(const Expr *LHS, - const Expr *RHS) { +// Check for a statement where a variable is self assigned to avoid an unused +// variable warning. We still report if the variable is used after the self +// assignment. +bool IdempotentOperationChecker::isUnusedSelfAssign(const Expr *LHS, + const Expr *RHS, + AnalysisContext *AC) { LHS = LHS->IgnoreParenCasts(); RHS = RHS->IgnoreParenCasts(); @@ -408,15 +419,22 @@ bool IdempotentOperationChecker::isParameterSelfAssign(const Expr *LHS, if (!LHS_DR) return false; - const ParmVarDecl *PD = dyn_cast<ParmVarDecl>(LHS_DR->getDecl()); - if (!PD) + const VarDecl *VD = dyn_cast<VarDecl>(LHS_DR->getDecl()); + if (!VD) return false; const DeclRefExpr *RHS_DR = dyn_cast<DeclRefExpr>(RHS); if (!RHS_DR) return false; - return PD == RHS_DR->getDecl(); + if (VD != RHS_DR->getDecl()) + return false; + + // If the var was used outside of a selfassign, then we should still report + if (AC->getPseudoConstantAnalysis()->wasReferenced(VD)) + return false; + + return true; } // Check for self casts truncating/extending a variable |