//===--- CodeCompletionResult.cpp -----------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2022 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/IDE/CodeCompletionResult.h"
#include "CodeCompletionDiagnostics.h"
#include "swift/AST/ASTDemangler.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Module.h"
#include "swift/Basic/Assertions.h"
#include "swift/IDE/CodeCompletionResultPrinter.h"
#include "swift/IDE/CodeCompletionResultSink.h"

using namespace swift;
using namespace swift::ide;

CodeCompletionMacroRoles swift::ide::getCompletionMacroRoles(const Decl *D) {
  CodeCompletionMacroRoles roles;

  auto *MD = dyn_cast<MacroDecl>(D);
  if (!MD)
    return roles;

  MacroRoles macroRoles = MD->getMacroRoles();
  if (macroRoles.contains(MacroRole::Expression)) {
    roles |= CodeCompletionMacroRole::Expression;
  }
  if (macroRoles.contains(MacroRole::Declaration)) {
    roles |= CodeCompletionMacroRole::Declaration;
  }
  if (macroRoles.contains(MacroRole::CodeItem)) {
    roles |= CodeCompletionMacroRole::CodeItem;
  }
  if (macroRoles.contains(MacroRole::Accessor)) {
    roles |= CodeCompletionMacroRole::AttachedVar;
  }
  if (macroRoles & MacroRoles({MacroRole::MemberAttribute, MacroRole::Member,
                               MacroRole::Conformance,
                               MacroRole::Extension,})) {
    roles |= CodeCompletionMacroRole::AttachedContext;
  }
  if (macroRoles.contains(MacroRole::Peer)) {
    roles |= CodeCompletionMacroRole::AttachedDecl;
  }
  if (macroRoles.contains(MacroRole::Body) ||
      macroRoles.contains(MacroRole::Preamble)) {
    roles |= CodeCompletionMacroRole::AttachedFunction;
  }

  return roles;
}

CodeCompletionMacroRoles
swift::ide::getCompletionMacroRoles(OptionSet<CustomAttributeKind> kinds) {
  CodeCompletionMacroRoles roles;
  if (kinds.contains(CustomAttributeKind::VarMacro)) {
    roles |= CodeCompletionMacroRole::AttachedVar;
  }
  if (kinds.contains(CustomAttributeKind::ContextMacro)) {
    roles |= CodeCompletionMacroRole::AttachedContext;
  }
  if (kinds.contains(CustomAttributeKind::DeclMacro)) {
    roles |= CodeCompletionMacroRole::AttachedDecl;
  }
  if (kinds.contains(CustomAttributeKind::FunctionMacro)) {
    roles |= CodeCompletionMacroRole::AttachedFunction;
  }
  return roles;
}

CodeCompletionMacroRoles
swift::ide::getCompletionMacroRoles(CodeCompletionFilter filter) {
  CodeCompletionMacroRoles roles;
  if (filter.contains(CodeCompletionFilterFlag::ExpressionMacro)) {
    roles |= CodeCompletionMacroRole::Expression;
  }
  if (filter.contains(CodeCompletionFilterFlag::DeclarationMacro)) {
    roles |= CodeCompletionMacroRole::Declaration;
  }
  if (filter.contains(CodeCompletionFilterFlag::CodeItemMacro)) {
    roles |= CodeCompletionMacroRole::CodeItem;
  }
  if (filter.contains(CodeCompletionFilterFlag::AttachedVarMacro)) {
    roles |= CodeCompletionMacroRole::AttachedVar;
  }
  if (filter.contains(CodeCompletionFilterFlag::AttachedContextMacro)) {
    roles |= CodeCompletionMacroRole::AttachedContext;
  }
  if (filter.contains(CodeCompletionFilterFlag::AttachedDeclMacro)) {
    roles |= CodeCompletionMacroRole::AttachedDecl;
  }
  if (filter.contains(CodeCompletionFilterFlag::AttachedFunctionMacro)) {
    roles |= CodeCompletionMacroRole::AttachedFunction;
  }
  return roles;
}

