From b7d8563973fe5970509d526c73697feb1081c4c2 Mon Sep 17 00:00:00 2001 From: Alex Denisov <1101.debian@gmail.com> Date: Fri, 24 Jul 2015 05:09:40 +0000 Subject: [PATCH] Cleanup ObjCInterfaceDecl lookup for ObjC literals llvm-svn: 243092 --- .../clang/Basic/DiagnosticSemaKinds.td | 15 +- clang/lib/Sema/SemaExprObjC.cpp | 197 +++++++++--------- clang/test/SemaObjC/objc-array-literal.m | 6 +- .../SemaObjC/objc-boxed-expressions-nsvalue.m | 2 +- clang/test/SemaObjC/objc-dictionary-literal.m | 6 +- clang/test/SemaObjC/objc-literal-nsnumber.m | 14 +- .../objc-boxed-expressions-nsvalue.mm | 2 +- 7 files changed, 117 insertions(+), 125 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 56597714ff9d..8a02b2ff5387 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2101,25 +2101,18 @@ def err_field_with_address_space : Error< "field may not be qualified with an address space">; def err_attr_objc_ownership_redundant : Error< "the type %0 is already explicitly ownership-qualified">; -def err_undeclared_nsnumber : Error< - "NSNumber must be available to use Objective-C literals">; -def err_undeclared_nsvalue : Error< - "NSValue must be available to use Objective-C boxed expressions">; def err_invalid_nsnumber_type : Error< "%0 is not a valid literal type for NSNumber">; -def err_undeclared_nsstring : Error< - "cannot box a string value because NSString has not been declared">; def err_objc_illegal_boxed_expression_type : Error< "illegal type %0 used in a boxed expression">; def err_objc_non_trivially_copyable_boxed_expression_type : Error< "non-trivially copyable type %0 cannot be used in a boxed expression">; def err_objc_incomplete_boxed_expression_type : Error< "incomplete type %0 used in a boxed expression">; -def err_undeclared_nsarray : Error< - "NSArray must be available to use Objective-C array literals">; -def err_undeclared_nsdictionary : Error< - "NSDictionary must be available to use Objective-C dictionary " - "literals">; +def err_undeclared_objc_literal_class : Error< + "definition of class %0 must be available to use Objective-C " + "%select{array literals|dictionary literals|numeric literals|boxed expressions|" + "string literals}1">; def err_undeclared_boxing_method : Error< "declaration of %0 is missing in %1 class">; def err_objc_literal_method_sig : Error< diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index 6cd062621951..904a7d5d447f 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -168,6 +168,76 @@ static bool validateBoxingMethod(Sema &S, SourceLocation Loc, return true; } +/// \brief Maps ObjCLiteralKind to NSClassIdKindKind +static NSAPI::NSClassIdKindKind ClassKindFromLiteralKind( + Sema::ObjCLiteralKind LiteralKind) { + switch (LiteralKind) { + case Sema::LK_Array: + return NSAPI::ClassId_NSArray; + case Sema::LK_Dictionary: + return NSAPI::ClassId_NSDictionary; + case Sema::LK_Numeric: + return NSAPI::ClassId_NSNumber; + case Sema::LK_String: + return NSAPI::ClassId_NSString; + case Sema::LK_Boxed: + return NSAPI::ClassId_NSValue; + + // there is no corresponding matching + // between LK_None/LK_Block and NSClassIdKindKind + case Sema::LK_Block: + case Sema::LK_None: + llvm_unreachable("LiteralKind can't be converted into a ClassKind"); + } +} + +/// \brief Validates ObjCInterfaceDecl availability. +/// ObjCInterfaceDecl, used to create ObjC literals, should be defined +/// if clang not in a debugger mode. +static bool ValidateObjCLiteralInterfaceDecl(Sema &S, ObjCInterfaceDecl *Decl, + SourceLocation Loc, + Sema::ObjCLiteralKind LiteralKind) { + if (!Decl) { + NSAPI::NSClassIdKindKind Kind = ClassKindFromLiteralKind(LiteralKind); + IdentifierInfo *II = S.NSAPIObj->getNSClassId(Kind); + S.Diag(Loc, diag::err_undeclared_objc_literal_class) + << II->getName() << LiteralKind; + return false; + } else if (!Decl->hasDefinition() && !S.getLangOpts().DebuggerObjCLiteral) { + S.Diag(Loc, diag::err_undeclared_objc_literal_class) + << Decl->getName() << LiteralKind; + S.Diag(Decl->getLocation(), diag::note_forward_class); + return false; + } + + return true; +} + +/// \brief Looks up ObjCInterfaceDecl of a given NSClassIdKindKind. +/// Used to create ObjC literals, such as NSDictionary (@{}), +/// NSArray (@[]) and Boxed Expressions (@()) +static ObjCInterfaceDecl *LookupObjCInterfaceDeclForLiteral(Sema &S, + SourceLocation Loc, + Sema::ObjCLiteralKind LiteralKind) { + NSAPI::NSClassIdKindKind ClassKind = ClassKindFromLiteralKind(LiteralKind); + IdentifierInfo *II = S.NSAPIObj->getNSClassId(ClassKind); + NamedDecl *IF = S.LookupSingleName(S.TUScope, II, Loc, + Sema::LookupOrdinaryName); + ObjCInterfaceDecl *ID = dyn_cast_or_null(IF); + if (!ID && S.getLangOpts().DebuggerObjCLiteral) { + ASTContext &Context = S.Context; + TranslationUnitDecl *TU = Context.getTranslationUnitDecl(); + ID = ObjCInterfaceDecl::Create (Context, TU, SourceLocation(), II, + nullptr, nullptr, SourceLocation()); + } + + if (!ValidateObjCLiteralInterfaceDecl(S, ID, Loc, LiteralKind)) { + ID = nullptr; + } + + return ID; +} + /// \brief Retrieve the NSNumber factory method that should be used to create /// an Objective-C literal for the given type. static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc, @@ -197,26 +267,9 @@ static ObjCMethodDecl *getNSNumberFactoryMethod(Sema &S, SourceLocation Loc, // Look up the NSNumber class, if we haven't done so already. It's cached // in the Sema instance. if (!S.NSNumberDecl) { - IdentifierInfo *NSNumberId = - S.NSAPIObj->getNSClassId(NSAPI::ClassId_NSNumber); - NamedDecl *IF = S.LookupSingleName(S.TUScope, NSNumberId, - Loc, Sema::LookupOrdinaryName); - S.NSNumberDecl = dyn_cast_or_null(IF); + S.NSNumberDecl = LookupObjCInterfaceDeclForLiteral(S, Loc, + Sema::LK_Numeric); if (!S.NSNumberDecl) { - if (S.getLangOpts().DebuggerObjCLiteral) { - // Create a stub definition of NSNumber. - S.NSNumberDecl = ObjCInterfaceDecl::Create(CX, - CX.getTranslationUnitDecl(), - SourceLocation(), NSNumberId, - nullptr, nullptr, - SourceLocation()); - } else { - // Otherwise, require a declaration of NSNumber. - S.Diag(Loc, diag::err_undeclared_nsnumber); - return nullptr; - } - } else if (!S.NSNumberDecl->hasDefinition()) { - S.Diag(Loc, diag::err_undeclared_nsnumber); return nullptr; } } @@ -457,6 +510,7 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { if (RValue.isInvalid()) { return ExprError(); } + SourceLocation Loc = SR.getBegin(); ValueExpr = RValue.get(); QualType ValueType(ValueExpr->getType()); if (const PointerType *PT = ValueType->getAs()) { @@ -464,29 +518,11 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { if (Context.hasSameUnqualifiedType(PointeeType, Context.CharTy)) { if (!NSStringDecl) { - IdentifierInfo *NSStringId = - NSAPIObj->getNSClassId(NSAPI::ClassId_NSString); - NamedDecl *Decl = LookupSingleName(TUScope, NSStringId, - SR.getBegin(), LookupOrdinaryName); - NSStringDecl = dyn_cast_or_null(Decl); + NSStringDecl = LookupObjCInterfaceDeclForLiteral(*this, Loc, + Sema::LK_String); if (!NSStringDecl) { - if (getLangOpts().DebuggerObjCLiteral) { - // Support boxed expressions in the debugger w/o NSString declaration. - DeclContext *TU = Context.getTranslationUnitDecl(); - NSStringDecl = ObjCInterfaceDecl::Create(Context, TU, - SourceLocation(), - NSStringId, - nullptr, nullptr, - SourceLocation()); - } else { - Diag(SR.getBegin(), diag::err_undeclared_nsstring); - return ExprError(); - } - } else if (!NSStringDecl->hasDefinition()) { - Diag(SR.getBegin(), diag::err_undeclared_nsstring); return ExprError(); } - assert(NSStringDecl && "NSStringDecl should not be NULL"); QualType NSStringObject = Context.getObjCInterfaceType(NSStringDecl); NSStringPointer = Context.getObjCObjectPointerType(NSStringObject); } @@ -520,7 +556,7 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { BoxingMethod = M; } - if (!validateBoxingMethod(*this, SR.getBegin(), NSStringDecl, + if (!validateBoxingMethod(*this, Loc, NSStringDecl, stringWithUTF8String, BoxingMethod)) return ExprError(); @@ -563,16 +599,16 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { // FIXME: Do I need to do anything special with BoolTy expressions? // Look for the appropriate method within NSNumber. - BoxingMethod = getNSNumberFactoryMethod(*this, SR.getBegin(), ValueType); + BoxingMethod = getNSNumberFactoryMethod(*this, Loc, ValueType); BoxedType = NSNumberPointer; } else if (const EnumType *ET = ValueType->getAs()) { if (!ET->getDecl()->isComplete()) { - Diag(SR.getBegin(), diag::err_objc_incomplete_boxed_expression_type) + Diag(Loc, diag::err_objc_incomplete_boxed_expression_type) << ValueType << ValueExpr->getSourceRange(); return ExprError(); } - BoxingMethod = getNSNumberFactoryMethod(*this, SR.getBegin(), + BoxingMethod = getNSNumberFactoryMethod(*this, Loc, ET->getDecl()->getIntegerType()); BoxedType = NSNumberPointer; } else if (ValueType->isObjCBoxableRecordType()) { @@ -582,29 +618,12 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { // Look up the NSValue class, if we haven't done so already. It's cached // in the Sema instance. if (!NSValueDecl) { - IdentifierInfo *NSValueId = - NSAPIObj->getNSClassId(NSAPI::ClassId_NSValue); - NamedDecl *IF = LookupSingleName(TUScope, NSValueId, - SR.getBegin(), Sema::LookupOrdinaryName); - NSValueDecl = dyn_cast_or_null(IF); + NSValueDecl = LookupObjCInterfaceDeclForLiteral(*this, Loc, + Sema::LK_Boxed); if (!NSValueDecl) { - if (getLangOpts().DebuggerObjCLiteral) { - // Create a stub definition of NSValue. - DeclContext *TU = Context.getTranslationUnitDecl(); - NSValueDecl = ObjCInterfaceDecl::Create(Context, TU, - SourceLocation(), NSValueId, - nullptr, nullptr, - SourceLocation()); - } else { - // Otherwise, require a declaration of NSValue. - Diag(SR.getBegin(), diag::err_undeclared_nsvalue); - return ExprError(); - } - } else if (!NSValueDecl->hasDefinition()) { - Diag(SR.getBegin(), diag::err_undeclared_nsvalue); return ExprError(); } - + // generate the pointer to NSValue type. QualType NSValueObject = Context.getObjCInterfaceType(NSValueDecl); NSValuePointer = Context.getObjCObjectPointerType(NSValueObject); @@ -663,7 +682,7 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { BoxingMethod = M; } - if (!validateBoxingMethod(*this, SR.getBegin(), NSValueDecl, + if (!validateBoxingMethod(*this, Loc, NSValueDecl, ValueWithBytesObjCType, BoxingMethod)) return ExprError(); @@ -671,8 +690,7 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { } if (!ValueType.isTriviallyCopyableType(Context)) { - Diag(SR.getBegin(), - diag::err_objc_non_trivially_copyable_boxed_expression_type) + Diag(Loc, diag::err_objc_non_trivially_copyable_boxed_expression_type) << ValueType << ValueExpr->getSourceRange(); return ExprError(); } @@ -682,12 +700,12 @@ ExprResult Sema::BuildObjCBoxedExpr(SourceRange SR, Expr *ValueExpr) { } if (!BoxingMethod) { - Diag(SR.getBegin(), diag::err_objc_illegal_boxed_expression_type) + Diag(Loc, diag::err_objc_illegal_boxed_expression_type) << ValueType << ValueExpr->getSourceRange(); return ExprError(); } - DiagnoseUseOfDecl(BoxingMethod, SR.getBegin()); + DiagnoseUseOfDecl(BoxingMethod, Loc); ExprResult ConvertedValueExpr; if (ValueType->isObjCBoxableRecordType()) { @@ -746,26 +764,16 @@ ExprResult Sema::BuildObjCSubscriptExpression(SourceLocation RB, Expr *BaseExpr, } ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) { - // Look up the NSArray class, if we haven't done so already. - if (!NSArrayDecl) { - NamedDecl *IF = LookupSingleName(TUScope, - NSAPIObj->getNSClassId(NSAPI::ClassId_NSArray), - SR.getBegin(), - LookupOrdinaryName); - NSArrayDecl = dyn_cast_or_null(IF); - if (!NSArrayDecl && getLangOpts().DebuggerObjCLiteral) - NSArrayDecl = ObjCInterfaceDecl::Create (Context, - Context.getTranslationUnitDecl(), - SourceLocation(), - NSAPIObj->getNSClassId(NSAPI::ClassId_NSArray), - nullptr, nullptr, SourceLocation()); + SourceLocation Loc = SR.getBegin(); + if (!NSArrayDecl) { + NSArrayDecl = LookupObjCInterfaceDeclForLiteral(*this, Loc, + Sema::LK_Array); if (!NSArrayDecl) { - Diag(SR.getBegin(), diag::err_undeclared_nsarray); return ExprError(); } } - + // Find the arrayWithObjects:count: method, if we haven't done so already. QualType IdT = Context.getObjCIdType(); if (!ArrayWithObjectsMethod) { @@ -801,7 +809,7 @@ ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) { Method->setMethodParams(Context, Params, None); } - if (!validateBoxingMethod(*this, SR.getBegin(), NSArrayDecl, Sel, Method)) + if (!validateBoxingMethod(*this, Loc, NSArrayDecl, Sel, Method)) return ExprError(); // Dig out the type that all elements should be converted to. @@ -862,25 +870,16 @@ ExprResult Sema::BuildObjCArrayLiteral(SourceRange SR, MultiExprArg Elements) { ExprResult Sema::BuildObjCDictionaryLiteral(SourceRange SR, ObjCDictionaryElement *Elements, unsigned NumElements) { - // Look up the NSDictionary class, if we haven't done so already. - if (!NSDictionaryDecl) { - NamedDecl *IF = LookupSingleName(TUScope, - NSAPIObj->getNSClassId(NSAPI::ClassId_NSDictionary), - SR.getBegin(), LookupOrdinaryName); - NSDictionaryDecl = dyn_cast_or_null(IF); - if (!NSDictionaryDecl && getLangOpts().DebuggerObjCLiteral) - NSDictionaryDecl = ObjCInterfaceDecl::Create (Context, - Context.getTranslationUnitDecl(), - SourceLocation(), - NSAPIObj->getNSClassId(NSAPI::ClassId_NSDictionary), - nullptr, nullptr, SourceLocation()); + SourceLocation Loc = SR.getBegin(); + if (!NSDictionaryDecl) { + NSDictionaryDecl = LookupObjCInterfaceDeclForLiteral(*this, Loc, + Sema::LK_Dictionary); if (!NSDictionaryDecl) { - Diag(SR.getBegin(), diag::err_undeclared_nsdictionary); - return ExprError(); + return ExprError(); } } - + // Find the dictionaryWithObjects:forKeys:count: method, if we haven't done // so already. QualType IdT = Context.getObjCIdType(); diff --git a/clang/test/SemaObjC/objc-array-literal.m b/clang/test/SemaObjC/objc-array-literal.m index 2971fcc45399..281994a758b2 100644 --- a/clang/test/SemaObjC/objc-array-literal.m +++ b/clang/test/SemaObjC/objc-array-literal.m @@ -11,14 +11,14 @@ typedef unsigned int NSUInteger; void checkNSArrayUnavailableDiagnostic() { id obj; - id arr = @[obj]; // expected-error {{NSArray must be available to use Objective-C array literals}} + id arr = @[obj]; // expected-error {{definition of class NSArray must be available to use Objective-C array literals}} } -@class NSArray; +@class NSArray; // expected-note {{forward declaration of class here}} void checkNSArrayFDDiagnostic() { id obj; - id arr = @[obj]; // expected-error {{declaration of 'arrayWithObjects:count:' is missing in NSArray class}} + id arr = @[obj]; // expected-error {{definition of class NSArray must be available to use Objective-C array literals}} } @class NSString; diff --git a/clang/test/SemaObjC/objc-boxed-expressions-nsvalue.m b/clang/test/SemaObjC/objc-boxed-expressions-nsvalue.m index 1599f28c7944..f5ef55ef2439 100644 --- a/clang/test/SemaObjC/objc-boxed-expressions-nsvalue.m +++ b/clang/test/SemaObjC/objc-boxed-expressions-nsvalue.m @@ -46,7 +46,7 @@ typedef union BOXABLE _BoxableUnion { void checkNSValueDiagnostic() { NSRect rect; - id value = @(rect); // expected-error{{NSValue must be available to use Objective-C boxed expressions}} + id value = @(rect); // expected-error{{definition of class NSValue must be available to use Objective-C boxed expressions}} } @interface NSValue diff --git a/clang/test/SemaObjC/objc-dictionary-literal.m b/clang/test/SemaObjC/objc-dictionary-literal.m index 87f127f9281e..1a2c29f74db0 100644 --- a/clang/test/SemaObjC/objc-dictionary-literal.m +++ b/clang/test/SemaObjC/objc-dictionary-literal.m @@ -8,15 +8,15 @@ void checkNSDictionaryUnavailableDiagnostic() { id key; id value; - id dict = @{ key : value }; // expected-error {{NSDictionary must be available to use Objective-C dictionary literals}} + id dict = @{ key : value }; // expected-error {{definition of class NSDictionary must be available to use Objective-C dictionary literals}} } -@class NSDictionary; +@class NSDictionary; // expected-note {{forward declaration of class here}} void checkNSDictionaryFDDiagnostic() { id key; id value; - id dic = @{ key : value }; // expected-error {{declaration of 'dictionaryWithObjects:forKeys:count:' is missing in NSDictionary class}} + id dic = @{ key : value }; // expected-error {{definition of class NSDictionary must be available to use Objective-C dictionary literals}} } @interface NSNumber diff --git a/clang/test/SemaObjC/objc-literal-nsnumber.m b/clang/test/SemaObjC/objc-literal-nsnumber.m index 57bc07b139e9..ceb31f8af156 100644 --- a/clang/test/SemaObjC/objc-literal-nsnumber.m +++ b/clang/test/SemaObjC/objc-literal-nsnumber.m @@ -10,20 +10,20 @@ typedef int NSInteger; #endif void checkNSNumberUnavailableDiagnostic() { - id num = @1000; // expected-error {{NSNumber must be available to use Objective-C literals}} + id num = @1000; // expected-error {{definition of class NSNumber must be available to use Objective-C numeric literals}} int x = 1000; - id num1 = @(x); // expected-error {{NSNumber must be available to use Objective-C literals}}\ + id num1 = @(x); // expected-error {{definition of class NSNumber must be available to use Objective-C numeric literals}}\ // expected-error {{illegal type 'int' used in a boxed expression}} } -@class NSNumber; +@class NSNumber; // expected-note 2 {{forward declaration of class here}} void checkNSNumberFDDiagnostic() { - id num = @1000; // expected-error {{NSNumber must be available to use Objective-C literals}} + id num = @1000; // expected-error {{definition of class NSNumber must be available to use Objective-C numeric literals}} int x = 1000; - id num1 = @(x); // expected-error {{declaration of 'numberWithInt:' is missing in NSNumber class}}\ + id num1 = @(x); // expected-error {{definition of class NSNumber must be available to use Objective-C numeric literals}}\ // expected-error {{illegal type 'int' used in a boxed expression}} } @@ -71,10 +71,10 @@ int main() { } // Dictionary test -@class NSDictionary; +@class NSDictionary; // expected-note {{forward declaration of class here}} NSDictionary *err() { - return @{@"name" : @"value"}; // expected-error {{declaration of 'dictionaryWithObjects:forKeys:count:' is missing in NSDictionary class}} + return @{@"name" : @"value"}; // expected-error {{definition of class NSDictionary must be available to use Objective-C dictionary literals}} } @interface NSDate : NSObject diff --git a/clang/test/SemaObjCXX/objc-boxed-expressions-nsvalue.mm b/clang/test/SemaObjCXX/objc-boxed-expressions-nsvalue.mm index 375b9555fcaf..3b590c46f004 100644 --- a/clang/test/SemaObjCXX/objc-boxed-expressions-nsvalue.mm +++ b/clang/test/SemaObjCXX/objc-boxed-expressions-nsvalue.mm @@ -48,7 +48,7 @@ struct BOXABLE NonTriviallyCopyable { void checkNSValueDiagnostic() { NSRect rect; - id value = @(rect); // expected-error{{NSValue must be available to use Objective-C boxed expressions}} + id value = @(rect); // expected-error{{definition of class NSValue must be available to use Objective-C boxed expressions}} } @interface NSValue