gamesh411 created this revision.
Herald added subscribers: steakhal, martong, Szelethus, dkrupp.
Herald added a reviewer: Szelethus.
Herald added a reviewer: NoQ.
Herald added a project: All.
gamesh411 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Modify EnumCastOutOfRange implementation


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D153954

Files:
  clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
  clang/test/Analysis/enum-cast-out-of-range.c
  clang/test/Analysis/enum-cast-out-of-range.cpp

Index: clang/test/Analysis/enum-cast-out-of-range.cpp
===================================================================
--- clang/test/Analysis/enum-cast-out-of-range.cpp
+++ clang/test/Analysis/enum-cast-out-of-range.cpp
@@ -2,199 +2,45 @@
 // RUN:   -analyzer-checker=core,alpha.cplusplus.EnumCastOutOfRange \
 // RUN:   -std=c++11 -verify %s
 
-enum unscoped_unspecified_t {
-  unscoped_unspecified_0 = -4,
-  unscoped_unspecified_1,
-  unscoped_unspecified_2 = 1,
-  unscoped_unspecified_3,
-  unscoped_unspecified_4 = 4
-};
+#define UINT_MAX (~0U)
+#define INT_MAX (int)(UINT_MAX & (UINT_MAX >> 1))
 
-enum unscoped_specified_t : int {
-  unscoped_specified_0 = -4,
-  unscoped_specified_1,
-  unscoped_specified_2 = 1,
-  unscoped_specified_3,
-  unscoped_specified_4 = 4
-};
+enum unscoped_specified_t : unsigned char;
 