CodeCompletionFilter
swift::ide::getCompletionFilter(CodeCompletionMacroRoles roles) {
  CodeCompletionFilter filter;
  if (roles.contains(CodeCompletionMacroRole::Expression)) {
    filter |= CodeCompletionFilterFlag::ExpressionMacro;
  }
  if (roles.contains(CodeCompletionMacroRole::Declaration)) {
    filter |= CodeCompletionFilterFlag::DeclarationMacro;
  }
  if (roles.contains(CodeCompletionMacroRole::CodeItem)) {
    filter |= CodeCompletionFilterFlag::CodeItemMacro;
  }
  if (roles.contains(CodeCompletionMacroRole::AttachedVar)) {
    filter |= CodeCompletionFilterFlag::AttachedVarMacro;
  }
  if (roles.contains(CodeCompletionMacroRole::AttachedContext)) {
    filter |= CodeCompletionFilterFlag::AttachedContextMacro;
  }
  if (roles.contains(CodeCompletionMacroRole::AttachedDecl)) {
    filter |= CodeCompletionFilterFlag::AttachedDeclMacro;
  }
  if (roles.contains(CodeCompletionMacroRole::AttachedFunction)) {
    filter |= CodeCompletionFilterFlag::AttachedFunctionMacro;
  }
  return filter;
}

// MARK: - ContextFreeCodeCompletionResult

ContextFreeCodeCompletionResult *
ContextFreeCodeCompletionResult::createPatternOrBuiltInOperatorResult(
    CodeCompletionResultSink &Sink, CodeCompletionResultKind Kind,
    CodeCompletionString *CompletionString,
    CodeCompletionOperatorKind KnownOperatorKind,
    NullTerminatedStringRef BriefDocComment,
    CodeCompletionResultType ResultType,
    ContextFreeNotRecommendedReason NotRecommended,
    CodeCompletionDiagnosticSeverity DiagnosticSeverity,
    NullTerminatedStringRef DiagnosticMessage) {
  if (Sink.shouldProduceContextFreeResults()) {
    ResultType = ResultType.usrBasedType(Sink.getUSRTypeArena());
  }
  NullTerminatedStringRef NameForDiagnostics;
  if (KnownOperatorKind == CodeCompletionOperatorKind::None) {
    NameForDiagnostics = "function";
  } else {
    NameForDiagnostics = "operator";
  }
  return new (Sink.getAllocator()) ContextFreeCodeCompletionResult(
      Kind, /*AssociatedKind=*/0, KnownOperatorKind, /*MacroRoles=*/{},
      /*IsSystem=*/false, /*HasAsyncAlternative=*/false, CompletionString,
      /*ModuleName=*/"", BriefDocComment,
      /*AssociatedUSRs=*/{}, /*SwiftUSR=*/"", ResultType, NotRecommended,
      DiagnosticSeverity, DiagnosticMessage,
      getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()),
      NameForDiagnostics);
}

ContextFreeCodeCompletionResult *
ContextFreeCodeCompletionResult::createKeywordResult(
    CodeCompletionResultSink &Sink, CodeCompletionKeywordKind Kind,
    CodeCompletionString *CompletionString,
    NullTerminatedStringRef BriefDocComment,
    CodeCompletionResultType ResultType) {
  if (Sink.shouldProduceContextFreeResults()) {
    ResultType = ResultType.usrBasedType(Sink.getUSRTypeArena());
  }
  return new (Sink.getAllocator()) ContextFreeCodeCompletionResult(
      CodeCompletionResultKind::Keyword, static_cast<uint8_t>(Kind),
      CodeCompletionOperatorKind::None, /*MacroRoles=*/{},
      /*IsSystem=*/false, /*HasAsyncAlternative=*/false, CompletionString,
      /*ModuleName=*/"", BriefDocComment, /*AssociatedUSRs=*/{},
      /*SwiftUSR=*/"", ResultType, ContextFreeNotRecommendedReason::None,
      CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"",
      getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()),
      /*NameForDiagnostics=*/"");
}

ContextFreeCodeCompletionResult *
ContextFreeCodeCompletionResult::createLiteralResult(
    CodeCompletionResultSink &Sink, CodeCompletionLiteralKind LiteralKind,
    CodeCompletionString *CompletionString,
    CodeCompletionResultType ResultType) {
  if (Sink.shouldProduceContextFreeResults()) {
    ResultType = ResultType.usrBasedType(Sink.getUSRTypeArena());
  }
  return new (Sink.getAllocator()) ContextFreeCodeCompletionResult(
      CodeCompletionResultKind::Literal, static_cast<uint8_t>(LiteralKind),
      CodeCompletionOperatorKind::None, /*MacroRoles=*/{},
      /*IsSystem=*/false, /*HasAsyncAlternative=*/false, CompletionString,
      /*ModuleName=*/"", /*BriefDocComment=*/"",
      /*AssociatedUSRs=*/{}, /*SwiftUSR=*/"", ResultType,
      ContextFreeNotRecommendedReason::None,
      CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"",
      getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()),
      /*NameForDiagnostics=*/"");
}

