[cfe-commits] r107706 - in /cfe/trunk: include/clang/AST/Stmt.h include/clang/Checker/PathSensitive/SVals.h include/clang/Driver/CC1Options.td include/clang/Frontend/AnalyzerOptions.h lib/AST/Stmt.cpp lib/Checker/AnalysisConsumer.cpp lib/Checker/CMakeLists.txt lib/Checker/GRExprEngineExperimentalChecks.h lib/Checker/IdempotentOperationChecker.cpp lib/Checker/SVals.cpp lib/Frontend/CompilerInvocation.cpp test/Analysis/idempotent-operations.c

Tom Care tcare at apple.com
Tue Jul 6 14:43:29 PDT 2010


Author: tcare
Date: Tue Jul  6 16:43:29 2010
New Revision: 107706

URL: http://llvm.org/viewvc/llvm-project?rev=107706&view=rev
Log:
Added a path-sensitive idempotent operation checker (-analyzer-idempotent-operation). Finds idempotent and/or tautological operations in a path sensitive context, flagging operations that have no effect or a predictable effect.

Example:
{
int a = 1;
int b = 5;
int c = b / a; // a is 1 on all paths
}

- New IdempotentOperationChecker class
- Moved recursive Stmt functions in r107675 to IdempotentOperationChecker
- Minor refactoring of SVal to allow checking for any integer
- Added command line option for check
- Added basic test cases

Added:
    cfe/trunk/lib/Checker/IdempotentOperationChecker.cpp
    cfe/trunk/test/Analysis/idempotent-operations.c
Modified:
    cfe/trunk/include/clang/AST/Stmt.h
    cfe/trunk/include/clang/Checker/PathSensitive/SVals.h
    cfe/trunk/include/clang/Driver/CC1Options.td
    cfe/trunk/include/clang/Frontend/AnalyzerOptions.h
    cfe/trunk/lib/AST/Stmt.cpp
    cfe/trunk/lib/Checker/AnalysisConsumer.cpp
    cfe/trunk/lib/Checker/CMakeLists.txt
    cfe/trunk/lib/Checker/GRExprEngineExperimentalChecks.h
    cfe/trunk/lib/Checker/SVals.cpp
    cfe/trunk/lib/Frontend/CompilerInvocation.cpp

Modified: cfe/trunk/include/clang/AST/Stmt.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Stmt.h?rev=107706&r1=107705&r2=107706&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Stmt.h (original)
+++ cfe/trunk/include/clang/AST/Stmt.h Tue Jul  6 16:43:29 2010
@@ -253,25 +253,6 @@
   ///  within CFGs.
   bool hasImplicitControlFlow() const;
 
-  /// contains* - Useful recursive methods to see if a statement contains an
-  ///   element somewhere. Used in static analysis to reduce false positives.
-  static bool containsMacro(const Stmt *S);
-  static bool containsEnum(const Stmt *S);
-  static bool containsZeroConstant(const Stmt *S);
-  static bool containsOneConstant(const Stmt *S);
-  template <class T> static bool containsStmt(const Stmt *S) {
-    if (isa<T>(S))
-        return true;
-
-    for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); ++I)
-        if (const Stmt *child = *I)
-          if (containsStmt<T>(child))
-            return true;
-
-      return false;
-  }
-
-
   /// Child Iterators: All subclasses must implement child_begin and child_end
   ///  to permit easy iteration over the substatements/subexpessions of an
   ///  AST node.  This permits easy iteration over all nodes in the AST.

Modified: cfe/trunk/include/clang/Checker/PathSensitive/SVals.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Checker/PathSensitive/SVals.h?rev=107706&r1=107705&r2=107706&view=diff
==============================================================================
--- cfe/trunk/include/clang/Checker/PathSensitive/SVals.h (original)
+++ cfe/trunk/include/clang/Checker/PathSensitive/SVals.h Tue Jul  6 16:43:29 2010
@@ -98,6 +98,8 @@
 
   bool isConstant() const;
 
