1 //===--- IndexSymbol.cpp - Types and functions for indexing symbols -------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/Index/IndexSymbol.h" 10 #include "clang/AST/DeclCXX.h" 11 #include "clang/AST/DeclObjC.h" 12 #include "clang/AST/DeclTemplate.h" 13 #include "clang/AST/PrettyPrinter.h" 14 #include "clang/Lex/MacroInfo.h" 15 16 using namespace clang; 17 using namespace clang::index; 18 19 /// \returns true if \c D is a subclass of 'XCTestCase'. 20 static bool isUnitTestCase(const ObjCInterfaceDecl *D) { 21 if (!D) 22 return false; 23 while (const ObjCInterfaceDecl *SuperD = D->getSuperClass()) { 24 if (SuperD->getName() == "XCTestCase") 25 return true; 26 D = SuperD; 27 } 28 return false; 29 } 30 31 /// \returns true if \c D is in a subclass of 'XCTestCase', returns void, has 32 /// no parameters, and its name starts with 'test'. 33 static bool isUnitTest(const ObjCMethodDecl *D) { 34 if (!D->parameters().empty()) 35 return false; 36 if (!D->getReturnType()->isVoidType()) 37 return false; 38 if (!D->getSelector().getNameForSlot(0).startswith("test")) 39 return false; 40 return isUnitTestCase(D->getClassInterface()); 41 } 42 43 static void checkForIBOutlets(const Decl *D, SymbolPropertySet &PropSet) { 44 if (D->hasAttr<IBOutletAttr>()) { 45 PropSet |= (SymbolPropertySet)SymbolProperty::IBAnnotated; 46 } else if (D->hasAttr<IBOutletCollectionAttr>()) { 47 PropSet |= (SymbolPropertySet)SymbolProperty::IBAnnotated; 48 PropSet |= (SymbolPropertySet)SymbolProperty::IBOutletCollection; 49 } 50 } 51 52 bool index::isFunctionLocalSymbol(const Decl *D) { 53 assert(D); 54 55 if (isa<ParmVarDecl>(D)) 56 return true; 57 58 if (isa<ObjCTypeParamDecl>(D)) 59 return true; 60 61 if (isa<UsingDirectiveDecl>(D)) 62 return false; 63 if (!D->getParentFunctionOrMethod()) 64 return false; 65 66 if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) { 67 switch (ND->getFormalLinkage()) { 68 case NoLinkage: 69 case InternalLinkage: 70 return true; 71 case VisibleNoLinkage: 72 case UniqueExternalLinkage: 73 case ModuleInternalLinkage: 74 llvm_unreachable("Not a sema linkage"); 75 case ModuleLinkage: 76 case ExternalLinkage: 77 return false; 78 } 79 } 80 81 return true; 82 } 83 84 SymbolInfo index::getSymbolInfo(const Decl *D) { 85 assert(D); 86 SymbolInfo Info; 87 Info.Kind = SymbolKind::Unknown; 88 Info.SubKind = SymbolSubKind::None; 89 Info.Properties = SymbolPropertySet(); 90 Info.Lang = SymbolLanguage::C; 91 92 if (isFunctionLocalSymbol(D)) { 93 Info.Properties |= (SymbolPropertySet)SymbolProperty::Local; 94 } 95 if (isa<ObjCProtocolDecl>(D->getDeclContext())) { 96 Info.Properties |= (SymbolPropertySet)SymbolProperty::ProtocolInterface; 97 } 98 99 if (const TagDecl *TD = dyn_cast<TagDecl>(D)) { 100 switch (TD->getTagKind()) { 101 case TTK_Struct: 102 Info.Kind = SymbolKind::Struct; break; 103 case TTK_Union: 104 Info.Kind = SymbolKind::Union; break; 105 case TTK_Class: 106 Info.Kind = SymbolKind::Class; 107 Info.Lang = SymbolLanguage::CXX; 108 break; 109 case TTK_Interface: 110 Info.Kind = SymbolKind::Protocol; 111 Info.Lang = SymbolLanguage::CXX; 112 break; 113 case TTK_Enum: 114 Info.Kind = SymbolKind::Enum; break; 115 } 116 117 if (const CXXRecordDecl *CXXRec = dyn_cast<CXXRecordDecl>(D)) { 118 if (!CXXRec->isCLike()) { 119 Info.Lang = SymbolLanguage::CXX; 120 if (CXXRec->getDescribedClassTemplate()) { 121 Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; 122 } 123 } 124 } 125 126 if (isa<ClassTemplatePartialSpecializationDecl>(D)) { 127 Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; 128 Info.Properties |= 129 (SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization; 130 } else if (isa<ClassTemplateSpecializationDecl>(D)) { 131 Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; 132 Info.Properties |= 133 (SymbolPropertySet)SymbolProperty::TemplateSpecialization; 134 } 135 136 } else if (auto *VD = dyn_cast<VarDecl>(D)) { 137 Info.Kind = SymbolKind::Variable; 138 if (isa<ParmVarDecl>(D)) { 139 Info.Kind = SymbolKind::Parameter; 140 } else if (isa<CXXRecordDecl>(D->getDeclContext())) { 141 Info.Kind = SymbolKind::StaticProperty; 142 Info.Lang = SymbolLanguage::CXX; 143 } 144 145 if (isa<VarTemplatePartialSpecializationDecl>(D)) { 146 Info.Lang = SymbolLanguage::CXX; 147 Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; 148 Info.Properties |= 149 (SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization; 150 } else if (isa<VarTemplateSpecializationDecl>(D)) { 151 Info.Lang = SymbolLanguage::CXX; 152 Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; 153 Info.Properties |= 154 (SymbolPropertySet)SymbolProperty::TemplateSpecialization; 155 } else if (VD->getDescribedVarTemplate()) { 156 Info.Lang = SymbolLanguage::CXX; 157 Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; 158 } 159 160 } else { 161 switch (D->getKind()) { 162 case Decl::Import: 163 Info.Kind = SymbolKind::Module; 164 break; 165 case Decl::Typedef: 166 Info.Kind = SymbolKind::TypeAlias; break; // Lang = C 167 case Decl::Function: 168 Info.Kind = SymbolKind::Function; 169 break; 170 case Decl::Field: 171 Info.Kind = SymbolKind::Field; 172 if (const CXXRecordDecl * 173 CXXRec = dyn_cast<CXXRecordDecl>(D->getDeclContext())) { 174 if (!CXXRec->isCLike()) 175 Info.Lang = SymbolLanguage::CXX; 176 } 177 break; 178 case Decl::EnumConstant: 179 Info.Kind = SymbolKind::EnumConstant; break; 180 case Decl::ObjCInterface: 181 case Decl::ObjCImplementation: { 182 Info.Kind = SymbolKind::Class; 183 Info.Lang = SymbolLanguage::ObjC; 184 const ObjCInterfaceDecl *ClsD = dyn_cast<ObjCInterfaceDecl>(D); 185 if (!ClsD) 186 ClsD = cast<ObjCImplementationDecl>(D)->getClassInterface(); 187 if (isUnitTestCase(ClsD)) 188 Info.Properties |= (SymbolPropertySet)SymbolProperty::UnitTest; 189 break; 190 } 191 case Decl::ObjCProtocol: 192 Info.Kind = SymbolKind::Protocol; 193 Info.Lang = SymbolLanguage::ObjC; 194 break; 195 case Decl::ObjCCategory: 196 case Decl::ObjCCategoryImpl: { 197 Info.Kind = SymbolKind::Extension; 198 Info.Lang = SymbolLanguage::ObjC; 199 const ObjCInterfaceDecl *ClsD = nullptr; 200 if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D)) 201 ClsD = CatD->getClassInterface(); 202 else 203 ClsD = cast<ObjCCategoryImplDecl>(D)->getClassInterface(); 204 if (isUnitTestCase(ClsD)) 205 Info.Properties |= (SymbolPropertySet)SymbolProperty::UnitTest; 206 break; 207 } 208 case Decl::ObjCMethod: { 209 const ObjCMethodDecl *MD = cast<ObjCMethodDecl>(D); 210 Info.Kind = MD->isInstanceMethod() ? SymbolKind::InstanceMethod : SymbolKind::ClassMethod; 211 if (MD->isPropertyAccessor()) { 212 if (MD->param_size()) 213 Info.SubKind = SymbolSubKind::AccessorSetter; 214 else 215 Info.SubKind = SymbolSubKind::AccessorGetter; 216 } 217 Info.Lang = SymbolLanguage::ObjC; 218 if (isUnitTest(MD)) 219 Info.Properties |= (SymbolPropertySet)SymbolProperty::UnitTest; 220 if (D->hasAttr<IBActionAttr>()) 221 Info.Properties |= (SymbolPropertySet)SymbolProperty::IBAnnotated; 222 break; 223 } 224 case Decl::ObjCProperty: 225 Info.Kind = SymbolKind::InstanceProperty; 226 Info.Lang = SymbolLanguage::ObjC; 227 checkForIBOutlets(D, Info.Properties); 228 if (auto *Annot = D->getAttr<AnnotateAttr>()) { 229 if (Annot->getAnnotation() == "gk_inspectable") 230 Info.Properties |= (SymbolPropertySet)SymbolProperty::GKInspectable; 231 } 232 break; 233 case Decl::ObjCIvar: 234 Info.Kind = SymbolKind::Field; 235 Info.Lang = SymbolLanguage::ObjC; 236 checkForIBOutlets(D, Info.Properties); 237 break; 238 case Decl::Namespace: 239 Info.Kind = SymbolKind::Namespace; 240 Info.Lang = SymbolLanguage::CXX; 241 break; 242 case Decl::NamespaceAlias: 243 Info.Kind = SymbolKind::NamespaceAlias; 244 Info.Lang = SymbolLanguage::CXX; 245 break; 246 case Decl::CXXConstructor: { 247 Info.Kind = SymbolKind::Constructor; 248 Info.Lang = SymbolLanguage::CXX; 249 auto *CD = cast<CXXConstructorDecl>(D); 250 if (CD->isCopyConstructor()) 251 Info.SubKind = SymbolSubKind::CXXCopyConstructor; 252 else if (CD->isMoveConstructor()) 253 Info.SubKind = SymbolSubKind::CXXMoveConstructor; 254 break; 255 } 256 case Decl::CXXDestructor: 257 Info.Kind = SymbolKind::Destructor; 258 Info.Lang = SymbolLanguage::CXX; 259 break; 260 case Decl::CXXConversion: 261 Info.Kind = SymbolKind::ConversionFunction; 262 Info.Lang = SymbolLanguage::CXX; 263 break; 264 case Decl::CXXMethod: { 265 const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); 266 if (MD->isStatic()) 267 Info.Kind = SymbolKind::StaticMethod; 268 else 269 Info.Kind = SymbolKind::InstanceMethod; 270 Info.Lang = SymbolLanguage::CXX; 271 break; 272 } 273 case Decl::ClassTemplate: 274 Info.Kind = SymbolKind::Class; 275 Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; 276 Info.Lang = SymbolLanguage::CXX; 277 break; 278 case Decl::FunctionTemplate: 279 Info.Kind = SymbolKind::Function; 280 Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; 281 Info.Lang = SymbolLanguage::CXX; 282 if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>( 283 cast<FunctionTemplateDecl>(D)->getTemplatedDecl())) { 284 if (isa<CXXConstructorDecl>(MD)) 285 Info.Kind = SymbolKind::Constructor; 286 else if (isa<CXXDestructorDecl>(MD)) 287 Info.Kind = SymbolKind::Destructor; 288 else if (isa<CXXConversionDecl>(MD)) 289 Info.Kind = SymbolKind::ConversionFunction; 290 else { 291 if (MD->isStatic()) 292 Info.Kind = SymbolKind::StaticMethod; 293 else 294 Info.Kind = SymbolKind::InstanceMethod; 295 } 296 } 297 break; 298 case Decl::TypeAliasTemplate: 299 Info.Kind = SymbolKind::TypeAlias; 300 Info.Lang = SymbolLanguage::CXX; 301 Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; 302 break; 303 case Decl::TypeAlias: 304 Info.Kind = SymbolKind::TypeAlias; 305 Info.Lang = SymbolLanguage::CXX; 306 break; 307 case Decl::UnresolvedUsingTypename: 308 Info.Kind = SymbolKind::Using; 309 Info.SubKind = SymbolSubKind::UsingTypename; 310 Info.Lang = SymbolLanguage::CXX; 311 Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; 312 break; 313 case Decl::UnresolvedUsingValue: 314 Info.Kind = SymbolKind::Using; 315 Info.SubKind = SymbolSubKind::UsingValue; 316 Info.Lang = SymbolLanguage::CXX; 317 Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; 318 break; 319 case Decl::Using: 320 Info.Kind = SymbolKind::Using; 321 Info.Lang = SymbolLanguage::CXX; 322 break; 323 case Decl::Binding: 324 Info.Kind = SymbolKind::Variable; 325 Info.Lang = SymbolLanguage::CXX; 326 break; 327 case Decl::MSProperty: 328 Info.Kind = SymbolKind::InstanceProperty; 329 if (const CXXRecordDecl *CXXRec = 330 dyn_cast<CXXRecordDecl>(D->getDeclContext())) { 331 if (!CXXRec->isCLike()) 332 Info.Lang = SymbolLanguage::CXX; 333 } 334 break; 335 default: 336 break; 337 } 338 } 339 340 if (Info.Kind == SymbolKind::Unknown) 341 return Info; 342 343 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { 344 if (FD->getTemplatedKind() == 345 FunctionDecl::TK_FunctionTemplateSpecialization) { 346 Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic; 347 Info.Properties |= 348 (SymbolPropertySet)SymbolProperty::TemplateSpecialization; 349 } 350 } 351 352 if (Info.Properties & (SymbolPropertySet)SymbolProperty::Generic) 353 Info.Lang = SymbolLanguage::CXX; 354 355 if (auto *attr = D->getExternalSourceSymbolAttr()) { 356 if (attr->getLanguage() == "Swift") 357 Info.Lang = SymbolLanguage::Swift; 358 } 359 360 return Info; 361 } 362 363 SymbolInfo index::getSymbolInfoForMacro(const MacroInfo &) { 364 SymbolInfo Info; 365 Info.Kind = SymbolKind::Macro; 366 Info.SubKind = SymbolSubKind::None; 367 Info.Properties = SymbolPropertySet(); 368 Info.Lang = SymbolLanguage::C; 369 return Info; 370 } 371 372 bool index::applyForEachSymbolRoleInterruptible(SymbolRoleSet Roles, 373 llvm::function_ref<bool(SymbolRole)> Fn) { 374 #define APPLY_FOR_ROLE(Role) \ 375 if (Roles & (unsigned)SymbolRole::Role) \ 376 if (!Fn(SymbolRole::Role)) \ 377 return false; 378 379 APPLY_FOR_ROLE(Declaration); 380 APPLY_FOR_ROLE(Definition); 381 APPLY_FOR_ROLE(Reference); 382 APPLY_FOR_ROLE(Read); 383 APPLY_FOR_ROLE(Write); 384 APPLY_FOR_ROLE(Call); 385 APPLY_FOR_ROLE(Dynamic); 386 APPLY_FOR_ROLE(AddressOf); 387 APPLY_FOR_ROLE(Implicit); 388 APPLY_FOR_ROLE(Undefinition); 389 APPLY_FOR_ROLE(RelationChildOf); 390 APPLY_FOR_ROLE(RelationBaseOf); 391 APPLY_FOR_ROLE(RelationOverrideOf); 392 APPLY_FOR_ROLE(RelationReceivedBy); 393 APPLY_FOR_ROLE(RelationCalledBy); 394 APPLY_FOR_ROLE(RelationExtendedBy); 395 APPLY_FOR_ROLE(RelationAccessorOf); 396 APPLY_FOR_ROLE(RelationContainedBy); 397 APPLY_FOR_ROLE(RelationIBTypeOf); 398 APPLY_FOR_ROLE(RelationSpecializationOf); 399 APPLY_FOR_ROLE(NameReference); 400 401 #undef APPLY_FOR_ROLE 402 403 return true; 404 } 405 406 void index::applyForEachSymbolRole(SymbolRoleSet Roles, 407 llvm::function_ref<void(SymbolRole)> Fn) { 408 applyForEachSymbolRoleInterruptible(Roles, [&](SymbolRole r) -> bool { 409 Fn(r); 410 return true; 411 }); 412 } 413 414 void index::printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS) { 415 bool VisitedOnce = false; 416 applyForEachSymbolRole(Roles, [&](SymbolRole Role) { 417 if (VisitedOnce) 418 OS << ','; 419 else 420 VisitedOnce = true; 421 switch (Role) { 422 case SymbolRole::Declaration: OS << "Decl"; break; 423 case SymbolRole::Definition: OS << "Def"; break; 424 case SymbolRole::Reference: OS << "Ref"; break; 425 case SymbolRole::Read: OS << "Read"; break; 426 case SymbolRole::Write: OS << "Writ"; break; 427 case SymbolRole::Call: OS << "Call"; break; 428 case SymbolRole::Dynamic: OS << "Dyn"; break; 429 case SymbolRole::AddressOf: OS << "Addr"; break; 430 case SymbolRole::Implicit: OS << "Impl"; break; 431 case SymbolRole::Undefinition: OS << "Undef"; break; 432 case SymbolRole::RelationChildOf: OS << "RelChild"; break; 433 case SymbolRole::RelationBaseOf: OS << "RelBase"; break; 434 case SymbolRole::RelationOverrideOf: OS << "RelOver"; break; 435 case SymbolRole::RelationReceivedBy: OS << "RelRec"; break; 436 case SymbolRole::RelationCalledBy: OS << "RelCall"; break; 437 case SymbolRole::RelationExtendedBy: OS << "RelExt"; break; 438 case SymbolRole::RelationAccessorOf: OS << "RelAcc"; break; 439 case SymbolRole::RelationContainedBy: OS << "RelCont"; break; 440 case SymbolRole::RelationIBTypeOf: OS << "RelIBType"; break; 441 case SymbolRole::RelationSpecializationOf: OS << "RelSpecialization"; break; 442 case SymbolRole::NameReference: OS << "NameReference"; break; 443 } 444 }); 445 } 446 447 bool index::printSymbolName(const Decl *D, const LangOptions &LO, 448 raw_ostream &OS) { 449 if (auto *ND = dyn_cast<NamedDecl>(D)) { 450 PrintingPolicy Policy(LO); 451 // Forward references can have different template argument names. Suppress 452 // the template argument names in constructors to make their name more 453 // stable. 454 Policy.SuppressTemplateArgsInCXXConstructors = true; 455 DeclarationName DeclName = ND->getDeclName(); 456 if (DeclName.isEmpty()) 457 return true; 458 DeclName.print(OS, Policy); 459 return false; 460 } else { 461 return true; 462 } 463 } 464 465 StringRef index::getSymbolKindString(SymbolKind K) { 466 switch (K) { 467 case SymbolKind::Unknown: return "<unknown>"; 468 case SymbolKind::Module: return "module"; 469 case SymbolKind::Namespace: return "namespace"; 470 case SymbolKind::NamespaceAlias: return "namespace-alias"; 471 case SymbolKind::Macro: return "macro"; 472 case SymbolKind::Enum: return "enum"; 473 case SymbolKind::Struct: return "struct"; 474 case SymbolKind::Class: return "class"; 475 case SymbolKind::Protocol: return "protocol"; 476 case SymbolKind::Extension: return "extension"; 477 case SymbolKind::Union: return "union"; 478 case SymbolKind::TypeAlias: return "type-alias"; 479 case SymbolKind::Function: return "function"; 480 case SymbolKind::Variable: return "variable"; 481 case SymbolKind::Field: return "field"; 482 case SymbolKind::EnumConstant: return "enumerator"; 483 case SymbolKind::InstanceMethod: return "instance-method"; 484 case SymbolKind::ClassMethod: return "class-method"; 485 case SymbolKind::StaticMethod: return "static-method"; 486 case SymbolKind::InstanceProperty: return "instance-property"; 487 case SymbolKind::ClassProperty: return "class-property"; 488 case SymbolKind::StaticProperty: return "static-property"; 489 case SymbolKind::Constructor: return "constructor"; 490 case SymbolKind::Destructor: return "destructor"; 491 case SymbolKind::ConversionFunction: return "coversion-func"; 492 case SymbolKind::Parameter: return "param"; 493 case SymbolKind::Using: return "using"; 494 } 495 llvm_unreachable("invalid symbol kind"); 496 } 497 498 StringRef index::getSymbolSubKindString(SymbolSubKind K) { 499 switch (K) { 500 case SymbolSubKind::None: return "<none>"; 501 case SymbolSubKind::CXXCopyConstructor: return "cxx-copy-ctor"; 502 case SymbolSubKind::CXXMoveConstructor: return "cxx-move-ctor"; 503 case SymbolSubKind::AccessorGetter: return "acc-get"; 504 case SymbolSubKind::AccessorSetter: return "acc-set"; 505 case SymbolSubKind::UsingTypename: return "using-typename"; 506 case SymbolSubKind::UsingValue: return "using-value"; 507 } 508 llvm_unreachable("invalid symbol subkind"); 509 } 510 511 StringRef index::getSymbolLanguageString(SymbolLanguage K) { 512 switch (K) { 513 case SymbolLanguage::C: return "C"; 514 case SymbolLanguage::ObjC: return "ObjC"; 515 case SymbolLanguage::CXX: return "C++"; 516 case SymbolLanguage::Swift: return "Swift"; 517 } 518 llvm_unreachable("invalid symbol language kind"); 519 } 520 521 void index::applyForEachSymbolProperty(SymbolPropertySet Props, 522 llvm::function_ref<void(SymbolProperty)> Fn) { 523 #define APPLY_FOR_PROPERTY(K) \ 524 if (Props & (SymbolPropertySet)SymbolProperty::K) \ 525 Fn(SymbolProperty::K) 526 527 APPLY_FOR_PROPERTY(Generic); 528 APPLY_FOR_PROPERTY(TemplatePartialSpecialization); 529 APPLY_FOR_PROPERTY(TemplateSpecialization); 530 APPLY_FOR_PROPERTY(UnitTest); 531 APPLY_FOR_PROPERTY(IBAnnotated); 532 APPLY_FOR_PROPERTY(IBOutletCollection); 533 APPLY_FOR_PROPERTY(GKInspectable); 534 APPLY_FOR_PROPERTY(Local); 535 APPLY_FOR_PROPERTY(ProtocolInterface); 536 537 #undef APPLY_FOR_PROPERTY 538 } 539 540 void index::printSymbolProperties(SymbolPropertySet Props, raw_ostream &OS) { 541 bool VisitedOnce = false; 542 applyForEachSymbolProperty(Props, [&](SymbolProperty Prop) { 543 if (VisitedOnce) 544 OS << ','; 545 else 546 VisitedOnce = true; 547 switch (Prop) { 548 case SymbolProperty::Generic: OS << "Gen"; break; 549 case SymbolProperty::TemplatePartialSpecialization: OS << "TPS"; break; 550 case SymbolProperty::TemplateSpecialization: OS << "TS"; break; 551 case SymbolProperty::UnitTest: OS << "test"; break; 552 case SymbolProperty::IBAnnotated: OS << "IB"; break; 553 case SymbolProperty::IBOutletCollection: OS << "IBColl"; break; 554 case SymbolProperty::GKInspectable: OS << "GKI"; break; 555 case SymbolProperty::Local: OS << "local"; break; 556 case SymbolProperty::ProtocolInterface: OS << "protocol"; break; 557 } 558 }); 559 } 560