diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2945,7 +2945,7 @@ "a numeric literal|a boxed expression|}0 has undefined behavior">, InGroup; def err_missing_atsign_prefix : Error< - "string literal must be prefixed by '@' ">; + "%select{string|numeric}0 literal must be prefixed by '@'">; def warn_objc_string_literal_comparison : Warning< "direct comparison of a string literal has undefined behavior">, InGroup; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9455,8 +9455,8 @@ QualType DestType, QualType SrcType, Expr *&SrcExpr, bool Diagnose = true); - bool ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&SrcExpr, - bool Diagnose = true); + bool CheckConversionToObjCLiteral(QualType DstType, Expr *&SrcExpr, + bool Diagnose = true); bool checkInitMethod(ObjCMethodDecl *method, QualType receiverTypeIfCall); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -9283,7 +9283,7 @@ if (getLangOpts().ObjC && (CheckObjCBridgeRelatedConversions(E->getBeginLoc(), LHSType, E->getType(), E, Diagnose) || - ConversionToObjCStringLiteralCheck(LHSType, E, Diagnose))) { + CheckConversionToObjCLiteral(LHSType, E, Diagnose))) { if (!Diagnose) return Incompatible; // Replace the expression with a corrected version and continue so we @@ -15228,21 +15228,15 @@ SourceLocExpr(Context, Kind, BuiltinLoc, RPLoc, ParentContext); } -bool Sema::ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&Exp, - bool Diagnose) { +bool Sema::CheckConversionToObjCLiteral(QualType DstType, Expr *&Exp, + bool Diagnose) { if (!getLangOpts().ObjC) return false; const ObjCObjectPointerType *PT = DstType->getAs(); if (!PT) return false; - - if (!PT->isObjCIdType()) { - // Check if the destination is the 'NSString' interface. - const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); - if (!ID || !ID->getIdentifier()->isStr("NSString")) - return false; - } + const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); // Ignore any parens, implicit casts (should only be // array-to-pointer decays), and not-so-opaque values. The last is @@ -15252,15 +15246,41 @@ if (OV->getSourceExpr()) SrcExpr = OV->getSourceExpr()->IgnoreParenImpCasts(); - StringLiteral *SL = dyn_cast(SrcExpr); - if (!SL || !SL->isAscii()) - return false; - if (Diagnose) { - Diag(SL->getBeginLoc(), diag::err_missing_atsign_prefix) - << FixItHint::CreateInsertion(SL->getBeginLoc(), "@"); - Exp = BuildObjCStringLiteral(SL->getBeginLoc(), SL).get(); + if (auto *SL = dyn_cast(SrcExpr)) { + if (!PT->isObjCIdType() && + !(ID && ID->getIdentifier()->isStr("NSString"))) + return false; + if (!SL->isAscii()) + return false; + + if (Diagnose) { + Diag(SL->getBeginLoc(), diag::err_missing_atsign_prefix) + << /*string*/0 << FixItHint::CreateInsertion(SL->getBeginLoc(), "@"); + Exp = BuildObjCStringLiteral(SL->getBeginLoc(), SL).get(); + } + return true; } - return true; + + if ((isa(SrcExpr) || isa(SrcExpr) || + isa(SrcExpr) || isa(SrcExpr) || + isa(SrcExpr)) && + !SrcExpr->isNullPointerConstant( + getASTContext(), Expr::NPC_NeverValueDependent)) { + if (!ID || !ID->getIdentifier()->isStr("NSNumber")) + return false; + if (Diagnose) { + Diag(SrcExpr->getBeginLoc(), diag::err_missing_atsign_prefix) + << /*number*/1 + << FixItHint::CreateInsertion(SrcExpr->getBeginLoc(), "@"); + Expr *NumLit = + BuildObjCNumericLiteral(SrcExpr->getBeginLoc(), SrcExpr).get(); + if (NumLit) + Exp = NumLit; + } + return true; + } + + return false; } static bool maybeDiagnoseAssignmentToFunction(Sema &S, QualType DstType, diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -4363,7 +4363,7 @@ // to 'NSString *', instead of falling through to report a "bridge cast" // diagnostic. if (castACTC == ACTC_retainable && exprACTC == ACTC_none && - ConversionToObjCStringLiteralCheck(castType, castExpr, Diagnose)) + CheckConversionToObjCLiteral(castType, castExpr, Diagnose)) return ACR_error; // Do not issue "bridge cast" diagnostic when implicit casting diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -5625,7 +5625,7 @@ if (S.CheckObjCBridgeRelatedConversions(Initializer->getBeginLoc(), DestType, Initializer->getType(), Initializer) || - S.ConversionToObjCStringLiteralCheck(DestType, Initializer)) + S.CheckConversionToObjCLiteral(DestType, Initializer)) Args[0] = Initializer; } if (!isa(Initializer)) diff --git a/clang/test/SemaObjC/objc-literal-fixit.m b/clang/test/SemaObjC/objc-literal-fixit.m new file mode 100644 --- /dev/null +++ b/clang/test/SemaObjC/objc-literal-fixit.m @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macos10.10 %s -verify=c,expected +// RUN: %clang_cc1 -triple x86_64-apple-macos10.10 %s -xobjective-c++ -verify=cxx,expected +// RUN: %clang_cc1 -triple x86_64-apple-macos10.10 %s -fobjc-arc -verify=c,arc,expected + +typedef signed char BOOL; +#define YES __objc_yes +#define NO __objc_no + +@interface NSNumber ++(instancetype)numberWithChar:(char)value; ++(instancetype)numberWithInt:(int)value; ++(instancetype)numberWithDouble:(double)value; ++(instancetype)numberWithBool:(BOOL)value; +@end + +void test() { + NSNumber *n = YES; // expected-error{{numeric literal must be prefixed by '@'}} + NSNumber *n1 = 1; // expected-error{{numeric literal must be prefixed by '@'}} + + NSNumber *n2 = NO; // c-warning{{expression which evaluates to zero treated as a null pointer constant}} + // cxx-error@-1{{numeric literal must be prefixed by '@'}} + NSNumber *n3 = 0; + NSNumber *n4 = 0.0; // expected-error{{numeric literal must be prefixed by '@'}} + + NSNumber *n5 = '\0'; // c-warning{{expression which evaluates to zero treated as a null pointer constant}} + // cxx-error@-1{{numeric literal must be prefixed by '@'}} + + + NSNumber *n6 = '1'; // expected-error{{numeric literal must be prefixed by '@'}} + + int i; + NSNumber *n7 = i; // c-warning{{incompatible integer to pointer conversion}} + // arc-error@-1{{implicit conversion of 'int' to 'NSNumber *' is disallowed with ARC}} + // cxx-error@-2{{cannot initialize a variable of type 'NSNumber *' with an lvalue of type 'int'}} + + id n8 = 1; // c-warning{{incompatible integer to pointer conversion}} + // arc-error@-1{{implicit conversion of 'int' to 'id' is disallowed with ARC}} + // cxx-error@-2{{cannot initialize a variable of type 'id' with an rvalue of type 'int'}} +}