Files
RedBear-OS/local/recipes/dev/libclc/source/clang/lib/Index/IndexSymbol.cpp
T
vasilito cb424d7448 build: static patch-sanity linter (shift-left the malformed-patch class)
verify-patch-sanity.py validates every active recipe .patch has internally-
consistent hunk line counts — catching the 'malformed patch at line N' failure
at commit/CI/preflight time instead of hours into a cook. This cycle hit that
class three times (qtwaylandscanner, sddm, xwayland), each only discovered when
cookbook tried to apply the patch.

Running it across the repo found 29 latent malformed patches (validated against
GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22').
They were harmless only because they sit in vendored recipes (baked, not re-
applied) — but would fail on any version-bump re-derivation. --fix recounts the
hunk headers (body untouched) and repaired all 29.

Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test
(test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats
(empty placeholders, bare-@@ git hunks).
2026-08-01 05:13:02 +03:00

609 lines
21 KiB
C++

//===--- IndexSymbol.cpp - Types and functions for indexing symbols -------===//
//
// 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/Index/IndexSymbol.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/Lex/MacroInfo.h"
using namespace clang;
using namespace clang::index;
/// \returns true if \c D is a subclass of 'XCTestCase'.
static bool isUnitTestCase(const ObjCInterfaceDecl *D) {
if (!D)
return false;
while (const ObjCInterfaceDecl *SuperD = D->getSuperClass()) {
if (SuperD->getName() == "XCTestCase")
return true;
D = SuperD;
}
return false;
}
/// \returns true if \c D is in a subclass of 'XCTestCase', returns void, has
/// no parameters, and its name starts with 'test'.
static bool isUnitTest(const ObjCMethodDecl *D) {
if (!D->parameters().empty())
return false;
if (!D->getReturnType()->isVoidType())
return false;
if (!D->getSelector().getNameForSlot(0).starts_with("test"))
return false;
return isUnitTestCase(D->getClassInterface());
}
static void checkForIBOutlets(const Decl *D, SymbolPropertySet &PropSet) {
if (D->hasAttr<IBOutletAttr>()) {
PropSet |= (SymbolPropertySet)SymbolProperty::IBAnnotated;
} else if (D->hasAttr<IBOutletCollectionAttr>()) {
PropSet |= (SymbolPropertySet)SymbolProperty::IBAnnotated;
PropSet |= (SymbolPropertySet)SymbolProperty::IBOutletCollection;
}
}
bool index::isFunctionLocalSymbol(const Decl *D) {
assert(D);
if (isa<ParmVarDecl>(D))
return true;
if (isa<ObjCTypeParamDecl>(D))
return true;
if (isa<UsingDirectiveDecl>(D))
return false;
if (!D->getParentFunctionOrMethod())
return false;
if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) {
switch (ND->getFormalLinkage()) {
case Linkage::Invalid:
llvm_unreachable("Linkage hasn't been computed!");
case Linkage::None:
case Linkage::Internal:
return true;
case Linkage::VisibleNone:
case Linkage::UniqueExternal:
llvm_unreachable("Not a sema linkage");
case Linkage::Module:
case Linkage::External:
return false;
}
}
return true;
}
SymbolInfo index::getSymbolInfo(const Decl *D) {
assert(D);
SymbolInfo Info;
Info.Kind = SymbolKind::Unknown;
Info.SubKind = SymbolSubKind::None;
Info.Properties = SymbolPropertySet();
Info.Lang = SymbolLanguage::C;
if (isFunctionLocalSymbol(D)) {
Info.Properties |= (SymbolPropertySet)SymbolProperty::Local;
}
if (isa<ObjCProtocolDecl>(D->getDeclContext())) {
Info.Properties |= (SymbolPropertySet)SymbolProperty::ProtocolInterface;
}
if (auto *VT = dyn_cast<VarTemplateDecl>(D)) {
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
Info.Lang = SymbolLanguage::CXX;
// All other fields are filled from the templated decl.
D = VT->getTemplatedDecl();
}
if (const TagDecl *TD = dyn_cast<TagDecl>(D)) {
switch (TD->getTagKind()) {
case TagTypeKind::Struct:
Info.Kind = SymbolKind::Struct; break;
case TagTypeKind::Union:
Info.Kind = SymbolKind::Union; break;
case TagTypeKind::Class:
Info.Kind = SymbolKind::Class;
Info.Lang = SymbolLanguage::CXX;
break;
case TagTypeKind::Interface:
Info.Kind = SymbolKind::Protocol;
Info.Lang = SymbolLanguage::CXX;
break;
case TagTypeKind::Enum:
Info.Kind = SymbolKind::Enum; break;
}
if (const CXXRecordDecl *CXXRec = dyn_cast<CXXRecordDecl>(D)) {
if (!CXXRec->isCLike()) {
Info.Lang = SymbolLanguage::CXX;
if (CXXRec->getDescribedClassTemplate()) {
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
}
}
}
if (isa<ClassTemplatePartialSpecializationDecl>(D)) {
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
Info.Properties |=
(SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization;
} else if (isa<ClassTemplateSpecializationDecl>(D)) {
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
Info.Properties |=
(SymbolPropertySet)SymbolProperty::TemplateSpecialization;
}
} else if (auto *VD = dyn_cast<VarDecl>(D)) {
Info.Kind = SymbolKind::Variable;
if (isa<ParmVarDecl>(D)) {
Info.Kind = SymbolKind::Parameter;
} else if (isa<CXXRecordDecl>(D->getDeclContext())) {
Info.Kind = SymbolKind::StaticProperty;
Info.Lang = SymbolLanguage::CXX;
}
if (isa<VarTemplatePartialSpecializationDecl>(D)) {
Info.Lang = SymbolLanguage::CXX;
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
Info.Properties |=
(SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization;
} else if (isa<VarTemplateSpecializationDecl>(D)) {
Info.Lang = SymbolLanguage::CXX;
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
Info.Properties |=
(SymbolPropertySet)SymbolProperty::TemplateSpecialization;
} else if (VD->getDescribedVarTemplate()) {
Info.Lang = SymbolLanguage::CXX;
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
}
} else {
switch (D->getKind()) {
case Decl::Import:
Info.Kind = SymbolKind::Module;
break;
case Decl::Typedef:
Info.Kind = SymbolKind::TypeAlias; break; // Lang = C
case Decl::Function:
Info.Kind = SymbolKind::Function;
break;
case Decl::Field:
case Decl::IndirectField:
Info.Kind = SymbolKind::Field;
if (const CXXRecordDecl *
CXXRec = dyn_cast<CXXRecordDecl>(D->getDeclContext())) {
if (!CXXRec->isCLike())
Info.Lang = SymbolLanguage::CXX;
}
break;
case Decl::EnumConstant:
Info.Kind = SymbolKind::EnumConstant; break;
case Decl::ObjCInterface:
case Decl::ObjCImplementation: {
Info.Kind = SymbolKind::Class;
Info.Lang = SymbolLanguage::ObjC;
const ObjCInterfaceDecl *ClsD = dyn_cast<ObjCInterfaceDecl>(D);
if (!ClsD)
ClsD = cast<ObjCImplementationDecl>(D)->getClassInterface();
if (isUnitTestCase(ClsD))
Info.Properties |= (SymbolPropertySet)SymbolProperty::UnitTest;
break;
}
case Decl::ObjCProtocol:
Info.Kind = SymbolKind::Protocol;
Info.Lang = SymbolLanguage::ObjC;
break;
case Decl::ObjCCategory:
case Decl::ObjCCategoryImpl: {
Info.Kind = SymbolKind::Extension;
Info.Lang = SymbolLanguage::ObjC;
const ObjCInterfaceDecl *ClsD = nullptr;
if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D))
ClsD = CatD->getClassInterface();
else
ClsD = cast<ObjCCategoryImplDecl>(D)->getClassInterface();
if (isUnitTestCase(ClsD))
Info.Properties |= (SymbolPropertySet)SymbolProperty::UnitTest;
break;
}
case Decl::ObjCMethod: {
const ObjCMethodDecl *MD = cast<ObjCMethodDecl>(D);
Info.Kind = MD->isInstanceMethod() ? SymbolKind::InstanceMethod : SymbolKind::ClassMethod;
if (MD->isPropertyAccessor()) {
if (MD->param_size())
Info.SubKind = SymbolSubKind::AccessorSetter;
else
Info.SubKind = SymbolSubKind::AccessorGetter;
}
Info.Lang = SymbolLanguage::ObjC;
if (isUnitTest(MD))
Info.Properties |= (SymbolPropertySet)SymbolProperty::UnitTest;
if (D->hasAttr<IBActionAttr>())
Info.Properties |= (SymbolPropertySet)SymbolProperty::IBAnnotated;
break;
}
case Decl::ObjCProperty:
Info.Kind = SymbolKind::InstanceProperty;
Info.Lang = SymbolLanguage::ObjC;
checkForIBOutlets(D, Info.Properties);
if (auto *Annot = D->getAttr<AnnotateAttr>()) {
if (Annot->getAnnotation() == "gk_inspectable")
Info.Properties |= (SymbolPropertySet)SymbolProperty::GKInspectable;
}
break;
case Decl::ObjCIvar:
Info.Kind = SymbolKind::Field;
Info.Lang = SymbolLanguage::ObjC;
checkForIBOutlets(D, Info.Properties);
break;
case Decl::Namespace:
Info.Kind = SymbolKind::Namespace;
Info.Lang = SymbolLanguage::CXX;
break;
case Decl::NamespaceAlias:
Info.Kind = SymbolKind::NamespaceAlias;
Info.Lang = SymbolLanguage::CXX;
break;
case Decl::CXXConstructor: {
Info.Kind = SymbolKind::Constructor;
Info.Lang = SymbolLanguage::CXX;
auto *CD = cast<CXXConstructorDecl>(D);
if (CD->isCopyConstructor())
Info.SubKind = SymbolSubKind::CXXCopyConstructor;
else if (CD->isMoveConstructor())
Info.SubKind = SymbolSubKind::CXXMoveConstructor;
break;
}
case Decl::CXXDestructor:
Info.Kind = SymbolKind::Destructor;
Info.Lang = SymbolLanguage::CXX;
break;
case Decl::CXXConversion:
Info.Kind = SymbolKind::ConversionFunction;
Info.Lang = SymbolLanguage::CXX;
break;
case Decl::CXXMethod: {
const CXXMethodDecl *MD = cast<CXXMethodDecl>(D);
if (MD->isStatic())
Info.Kind = SymbolKind::StaticMethod;
else
Info.Kind = SymbolKind::InstanceMethod;
Info.Lang = SymbolLanguage::CXX;
break;
}
case Decl::ClassTemplate:
Info.Kind = SymbolKind::Class;
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
Info.Lang = SymbolLanguage::CXX;
break;
case Decl::FunctionTemplate:
Info.Kind = SymbolKind::Function;
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
Info.Lang = SymbolLanguage::CXX;
if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(
cast<FunctionTemplateDecl>(D)->getTemplatedDecl())) {
if (isa<CXXConstructorDecl>(MD))
Info.Kind = SymbolKind::Constructor;
else if (isa<CXXDestructorDecl>(MD))
Info.Kind = SymbolKind::Destructor;
else if (isa<CXXConversionDecl>(MD))
Info.Kind = SymbolKind::ConversionFunction;
else {
if (MD->isStatic())
Info.Kind = SymbolKind::StaticMethod;
else
Info.Kind = SymbolKind::InstanceMethod;
}
}
break;
case Decl::TypeAliasTemplate:
Info.Kind = SymbolKind::TypeAlias;
Info.Lang = SymbolLanguage::CXX;
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
break;
case Decl::TypeAlias:
Info.Kind = SymbolKind::TypeAlias;
Info.Lang = SymbolLanguage::CXX;
break;
case Decl::UnresolvedUsingTypename:
Info.Kind = SymbolKind::Using;
Info.SubKind = SymbolSubKind::UsingTypename;
Info.Lang = SymbolLanguage::CXX;
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
break;
case Decl::UnresolvedUsingValue:
Info.Kind = SymbolKind::Using;
Info.SubKind = SymbolSubKind::UsingValue;
Info.Lang = SymbolLanguage::CXX;
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
break;
case Decl::Using:
Info.Kind = SymbolKind::Using;
Info.Lang = SymbolLanguage::CXX;
break;
case Decl::UsingEnum:
Info.Kind = SymbolKind::Using;
Info.Lang = SymbolLanguage::CXX;
Info.SubKind = SymbolSubKind::UsingEnum;
break;
case Decl::Binding:
Info.Kind = SymbolKind::Variable;
Info.Lang = SymbolLanguage::CXX;
break;
case Decl::MSProperty:
Info.Kind = SymbolKind::InstanceProperty;
if (const CXXRecordDecl *CXXRec =
dyn_cast<CXXRecordDecl>(D->getDeclContext())) {
if (!CXXRec->isCLike())
Info.Lang = SymbolLanguage::CXX;
}
break;
case Decl::ClassTemplatePartialSpecialization:
case Decl::ClassTemplateSpecialization:
case Decl::CXXRecord:
case Decl::Enum:
case Decl::Record:
llvm_unreachable("records handled before");
break;
case Decl::VarTemplateSpecialization:
case Decl::VarTemplatePartialSpecialization:
case Decl::ImplicitParam:
case Decl::ParmVar:
case Decl::Var:
case Decl::VarTemplate:
llvm_unreachable("variables handled before");
break;
case Decl::TemplateTypeParm:
Info.Kind = SymbolKind::TemplateTypeParm;
break;
case Decl::TemplateTemplateParm:
Info.Kind = SymbolKind::TemplateTemplateParm;
break;
case Decl::NonTypeTemplateParm:
Info.Kind = SymbolKind::NonTypeTemplateParm;
break;
case Decl::Concept:
Info.Kind = SymbolKind::Concept;
break;
// Other decls get the 'unknown' kind.
default:
break;
}
}
if (Info.Kind == SymbolKind::Unknown)
return Info;
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
if (FD->getTemplatedKind() ==
FunctionDecl::TK_FunctionTemplateSpecialization) {
Info.Properties |= (SymbolPropertySet)SymbolProperty::Generic;
Info.Properties |=
(SymbolPropertySet)SymbolProperty::TemplateSpecialization;
}
}
if (Info.Properties & (SymbolPropertySet)SymbolProperty::Generic)
Info.Lang = SymbolLanguage::CXX;
if (auto *attr = D->getExternalSourceSymbolAttr()) {
if (attr->getLanguage() == "Swift")
Info.Lang = SymbolLanguage::Swift;
}
return Info;
}
SymbolInfo index::getSymbolInfoForMacro(const MacroInfo &) {
SymbolInfo Info;
Info.Kind = SymbolKind::Macro;
Info.SubKind = SymbolSubKind::None;
Info.Properties = SymbolPropertySet();
Info.Lang = SymbolLanguage::C;
return Info;
}
bool index::applyForEachSymbolRoleInterruptible(SymbolRoleSet Roles,
llvm::function_ref<bool(SymbolRole)> Fn) {
#define APPLY_FOR_ROLE(Role) \
if (Roles & (unsigned)SymbolRole::Role) \
if (!Fn(SymbolRole::Role)) \
return false;
APPLY_FOR_ROLE(Declaration);
APPLY_FOR_ROLE(Definition);
APPLY_FOR_ROLE(Reference);
APPLY_FOR_ROLE(Read);
APPLY_FOR_ROLE(Write);
APPLY_FOR_ROLE(Call);
APPLY_FOR_ROLE(Dynamic);
APPLY_FOR_ROLE(AddressOf);
APPLY_FOR_ROLE(Implicit);
APPLY_FOR_ROLE(Undefinition);
APPLY_FOR_ROLE(RelationChildOf);
APPLY_FOR_ROLE(RelationBaseOf);
APPLY_FOR_ROLE(RelationOverrideOf);
APPLY_FOR_ROLE(RelationReceivedBy);
APPLY_FOR_ROLE(RelationCalledBy);
APPLY_FOR_ROLE(RelationExtendedBy);
APPLY_FOR_ROLE(RelationAccessorOf);
APPLY_FOR_ROLE(RelationContainedBy);
APPLY_FOR_ROLE(RelationIBTypeOf);
APPLY_FOR_ROLE(RelationSpecializationOf);
APPLY_FOR_ROLE(NameReference);
#undef APPLY_FOR_ROLE
return true;
}
void index::applyForEachSymbolRole(SymbolRoleSet Roles,
llvm::function_ref<void(SymbolRole)> Fn) {
applyForEachSymbolRoleInterruptible(Roles, [&](SymbolRole r) -> bool {
Fn(r);
return true;
});
}
void index::printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS) {
bool VisitedOnce = false;
applyForEachSymbolRole(Roles, [&](SymbolRole Role) {
if (VisitedOnce)
OS << ',';
else
VisitedOnce = true;
switch (Role) {
case SymbolRole::Declaration: OS << "Decl"; break;
case SymbolRole::Definition: OS << "Def"; break;
case SymbolRole::Reference: OS << "Ref"; break;
case SymbolRole::Read: OS << "Read"; break;
case SymbolRole::Write: OS << "Writ"; break;
case SymbolRole::Call: OS << "Call"; break;
case SymbolRole::Dynamic: OS << "Dyn"; break;
case SymbolRole::AddressOf: OS << "Addr"; break;
case SymbolRole::Implicit: OS << "Impl"; break;
case SymbolRole::Undefinition: OS << "Undef"; break;
case SymbolRole::RelationChildOf: OS << "RelChild"; break;
case SymbolRole::RelationBaseOf: OS << "RelBase"; break;
case SymbolRole::RelationOverrideOf: OS << "RelOver"; break;
case SymbolRole::RelationReceivedBy: OS << "RelRec"; break;
case SymbolRole::RelationCalledBy: OS << "RelCall"; break;
case SymbolRole::RelationExtendedBy: OS << "RelExt"; break;
case SymbolRole::RelationAccessorOf: OS << "RelAcc"; break;
case SymbolRole::RelationContainedBy: OS << "RelCont"; break;
case SymbolRole::RelationIBTypeOf: OS << "RelIBType"; break;
case SymbolRole::RelationSpecializationOf: OS << "RelSpecialization"; break;
case SymbolRole::NameReference: OS << "NameReference"; break;
}
});
}
bool index::printSymbolName(const Decl *D, const LangOptions &LO,
raw_ostream &OS) {
if (auto *ND = dyn_cast<NamedDecl>(D)) {
PrintingPolicy Policy(LO);
// Forward references can have different template argument names. Suppress
// the template argument names in constructors to make their name more
// stable.
Policy.SuppressTemplateArgsInCXXConstructors = true;
DeclarationName DeclName = ND->getDeclName();
if (DeclName.isEmpty())
return true;
DeclName.print(OS, Policy);
return false;
} else {
return true;
}
}
StringRef index::getSymbolKindString(SymbolKind K) {
switch (K) {
case SymbolKind::Unknown: return "<unknown>";
case SymbolKind::Module: return "module";
case SymbolKind::Namespace: return "namespace";
case SymbolKind::NamespaceAlias: return "namespace-alias";
case SymbolKind::Macro: return "macro";
case SymbolKind::Enum: return "enum";
case SymbolKind::Struct: return "struct";
case SymbolKind::Class: return "class";
case SymbolKind::Protocol: return "protocol";
case SymbolKind::Extension: return "extension";
case SymbolKind::Union: return "union";
case SymbolKind::TypeAlias: return "type-alias";
case SymbolKind::Function: return "function";
case SymbolKind::Variable: return "variable";
case SymbolKind::Field: return "field";
case SymbolKind::EnumConstant: return "enumerator";
case SymbolKind::InstanceMethod: return "instance-method";
case SymbolKind::ClassMethod: return "class-method";
case SymbolKind::StaticMethod: return "static-method";
case SymbolKind::InstanceProperty: return "instance-property";
case SymbolKind::ClassProperty: return "class-property";
case SymbolKind::StaticProperty: return "static-property";
case SymbolKind::Constructor: return "constructor";
case SymbolKind::Destructor: return "destructor";
case SymbolKind::ConversionFunction: return "conversion-func";
case SymbolKind::Parameter: return "param";
case SymbolKind::Using: return "using";
case SymbolKind::TemplateTypeParm: return "template-type-param";
case SymbolKind::TemplateTemplateParm: return "template-template-param";
case SymbolKind::NonTypeTemplateParm: return "non-type-template-param";
case SymbolKind::Concept:
return "concept";
}
llvm_unreachable("invalid symbol kind");
}
StringRef index::getSymbolSubKindString(SymbolSubKind K) {
switch (K) {
case SymbolSubKind::None: return "<none>";
case SymbolSubKind::CXXCopyConstructor: return "cxx-copy-ctor";
case SymbolSubKind::CXXMoveConstructor: return "cxx-move-ctor";
case SymbolSubKind::AccessorGetter: return "acc-get";
case SymbolSubKind::AccessorSetter: return "acc-set";
case SymbolSubKind::UsingTypename: return "using-typename";
case SymbolSubKind::UsingValue: return "using-value";
case SymbolSubKind::UsingEnum: return "using-enum";
}
llvm_unreachable("invalid symbol subkind");
}
StringRef index::getSymbolLanguageString(SymbolLanguage K) {
switch (K) {
case SymbolLanguage::C: return "C";
case SymbolLanguage::ObjC: return "ObjC";
case SymbolLanguage::CXX: return "C++";
case SymbolLanguage::Swift: return "Swift";
}
llvm_unreachable("invalid symbol language kind");
}
void index::applyForEachSymbolProperty(SymbolPropertySet Props,
llvm::function_ref<void(SymbolProperty)> Fn) {
#define APPLY_FOR_PROPERTY(K) \
if (Props & (SymbolPropertySet)SymbolProperty::K) \
Fn(SymbolProperty::K)
APPLY_FOR_PROPERTY(Generic);
APPLY_FOR_PROPERTY(TemplatePartialSpecialization);
APPLY_FOR_PROPERTY(TemplateSpecialization);
APPLY_FOR_PROPERTY(UnitTest);
APPLY_FOR_PROPERTY(IBAnnotated);
APPLY_FOR_PROPERTY(IBOutletCollection);
APPLY_FOR_PROPERTY(GKInspectable);
APPLY_FOR_PROPERTY(Local);
APPLY_FOR_PROPERTY(ProtocolInterface);
#undef APPLY_FOR_PROPERTY
}
void index::printSymbolProperties(SymbolPropertySet Props, raw_ostream &OS) {
bool VisitedOnce = false;
applyForEachSymbolProperty(Props, [&](SymbolProperty Prop) {
if (VisitedOnce)
OS << ',';
else
VisitedOnce = true;
switch (Prop) {
case SymbolProperty::Generic: OS << "Gen"; break;
case SymbolProperty::TemplatePartialSpecialization: OS << "TPS"; break;
case SymbolProperty::TemplateSpecialization: OS << "TS"; break;
case SymbolProperty::UnitTest: OS << "test"; break;
case SymbolProperty::IBAnnotated: OS << "IB"; break;
case SymbolProperty::IBOutletCollection: OS << "IBColl"; break;
case SymbolProperty::GKInspectable: OS << "GKI"; break;
case SymbolProperty::Local: OS << "local"; break;
case SymbolProperty::ProtocolInterface: OS << "protocol"; break;
}
});
}