static NullTerminatedStringRef
getDeclNameForDiagnostics(const Decl *D, CodeCompletionResultSink &Sink) {
  if (auto VD = dyn_cast<ValueDecl>(D)) {
    llvm::SmallString<64> Name;
    llvm::raw_svector_ostream NameOS(Name);
    NameOS << "'";
    llvm::SmallString<64> Scratch;
    VD->getName().printPretty(NameOS);
    NameOS << "'";
    return NullTerminatedStringRef(NameOS.str(), Sink.getAllocator());
  } else {
    return "";
  }
}

ContextFreeCodeCompletionResult *
ContextFreeCodeCompletionResult::createDeclResult(
    CodeCompletionResultSink &Sink, CodeCompletionString *CompletionString,
    const Decl *AssociatedDecl, bool HasAsyncAlternative,
    NullTerminatedStringRef ModuleName, NullTerminatedStringRef BriefDocComment,
    ArrayRef<NullTerminatedStringRef> AssociatedUSRs,
    NullTerminatedStringRef SwiftUSR, CodeCompletionResultType ResultType,
    ContextFreeNotRecommendedReason NotRecommended,
    CodeCompletionDiagnosticSeverity DiagnosticSeverity,
    NullTerminatedStringRef DiagnosticMessage) {
  assert(AssociatedDecl && "should have a decl");
  if (Sink.shouldProduceContextFreeResults()) {
    ResultType = ResultType.usrBasedType(Sink.getUSRTypeArena());
  }
  return new (Sink.getAllocator()) ContextFreeCodeCompletionResult(
      CodeCompletionResultKind::Declaration,
      static_cast<uint8_t>(getCodeCompletionDeclKind(AssociatedDecl)),
      CodeCompletionOperatorKind::None, getCompletionMacroRoles(AssociatedDecl),
      getDeclIsSystem(AssociatedDecl), HasAsyncAlternative, CompletionString,
      ModuleName, BriefDocComment, AssociatedUSRs, SwiftUSR, ResultType,
      NotRecommended, DiagnosticSeverity, DiagnosticMessage,
      getCodeCompletionResultFilterName(CompletionString, Sink.getAllocator()),
      /*NameForDiagnostics=*/getDeclNameForDiagnostics(AssociatedDecl, Sink));
}

