Diagnose -Wunused-value based on CFG reachability

(This relands 59337263ab and makes sure comma operator
 diagnostics are suppressed in a SFINAE context.)

While at it, add the diagnosis message "left operand of comma operator has no effect" (used by GCC) for comma operator.

This also makes Clang diagnose in the constant evaluation context which aligns with GCC/MSVC behavior. (https://godbolt.org/z/7zxb8Tx96)

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D103938
This commit is contained in:
Yuanfang Chen 2021-06-23 23:46:42 -07:00
parent 74a47e54be
commit 27a972a699
41 changed files with 168 additions and 108 deletions

View File

@ -8560,6 +8560,9 @@ def err_typecheck_choose_expr_requires_constant : Error<
"'__builtin_choose_expr' requires a constant expression">;
def warn_unused_expr : Warning<"expression result unused">,
InGroup<UnusedValue>;
def warn_unused_comma_left_operand : Warning<
"left operand of comma operator has no effect">,
InGroup<UnusedValue>;
def warn_unused_voidptr : Warning<
"expression result unused; should this cast be to 'void'?">,
InGroup<UnusedValue>;

View File

@ -4908,7 +4908,7 @@ public:
/// DiagnoseUnusedExprResult - If the statement passed in is an expression
/// whose result is unused, warn.
void DiagnoseUnusedExprResult(const Stmt *S);
void DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID);
void DiagnoseUnusedNestedTypedefs(const RecordDecl *D);
void DiagnoseUnusedDecl(const NamedDecl *ND);
@ -5114,6 +5114,16 @@ public:
/// conversion.
ExprResult tryConvertExprToType(Expr *E, QualType Ty);
/// Conditionally issue a diagnostic based on the statement's 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.
///

View File

