| //===--- 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/ClangImporter/ClangModule.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/CodeGen/ObjectFilePCHContainerOperations.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Frontend/TextDiagnosticBuffer.h" |
| #include "clang/Lex/PreprocessorOptions.h" |
| #include "clang/Serialization/ASTReader.h" |
| #include "clang/Rewrite/Core/RewriteBuffer.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); |
| Parser &P = Parse.getParser(); |
| |
| bool Done; |
| do { |
| P.parseTopLevel(); |
| Done = P.Tok.is(tok::eof); |
| } while (!Done); |
| |
| SourceCompleteResult SCR; |
| SCR.IsComplete = !P.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); |
| } |
| |
| // 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(Str.str()); |
| } |
| |
| if (PPOpts.DetailedRecord) { |
| Invok.getClangImporterOptions().DetailedPreprocessingRecord = true; |
| } |
| |
| if (!ClangInvok->getFrontendOpts().Inputs.empty()) { |
| Invok.getFrontendOptions().ImplicitObjCHeaderPath = |
| ClangInvok->getFrontendOpts().Inputs[0].getFile(); |
| } |
| |
| 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; |
| }); |
| } |
| |
| static std::string getPlistEntry(const llvm::Twine &Path, StringRef KeyName) { |
| auto BufOrErr = llvm::MemoryBuffer::getFile(Path); |
| if (!BufOrErr) { |
| llvm::errs() << BufOrErr.getError().message() << '\n'; |
| return {}; |
| } |
| |
| std::string Key = "<key>"; |
| Key += KeyName; |
| Key += "</key>"; |
| |
| StringRef Lines = BufOrErr.get()->getBuffer(); |
| while (!Lines.empty()) { |
| StringRef CurLine; |
| std::tie(CurLine, Lines) = Lines.split('\n'); |
| if (CurLine.find(Key) != StringRef::npos) { |
| std::tie(CurLine, Lines) = Lines.split('\n'); |
| unsigned Begin = CurLine.find("<string>") + strlen("<string>"); |
| unsigned End = CurLine.find("</string>"); |
| return CurLine.substr(Begin, End-Begin); |
| } |
| } |
| |
| return {}; |
| } |
| |
| std::string ide::getSDKName(StringRef Path) { |
| std::string Name = getPlistEntry(llvm::Twine(Path)+"/SDKSettings.plist", |
| "CanonicalName"); |
| if (Name.empty() && Path.endswith(".sdk")) { |
| Name = llvm::sys::path::filename(Path).drop_back(strlen(".sdk")); |
| } |
| return Name; |
| } |
| |
| std::string ide::getSDKVersion(StringRef Path) { |
| return getPlistEntry(llvm::Twine(Path)+"/System/Library/CoreServices/" |
| "SystemVersion.plist", "ProductBuildVersion"); |
| } |
| |
| // 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}); |
| } |
| }; |
| |
| 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) { |
| // Directly... |
| if (auto clangNode = decl->getClangNode()) |
| return clangNode; |
| |
| // Or via the nested "Code" enum. |
| if (auto nominal = |
| const_cast<NominalTypeDecl *>(dyn_cast<NominalTypeDecl>(decl))) { |
| auto &ctx = nominal->getASTContext(); |
| auto flags = OptionSet<NominalTypeDecl::LookupDirectFlags>(); |
| flags |= NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions; |
| for (auto code : nominal->lookupDirect(ctx.Id_Code, flags)) { |
| if (auto clangDecl = code->getClangDecl()) |
| return clangDecl; |
| } |
| } |
| |
| return ClangNode(); |
| } |
| |
| /// 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(); |
| } |