| //===--- IndexSymbol.cpp --------------------------------------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "swift/Index/IndexSymbol.h" |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/Decl.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/AST/Types.h" |
| |
| using namespace swift; |
| using namespace swift::index; |
| |
| static NominalTypeDecl *getNominalParent(const ValueDecl *D) { |
| Type Ty = D->getDeclContext()->getDeclaredTypeOfContext(); |
| if (!Ty) |
| return nullptr; |
| return Ty->getAnyNominal(); |
| } |
| |
| /// \returns true if \c D is a subclass of 'XCTestCase'. |
| static bool isUnitTestCase(const ClassDecl *D) { |
| if (!D) |
| return false; |
| while (auto *SuperD = D->getSuperclassDecl()) { |
| if (SuperD->getNameStr() == "XCTestCase") |
| return true; |
| D = SuperD; |
| } |
| return false; |
| } |
| |
| static bool isUnitTest(const ValueDecl *D) { |
| if (!D->hasName()) |
| return false; |
| |
| // A 'test candidate' is: |
| // 1. An instance method... |
| auto FD = dyn_cast<FuncDecl>(D); |
| if (!FD) |
| return false; |
| if (!D->isInstanceMember()) |
| return false; |
| |
| // 2. ...on a class or extension (not a struct) subclass of XCTestCase... |
| auto parentNTD = getNominalParent(D); |
| if (!parentNTD) |
| return false; |
| if (!isa<ClassDecl>(parentNTD)) |
| return false; |
| if (!isUnitTestCase(cast<ClassDecl>(parentNTD))) |
| return false; |
| |
| // 3. ...that returns void... |
| Type RetTy = FD->getResultInterfaceType(); |
| if (RetTy && !RetTy->isVoid()) |
| return false; |
| |
| // 4. ...takes no parameters... |
| if (FD->getParameterLists().size() != 2) |
| return false; |
| if (FD->getParameterList(1)->size() != 0) |
| return false; |
| |
| // 5. ...is of at least 'internal' accessibility (unless we can use |
| // Objective-C reflection)... |
| if (!D->getASTContext().LangOpts.EnableObjCInterop && |
| (D->getFormalAccess() < Accessibility::Internal || |
| parentNTD->getFormalAccess() < Accessibility::Internal)) |
| return false; |
| |
| // 6. ...and starts with "test". |
| if (FD->getName().str().startswith("test")) |
| return true; |
| |
| return false; |
| } |
| |
| static void setFuncSymbolInfo(const FuncDecl *FD, SymbolInfo &sym) { |
| sym.Kind = SymbolKind::Function; |
| |
| if (FD->getAttrs().hasAttribute<IBActionAttr>()) |
| sym.Properties |= SymbolProperty::IBAnnotated; |
| |
| if (isUnitTest(FD)) |
| sym.Properties |= SymbolProperty::UnitTest; |
| |
| if (FD->getDeclContext()->isTypeContext()) { |
| if (FD->isStatic()) { |
| if (FD->getCorrectStaticSpelling() == StaticSpellingKind::KeywordClass) |
| sym.Kind = SymbolKind::ClassMethod; |
| else |
| sym.Kind = SymbolKind::StaticMethod; |
| } else { |
| sym.Kind = SymbolKind::InstanceMethod; |
| } |
| } |
| |
| if (FD->isAccessor()) { |
| sym.SubKind = getSubKindForAccessor(FD->getAccessorKind()); |
| return; |
| } |
| |
| if (auto *op = FD->getOperatorDecl()) { |
| switch (op->getKind()) { |
| case DeclKind::PrefixOperator: |
| sym.SubKind = SymbolSubKind::SwiftPrefixOperator; |
| return; |
| case DeclKind::PostfixOperator: |
| sym.SubKind = SymbolSubKind::SwiftPostfixOperator; |
| return; |
| case DeclKind::InfixOperator: |
| sym.SubKind = SymbolSubKind::SwiftInfixOperator; |
| return; |
| default: |
| llvm_unreachable("unexpected operator kind"); |
| } |
| } |
| } |
| |
| static SymbolKind getVarSymbolKind(const VarDecl *VD) { |
| auto *DC = VD->getDeclContext(); |
| if (DC->isTypeContext()) { |
| if (VD->isStatic()) { |
| if (VD->getCorrectStaticSpelling() == StaticSpellingKind::KeywordClass) |
| return SymbolKind::ClassProperty; |
| return SymbolKind::StaticProperty; |
| } |
| return SymbolKind::InstanceProperty; |
| } |
| return SymbolKind::Variable; |
| } |
| |
| SymbolInfo index::getSymbolInfoForDecl(const Decl *D) { |
| SymbolInfo info{ SymbolKind::Unknown, SymbolSubKind::None, |
| SymbolPropertySet(), SymbolLanguage::Swift }; |
| switch (D->getKind()) { |
| case DeclKind::Enum: info.Kind = SymbolKind::Enum; break; |
| case DeclKind::Struct: info.Kind = SymbolKind::Struct; break; |
| case DeclKind::Protocol: info.Kind = SymbolKind::Protocol; break; |
| case DeclKind::Class: |
| info.Kind = SymbolKind::Class; |
| if (isUnitTestCase(cast<ClassDecl>(D))) |
| info.Properties |= SymbolProperty::UnitTest; |
| break; |
| case DeclKind::Extension: { |
| info.Kind = SymbolKind::Extension; |
| auto *ED = cast<ExtensionDecl>(D); |
| if (!ED->getExtendedType()) |
| break; |
| NominalTypeDecl *NTD = ED->getExtendedType()->getAnyNominal(); |
| if (!NTD) |
| break; |
| if (isa<StructDecl>(NTD)) |
| info.SubKind = SymbolSubKind::SwiftExtensionOfStruct; |
| else if (auto *CD = dyn_cast<ClassDecl>(NTD)) { |
| info.SubKind = SymbolSubKind::SwiftExtensionOfClass; |
| if (isUnitTestCase(CD)) |
| info.Properties |= SymbolProperty::UnitTest; |
| } else if (isa<EnumDecl>(NTD)) |
| info.SubKind = SymbolSubKind::SwiftExtensionOfEnum; |
| else if (isa<ProtocolDecl>(NTD)) |
| info.SubKind = SymbolSubKind::SwiftExtensionOfProtocol; |
| assert(info.SubKind != SymbolSubKind::None); |
| break; |
| } |
| case DeclKind::TypeAlias: info.Kind = SymbolKind::TypeAlias; break; |
| case DeclKind::AssociatedType: |
| info.Kind = SymbolKind::TypeAlias; |
| info.SubKind = SymbolSubKind::SwiftAssociatedType; |
| break; |
| case DeclKind::GenericTypeParam: |
| info.Kind = SymbolKind::TypeAlias; |
| info.SubKind = SymbolSubKind::SwiftGenericTypeParam; |
| break; |
| case DeclKind::EnumElement: info.Kind = SymbolKind::EnumConstant; break; |
| case DeclKind::Subscript: |
| info.Kind = SymbolKind::InstanceProperty; |
| info.SubKind = SymbolSubKind::SwiftSubscript; |
| break; |
| case DeclKind::Constructor: info.Kind = SymbolKind::Constructor; break; |
| case DeclKind::Destructor: info.Kind = SymbolKind::Destructor; break; |
| case DeclKind::Param: |
| info.Kind = SymbolKind::Parameter; |
| break; |
| case DeclKind::Func: |
| setFuncSymbolInfo(cast<FuncDecl>(D), info); |
| break; |
| case DeclKind::Var: |
| info.Kind = getVarSymbolKind(cast<VarDecl>(D)); |
| if (D->getAttrs().hasAttribute<IBOutletAttr>()) |
| info.Properties |= SymbolProperty::IBAnnotated; |
| if (D->getAttrs().hasAttribute<GKInspectableAttr>()) |
| info.Properties |= SymbolProperty::GKInspectable; |
| break; |
| |
| default: |
| break; |
| } |
| |
| if (isLocalSymbol(D)) { |
| info.Properties |= SymbolProperty::Local; |
| } |
| |
| return info; |
| } |
| |
| SymbolSubKind index::getSubKindForAccessor(AccessorKind AK) { |
| switch (AK) { |
| case AccessorKind::NotAccessor: return SymbolSubKind::None; |
| case AccessorKind::IsGetter: return SymbolSubKind::AccessorGetter; |
| case AccessorKind::IsSetter: return SymbolSubKind::AccessorSetter; |
| case AccessorKind::IsWillSet: return SymbolSubKind::SwiftAccessorWillSet; |
| case AccessorKind::IsDidSet: return SymbolSubKind::SwiftAccessorDidSet; |
| case AccessorKind::IsAddressor: return SymbolSubKind::SwiftAccessorAddressor; |
| case AccessorKind::IsMutableAddressor: |
| return SymbolSubKind::SwiftAccessorMutableAddressor; |
| case AccessorKind::IsMaterializeForSet: |
| llvm_unreachable("unexpected MaterializeForSet"); |
| } |
| |
| llvm_unreachable("Unhandled AccessorKind in switch."); |
| } |
| |
| bool index::isLocalSymbol(const swift::Decl *D) { |
| return D->getDeclContext()->getLocalContext() && |
| (!isa<ParamDecl>(D) || cast<ParamDecl>(D)->getArgumentNameLoc().isValid() || |
| D->getDeclContext()->getContextKind() == DeclContextKind::AbstractClosureExpr); |
| } |