ychen updated this revision to Diff 351343. ychen added a comment. - Add `DiagIfReachable` and use it in `Sema::DiagnoseUnusedExprResult`. - Update tests.
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D103938/new/ https://reviews.llvm.org/D103938 Files: clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaStmt.cpp clang/test/CXX/drs/dr14xx.cpp clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp clang/test/CodeCompletion/pragma-macro-token-caching.c clang/test/PCH/cxx-explicit-specifier.cpp clang/test/Parser/cxx-ambig-decl-expr.cpp clang/test/Parser/cxx0x-ambig.cpp clang/test/Sema/const-eval.c clang/test/Sema/exprs.c clang/test/Sema/i-c-e.c clang/test/Sema/vla-2.c clang/test/SemaCXX/attr-annotate.cpp clang/test/SemaCXX/builtin-constant-p.cpp clang/test/SemaCXX/constant-expression-cxx2a.cpp clang/test/SemaCXX/constant-expression.cpp clang/test/SemaCXX/expression-traits.cpp clang/test/SemaCXX/warn-comma-operator.cpp clang/test/SemaCXX/warn-unused-value.cpp
Index: clang/test/SemaCXX/warn-unused-value.cpp =================================================================== --- clang/test/SemaCXX/warn-unused-value.cpp +++ clang/test/SemaCXX/warn-unused-value.cpp @@ -138,3 +138,13 @@ (void)arr3; (void)arr4; } + +#if __cplusplus >= 201103L // C++11 or later +namespace test5 { +int v[(5, 6)]; // expected-warning {{expression result unused}} +void foo() { + new double[false ? (1, 2) : 3] + [false ? (1, 2) : 3]; // expected-warning {{expression result unused}} +} +} // namespace test5 +#endif Index: clang/test/SemaCXX/warn-comma-operator.cpp =================================================================== --- clang/test/SemaCXX/warn-comma-operator.cpp +++ clang/test/SemaCXX/warn-comma-operator.cpp @@ -242,8 +242,8 @@ template <typename... xs> class Foo { - typedef bool_seq<(xs::value, true)...> all_true; - typedef bool_seq<(xs::value, false)...> all_false; + typedef bool_seq<((void)xs::value, true)...> all_true; + typedef bool_seq<((void)xs::value, false)...> all_false; typedef bool_seq<xs::value...> seq; }; Index: clang/test/SemaCXX/expression-traits.cpp =================================================================== --- clang/test/SemaCXX/expression-traits.cpp +++ clang/test/SemaCXX/expression-traits.cpp @@ -583,10 +583,10 @@ // Can't use the ASSERT_XXXX macros without adding parens around // the comma expression. - static_assert(__is_lvalue_expr(x,x), "expected an lvalue"); - static_assert(__is_rvalue_expr(x,1), "expected an rvalue"); - static_assert(__is_lvalue_expr(1,x), "expected an lvalue"); - static_assert(__is_rvalue_expr(1,1), "expected an rvalue"); + static_assert(__is_lvalue_expr((void)x,x), "expected an lvalue"); + static_assert(__is_rvalue_expr((void)x,1), "expected an rvalue"); + static_assert(__is_lvalue_expr((void)1,x), "expected an lvalue"); + static_assert(__is_rvalue_expr((void)1,1), "expected an rvalue"); } #if 0 Index: clang/test/SemaCXX/constant-expression.cpp =================================================================== --- clang/test/SemaCXX/constant-expression.cpp +++ clang/test/SemaCXX/constant-expression.cpp @@ -88,8 +88,8 @@ void diags(int n) { switch (n) { - case (1/0, 1): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} - case (int)(1/0, 2.0): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} + case (1/0, 1): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} expected-warning {{expression result unused}} + case (int)(1/0, 2.0): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} expected-warning {{expression result unused}} case __imag(1/0): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} case (int)__imag((double)(1/0)): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} ; Index: clang/test/SemaCXX/constant-expression-cxx2a.cpp =================================================================== --- clang/test/SemaCXX/constant-expression-cxx2a.cpp +++ clang/test/SemaCXX/constant-expression-cxx2a.cpp @@ -745,7 +745,7 @@ // Ensure that we can handle temporary cleanups for array temporaries. struct ArrElem { constexpr ~ArrElem() {} }; using Arr = ArrElem[3]; - static_assert((Arr{}, true)); + static_assert(((void)Arr{}, true)); } namespace dynamic_alloc { Index: clang/test/SemaCXX/builtin-constant-p.cpp =================================================================== --- clang/test/SemaCXX/builtin-constant-p.cpp +++ clang/test/SemaCXX/builtin-constant-p.cpp @@ -157,12 +157,12 @@ constexpr ~A() { *p = 0; } }; struct Q { int n; constexpr int *get() { return &n; } }; - static_assert(!__builtin_constant_p((A{}, 123))); + static_assert(!__builtin_constant_p(((void)A{}, 123))); // FIXME: We should probably accept this. GCC does. // However, GCC appears to do so by running the destructors at the end of the // enclosing full-expression, which seems broken; running them at the end of // the evaluation of the __builtin_constant_p argument would be more // defensible. - static_assert(!__builtin_constant_p((A{Q().get()}, 123))); + static_assert(!__builtin_constant_p(((void)A{Q().get()}, 123))); } #endif Index: clang/test/SemaCXX/attr-annotate.cpp =================================================================== --- clang/test/SemaCXX/attr-annotate.cpp +++ clang/test/SemaCXX/attr-annotate.cpp @@ -42,7 +42,7 @@ template<typename T> struct B { - [[clang::annotate("test", (T{}, 9))]] void t() {} + [[clang::annotate("test", ((void)T{}, 9))]] void t() {} // expected-error@-1 {{illegal initializer type 'void'}} }; B<int> b; @@ -73,7 +73,7 @@ [[clang::annotate("jui", b, cf)]] void t2() {} // expected-error@-1 {{'annotate' attribute requires parameter 1 to be a constant expression}} // expected-note@-2 {{is not allowed in a constant expression}} - [[clang::annotate("jui", (b, 0), cf)]] [[clang::annotate("jui", &b, cf, &foo::t2, str())]] void t3() {} + [[clang::annotate("jui", ((void)b, 0), cf)]] [[clang::annotate("jui", &b, cf, &foo::t2, str())]] void t3() {} }; }; Index: clang/test/Sema/vla-2.c =================================================================== --- clang/test/Sema/vla-2.c +++ clang/test/Sema/vla-2.c @@ -13,5 +13,5 @@ } void PotentiallyEvaluatedArrayBoundWarn(int n) { - (void)*(int(*)[(0 << 32,n)])0; // FIXME: We should warn here. + (void)*(int(*)[(0 << 32,n)])0; // expected-warning {{expression result unused}} } Index: clang/test/Sema/i-c-e.c =================================================================== --- clang/test/Sema/i-c-e.c +++ clang/test/Sema/i-c-e.c @@ -70,10 +70,12 @@ char z[__builtin_constant_p(4) ? 1 : -1]; // Comma tests -int comma1[0?1,2:3]; -int comma2[1||(1,2)]; // expected-warning {{use of logical '||' with constant operand}} \ - // expected-note {{use '|' for a bitwise operation}} -int comma3[(1,2)]; // expected-warning {{variable length array folded to constant array as an extension}} +int comma1[0?1,2:3]; // expected-warning {{expression result unused}} +int comma2[1 || (1, 2)]; // expected-warning {{use of logical '||' with constant operand}} \ + // expected-note {{use '|' for a bitwise operation}} \ + // expected-warning {{expression result unused}} +int comma3[(1, 2)]; // expected-warning {{variable length array folded to constant array as an extension}} \ + // expected-warning {{expression result unused}} // Pointer + __builtin_constant_p char pbcp[__builtin_constant_p(4) ? (intptr_t)&expr : 0]; // expected-error {{variable length array declaration not allowed at file scope}} Index: clang/test/Sema/exprs.c =================================================================== --- clang/test/Sema/exprs.c +++ clang/test/Sema/exprs.c @@ -13,10 +13,9 @@ // Test that we don't report divide-by-zero errors in unreachable code. -// This test should be left as is, as it also tests CFG functionality. void radar9171946() { if (0) { - 0 / (0 ? 1 : 0); // expected-warning {{expression result unused}} + 0 / (0 ? 1 : 0); // no-warning } } Index: clang/test/Sema/const-eval.c =================================================================== --- clang/test/Sema/const-eval.c +++ clang/test/Sema/const-eval.c @@ -74,7 +74,7 @@ EVAL_EXPR(35, constbool) EVAL_EXPR(36, constbool) -EVAL_EXPR(37, (1,2.0) == 2.0 ? 1 : -1) +EVAL_EXPR(37, ((void)1,2.0) == 2.0 ? 1 : -1) EVAL_EXPR(38, __builtin_expect(1,1) == 1 ? 1 : -1) // PR7884 Index: clang/test/Parser/cxx0x-ambig.cpp =================================================================== --- clang/test/Parser/cxx0x-ambig.cpp +++ clang/test/Parser/cxx0x-ambig.cpp @@ -163,7 +163,7 @@ (void)p1; UnsignedTmplArgSink<T(CtorSink(t ...)) ...> *t0; // ok - UnsignedTmplArgSink<((T *)0, 42u) ...> **t0p = &t0; + UnsignedTmplArgSink<((T *)0, 42u) ...> **t0p = &t0; // expected-warning 2{{expression result unused}} } template void foo(int, int, int); // expected-note {{in instantiation of function template specialization 'ellipsis::foo<int, int>' requested here}} Index: clang/test/Parser/cxx-ambig-decl-expr.cpp =================================================================== --- clang/test/Parser/cxx-ambig-decl-expr.cpp +++ clang/test/Parser/cxx-ambig-decl-expr.cpp @@ -36,8 +36,8 @@ int(a[{0}]); // expected-warning {{unused}} // These are array declarations. - int(x[(1,1)]); // expected-error {{redefinition}} - int(x[true ? 1,1 : 1]); // expected-error {{redefinition}} + int(x[((void)1,1)]); // expected-error {{redefinition}} + int(x[true ? 1 : (1,1)]); // expected-error {{redefinition}} // expected-warning {{unused}} int (*_Atomic atomic_ptr_to_int); *atomic_ptr_to_int = 42; Index: clang/test/PCH/cxx-explicit-specifier.cpp =================================================================== --- clang/test/PCH/cxx-explicit-specifier.cpp +++ clang/test/PCH/cxx-explicit-specifier.cpp @@ -12,7 +12,7 @@ template<typename X, typename Y> struct T { template<typename A> - explicit((Y{}, true)) T(A &&a) {} + explicit(((void)Y{}, true)) T(A &&a) {} }; template<typename X, typename Y> struct U : T<X, Y> { @@ -28,7 +28,7 @@ U<S, char> a = foo('0'); } -//CHECK: explicit((char{} , true)) +//CHECK: explicit(((void)char{} , true)) #endif Index: clang/test/CodeCompletion/pragma-macro-token-caching.c =================================================================== --- clang/test/CodeCompletion/pragma-macro-token-caching.c +++ clang/test/CodeCompletion/pragma-macro-token-caching.c @@ -12,7 +12,7 @@ void completeParamPragmaError(int param) { Outer(__extension__({ _Pragma(2) })); // expected-error {{_Pragma takes a parenthesized string literal}} - param; // expected-warning {{expression result unused}} + param; } // RUN: %clang_cc1 -fsyntax-only -verify -code-completion-at=%s:16:1 %s | FileCheck %s Index: clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp =================================================================== --- clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp +++ clang/test/CXX/temp/temp.constr/temp.constr.constr/partial-specializations.cpp @@ -26,7 +26,7 @@ template<typename T> requires (T{}) // expected-error{{atomic constraint must be of type 'bool' (found 'int')}} struct B<T**> {}; - static_assert((B<int**>{}, true)); // expected-note{{while checking constraint satisfaction for class template partial specialization 'B<int *>' required here}} + static_assert(((void)B<int**>{}, true)); // expected-note{{while checking constraint satisfaction for class template partial specialization 'B<int *>' required here}} // expected-note@-1{{while checking constraint satisfaction for class template partial specialization 'B<int>' required here}} // expected-note@-2{{during template argument deduction for class template partial specialization 'B<T *>' [with T = int *]}} // expected-note@-3{{during template argument deduction for class template partial specialization 'B<T **>' [with T = int]}} Index: clang/test/CXX/drs/dr14xx.cpp =================================================================== --- clang/test/CXX/drs/dr14xx.cpp +++ clang/test/CXX/drs/dr14xx.cpp @@ -18,7 +18,7 @@ Check<true ? 0 : A::unknown_spec>::type *var1; // expected-error {{undeclared identifier 'var1'}} Check<true ? 0 : a>::type *var2; // ok, variable declaration expected-note 0+{{here}} Check<true ? 0 : b>::type *var3; // expected-error {{undeclared identifier 'var3'}} - Check<true ? 0 : (c, 0)>::type *var4; // expected-error {{undeclared identifier 'var4'}} + Check<true ? 0 : ((void)c, 0)>::type *var4; // expected-error {{undeclared identifier 'var4'}} // value-dependent because of the implied type-dependent 'this->', not because of 'd' Check<true ? 0 : (d(), 0)>::type *var5; // expected-error {{undeclared identifier 'var5'}} // value-dependent because of the value-dependent '&' operator, not because of 'A::d' Index: clang/lib/Sema/SemaStmt.cpp =================================================================== --- clang/lib/Sema/SemaStmt.cpp +++ clang/lib/Sema/SemaStmt.cpp @@ -379,7 +379,8 @@ return; } - DiagRuntimeBehavior(Loc, nullptr, PDiag(DiagID) << R1 << R2); + DiagIfReachable(Loc, S ? llvm::makeArrayRef(S) : llvm::None, + PDiag(DiagID) << R1 << R2); } void Sema::ActOnStartOfCompoundStmt(bool IsStmtExpr) { Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -18779,6 +18779,37 @@ EvaluatedExprMarker(*this, SkipLocalVariables).Visit(E); } +/// Emit a diagnostic when statements are reachable. +/// FIXME: check for reachability even in expressions for which we don't build a +/// CFG (eg, in the initializer of a global or in a constant expression). +/// For example, +/// namespace { auto *p = new double[3][false ? (1, 2) : 3]; } +bool Sema::DiagIfReachable(SourceLocation Loc, ArrayRef<const Stmt *> Stmts, + const PartialDiagnostic &PD) { + if (!Stmts.empty() && !FunctionScopes.empty()) { + FunctionScopes.back()->PossiblyUnreachableDiags.push_back( + sema::PossiblyUnreachableDiag(PD, Loc, Stmts)); + return true; + } + + // The initializer of a constexpr variable or of the first declaration of a + // static data member is not syntactically a constant evaluated constant, + // but nonetheless is always required to be a constant expression, so we + // can skip diagnosing. + // FIXME: Using the mangling context here is a hack. + if (auto *VD = dyn_cast_or_null<VarDecl>( + ExprEvalContexts.back().ManglingContextDecl)) { + if (VD->isConstexpr() || + (VD->isStaticDataMember() && VD->isFirstDecl() && !VD->isInline())) + return false; + // FIXME: For any other kind of variable, we should build a CFG for its + // initializer and check whether the context in question is reachable. + } + + Diag(Loc, PD); + return true; +} + /// Emit a diagnostic that describes an effect on the run-time behavior /// of the program being compiled. /// @@ -18811,28 +18842,7 @@ case ExpressionEvaluationContext::PotentiallyEvaluated: case ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: - if (!Stmts.empty() && getCurFunctionOrMethodDecl()) { - FunctionScopes.back()->PossiblyUnreachableDiags. - push_back(sema::PossiblyUnreachableDiag(PD, Loc, Stmts)); - return true; - } - - // The initializer of a constexpr variable or of the first declaration of a - // static data member is not syntactically a constant evaluated constant, - // but nonetheless is always required to be a constant expression, so we - // can skip diagnosing. - // FIXME: Using the mangling context here is a hack. - if (auto *VD = dyn_cast_or_null<VarDecl>( - ExprEvalContexts.back().ManglingContextDecl)) { - if (VD->isConstexpr() || - (VD->isStaticDataMember() && VD->isFirstDecl() && !VD->isInline())) - break; - // FIXME: For any other kind of variable, we should build a CFG for its - // initializer and check whether the context in question is reachable. - } - - Diag(Loc, PD); - return true; + return DiagIfReachable(Loc, Stmts, PD); } return false; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -5077,6 +5077,16 @@ /// conversion. ExprResult tryConvertExprToType(Expr *E, QualType Ty); + /// Conditionally issue a diagnostic based on the statements reachability + /// analysis evaluation context. + /// + /// \param Statement If Statement is non-null, delay reporting the + /// diagnostic until the function body is parsed, and then do a basic + /// reachability analysis to determine if the statement is reachable. + /// If it is unreachable, the diagnostic will not be emitted. + bool DiagIfReachable(SourceLocation Loc, ArrayRef<const Stmt *> Stmts, + const PartialDiagnostic &PD); + /// Conditionally issue a diagnostic based on the current /// evaluation context. ///
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits