| //===--- Utils.cpp - Misc utilities ---------------------------------------===// |
| // |
| // 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/IDE/Utils.h" |
| #include "swift/Basic/Edit.h" |
| #include "swift/Basic/SourceManager.h" |
| #include "swift/Basic/Platform.h" |
| #include "swift/ClangImporter/ClangModule.h" |
| #include "swift/Driver/FrontendUtil.h" |
| #include "swift/Frontend/Frontend.h" |
| #include "swift/Parse/Parser.h" |
| #include "swift/Subsystems.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/DeclObjC.h" |
| #include "clang/Basic/TargetInfo.h" |
| #include "clang/CodeGen/ObjectFilePCHContainerOperations.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Frontend/TextDiagnosticBuffer.h" |
| #include "clang/Lex/PreprocessorOptions.h" |
| #include "clang/Rewrite/Core/RewriteBuffer.h" |
| #include "clang/Serialization/ASTReader.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/Support/Compiler.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/Path.h" |
| |
| using namespace swift; |
| using namespace ide; |
| |
| static const char *skipStringInCode(const char *p, const char *End); |
| |
| static const char *skipParenExpression(const char *p, const char *End) { |
| const char *e = p; |
| if (*e == '(') { |
| uint32_t ParenCount = 1; |
| bool done = false; |
| for (++e; e < End; ++e) { |
| switch (*e) { |
| case ')': |
| done = --ParenCount == 0; |
| break; |
| |
| case '(': |
| ++ParenCount; |
| break; |
| |
| case '"': |
| e = skipStringInCode (e, End); |
| break; |
| |
| default: |
| break; |
| } |
| // If "done" is true make sure we don't increment "e" |
| if (done) |
| break; |
| } |
| } |
| if (e >= End) |
| return End; |
| return e; |
| } |
| |
| static const char *skipStringInCode(const char *p, const char *End) { |
| const char *e = p; |
| if (*e == '"') { |
| bool done = false; |
| for (++e; e < End; ++e) { |
| switch (*e) { |
| case '"': |
| done = true; |
| break; |
| |
| case '\\': |
| ++e; |
| if (e >= End) |
| done = true; |
| else if (*e == '(') |
| e = skipParenExpression (e, End); |
| break; |
| |
| default: |
| break; |
| } |
| // If "done" is true make sure we don't increment "e" |
| if (done) |
| break; |
| } |
| } |
| if (e >= End) |
| return End; |
| return e; |
| } |
| |
| SourceCompleteResult |
| ide::isSourceInputComplete(std::unique_ptr<llvm::MemoryBuffer> MemBuf, |
| SourceFileKind SFKind) { |
| SourceManager SM; |
| auto BufferID = SM.addNewSourceBuffer(std::move(MemBuf)); |
| ParserUnit Parse(SM, SFKind, BufferID); |
| Parse.parse(); |
| SourceCompleteResult SCR; |
| SCR.IsComplete = !Parse.getParser().isInputIncomplete(); |
| |
| // Use the same code that was in the REPL code to track the indent level |
| // for now. In the future we should get this from the Parser if possible. |
| |
| CharSourceRange entireRange = SM.getRangeForBuffer(BufferID); |
| StringRef Buffer = SM.extractText(entireRange); |
| const char *SourceStart = Buffer.data(); |
| const char *SourceEnd = Buffer.data() + Buffer.size(); |
| const char *LineStart = SourceStart; |
| const char *LineSourceStart = nullptr; |
| uint32_t LineIndent = 0; |
| struct IndentInfo { |
| StringRef Prefix; |
| uint32_t Indent; |
| IndentInfo(const char *s, size_t n, uint32_t i) : |
| Prefix(s, n), |
| Indent(i) {} |
| }; |
| SmallVector<IndentInfo, 4> IndentInfos; |
| for (const char *p = SourceStart; p<SourceEnd; ++p) { |
| switch (*p) { |
| case '\r': |
| case '\n': |
| LineIndent = 0; |
| LineSourceStart = nullptr; |
| LineStart = p + 1; |
| break; |
| |
| case '"': |
| p = skipStringInCode (p, SourceEnd); |
| break; |
| |
| case '{': |
| case '(': |
| case '[': |
| ++LineIndent; |
| if (LineSourceStart == nullptr) |
| IndentInfos.push_back(IndentInfo(LineStart, |
| p - LineStart, |
| LineIndent)); |
| else |
| IndentInfos.push_back(IndentInfo(LineStart, |
| LineSourceStart - LineStart, |
| LineIndent)); |
| break; |
| |
| case '}': |
| case ')': |
| case ']': |
| if (LineIndent > 0) |
| --LineIndent; |
| if (!IndentInfos.empty()) |
| IndentInfos.pop_back(); |
| break; |
| |
| default: |
| if (LineSourceStart == nullptr && !isspace(*p)) |
| LineSourceStart = p; |
| break; |
| } |
| if (*p == '\0') |
| break; |
| } |
| if (!IndentInfos.empty()) { |
| SCR.IndentPrefix = IndentInfos.back().Prefix.str(); |
| // Trim off anything that follows a non-space character |
| const size_t pos = SCR.IndentPrefix.find_first_not_of(" \t"); |
| if (pos != std::string::npos) |
| SCR.IndentPrefix.erase(pos); |
| SCR.IndentLevel = IndentInfos.back().Indent; |
| } |
| return SCR; |
| } |
| |
| SourceCompleteResult |
| ide::isSourceInputComplete(StringRef Text,SourceFileKind SFKind) { |
| return ide::isSourceInputComplete(llvm::MemoryBuffer::getMemBufferCopy(Text), |
| SFKind); |
| } |
| |
| static FrontendInputsAndOutputs resolveSymbolicLinksInInputs( |
| FrontendInputsAndOutputs &inputsAndOutputs, StringRef UnresolvedPrimaryFile, |
| llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem, |
| std::string &Error) { |
| assert(FileSystem); |
| |
| llvm::SmallString<128> PrimaryFile; |
| if (auto err = FileSystem->getRealPath(UnresolvedPrimaryFile, PrimaryFile)) |
| PrimaryFile = UnresolvedPrimaryFile; |
| |
| unsigned primaryCount = 0; |
| // FIXME: The frontend should be dealing with symlinks, maybe similar to |
| // clang's FileManager ? |
| FrontendInputsAndOutputs replacementInputsAndOutputs; |
| for (const InputFile &input : inputsAndOutputs.getAllInputs()) { |
| llvm::SmallString<128> newFilename; |
| if (auto err = FileSystem->getRealPath(input.getFileName(), newFilename)) |
| newFilename = input.getFileName(); |
| llvm::sys::path::native(newFilename); |
| bool newIsPrimary = input.isPrimary() || |
| (!PrimaryFile.empty() && PrimaryFile == newFilename); |
| if (newIsPrimary) { |
| ++primaryCount; |
| } |
| assert(primaryCount < 2 && "cannot handle multiple primaries"); |
| |
| replacementInputsAndOutputs.addInput( |
| InputFile(newFilename.str(), newIsPrimary, input.getBuffer())); |
| } |
| |
| if (PrimaryFile.empty() || primaryCount == 1) { |
| return replacementInputsAndOutputs; |
| } |
| |
| llvm::SmallString<64> Err; |
| llvm::raw_svector_ostream OS(Err); |
| OS << "'" << PrimaryFile << "' is not part of the input files"; |
| Error = std::string(OS.str()); |
| return replacementInputsAndOutputs; |
| } |
| |
| static void disableExpensiveSILOptions(SILOptions &Opts) { |
| // Disable the sanitizers. |
| Opts.Sanitizers = {}; |
| |
| // Disable PGO and code coverage. |
| Opts.GenerateProfile = false; |
| Opts.EmitProfileCoverageMapping = false; |
| Opts.UseProfile = ""; |
| } |
| |
| namespace { |
| class StreamDiagConsumer : public DiagnosticConsumer { |
| llvm::raw_ostream &OS; |
| |
| public: |
| StreamDiagConsumer(llvm::raw_ostream &OS) : OS(OS) {} |
| |
| void handleDiagnostic(SourceManager &SM, |
| const DiagnosticInfo &Info) override { |
| // FIXME: Print location info if available. |
| switch (Info.Kind) { |
| case DiagnosticKind::Error: |
| OS << "error: "; |
| break; |
| case DiagnosticKind::Warning: |
| OS << "warning: "; |
| break; |
| case DiagnosticKind::Note: |
| OS << "note: "; |
| break; |
| case DiagnosticKind::Remark: |
| OS << "remark: "; |
| break; |
| } |
| DiagnosticEngine::formatDiagnosticText(OS, Info.FormatString, |
| Info.FormatArgs); |
| } |
| }; |
| } // end anonymous namespace |
| |
| bool ide::initCompilerInvocation( |
| CompilerInvocation &Invocation, ArrayRef<const char *> OrigArgs, |
| DiagnosticEngine &Diags, StringRef UnresolvedPrimaryFile, |
| llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem, |
| // SWIFT_ENABLE_TENSORFLOW |
| llvm::IntrusiveRefCntPtr<clang::InMemoryOutputFileSystem> InMemoryOutputFileSystem, |
| // SWIFT_ENABLE_TENSORFLOW END |
| const std::string &runtimeResourcePath, |
| const std::string &diagnosticDocumentationPath, |
| bool shouldOptimizeForIDE, time_t sessionTimestamp, std::string &Error) { |
| SmallVector<const char *, 16> Args; |
| // Make sure to put '-resource-dir' and '-diagnostic-documentation-path' at |
| // the top to allow overriding them with the passed in arguments. |
| Args.push_back("-resource-dir"); |
| Args.push_back(runtimeResourcePath.c_str()); |
| Args.push_back("-Xfrontend"); |
| Args.push_back("-diagnostic-documentation-path"); |
| Args.push_back("-Xfrontend"); |
| Args.push_back(diagnosticDocumentationPath.c_str()); |
| Args.append(OrigArgs.begin(), OrigArgs.end()); |
| |
| SmallString<32> ErrStr; |
| llvm::raw_svector_ostream ErrOS(ErrStr); |
| StreamDiagConsumer DiagConsumer(ErrOS); |
| Diags.addConsumer(DiagConsumer); |
| |
| bool HadError = driver::getSingleFrontendInvocationFromDriverArguments( |
| Args, Diags, [&](ArrayRef<const char *> FrontendArgs) { |
| return Invocation.parseArgs(FrontendArgs, Diags); |
| }, /*ForceNoOutputs=*/true); |
| |
| // Remove the StreamDiagConsumer as it's no longer needed. |
| Diags.removeConsumer(DiagConsumer); |
| |
| if (HadError) { |
| Error = std::string(ErrOS.str()); |
| return true; |
| } |
| |
| Invocation.getFrontendOptions().InputsAndOutputs = |
| resolveSymbolicLinksInInputs( |
| Invocation.getFrontendOptions().InputsAndOutputs, |
| UnresolvedPrimaryFile, FileSystem, Error); |
| |
| // SourceKit functionalities want to proceed even if there are missing inputs. |
| Invocation.getFrontendOptions().InputsAndOutputs |
| .setShouldRecoverMissingInputs(); |
| |
| if (!Error.empty()) |
| return true; |
| |
| ClangImporterOptions &ImporterOpts = Invocation.getClangImporterOptions(); |
| ImporterOpts.DetailedPreprocessingRecord = true; |
| // SWIFT_ENABLE_TENSORFLOW |
| ImporterOpts.InMemoryOutputFileSystem = InMemoryOutputFileSystem; |
| // SWIFT_ENABLE_TENSORFLOW END |
| |
| assert(!Invocation.getModuleName().empty()); |
| |
| auto &LangOpts = Invocation.getLangOptions(); |
| LangOpts.AttachCommentsToDecls = true; |
| LangOpts.DiagnosticsEditorMode = true; |
| LangOpts.CollectParsedToken = true; |
| if (LangOpts.PlaygroundTransform) { |
| // The playground instrumenter changes the AST in ways that disrupt the |
| // SourceKit functionality. Since we don't need the instrumenter, and all we |
| // actually need is the playground semantics visible to the user, like |
| // silencing the "expression resolves to an unused l-value" error, disable it. |
| LangOpts.PlaygroundTransform = false; |
| } |
| |
| // Disable the index-store functionality for the sourcekitd requests. |
| auto &FrontendOpts = Invocation.getFrontendOptions(); |
| FrontendOpts.IndexStorePath.clear(); |
| ImporterOpts.IndexStorePath.clear(); |
| |
| // Force the action type to be -typecheck. This affects importing the |
| // SwiftONoneSupport module. |
| FrontendOpts.RequestedAction = FrontendOptions::ActionType::Typecheck; |
| |
| // We don't care about LLVMArgs |
| FrontendOpts.LLVMArgs.clear(); |
| |
| // SwiftSourceInfo files provide source location information for decls coming |
| // from loaded modules. For most IDE use cases it either has an undesirable |
| // impact on performance with no benefit (code completion), results in stale |
| // locations being used instead of more up-to-date indexer locations (cursor |
| // info), or has no observable effect (diagnostics, which are filtered to just |
| // those with a location in the primary file, and everything else). |
| if (shouldOptimizeForIDE) |
| FrontendOpts.IgnoreSwiftSourceInfo = true; |
| |
| // To save the time for module validation, consider the lifetime of ASTManager |
| // as a single build session. |
| // NOTE: Do this only if '-disable-modules-validate-system-headers' is *not* |
| // explicitly enabled. |
| auto &SearchPathOpts = Invocation.getSearchPathOptions(); |
| if (!SearchPathOpts.DisableModulesValidateSystemDependencies) { |
| // NOTE: 'SessionTimestamp - 1' because clang compares it with '<=' that may |
| // cause unnecessary validations if they happens within one second |
| // from the SourceKit startup. |
| ImporterOpts.ExtraArgs.push_back("-fbuild-session-timestamp=" + |
| std::to_string(sessionTimestamp - 1)); |
| ImporterOpts.ExtraArgs.push_back( |
| "-fmodules-validate-once-per-build-session"); |
| |
| SearchPathOpts.DisableModulesValidateSystemDependencies = true; |
| } |
| |
| // Disable expensive SIL options to reduce time spent in SILGen. |
| disableExpensiveSILOptions(Invocation.getSILOptions()); |
| |
| return false; |
| } |
| |
| // Adjust the cc1 triple string we got from clang, to make sure it will be |
| // accepted when it goes through the swift clang importer. |
| static std::string adjustClangTriple(StringRef TripleStr) { |
| std::string Result; |
| llvm::raw_string_ostream OS(Result); |
| |
| llvm::Triple Triple(TripleStr); |
| switch (Triple.getSubArch()) { |
| case llvm::Triple::SubArchType::ARMSubArch_v7: |
| OS << "armv7"; break; |
| case llvm::Triple::SubArchType::ARMSubArch_v7s: |
| OS << "armv7s"; break; |
| case llvm::Triple::SubArchType::ARMSubArch_v7k: |
| OS << "armv7k"; break; |
| case llvm::Triple::SubArchType::ARMSubArch_v6: |
| OS << "armv6"; break; |
| case llvm::Triple::SubArchType::ARMSubArch_v6m: |
| OS << "armv6m"; break; |
| case llvm::Triple::SubArchType::ARMSubArch_v6k: |
| OS << "armv6k"; break; |
| case llvm::Triple::SubArchType::ARMSubArch_v6t2: |
| OS << "armv6t2"; break; |
| default: |
| // Adjust i386-macosx to x86_64 because there is no Swift stdlib for i386. |
| if ((Triple.getOS() == llvm::Triple::MacOSX || |
| Triple.getOS() == llvm::Triple::Darwin) && Triple.getArch() == llvm::Triple::x86) { |
| OS << "x86_64"; |
| } else { |
| OS << Triple.getArchName(); |
| } |
| break; |
| } |
| OS << '-' << Triple.getVendorName() << '-' << |
| Triple.getOSAndEnvironmentName(); |
| OS.flush(); |
| return Result; |
| } |
| |
| bool ide::initInvocationByClangArguments(ArrayRef<const char *> ArgList, |
| CompilerInvocation &Invok, |
| std::string &Error) { |
| llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts{ |
| new clang::DiagnosticOptions() |
| }; |
| |
| clang::TextDiagnosticBuffer DiagBuf; |
| llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> ClangDiags = |
| clang::CompilerInstance::createDiagnostics(DiagOpts.get(), |
| &DiagBuf, |
| /*ShouldOwnClient=*/false); |
| |
| // Clang expects this to be like an actual command line. So we need to pass in |
| // "clang" for argv[0]. |
| std::vector<const char *> ClangArgList; |
| ClangArgList.push_back("clang"); |
| ClangArgList.insert(ClangArgList.end(), ArgList.begin(), ArgList.end()); |
| |
| // Create a new Clang compiler invocation. |
| std::unique_ptr<clang::CompilerInvocation> ClangInvok = |
| clang::createInvocationFromCommandLine(ClangArgList, ClangDiags); |
| if (!ClangInvok || ClangDiags->hasErrorOccurred()) { |
| for (auto I = DiagBuf.err_begin(), E = DiagBuf.err_end(); I != E; ++I) { |
| Error += I->second; |
| Error += " "; |
| } |
| return true; |
| } |
| |
| auto &PPOpts = ClangInvok->getPreprocessorOpts(); |
| auto &HSOpts = ClangInvok->getHeaderSearchOpts(); |
| |
| Invok.setTargetTriple(adjustClangTriple(ClangInvok->getTargetOpts().Triple)); |
| if (!HSOpts.Sysroot.empty()) |
| Invok.setSDKPath(HSOpts.Sysroot); |
| if (!HSOpts.ModuleCachePath.empty()) |
| Invok.setClangModuleCachePath(HSOpts.ModuleCachePath); |
| |
| auto &CCArgs = Invok.getClangImporterOptions().ExtraArgs; |
| for (auto MacroEntry : PPOpts.Macros) { |
| std::string MacroFlag; |
| if (MacroEntry.second) |
| MacroFlag += "-U"; |
| else |
| MacroFlag += "-D"; |
| MacroFlag += MacroEntry.first; |
| CCArgs.push_back(MacroFlag); |
| } |
| |
| for (auto &Entry : HSOpts.UserEntries) { |
| switch (Entry.Group) { |
| case clang::frontend::Quoted: |
| CCArgs.push_back("-iquote"); |
| CCArgs.push_back(Entry.Path); |
| break; |
| case clang::frontend::IndexHeaderMap: |
| CCArgs.push_back("-index-header-map"); |
| LLVM_FALLTHROUGH; |
| case clang::frontend::Angled: { |
| std::string Flag; |
| if (Entry.IsFramework) |
| Flag += "-F"; |
| else |
| Flag += "-I"; |
| Flag += Entry.Path; |
| CCArgs.push_back(Flag); |
| break; |
| } |
| case clang::frontend::System: |
| if (Entry.IsFramework) |
| CCArgs.push_back("-iframework"); |
| else |
| CCArgs.push_back("-isystem"); |
| CCArgs.push_back(Entry.Path); |
| break; |
| case clang::frontend::ExternCSystem: |
| case clang::frontend::CSystem: |
| case clang::frontend::CXXSystem: |
| case clang::frontend::ObjCSystem: |
| case clang::frontend::ObjCXXSystem: |
| case clang::frontend::After: |
| break; |
| } |
| } |
| |
| if (!PPOpts.ImplicitPCHInclude.empty()) { |
| clang::FileSystemOptions FileSysOpts; |
| clang::FileManager FileMgr(FileSysOpts); |
| auto PCHContainerOperations = |
| std::make_shared<clang::PCHContainerOperations>(); |
| std::string HeaderFile = clang::ASTReader::getOriginalSourceFile( |
| PPOpts.ImplicitPCHInclude, FileMgr, |
| PCHContainerOperations->getRawReader(), *ClangDiags); |
| if (!HeaderFile.empty()) { |
| CCArgs.push_back("-include"); |
| CCArgs.push_back(std::move(HeaderFile)); |
| } |
| } |
| for (auto &Header : PPOpts.Includes) { |
| CCArgs.push_back("-include"); |
| CCArgs.push_back(Header); |
| } |
| |
| for (auto &Entry : HSOpts.ModulesIgnoreMacros) { |
| std::string Flag = "-fmodules-ignore-macro="; |
| Flag += Entry; |
| CCArgs.push_back(Flag); |
| } |
| |
| for (auto &Entry : HSOpts.VFSOverlayFiles) { |
| CCArgs.push_back("-ivfsoverlay"); |
| CCArgs.push_back(Entry); |
| } |
| |
| if (!ClangInvok->getLangOpts()->isCompilingModule()) { |
| CCArgs.push_back("-Xclang"); |
| llvm::SmallString<64> Str; |
| Str += "-fmodule-name="; |
| Str += ClangInvok->getLangOpts()->CurrentModule; |
| CCArgs.push_back(std::string(Str.str())); |
| } |
| |
| if (PPOpts.DetailedRecord) { |
| Invok.getClangImporterOptions().DetailedPreprocessingRecord = true; |
| } |
| |
| if (!ClangInvok->getFrontendOpts().Inputs.empty()) { |
| Invok.getFrontendOptions().ImplicitObjCHeaderPath = |
| ClangInvok->getFrontendOpts().Inputs[0].getFile().str(); |
| } |
| |
| return false; |
| } |
| |
| template <typename FnTy> |
| static void walkOverriddenClangDecls(const clang::NamedDecl *D, const FnTy &Fn){ |
| SmallVector<const clang::NamedDecl *, 8> OverDecls; |
| D->getASTContext().getOverriddenMethods(D, OverDecls); |
| for (auto Over : OverDecls) |
| Fn(Over); |
| for (auto Over : OverDecls) |
| walkOverriddenClangDecls(Over, Fn); |
| } |
| |
| void |
| ide::walkOverriddenDecls(const ValueDecl *VD, |
| llvm::function_ref<void(llvm::PointerUnion< |
| const ValueDecl*, const clang::NamedDecl*>)> Fn) { |
| for (auto CurrOver = VD; CurrOver; CurrOver = CurrOver->getOverriddenDecl()) { |
| if (CurrOver != VD) |
| Fn(CurrOver); |
| if (auto ClangD = |
| dyn_cast_or_null<clang::NamedDecl>(CurrOver->getClangDecl())) { |
| walkOverriddenClangDecls(ClangD, Fn); |
| return; |
| } |
| for (auto Conf: CurrOver->getSatisfiedProtocolRequirements()) |
| Fn(Conf); |
| } |
| } |
| |
| /// \returns true if a placeholder was found. |
| static bool findPlaceholder(StringRef Input, PlaceholderOccurrence &Occur) { |
| while (true) { |
| size_t Pos = Input.find("<#"); |
| if (Pos == StringRef::npos) |
| return false; |
| |
| const char *Begin = Input.begin() + Pos; |
| const char *Ptr = Begin + 2; |
| const char *End = Input.end(); |
| for (; Ptr < End-1; ++Ptr) { |
| if (*Ptr == '\n') |
| break; |
| if (Ptr[0] == '<' && Ptr[1] == '#') |
| break; |
| if (Ptr[0] == '#' && Ptr[1] == '>') { |
| // Found it. |
| Occur.FullPlaceholder = Input.substr(Pos, Ptr-Begin + 2); |
| Occur.PlaceholderContent = |
| Occur.FullPlaceholder.drop_front(2).drop_back(2); |
| return true; |
| } |
| } |
| |
| // Try again. |
| Input = Input.substr(Ptr - Input.begin()); |
| } |
| } |
| |
| std::unique_ptr<llvm::MemoryBuffer> |
| ide::replacePlaceholders(std::unique_ptr<llvm::MemoryBuffer> InputBuf, |
| llvm::function_ref<void(const PlaceholderOccurrence &)> Callback) { |
| StringRef Input = InputBuf->getBuffer(); |
| PlaceholderOccurrence Occur; |
| bool Found = findPlaceholder(Input, Occur); |
| if (!Found) |
| return InputBuf; |
| |
| std::unique_ptr<llvm::MemoryBuffer> NewBuf = |
| llvm::MemoryBuffer::getMemBufferCopy(InputBuf->getBuffer(), |
| InputBuf->getBufferIdentifier()); |
| |
| unsigned Counter = 0; |
| auto replacePlaceholder = [&](PlaceholderOccurrence &Occur) { |
| llvm::SmallString<10> Id; |
| Id = "$_"; |
| llvm::raw_svector_ostream(Id) << (Counter++); |
| assert(Occur.FullPlaceholder.size() >= 2); |
| if (Id.size() > Occur.FullPlaceholder.size()) { |
| // The identifier+counter exceeds placeholder size; replace it without |
| // using the counter. |
| Id = "$"; |
| Id.append(Occur.FullPlaceholder.size()-1, '_'); |
| } else { |
| Id.append(Occur.FullPlaceholder.size()-Id.size(), '_'); |
| } |
| assert(Id.size() == Occur.FullPlaceholder.size()); |
| |
| unsigned Offset = Occur.FullPlaceholder.data() - InputBuf->getBufferStart(); |
| char *Ptr = const_cast<char *>(NewBuf->getBufferStart()) + Offset; |
| std::copy(Id.begin(), Id.end(), Ptr); |
| |
| Occur.IdentifierReplacement = Id.str(); |
| Callback(Occur); |
| }; |
| |
| while (true) { |
| replacePlaceholder(Occur); |
| unsigned Offset = Occur.FullPlaceholder.data() - InputBuf->getBufferStart(); |
| Input = InputBuf->getBuffer().substr(Offset+Occur.FullPlaceholder.size()); |
| |
| bool Found = findPlaceholder(Input, Occur); |
| if (!Found) |
| break; |
| } |
| |
| return NewBuf; |
| } |
| |
| std::unique_ptr<llvm::MemoryBuffer> |
| ide::replacePlaceholders(std::unique_ptr<llvm::MemoryBuffer> InputBuf, |
| bool *HadPlaceholder) { |
| if (HadPlaceholder) |
| *HadPlaceholder = false; |
| return replacePlaceholders(std::move(InputBuf), |
| [&](const PlaceholderOccurrence &){ |
| if (HadPlaceholder) |
| *HadPlaceholder = true; |
| }); |
| } |
| |
| // Modules failing to load are commented-out. |
| static const char *OSXModuleList[] = { |
| "AGL", |
| "AVFoundation", |
| "AVKit", |
| "Accelerate", |
| "Accounts", |
| "AddressBook", |
| "AppKit", |
| "AppKitScripting", |
| "AppleScriptKit", |
| "AppleScriptObjC", |
| "ApplicationServices", |
| "AudioToolbox", |
| "AudioUnit", |
| "AudioVideoBridging", |
| "Automator", |
| "CFNetwork", |
| // "CalendarStore", |
| "Carbon", |
| "CloudKit", |
| "Cocoa", |
| "Collaboration", |
| "Contacts", |
| "CoreAudio", |
| "CoreAudioKit", |
| "CoreBluetooth", |
| "CoreData", |
| "CoreFoundation", |
| "CoreGraphics", |
| "CoreImage", |
| "CoreLocation", |
| "CoreMIDI", |
| // "CoreMIDIServer", |
| "CoreMedia", |
| "CoreMediaIO", |
| "CoreServices", |
| "CoreTelephony", |
| "CoreText", |
| "CoreVideo", |
| "CoreWLAN", |
| // "CryptoTokenKit", |
| // "DVComponentGlue", |
| "DVDPlayback", |
| "Darwin", |
| "DirectoryService", |
| "DiscRecording", |
| "DiscRecordingUI", |
| "DiskArbitration", |
| "Dispatch", |
| // "DrawSprocket", |
| "EventKit", |
| "ExceptionHandling", |
| "FWAUserLib", |
| "FinderSync", |
| "ForceFeedback", |
| "Foundation", |
| "GLKit", |
| "GLUT", |
| "GSS", |
| "GameController", |
| "GameKit", |
| "GameplayKit", |
| "Hypervisor", |
| // "ICADevices", |
| "IMServicePlugIn", |
| "IOBluetooth", |
| "IOBluetoothUI", |
| "IOKit", |
| "IOSurface", |
| "ImageCaptureCore", |
| "ImageIO", |
| "InputMethodKit", |
| // "InstallerPlugins", |
| // "InstantMessage", |
| // "JavaFrameEmbedding", |
| "JavaScriptCore", |
| // "JavaVM", |
| // "Kerberos", |
| // "LDAP", |
| "LatentSemanticMapping", |
| "LocalAuthentication", |
| "MachO", |
| "MapKit", |
| "MediaAccessibility", |
| "MediaLibrary", |
| "MediaToolbox", |
| // "Message", |
| "Metal", |
| "MetalKit", |
| "ModelIO", |
| "MultipeerConnectivity", |
| "NetFS", |
| // "NetworkExtension", |
| "NotificationCenter", |
| "OSAKit", |
| "ObjectiveC", |
| "OpenAL", |
| "OpenCL", |
| "OpenDirectory", |
| "OpenGL", |
| // "PCSC", |
| "PreferencePanes", |
| "PubSub", |
| "Python", |
| // "QTKit", QTKit is unavailable on Swift. |
| "Quartz", |
| "QuartzCore", |
| "QuickLook", |
| "QuickTime", |
| // "Ruby", |
| "SceneKit", |
| "ScreenSaver", |
| "Scripting", |
| "ScriptingBridge", |
| "Security", |
| "SecurityFoundation", |
| "SecurityInterface", |
| // "ServiceManagement", |
| "Social", |
| "SpriteKit", |
| "StoreKit", |
| // "SyncServices", |
| "SystemConfiguration", |
| "TWAIN", |
| "Tcl", |
| // "VideoDecodeAcceleration", |
| "VideoToolbox", |
| "WebKit", |
| "XPC", |
| "libkern", |
| "os", |
| // "vecLib", |
| "vmnet", |
| }; |
| |
| // Modules failing to load are commented-out. |
| static const char *iOSModuleList[] = { |
| "AVFoundation", |
| "AVKit", |
| "Accelerate", |
| "Accounts", |
| "AdSupport", |
| "AddressBook", |
| "AddressBookUI", |
| "AssetsLibrary", |
| "AudioToolbox", |
| "AudioUnit", |
| "CFNetwork", |
| "CloudKit", |
| "Contacts", |
| "ContactsUI", |
| "CoreAudio", |
| "CoreAudioKit", |
| "CoreBluetooth", |
| "CoreData", |
| "CoreFoundation", |
| "CoreGraphics", |
| "CoreImage", |
| "CoreLocation", |
| "CoreMIDI", |
| "CoreMedia", |
| "CoreMotion", |
| "CoreSpotlight", |
| "CoreTelephony", |
| "CoreText", |
| "CoreVideo", |
| "Darwin", |
| "Dispatch", |
| "EventKit", |
| "EventKitUI", |
| "ExternalAccessory", |
| "Foundation", |
| "GLKit", |
| "GSS", |
| "GameController", |
| "GameFoundation", |
| "GameKit", |
| "GameplayKit", |
| "HealthKit", |
| "HomeKit", |
| "IMCommonCore", |
| // "IOKit", |
| "ImageIO", |
| "JavaScriptCore", |
| "LocalAuthentication", |
| "MachO", |
| "MapKit", |
| "MediaAccessibility", |
| "MediaPlayer", |
| "MediaToolbox", |
| "MessageUI", |
| "MobileCoreServices", |
| "ModelIO", |
| "MultipeerConnectivity", |
| "NetworkExtension", |
| "NewsstandKit", |
| "NotificationCenter", |
| "ObjectiveC", |
| "OpenAL", |
| "OpenGLES", |
| "PassKit", |
| "Photos", |
| "PhotosUI", |
| "PushKit", |
| "QuartzCore", |
| "QuickLook", |
| "SafariServices", |
| "SceneKit", |
| "ScreenRecorder", |
| "Security", |
| "Social", |
| "SpriteKit", |
| "StoreKit", |
| "SystemConfiguration", |
| "Twitter", |
| "UIKit", |
| "UIKit.UIGestureRecognizerSubclass", |
| "VideoToolbox", |
| "WatchConnectivity", |
| "WatchKit", |
| "WebKit", |
| "iAd", |
| "libkern", |
| "os", |
| }; |
| |
| static const char *DeviceOnlyModuleList[] = { |
| "Metal", |
| "MetalKit", |
| "MetalShaders", |
| }; |
| |
| |
| static ArrayRef<const char *> getOSXModuleList() { |
| return OSXModuleList; |
| } |
| |
| static ArrayRef<const char *> getiOSModuleList() { |
| return iOSModuleList; |
| } |
| |
| static ArrayRef<const char *> getDeviceOnlyModuleList() { |
| return DeviceOnlyModuleList; |
| } |
| |
| void ide::collectModuleNames(StringRef SDKPath, |
| std::vector<std::string> &Modules) { |
| std::string SDKName = getSDKName(SDKPath); |
| std::string lowerSDKName = StringRef(SDKName).lower(); |
| bool isOSXSDK = StringRef(lowerSDKName).find("macosx") != StringRef::npos; |
| bool isDeviceOnly = StringRef(lowerSDKName).find("iphoneos") != StringRef::npos; |
| auto Mods = isOSXSDK ? getOSXModuleList() : getiOSModuleList(); |
| Modules.insert(Modules.end(), Mods.begin(), Mods.end()); |
| if (isDeviceOnly) { |
| Mods = getDeviceOnlyModuleList(); |
| Modules.insert(Modules.end(), Mods.begin(), Mods.end()); |
| } |
| } |
| |
| DeclNameViewer::DeclNameViewer(StringRef Text): IsValid(true), HasParen(false) { |
| auto ArgStart = Text.find_first_of('('); |
| if (ArgStart == StringRef::npos) { |
| BaseName = Text; |
| return; |
| } |
| HasParen = true; |
| BaseName = Text.substr(0, ArgStart); |
| auto ArgEnd = Text.find_last_of(')'); |
| if (ArgEnd == StringRef::npos) { |
| IsValid = false; |
| return; |
| } |
| StringRef AllArgs = Text.substr(ArgStart + 1, ArgEnd - ArgStart - 1); |
| AllArgs.split(Labels, ":"); |
| if (Labels.empty()) |
| return; |
| if ((IsValid = Labels.back().empty())) { |
| Labels.pop_back(); |
| std::transform(Labels.begin(), Labels.end(), Labels.begin(), |
| [](StringRef Label) { |
| return Label == "_" ? StringRef() : Label; |
| }); |
| } |
| } |
| |
| unsigned DeclNameViewer::commonPartsCount(DeclNameViewer &Other) const { |
| if (base() != Other.base()) |
| return 0; |
| unsigned Result = 1; |
| unsigned Len = std::min(args().size(), Other.args().size()); |
| for (unsigned I = 0; I < Len; ++ I) { |
| if (args()[I] == Other.args()[I]) |
| ++Result; |
| else |
| return Result; |
| } |
| return Result; |
| } |
| |
| void swift::ide::SourceEditConsumer:: |
| accept(SourceManager &SM, SourceLoc Loc, StringRef Text, |
| ArrayRef<NoteRegion> SubRegions) { |
| accept(SM, CharSourceRange(Loc, 0), Text, SubRegions); |
| } |
| |
| void swift::ide::SourceEditConsumer:: |
| accept(SourceManager &SM, CharSourceRange Range, StringRef Text, |
| ArrayRef<NoteRegion> SubRegions) { |
| accept(SM, RegionType::ActiveCode, {{Range, Text, SubRegions}}); |
| } |
| |
| void swift::ide::SourceEditConsumer:: |
| insertAfter(SourceManager &SM, SourceLoc Loc, StringRef Text, |
| ArrayRef<NoteRegion> SubRegions) { |
| accept(SM, Lexer::getLocForEndOfToken(SM, Loc), Text, SubRegions); |
| } |
| |
| void swift::ide::SourceEditConsumer:: |
| remove(SourceManager &SM, CharSourceRange Range) { |
| accept(SM, Range, ""); |
| } |
| |
| struct swift::ide::SourceEditJsonConsumer::Implementation { |
| llvm::raw_ostream &OS; |
| std::vector<SingleEdit> AllEdits; |
| Implementation(llvm::raw_ostream &OS) : OS(OS) {} |
| ~Implementation() { |
| writeEditsInJson(AllEdits, OS); |
| } |
| void accept(SourceManager &SM, CharSourceRange Range, |
| llvm::StringRef Text) { |
| AllEdits.push_back({SM, Range, Text.str()}); |
| } |
| }; |
| |
| swift::ide::SourceEditJsonConsumer::SourceEditJsonConsumer(llvm::raw_ostream &OS) : |
| Impl(*new Implementation(OS)) {} |
| |
| swift::ide::SourceEditJsonConsumer::~SourceEditJsonConsumer() { delete &Impl; } |
| |
| void swift::ide::SourceEditJsonConsumer:: |
| accept(SourceManager &SM, RegionType Type, ArrayRef<Replacement> Replacements) { |
| for (const auto &Replacement: Replacements) { |
| Impl.accept(SM, Replacement.Range, Replacement.Text); |
| } |
| } |
| namespace { |
| class ClangFileRewriterHelper { |
| unsigned InterestedId; |
| clang::RewriteBuffer RewriteBuf; |
| bool HasChange; |
| llvm::raw_ostream &OS; |
| |
| void removeCommentLines(clang::RewriteBuffer &Buffer, StringRef Input, |
| StringRef LineHeader) { |
| size_t Pos = 0; |
| while (true) { |
| Pos = Input.find(LineHeader, Pos); |
| if (Pos == StringRef::npos) |
| break; |
| Pos = Input.substr(0, Pos).rfind("//"); |
| assert(Pos != StringRef::npos); |
| size_t EndLine = Input.find('\n', Pos); |
| assert(EndLine != StringRef::npos); |
| ++EndLine; |
| Buffer.RemoveText(Pos, EndLine-Pos); |
| Pos = EndLine; |
| } |
| } |
| |
| public: |
| ClangFileRewriterHelper(SourceManager &SM, unsigned InterestedId, |
| llvm::raw_ostream &OS) |
| : InterestedId(InterestedId), HasChange(false), OS(OS) { |
| StringRef Input(SM.getLLVMSourceMgr().getMemoryBuffer(InterestedId)-> |
| getBuffer()); |
| RewriteBuf.Initialize(Input); |
| removeCommentLines(RewriteBuf, Input, "RUN"); |
| removeCommentLines(RewriteBuf, Input, "CHECK"); |
| } |
| |
| void replaceText(SourceManager &SM, CharSourceRange Range, StringRef Text) { |
| auto BufferId = SM.findBufferContainingLoc(Range.getStart()); |
| if (BufferId == InterestedId) { |
| HasChange = true; |
| auto StartLoc = SM.getLocOffsetInBuffer(Range.getStart(), BufferId); |
| if (!Range.getByteLength()) |
| RewriteBuf.InsertText(StartLoc, Text); |
| else |
| RewriteBuf.ReplaceText(StartLoc, Range.str().size(), Text); |
| } |
| } |
| |
| ~ClangFileRewriterHelper() { |
| if (HasChange) |
| RewriteBuf.write(OS); |
| } |
| }; |
| } // end anonymous namespace |
| struct swift::ide::SourceEditOutputConsumer::Implementation { |
| ClangFileRewriterHelper Rewriter; |
| Implementation(SourceManager &SM, unsigned BufferId, llvm::raw_ostream &OS) |
| : Rewriter(SM, BufferId, OS) {} |
| void accept(SourceManager &SM, CharSourceRange Range, StringRef Text) { |
| Rewriter.replaceText(SM, Range, Text); |
| } |
| }; |
| |
| swift::ide::SourceEditOutputConsumer:: |
| SourceEditOutputConsumer(SourceManager &SM, unsigned BufferId, |
| llvm::raw_ostream &OS) : Impl(*new Implementation(SM, BufferId, OS)) {} |
| |
| swift::ide::SourceEditOutputConsumer::~SourceEditOutputConsumer() { delete &Impl; } |
| |
| void swift::ide::SourceEditOutputConsumer:: |
| accept(SourceManager &SM, RegionType RegionType, |
| ArrayRef<Replacement> Replacements) { |
| // ignore mismatched or |
| if (RegionType == RegionType::Unmatched || RegionType == RegionType::Mismatch) |
| return; |
| |
| for (const auto &Replacement : Replacements) { |
| Impl.accept(SM, Replacement.Range, Replacement.Text); |
| } |
| } |
| |
| bool swift::ide::isFromClang(const Decl *D) { |
| if (getEffectiveClangNode(D)) |
| return true; |
| if (auto *Ext = dyn_cast<ExtensionDecl>(D)) |
| return static_cast<bool>(extensionGetClangNode(Ext)); |
| return false; |
| } |
| |
| ClangNode swift::ide::getEffectiveClangNode(const Decl *decl) { |
| auto &ctx = decl->getASTContext(); |
| auto *importer = static_cast<ClangImporter *>(ctx.getClangModuleLoader()); |
| return importer->getEffectiveClangNode(decl); |
| } |
| |
| /// Retrieve the Clang node for the given extension, if it has one. |
| ClangNode swift::ide::extensionGetClangNode(const ExtensionDecl *ext) { |
| // If it has a Clang node (directly), |
| if (ext->hasClangNode()) return ext->getClangNode(); |
| |
| // Check whether it was syntheszed into a module-scope context. |
| if (!isa<ClangModuleUnit>(ext->getModuleScopeContext())) |
| return ClangNode(); |
| |
| // It may have a global imported as a member. |
| for (auto member : ext->getMembers()) { |
| if (auto clangNode = getEffectiveClangNode(member)) |
| return clangNode; |
| } |
| |
| return ClangNode(); |
| } |
| |
| std::pair<Type, ConcreteDeclRef> swift::ide::getReferencedDecl(Expr *expr) { |
| auto exprTy = expr->getType(); |
| |
| // Look through unbound instance member accesses. |
| if (auto *dotSyntaxExpr = dyn_cast<DotSyntaxBaseIgnoredExpr>(expr)) |
| expr = dotSyntaxExpr->getRHS(); |
| |
| // Look through the 'self' application. |
| if (auto *selfApplyExpr = dyn_cast<SelfApplyExpr>(expr)) |
| expr = selfApplyExpr->getFn(); |
| |
| // Look through curry thunks. |
| if (auto *closure = dyn_cast<AutoClosureExpr>(expr)) |
| if (auto *unwrappedThunk = closure->getUnwrappedCurryThunkExpr()) |
| expr = unwrappedThunk; |
| |
| // If this is an IUO result, unwrap the optional type. |
| auto refDecl = expr->getReferencedDecl(); |
| if (!refDecl) { |
| if (auto *applyExpr = dyn_cast<ApplyExpr>(expr)) { |
| auto fnDecl = applyExpr->getFn()->getReferencedDecl(); |
| if (auto *func = fnDecl.getDecl()) { |
| if (func->isImplicitlyUnwrappedOptional()) { |
| if (auto objectTy = exprTy->getOptionalObjectType()) |
| exprTy = objectTy; |
| } |
| } |
| } |
| } |
| |
| return std::make_pair(exprTy, refDecl); |
| } |