text: #include "swift/AST/ASTPrinter.h"
  #include "swift/AST/Decl.h"
  #include "swift/AST/NameLookup.h"
  #include "swift/Basic/SourceManager.h"
  #include "swift/Frontend/Frontend.h"
  #include "swift/Frontend/PrintingDiagnosticConsumer.h"
  #include "swift/IDE/CommentConversion.h"
  #include "swift/IDE/Utils.h"
  #include "swift/Markup/XMLUtils.h"
  
  #include "clang/AST/ASTContext.h"
  #include "clang/AST/DeclObjC.h"
  #include "clang/Basic/Module.h"
  #include "clang/Index/USRGeneration.h"
  #include "clang/Lex/Lexer.h"
  
  #include "llvm/Support/MemoryBuffer.h"
  
  using namespace swift;
  using namespace swift::ide;
  
  Optional<std::pair<unsigned, unsigned>> swift::ide::parseLineCol(StringRef LineCol) {
    unsigned Line, Col;
    size_t ColonIdx = LineCol.find(':');
    if (ColonIdx == StringRef::npos) {
      llvm::errs() << "wrong pos format, it should be '<line>:<column'\n";
      return None;
    }
    if (LineCol.substr(0, ColonIdx).getAsInteger(10, Line)) {
      llvm::errs() << "wrong pos format, it should be '<line>:<column'\n";
      return None;
    }
    if (LineCol.substr(ColonIdx+1).getAsInteger(10, Col)) {
      llvm::errs() << "wrong pos format, it should be '<line>:<column'\n";
      return None;
    }
  
    if (Line == 0 || Col == 0) {
      llvm::errs() << "wrong pos format, line/col should start from 1\n";
      return None;
    }
  
    return std::make_pair(Line, Col);
  }
  
  void XMLEscapingPrinter::printText(StringRef Text) {
    llvm::markup::appendWithXMLEscaping(OS, Text);
  }
  
  void XMLEscapingPrinter::printXML(StringRef Text) {
    OS << Text;
  }
  
  SourceManager &SemaLocResolver::getSourceMgr() const
  {
    return SrcFile.getASTContext().SourceMgr;
  }
  
  bool SemaLocResolver::tryResolve(ValueDecl *D, TypeDecl *CtorTyRef,
                  SourceLoc Loc, bool IsRef, Type Ty) {
    if (!D->hasName())
      return false;
  
    if (Loc == LocToResolve) {
      SemaTok = { D, CtorTyRef, Loc, IsRef, Ty };
      return true;
    }
    return false;
  }
  
  bool SemaLocResolver::tryResolve(ModuleEntity Mod, SourceLoc Loc) {
    if (Loc == LocToResolve) {
      SemaTok = { Mod, Loc };
      return true;
    }
    return false;
  }
  
  bool SemaLocResolver::visitSubscriptReference(ValueDecl *D, CharSourceRange Range,
                                                bool IsOpenBracket) {
    // We should treat both open and close brackets equally
    return visitDeclReference(D, Range, nullptr, Type());
  }
  
  SemaToken SemaLocResolver::resolve(SourceLoc Loc) {
    assert(Loc.isValid());
    LocToResolve = Loc;
    SemaTok = SemaToken();
    walk(SrcFile);
    return SemaTok;
  }
  
  bool SemaLocResolver::walkToDeclPre(Decl *D, CharSourceRange Range) {
    if (!rangeContainsLoc(D->getSourceRange()))
      return false;
  
    if (isa<ExtensionDecl>(D))
      return true;
  
    if (ValueDecl *VD = dyn_cast<ValueDecl>(D))
      return !tryResolve(VD, /*CtorTyRef=*/nullptr, Range.getStart(),
                         /*IsRef=*/false);
  
    return true;
  }
  
  bool SemaLocResolver::walkToDeclPost(Decl *D) {
    if (isDone())
      return false;
    if (getSourceMgr().isBeforeInBuffer(LocToResolve, D->getStartLoc()))
      return false;
    return true;
  }
  
  bool SemaLocResolver::walkToStmtPre(Stmt *S) {
    // FIXME: Even implicit Stmts should have proper ranges that include any
    // non-implicit Stmts (fix Stmts created for lazy vars).
    if (!S->isImplicit() && !rangeContainsLoc(S->getSourceRange()))
      return false;
    return true;
  }
  
  bool SemaLocResolver::walkToStmtPost(Stmt *S) {
    if (isDone())
      return false;
    // FIXME: Even implicit Stmts should have proper ranges that include any
    // non-implicit Stmts (fix Stmts created for lazy vars).
    if (!S->isImplicit() && getSourceMgr().isBeforeInBuffer(LocToResolve,
                                                            S->getStartLoc()))
      return false;
    return true;
  }
  
  bool SemaLocResolver::visitDeclReference(ValueDecl *D, CharSourceRange Range,
                                           TypeDecl *CtorTyRef, Type T) {
    if (isDone())
      return false;
    return !tryResolve(D, CtorTyRef, Range.getStart(), /*IsRef=*/true, T);
  }
  
  bool SemaLocResolver::visitCallArgName(Identifier Name, CharSourceRange Range,
                                         ValueDecl *D) {
    if (isDone())
      return false;
    bool Found = tryResolve(D, nullptr, Range.getStart(), /*IsRef=*/true);
    if (Found)
      SemaTok.IsKeywordArgument = true;
    return !Found;
  }
  
  bool SemaLocResolver::visitModuleReference(ModuleEntity Mod,
                                             CharSourceRange Range) {
    if (isDone())
      return false;
    if (Mod.isBuiltinModule())
      return true; // Ignore.
    return !tryResolve(Mod, Range.getStart());
  }
  
  void swift::ide::getLocationInfoForClangNode(ClangNode ClangNode,
                                               ClangImporter *Importer,
                    llvm::Optional<std::pair<unsigned, unsigned>> &DeclarationLoc,
                                               StringRef &Filename) {
    clang::ASTContext &ClangCtx = Importer->getClangASTContext();
    clang::SourceManager &ClangSM = ClangCtx.getSourceManager();
  
    clang::SourceRange SR = ClangNode.getLocation();
    if (auto MD = dyn_cast_or_null<clang::ObjCMethodDecl>(ClangNode.getAsDecl())) {
      SR = clang::SourceRange(MD->getSelectorStartLoc(),
                              MD->getDeclaratorEndLoc());
    }
  
    clang::CharSourceRange CharRange =
        clang::Lexer::makeFileCharRange(clang::CharSourceRange::getTokenRange(SR),
                                        ClangSM, ClangCtx.getLangOpts());
    if (CharRange.isInvalid())
      return;
  
    std::pair<clang::FileID, unsigned>
        Decomp = ClangSM.getDecomposedLoc(CharRange.getBegin());
    if (!Decomp.first.isInvalid()) {
      if (auto FE = ClangSM.getFileEntryForID(Decomp.first)) {
        Filename = FE->getName();
  
        std::pair<clang::FileID, unsigned>
            EndDecomp = ClangSM.getDecomposedLoc(CharRange.getEnd());
  
        DeclarationLoc = { Decomp.second, EndDecomp.second-Decomp.second };
      }
    }
  }
  
  static unsigned getCharLength(SourceManager &SM, SourceRange TokenRange) {
    SourceLoc CharEndLoc = Lexer::getLocForEndOfToken(SM, TokenRange.End);
    return SM.getByteDistance(TokenRange.Start, CharEndLoc);
  }
  
  void swift::ide::getLocationInfo(const ValueDecl *VD,
                    llvm::Optional<std::pair<unsigned, unsigned>> &DeclarationLoc,
                                   StringRef &Filename) {
    ASTContext &Ctx = VD->getASTContext();
    SourceManager &SM = Ctx.SourceMgr;
  
    auto ClangNode = VD->getClangNode();
  
    if (VD->getLoc().isValid()) {
      unsigned NameLen;
      if (auto FD = dyn_cast<AbstractFunctionDecl>(VD)) {
        SourceRange R = FD->getSignatureSourceRange();
        if (R.isInvalid())
          return;
        NameLen = getCharLength(SM, R);
      } else {
        if (VD->hasName()) {
          NameLen = VD->getName().getLength();
        } else {
          NameLen = getCharLength(SM, VD->getLoc());
        }
      }
  
      unsigned DeclBufID = SM.findBufferContainingLoc(VD->getLoc());
      DeclarationLoc = { SM.getLocOffsetInBuffer(VD->getLoc(), DeclBufID),
                         NameLen };
      Filename = SM.getIdentifierForBuffer(DeclBufID);
  
    } else if (ClangNode) {
      ClangImporter *Importer =
          static_cast<ClangImporter*>(Ctx.getClangModuleLoader());
      return getLocationInfoForClangNode(ClangNode, Importer,
                                         DeclarationLoc, Filename);
    }
  }
  