+  bool isConstant(int I) const;
+
   bool isZeroConstant() const;
 
   /// hasConjuredSymbol - If this SVal wraps a conjured symbol, return true;

Modified: cfe/trunk/include/clang/Driver/CC1Options.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/CC1Options.td?rev=107706&r1=107705&r2=107706&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/CC1Options.td (original)
+++ cfe/trunk/include/clang/Driver/CC1Options.td Tue Jul  6 16:43:29 2010
@@ -81,6 +81,8 @@
   HelpText<"Emit verbose output about the analyzer's progress">;
 def analyzer_experimental_checks : Flag<"-analyzer-experimental-checks">,
   HelpText<"Use experimental path-sensitive checks">;
+def analyzer_idempotent_operation : Flag<"-analyzer-idempotent-operation">,
+  HelpText<"Use experimental path-sensitive idempotent operation checker">;
 def analyzer_experimental_internal_checks :
   Flag<"-analyzer-experimental-internal-checks">,
   HelpText<"Use new default path-sensitive checks currently in testing">;

Modified: cfe/trunk/include/clang/Frontend/AnalyzerOptions.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/AnalyzerOptions.h?rev=107706&r1=107705&r2=107706&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/AnalyzerOptions.h (original)
+++ cfe/trunk/include/clang/Frontend/AnalyzerOptions.h Tue Jul  6 16:43:29 2010
@@ -72,6 +72,7 @@
   unsigned VisualizeEGUbi : 1;
   unsigned EnableExperimentalChecks : 1;
   unsigned EnableExperimentalInternalChecks : 1;
+  unsigned EnableIdempotentOperationChecker : 1;
   unsigned InlineCall : 1;
 
 public:

Modified: cfe/trunk/lib/AST/Stmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Stmt.cpp?rev=107706&r1=107705&r2=107706&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Stmt.cpp (original)
+++ cfe/trunk/lib/AST/Stmt.cpp Tue Jul  6 16:43:29 2010
@@ -127,72 +127,6 @@
   }
 }
 
-// Recursively find any substatements containing macros
-bool Stmt::containsMacro(const Stmt *S) {
-  if (S->getLocStart().isMacroID())
-    return true;
-
-  if (S->getLocEnd().isMacroID())
-    return true;
-
-  for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); ++I)
-    if (const Stmt *child = *I)
-      if (containsMacro(child))
-        return true;
-
-  return false;
-}
-
-// Recursively find any substatements containing enum constants
-bool Stmt::containsEnum(const Stmt *S) {
-  const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
-
-  if (DR && isa<EnumConstantDecl>(DR->getDecl()))
-    return true;
-
-  for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); ++I)
-      if (const Stmt *child = *I)
-        if (containsEnum(child))
-          return true;
-
-  return false;
-}
-
-bool Stmt::containsZeroConstant(const Stmt *S) {
-  const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S);
-  if (IL && IL->getValue() == 0)
-    return true;
-
-  const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(S);
-  if (FL && FL->getValue().isZero())
-    return true;
-
-  for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); ++I)
-    if (const Stmt *child = *I)
-      if (containsZeroConstant(child))
-        return true;
-
-  return false;
-}
-
-bool Stmt::containsOneConstant(const Stmt *S) {
-  const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S);
-  if (IL && IL->getValue() == 1)
-    return true;
-
-  const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(S);
-  const llvm::APFloat one(1.0);
-  if (FL && FL->getValue().compare(one) == llvm::APFloat::cmpEqual)
-    return true;
-
-  for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); ++I)
-    if (const Stmt *child = *I)
-      if (containsOneConstant(child))
-        return true;
-
-  return false;
-}
-
 Expr *AsmStmt::getOutputExpr(unsigned i) {
   return cast<Expr>(Exprs[i]);
 }

