Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3245,6 +3245,10 @@ def warn_impcast_bitfield_precision_constant : Warning< "implicit truncation from %2 to bit-field changes value from %0 to %1">, InGroup; +def warn_impcast_constant_int_to_objc_bool : Warning< + "implicit conversion from %0 to BOOL; " + "the only well defined values for BOOL are YES and NO">, + InGroup; def warn_impcast_fixed_point_range : Warning< "implicit conversion from %0 cannot fit within the range of values for %1">, Index: clang/lib/Sema/SemaChecking.cpp =================================================================== --- clang/lib/Sema/SemaChecking.cpp +++ clang/lib/Sema/SemaChecking.cpp @@ -11116,6 +11116,11 @@ return true; } +static bool isObjCSignedCharBool(Sema &S, QualType Ty) { + return Ty->isSpecificBuiltinType(BuiltinType::SChar) && + S.getLangOpts().ObjC && S.NSAPIObj->isObjCBOOLType(Ty); +} + static void CheckImplicitConversion(Sema &S, Expr *E, QualType T, SourceLocation CC, bool *ICContext = nullptr) { @@ -11159,6 +11164,29 @@ } } + // If the we're converting a constant to an ObjC BOOL on a platform where BOOL + // is a typedef for signed char (macOS), then that constant value has to be 1 + // or 0. + if (isObjCSignedCharBool(S, T) && Source->isIntegralType(S.Context)) { + Expr::EvalResult Result; + if (E->EvaluateAsInt(Result, S.getASTContext(), + Expr::SE_AllowSideEffects) && + Result.Val.getInt() != 1 && Result.Val.getInt() != 0) { + auto Builder = S.Diag(CC, diag::warn_impcast_constant_int_to_objc_bool) + << Result.Val.getInt().toString(10); + Expr *Ignored = E->IgnoreImplicit(); + bool NeedsParens = isa(Ignored) || + isa(Ignored) || + isa(Ignored); + SourceLocation EndLoc = S.getLocForEndOfToken(E->getEndLoc()); + if (NeedsParens) + Builder << FixItHint::CreateInsertion(E->getBeginLoc(), "(") + << FixItHint::CreateInsertion(EndLoc, ")"); + Builder << FixItHint::CreateInsertion(EndLoc, " ? YES : NO"); + return; + } + } + // Check implicit casts from Objective-C collection literals to specialized // collection types, e.g., NSArray *. if (auto *ArrayLiteral = dyn_cast(E)) Index: clang/test/Sema/objc-bool-constant-conversion-fixit.m =================================================================== --- /dev/null +++ clang/test/Sema/objc-bool-constant-conversion-fixit.m @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -Werror=constant-conversion %s -fixit-recompile -fixit-to-temporary -E -o - | FileCheck %s + +typedef signed char BOOL; + +BOOL b; + +int main() { + BOOL b = 2; + // CHECK: BOOL b = 2 ? YES : NO; + + b = b ? 2 : 1; + // CHECK: b = b ? 2 ? YES : NO : 1; + + b = b ? 1 : 2; + // CHECK: b = b ? 1 : 2 ? YES : NO; + + b = b ? 2 : 2; + // CHECK: b = b ? 2 ? YES : NO : 2 ? YES : NO; + + b = 1 + 1; + // CHECK: b = (1 + 1) ? YES : NO; + + b = 1 | 2; + // CHECK: b = (1 | 2) ? YES : NO; + + b = 1 << 1; + // CHECK: b = (1 << 1) ? YES : NO; +} + +@interface BoolProp +@property BOOL b; +@end + +void f(BoolProp *bp) { + bp.b = 43; + // CHECK: bp.b = 43 ? YES : NO; + + [bp setB:43]; + // CHECK: [bp setB:43 ? YES : NO]; +} Index: clang/test/Sema/objc-bool-constant-conversion.m =================================================================== --- /dev/null +++ clang/test/Sema/objc-bool-constant-conversion.m @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only + +typedef signed char BOOL; +#define YES __objc_yes +#define NO __objc_no + +BOOL B; + +int main() { + B = 0; + B = 1; + B = YES; + B = NO; + + B = -1; // expected-warning{{implicit conversion from -1 to BOOL; the only well defined values for BOOL are YES and NO}} + B = 0 - 1; // expected-warning{{implicit conversion from -1 to BOOL; the only well defined values for BOOL are YES and NO}} + B = YES + YES; // expected-warning {{implicit conversion from 2 to BOOL; the only well defined values for BOOL are YES and NO}} + B = YES | YES; + + B = B ? 2 : 2; // expected-warning 2 {{implicit conversion from 2 to BOOL; the only well defined values for BOOL are YES and NO}} + + BOOL Init = -1; // expected-warning{{implicit conversion from -1 to BOOL; the only well defined values for BOOL are YES and NO}} + BOOL Init2 = B ? 2 : 2; // expected-warning 2 {{implicit conversion from 2 to BOOL; the only well defined values for BOOL are YES and NO}} + + void takesbool(BOOL); + takesbool(43); // expected-warning {{implicit conversion from 43 to BOOL; the only well defined values for BOOL are YES and NO}} + + BOOL OutOfRange = 400; // expected-warning{{implicit conversion from 400 to BOOL; the only well defined values for BOOL are YES and NO}} +} + +@interface BoolProp +@property BOOL b; +@end + +void f(BoolProp *bp) { + bp.b = 43; // expected-warning {{implicit conversion from 43 to BOOL; the only well defined values for BOOL are YES and NO}} + [bp setB:43]; // expected-warning {{implicit conversion from 43 to BOOL; the only well defined values for BOOL are YES and NO}} +}