| //===--- NSAPI.cpp - NSFoundation APIs ------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/AST/NSAPI.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/DeclObjC.h" |
| #include "clang/AST/Expr.h" |
| #include "llvm/ADT/StringSwitch.h" |
| |
| using namespace clang; |
| |
| NSAPI::NSAPI(ASTContext &ctx) |
| : Ctx(ctx), ClassIds(), BOOLId(nullptr), NSIntegerId(nullptr), |
| NSUIntegerId(nullptr), NSASCIIStringEncodingId(nullptr), |
| NSUTF8StringEncodingId(nullptr) {} |
| |
| IdentifierInfo *NSAPI::getNSClassId(NSClassIdKindKind K) const { |
| static const char *ClassName[NumClassIds] = { |
| "NSObject", |
| "NSString", |
| "NSArray", |
| "NSMutableArray", |
| "NSDictionary", |
| "NSMutableDictionary", |
| "NSNumber", |
| "NSMutableSet", |
| "NSMutableOrderedSet", |
| "NSValue" |
| }; |
| |
| if (!ClassIds[K]) |
| return (ClassIds[K] = &Ctx.Idents.get(ClassName[K])); |
| |
| return ClassIds[K]; |
| } |
| |
| Selector NSAPI::getNSStringSelector(NSStringMethodKind MK) const { |
| if (NSStringSelectors[MK].isNull()) { |
| Selector Sel; |
| switch (MK) { |
| case NSStr_stringWithString: |
| Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("stringWithString")); |
| break; |
| case NSStr_stringWithUTF8String: |
| Sel = Ctx.Selectors.getUnarySelector( |
| &Ctx.Idents.get("stringWithUTF8String")); |
| break; |
| case NSStr_initWithUTF8String: |
| Sel = Ctx.Selectors.getUnarySelector( |
| &Ctx.Idents.get("initWithUTF8String")); |
| break; |
| case NSStr_stringWithCStringEncoding: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("stringWithCString"), |
| &Ctx.Idents.get("encoding") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| case NSStr_stringWithCString: |
| Sel= Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("stringWithCString")); |
| break; |
| case NSStr_initWithString: |
| Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("initWithString")); |
| break; |
| } |
| return (NSStringSelectors[MK] = Sel); |
| } |
| |
| return NSStringSelectors[MK]; |
| } |
| |
| Selector NSAPI::getNSArraySelector(NSArrayMethodKind MK) const { |
| if (NSArraySelectors[MK].isNull()) { |
| Selector Sel; |
| switch (MK) { |
| case NSArr_array: |
| Sel = Ctx.Selectors.getNullarySelector(&Ctx.Idents.get("array")); |
| break; |
| case NSArr_arrayWithArray: |
| Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("arrayWithArray")); |
| break; |
| case NSArr_arrayWithObject: |
| Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("arrayWithObject")); |
| break; |
| case NSArr_arrayWithObjects: |
| Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("arrayWithObjects")); |
| break; |
| case NSArr_arrayWithObjectsCount: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("arrayWithObjects"), |
| &Ctx.Idents.get("count") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| case NSArr_initWithArray: |
| Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("initWithArray")); |
| break; |
| case NSArr_initWithObjects: |
| Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("initWithObjects")); |
| break; |
| case NSArr_objectAtIndex: |
| Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("objectAtIndex")); |
| break; |
| case NSMutableArr_replaceObjectAtIndex: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("replaceObjectAtIndex"), |
| &Ctx.Idents.get("withObject") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| case NSMutableArr_addObject: |
| Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("addObject")); |
| break; |
| case NSMutableArr_insertObjectAtIndex: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("insertObject"), |
| &Ctx.Idents.get("atIndex") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| case NSMutableArr_setObjectAtIndexedSubscript: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("setObject"), |
| &Ctx.Idents.get("atIndexedSubscript") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| } |
| return (NSArraySelectors[MK] = Sel); |
| } |
| |
| return NSArraySelectors[MK]; |
| } |
| |
| Optional<NSAPI::NSArrayMethodKind> NSAPI::getNSArrayMethodKind(Selector Sel) { |
| for (unsigned i = 0; i != NumNSArrayMethods; ++i) { |
| NSArrayMethodKind MK = NSArrayMethodKind(i); |
| if (Sel == getNSArraySelector(MK)) |
| return MK; |
| } |
| |
| return None; |
| } |
| |
| Selector NSAPI::getNSDictionarySelector( |
| NSDictionaryMethodKind MK) const { |
| if (NSDictionarySelectors[MK].isNull()) { |
| Selector Sel; |
| switch (MK) { |
| case NSDict_dictionary: |
| Sel = Ctx.Selectors.getNullarySelector(&Ctx.Idents.get("dictionary")); |
| break; |
| case NSDict_dictionaryWithDictionary: |
| Sel = Ctx.Selectors.getUnarySelector( |
| &Ctx.Idents.get("dictionaryWithDictionary")); |
| break; |
| case NSDict_dictionaryWithObjectForKey: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("dictionaryWithObject"), |
| &Ctx.Idents.get("forKey") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| case NSDict_dictionaryWithObjectsForKeys: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("dictionaryWithObjects"), |
| &Ctx.Idents.get("forKeys") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| case NSDict_dictionaryWithObjectsForKeysCount: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("dictionaryWithObjects"), |
| &Ctx.Idents.get("forKeys"), |
| &Ctx.Idents.get("count") |
| }; |
| Sel = Ctx.Selectors.getSelector(3, KeyIdents); |
| break; |
| } |
| case NSDict_dictionaryWithObjectsAndKeys: |
| Sel = Ctx.Selectors.getUnarySelector( |
| &Ctx.Idents.get("dictionaryWithObjectsAndKeys")); |
| break; |
| case NSDict_initWithDictionary: |
| Sel = Ctx.Selectors.getUnarySelector( |
| &Ctx.Idents.get("initWithDictionary")); |
| break; |
| case NSDict_initWithObjectsAndKeys: |
| Sel = Ctx.Selectors.getUnarySelector( |
| &Ctx.Idents.get("initWithObjectsAndKeys")); |
| break; |
| case NSDict_initWithObjectsForKeys: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("initWithObjects"), |
| &Ctx.Idents.get("forKeys") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| case NSDict_objectForKey: |
| Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("objectForKey")); |
| break; |
| case NSMutableDict_setObjectForKey: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("setObject"), |
| &Ctx.Idents.get("forKey") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| case NSMutableDict_setObjectForKeyedSubscript: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("setObject"), |
| &Ctx.Idents.get("forKeyedSubscript") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| case NSMutableDict_setValueForKey: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("setValue"), |
| &Ctx.Idents.get("forKey") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| } |
| return (NSDictionarySelectors[MK] = Sel); |
| } |
| |
| return NSDictionarySelectors[MK]; |
| } |
| |
| Optional<NSAPI::NSDictionaryMethodKind> |
| NSAPI::getNSDictionaryMethodKind(Selector Sel) { |
| for (unsigned i = 0; i != NumNSDictionaryMethods; ++i) { |
| NSDictionaryMethodKind MK = NSDictionaryMethodKind(i); |
| if (Sel == getNSDictionarySelector(MK)) |
| return MK; |
| } |
| |
| return None; |
| } |
| |
| Selector NSAPI::getNSSetSelector(NSSetMethodKind MK) const { |
| if (NSSetSelectors[MK].isNull()) { |
| Selector Sel; |
| switch (MK) { |
| case NSMutableSet_addObject: |
| Sel = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get("addObject")); |
| break; |
| case NSOrderedSet_insertObjectAtIndex: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("insertObject"), |
| &Ctx.Idents.get("atIndex") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| case NSOrderedSet_setObjectAtIndex: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("setObject"), |
| &Ctx.Idents.get("atIndex") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| case NSOrderedSet_setObjectAtIndexedSubscript: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("setObject"), |
| &Ctx.Idents.get("atIndexedSubscript") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| case NSOrderedSet_replaceObjectAtIndexWithObject: { |
| IdentifierInfo *KeyIdents[] = { |
| &Ctx.Idents.get("replaceObjectAtIndex"), |
| &Ctx.Idents.get("withObject") |
| }; |
| Sel = Ctx.Selectors.getSelector(2, KeyIdents); |
| break; |
| } |
| } |
| return (NSSetSelectors[MK] = Sel); |
| } |
| |
| return NSSetSelectors[MK]; |
| } |
| |
| Optional<NSAPI::NSSetMethodKind> |
| NSAPI::getNSSetMethodKind(Selector Sel) { |
| for (unsigned i = 0; i != NumNSSetMethods; ++i) { |
| NSSetMethodKind MK = NSSetMethodKind(i); |
| if (Sel == getNSSetSelector(MK)) |
| return MK; |
| } |
| |
| return None; |
| } |
| |
| Selector NSAPI::getNSNumberLiteralSelector(NSNumberLiteralMethodKind MK, |
| bool Instance) const { |
| static const char *ClassSelectorName[NumNSNumberLiteralMethods] = { |
| "numberWithChar", |
| "numberWithUnsignedChar", |
| "numberWithShort", |
| "numberWithUnsignedShort", |
| "numberWithInt", |
| "numberWithUnsignedInt", |
| "numberWithLong", |
| "numberWithUnsignedLong", |
| "numberWithLongLong", |
| "numberWithUnsignedLongLong", |
| "numberWithFloat", |
| "numberWithDouble", |
| "numberWithBool", |
| "numberWithInteger", |
| "numberWithUnsignedInteger" |
| }; |
| static const char *InstanceSelectorName[NumNSNumberLiteralMethods] = { |
| "initWithChar", |
| "initWithUnsignedChar", |
| "initWithShort", |
| "initWithUnsignedShort", |
| "initWithInt", |
| "initWithUnsignedInt", |
| "initWithLong", |
| "initWithUnsignedLong", |
| "initWithLongLong", |
| "initWithUnsignedLongLong", |
| "initWithFloat", |
| "initWithDouble", |
| "initWithBool", |
| "initWithInteger", |
| "initWithUnsignedInteger" |
| }; |
| |
| Selector *Sels; |
| const char **Names; |
| if (Instance) { |
| Sels = NSNumberInstanceSelectors; |
| Names = InstanceSelectorName; |
| } else { |
| Sels = NSNumberClassSelectors; |
| Names = ClassSelectorName; |
| } |
| |
| if (Sels[MK].isNull()) |
| Sels[MK] = Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(Names[MK])); |
| return Sels[MK]; |
| } |
| |
| Optional<NSAPI::NSNumberLiteralMethodKind> |
| NSAPI::getNSNumberLiteralMethodKind(Selector Sel) const { |
| for (unsigned i = 0; i != NumNSNumberLiteralMethods; ++i) { |
| NSNumberLiteralMethodKind MK = NSNumberLiteralMethodKind(i); |
| if (isNSNumberLiteralSelector(MK, Sel)) |
| return MK; |
| } |
| |
| return None; |
| } |
| |
| Optional<NSAPI::NSNumberLiteralMethodKind> |
| NSAPI::getNSNumberFactoryMethodKind(QualType T) const { |
| const BuiltinType *BT = T->getAs<BuiltinType>(); |
| if (!BT) |
| return None; |
| |
| const TypedefType *TDT = T->getAs<TypedefType>(); |
| if (TDT) { |
| QualType TDTTy = QualType(TDT, 0); |
| if (isObjCBOOLType(TDTTy)) |
| return NSAPI::NSNumberWithBool; |
| if (isObjCNSIntegerType(TDTTy)) |
| return NSAPI::NSNumberWithInteger; |
| if (isObjCNSUIntegerType(TDTTy)) |
| return NSAPI::NSNumberWithUnsignedInteger; |
| } |
| |
| switch (BT->getKind()) { |
| case BuiltinType::Char_S: |
| case BuiltinType::SChar: |
| return NSAPI::NSNumberWithChar; |
| case BuiltinType::Char_U: |
| case BuiltinType::UChar: |
| return NSAPI::NSNumberWithUnsignedChar; |
| case BuiltinType::Short: |
| return NSAPI::NSNumberWithShort; |
| case BuiltinType::UShort: |
| return NSAPI::NSNumberWithUnsignedShort; |
| case BuiltinType::Int: |
| return NSAPI::NSNumberWithInt; |
| case BuiltinType::UInt: |
| return NSAPI::NSNumberWithUnsignedInt; |
| case BuiltinType::Long: |
| return NSAPI::NSNumberWithLong; |
| case BuiltinType::ULong: |
| return NSAPI::NSNumberWithUnsignedLong; |
| case BuiltinType::LongLong: |
| return NSAPI::NSNumberWithLongLong; |
| case BuiltinType::ULongLong: |
| return NSAPI::NSNumberWithUnsignedLongLong; |
| case BuiltinType::Float: |
| return NSAPI::NSNumberWithFloat; |
| case BuiltinType::Double: |
| return NSAPI::NSNumberWithDouble; |
| case BuiltinType::Bool: |
| return NSAPI::NSNumberWithBool; |
| |
| case BuiltinType::Void: |
| case BuiltinType::WChar_U: |
| case BuiltinType::WChar_S: |
| case BuiltinType::Char8: |
| case BuiltinType::Char16: |
| case BuiltinType::Char32: |
| case BuiltinType::Int128: |
| case BuiltinType::LongDouble: |
| case BuiltinType::ShortAccum: |
| case BuiltinType::Accum: |
| case BuiltinType::LongAccum: |
| case BuiltinType::UShortAccum: |
| case BuiltinType::UAccum: |
| case BuiltinType::ULongAccum: |
| case BuiltinType::ShortFract: |
| case BuiltinType::Fract: |
| case BuiltinType::LongFract: |
| case BuiltinType::UShortFract: |
| case BuiltinType::UFract: |
| case BuiltinType::ULongFract: |
| case BuiltinType::SatShortAccum: |
| case BuiltinType::SatAccum: |
| case BuiltinType::SatLongAccum: |
| case BuiltinType::SatUShortAccum: |
| case BuiltinType::SatUAccum: |
| case BuiltinType::SatULongAccum: |
| case BuiltinType::SatShortFract: |
| case BuiltinType::SatFract: |
| case BuiltinType::SatLongFract: |
| case BuiltinType::SatUShortFract: |
| case BuiltinType::SatUFract: |
| case BuiltinType::SatULongFract: |
| case BuiltinType::UInt128: |
| case BuiltinType::Float16: |
| case BuiltinType::Float128: |
| case BuiltinType::Ibm128: |
| case BuiltinType::NullPtr: |
| case BuiltinType::ObjCClass: |
| case BuiltinType::ObjCId: |
| case BuiltinType::ObjCSel: |
| #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ |
| case BuiltinType::Id: |
| #include "clang/Basic/OpenCLImageTypes.def" |
| #define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ |
| case BuiltinType::Id: |
| #include "clang/Basic/OpenCLExtensionTypes.def" |
| case BuiltinType::OCLSampler: |
| case BuiltinType::OCLEvent: |
| case BuiltinType::OCLClkEvent: |
| case BuiltinType::OCLQueue: |
| case BuiltinType::OCLReserveID: |
| #define SVE_TYPE(Name, Id, SingletonId) \ |
| case BuiltinType::Id: |
| #include "clang/Basic/AArch64SVEACLETypes.def" |
| #define PPC_VECTOR_TYPE(Name, Id, Size) \ |
| case BuiltinType::Id: |
| #include "clang/Basic/PPCTypes.def" |
| #define RVV_TYPE(Name, Id, SingletonId) case BuiltinType::Id: |
| #include "clang/Basic/RISCVVTypes.def" |
| case BuiltinType::BoundMember: |
| case BuiltinType::Dependent: |
| case BuiltinType::Overload: |
| case BuiltinType::UnknownAny: |
| case BuiltinType::ARCUnbridgedCast: |
| case BuiltinType::Half: |
| case BuiltinType::PseudoObject: |
| case BuiltinType::BuiltinFn: |
| case BuiltinType::IncompleteMatrixIdx: |
| case BuiltinType::OMPArraySection: |
| case BuiltinType::OMPArrayShaping: |
| case BuiltinType::OMPIterator: |
| case BuiltinType::BFloat16: |
| break; |
| } |
| |
| return None; |
| } |
| |
| /// Returns true if \param T is a typedef of "BOOL" in objective-c. |
| bool NSAPI::isObjCBOOLType(QualType T) const { |
| return isObjCTypedef(T, "BOOL", BOOLId); |
| } |
| /// Returns true if \param T is a typedef of "NSInteger" in objective-c. |
| bool NSAPI::isObjCNSIntegerType(QualType T) const { |
| return isObjCTypedef(T, "NSInteger", NSIntegerId); |
| } |
| /// Returns true if \param T is a typedef of "NSUInteger" in objective-c. |
| bool NSAPI::isObjCNSUIntegerType(QualType T) const { |
| return isObjCTypedef(T, "NSUInteger", NSUIntegerId); |
| } |
| |
| StringRef NSAPI::GetNSIntegralKind(QualType T) const { |
| if (!Ctx.getLangOpts().ObjC || T.isNull()) |
| return StringRef(); |
| |
| while (const TypedefType *TDT = T->getAs<TypedefType>()) { |
| StringRef NSIntegralResust = |
| llvm::StringSwitch<StringRef>( |
| TDT->getDecl()->getDeclName().getAsIdentifierInfo()->getName()) |
| .Case("int8_t", "int8_t") |
| .Case("int16_t", "int16_t") |
| .Case("int32_t", "int32_t") |
| .Case("NSInteger", "NSInteger") |
| .Case("int64_t", "int64_t") |
| .Case("uint8_t", "uint8_t") |
| .Case("uint16_t", "uint16_t") |
| .Case("uint32_t", "uint32_t") |
| .Case("NSUInteger", "NSUInteger") |
| .Case("uint64_t", "uint64_t") |
| .Default(StringRef()); |
| if (!NSIntegralResust.empty()) |
| return NSIntegralResust; |
| T = TDT->desugar(); |
| } |
| return StringRef(); |
| } |
| |
| bool NSAPI::isMacroDefined(StringRef Id) const { |
| // FIXME: Check whether the relevant module macros are visible. |
| return Ctx.Idents.get(Id).hasMacroDefinition(); |
| } |
| |
| bool NSAPI::isSubclassOfNSClass(ObjCInterfaceDecl *InterfaceDecl, |
| NSClassIdKindKind NSClassKind) const { |
| if (!InterfaceDecl) { |
| return false; |
| } |
| |
| IdentifierInfo *NSClassID = getNSClassId(NSClassKind); |
| |
| bool IsSubclass = false; |
| do { |
| IsSubclass = NSClassID == InterfaceDecl->getIdentifier(); |
| |
| if (IsSubclass) { |
| break; |
| } |
| } while ((InterfaceDecl = InterfaceDecl->getSuperClass())); |
| |
| return IsSubclass; |
| } |
| |
| bool NSAPI::isObjCTypedef(QualType T, |
| StringRef name, IdentifierInfo *&II) const { |
| if (!Ctx.getLangOpts().ObjC) |
| return false; |
| if (T.isNull()) |
| return false; |
| |
| if (!II) |
| II = &Ctx.Idents.get(name); |
| |
| while (const TypedefType *TDT = T->getAs<TypedefType>()) { |
| if (TDT->getDecl()->getDeclName().getAsIdentifierInfo() == II) |
| return true; |
| T = TDT->desugar(); |
| } |
| |
| return false; |
| } |
| |
| bool NSAPI::isObjCEnumerator(const Expr *E, |
| StringRef name, IdentifierInfo *&II) const { |
| if (!Ctx.getLangOpts().ObjC) |
| return false; |
| if (!E) |
| return false; |
| |
| if (!II) |
| II = &Ctx.Idents.get(name); |
| |
| if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) |
| if (const EnumConstantDecl * |
| EnumD = dyn_cast_or_null<EnumConstantDecl>(DRE->getDecl())) |
| return EnumD->getIdentifier() == II; |
| |
| return false; |
| } |
| |
| Selector NSAPI::getOrInitSelector(ArrayRef<StringRef> Ids, |
| Selector &Sel) const { |
| if (Sel.isNull()) { |
| SmallVector<IdentifierInfo *, 4> Idents; |
| for (ArrayRef<StringRef>::const_iterator |
| I = Ids.begin(), E = Ids.end(); I != E; ++I) |
| Idents.push_back(&Ctx.Idents.get(*I)); |
| Sel = Ctx.Selectors.getSelector(Idents.size(), Idents.data()); |
| } |
| return Sel; |
| } |
| |
| Selector NSAPI::getOrInitNullarySelector(StringRef Id, Selector &Sel) const { |
| if (Sel.isNull()) { |
| IdentifierInfo *Ident = &Ctx.Idents.get(Id); |
| Sel = Ctx.Selectors.getSelector(0, &Ident); |
| } |
| return Sel; |
| } |