CodeCompletionOperatorKind
ContextFreeCodeCompletionResult::getCodeCompletionOperatorKind(
    const CodeCompletionString *str) {
  StringRef name = str->getFirstTextChunk(/*includeLeadingPunctuation=*/true);
  using CCOK = CodeCompletionOperatorKind;
  using OpPair = std::pair<StringRef, CCOK>;

  // This list must be kept in lexicographic order.
  static OpPair ops[] = {
      std::make_pair("!", CCOK::Bang),
      std::make_pair("!=", CCOK::NotEq),
      std::make_pair("!==", CCOK::NotEqEq),
      std::make_pair("%", CCOK::Modulo),
      std::make_pair("%=", CCOK::ModuloEq),
      std::make_pair("&", CCOK::Amp),
      std::make_pair("&&", CCOK::AmpAmp),
      std::make_pair("&*", CCOK::AmpStar),
      std::make_pair("&+", CCOK::AmpPlus),
      std::make_pair("&-", CCOK::AmpMinus),
      std::make_pair("&=", CCOK::AmpEq),
      std::make_pair("(", CCOK::LParen),
      std::make_pair("*", CCOK::Star),
      std::make_pair("*=", CCOK::StarEq),
      std::make_pair("+", CCOK::Plus),
      std::make_pair("+=", CCOK::PlusEq),
      std::make_pair("-", CCOK::Minus),
      std::make_pair("-=", CCOK::MinusEq),
      std::make_pair(".", CCOK::Dot),
      std::make_pair("...", CCOK::DotDotDot),
      std::make_pair("..<", CCOK::DotDotLess),
      std::make_pair("/", CCOK::Slash),
      std::make_pair("/=", CCOK::SlashEq),
      std::make_pair("<", CCOK::Less),
      std::make_pair("<<", CCOK::LessLess),
      std::make_pair("<<=", CCOK::LessLessEq),
      std::make_pair("<=", CCOK::LessEq),
      std::make_pair("=", CCOK::Eq),
      std::make_pair("==", CCOK::EqEq),
      std::make_pair("===", CCOK::EqEqEq),
      std::make_pair(">", CCOK::Greater),
      std::make_pair(">=", CCOK::GreaterEq),
      std::make_pair(">>", CCOK::GreaterGreater),
      std::make_pair(">>=", CCOK::GreaterGreaterEq),
      std::make_pair("?.", CCOK::QuestionDot),
      std::make_pair("^", CCOK::Caret),
      std::make_pair("^=", CCOK::CaretEq),
      std::make_pair("|", CCOK::Pipe),
      std::make_pair("|=", CCOK::PipeEq),
      std::make_pair("||", CCOK::PipePipe),
      std::make_pair("~=", CCOK::TildeEq),
  };

  auto I = std::lower_bound(
      std::begin(ops), std::end(ops), std::make_pair(name, CCOK::None),
      [](const OpPair &a, const OpPair &b) { return a.first < b.first; });

  if (I == std::end(ops) || I->first != name)
    return CCOK::Unknown;
  return I->second;
}

CodeCompletionDeclKind
ContextFreeCodeCompletionResult::getCodeCompletionDeclKind(const Decl *D) {
  switch (D->getKind()) {
  case DeclKind::Import:
  case DeclKind::Extension:
  case DeclKind::PatternBinding:
  case DeclKind::EnumCase:
  case DeclKind::TopLevelCode:
  case DeclKind::Missing:
  case DeclKind::MissingMember:
  case DeclKind::OpaqueType:
  case DeclKind::BuiltinTuple:
  case DeclKind::MacroExpansion:
  case DeclKind::Using:
    llvm_unreachable("not expecting such a declaration result");
  case DeclKind::Module:
    return CodeCompletionDeclKind::Module;
  case DeclKind::TypeAlias:
    return CodeCompletionDeclKind::TypeAlias;
  case DeclKind::AssociatedType:
    return CodeCompletionDeclKind::AssociatedType;
  case DeclKind::GenericTypeParam:
    return CodeCompletionDeclKind::GenericTypeParam;
  case DeclKind::Enum:
    return CodeCompletionDeclKind::Enum;
  case DeclKind::Struct:
    return CodeCompletionDeclKind::Struct;
  case DeclKind::Class:
    if (cast<ClassDecl>(D)->isAnyActor()) {
      return CodeCompletionDeclKind::Actor;
    } else {
      return CodeCompletionDeclKind::Class;
    }
  case DeclKind::Protocol:
    return CodeCompletionDeclKind::Protocol;
  case DeclKind::Var:
  case DeclKind::Param: {
    auto DC = D->getDeclContext();
    if (DC->isTypeContext()) {
      if (cast<VarDecl>(D)->isStatic())
        return CodeCompletionDeclKind::StaticVar;
      else
        return CodeCompletionDeclKind::InstanceVar;
    }
    if (DC->isLocalContext())
      return CodeCompletionDeclKind::LocalVar;
    return CodeCompletionDeclKind::GlobalVar;
  }
  case DeclKind::Constructor:
    return CodeCompletionDeclKind::Constructor;
  case DeclKind::Destructor:
    return CodeCompletionDeclKind::Destructor;
  case DeclKind::Accessor:
  case DeclKind::Func: {
    auto DC = D->getDeclContext();
    auto FD = cast<FuncDecl>(D);
    if (DC->isTypeContext()) {
      if (FD->isStatic())
        return CodeCompletionDeclKind::StaticMethod;
      return CodeCompletionDeclKind::InstanceMethod;
    }
    if (FD->isOperator()) {
      if (auto op = FD->getOperatorDecl()) {
        switch (op->getKind()) {
        case DeclKind::PrefixOperator:
          return CodeCompletionDeclKind::PrefixOperatorFunction;
        case DeclKind::PostfixOperator:
          return CodeCompletionDeclKind::PostfixOperatorFunction;
        case DeclKind::InfixOperator:
          return CodeCompletionDeclKind::InfixOperatorFunction;
        default:
          llvm_unreachable("unexpected operator kind");
        }
      } else {
        return CodeCompletionDeclKind::InfixOperatorFunction;
      }
    }
    return CodeCompletionDeclKind::FreeFunction;
  }
  case DeclKind::InfixOperator:
    return CodeCompletionDeclKind::InfixOperatorFunction;
  case DeclKind::PrefixOperator:
    return CodeCompletionDeclKind::PrefixOperatorFunction;
  case DeclKind::PostfixOperator:
    return CodeCompletionDeclKind::PostfixOperatorFunction;
  case DeclKind::PrecedenceGroup:
    return CodeCompletionDeclKind::PrecedenceGroup;
  case DeclKind::EnumElement:
    return CodeCompletionDeclKind::EnumElement;
  case DeclKind::Subscript:
    return CodeCompletionDeclKind::Subscript;
  case DeclKind::Macro:
    return CodeCompletionDeclKind::Macro;
  }
  llvm_unreachable("invalid DeclKind");
}

