Improvements to IdempotentOperationChecker and its use of PseudoConstantAnalysis
- Added wasReferenced function to PseudoConstantAnalysis to determine if a variable was ever referenced in a function (outside of a self-assignment)
- BlockDeclRefExpr referenced variables are now explicitly added to the non-constant list
- Remove unnecessary ignore of implicit casts
- Generalized parameter self-assign detection to detect deliberate self-assigns of variables to avoid unused variable warnings
- Updated test cases with deliberate self-assignments
- Fixed bug with C++ references and pseudoconstants
- Added test case for C++ references and pseudoconstants

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@111965 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Checker/IdempotentOperationChecker.cpp b/lib/Checker/IdempotentOperationChecker.cpp
index 2cfcaa4..d893620 100644
--- a/lib/Checker/IdempotentOperationChecker.cpp
+++ b/lib/Checker/IdempotentOperationChecker.cpp
@@ -74,7 +74,9 @@
     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 @@
 
   // 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 @@
   }
 }
 
-// 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 @@
   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