@ -28,6 +28,7 @@
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
@ -13371,7 +13372,7 @@ static QualType CheckCommaOperands(Sema &S, ExprResult &LHS, ExprResult &RHS,
if (LHS.isInvalid())
return QualType();
S.DiagnoseUnusedExprResult(LHS.get());
S.DiagnoseUnusedExprResult(LHS.get(), diag::warn_unused_comma_left_operand);
if (!S.getLangOpts().CPlusPlus) {
RHS = S.DefaultFunctionArrayLvalueConversion(RHS.get());
@ -18898,6 +18899,38 @@ void Sema::MarkDeclarationsReferencedInExpr(Expr *E,
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() && getCurFunctionOrMethodDecl()) {
if (!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.
///
@ -18930,28 +18963,7 @@ bool Sema::DiagRuntimeBehavior(SourceLocation Loc, ArrayRef<const Stmt*> Stmts,
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;

View File

@ -8523,7 +8523,7 @@ ExprResult Sema::ActOnFinishFullExpr(Expr *FE, SourceLocation CC,
if (FullExpr.isInvalid())
return ExprError();
DiagnoseUnusedExprResult(FullExpr.get());
DiagnoseUnusedExprResult(FullExpr.get(), diag::warn_unused_expr);
}
FullExpr = CorrectDelayedTyposInExpr(FullExpr.get(), /*InitDecl=*/nullptr,

View File

@ -216,9 +216,9 @@ static bool DiagnoseNoDiscard(Sema &S, const WarnUnusedResultAttr *A,
return S.Diag(Loc, diag::warn_unused_result_msg) << A << Msg << R1 << R2;
}
void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
if (const LabelStmt *Label = dyn_cast_or_null<LabelStmt>(S))
return DiagnoseUnusedExprResult(Label->getSubStmt());
return DiagnoseUnusedExprResult(Label->getSubStmt(), DiagID);
const Expr *E = dyn_cast_or_null<Expr>(S);
if (!E)
@ -264,7 +264,6 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
// Okay, we have an unused result. Depending on what the base expression is,
// we might want to make a more specific diagnostic. Check for one of these
// cases now.
unsigned DiagID = diag::warn_unused_expr;
if (const FullExpr *Temps = dyn_cast<FullExpr>(E))
E = Temps->getSubExpr();
if (const CXXBindTemporaryExpr *TempExpr = dyn_cast<CXXBindTemporaryExpr>(E))
@ -339,7 +338,7 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
if (LangOpts.OpenMP && isa<CallExpr>(Source) &&
POE->getNumSemanticExprs() == 1 &&
isa<CallExpr>(POE->getSemanticExpr(0)))
return DiagnoseUnusedExprResult(POE->getSemanticExpr(0));
return DiagnoseUnusedExprResult(POE->getSemanticExpr(0), DiagID);
if (isa<ObjCSubscriptRefExpr>(Source))
DiagID = diag::warn_unused_container_subscript_expr;
else
@ -379,7 +378,12 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) {
return;
}
DiagRuntimeBehavior(Loc, nullptr, PDiag(DiagID) << R1 << R2);
// Do not diagnose use of a comma operator in a SFINAE context because the
// type of the left operand could be used for SFINAE, so technically it is
// *used*.
if (DiagID != diag::warn_unused_comma_left_operand || !isSFINAEContext())
DiagIfReachable(Loc, S ? llvm::makeArrayRef(S) : llvm::None,
PDiag(DiagID) << R1 << R2);
}
void Sema::ActOnStartOfCompoundStmt(bool IsStmtExpr) {

View File

@ -339,12 +339,12 @@ void f22() {
(void)(0 && x);
(void)y7;
(void)(0 || (y8, ({ return; }), 1));
// non-nested-warning@-1 {{expression result unused}}
// non-nested-warning@-1 {{left operand of comma operator has no effect}}
(void)x;
break;
case 8:
(void)(1 && (y9, ({ return; }), 1));
// non-nested-warning@-1 {{expression result unused}}
// non-nested-warning@-1 {{left operand of comma operator has no effect}}
(void)x;
break;
case 9:

View File

@ -27,7 +27,7 @@ extern Linkage1 linkage1f();
void linkage2f(Linkage2);
void use_linkage() {
&linkage1v, &linkage1iv, &linkage2v, &linkage2iv, &linkaget1v; // expected-warning 5{{unused}}
&linkage1v, &linkage1iv, &linkage2v, &linkage2iv, &linkaget1v; // expected-warning 4{{left operand of comma operator has no effect}} expected-warning {{unused}}
linkage1f();
linkage2f({});
}

View File

@ -18,7 +18,7 @@ namespace dr1413 { // dr1413: 12
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'

View File

@ -221,7 +221,7 @@ namespace dr2083 { // dr2083: partial
a.*&A::x; // expected-warning {{unused}}
true ? a.x : a.y; // expected-warning {{unused}}
(void)a.x;
a.x, discarded_lval(); // expected-warning {{unused}}
a.x, discarded_lval(); // expected-warning {{left operand of comma operator has no effect}}
#if 1 // FIXME: These errors are all incorrect; the above code is valid.
// expected-error@-6 {{enclosing function}}
// expected-error@-6 {{enclosing function}}

View File

@ -26,12 +26,12 @@ namespace dr712 { // dr712: partial
use(a);
use((a));
use(cond ? a : a);
use((cond, a)); // expected-warning 2{{unused}} FIXME: should only warn once
use((cond, a)); // expected-warning 2{{left operand of comma operator has no effect}} FIXME: should only warn once
(void)a; // FIXME: expected-error {{declared in enclosing}}
(void)(a); // FIXME: expected-error {{declared in enclosing}}
(void)(cond ? a : a); // FIXME: expected-error 2{{declared in enclosing}}
(void)(cond, a); // FIXME: expected-error {{declared in enclosing}} expected-warning {{unused}}
(void)(cond, a); // FIXME: expected-error {{declared in enclosing}} expected-warning {{left operand of comma operator has no effect}}
}
};
}

View File

@ -26,7 +26,7 @@ namespace class_templates
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]}}

View File

@ -12,7 +12,7 @@ void completeParam(int param) {
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

View File

@ -14,7 +14,7 @@ int fn1() {
int fn2() {
union a m;
m.x = 7, 5.6k; // expected-warning {{expression result unused}}
return m.x, m.i; // expected-warning {{expression result unused}}
return m.x, m.i; // expected-warning {{left operand of comma operator has no effect}}
}
_Accum acc = (0.5r, 6.9k); // expected-warning {{expression result unused}}
_Accum acc = (0.5r, 6.9k); // expected-warning {{left operand of comma operator has no effect}}

View File

@ -12,7 +12,7 @@ namespace inheriting_constructor {
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 @@ namespace inheriting_constructor {
U<S, char> a = foo('0');
}
//CHECK: explicit((char{} , true))
//CHECK: explicit(((void)char{} , true))
#endif

View File

@ -24,7 +24,7 @@ void arr() {
// This is array indexing not an array declarator because a comma expression
// is not syntactically a constant-expression.
int(x[1,1]); // expected-warning 2{{unused}}
int(x[1,1]); // expected-warning {{left operand of comma operator has no effect}} expected-warning {{unused}}
// This is array indexing not an array declaration because a braced-init-list
// is not syntactically a constant-expression.
@ -36,8 +36,8 @@ void arr() {
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 {{left operand of comma operator has no effect}}
int (*_Atomic atomic_ptr_to_int);
*atomic_ptr_to_int = 42;

View File

@ -163,7 +163,7 @@ namespace ellipsis {
(void)p1;
UnsignedTmplArgSink<T(CtorSink(t ...)) ...> *t0; // ok
UnsignedTmplArgSink<((T *)0, 42u) ...> **t0p = &t0;
UnsignedTmplArgSink<((T *)0, 42u) ...> **t0p = &t0; // expected-warning 2{{left operand of comma operator has no effect}}
}
template void foo(int, int, int); // expected-note {{in instantiation of function template specialization 'ellipsis::foo<int, int>' requested here}}

View File

@ -14,8 +14,8 @@ int f() {
// init-statement expressions
if (T{f()}; f()) {} // expected-warning {{expression result unused}}
if (T{f()}, g, h; f()) {} // expected-warning 2{{unused}} expected-warning {{expression result unused}}
if (T(f()), g, h + 1; f()) {} // expected-warning 2{{unused}} expected-warning {{expression result unused}}
if (T{f()}, g, h; f()) {} // expected-warning 2{{left operand of comma operator has no effect}} expected-warning {{expression result unused}}
if (T(f()), g, h + 1; f()) {} // expected-warning 2{{left operand of comma operator has no effect}} expected-warning {{expression result unused}}
// condition declarations
if (T(n){g}) {}
@ -26,8 +26,8 @@ int f() {
// condition expressions
if (T(f())) {}
if (T{f()}) {}
if (T(f()), g, h) {} // expected-warning 2{{unused}}
if (T{f()}, g, h) {} // expected-warning 2{{unused}}
if (T(f()), g, h) {} // expected-warning 2{{left operand of comma operator has no effect}}
if (T{f()}, g, h) {} // expected-warning 2{{left operand of comma operator has no effect}}
// none of the above, disambiguated as expression (can't be a declaration)
if (T(n)(g)) {} // expected-error {{undeclared identifier 'n'}}

View File

@ -7,20 +7,20 @@ int main ()
[a ii]; // expected-warning{{not found}}
[a if: 1 :2]; // expected-warning{{not found}}
[a inout: 1 :2 another:(2,3,4)]; // expected-warning{{not found}} \
// expected-warning 2{{expression result unused}}
// expected-warning 2{{left operand of comma operator has no effect}}
[a inout: 1 :2 another:(2,3,4), 6,6,8]; // expected-warning{{not found}} \
// expected-warning 2{{expression result unused}}
// expected-warning 2{{left operand of comma operator has no effect}}
[a inout: 1 :2 another:(2,3,4), (6,4,5),6,8]; // expected-warning{{not found}} \
// expected-warning 4{{expression result unused}}
// expected-warning 4{{left operand of comma operator has no effect}}
[a inout: 1 :2 another:(i+10), (i,j-1,5),6,8]; // expected-warning{{not found}} \
// expected-warning 2{{expression result unused}}
// expected-warning 2{{left operand of comma operator has no effect}}
[a long: 1 :2 another:(i+10), (i,j-1,5),6,8]; // expected-warning{{not found}} \
// expected-warning 2{{expression result unused}}
// expected-warning 2{{left operand of comma operator has no effect}}
[a : "Hello\n" :2 another:(i+10), (i,j-1,5),6,8]; // expected-warning{{not found}} \
// expected-warning 2{{expression result unused}}
// expected-warning 2{{left operand of comma operator has no effect}}
// Comma expression as receiver (rdar://6222856)
[a, b, c foo]; // expected-warning{{not found}} \
// expected-warning 2{{expression result unused}}
// expected-warning 2{{left operand of comma operator has no effect}}
}

View File

@ -28,14 +28,13 @@ void * foo()
}
@catch (Frob* ex) {
@throw 1,2; // expected-error {{@throw requires an Objective-C object type ('int' invalid)}} \
// expected-warning {{expression result unused}}
// expected-warning {{left operand of comma operator has no effect}}
}
@catch (float x) { // expected-error {{@catch parameter is not a pointer to an interface type}}
}
@catch(...) {
@throw (4,3,proc()); // expected-warning {{expression result unused}} \
// expected-warning {{expression result unused}}
@throw (4,3,proc()); // expected-warning 2{{left operand of comma operator has no effect}}
}
}

View File

@ -22,7 +22,7 @@ void f(X *noreturn) {
// A message send which contains a message send is OK.
[ [ X alloc ] init ];
[ [ int(), noreturn getSelf ] getSize ]; // expected-warning {{unused}}
[ [ int(), noreturn getSelf ] getSize ]; // expected-warning {{left operand of comma operator has no effect}}
// A message send which contains a lambda is OK.
[ [noreturn] { return noreturn; } () setSize: 4 ];

View File

@ -74,7 +74,7 @@ const _Bool constbool = 0;
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

View File

@ -16,7 +16,7 @@
// 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
}
}

View File

@ -70,10 +70,12 @@ char y[__builtin_constant_p(expr) ? -1 : 1];
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 {{left operand of comma operator has no effect}}
int comma2[1 || (1, 2)]; // expected-warning {{use of logical '||' with constant operand}} \
// expected-note {{use '|' for a bitwise operation}} \
// expected-warning {{left operand of comma operator has no effect}}
int comma3[(1, 2)]; // expected-warning {{variable length array folded to constant array as an extension}} \
// expected-warning {{left operand of comma operator has no effect}}
// 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}}

View File

@ -76,9 +76,9 @@ void func(int sel) {
(void)local_int8;
local_int8, 0; // expected-warning + {{expression result unused}}
local_int8, 0; // expected-warning {{left operand of comma operator has no effect}}
0, local_int8; // expected-warning + {{expression result unused}}
0, local_int8; // expected-warning {{left operand of comma operator has no effect}} expected-warning {{expression result unused}}
svint8_t init_int8 = local_int8;
svint8_t bad_init_int8 = for; // expected-error {{expected expression}}

View File

@ -50,7 +50,7 @@ int f(int i) {
return 0;
}
return (i, 65537) * 65537; // expected-warning {{overflow in expression; result is 131073 with type 'int'}} \
// expected-warning {{expression result unused}}
// expected-warning {{left operand of comma operator has no effect}}
}
// rdar://18405357

View File

@ -4,14 +4,14 @@
// a different codepath when we have already emitted an error.)
int PotentiallyEvaluatedSizeofWarn(int n) {
return (int)sizeof *(0 << 32,(int(*)[n])0); // expected-warning {{expression result unused}} expected-warning {{shift count >= width of type}}
return (int)sizeof *(0 << 32,(int(*)[n])0); // expected-warning {{left operand of comma operator has no effect}} expected-warning {{shift count >= width of type}}
}
void PotentiallyEvaluatedTypeofWarn(int n) {
__typeof(*(0 << 32,(int(*)[n])0)) x; // expected-warning {{expression result unused}} expected-warning {{shift count >= width of type}}
__typeof(*(0 << 32,(int(*)[n])0)) x; // expected-warning {{left operand of comma operator has no effect}} expected-warning {{shift count >= width of type}}
(void)x;
}
void PotentiallyEvaluatedArrayBoundWarn(int n) {
(void)*(int(*)[(0 << 32,n)])0; // FIXME: We should warn here.
(void)*(int(*)[(0 << 32,n)])0; // expected-warning {{left operand of comma operator has no effect}}
}

View File

@ -145,7 +145,7 @@ void test_argument_with_type_tag(struct flock *f)
void test_tag_expresssion(int b) {
fcntl(0, b ? F_DUPFD : F_SETLK, 10); // no-warning
fcntl(0, b + F_DUPFD, 10); // no-warning
fcntl(0, (b, F_DUPFD), 10); // expected-warning {{expression result unused}}
fcntl(0, (b, F_DUPFD), 10); // expected-warning {{left operand of comma operator has no effect}}
}
// Check that using 64-bit magic values as tags works and tag values do not

View File

@ -9,31 +9,31 @@ void foo();
// PR4806
void pr4806() {
1,foo(); // expected-warning {{expression result unused}}
1,foo(); // expected-warning {{left operand of comma operator has no effect}}
// other
foo();
i; // expected-warning {{expression result unused}}
i,foo(); // expected-warning {{expression result unused}}
i,foo(); // expected-warning {{left operand of comma operator has no effect}}
foo(),i; // expected-warning {{expression result unused}}
i,j,foo(); // expected-warning {{expression result unused}} expected-warning {{expression result unused}}
i,foo(),j; // expected-warning {{expression result unused}} expected-warning {{expression result unused}}
foo(),i,j; // expected-warning {{expression result unused}} expected-warning {{expression result unused}}
i,j,foo(); // expected-warning 2{{left operand of comma operator has no effect}}
i,foo(),j; // expected-warning {{left operand of comma operator has no effect}} expected-warning {{expression result unused}}
foo(),i,j; // expected-warning {{expression result unused}} expected-warning {{left operand of comma operator has no effect}}
i++;
i++,foo();
foo(),i++;
i++,j,foo(); // expected-warning {{expression result unused}}
i++,j,foo(); // expected-warning {{left operand of comma operator has no effect}}
i++,foo(),j; // expected-warning {{expression result unused}}
foo(),i++,j; // expected-warning {{expression result unused}}
i,j++,foo(); // expected-warning {{expression result unused}}
i,foo(),j++; // expected-warning {{expression result unused}}
foo(),i,j++; // expected-warning {{expression result unused}}
i,j++,foo(); // expected-warning {{left operand of comma operator has no effect}}
i,foo(),j++; // expected-warning {{left operand of comma operator has no effect}}
foo(),i,j++; // expected-warning {{left operand of comma operator has no effect}}
i++,j++,foo();
i++,foo(),j++;
@ -86,7 +86,7 @@ struct s0 { int f0; };
void f0(int a);
void f1(struct s0 *a) {
// rdar://8139785
f0((int)(a->f0 + 1, 10)); // expected-warning {{expression result unused}}
f0((int)(a->f0 + 1, 10)); // expected-warning {{left operand of comma operator has no effect}}
}
void blah(int a);

View File

@ -42,7 +42,7 @@ namespace test0 {
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 @@ struct B {
[[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() {}
};
};

View File

@ -157,12 +157,12 @@ namespace constexpr_dtor {
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

View File

@ -745,7 +745,7 @@ namespace dtor {
// 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 {

View File

@ -88,8 +88,8 @@ enum {
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 {{left operand of comma operator has no effect}}
case (int)(1/0, 2.0): // expected-error {{not an integral constant expression}} expected-note {{division by zero}} expected-warning {{left operand of comma operator has no effect}}
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}}
;

View File

@ -583,10 +583,10 @@ void expr_comma(int x)
// 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

View File

@ -179,12 +179,12 @@ void insert(sx5x10_t a, float f) {
a[4, 5] = 5.0;
// expected-error@-1 {{comma expressions are not allowed as indices in matrix subscript expressions}}
// expected-warning@-2 {{expression result unused}}
// expected-warning@-2 {{left operand of comma operator has no effect}}
a[4, 5, 4] = 5.0;
// expected-error@-1 {{comma expressions are not allowed as indices in matrix subscript expressions}}
// expected-warning@-2 {{expression result unused}}
// expected-warning@-3 {{expression result unused}}
// expected-warning@-2 {{left operand of comma operator has no effect}}
// expected-warning@-3 {{left operand of comma operator has no effect}}
}
void extract(sx5x10_t a, float f) {

View File

@ -157,7 +157,7 @@ bool& operator,(X, Y);
void test_comma(X x, Y y) {
bool& b1 = (x, y);
X& xr = (x, x); // expected-warning {{expression result unused}}
X& xr = (x, x); // expected-warning {{left operand of comma operator has no effect}}
}
struct Callable {

View File

@ -85,9 +85,9 @@ void func(int sel) {
(void)local_int8;
local_int8, 0; // expected-warning + {{expression result unused}}
local_int8, 0; // expected-warning {{left operand of comma operator has no effect}}
0, local_int8; // expected-warning + {{expression result unused}}
0, local_int8; // expected-warning {{left operand of comma operator has no effect}} expected-warning {{expression result unused}}
svint8_t init_int8 = local_int8;
svint8_t bad_init_int8 = for; // expected-error {{expected expression}}

View File

@ -381,8 +381,8 @@ void Init() {
typedef int inte2 __attribute__((__ext_vector_type__(2)));
void test_vector_literal(inte4 res) {
inte2 a = (inte2)(1, 2); //expected-warning{{expression result unused}}
inte4 b = (inte4)(a, a); //expected-error{{C-style cast from vector 'inte2' (vector of 2 'int' values) to vector 'inte4' (vector of 4 'int' values) of different size}} //expected-warning{{expression result unused}}
inte2 a = (inte2)(1, 2); //expected-warning{{left operand of comma operator has no effect}}
inte4 b = (inte4)(a, a); //expected-error{{C-style cast from vector 'inte2' (vector of 2 'int' values) to vector 'inte4' (vector of 4 'int' values) of different size}} //expected-warning{{left operand of comma operator has no effect}}
}
typedef __attribute__((__ext_vector_type__(4))) float vector_float4;

View File

@ -242,8 +242,8 @@ struct bool_seq;
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;
};

View File

@ -1,6 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wunused-value %s
// RUN: %clang_cc1 -fsyntax-only -verify -Wunused-value -std=c++98 %s
// RUN: %clang_cc1 -fsyntax-only -verify -Wunused-value -std=c++11 %s
// RUN: %clang_cc1 -fsyntax-only -verify -Wunused-value -std=c++17 %s
// PR4806
namespace test0 {
@ -138,3 +139,32 @@ void volatile_array() {
(void)arr3;
(void)arr4;
}
#if __cplusplus >= 201103L // C++11 or later
namespace test5 {
int v[(5, 6)]; // expected-warning {{left operand of comma operator has no effect}}
void foo() {
new double[false ? (1, 2) : 3]
// FIXME: We shouldn't diagnose the unreachable constant expression
// here.
[false ? (1, 2) : 3]; // expected-warning {{left operand of comma operator has no effect}}
}
} // namespace test5
// comma operator diagnostics should be suppressed in a SFINAE context.
template <typename T, int = (T{},0)> int c(int) { return 0; }
template <typename T, int> int c(double) { return 1; }
int foo() { return c<int>(0); }
#endif
#if __cplusplus >= 201703L // C++17 or later
namespace test6 {
auto b() {
if constexpr (false)
return (1,0);
else
return (1.0,0.0); // expected-warning {{left operand of comma operator has no effect}}
}
} // namespace test6
#endif

View File

@ -49,6 +49,6 @@ namespace rdar14183893 {
class A {
TFP m_p;
void Enable() { 0, A(); } // expected-warning {{unused}}
void Enable() { 0, A(); } // expected-warning {{left operand of comma operator has no effect}}
};
}

View File

@ -18,7 +18,7 @@ template void f(int, char, double);
namespace PR41576 {
template <class... Xs> constexpr int f(Xs ...xs) {
return [&](auto ...ys) { // expected-note {{instantiation}}
return ((xs + ys), ...); // expected-warning {{unused}}
return ((xs + ys), ...); // expected-warning {{left operand of comma operator has no effect}}
}(1, 2);
}
static_assert(f(3, 4) == 6); // expected-note {{instantiation}}