diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 1cce51ee311172e5550f8b89b5cabdc961392a05..10affb07d31b220e5cdcc0d77264a0ee8dd906ac 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -436,3 +436,8 @@ def ObjCRedundantAPIUse : DiagGroup<"objc-redundant-api-use", [ def ObjCCocoaAPI : DiagGroup<"objc-cocoa-api", [ ObjCRedundantAPIUse ]>; + +def ObjCStringComparison : DiagGroup<"objc-string-compare">; +def ObjCLiteralComparison : DiagGroup<"objc-literal-compare", [ + ObjCStringComparison + ]>; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index e5eea9d48a1872bb65dd90f146f840c0a14bb8c6..0c5c01a6e188504467d619b74dcbf8e278a529fb 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1611,19 +1611,23 @@ def err_box_literal_collection : Error< "%select{string|character|boolean|numeric}0 literal must be prefixed by '@' " "in a collection">; def warn_objc_literal_comparison : Warning< - "direct comparison of %select{a string literal|an array literal|" - "a dictionary literal|a numeric literal|a boxed expression|}0 has " - "undefined behavior">, InGroup>; + "direct comparison of %select{an array literal|a dictionary literal|" + "a numeric literal|a boxed expression|}0 has undefined behavior">, + InGroup; +def warn_objc_string_literal_comparison : Warning< + "direct comparison of a string literal has undefined behavior">, + InGroup; def note_objc_literal_comparison_isequal : Note< "use 'isEqual:' instead">; -def err_attr_tlsmodel_arg : Error<"tls_model must be \"global-dynamic\", " - "\"local-dynamic\", \"initial-exec\" or \"local-exec\"">; let CategoryName = "Cocoa API Issue" in { def warn_objc_redundant_literal_use : Warning< "using %0 with a literal is redundant">, InGroup; } +def err_attr_tlsmodel_arg : Error<"tls_model must be \"global-dynamic\", " + "\"local-dynamic\", \"initial-exec\" or \"local-exec\"">; + def err_only_annotate_after_access_spec : Error< "access specifier can only have annotation attributes">; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 0548cccd1fe2e5688989f96b2c11c2d25f936ae4..fabd673d1b160674847a15f112f679334206d89d 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6744,19 +6744,28 @@ static void diagnoseObjCLiteralComparison(Sema &S, SourceLocation Loc, BinaryOperator::Opcode Opc){ Expr *Literal = (isObjCObjectLiteral(LHS) ? LHS : RHS).get(); - unsigned LiteralKind; + // This should be kept in sync with warn_objc_literal_comparison. + // LK_String should always be last, since it has its own flag. + enum { + LK_Array, + LK_Dictionary, + LK_Numeric, + LK_Boxed, + LK_String + } LiteralKind; + switch (Literal->getStmtClass()) { case Stmt::ObjCStringLiteralClass: // "string literal" - LiteralKind = 0; + LiteralKind = LK_String; break; case Stmt::ObjCArrayLiteralClass: // "array literal" - LiteralKind = 1; + LiteralKind = LK_Array; break; case Stmt::ObjCDictionaryLiteralClass: // "dictionary literal" - LiteralKind = 2; + LiteralKind = LK_Dictionary; break; case Stmt::ObjCBoxedExprClass: { Expr *Inner = cast(Literal)->getSubExpr(); @@ -6767,20 +6776,20 @@ static void diagnoseObjCLiteralComparison(Sema &S, SourceLocation Loc, case Stmt::ObjCBoolLiteralExprClass: case Stmt::CXXBoolLiteralExprClass: // "numeric literal" - LiteralKind = 3; + LiteralKind = LK_Numeric; break; case Stmt::ImplicitCastExprClass: { CastKind CK = cast(Inner)->getCastKind(); // Boolean literals can be represented by implicit casts. if (CK == CK_IntegralToBoolean || CK == CK_IntegralCast) { - LiteralKind = 3; + LiteralKind = LK_Numeric; break; } // FALLTHROUGH } default: // "boxed expression" - LiteralKind = 4; + LiteralKind = LK_Boxed; break; } break; @@ -6789,8 +6798,12 @@ static void diagnoseObjCLiteralComparison(Sema &S, SourceLocation Loc, llvm_unreachable("Unknown Objective-C object literal kind"); } - S.Diag(Loc, diag::warn_objc_literal_comparison) - << LiteralKind << Literal->getSourceRange(); + if (LiteralKind == LK_String) + S.Diag(Loc, diag::warn_objc_string_literal_comparison) + << Literal->getSourceRange(); + else + S.Diag(Loc, diag::warn_objc_literal_comparison) + << LiteralKind << Literal->getSourceRange(); if (BinaryOperator::isEqualityOp(Opc) && hasIsEqualMethod(S, LHS.get(), RHS.get())) { diff --git a/clang/test/SemaObjC/objc-literal-comparison.m b/clang/test/SemaObjC/objc-literal-comparison.m index b0ca139961a0f985de5348206277bcc5984e386d..ea42a176ef03f8f02fc4de181d131c9854495577 100644 --- a/clang/test/SemaObjC/objc-literal-comparison.m +++ b/clang/test/SemaObjC/objc-literal-comparison.m @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wno-everything -Wobjc-literal-compare -verify %s + +// (test the warning flag as well) typedef unsigned char BOOL; @@ -63,3 +65,17 @@ void testComparisonsWithoutFixits() { if (@"" >= @"") return; // expected-warning{{direct comparison of a string literal has undefined behavior}} } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-string-compare" + +void testWarningFlags(id obj) { + if (obj == @"") return; // no-warning + if (@"" == obj) return; // no-warning + + if (obj == @1) return; // expected-warning{{direct comparison of a numeric literal has undefined behavior}} expected-note{{use 'isEqual:' instead}} + if (@1 == obj) return; // expected-warning{{direct comparison of a numeric literal has undefined behavior}} expected-note{{use 'isEqual:' instead}} +} + +#pragma clang diagnostic pop +