-enum class scoped_unspecified_t {
-  scoped_unspecified_0 = -4,
-  scoped_unspecified_1,
-  scoped_unspecified_2 = 1,
-  scoped_unspecified_3,
-  scoped_unspecified_4 = 4
-};
-
-enum class scoped_specified_t : int {
-  scoped_specified_0 = -4,
-  scoped_specified_1,
-  scoped_specified_2 = 1,
-  scoped_specified_3,
-  scoped_specified_4 = 4
-};
-
-struct S {
-  unscoped_unspecified_t E : 5;
-};
-
-void unscopedUnspecified() {
-  unscoped_unspecified_t InvalidBeforeRangeBegin = static_cast<unscoped_unspecified_t>(-5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_unspecified_t ValidNegativeValue1 = static_cast<unscoped_unspecified_t>(-4); // OK.
-  unscoped_unspecified_t ValidNegativeValue2 = static_cast<unscoped_unspecified_t>(-3); // OK.
-  unscoped_unspecified_t InvalidInsideRange1 = static_cast<unscoped_unspecified_t>(-2); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_unspecified_t InvalidInsideRange2 = static_cast<unscoped_unspecified_t>(-1); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_unspecified_t InvalidInsideRange3 = static_cast<unscoped_unspecified_t>(0); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_unspecified_t ValidPositiveValue1 = static_cast<unscoped_unspecified_t>(1); // OK.
-  unscoped_unspecified_t ValidPositiveValue2 = static_cast<unscoped_unspecified_t>(2); // OK.
-  unscoped_unspecified_t InvalidInsideRange4 = static_cast<unscoped_unspecified_t>(3); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_unspecified_t ValidPositiveValue3 = static_cast<unscoped_unspecified_t>(4); // OK.
-  unscoped_unspecified_t InvalidAfterRangeEnd = static_cast<unscoped_unspecified_t>(5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-}
+enum class scoped_specified_t : unsigned char;
 
 void unscopedSpecified() {
-  unscoped_specified_t InvalidBeforeRangeBegin = static_cast<unscoped_specified_t>(-5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_specified_t ValidNegativeValue1 = static_cast<unscoped_specified_t>(-4); // OK.
-  unscoped_specified_t ValidNegativeValue2 = static_cast<unscoped_specified_t>(-3); // OK.
-  unscoped_specified_t InvalidInsideRange1 = static_cast<unscoped_specified_t>(-2); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_specified_t InvalidInsideRange2 = static_cast<unscoped_specified_t>(-1); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_specified_t InvalidInsideRange3 = static_cast<unscoped_specified_t>(0); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_specified_t ValidPositiveValue1 = static_cast<unscoped_specified_t>(1); // OK.
-  unscoped_specified_t ValidPositiveValue2 = static_cast<unscoped_specified_t>(2); // OK.
-  unscoped_specified_t InvalidInsideRange4 = static_cast<unscoped_specified_t>(3); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_specified_t ValidPositiveValue3 = static_cast<unscoped_specified_t>(4); // OK.
-  unscoped_specified_t InvalidAfterRangeEnd = static_cast<unscoped_specified_t>(5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-}
+  auto InvalidBeforeRangeBegin = static_cast<unscoped_specified_t>(-1); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
+  auto Zero = static_cast<unscoped_specified_t>(0); // OK.
+  auto VeryBigValueForEnum = static_cast<unscoped_specified_t>(INT_MAX); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
 
-void scopedUnspecified() {
-  scoped_unspecified_t InvalidBeforeRangeBegin = static_cast<scoped_unspecified_t>(-5); // expected-warning{{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_unspecified_t ValidNegativeValue1 = static_cast<scoped_unspecified_t>(-4); // OK.
-  scoped_unspecified_t ValidNegativeValue2 = static_cast<scoped_unspecified_t>(-3); // OK.
-  scoped_unspecified_t InvalidInsideRange1 = static_cast<scoped_unspecified_t>(-2); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_unspecified_t InvalidInsideRange2 = static_cast<scoped_unspecified_t>(-1); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_unspecified_t InvalidInsideRange3 = static_cast<scoped_unspecified_t>(0); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_unspecified_t ValidPositiveValue1 = static_cast<scoped_unspecified_t>(1); // OK.
-  scoped_unspecified_t ValidPositiveValue2 = static_cast<scoped_unspecified_t>(2); // OK.
-  scoped_unspecified_t InvalidInsideRange4 = static_cast<scoped_unspecified_t>(3); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_unspecified_t ValidPositiveValue3 = static_cast<scoped_unspecified_t>(4); // OK.
-  scoped_unspecified_t InvalidAfterRangeEnd = static_cast<scoped_unspecified_t>(5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
+  // Suppress unused warnings
+  [](...){}(InvalidBeforeRangeBegin,Zero,VeryBigValueForEnum);
 }
 
 void scopedSpecified() {
-  scoped_specified_t InvalidBeforeRangeBegin = static_cast<scoped_specified_t>(-5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_specified_t ValidNegativeValue1 = static_cast<scoped_specified_t>(-4); // OK.
-  scoped_specified_t ValidNegativeValue2 = static_cast<scoped_specified_t>(-3); // OK.
-  scoped_specified_t InvalidInsideRange1 = static_cast<scoped_specified_t>(-2); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_specified_t InvalidInsideRange2 = static_cast<scoped_specified_t>(-1); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_specified_t InvalidInsideRange3 = static_cast<scoped_specified_t>(0); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_specified_t ValidPositiveValue1 = static_cast<scoped_specified_t>(1); // OK.
-  scoped_specified_t ValidPositiveValue2 = static_cast<scoped_specified_t>(2); // OK.
-  scoped_specified_t InvalidInsideRange4 = static_cast<scoped_specified_t>(3); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_specified_t ValidPositiveValue3 = static_cast<scoped_specified_t>(4); // OK.
-  scoped_specified_t InvalidAfterRangeEnd = static_cast<scoped_specified_t>(5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
+  auto InvalidBeforeRangeBegin = static_cast<scoped_specified_t>(-1); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
+  auto Zero = static_cast<scoped_specified_t>(0); // OK.
+  auto VeryBigValueForEnum = static_cast<scoped_specified_t>(INT_MAX); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
+                                                                        //
+  // Suppress unused warnings
+  [](...){}(InvalidBeforeRangeBegin,Zero,VeryBigValueForEnum);
 }
 
-void unscopedUnspecifiedCStyle() {
-  unscoped_unspecified_t InvalidBeforeRangeBegin = (unscoped_unspecified_t)(-5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_unspecified_t ValidNegativeValue1 = (unscoped_unspecified_t)(-4); // OK.
-  unscoped_unspecified_t ValidNegativeValue2 = (unscoped_unspecified_t)(-3); // OK.
-  unscoped_unspecified_t InvalidInsideRange1 = (unscoped_unspecified_t)(-2); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_unspecified_t InvalidInsideRange2 = (unscoped_unspecified_t)(-1); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_unspecified_t InvalidInsideRange3 = (unscoped_unspecified_t)(0); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_unspecified_t ValidPositiveValue1 = (unscoped_unspecified_t)(1); // OK.
-  unscoped_unspecified_t ValidPositiveValue2 = (unscoped_unspecified_t)(2); // OK.
-  unscoped_unspecified_t InvalidInsideRange4 = (unscoped_unspecified_t)(3); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_unspecified_t ValidPositiveValue3 = (unscoped_unspecified_t)(4); // OK.
-  unscoped_unspecified_t InvalidAfterRangeEnd = (unscoped_unspecified_t)(5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-}
+void undefinedInput(int input) {
+  auto unscoped  = static_cast<unscoped_specified_t>(input); // OK.
+  auto scoped  = static_cast<scoped_specified_t>(input); // OK.
 
-void unscopedSpecifiedCStyle() {
-  unscoped_specified_t InvalidBeforeRangeBegin = (unscoped_specified_t)(-5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_specified_t ValidNegativeValue1 = (unscoped_specified_t)(-4); // OK.
-  unscoped_specified_t ValidNegativeValue2 = (unscoped_specified_t)(-3); // OK.
-  unscoped_specified_t InvalidInsideRange1 = (unscoped_specified_t)(-2); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_specified_t InvalidInsideRange2 = (unscoped_specified_t)(-1); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_specified_t InvalidInsideRange3 = (unscoped_specified_t)(0); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_specified_t ValidPositiveValue1 = (unscoped_specified_t)(1); // OK.
-  unscoped_specified_t ValidPositiveValue2 = (unscoped_specified_t)(2); // OK.
-  unscoped_specified_t InvalidInsideRange4 = (unscoped_specified_t)(3); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  unscoped_specified_t ValidPositiveValue3 = (unscoped_specified_t)(4); // OK.
-  unscoped_specified_t InvalidAfterRangeEnd = (unscoped_specified_t)(5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-}
-
-void scopedUnspecifiedCStyle() {
-  scoped_unspecified_t InvalidBeforeRangeBegin = (scoped_unspecified_t)(-5); // expected-warning{{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_unspecified_t ValidNegativeValue1 = (scoped_unspecified_t)(-4); // OK.
-  scoped_unspecified_t ValidNegativeValue2 = (scoped_unspecified_t)(-3); // OK.
-  scoped_unspecified_t InvalidInsideRange1 = (scoped_unspecified_t)(-2); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_unspecified_t InvalidInsideRange2 = (scoped_unspecified_t)(-1); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_unspecified_t InvalidInsideRange3 = (scoped_unspecified_t)(0); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_unspecified_t ValidPositiveValue1 = (scoped_unspecified_t)(1); // OK.
-  scoped_unspecified_t ValidPositiveValue2 = (scoped_unspecified_t)(2); // OK.
-  scoped_unspecified_t InvalidInsideRange4 = (scoped_unspecified_t)(3); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_unspecified_t ValidPositiveValue3 = (scoped_unspecified_t)(4); // OK.
-  scoped_unspecified_t InvalidAfterRangeEnd = (scoped_unspecified_t)(5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-}
-
-void scopedSpecifiedCStyle() {
-  scoped_specified_t InvalidBeforeRangeBegin = (scoped_specified_t)(-5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_specified_t ValidNegativeValue1 = (scoped_specified_t)(-4); // OK.
-  scoped_specified_t ValidNegativeValue2 = (scoped_specified_t)(-3); // OK.
-  scoped_specified_t InvalidInsideRange1 = (scoped_specified_t)(-2); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_specified_t InvalidInsideRange2 = (scoped_specified_t)(-1); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_specified_t InvalidInsideRange3 = (scoped_specified_t)(0); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_specified_t ValidPositiveValue1 = (scoped_specified_t)(1); // OK.
-  scoped_specified_t ValidPositiveValue2 = (scoped_specified_t)(2); // OK.
-  scoped_specified_t InvalidInsideRange4 = (scoped_specified_t)(3); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-  scoped_specified_t ValidPositiveValue3 = (scoped_specified_t)(4); // OK.
-  scoped_specified_t InvalidAfterRangeEnd = (scoped_specified_t)(5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-}
-
-unscoped_unspecified_t unused;
-void unusedExpr() {
-  // following line is not something that EnumCastOutOfRangeChecker should evaluate.  checker should either ignore this line
-  // or process it without producing any warnings.  However, compilation will (and should) still generate a warning having
-  // nothing to do with this checker.
-  unused; // expected-warning {{expression result unused}}
-}
-
-void rangeConstrained1(int input) {
-  if (input > -5 && input < 5)
-    auto value = static_cast<scoped_specified_t>(input); // OK. Being conservative, this is a possibly good value.
+   // Suppress unused warnings
+  [](...){}(unscoped, scoped);
 }
 
 void rangeConstrained2(int input) {
-  if (input < -5)
-    auto value = static_cast<scoped_specified_t>(input); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-}
-
-void rangeConstrained3(int input) {
-  if (input >= -2 && input <= -1)
-    auto value = static_cast<scoped_specified_t>(input); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-}
-
-void rangeConstrained4(int input) {
-  if (input >= -2 && input <= 1)
-    auto value = static_cast<scoped_specified_t>(input); // OK. Possibly 1.
-}
-
-void rangeConstrained5(int input) {
-  if (input >= 1 && input <= 2)
-    auto value = static_cast<scoped_specified_t>(input); // OK. Strict inner matching.
-}
-
-void rangeConstrained6(int input) {
-  if (input >= 2 && input <= 4)
-    auto value = static_cast<scoped_specified_t>(input); // OK. The value is possibly 2 or 4, dont warn.
-}
-
-void rangeConstrained7(int input) {
-  if (input >= 3 && input <= 3)
-    auto value = static_cast<scoped_specified_t>(input); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
-}
+  if (input < 0) {
+    auto unscoped = static_cast<unscoped_specified_t>(input); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
+    auto scoped = static_cast<scoped_specified_t>(input); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
 
-void enumBitFieldAssignment() {
-  S s;
-  s.E = static_cast<unscoped_unspecified_t>(4); // OK.
-  s.E = static_cast<unscoped_unspecified_t>(5); // expected-warning {{The value provided to the cast expression is not in the valid range of values for the enum}}
+    // Suppress unused warnings
+    [](...){}(unscoped, scoped);
+  }
 }
Index: clang/test/Analysis/enum-cast-out-of-range.c
===================================================================
--- clang/test/Analysis/enum-cast-out-of-range.c
+++ clang/test/Analysis/enum-cast-out-of-range.c
@@ -2,29 +2,58 @@
 // RUN:   -analyzer-checker=core,alpha.cplusplus.EnumCastOutOfRange \
 // RUN:   -verify %s
 
-enum En_t {
-  En_0 = -4,
-  En_1,
-  En_2 = 1,
-  En_3,
-  En_4 = 4
+#define UINT_MAX (~0U)
+#define INT_MAX (int)(UINT_MAX & (UINT_MAX >> 1))
+#define INT_MIN (int)(UINT_MAX & ~(UINT_MAX >> 1))
+
+// Enum with enumeration values only non-negative and max value smaller than
+// INT_MAX
+// this allows a signed or unsigned underlying type as well, but the max value
+// of this type is definitely smaller than INT_MAX.
+enum Nonneg_t {
+  Small_nonneg_0 = 0,
+  Small_nonneg_1, // 1
+  Small_nonneg_2 = 4
+};
+
+// Enum with enumeration values close to 0 (so having small absolute value).
+// this forces a signed representation.
+enum Small_abs_t {
+  Small_abs_0 = -1,
+  Small_abs_1, // 0
+  Small_abs_2 = 4
+};
+
+// Enum with big enumeration values.
+enum Big_abs_t {
+  Big_abs_0 = (long long)INT_MIN - 1,
+  Big_abs_1 = (long long)INT_MAX + 1
 };
 
-void unscopedUnspecifiedCStyle(void) {
-  enum En_t Below = (enum En_t)(-5);    // expected-warning {{not in the valid range}}
-  enum En_t NegVal1 = (enum En_t)(-4);  // OK.
-  enum En_t NegVal2 = (enum En_t)(-3);  // OK.
-  enum En_t InRange1 = (enum En_t)(-2); // expected-warning {{not in the valid range}}
-  enum En_t InRange2 = (enum En_t)(-1); // expected-warning {{not in the valid range}}
-  enum En_t InRange3 = (enum En_t)(0);  // expected-warning {{not in the valid range}}
-  enum En_t PosVal1 = (enum En_t)(1);   // OK.
-  enum En_t PosVal2 = (enum En_t)(2);   // OK.
-  enum En_t InRange4 = (enum En_t)(3);  // expected-warning {{not in the valid range}}
-  enum En_t PosVal3 = (enum En_t)(4);   // OK.
-  enum En_t Above = (enum En_t)(5);     // expected-warning {{not in the valid range}}
+void unscopedUnspecifiedNonnegEnumCStyle(void) {
+  // the underlying type may be unsigned
+  enum Nonneg_t NegVal = (enum Nonneg_t)(-1);    // expected-warning {{not in the valid range}}
+  // enum Nonneg_t ZeroVal = (enum Nonneg_t)(0);    // OK
+  // enum Nonneg_t NonMentionedVal = (enum Nonneg_t)(2);    // OK as the underlying type should be able to represent [0..4]
+  // the underlying type is not wider than int
+  enum Nonneg_t BigVal = (enum Nonneg_t)((long long)INT_MAX + 1);  //expected-warning {{not in the valid range}}
+}
+
+void unscopedUnspecifiedSmallEnumCStyle(void) {
+  enum Small_abs_t NegVal = (enum Small_abs_t)(-1);    // OK
+  enum Small_abs_t ZeroVal = (enum Small_abs_t)(0);    // OK
+  enum Small_abs_t NonMentionedVal = (enum Small_abs_t)(2);    // OK as the underlying type should be able to represent [-1..4]
+  // the underlying type is not wider than int
+  enum Small_abs_t BigVal = (enum Small_abs_t)((long long)INT_MAX + 1);  //expected-warning {{not in the valid range}}
+}
+
+void unscopedUnspecifiedBigEnumCStyle(void) {
+  enum Big_abs_t NegVal = (enum Big_abs_t)((long long)INT_MIN - 1);    // OK
+  enum Big_abs_t ZeroVal = (enum Big_abs_t)(0);    // OK
+  enum Big_abs_t BigVal = (enum Big_abs_t)((long long)INT_MAX + 1);  // OK
 }
 
-enum En_t unused;
+enum Unused_t { Unused_enum_val } unused;
 void unusedExpr(void) {
   // Following line is not something that EnumCastOutOfRangeChecker should
   // evaluate.  Checker should either ignore this line or process it without
Index: clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
@@ -22,32 +22,76 @@
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/APSInt.h"
 #include <optional>
 
 using namespace clang;
 using namespace ento;
 
 namespace {
-// This evaluator checks two SVals for equality. The first SVal is provided via
-// the constructor, the second is the parameter of the overloaded () operator.
-// It uses the in-built ConstraintManager to resolve the equlity to possible or
-// not possible ProgramStates.
-class ConstraintBasedEQEvaluator {
-  const DefinedOrUnknownSVal CompareValue;
-  const ProgramStateRef PS;
-  SValBuilder &SVB;
-
-public:
-  ConstraintBasedEQEvaluator(CheckerContext &C,
-                             const DefinedOrUnknownSVal CompareValue)
-      : CompareValue(CompareValue), PS(C.getState()), SVB(C.getSValBuilder()) {}
+class ValueMatchEvaluator {
+protected:
+  CheckerContext &C;
+  SValBuilder &SVB = C.getSValBuilder();
+  const ProgramStateRef PS = C.getState();
+  ValueMatchEvaluator(CheckerContext &C)
+      : C(C), SVB(C.getSValBuilder()), PS(C.getState()) {}
+};
 
-  bool operator()(const llvm::APSInt &EnumDeclInitValue) {
-    DefinedOrUnknownSVal EnumDeclValue = SVB.makeIntVal(EnumDeclInitValue);
-    DefinedOrUnknownSVal ElemEqualsValueToCast =
-        SVB.evalEQ(PS, EnumDeclValue, CompareValue);
+class RangeBasedValueMatchEvaluator : ValueMatchEvaluator {
+  llvm::APSInt Min, Max;
 
-    return static_cast<bool>(PS->assume(ElemEqualsValueToCast, true));
+public:
+  RangeBasedValueMatchEvaluator(CheckerContext &C, const llvm::APSInt &Min,
+                                const llvm::APSInt &Max)
+      : ValueMatchEvaluator(C), Min(Min), Max(Max) {}
+  bool possiblyContains(const DefinedOrUnknownSVal &CompareValue) {
+    // Check if there is a possibility of CompareValue between the min and max
+    // values of the underlying type's range.
+
+    // Build the ExplodedGraph to incrementally model valid assumptions.
+    ExplodedNode *Pred = C.getPredecessor();
+
+    auto CheckAssumption =
+        [this, &Pred](
+            SVal Assumption) -> std::pair<ProgramStateRef, ProgramStateRef> {
+      if (Assumption.isUndef())
+        return {nullptr, nullptr};
+
+      const auto DefinedOrUnknownAssumption =
+          Assumption.castAs<DefinedOrUnknownSVal>();
+      const auto [TrueState, FalseState] =
+          C.getState()->assume(DefinedOrUnknownAssumption);
+      if (TrueState && !FalseState) {
+        Pred = C.addTransition(TrueState, Pred);
+      } else if (FalseState && !TrueState) {
+        Pred = C.addTransition(FalseState, Pred);
+      }
+      return {TrueState, FalseState};
+    };
+
+    // Check lower bound, i.e.: CompareValue >= Min.
+    const SVal LowerBound = SVB.makeIntVal(Min);
+    const SVal CompareValueAtLeastMin =
+        SVB.evalBinOp(C.getState(), BinaryOperator::Opcode::BO_GE, CompareValue,
+                      LowerBound, SVB.getConditionType());
+    const auto [AtLeastMin, NotAtLeastMin] =
+        CheckAssumption(CompareValueAtLeastMin);
+
+    // Check upper bound, i.e.: CompareValue <= Max.
+    const SVal UpperBound = SVB.makeIntVal(Max);
+    SVal CompareValueAtMostMax =
+        SVB.evalBinOp(C.getState(), BinaryOperator::Opcode::BO_LE, CompareValue,
+                      UpperBound, SVB.getConditionType());
+    const auto [AtMostMax, NotAtMostMax] =
+        CheckAssumption(CompareValueAtMostMax);
+
+    const bool DefinitelyLowerThanMin = NotAtLeastMin && !AtLeastMin;
+    const bool DefinitelyHigherThanMax = NotAtMostMax && !AtMostMax;
+    const bool DefinitelyOutOfRange =
+        DefinitelyLowerThanMin || DefinitelyHigherThanMax;
+    const bool PossiblyInRange = !DefinitelyOutOfRange;
+    return PossiblyInRange;
   }
 };
 
@@ -64,17 +108,6 @@
 public:
   void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
 };
-
-using EnumValueVector = llvm::SmallVector<llvm::APSInt, 6>;
-
-// Collects all of the values an enum can represent (as SVals).
-EnumValueVector getDeclValuesForEnum(const EnumDecl *ED) {
-  EnumValueVector DeclValues(
-      std::distance(ED->enumerator_begin(), ED->enumerator_end()));
-  llvm::transform(ED->enumerators(), DeclValues.begin(),
-                 [](const EnumConstantDecl *D) { return D->getInitVal(); });
-  return DeclValues;
-}
 } // namespace
 
 void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const {
@@ -128,10 +161,30 @@
   // function to handle this.
   const EnumDecl *ED = T->castAs<EnumType>()->getDecl();
 
-  EnumValueVector DeclValues = getDeclValuesForEnum(ED);
-  // Check if any of the enum values possibly match.
-  bool PossibleValueMatch = llvm::any_of(
-      DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast));
+  const bool IsFixed = ED->isFixed();
+
+  bool PossibleValueMatch = false;
+
+  if (IsFixed) {
+    QualType UnderlyingType = ED->getIntegerType();
+    const unsigned Bitwidth = C.getASTContext().getIntWidth(UnderlyingType);
+    const bool isUnsigned = UnderlyingType->isUnsignedIntegerType();
+    llvm::APSInt UnderlyingTypeMin =
+        llvm::APSInt::getMinValue(Bitwidth, isUnsigned);
+    llvm::APSInt UnderlyingTypeMax =
+        llvm::APSInt::getMaxValue(Bitwidth, isUnsigned);
+
+    PossibleValueMatch =
+        RangeBasedValueMatchEvaluator(C, UnderlyingTypeMin, UnderlyingTypeMax)
+            .possiblyContains(*ValueToCast);
+  } else {
+    llvm::APSInt RepresentationMin, RepresentationMax;
+    ED->getValueRange(RepresentationMax, RepresentationMin);
+
+    PossibleValueMatch =
+        RangeBasedValueMatchEvaluator(C, RepresentationMin, RepresentationMax)
+            .possiblyContains(*ValueToCast);
+  }
 
   // If there is no value that can possibly match any of the enum values, then
   // warn.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to