diff --git a/clang/lib/Analysis/ReachableCode.cpp b/clang/lib/Analysis/ReachableCode.cpp index ffe576cd0524..0a9e82bb0ada 100644 --- a/clang/lib/Analysis/ReachableCode.cpp +++ b/clang/lib/Analysis/ReachableCode.cpp @@ -122,6 +122,8 @@ static bool isExpandedFromConfigurationMacro(const Stmt *S, return false; } +static bool isConfigurationValue(const ValueDecl *D, Preprocessor &PP); + /// Returns true if the statement represents a configuration value. /// /// A configuration value is something usually determined at compile-time @@ -144,34 +146,15 @@ static bool isConfigurationValue(const Stmt *S, dyn_cast_or_null(cast(S)->getCalleeDecl()); return Callee ? Callee->isConstexpr() : false; } - case Stmt::DeclRefExprClass: { - const DeclRefExpr *DR = cast(S); - const ValueDecl *D = DR->getDecl(); - if (const EnumConstantDecl *ED = dyn_cast(D)) - return isConfigurationValue(ED->getInitExpr(), PP); - if (const VarDecl *VD = dyn_cast(D)) { - // As a heuristic, treat globals as configuration values. Note - // that we only will get here if Sema evaluated this - // condition to a constant expression, which means the global - // had to be declared in a way to be a truly constant value. - // We could generalize this to local variables, but it isn't - // clear if those truly represent configuration values that - // gate unreachable code. - if (!VD->hasLocalStorage()) - return true; - - // As a heuristic, locals that have been marked 'const' explicitly - // can be treated as configuration values as well. - return VD->getType().isLocalConstQualified(); - } - return false; - } + case Stmt::DeclRefExprClass: + return isConfigurationValue(cast(S)->getDecl(), PP); case Stmt::IntegerLiteralClass: return IncludeIntegers ? isExpandedFromConfigurationMacro(S, PP) : false; + case Stmt::MemberExprClass: + return isConfigurationValue(cast(S)->getMemberDecl(), PP); case Stmt::ObjCBoolLiteralExprClass: return isExpandedFromConfigurationMacro(S, PP, /* IgnoreYES_NO */ true); - case Stmt::UnaryExprOrTypeTraitExprClass: return true; case Stmt::BinaryOperatorClass: { @@ -193,6 +176,27 @@ static bool isConfigurationValue(const Stmt *S, } } +static bool isConfigurationValue(const ValueDecl *D, Preprocessor &PP) { + if (const EnumConstantDecl *ED = dyn_cast(D)) + return isConfigurationValue(ED->getInitExpr(), PP); + if (const VarDecl *VD = dyn_cast(D)) { + // As a heuristic, treat globals as configuration values. Note + // that we only will get here if Sema evaluated this + // condition to a constant expression, which means the global + // had to be declared in a way to be a truly constant value. + // We could generalize this to local variables, but it isn't + // clear if those truly represent configuration values that + // gate unreachable code. + if (!VD->hasLocalStorage()) + return true; + + // As a heuristic, locals that have been marked 'const' explicitly + // can be treated as configuration values as well. + return VD->getType().isLocalConstQualified(); + } + return false; +} + /// Returns true if we should always explore all successors of a block. static bool shouldTreatSuccessorsAsReachable(const CFGBlock *B, Preprocessor &PP) { diff --git a/clang/test/SemaCXX/warn-unreachable.cpp b/clang/test/SemaCXX/warn-unreachable.cpp index 9db75926d23e..7f74732a630e 100644 --- a/clang/test/SemaCXX/warn-unreachable.cpp +++ b/clang/test/SemaCXX/warn-unreachable.cpp @@ -236,22 +236,41 @@ Frobozz test_return_object_control_flow(int flag) { void somethingToCall(); - static constexpr bool isConstExprConfigValue() { return true; } - - int test_const_expr_config_value() { - if (isConstExprConfigValue()) { - somethingToCall(); - return 0; - } - somethingToCall(); // no-warning - return 1; - } - int test_const_expr_config_value_2() { - if (!isConstExprConfigValue()) { - somethingToCall(); // no-warning - return 0; - } - somethingToCall(); - return 1; - } +static constexpr bool isConstExprConfigValue() { return true; } + +int test_const_expr_config_value() { + if (isConstExprConfigValue()) { + somethingToCall(); + return 0; + } + somethingToCall(); // no-warning + return 1; +} +int test_const_expr_config_value_2() { + if (!isConstExprConfigValue()) { + somethingToCall(); // no-warning + return 0; + } + somethingToCall(); + return 1; +} + +class Frodo { +public: + static const bool aHobbit = true; +}; + +void test_static_class_var() { + if (Frodo::aHobbit) + somethingToCall(); + else + somethingToCall(); // no-warning +} + +void test_static_class_var(Frodo &F) { + if (F.aHobbit) + somethingToCall(); + else + somethingToCall(); // no-warning +}