Files
swift-mirror/lib/Index/IndexSymbol.cpp
John McCall 7a4aeed570 Implement generalized accessors using yield-once coroutines.
For now, the accessors have been underscored as `_read` and `_modify`.
I'll prepare an evolution proposal for this feature which should allow
us to remove the underscores or, y'know, rename them to `purple` and
`lettuce`.

`_read` accessors do not make any effort yet to avoid copying the
value being yielded.  I'll work on it in follow-up patches.

Opaque accesses to properties and subscripts defined with `_modify`
accessors will use an inefficient `materializeForSet` pattern that
materializes the value to a temporary instead of accessing it in-place.
That will be fixed by migrating to `modify` over `materializeForSet`,
which is next up after the `read` optimizations.

SIL ownership verification doesn't pass yet for the test cases here
because of a general fault in SILGen where borrows can outlive their
borrowed value due to being cleaned up on the general cleanup stack
when the borrowed value is cleaned up on the formal-access stack.
Michael, Andy, and I discussed various ways to fix this, but it seems
clear to me that it's not in any way specific to coroutine accesses.

rdar://35399664
2018-07-23 18:59:58 -04:00

251 lines
8.2 KiB
C++

//===--- 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) {
return D->getDeclContext()->getAsNominalTypeOrNominalTypeExtensionContext();
}
/// \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->getParameters()->size() != 0)
return false;
// 5. ...is of at least 'internal' access (unless we can use
// Objective-C reflection)...
if (!D->getASTContext().LangOpts.EnableObjCInterop &&
(D->getFormalAccess() < AccessLevel::Internal ||
parentNTD->getFormalAccess() < AccessLevel::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 (auto accessor = dyn_cast<AccessorDecl>(FD)) {
sym.SubKind = getSubKindForAccessor(accessor->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,
SymbolLanguage::Swift, SymbolPropertySet() };
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::Accessor:
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;
// Arguably these should be indexed?
case DeclKind::PrecedenceGroup:
case DeclKind::InfixOperator:
case DeclKind::PrefixOperator:
case DeclKind::PostfixOperator:
break;
// These all reflect some sort of uninteresting syntactic structure
// and don't merit indexing.
case DeclKind::Import:
case DeclKind::PatternBinding:
case DeclKind::EnumCase:
case DeclKind::TopLevelCode:
case DeclKind::IfConfig:
case DeclKind::PoundDiagnostic:
case DeclKind::MissingMember:
case DeclKind::Module:
break;
}
if (isLocalSymbol(D)) {
info.Properties |= SymbolProperty::Local;
}
return info;
}
SymbolSubKind index::getSubKindForAccessor(AccessorKind AK) {
switch (AK) {
case AccessorKind::Get: return SymbolSubKind::AccessorGetter;
case AccessorKind::Set: return SymbolSubKind::AccessorSetter;
case AccessorKind::WillSet: return SymbolSubKind::SwiftAccessorWillSet;
case AccessorKind::DidSet: return SymbolSubKind::SwiftAccessorDidSet;
case AccessorKind::Address: return SymbolSubKind::SwiftAccessorAddressor;
case AccessorKind::MutableAddress:
return SymbolSubKind::SwiftAccessorMutableAddressor;
case AccessorKind::Read: return SymbolSubKind::SwiftAccessorRead;
case AccessorKind::Modify: return SymbolSubKind::SwiftAccessorModify;
case AccessorKind::MaterializeForSet:
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);
}