mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
When completing in the only expression of closure, use the return type of the closure as the type context for the code-completion. However, since code-completion may be on an incomplete input, we only use the return type to improve the quality of the result, not to mark it invalid, since (a) we may add another statement afterwards, or (b) if the context type is Void it doesn't need to match the value.
436 lines
13 KiB
C++
436 lines
13 KiB
C++
//===--- CodeCompletionResultBuilder.h - Build completion results ---------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_LIB_IDE_CODE_COMPLETION_RESULT_BUILDER_H
|
|
#define SWIFT_LIB_IDE_CODE_COMPLETION_RESULT_BUILDER_H
|
|
|
|
#include "swift/IDE/CodeCompletion.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/Basic/StringExtras.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
|
|
namespace clang {
|
|
class Module;
|
|
}
|
|
|
|
namespace swift {
|
|
class Decl;
|
|
class ModuleDecl;
|
|
|
|
namespace ide {
|
|
|
|
struct ExpectedTypeContext;
|
|
|
|
class CodeCompletionResultBuilder {
|
|
CodeCompletionResultSink &Sink;
|
|
CodeCompletionResult::ResultKind Kind;
|
|
SemanticContextKind SemanticContext;
|
|
unsigned NumBytesToErase = 0;
|
|
const Decl *AssociatedDecl = nullptr;
|
|
Optional<CodeCompletionLiteralKind> LiteralKind;
|
|
CodeCompletionKeywordKind KeywordKind = CodeCompletionKeywordKind::None;
|
|
unsigned CurrentNestingLevel = 0;
|
|
SmallVector<CodeCompletionString::Chunk, 4> Chunks;
|
|
llvm::PointerUnion<const ModuleDecl *, const clang::Module *>
|
|
CurrentModule;
|
|
const ExpectedTypeContext &declTypeContext;
|
|
CodeCompletionResult::ExpectedTypeRelation ExpectedTypeRelation =
|
|
CodeCompletionResult::Unrelated;
|
|
bool Cancelled = false;
|
|
ArrayRef<std::pair<StringRef, StringRef>> CommentWords;
|
|
bool IsNotRecommended = false;
|
|
CodeCompletionResult::NotRecommendedReason NotRecReason =
|
|
CodeCompletionResult::NotRecommendedReason::NoReason;
|
|
|
|
void addChunkWithText(CodeCompletionString::Chunk::ChunkKind Kind,
|
|
StringRef Text);
|
|
|
|
void addChunkWithTextNoCopy(CodeCompletionString::Chunk::ChunkKind Kind,
|
|
StringRef Text) {
|
|
Chunks.push_back(CodeCompletionString::Chunk::createWithText(
|
|
Kind, CurrentNestingLevel, Text));
|
|
}
|
|
|
|
void addSimpleChunk(CodeCompletionString::Chunk::ChunkKind Kind) {
|
|
Chunks.push_back(
|
|
CodeCompletionString::Chunk::createSimple(Kind,
|
|
CurrentNestingLevel));
|
|
}
|
|
|
|
CodeCompletionString::Chunk &getLastChunk() {
|
|
return Chunks.back();
|
|
}
|
|
|
|
CodeCompletionResult *takeResult();
|
|
void finishResult();
|
|
|
|
public:
|
|
CodeCompletionResultBuilder(CodeCompletionResultSink &Sink,
|
|
CodeCompletionResult::ResultKind Kind,
|
|
SemanticContextKind SemanticContext,
|
|
const ExpectedTypeContext &declTypeContext)
|
|
: Sink(Sink), Kind(Kind), SemanticContext(SemanticContext),
|
|
declTypeContext(declTypeContext) {}
|
|
|
|
~CodeCompletionResultBuilder() {
|
|
finishResult();
|
|
}
|
|
|
|
void cancel() {
|
|
Cancelled = true;
|
|
}
|
|
|
|
void setNumBytesToErase(unsigned N) {
|
|
NumBytesToErase = N;
|
|
}
|
|
|
|
void setAssociatedDecl(const Decl *D);
|
|
|
|
void setLiteralKind(CodeCompletionLiteralKind kind) { LiteralKind = kind; }
|
|
void setKeywordKind(CodeCompletionKeywordKind kind) { KeywordKind = kind; }
|
|
void setNotRecommended(CodeCompletionResult::NotRecommendedReason Reason) {
|
|
IsNotRecommended = true;
|
|
NotRecReason = Reason;
|
|
}
|
|
|
|
void
|
|
setExpectedTypeRelation(CodeCompletionResult::ExpectedTypeRelation relation) {
|
|
ExpectedTypeRelation = relation;
|
|
}
|
|
|
|
void addAccessControlKeyword(AccessLevel Access) {
|
|
switch (Access) {
|
|
case AccessLevel::Private:
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::AccessControlKeyword,
|
|
"private ");
|
|
break;
|
|
case AccessLevel::FilePrivate:
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::AccessControlKeyword,
|
|
"fileprivate ");
|
|
break;
|
|
case AccessLevel::Internal:
|
|
// 'internal' is the default, don't add it.
|
|
break;
|
|
case AccessLevel::Public:
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::AccessControlKeyword,
|
|
"public ");
|
|
break;
|
|
case AccessLevel::Open:
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::AccessControlKeyword,
|
|
"open ");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void addOverrideKeyword() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::OverrideKeyword, "override ");
|
|
}
|
|
|
|
void addDeclIntroducer(StringRef Text) {
|
|
addChunkWithText(CodeCompletionString::Chunk::ChunkKind::DeclIntroducer,
|
|
Text);
|
|
}
|
|
|
|
void addTextChunk(StringRef Text) {
|
|
addChunkWithText(CodeCompletionString::Chunk::ChunkKind::Text, Text);
|
|
}
|
|
|
|
void addAnnotatedThrows() {
|
|
addThrows();
|
|
getLastChunk().setIsAnnotation();
|
|
}
|
|
|
|
void addThrows() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::ThrowsKeyword,
|
|
" throws");
|
|
}
|
|
|
|
void addDeclDocCommentWords(ArrayRef<std::pair<StringRef, StringRef>> Pairs) {
|
|
assert(Kind == CodeCompletionResult::ResultKind::Declaration);
|
|
CommentWords = Pairs;
|
|
}
|
|
|
|
void addAnnotatedRethrows() {
|
|
addRethrows();
|
|
getLastChunk().setIsAnnotation();
|
|
}
|
|
|
|
void addRethrows() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::RethrowsKeyword,
|
|
" rethrows");
|
|
}
|
|
|
|
void addAnnotatedLeftParen() {
|
|
addLeftParen();
|
|
getLastChunk().setIsAnnotation();
|
|
}
|
|
|
|
void addLeftParen() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::LeftParen, "(");
|
|
}
|
|
|
|
void addAnnotatedRightParen() {
|
|
addRightParen();
|
|
getLastChunk().setIsAnnotation();
|
|
}
|
|
|
|
void addRightParen() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::RightParen, ")");
|
|
}
|
|
|
|
void addLeftBracket() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::LeftBracket, "[");
|
|
}
|
|
|
|
void addRightBracket() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::RightBracket, "]");
|
|
}
|
|
|
|
void addLeftAngle() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::LeftAngle, "<");
|
|
}
|
|
|
|
void addRightAngle() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::RightAngle, ">");
|
|
}
|
|
|
|
void addLeadingDot() {
|
|
addDot();
|
|
}
|
|
|
|
void addDot() {
|
|
addChunkWithTextNoCopy(CodeCompletionString::Chunk::ChunkKind::Dot, ".");
|
|
}
|
|
|
|
void addEllipsis() {
|
|
addChunkWithTextNoCopy(CodeCompletionString::Chunk::ChunkKind::Ellipsis,
|
|
"...");
|
|
}
|
|
|
|
void addComma() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::Comma, ", ");
|
|
}
|
|
|
|
void addExclamationMark() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::ExclamationMark, "!");
|
|
}
|
|
|
|
void addQuestionMark() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::QuestionMark, "?");
|
|
}
|
|
|
|
void addEqual() {
|
|
addChunkWithTextNoCopy(CodeCompletionString::Chunk::ChunkKind::Equal, "=");
|
|
}
|
|
|
|
void addDeclAttrParamKeyword(StringRef Name, StringRef Annotation,
|
|
bool NeedSpecify) {
|
|
addChunkWithText(CodeCompletionString::Chunk::ChunkKind::
|
|
DeclAttrParamKeyword, Name);
|
|
if (NeedSpecify)
|
|
addChunkWithText(CodeCompletionString::Chunk::ChunkKind::
|
|
DeclAttrParamColon, ": ");
|
|
if (!Annotation.empty())
|
|
addTypeAnnotation(Annotation);
|
|
}
|
|
|
|
void addDeclAttrKeyword(StringRef Name, StringRef Annotation) {
|
|
addChunkWithText(CodeCompletionString::Chunk::ChunkKind::
|
|
DeclAttrKeyword, Name);
|
|
if (!Annotation.empty())
|
|
addTypeAnnotation(Annotation);
|
|
}
|
|
|
|
StringRef escapeArgumentLabel(StringRef Word,
|
|
bool escapeAllKeywords,
|
|
llvm::SmallString<16> &EscapedKeyword) {
|
|
bool shouldEscape = false;
|
|
if (escapeAllKeywords) {
|
|
#define KEYWORD(kw) .Case(#kw, true)
|
|
shouldEscape = llvm::StringSwitch<bool>(Word)
|
|
#include "swift/Syntax/TokenKinds.def"
|
|
.Default(false);
|
|
} else {
|
|
shouldEscape = !canBeArgumentLabel(Word);
|
|
}
|
|
|
|
if (!shouldEscape)
|
|
return Word;
|
|
|
|
EscapedKeyword.append("`");
|
|
EscapedKeyword.append(Word);
|
|
EscapedKeyword.append("`");
|
|
return EscapedKeyword;
|
|
}
|
|
|
|
void addCallParameterColon() {
|
|
addChunkWithText(CodeCompletionString::Chunk::ChunkKind::
|
|
CallParameterColon, ": ");
|
|
}
|
|
|
|
void addSimpleNamedParameter(StringRef name) {
|
|
CurrentNestingLevel++;
|
|
addSimpleChunk(CodeCompletionString::Chunk::ChunkKind::CallParameterBegin);
|
|
// Use internal, since we don't want the name to be outside the placeholder.
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::CallParameterInternalName,
|
|
name);
|
|
CurrentNestingLevel--;
|
|
}
|
|
|
|
void addSimpleTypedParameter(StringRef Annotation, bool IsVarArg = false) {
|
|
CurrentNestingLevel++;
|
|
addSimpleChunk(CodeCompletionString::Chunk::ChunkKind::CallParameterBegin);
|
|
addChunkWithText(CodeCompletionString::Chunk::ChunkKind::CallParameterType,
|
|
Annotation);
|
|
if (IsVarArg)
|
|
addEllipsis();
|
|
CurrentNestingLevel--;
|
|
}
|
|
|
|
void addCallParameter(Identifier Name, Identifier LocalName, Type Ty,
|
|
bool IsVarArg, bool Outermost, bool IsInOut, bool IsIUO,
|
|
bool isAutoClosure) {
|
|
CurrentNestingLevel++;
|
|
|
|
addSimpleChunk(CodeCompletionString::Chunk::ChunkKind::CallParameterBegin);
|
|
|
|
if (!Name.empty()) {
|
|
llvm::SmallString<16> EscapedKeyword;
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::CallParameterName,
|
|
escapeArgumentLabel(Name.str(), !Outermost, EscapedKeyword));
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": ");
|
|
} else if (!LocalName.empty()) {
|
|
// Use local (non-API) parameter name if we have nothing else.
|
|
llvm::SmallString<16> EscapedKeyword;
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::CallParameterInternalName,
|
|
escapeArgumentLabel(LocalName.str(), !Outermost, EscapedKeyword));
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": ");
|
|
}
|
|
|
|
// 'inout' arguments are printed specially.
|
|
if (IsInOut) {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::Ampersand, "&");
|
|
Ty = Ty->getInOutObjectType();
|
|
}
|
|
|
|
// If the parameter is of the type @autoclosure ()->output, then the
|
|
// code completion should show the parameter of the output type
|
|
// instead of the function type ()->output.
|
|
if (isAutoClosure)
|
|
Ty = Ty->castTo<FunctionType>()->getResult();
|
|
|
|
PrintOptions PO;
|
|
PO.SkipAttributes = true;
|
|
PO.PrintOptionalAsImplicitlyUnwrapped = IsIUO;
|
|
std::string TypeName = Ty->getString(PO);
|
|
addChunkWithText(CodeCompletionString::Chunk::ChunkKind::CallParameterType,
|
|
TypeName);
|
|
|
|
// Look through optional types and type aliases to find out if we have
|
|
// function type.
|
|
Ty = Ty->lookThroughAllOptionalTypes();
|
|
if (auto AFT = Ty->getAs<AnyFunctionType>()) {
|
|
// If this is a closure type, add ChunkKind::CallParameterClosureType.
|
|
PrintOptions PO;
|
|
PO.PrintFunctionRepresentationAttrs = false;
|
|
PO.SkipAttributes = true;
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::CallParameterClosureType,
|
|
AFT->getString(PO));
|
|
}
|
|
|
|
if (IsVarArg)
|
|
addEllipsis();
|
|
CurrentNestingLevel--;
|
|
}
|
|
|
|
void addCallParameter(Identifier Name, Type Ty, bool IsVarArg, bool Outermost,
|
|
bool IsInOut, bool IsIUO, bool isAutoClosure) {
|
|
addCallParameter(Name, Identifier(), Ty, IsVarArg, Outermost, IsInOut,
|
|
IsIUO, isAutoClosure);
|
|
}
|
|
|
|
void addGenericParameter(StringRef Name) {
|
|
CurrentNestingLevel++;
|
|
addSimpleChunk(
|
|
CodeCompletionString::Chunk::ChunkKind::GenericParameterBegin);
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::GenericParameterName, Name);
|
|
CurrentNestingLevel--;
|
|
}
|
|
|
|
void addDynamicLookupMethodCallTail() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::DynamicLookupMethodCallTail,
|
|
"!");
|
|
getLastChunk().setIsAnnotation();
|
|
}
|
|
|
|
void addOptionalMethodCallTail() {
|
|
addChunkWithTextNoCopy(
|
|
CodeCompletionString::Chunk::ChunkKind::OptionalMethodCallTail, "?");
|
|
}
|
|
|
|
void addTypeAnnotation(StringRef Type) {
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::TypeAnnotation, Type);
|
|
getLastChunk().setIsAnnotation();
|
|
}
|
|
|
|
void addBraceStmtWithCursor(StringRef Description = "") {
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::BraceStmtWithCursor,
|
|
Description);
|
|
}
|
|
|
|
void addWhitespace(StringRef space) {
|
|
addChunkWithText(
|
|
CodeCompletionString::Chunk::ChunkKind::Whitespace, space);
|
|
}
|
|
|
|
void addAnnotatedWhitespace(StringRef space) {
|
|
addWhitespace(space);
|
|
getLastChunk().setIsAnnotation();
|
|
}
|
|
};
|
|
|
|
} // namespace ide
|
|
} // namespace swift
|
|
|
|
#endif // SWIFT_LIB_IDE_CODE_COMPLETION_RESULT_BUILDER_H
|
|
|