mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
303 lines
10 KiB
C++
303 lines
10 KiB
C++
//===--- CodeCompletionResultBuilder.cpp ----------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 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 "CodeCompletionResultBuilder.h"
|
|
#include "CodeCompletionDiagnostics.h"
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/ASTDemangler.h"
|
|
#include "swift/AST/USRGeneration.h"
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/IDE/CodeCompletionStringPrinter.h"
|
|
#include "swift/IDE/CommentConversion.h"
|
|
#include "swift/IDE/Utils.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Comment.h"
|
|
#include "clang/Basic/Module.h"
|
|
#include "clang/Index/USRGeneration.h"
|
|
|
|
using namespace swift;
|
|
using namespace swift::ide;
|
|
|
|
static bool shouldCopyAssociatedUSRForDecl(const ValueDecl *VD) {
|
|
// Avoid trying to generate a USR for some declaration types.
|
|
if (isa<GenericTypeParamDecl>(VD))
|
|
return false;
|
|
if (isa<ParamDecl>(VD))
|
|
return false;
|
|
if (isa<ModuleDecl>(VD))
|
|
return false;
|
|
if (VD->hasClangNode() && !VD->getClangDecl())
|
|
return false;
|
|
|
|
// Avoid generating USRs for decls in local contexts, we cannot guarantee
|
|
// any parent closures will be type-checked, which is needed for mangling.
|
|
if (VD->getDeclContext()->getLocalContext())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename FnTy>
|
|
static void walkValueDeclAndOverriddenDecls(const Decl *D, const FnTy &Fn) {
|
|
if (auto *VD = dyn_cast<ValueDecl>(D)) {
|
|
Fn(VD);
|
|
walkOverriddenDecls(VD, Fn);
|
|
}
|
|
}
|
|
|
|
static ArrayRef<NullTerminatedStringRef>
|
|
copyAssociatedUSRs(llvm::BumpPtrAllocator &Allocator, const Decl *D) {
|
|
llvm::SmallVector<NullTerminatedStringRef, 4> USRs;
|
|
walkValueDeclAndOverriddenDecls(
|
|
D,
|
|
[&](llvm::PointerUnion<const ValueDecl *, const clang::NamedDecl *> OD) {
|
|
llvm::SmallString<128> SS;
|
|
bool Ignored = true;
|
|
if (auto *OVD = OD.dyn_cast<const ValueDecl *>()) {
|
|
if (shouldCopyAssociatedUSRForDecl(OVD)) {
|
|
llvm::raw_svector_ostream OS(SS);
|
|
Ignored = printValueDeclUSR(OVD, OS);
|
|
}
|
|
} else if (auto *OND = OD.dyn_cast<const clang::NamedDecl *>()) {
|
|
Ignored = clang::index::generateUSRForDecl(OND, SS);
|
|
}
|
|
|
|
if (!Ignored)
|
|
USRs.emplace_back(SS, Allocator);
|
|
});
|
|
|
|
if (!USRs.empty())
|
|
return llvm::ArrayRef(USRs).copy(Allocator);
|
|
|
|
return {};
|
|
}
|
|
|
|
static NullTerminatedStringRef copySwiftUSR(llvm::BumpPtrAllocator &Allocator,
|
|
const Decl *D) {
|
|
auto *VD = dyn_cast<ValueDecl>(D);
|
|
if (!VD || !shouldCopyAssociatedUSRForDecl(VD))
|
|
return NullTerminatedStringRef();
|
|
|
|
SmallString<128> SS;
|
|
llvm::raw_svector_ostream OS(SS);
|
|
if (!ide::printValueDeclSwiftUSR(VD, OS))
|
|
return NullTerminatedStringRef(SS, Allocator);
|
|
|
|
return NullTerminatedStringRef();
|
|
}
|
|
|
|
/// Tries to reconstruct the provided \p D declaration using \c
|
|
/// Demangle::getDeclForUSR and verifies that the declarations match. This only
|
|
/// works if \p D is a \c ValueDecl and \c shouldCopyAssociatedUSRForDecl is
|
|
/// true.
|
|
///
|
|
/// This is intended for testing only.
|
|
static void verifyUSRToDeclReconstruction(const Decl *D) {
|
|
auto *VD = dyn_cast<ValueDecl>(D);
|
|
if (!VD)
|
|
return;
|
|
|
|
if (!shouldCopyAssociatedUSRForDecl(VD))
|
|
return;
|
|
|
|
SmallString<128> SwiftUSR;
|
|
|
|
llvm::raw_svector_ostream OS(SwiftUSR);
|
|
if (ide::printValueDeclSwiftUSR(VD, OS)) {
|
|
ABORT([&](auto &out) {
|
|
out << "Declaration should have a Swift USR:\n";
|
|
VD->dump(out);
|
|
});
|
|
}
|
|
|
|
auto &Ctx = VD->getASTContext();
|
|
auto *Reconstructed = Demangle::getDeclForUSR(Ctx, SwiftUSR);
|
|
|
|
if (!Reconstructed) {
|
|
ABORT([&](auto &out) {
|
|
out << "Reconstructed declaration shouldn't be null\n"
|
|
<< "Swift USR: " << SwiftUSR << ", original declaration:\n";
|
|
VD->dump(out);
|
|
});
|
|
}
|
|
|
|
if (Reconstructed != VD) {
|
|
ABORT([&](auto &out) {
|
|
out << "Reconstructed declaration should match the original one\n"
|
|
<< "Swift USR: " << SwiftUSR << "\n"
|
|
<< "Original declaration:\n";
|
|
VD->dump(out);
|
|
out << "Reconstructed declaration:\n";
|
|
Reconstructed->dump(out);
|
|
});
|
|
}
|
|
}
|
|
|
|
CodeCompletionResult *CodeCompletionResultBuilder::takeResult() {
|
|
auto &Allocator = *Sink.Allocator;
|
|
auto *CCS = createCompletionString();
|
|
|
|
if (Sink.verifyUSRToDecl && AssociatedDecl) {
|
|
verifyUSRToDeclReconstruction(AssociatedDecl);
|
|
}
|
|
|
|
CodeCompletionDiagnosticSeverity ContextFreeDiagnosticSeverity =
|
|
CodeCompletionDiagnosticSeverity::None;
|
|
NullTerminatedStringRef ContextFreeDiagnosticMessage;
|
|
if (ContextFreeNotRecReason != ContextFreeNotRecommendedReason::None) {
|
|
assert(AssociatedDecl != nullptr &&
|
|
"There should be no case where ContextFreeNotRecReason != None && "
|
|
"AssociatedDecl == nulptr");
|
|
// FIXME: We should generate the message lazily.
|
|
if (const auto *VD = dyn_cast_or_null<ValueDecl>(AssociatedDecl)) {
|
|
CodeCompletionDiagnosticSeverity severity;
|
|
SmallString<256> message;
|
|
llvm::raw_svector_ostream messageOS(message);
|
|
if (!getContextFreeCompletionDiagnostics(ContextFreeNotRecReason, VD,
|
|
severity, messageOS)) {
|
|
ContextFreeDiagnosticSeverity = severity;
|
|
ContextFreeDiagnosticMessage =
|
|
NullTerminatedStringRef(message, Allocator);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// This variable should be initialized by all the switch cases below.
|
|
ContextFreeCodeCompletionResult *ContextFreeResult = nullptr;
|
|
|
|
switch (Kind) {
|
|
case CodeCompletionResultKind::Declaration: {
|
|
NullTerminatedStringRef ModuleName;
|
|
if (CurrentModule) {
|
|
if (Sink.LastModule.first == CurrentModule.getOpaqueValue()) {
|
|
ModuleName = Sink.LastModule.second;
|
|
} else {
|
|
if (auto *C = CurrentModule.dyn_cast<const clang::Module *>()) {
|
|
ModuleName =
|
|
NullTerminatedStringRef(C->getFullModuleName(), Allocator);
|
|
} else {
|
|
ModuleName = NullTerminatedStringRef(
|
|
cast<const swift::ModuleDecl *>(CurrentModule)->getName().str(),
|
|
Allocator);
|
|
}
|
|
Sink.LastModule.first = CurrentModule.getOpaqueValue();
|
|
Sink.LastModule.second = ModuleName;
|
|
}
|
|
}
|
|
|
|
ContextFreeResult = ContextFreeCodeCompletionResult::createDeclResult(
|
|
Sink, CCS, AssociatedDecl, HasAsyncAlternative, ModuleName,
|
|
NullTerminatedStringRef(BriefDocComment, Allocator),
|
|
copyAssociatedUSRs(Allocator, AssociatedDecl),
|
|
copySwiftUSR(Allocator, AssociatedDecl), ResultType,
|
|
ContextFreeNotRecReason, ContextFreeDiagnosticSeverity,
|
|
ContextFreeDiagnosticMessage);
|
|
break;
|
|
}
|
|
|
|
case CodeCompletionResultKind::Keyword:
|
|
ContextFreeResult = ContextFreeCodeCompletionResult::createKeywordResult(
|
|
Sink, KeywordKind, CCS,
|
|
NullTerminatedStringRef(BriefDocComment, Allocator), ResultType);
|
|
break;
|
|
case CodeCompletionResultKind::BuiltinOperator:
|
|
case CodeCompletionResultKind::Pattern:
|
|
ContextFreeResult =
|
|
ContextFreeCodeCompletionResult::createPatternOrBuiltInOperatorResult(
|
|
Sink, Kind, CCS, CodeCompletionOperatorKind::None,
|
|
NullTerminatedStringRef(BriefDocComment, Allocator), ResultType,
|
|
ContextFreeNotRecReason, ContextFreeDiagnosticSeverity,
|
|
ContextFreeDiagnosticMessage);
|
|
break;
|
|
case CodeCompletionResultKind::Literal:
|
|
assert(LiteralKind.has_value());
|
|
ContextFreeResult = ContextFreeCodeCompletionResult::createLiteralResult(
|
|
Sink, *LiteralKind, CCS, ResultType);
|
|
break;
|
|
}
|
|
|
|
if (Sink.shouldProduceContextFreeResults()) {
|
|
// If the sink only intends to store the context free results in the cache,
|
|
// we don't need to compute any contextual properties.
|
|
return new (Allocator) CodeCompletionResult(
|
|
*ContextFreeResult, AssociatedDecl, SemanticContextKind::None,
|
|
CodeCompletionFlair(), /*NumBytesToErase=*/0,
|
|
CodeCompletionResultTypeRelation::Unrelated,
|
|
ContextualNotRecommendedReason::None);
|
|
} else {
|
|
assert(
|
|
ContextFreeResult != nullptr &&
|
|
"ContextFreeResult should have been constructed by the switch above");
|
|
|
|
// We know that the ContextFreeResult has an AST-based type because it was
|
|
// just computed and not read from the cache and
|
|
// Sink.shouldProduceContextFreeResults() is false. So we can pass nullptr
|
|
// for USRTypeContext.
|
|
CodeCompletionResultTypeRelation typeRelation =
|
|
ContextFreeResult->calculateContextualTypeRelation(
|
|
DC, TypeContext, /*usrTypeContext=*/nullptr);
|
|
ContextualNotRecommendedReason notRecommendedReason =
|
|
ContextFreeResult->calculateContextualNotRecommendedReason(
|
|
ContextualNotRecReason, CanCurrDeclContextHandleAsync);
|
|
|
|
return new (Allocator) CodeCompletionResult(
|
|
*ContextFreeResult, AssociatedDecl, SemanticContext, Flair,
|
|
NumBytesToErase, typeRelation, notRecommendedReason);
|
|
}
|
|
}
|
|
|
|
void CodeCompletionResultBuilder::finishResult() {
|
|
if (!Cancelled)
|
|
Sink.Results.push_back(takeResult());
|
|
}
|
|
|
|
void CodeCompletionResultBuilder::setAssociatedDecl(const Decl *D) {
|
|
assert(Kind == CodeCompletionResultKind::Declaration);
|
|
|
|
AssociatedDecl = D;
|
|
|
|
if (auto *ClangD = D->getClangDecl())
|
|
CurrentModule = ClangD->getImportedOwningModule();
|
|
// FIXME: macros
|
|
// FIXME: imported header module
|
|
|
|
if (!CurrentModule) {
|
|
ModuleDecl *MD = D->getModuleContext();
|
|
|
|
// If this is an underscored cross-import overlay, map it to the underlying
|
|
// module that declares it instead.
|
|
if (ModuleDecl *Declaring = MD->getDeclaringModuleIfCrossImportOverlay())
|
|
MD = Declaring;
|
|
|
|
CurrentModule = MD;
|
|
}
|
|
|
|
if (D->isDeprecated())
|
|
setContextFreeNotRecommended(ContextFreeNotRecommendedReason::Deprecated);
|
|
else if (D->getSoftDeprecatedAttr())
|
|
setContextFreeNotRecommended(
|
|
ContextFreeNotRecommendedReason::SoftDeprecated);
|
|
|
|
if (D->getClangNode()) {
|
|
if (auto *ClangD = D->getClangDecl()) {
|
|
const auto &ClangContext = ClangD->getASTContext();
|
|
if (const clang::RawComment *RC =
|
|
ClangContext.getRawCommentForAnyRedecl(ClangD)) {
|
|
setBriefDocComment(RC->getBriefText(ClangContext));
|
|
}
|
|
}
|
|
} else {
|
|
setBriefDocComment(AssociatedDecl->getSemanticBriefComment());
|
|
}
|
|
}
|