bool ContextFreeCodeCompletionResult::getDeclIsSystem(const Decl *D) {
  return D->getModuleContext()->isNonUserModule();
}

ContextualNotRecommendedReason
ContextFreeCodeCompletionResult::calculateContextualNotRecommendedReason(
    ContextualNotRecommendedReason explicitReason,
    bool canCurrDeclContextHandleAsync) const {
  if (explicitReason != ContextualNotRecommendedReason::None) {
    return explicitReason;
  }
  if (HasAsyncAlternative && canCurrDeclContextHandleAsync) {
    return ContextualNotRecommendedReason::
        NonAsyncAlternativeUsedInAsyncContext;
  }
  return ContextualNotRecommendedReason::None;
}

CodeCompletionResultTypeRelation
ContextFreeCodeCompletionResult::calculateContextualTypeRelation(
    const DeclContext *dc, const ExpectedTypeContext *typeContext,
    const USRBasedTypeContext *usrTypeContext) const {
  CodeCompletionResultTypeRelation typeRelation =
      getResultType().calculateTypeRelation(typeContext, dc, usrTypeContext);
  if (typeRelation >= CodeCompletionResultTypeRelation::Convertible ||
      !typeContext)
    return typeRelation;

  CodeCompletionMacroRoles expectedRoles =
      getCompletionMacroRoles(typeContext->getExpectedCustomAttributeKinds());
  if (MacroRoles & expectedRoles)
    return CodeCompletionResultTypeRelation::Convertible;
  return typeRelation;
}

// MARK: - CodeCompletionResult

const Decl *CodeCompletionResult::getAssociatedDecl() const {
  if (auto *Ctx = DeclOrCtx.dyn_cast<ASTContext *>()) {
    auto SwiftUSR = ContextFree.getSwiftUSR();
    if (SwiftUSR.empty())
      return nullptr;

    DeclOrCtx = Demangle::getDeclForUSR(*Ctx, SwiftUSR);
  }

  return DeclOrCtx.dyn_cast<const Decl *>();
}

CodeCompletionResult *
CodeCompletionResult::withFlair(CodeCompletionFlair NewFlair,
                                CodeCompletionResultSink &Sink) const {
  return new (*Sink.Allocator)
      CodeCompletionResult(ContextFree, DeclOrCtx, SemanticContext, NewFlair,
                           NumBytesToErase, TypeDistance, NotRecommended);
}

CodeCompletionResult *
CodeCompletionResult::withContextFreeResultSemanticContextAndFlair(
    const ContextFreeCodeCompletionResult &NewContextFree,
    SemanticContextKind NewSemanticContext, CodeCompletionFlair NewFlair,
    CodeCompletionResultSink &Sink) const {
  return new (*Sink.Allocator) CodeCompletionResult(
      NewContextFree, DeclOrCtx, NewSemanticContext, NewFlair, NumBytesToErase,
      TypeDistance, NotRecommended);
}

