Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -15234,6 +15234,10 @@ assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsBooleanCondition"); + if (isa(this)) { + Result = true; + return true; + } EvalResult Scratch; return EvaluateAsRValue(Scratch, Ctx, InConstantContext) && HandleConversionToBool(Scratch.Val, Result); Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -15226,11 +15226,18 @@ if (BinaryOperator *Bop = dyn_cast(LHSExpr)) { if (Bop->getOpcode() == BO_LAnd) { // If it's "a && b || 0" don't warn since the precedence doesn't matter. - if (EvaluatesAsFalse(S, RHSExpr)) + if (isa( + RHSExpr) && + EvaluatesAsFalse(S, RHSExpr)) return; + Expr *LLHS = Bop->getLHS()->IgnoreParenImpCasts(); // If it's "1 && a || b" don't warn since the precedence doesn't matter. - if (!EvaluatesAsTrue(S, Bop->getLHS())) - return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, Bop); + if (isa(LLHS) && + EvaluatesAsTrue(S, LLHS)) + return; + return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, Bop); } else if (Bop->getOpcode() == BO_LOr) { if (BinaryOperator *RBop = dyn_cast(Bop->getRHS())) { // If it's "a || b && 1 || c" we didn't warn earlier for @@ -15248,11 +15255,18 @@ if (BinaryOperator *Bop = dyn_cast(RHSExpr)) { if (Bop->getOpcode() == BO_LAnd) { // If it's "0 || a && b" don't warn since the precedence doesn't matter. - if (EvaluatesAsFalse(S, LHSExpr)) + if (isa( + LHSExpr) && + EvaluatesAsFalse(S, LHSExpr)) return; // If it's "a || b && 1" don't warn since the precedence doesn't matter. - if (!EvaluatesAsTrue(S, Bop->getRHS())) - return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, Bop); + Expr *RRHS = Bop->getRHS()->IgnoreParenImpCasts(); + if (isa(RRHS) && + EvaluatesAsTrue(S, RRHS)) + return; + return EmitDiagnosticForLogicalAndInLogicalOr(S, OpLoc, Bop); } } } Index: clang/test/Sema/logical-op-parentheses.c =================================================================== --- clang/test/Sema/logical-op-parentheses.c +++ clang/test/Sema/logical-op-parentheses.c @@ -8,6 +8,7 @@ #endif void logical_op_parentheses(unsigned i) { + const unsigned t = 1; (void)(i || i && i); #ifndef SILENCE @@ -17,8 +18,31 @@ // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:14-[[@LINE-5]]:14}:"(" // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:20-[[@LINE-6]]:20}:")" - (void)(i || i && "w00t"); + (void)(t || + t && t); +#ifndef SILENCE + // expected-warning@-2 {{'&&' within '||'}} + // expected-note@-3 {{place parentheses around the '&&' expression to silence this warning}} +#endif + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:14-[[@LINE-5]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:20-[[@LINE-6]]:20}:")" + + (void)(t && + t || t); +#ifndef SILENCE + // expected-warning@-3 {{'&&' within '||'}} + // expected-note@-4 {{place parentheses around the '&&' expression to silence this warning}} +#endif + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:10-[[@LINE-6]]:10}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:15-[[@LINE-6]]:15}:")" + + (void)(i || i && "w00t"); (void)("w00t" && i || i); + (void)("w00t" && t || t); + (void)(t && t || 0); + (void)(1 && t || t); + (void)(0 || t && t); + (void)(t || t && 1); (void)(i || i && "w00t" || i); #ifndef SILENCE Index: clang/test/Sema/logical-op-parentheses.cpp =================================================================== --- /dev/null +++ clang/test/Sema/logical-op-parentheses.cpp @@ -0,0 +1,66 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -DSILENCE +// RUN: %clang_cc1 -fsyntax-only -verify %s -Wlogical-op-parentheses +// RUN: %clang_cc1 -fsyntax-only -verify %s -Wparentheses +// RUN: %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits %s -Wlogical-op-parentheses 2>&1 | FileCheck %s + +#ifdef SILENCE +// expected-no-diagnostics +#endif + +void logical_op_parentheses(unsigned i) { + constexpr bool t = 1; + (void)(i || + i && i); +#ifndef SILENCE + // expected-warning@-2 {{'&&' within '||'}} + // expected-note@-3 {{place parentheses around the '&&' expression to silence this warning}} +#endif + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:14-[[@LINE-5]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:20-[[@LINE-6]]:20}:")" + + static_assert(t || + t && t); +#ifndef SILENCE + // expected-warning@-2 {{'&&' within '||'}} + // expected-note@-3 {{place parentheses around the '&&' expression to silence this warning}} +#endif + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:14-[[@LINE-5]]:14}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:20-[[@LINE-6]]:20}:")" + + static_assert(t && + t || t); +#ifndef SILENCE + // expected-warning@-3 {{'&&' within '||'}} + // expected-note@-4 {{place parentheses around the '&&' expression to silence this warning}} +#endif + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:17-[[@LINE-6]]:17}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:15-[[@LINE-6]]:15}:")" + + (void)(i || i && "w00t"); + (void)("w00t" && i || i); + (void)("w00t" && t || t); + static_assert(t && t || 0); + static_assert(1 && t || t); + static_assert(0 || t && t); + static_assert(t || t && 1); + + (void)(i || i && "w00t" || i); +#ifndef SILENCE + // expected-warning@-2 {{'&&' within '||'}} + // expected-note@-3 {{place parentheses around the '&&' expression to silence this warning}} +#endif + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:15-[[@LINE-5]]:15}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:26-[[@LINE-6]]:26}:")" + + (void)(i || "w00t" && i || i); +#ifndef SILENCE + // expected-warning@-2 {{'&&' within '||'}} + // expected-note@-3 {{place parentheses around the '&&' expression to silence this warning}} +#endif + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:15-[[@LINE-5]]:15}:"(" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:26-[[@LINE-6]]:26}:")" + + (void)(i && i || 0); + (void)(0 || i && i); +} +