Modified: cfe/trunk/lib/Checker/AnalysisConsumer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/AnalysisConsumer.cpp?rev=107706&r1=107705&r2=107706&view=diff
==============================================================================
--- cfe/trunk/lib/Checker/AnalysisConsumer.cpp (original)
+++ cfe/trunk/lib/Checker/AnalysisConsumer.cpp Tue Jul  6 16:43:29 2010
@@ -28,6 +28,7 @@
 #include "clang/Checker/PathSensitive/GRExprEngine.h"
 #include "clang/Checker/PathSensitive/GRTransferFuncs.h"
 #include "clang/Checker/PathDiagnosticClients.h"
+#include "GRExprEngineExperimentalChecks.h"
 #include "clang/Basic/FileManager.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Frontend/AnalyzerOptions.h"
@@ -340,6 +341,9 @@
   if (C.Opts.EnableExperimentalChecks)
     RegisterExperimentalChecks(Eng);
 
+  if (C.Opts.EnableIdempotentOperationChecker)
+    RegisterIdempotentOperationChecker(Eng);
+
   // Set the graph auditor.
   llvm::OwningPtr<ExplodedNode::Auditor> Auditor;
   if (mgr.shouldVisualizeUbigraph()) {

Modified: cfe/trunk/lib/Checker/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/CMakeLists.txt?rev=107706&r1=107705&r2=107706&view=diff
==============================================================================
--- cfe/trunk/lib/Checker/CMakeLists.txt (original)
+++ cfe/trunk/lib/Checker/CMakeLists.txt Tue Jul  6 16:43:29 2010
@@ -39,6 +39,7 @@
   GRExprEngineExperimentalChecks.cpp
   GRState.cpp
   HTMLDiagnostics.cpp
+  IdempotentOperationChecker.cpp
   LLVMConventionsChecker.cpp
   MacOSXAPIChecker.cpp
   MallocChecker.cpp

Modified: cfe/trunk/lib/Checker/GRExprEngineExperimentalChecks.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/GRExprEngineExperimentalChecks.h?rev=107706&r1=107705&r2=107706&view=diff
==============================================================================
--- cfe/trunk/lib/Checker/GRExprEngineExperimentalChecks.h (original)
+++ cfe/trunk/lib/Checker/GRExprEngineExperimentalChecks.h Tue Jul  6 16:43:29 2010
@@ -22,6 +22,7 @@
 void RegisterPthreadLockChecker(GRExprEngine &Eng);
 void RegisterMallocChecker(GRExprEngine &Eng);
 void RegisterStreamChecker(GRExprEngine &Eng);
+void RegisterIdempotentOperationChecker(GRExprEngine &Eng);
 
 } // end clang namespace
 #endif