std::pair<CodeCompletionDiagnosticSeverity, NullTerminatedStringRef>
CodeCompletionResult::getContextualDiagnosticSeverityAndMessage(
    SmallVectorImpl<char> &Scratch, const ASTContext &Ctx) const {
  llvm::raw_svector_ostream Out(Scratch);
  CodeCompletionDiagnosticSeverity Severity;
  getContextualCompletionDiagnostics(
      NotRecommended, ContextFree.getNameForDiagnostics(), Severity, Out, Ctx);
  Out << '\0';
  NullTerminatedStringRef Message(Out.str().data(), Out.str().size() - 1);
  return std::make_pair(Severity, Message);
}

void CodeCompletionResult::printPrefix(raw_ostream &OS) const {
  llvm::SmallString<64> Prefix;
  switch (getKind()) {
  case CodeCompletionResultKind::Declaration:
    Prefix.append("Decl");
    switch (getAssociatedDeclKind()) {
    case CodeCompletionDeclKind::Class:
      Prefix.append("[Class]");
      break;
    case CodeCompletionDeclKind::Actor:
      Prefix.append("[Actor]");
      break;
    case CodeCompletionDeclKind::Struct:
      Prefix.append("[Struct]");
      break;
    case CodeCompletionDeclKind::Enum:
      Prefix.append("[Enum]");
      break;
    case CodeCompletionDeclKind::EnumElement:
      Prefix.append("[EnumElement]");
      break;
    case CodeCompletionDeclKind::Protocol:
      Prefix.append("[Protocol]");
      break;
    case CodeCompletionDeclKind::TypeAlias:
      Prefix.append("[TypeAlias]");
      break;
    case CodeCompletionDeclKind::AssociatedType:
      Prefix.append("[AssociatedType]");
      break;
    case CodeCompletionDeclKind::GenericTypeParam:
      Prefix.append("[GenericTypeParam]");
      break;
    case CodeCompletionDeclKind::Constructor:
      Prefix.append("[Constructor]");
      break;
    case CodeCompletionDeclKind::Destructor:
      Prefix.append("[Destructor]");
      break;
    case CodeCompletionDeclKind::Subscript:
      Prefix.append("[Subscript]");
      break;
    case CodeCompletionDeclKind::StaticMethod:
      Prefix.append("[StaticMethod]");
      break;
    case CodeCompletionDeclKind::InstanceMethod:
      Prefix.append("[InstanceMethod]");
      break;
    case CodeCompletionDeclKind::PrefixOperatorFunction:
      Prefix.append("[PrefixOperatorFunction]");
      break;
    case CodeCompletionDeclKind::PostfixOperatorFunction:
      Prefix.append("[PostfixOperatorFunction]");
      break;
    case CodeCompletionDeclKind::InfixOperatorFunction:
      Prefix.append("[InfixOperatorFunction]");
      break;
    case CodeCompletionDeclKind::FreeFunction:
      Prefix.append("[FreeFunction]");
      break;
    case CodeCompletionDeclKind::StaticVar:
      Prefix.append("[StaticVar]");
      break;
    case CodeCompletionDeclKind::InstanceVar:
      Prefix.append("[InstanceVar]");
      break;
    case CodeCompletionDeclKind::LocalVar:
      Prefix.append("[LocalVar]");
      break;
    case CodeCompletionDeclKind::GlobalVar:
      Prefix.append("[GlobalVar]");
      break;
    case CodeCompletionDeclKind::Module:
      Prefix.append("[Module]");
      break;
    case CodeCompletionDeclKind::PrecedenceGroup:
      Prefix.append("[PrecedenceGroup]");
      break;
    case CodeCompletionDeclKind::Macro:
      Prefix.append("[Macro]");
      break;
    }
    break;
  case CodeCompletionResultKind::Keyword:
    Prefix.append("Keyword");
    switch (getKeywordKind()) {
    case CodeCompletionKeywordKind::None:
      break;
#define KEYWORD(X)                                                             \
  case CodeCompletionKeywordKind::kw_##X:                                      \
    Prefix.append("[" #X "]");                                                 \
    break;
#define POUND_KEYWORD(X)                                                       \
  case CodeCompletionKeywordKind::pound_##X:                                   \
    Prefix.append("[#" #X "]");                                                \
    break;
#include "swift/AST/TokenKinds.def"
    }
    break;
  case CodeCompletionResultKind::Pattern:
    Prefix.append("Pattern");
    break;
  case CodeCompletionResultKind::Literal:
    Prefix.append("Literal");
    switch (getLiteralKind()) {
    case CodeCompletionLiteralKind::ArrayLiteral:
      Prefix.append("[Array]");
      break;
    case CodeCompletionLiteralKind::BooleanLiteral:
      Prefix.append("[Boolean]");
      break;
    case CodeCompletionLiteralKind::ColorLiteral:
      Prefix.append("[_Color]");
      break;
    case CodeCompletionLiteralKind::ImageLiteral:
      Prefix.append("[_Image]");
      break;
    case CodeCompletionLiteralKind::DictionaryLiteral:
      Prefix.append("[Dictionary]");
      break;
    case CodeCompletionLiteralKind::IntegerLiteral:
      Prefix.append("[Integer]");
      break;
    case CodeCompletionLiteralKind::NilLiteral:
      Prefix.append("[Nil]");
      break;
    case CodeCompletionLiteralKind::StringLiteral:
      Prefix.append("[String]");
      break;
    case CodeCompletionLiteralKind::Tuple:
      Prefix.append("[Tuple]");
      break;
    }
    break;
  case CodeCompletionResultKind::BuiltinOperator:
    Prefix.append("BuiltinOperator");
    break;
  }
  Prefix.append("/");
  switch (getSemanticContext()) {
  case SemanticContextKind::None:
    Prefix.append("None");
    break;
  case SemanticContextKind::Local:
    Prefix.append("Local");
    break;
  case SemanticContextKind::CurrentNominal:
    Prefix.append("CurrNominal");
    break;
  case SemanticContextKind::Super:
    Prefix.append("Super");
    break;
  case SemanticContextKind::OutsideNominal:
    Prefix.append("OutNominal");
    break;
  case SemanticContextKind::CurrentModule:
    Prefix.append("CurrModule");
    break;
  case SemanticContextKind::OtherModule:
    Prefix.append("OtherModule");
    if (!getModuleName().empty())
      Prefix.append((Twine("[") + StringRef(getModuleName()) + "]").str());
    break;
  }
  if (getFlair().toRaw()) {
    Prefix.append("/Flair[");
    bool isFirstFlair = true;
#define PRINT_FLAIR(KIND, NAME)                                                \
  if (getFlair().contains(CodeCompletionFlairBit::KIND)) {                     \
    if (isFirstFlair) {                                                        \
      isFirstFlair = false;                                                    \
    } else {                                                                   \
      Prefix.append(",");                                                      \
    }                                                                          \
    Prefix.append(NAME);                                                       \
  }
    PRINT_FLAIR(ExpressionSpecific, "ExprSpecific");
    PRINT_FLAIR(SuperChain, "SuperChain");
    PRINT_FLAIR(ArgumentLabels, "ArgLabels");
    PRINT_FLAIR(CommonKeywordAtCurrentPosition, "CommonKeyword")
    PRINT_FLAIR(RareKeywordAtCurrentPosition, "RareKeyword")
    PRINT_FLAIR(RareTypeAtCurrentPosition, "RareType")
    PRINT_FLAIR(ExpressionAtNonScriptOrMainFileScope, "ExprAtFileScope")
    Prefix.append("]");
  }
  if (isNotRecommended())
    Prefix.append("/NotRecommended");
  if (isSystem())
    Prefix.append("/IsSystem");
  if (NumBytesToErase != 0) {
    Prefix.append("/Erase[");
    Prefix.append(Twine(NumBytesToErase).str());
    Prefix.append("]");
  }
  switch (getExpectedTypeRelation()) {
  case CodeCompletionResultTypeRelation::Invalid:
    Prefix.append("/TypeRelation[Invalid]");
    break;
  case CodeCompletionResultTypeRelation::Convertible:
    Prefix.append("/TypeRelation[Convertible]");
    break;
  case CodeCompletionResultTypeRelation::NotApplicable:
  case CodeCompletionResultTypeRelation::Unknown:
  case CodeCompletionResultTypeRelation::Unrelated:
    break;
  }

  Prefix.append(": ");
  while (Prefix.size() < 36) {
    Prefix.append(" ");
  }
  OS << Prefix;
}

void CodeCompletionResult::dump() const {
  printPrefix(llvm::errs());
  getCompletionString()->print(llvm::errs());
  llvm::errs() << "\n";
}
