diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -95,11 +95,22 @@ } bool haveSameType(QualType Ty1, QualType Ty2) { + const auto IsIntegralOrUnscopedCompleteEnumerationType = [](QualType Ty) { + const Type *CanonicalType = Ty.getCanonicalType().getTypePtr(); + if (const auto *ET = dyn_cast(CanonicalType)) + return ET->getDecl()->isComplete() && !ET->getDecl()->isScoped(); + + return Ty->isIntegralOrEnumerationType(); + }; + // FIXME: Remove the second disjunct when we support symbolic // truncation/extension. - return (Context.getCanonicalType(Ty1) == Context.getCanonicalType(Ty2) || - (Ty1->isIntegralOrEnumerationType() && - Ty2->isIntegralOrEnumerationType())); + const bool BothHaveSameCanonicalTypes = + Context.getCanonicalType(Ty1) == Context.getCanonicalType(Ty2); + const bool BothHaveIntegralLikeTypes = + IsIntegralOrUnscopedCompleteEnumerationType(Ty1) && + IsIntegralOrUnscopedCompleteEnumerationType(Ty2); + return BothHaveSameCanonicalTypes || BothHaveIntegralLikeTypes; } SVal evalCast(SVal val, QualType castTy, QualType originalType); diff --git a/clang/test/Analysis/z3-refute-enum-crash.cpp b/clang/test/Analysis/z3-refute-enum-crash.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/z3-refute-enum-crash.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config crosscheck-with-z3=true -verify %s + +void clang_analyzer_warnIfReached(); + +using sugar_t = unsigned char; +enum class ScopedSugared : sugar_t {}; +enum class ScopedPrimitive : unsigned char {}; +enum UnscopedSugared : sugar_t {}; +enum UnscopedPrimitive : unsigned char {}; + +ScopedSugared conjure_scoped_enum_with_sugar_type(); +ScopedPrimitive conjure_scoped_enum_with_primitive_type(); +UnscopedSugared conjure_unscoped_enum_with_sugar_type(); +UnscopedPrimitive conjure_unscoped_enum_with_primitive_type(); + +void test() { + auto var1 = conjure_scoped_enum_with_sugar_type(); + auto var2 = conjure_scoped_enum_with_primitive_type(); + auto var3 = conjure_unscoped_enum_with_sugar_type(); + auto var4 = conjure_unscoped_enum_with_primitive_type(); + + int sym1 = static_cast(var1) & 0x0F; + if (sym1) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} no-crash + + int sym2 = static_cast(var2) & 0x0F; + if (sym2) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} no-crash + + int sym3 = static_cast(var3) & 0x0F; + if (sym3) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} no-crash + + int sym4 = static_cast(var4) & 0x0F; + if (sym4) + clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} no-crash +}