Added: cfe/trunk/lib/Checker/IdempotentOperationChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/IdempotentOperationChecker.cpp?rev=107706&view=auto
==============================================================================
--- cfe/trunk/lib/Checker/IdempotentOperationChecker.cpp (added)
+++ cfe/trunk/lib/Checker/IdempotentOperationChecker.cpp Tue Jul  6 16:43:29 2010
@@ -0,0 +1,453 @@
+//==- IdempotentOperationChecker.cpp - Idempotent Operations ----*- 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 a set of path-sensitive checks for idempotent and/or
+// tautological operations. Each potential operation is checked along all paths
+// to see if every path results in a pointless operation.
+//                 +-------------------------------------------+
+//                 |Table of idempotent/tautological operations|
+//                 +-------------------------------------------+
+//+--------------------------------------------------------------------------+
+//|Operator | x op x | x op 1 | 1 op x | x op 0 | 0 op x | x op ~0 | ~0 op x |
+//+--------------------------------------------------------------------------+
+//  +, +=   |        |        |        |   x    |   x    |         |
+//  -, -=   |        |        |        |   x    |   -x   |         |
+//  *, *=   |        |   x    |   x    |   0    |   0    |         |
+//  /, /=   |   1    |   x    |        |  N/A   |   0    |         |
+//  &, &=   |   x    |        |        |   0    |   0    |   x     |    x
+//  |, |=   |   x    |        |        |   x    |   x    |   ~0    |    ~0
+//  ^, ^=   |   0    |        |        |   x    |   x    |         |
+//  <<, <<= |        |        |        |   x    |   0    |         |
+//  >>, >>= |        |        |        |   x    |   0    |         |
+//  ||      |   1    |   1    |   1    |   x    |   x    |   1     |    1
+//  &&      |   1    |   x    |   x    |   0    |   0    |   x     |    x
+//  =       |   x    |        |        |        |        |         |
+//  ==      |   1    |        |        |        |        |         |
+//  >=      |   1    |        |        |        |        |         |
+//  <=      |   1    |        |        |        |        |         |
+//  >       |   0    |        |        |        |        |         |
+//  <       |   0    |        |        |        |        |         |
+//  !=      |   0    |        |        |        |        |         |
+//===----------------------------------------------------------------------===//
+//
+// Ways to reduce false positives (that need to be implemented):
+// - Don't flag downsizing casts
+// - Improved handling of static/global variables
+// - Per-block marking of incomplete analysis
+// - Handling ~0 values
+// - False positives involving silencing unused variable warnings
+//
+// Other things TODO:
+// - Improved error messages
+// - Handle mixed assumptions (which assumptions can belong together?)
+// - Finer grained false positive control (levels)
+
+#include "GRExprEngineExperimentalChecks.h"
+#include "clang/Checker/BugReporter/BugType.h"
+#include "clang/Checker/PathSensitive/CheckerVisitor.h"
+#include "clang/Checker/PathSensitive/SVals.h"
+#include "clang/AST/Stmt.h"
+#include "llvm/ADT/DenseMap.h"
+
+using namespace clang;
+
+namespace {
+class IdempotentOperationChecker
+  : public CheckerVisitor<IdempotentOperationChecker> {
+  public:
+    static void *getTag();
+    void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B);
+    void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B,
+        bool hasWorkRemaining);
+
+  private:
+    // Our assumption about a particular operation.
+    enum Assumption { Possible, Impossible, Equal, LHSis1, RHSis1, LHSis0,
+        RHSis0 };
+
+    void UpdateAssumption(Assumption &A, const Assumption &New);
+
+    /// contains* - Useful recursive methods to see if a statement contains an
+    ///   element somewhere. Used in static analysis to reduce false positives.
+    static bool containsMacro(const Stmt *S);
+    static bool containsEnum(const Stmt *S);
+    static bool containsBuiltinOffsetOf(const Stmt *S);
+    static bool containsZeroConstant(const Stmt *S);
+    static bool containsOneConstant(const Stmt *S);
+    template <class T> static bool containsStmt(const Stmt *S) {
+      if (isa<T>(S))
+          return true;
+
+      for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
+          ++I)
+        if (const Stmt *child = *I)
+          if (containsStmt<T>(child))
+            return true;
+
+        return false;
+    }
+
+    // Hash table
+    typedef llvm::DenseMap<const BinaryOperator *, Assumption> AssumptionMap;
+    AssumptionMap hash;
+};
+}
+
+void *IdempotentOperationChecker::getTag() {
+  static int x = 0;
+  return &x;
+}
+
+void clang::RegisterIdempotentOperationChecker(GRExprEngine &Eng) {
+  Eng.registerCheck(new IdempotentOperationChecker());
+}
+
+void IdempotentOperationChecker::PreVisitBinaryOperator(
+                                                      CheckerContext &C,
+                                                      const BinaryOperator *B) {
+  // Find or create an entry in the hash for this BinaryOperator instance
+  AssumptionMap::iterator i = hash.find(B);
+  Assumption &A = i == hash.end() ? hash[B] : i->second;
+
+  // If we had to create an entry, initialise the value to Possible
+  if (i == hash.end())
+    A = Possible;
+
+  // If we already have visited this node on a path that does not contain an
+  // idempotent operation, return immediately.
+  if (A == Impossible)
+    return;
+
+  // Skip binary operators containing common false positives
+  if (containsMacro(B) || containsEnum(B) || containsStmt<SizeOfAlignOfExpr>(B)
+      || containsZeroConstant(B) || containsOneConstant(B)
+      || containsBuiltinOffsetOf(B)) {
+    A = Impossible;
+    return;
+  }
+
+  const Expr *LHS = B->getLHS();
+  const Expr *RHS = B->getRHS();
+
+  const GRState *state = C.getState();
+
+  SVal LHSVal = state->getSVal(LHS);
+  SVal RHSVal = state->getSVal(RHS);
+
+  // If either value is unknown, we can't be 100% sure of all paths.
+  if (LHSVal.isUnknownOrUndef() || RHSVal.isUnknownOrUndef()) {
+    A = Impossible;
+    return;
+  }
+  BinaryOperator::Opcode Op = B->getOpcode();
+
+  // Dereference the LHS SVal if this is an assign operation
+  switch (Op) {
+  default:
+    break;
+
+  // Fall through intentional
+  case BinaryOperator::AddAssign:
+  case BinaryOperator::SubAssign:
+  case BinaryOperator::MulAssign:
+  case BinaryOperator::DivAssign:
+  case BinaryOperator::AndAssign:
+  case BinaryOperator::OrAssign:
+  case BinaryOperator::XorAssign:
+  case BinaryOperator::ShlAssign:
+  case BinaryOperator::ShrAssign:
+  case BinaryOperator::Assign:
+  // Assign statements have one extra level of indirection
+    if (!isa<Loc>(LHSVal)) {
+      A = Impossible;
+      return;
+    }
+    LHSVal = state->getSVal(cast<Loc>(LHSVal));
+  }
+
+
+  // We now check for various cases which result in an idempotent operation.
+
+  // x op x
+  switch (Op) {
+  default:
+    break; // We don't care about any other operators.
+
+  // Fall through intentional
+  case BinaryOperator::SubAssign:
+  case BinaryOperator::DivAssign:
+  case BinaryOperator::AndAssign:
+  case BinaryOperator::OrAssign:
+  case BinaryOperator::XorAssign:
+  case BinaryOperator::Assign:
+  case BinaryOperator::Sub:
+  case BinaryOperator::Div:
+  case BinaryOperator::And:
+  case BinaryOperator::Or:
+  case BinaryOperator::Xor:
+  case BinaryOperator::LOr:
+  case BinaryOperator::LAnd:
+    if (LHSVal != RHSVal)
+      break;
+    UpdateAssumption(A, Equal);
+    return;
+  }
+
+  // x op 1
+  switch (Op) {
+   default:
+     break; // We don't care about any other operators.
+
+   // Fall through intentional
+   case BinaryOperator::MulAssign:
+   case BinaryOperator::DivAssign:
+   case BinaryOperator::Mul:
+   case BinaryOperator::Div:
+   case BinaryOperator::LOr:
+   case BinaryOperator::LAnd:
+     if (!RHSVal.isConstant(1))
+       break;
+     UpdateAssumption(A, RHSis1);
+     return;
+  }
+
+  // 1 op x
+  switch (Op) {
+  default:
+    break; // We don't care about any other operators.
+
+  // Fall through intentional
+  case BinaryOperator::MulAssign:
+  case BinaryOperator::Mul:
+  case BinaryOperator::LOr:
+  case BinaryOperator::LAnd:
+    if (!LHSVal.isConstant(1))
+      break;
+    UpdateAssumption(A, LHSis1);
+    return;
+  }
+
+  // x op 0
+  switch (Op) {
+  default:
+    break; // We don't care about any other operators.
+
+  // Fall through intentional
+  case BinaryOperator::AddAssign:
+  case BinaryOperator::SubAssign:
+  case BinaryOperator::MulAssign:
+  case BinaryOperator::AndAssign:
+  case BinaryOperator::OrAssign:
+  case BinaryOperator::XorAssign:
+  case BinaryOperator::Add:
+  case BinaryOperator::Sub:
+  case BinaryOperator::Mul:
+  case BinaryOperator::And:
+  case BinaryOperator::Or:
+  case BinaryOperator::Xor:
+  case BinaryOperator::Shl:
+  case BinaryOperator::Shr:
+  case BinaryOperator::LOr:
+  case BinaryOperator::LAnd:
+    if (!RHSVal.isConstant(0))
+      break;
+    UpdateAssumption(A, RHSis0);
+    return;
+  }
+
+  // 0 op x
+  switch (Op) {
+  default:
+    break; // We don't care about any other operators.
+
+  // Fall through intentional
+  //case BinaryOperator::AddAssign: // Common false positive
+  case BinaryOperator::SubAssign: // Check only if unsigned
+  case BinaryOperator::MulAssign:
+  case BinaryOperator::DivAssign:
+  case BinaryOperator::AndAssign:
+  //case BinaryOperator::OrAssign: // Common false positive
+  //case BinaryOperator::XorAssign: // Common false positive
+  case BinaryOperator::ShlAssign:
+  case BinaryOperator::ShrAssign:
+  case BinaryOperator::Add:
+  case BinaryOperator::Sub:
+  case BinaryOperator::Mul:
+  case BinaryOperator::Div:
+  case BinaryOperator::And:
+  case BinaryOperator::Or:
+  case BinaryOperator::Xor:
+  case BinaryOperator::Shl:
+  case BinaryOperator::Shr:
+  case BinaryOperator::LOr:
+  case BinaryOperator::LAnd:
+    if (!LHSVal.isConstant(0))
+      break;
+    UpdateAssumption(A, LHSis0);
+    return;
+  }
+
+  // If we get to this point, there has been a valid use of this operation.
+  A = Impossible;
+}
+
+void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G,
+                                                  BugReporter &B,
+                                                  bool hasWorkRemaining) {
+  // If there is any work remaining we cannot be 100% sure about our warnings
+  if (hasWorkRemaining)
+    return;
+
+  // Iterate over the hash to see if we have any paths with definite
+  // idempotent operations.
+  for (AssumptionMap::const_iterator i =
+      hash.begin(); i != hash.end(); ++i) {
+    if (i->second != Impossible) {
+      // Select the error message.
+      const char *msg;
+      switch (i->second) {
+      case Equal:
+        msg = "idempotent operation; both operands are always equal in value";
+        break;
+      case LHSis1:
+        msg = "idempotent operation; the left operand is always 1";
+        break;
+      case RHSis1:
+        msg = "idempotent operation; the right operand is always 1";
+        break;
+      case LHSis0:
+        msg = "idempotent operation; the left operand is always 0";
+        break;
+      case RHSis0:
+        msg = "idempotent operation; the right operand is always 0";
+        break;
+      case Impossible:
+        break;
+      case Possible:
+        assert(0 && "Operation was never marked with an assumption");
+      }
+
+      // Create the SourceRange Arrays
+      SourceRange S[2] = { i->first->getLHS()->getSourceRange(),
+                           i->first->getRHS()->getSourceRange() };
+      B.EmitBasicReport("Idempotent operation", msg, i->first->getOperatorLoc(),
+          S, 2);
+    }
+  }
+}
+
+// Updates the current assumption given the new assumption
+inline void IdempotentOperationChecker::UpdateAssumption(Assumption &A,
+                                                        const Assumption &New) {
+  switch (A) {
+  // If we don't currently have an assumption, set it
+  case Possible:
+    A = New;
+    return;
+
+  // If we have determined that a valid state happened, ignore the new
+  // assumption.
+  case Impossible:
+    return;
+
+  // Any other case means that we had a different assumption last time. We don't
+  // currently support mixing assumptions for diagnostic reasons, so we set
+  // our assumption to be impossible.
+  default:
+    A = Impossible;
+    return;
+  }
+}
+
+// Recursively find any substatements containing macros
+bool IdempotentOperationChecker::containsMacro(const Stmt *S) {
+  if (S->getLocStart().isMacroID())
+    return true;
+
+  if (S->getLocEnd().isMacroID())
+    return true;
+
+  for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
+      ++I)
+    if (const Stmt *child = *I)
+      if (containsMacro(child))
+        return true;
+
+  return false;
+}
+
+// Recursively find any substatements containing enum constants
+bool IdempotentOperationChecker::containsEnum(const Stmt *S) {
+  const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
+
+  if (DR && isa<EnumConstantDecl>(DR->getDecl()))
+    return true;
+
+  for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
+      ++I)
+    if (const Stmt *child = *I)
+      if (containsEnum(child))
+        return true;
+
+  return false;
+}
+
+// Recursively find any substatements containing __builtin_offset_of
+bool IdempotentOperationChecker::containsBuiltinOffsetOf(const Stmt *S) {
+  const UnaryOperator *UO = dyn_cast<UnaryOperator>(S);
+
+  if (UO && UO->getOpcode() == UnaryOperator::OffsetOf)
+    return true;
+
+  for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
+      ++I)
+    if (const Stmt *child = *I)
+      if (containsBuiltinOffsetOf(child))
+        return true;
+
+  return false;
+}
+
+bool IdempotentOperationChecker::containsZeroConstant(const Stmt *S) {
+  const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S);
+  if (IL && IL->getValue() == 0)
+    return true;
+
+  const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(S);
+  if (FL && FL->getValue().isZero())
+    return true;
+
+  for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
+      ++I)
+    if (const Stmt *child = *I)
+      if (containsZeroConstant(child))
+        return true;
+
+  return false;
+}
+
+bool IdempotentOperationChecker::containsOneConstant(const Stmt *S) {
+  const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S);
+  if (IL && IL->getValue() == 1)
+    return true;
+
+  const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(S);
+  const llvm::APFloat one(1.0);
+  if (FL && FL->getValue().compare(one) == llvm::APFloat::cmpEqual)
+    return true;
+
+  for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
+      ++I)
+    if (const Stmt *child = *I)
+      if (containsOneConstant(child))
+        return true;
+
+  return false;
+}
+

Modified: cfe/trunk/lib/Checker/SVals.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/SVals.cpp?rev=107706&r1=107705&r2=107706&view=diff
==============================================================================
--- cfe/trunk/lib/Checker/SVals.cpp (original)
+++ cfe/trunk/lib/Checker/SVals.cpp Tue Jul  6 16:43:29 2010
@@ -200,15 +200,19 @@
   return isa<nonloc::ConcreteInt>(this) || isa<loc::ConcreteInt>(this);
 }
 
-bool SVal::isZeroConstant() const {
+bool SVal::isConstant(int I) const {
   if (isa<loc::ConcreteInt>(*this))
-    return cast<loc::ConcreteInt>(*this).getValue() == 0;
+    return cast<loc::ConcreteInt>(*this).getValue() == I;
   else if (isa<nonloc::ConcreteInt>(*this))
-    return cast<nonloc::ConcreteInt>(*this).getValue() == 0;
+    return cast<nonloc::ConcreteInt>(*this).getValue() == I;
   else
     return false;
 }
 
+bool SVal::isZeroConstant() const {
+  return isConstant(0);
+}
+
 
 //===----------------------------------------------------------------------===//
 // Transfer function dispatch for Non-Locs.

Modified: cfe/trunk/lib/Frontend/CompilerInvocation.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInvocation.cpp?rev=107706&r1=107705&r2=107706&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInvocation.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp Tue Jul  6 16:43:29 2010
@@ -112,6 +112,8 @@
     Res.push_back("-analyzer-experimental-checks");
   if (Opts.EnableExperimentalInternalChecks)
     Res.push_back("-analyzer-experimental-internal-checks");
+  if (Opts.EnableIdempotentOperationChecker)
+    Res.push_back("-analyzer-idempotent-operation");
 }
 
 static void CodeGenOptsToArgs(const CodeGenOptions &Opts,
@@ -788,6 +790,8 @@
   Opts.EnableExperimentalChecks = Args.hasArg(OPT_analyzer_experimental_checks);
   Opts.EnableExperimentalInternalChecks =
     Args.hasArg(OPT_analyzer_experimental_internal_checks);
+  Opts.EnableIdempotentOperationChecker =
+    Args.hasArg(OPT_analyzer_idempotent_operation);
   Opts.TrimGraph = Args.hasArg(OPT_trim_egraph);
   Opts.MaxNodes = Args.getLastArgIntValue(OPT_analyzer_max_nodes, 150000,Diags);
   Opts.MaxLoop = Args.getLastArgIntValue(OPT_analyzer_max_loop, 3, Diags);

Added: cfe/trunk/test/Analysis/idempotent-operations.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/idempotent-operations.c?rev=107706&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/idempotent-operations.c (added)
+++ cfe/trunk/test/Analysis/idempotent-operations.c Tue Jul  6 16:43:29 2010
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -Wunused-variable -analyze -analyzer-idempotent-operation -analyzer-store=basic -analyzer-constraints=range
+
+// Basic tests
+
+extern void test(int i);
+
+void basic() {
+  int x = 10, zero = 0, one = 1;
+
+  // x op x
+  x = x;        // expected-warning {{idempotent operation; both operands are always equal in value}}
+  test(x - x);  // expected-warning {{idempotent operation; both operands are always equal in value}}
+  x -= x;       // expected-warning {{idempotent operation; both operands are always equal in value}}
+  x = 10;       // no-warning
+  test(x / x);  // expected-warning {{idempotent operation; both operands are always equal in value}}
+  x /= x;       // expected-warning {{idempotent operation; both operands are always equal in value}}
+  x = 10;       // no-warning
+  test(x & x);  // expected-warning {{idempotent operation; both operands are always equal in value}}
+  x &= x;       // expected-warning {{idempotent operation; both operands are always equal in value}}
+  test(x | x);  // expected-warning {{idempotent operation; both operands are always equal in value}}
+  x |= x;       // expected-warning {{idempotent operation; both operands are always equal in value}}
+
+  // x op 1
+  test(x * one);  // expected-warning {{idempotent operation; the right operand is always 1}}
+  x *= one;       // expected-warning {{idempotent operation; the right operand is always 1}}
+  test(x / one);  // expected-warning {{idempotent operation; the right operand is always 1}}
+  x /= one;       // expected-warning {{idempotent operation; the right operand is always 1}}
+
+  // 1 op x
+  test(one * x);   // expected-warning {{idempotent operation; the left operand is always 1}}
+
+  // x op 0
+  test(x + zero);  // expected-warning {{idempotent operation; the right operand is always 0}}
+  test(x - zero);  // expected-warning {{idempotent operation; the right operand is always 0}}
+  test(x * zero);  // expected-warning {{idempotent operation; the right operand is always 0}}
+  test(x & zero);  // expected-warning {{idempotent operation; the right operand is always 0}}
+  test(x | zero);  // expected-warning {{idempotent operation; the right operand is always 0}}
+  test(x ^ zero);  // expected-warning {{idempotent operation; the right operand is always 0}}
+  test(x << zero); // expected-warning {{idempotent operation; the right operand is always 0}}
+  test(x >> zero); // expected-warning {{idempotent operation; the right operand is always 0}}
+
+  // 0 op x
+  test(zero + x);  // expected-warning {{idempotent operation; the left operand is always 0}}
+  test(zero - x);  // expected-warning {{idempotent operation; the left operand is always 0}}
+  test(zero / x);  // expected-warning {{idempotent operation; the left operand is always 0}}
+  test(zero * x);  // expected-warning {{idempotent operation; the left operand is always 0}}
+  test(zero & x);  // expected-warning {{idempotent operation; the left operand is always 0}}
+  test(zero | x);  // expected-warning {{idempotent operation; the left operand is always 0}}
+  test(zero ^ x);  // expected-warning {{idempotent operation; the left operand is always 0}}
+  test(zero << x); // expected-warning {{idempotent operation; the left operand is always 0}}
+  test(zero >> x); // expected-warning {{idempotent operation; the left operand is always 0}}
+}





More information about the cfe-commits mailing list