| //===- CRefactor.cpp - Refactoring API hooks ------------------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements the Clang-C refactoring library. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "CIndexDiagnostic.h" |
| #include "CIndexer.h" |
| #include "CLog.h" |
| #include "CXCursor.h" |
| #include "CXSourceLocation.h" |
| #include "CXString.h" |
| #include "CXTranslationUnit.h" |
| #include "clang-c/Refactor.h" |
| #include "clang/AST/ASTConsumer.h" |
| #include "clang/Basic/DiagnosticCategories.h" |
| #include "clang/Frontend/ASTUnit.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Frontend/CompilerInvocation.h" |
| #include "clang/Frontend/Utils.h" |
| #include "clang/Index/USRGeneration.h" |
| #include "clang/Tooling/Refactor/IndexerQuery.h" |
| #include "clang/Tooling/Refactor/RefactoringActionFinder.h" |
| #include "clang/Tooling/Refactor/RefactoringActions.h" |
| #include "clang/Tooling/Refactor/RefactoringOperation.h" |
| #include "clang/Tooling/Refactor/RefactoringOptions.h" |
| #include "clang/Tooling/Refactor/RenameIndexedFile.h" |
| #include "clang/Tooling/Refactor/RenamingOperation.h" |
| #include "clang/Tooling/Refactor/SymbolOccurrenceFinder.h" |
| #include "clang/Tooling/Refactor/USRFinder.h" |
| #include "clang/Tooling/Tooling.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/StringMap.h" |
| #include <set> |
| #include <vector> |
| |
| using namespace clang; |
| using namespace clang::tooling; |
| |
| static RefactoringActionType |
| translateRefactoringActionType(CXRefactoringActionType Action) { |
| switch (Action) { |
| #define REFACTORING_ACTION(Name, Spelling) \ |
| case CXRefactor_##Name: \ |
| return RefactoringActionType::Name; |
| #include "clang/Tooling/Refactor/RefactoringActions.def" |
| } |
| } |
| |
| static CXRefactoringActionType |
| translateRefactoringActionType(RefactoringActionType Action) { |
| switch (Action) { |
| #define REFACTORING_ACTION(Name, Spelling) \ |
| case RefactoringActionType::Name: \ |
| return CXRefactor_##Name; |
| #include "clang/Tooling/Refactor/RefactoringActions.def" |
| } |
| } |
| |
| static CXSymbolOccurrenceKind |
| translateOccurrenceKind(rename::SymbolOccurrence::OccurrenceKind Kind) { |
| switch (Kind) { |
| case rename::SymbolOccurrence::MatchingSymbol: |
| return CXSymbolOccurrence_MatchingSymbol; |
| case rename::SymbolOccurrence::MatchingSelector: |
| return CXSymbolOccurrence_MatchingSelector; |
| case rename::SymbolOccurrence::MatchingImplicitProperty: |
| return CXSymbolOccurrence_MatchingImplicitProperty; |
| case rename::SymbolOccurrence::MatchingComment: |
| return CXSymbolOccurrence_MatchingCommentString; |
| case rename::SymbolOccurrence::MatchingDocComment: |
| return CXSymbolOccurrence_MatchingDocCommentString; |
| case rename::SymbolOccurrence::MatchingFilename: |
| return CXSymbolOccurrence_MatchingFilename; |
| } |
| } |
| |
| namespace { |
| |
| // TODO: Remove |
| class RenamingResult { |
| struct RenamedNameString { |
| CXString NewString; |
| unsigned OldLength; |
| }; |
| typedef SmallVector<RenamedNameString, 4> SymbolNameInfo; |
| std::vector<SymbolNameInfo> NameInfo; |
| |
| /// The set of files that have to be modified. |
| llvm::SmallVector<CXString, 2> Filenames; |
| llvm::SpecificBumpPtrAllocator<CXRefactoringReplacement_Old> Replacements; |
| std::vector<std::vector<CXRenamedSymbolOccurrence>> Occurrences; |
| |
| void addOccurrence(const rename::SymbolOccurrence &RenamedOccurrence, |
| const SourceManager &SM, const LangOptions &LangOpts) { |
| CXRefactoringReplacement_Old *OccurrenceReplacements = |
| Replacements.Allocate(RenamedOccurrence.locations().size()); |
| |
| unsigned I = 0; |
| const auto &SymbolNameInfo = NameInfo[RenamedOccurrence.SymbolIndex]; |
| if (!RenamedOccurrence.IsMacroExpansion && |
| RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingComment && |
| RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingDocComment) |
| assert(RenamedOccurrence.locations().size() == SymbolNameInfo.size()); |
| for (const auto &Location : RenamedOccurrence.locations()) { |
| CXSourceRange Range = cxloc::translateSourceRange( |
| SM, LangOpts, |
| CharSourceRange::getCharRange(RenamedOccurrence.getLocationRange( |
| Location, SymbolNameInfo[I].OldLength))); |
| CXFileLocation Begin, End; |
| clang_getFileLocation(clang_getRangeStart(Range), nullptr, &Begin.Line, |
| &Begin.Column, nullptr); |
| clang_getFileLocation(clang_getRangeEnd(Range), nullptr, &End.Line, |
| &End.Column, nullptr); |
| |
| OccurrenceReplacements[I] = CXRefactoringReplacement_Old{ |
| {Begin, End}, |
| RenamedOccurrence.IsMacroExpansion ? cxstring::createNull() |
| : SymbolNameInfo[I].NewString}; |
| ++I; |
| } |
| |
| Occurrences.back().push_back(CXRenamedSymbolOccurrence{ |
| OccurrenceReplacements, I, |
| translateOccurrenceKind(RenamedOccurrence.Kind), |
| RenamedOccurrence.IsMacroExpansion}); |
| } |
| |
| public: |
| RenamingResult(ArrayRef<SymbolName> NewNames, |
| ArrayRef<rename::Symbol> Symbols) { |
| assert(NewNames.size() == Symbols.size()); |
| for (size_t I = 0, E = NewNames.size(); I != E; ++I) { |
| const auto &NewName = NewNames[I]; |
| const auto &OldName = Symbols[I].Name; |
| |
| assert(NewName.size() == OldName.size()); |
| SymbolNameInfo Info; |
| for (size_t I = 0, E = NewName.size(); I != E; ++I) |
| Info.push_back(RenamedNameString{cxstring::createDup(NewName[I]), |
| (unsigned)OldName[I].size()}); |
| NameInfo.push_back(std::move(Info)); |
| } |
| } |
| |
| // FIXME: Don't duplicate code, Use just one constructor. |
| RenamingResult(ArrayRef<SymbolName> NewNames, ArrayRef<SymbolName> OldNames) { |
| assert(NewNames.size() == OldNames.size()); |
| for (size_t I = 0, E = NewNames.size(); I != E; ++I) { |
| const auto &NewName = NewNames[I]; |
| const auto &OldName = OldNames[I]; |
| |
| assert(NewName.size() == OldName.size()); |
| SymbolNameInfo Info; |
| for (size_t I = 0, E = NewName.size(); I != E; ++I) |
| Info.push_back(RenamedNameString{cxstring::createDup(NewName[I]), |
| (unsigned)OldName[I].size()}); |
| NameInfo.push_back(std::move(Info)); |
| } |
| } |
| |
| ~RenamingResult() { |
| for (const auto &SymbolInfo : NameInfo) |
| for (const auto &NameString : SymbolInfo) |
| clang_disposeString(NameString.NewString); |
| for (const auto &Filename : Filenames) |
| clang_disposeString(Filename); |
| } |
| |
| void |
| handleTUResults(CXTranslationUnit TU, |
| llvm::MutableArrayRef<rename::SymbolOccurrence> Results) { |
| ASTUnit *Unit = cxtu::getASTUnit(TU); |
| assert(Unit && "Invalid TU"); |
| auto &Ctx = Unit->getASTContext(); |
| |
| // Find the set of files that have to be modified and gather the indices of |
| // the occurrences for each file. |
| const SourceManager &SM = Ctx.getSourceManager(); |
| typedef std::set<rename::SymbolOccurrence> OccurrenceSet; |
| llvm::StringMap<OccurrenceSet> FilenamesToSymbolOccurrences; |
| for (auto &Occurrence : Results) { |
| const std::pair<FileID, unsigned> DecomposedLocation = |
| SM.getDecomposedLoc(Occurrence.locations()[0]); |
| const FileEntry *Entry = SM.getFileEntryForID(DecomposedLocation.first); |
| assert(Entry && "Invalid file entry"); |
| auto &FileOccurrences = |
| FilenamesToSymbolOccurrences |
| .try_emplace(Entry->getName(), OccurrenceSet()) |
| .first->getValue(); |
| FileOccurrences.insert(std::move(Occurrence)); |
| } |
| |
| // Create the filenames |
| for (const auto &FilenameCount : FilenamesToSymbolOccurrences) |
| Filenames.push_back(cxstring::createDup(FilenameCount.getKey())); |
| |
| unsigned FileIndex = 0; |
| for (const auto &RenamedOccurrences : FilenamesToSymbolOccurrences) { |
| assert(clang_getCString(Filenames[FileIndex]) == |
| RenamedOccurrences.getKey() && |
| "Unstable iteration order"); |
| Occurrences.push_back(std::vector<CXRenamedSymbolOccurrence>()); |
| for (const auto &Occurrence : RenamedOccurrences.getValue()) |
| addOccurrence(Occurrence, SM, Ctx.getLangOpts()); |
| ++FileIndex; |
| } |
| } |
| |
| void addMainFilename(const SourceManager &SM) { |
| assert(Filenames.empty() && "Main filename should be added only once"); |
| Filenames.push_back(cxstring::createDup( |
| SM.getFileEntryForID(SM.getMainFileID())->getName())); |
| Occurrences.push_back(std::vector<CXRenamedSymbolOccurrence>()); |
| } |
| |
| void |
| handleSingleFileTUResults(const ASTContext &Ctx, |
| ArrayRef<rename::SymbolOccurrence> Occurrences) { |
| addMainFilename(Ctx.getSourceManager()); |
| for (const auto &Occurrence : Occurrences) |
| addOccurrence(Occurrence, Ctx.getSourceManager(), Ctx.getLangOpts()); |
| } |
| |
| void handleIndexedFileOccurrence(const rename::SymbolOccurrence &Occurrence, |
| const SourceManager &SM, |
| const LangOptions &LangOpts) { |
| if (Filenames.empty()) { |
| addMainFilename(SM); |
| } |
| addOccurrence(Occurrence, SM, LangOpts); |
| } |
| |
| ArrayRef<CXRenamedSymbolOccurrence> getOccurrences(unsigned FileIndex) const { |
| return Occurrences[FileIndex]; |
| } |
| |
| ArrayRef<CXString> getFilenames() const { return Filenames; } |
| }; |
| |
| class SymbolOccurrencesResult { |
| struct SymbolNamePiece { |
| unsigned OldLength; |
| }; |
| typedef SmallVector<SymbolNamePiece, 4> SymbolNameInfo; |
| std::vector<SymbolNameInfo> NameInfo; |
| |
| /// The set of files that have to be modified. |
| llvm::SmallVector<CXString, 2> Filenames; |
| llvm::SpecificBumpPtrAllocator<CXFileRange> Ranges; |
| std::vector<std::vector<CXSymbolOccurrence>> SymbolOccurrences; |
| |
| void addOccurrence(const rename::SymbolOccurrence &RenamedOccurrence, |
| const SourceManager &SM, const LangOptions &LangOpts) { |
| ArrayRef<SourceLocation> Locations = RenamedOccurrence.locations(); |
| CXFileRange *OccurrenceRanges = Ranges.Allocate(Locations.size()); |
| |
| unsigned I = 0; |
| const auto &SymbolNameInfo = NameInfo[RenamedOccurrence.SymbolIndex]; |
| if (!RenamedOccurrence.IsMacroExpansion && |
| RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingComment && |
| RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingDocComment) |
| assert(Locations.size() == SymbolNameInfo.size()); |
| for (const auto &Location : Locations) { |
| CXSourceRange Range = cxloc::translateSourceRange( |
| SM, LangOpts, |
| CharSourceRange::getCharRange(RenamedOccurrence.getLocationRange( |
| Location, SymbolNameInfo[I].OldLength))); |
| CXFileLocation Begin, End; |
| clang_getFileLocation(clang_getRangeStart(Range), nullptr, &Begin.Line, |
| &Begin.Column, nullptr); |
| clang_getFileLocation(clang_getRangeEnd(Range), nullptr, &End.Line, |
| &End.Column, nullptr); |
| OccurrenceRanges[I] = CXFileRange{Begin, End}; |
| ++I; |
| } |
| |
| SymbolOccurrences.back().push_back(CXSymbolOccurrence{ |
| OccurrenceRanges, /*NumNamePieces=*/I, |
| translateOccurrenceKind(RenamedOccurrence.Kind), |
| RenamedOccurrence.IsMacroExpansion, RenamedOccurrence.SymbolIndex}); |
| } |
| |
| public: |
| SymbolOccurrencesResult(ArrayRef<rename::Symbol> Symbols) { |
| for (const auto &Symbol : Symbols) { |
| const SymbolName &Name = Symbol.Name; |
| SymbolNameInfo Info; |
| for (size_t I = 0, E = Name.size(); I != E; ++I) |
| Info.push_back(SymbolNamePiece{(unsigned)Name[I].size()}); |
| NameInfo.push_back(std::move(Info)); |
| } |
| } |
| |
| SymbolOccurrencesResult(ArrayRef<SymbolName> Names) { |
| for (const SymbolName &Name : Names) { |
| SymbolNameInfo Info; |
| for (size_t I = 0, E = Name.size(); I != E; ++I) |
| Info.push_back(SymbolNamePiece{(unsigned)Name[I].size()}); |
| NameInfo.push_back(std::move(Info)); |
| } |
| } |
| |
| ~SymbolOccurrencesResult() { |
| for (const auto &Filename : Filenames) |
| clang_disposeString(Filename); |
| } |
| |
| void |
| handleTUResults(CXTranslationUnit TU, |
| llvm::MutableArrayRef<rename::SymbolOccurrence> Results) { |
| ASTUnit *Unit = cxtu::getASTUnit(TU); |
| assert(Unit && "Invalid TU"); |
| auto &Ctx = Unit->getASTContext(); |
| |
| // Find the set of files that have to be modified and gather the indices of |
| // the occurrences for each file. |
| const SourceManager &SM = Ctx.getSourceManager(); |
| typedef std::set<rename::SymbolOccurrence> OccurrenceSet; |
| llvm::StringMap<OccurrenceSet> FilenamesToSymbolOccurrences; |
| for (auto &Occurrence : Results) { |
| const std::pair<FileID, unsigned> DecomposedLocation = |
| SM.getDecomposedLoc(Occurrence.locations()[0]); |
| const FileEntry *Entry = SM.getFileEntryForID(DecomposedLocation.first); |
| assert(Entry && "Invalid file entry"); |
| auto &FileOccurrences = |
| FilenamesToSymbolOccurrences |
| .try_emplace(Entry->getName(), OccurrenceSet()) |
| .first->getValue(); |
| FileOccurrences.insert(std::move(Occurrence)); |
| } |
| |
| // Create the filenames |
| for (const auto &FilenameCount : FilenamesToSymbolOccurrences) |
| Filenames.push_back(cxstring::createDup(FilenameCount.getKey())); |
| |
| unsigned FileIndex = 0; |
| for (const auto &RenamedOccurrences : FilenamesToSymbolOccurrences) { |
| assert(clang_getCString(Filenames[FileIndex]) == |
| RenamedOccurrences.getKey() && |
| "Unstable iteration order"); |
| SymbolOccurrences.push_back(std::vector<CXSymbolOccurrence>()); |
| for (const auto &Occurrence : RenamedOccurrences.getValue()) |
| addOccurrence(Occurrence, SM, Ctx.getLangOpts()); |
| ++FileIndex; |
| } |
| } |
| |
| void addMainFilename(const SourceManager &SM) { |
| assert(Filenames.empty() && "Main filename should be added only once"); |
| Filenames.push_back(cxstring::createDup( |
| SM.getFileEntryForID(SM.getMainFileID())->getName())); |
| SymbolOccurrences.push_back(std::vector<CXSymbolOccurrence>()); |
| } |
| |
| void handleIndexedFileOccurrence(const rename::SymbolOccurrence &Occurrence, |
| const SourceManager &SM, |
| const LangOptions &LangOpts) { |
| if (Filenames.empty()) { |
| addMainFilename(SM); |
| } |
| addOccurrence(Occurrence, SM, LangOpts); |
| } |
| |
| ArrayRef<CXSymbolOccurrence> getOccurrences(unsigned FileIndex) const { |
| return SymbolOccurrences[FileIndex]; |
| } |
| |
| ArrayRef<CXString> getFilenames() const { return Filenames; } |
| }; |
| |
| class RenamingAction { |
| public: |
| LangOptions LangOpts; |
| IdentifierTable IDs; |
| // TODO: Remove |
| SmallVector<SymbolName, 4> NewNames; |
| SymbolOperation Operation; |
| |
| RenamingAction(const LangOptions &LangOpts, SymbolOperation Operation) |
| : LangOpts(LangOpts), IDs(LangOpts), Operation(std::move(Operation)) {} |
| |
| /// \brief Sets the new renaming name and returns CXError_Success on success. |
| // TODO: Remove |
| CXErrorCode setNewName(StringRef Name) { |
| SymbolName NewSymbolName(Name, LangOpts); |
| if (NewSymbolName.size() != Operation.symbols()[0].Name.size()) |
| return CXError_RefactoringNameSizeMismatch; |
| if (!rename::isNewNameValid(NewSymbolName, Operation, IDs, LangOpts)) |
| return CXError_RefactoringNameInvalid; |
| rename::determineNewNames(std::move(NewSymbolName), Operation, NewNames, |
| LangOpts); |
| return CXError_Success; |
| } |
| |
| // TODO: Remove |
| CXString usrForSymbolAt(unsigned Index) { |
| llvm::SmallVector<char, 128> Buff; |
| if (index::generateUSRForDecl(Operation.symbols()[Index].FoundDecl, Buff)) |
| return cxstring::createNull(); |
| return cxstring::createDup(StringRef(Buff.begin(), Buff.size())); |
| } |
| |
| // TODO: Remove |
| CXString getUSRThatRequiresImplementationTU() { |
| llvm::SmallVector<char, 128> Buff; |
| if (!Operation.requiresImplementationTU() || |
| index::generateUSRForDecl(Operation.declThatRequiresImplementationTU(), |
| Buff)) |
| return cxstring::createNull(); |
| return cxstring::createDup(StringRef(Buff.begin(), Buff.size())); |
| } |
| |
| // TODO: Remove |
| RenamingResult *handlePrimaryTU(CXTranslationUnit TU, ASTUnit &Unit) { |
| // Perform the renaming. |
| if (NewNames.empty()) |
| return nullptr; |
| |
| const ASTContext &Context = Unit.getASTContext(); |
| auto Occurrences = rename::findSymbolOccurrences( |
| Operation, Context.getTranslationUnitDecl()); |
| auto *Result = new RenamingResult(NewNames, Operation.symbols()); |
| Result->handleTUResults(TU, Occurrences); |
| return Result; |
| } |
| |
| SymbolOccurrencesResult *findSymbolsInInitiationTU(CXTranslationUnit TU, |
| ASTUnit &Unit) { |
| const ASTContext &Context = Unit.getASTContext(); |
| auto Occurrences = rename::findSymbolOccurrences( |
| Operation, Context.getTranslationUnitDecl()); |
| auto *Result = new SymbolOccurrencesResult(Operation.symbols()); |
| Result->handleTUResults(TU, Occurrences); |
| return Result; |
| } |
| }; |
| |
| static bool isObjCSelectorKind(CXCursorKind Kind) { |
| return Kind == CXCursor_ObjCInstanceMethodDecl || |
| Kind == CXCursor_ObjCClassMethodDecl || |
| Kind == CXCursor_ObjCMessageExpr; |
| } |
| |
| // TODO: Remove |
| static bool isObjCSelector(const CXRenamedIndexedSymbol &Symbol) { |
| if (isObjCSelectorKind(Symbol.CursorKind)) |
| return true; |
| for (const auto &Occurrence : llvm::makeArrayRef( |
| Symbol.IndexedLocations, Symbol.IndexedLocationCount)) { |
| if (isObjCSelectorKind(Occurrence.CursorKind)) |
| return true; |
| } |
| return false; |
| } |
| |
| static bool isObjCSelector(const CXIndexedSymbol &Symbol) { |
| if (isObjCSelectorKind(Symbol.CursorKind)) |
| return true; |
| for (const auto &Occurrence : llvm::makeArrayRef( |
| Symbol.IndexedLocations, Symbol.IndexedLocationCount)) { |
| if (isObjCSelectorKind(Occurrence.CursorKind)) |
| return true; |
| } |
| return false; |
| } |
| |
| // New names are initialized and verified after the LangOptions are created. |
| CXErrorCode computeNewNames(ArrayRef<CXRenamedIndexedSymbol> Symbols, |
| ArrayRef<SymbolName> SymbolNames, |
| const LangOptions &LangOpts, |
| SmallVectorImpl<SymbolName> &NewNames) { |
| IdentifierTable IDs(LangOpts); |
| for (const auto &Symbol : Symbols) { |
| SymbolName NewSymbolName(Symbol.NewName, LangOpts); |
| if (NewSymbolName.size() != SymbolNames[0].size()) |
| return CXError_RefactoringNameSizeMismatch; |
| if (!rename::isNewNameValid(NewSymbolName, isObjCSelector(Symbol), IDs, |
| LangOpts)) |
| return CXError_RefactoringNameInvalid; |
| NewNames.push_back(std::move(NewSymbolName)); |
| } |
| return CXError_Success; |
| } |
| |
| static rename::IndexedOccurrence::OccurrenceKind |
| translateIndexedOccurrenceKind(CXCursorKind Kind) { |
| switch (Kind) { |
| case CXCursor_ObjCMessageExpr: |
| return rename::IndexedOccurrence::IndexedObjCMessageSend; |
| case CXCursor_InclusionDirective: |
| return rename::IndexedOccurrence::InclusionDirective; |
| default: |
| return rename::IndexedOccurrence::IndexedSymbol; |
| } |
| } |
| |
| // TODO: Remove |
| CXErrorCode performIndexedFileRename( |
| ArrayRef<CXRenamedIndexedSymbol> Symbols, StringRef Filename, |
| ArrayRef<const char *> Arguments, CXIndex CIdx, |
| MutableArrayRef<CXUnsavedFile> UnsavedFiles, |
| const RefactoringOptionSet *Options, CXRenamingResult &Result) { |
| Result = nullptr; |
| |
| // Adjust the given command line arguments to ensure that any positional |
| // arguments in them are stripped. |
| std::vector<const char *> ClangToolArguments; |
| ClangToolArguments.push_back("--"); |
| for (const auto &Arg : Arguments) { |
| // Remove the '-gmodules' option, as the -fmodules-format=obj isn't |
| // supported without the linked object reader. |
| if (StringRef(Arg) == "-gmodules") |
| continue; |
| ClangToolArguments.push_back(Arg); |
| } |
| int Argc = ClangToolArguments.size(); |
| std::string ErrorMessage; |
| std::unique_ptr<CompilationDatabase> Compilations = |
| FixedCompilationDatabase::loadFromCommandLine( |
| Argc, ClangToolArguments.data(), ErrorMessage); |
| if (!Compilations) { |
| llvm::errs() << "CRefactor: Failed to load command line: " << ErrorMessage |
| << "\n"; |
| return CXError_Failure; |
| } |
| |
| // Translate the symbols. |
| llvm::SmallVector<rename::IndexedSymbol, 4> IndexedSymbols; |
| for (const auto &Symbol : Symbols) { |
| |
| // Parse the symbol name. |
| bool IsObjCSelector = false; |
| // Selectors have to be parsed. |
| if (isObjCSelector(Symbol)) |
| IsObjCSelector = true; |
| // Ensure that we don't get selectors with incorrect symbol kind. |
| else if (StringRef(Symbol.Name).contains(':')) |
| return CXError_InvalidArguments; |
| |
| std::vector<rename::IndexedOccurrence> IndexedOccurrences; |
| for (const auto &Loc : llvm::makeArrayRef(Symbol.IndexedLocations, |
| Symbol.IndexedLocationCount)) { |
| rename::IndexedOccurrence Result; |
| Result.Line = Loc.Location.Line; |
| Result.Column = Loc.Location.Column; |
| Result.Kind = translateIndexedOccurrenceKind(Loc.CursorKind); |
| IndexedOccurrences.push_back(Result); |
| } |
| |
| IndexedSymbols.emplace_back(SymbolName(Symbol.Name, IsObjCSelector), |
| IndexedOccurrences, |
| /*IsObjCSelector=*/IsObjCSelector); |
| } |
| |
| class ToolRunner final : public FrontendActionFactory, |
| public rename::IndexedFileOccurrenceConsumer { |
| ArrayRef<CXRenamedIndexedSymbol> Symbols; |
| ArrayRef<rename::IndexedSymbol> IndexedSymbols; |
| const RefactoringOptionSet *Options; |
| |
| public: |
| RenamingResult *Result; |
| CXErrorCode Err; |
| |
| ToolRunner(ArrayRef<CXRenamedIndexedSymbol> Symbols, |
| ArrayRef<rename::IndexedSymbol> IndexedSymbols, |
| const RefactoringOptionSet *Options) |
| : Symbols(Symbols), IndexedSymbols(IndexedSymbols), Options(Options), |
| Result(nullptr), Err(CXError_Success) {} |
| |
| clang::FrontendAction *create() override { |
| return new rename::IndexedFileOccurrenceProducer(IndexedSymbols, *this, |
| Options); |
| } |
| |
| void handleOccurrence(const rename::SymbolOccurrence &Occurrence, |
| SourceManager &SM, |
| const LangOptions &LangOpts) override { |
| if (Err != CXError_Success) |
| return; |
| if (!Result) { |
| SmallVector<SymbolName, 4> SymbolNames; |
| for (const auto &Symbol : IndexedSymbols) |
| SymbolNames.push_back(Symbol.Name); |
| SmallVector<SymbolName, 4> NewNames; |
| Err = computeNewNames(Symbols, SymbolNames, LangOpts, NewNames); |
| if (Err != CXError_Success) |
| return; |
| Result = new RenamingResult(NewNames, SymbolNames); |
| } |
| Result->handleIndexedFileOccurrence(Occurrence, SM, LangOpts); |
| } |
| }; |
| |
| auto Runner = llvm::make_unique<ToolRunner>(Symbols, IndexedSymbols, Options); |
| |
| // Run a clang tool on the input file. |
| std::string Name = Filename.str(); |
| ClangTool Tool(*Compilations, Name); |
| Tool.run(Runner.get()); |
| if (Runner->Err != CXError_Success) |
| return Runner->Err; |
| Result = Runner->Result; |
| return CXError_Success; |
| } |
| |
| CXErrorCode performIndexedSymbolSearch( |
| ArrayRef<CXIndexedSymbol> Symbols, StringRef Filename, |
| ArrayRef<const char *> Arguments, CXIndex CIdx, |
| MutableArrayRef<CXUnsavedFile> UnsavedFiles, |
| const RefactoringOptionSet *Options, CXSymbolOccurrencesResult &Result) { |
| Result = nullptr; |
| |
| // Adjust the given command line arguments to ensure that any positional |
| // arguments in them are stripped. |
| std::vector<const char *> ClangToolArguments; |
| ClangToolArguments.push_back("--"); |
| for (const auto &Arg : Arguments) { |
| // Remove the '-gmodules' option, as the -fmodules-format=obj isn't |
| // supported without the linked object reader. |
| if (StringRef(Arg) == "-gmodules") |
| continue; |
| ClangToolArguments.push_back(Arg); |
| } |
| int Argc = ClangToolArguments.size(); |
| std::string ErrorMessage; |
| std::unique_ptr<CompilationDatabase> Compilations = |
| FixedCompilationDatabase::loadFromCommandLine( |
| Argc, ClangToolArguments.data(), ErrorMessage); |
| if (!Compilations) { |
| llvm::errs() << "CRefactor: Failed to load command line: " << ErrorMessage |
| << "\n"; |
| return CXError_Failure; |
| } |
| |
| // Translate the symbols. |
| llvm::SmallVector<rename::IndexedSymbol, 4> IndexedSymbols; |
| for (const auto &Symbol : Symbols) { |
| |
| // Parse the symbol name. |
| bool IsObjCSelector = false; |
| // Selectors have to be parsed. |
| if (isObjCSelector(Symbol)) |
| IsObjCSelector = true; |
| // Ensure that we don't get selectors with incorrect symbol kind. |
| else if (StringRef(Symbol.Name).contains(':')) |
| return CXError_InvalidArguments; |
| |
| std::vector<rename::IndexedOccurrence> IndexedOccurrences; |
| for (const auto &Loc : llvm::makeArrayRef(Symbol.IndexedLocations, |
| Symbol.IndexedLocationCount)) { |
| rename::IndexedOccurrence Result; |
| Result.Line = Loc.Location.Line; |
| Result.Column = Loc.Location.Column; |
| Result.Kind = translateIndexedOccurrenceKind(Loc.CursorKind); |
| IndexedOccurrences.push_back(Result); |
| } |
| |
| IndexedSymbols.emplace_back(SymbolName(Symbol.Name, IsObjCSelector), |
| IndexedOccurrences, |
| /*IsObjCSelector=*/IsObjCSelector); |
| } |
| |
| class ToolRunner final : public FrontendActionFactory, |
| public rename::IndexedFileOccurrenceConsumer { |
| ArrayRef<rename::IndexedSymbol> IndexedSymbols; |
| const RefactoringOptionSet *Options; |
| |
| public: |
| SymbolOccurrencesResult *Result; |
| |
| ToolRunner(ArrayRef<rename::IndexedSymbol> IndexedSymbols, |
| const RefactoringOptionSet *Options) |
| : IndexedSymbols(IndexedSymbols), Options(Options), Result(nullptr) {} |
| |
| clang::FrontendAction *create() override { |
| return new rename::IndexedFileOccurrenceProducer(IndexedSymbols, *this, |
| Options); |
| } |
| |
| void handleOccurrence(const rename::SymbolOccurrence &Occurrence, |
| SourceManager &SM, |
| const LangOptions &LangOpts) override { |
| if (!Result) { |
| SmallVector<SymbolName, 4> SymbolNames; |
| for (const auto &Symbol : IndexedSymbols) |
| SymbolNames.push_back(Symbol.Name); |
| Result = new SymbolOccurrencesResult(SymbolNames); |
| } |
| Result->handleIndexedFileOccurrence(Occurrence, SM, LangOpts); |
| } |
| }; |
| |
| auto Runner = llvm::make_unique<ToolRunner>(IndexedSymbols, Options); |
| |
| // Run a clang tool on the input file. |
| std::string Name = Filename.str(); |
| ClangTool Tool(*Compilations, Name); |
| for (const CXUnsavedFile &File : UnsavedFiles) |
| Tool.mapVirtualFile(File.Filename, StringRef(File.Contents, File.Length)); |
| if (Tool.run(Runner.get())) |
| return CXError_Failure; |
| Result = Runner->Result; |
| return CXError_Success; |
| } |
| |
| class RefactoringAction { |
| std::unique_ptr<RefactoringOperation> Operation; |
| std::unique_ptr<RenamingAction> Rename; |
| |
| SmallVector<CXRefactoringCandidate, 2> RefactoringCandidates; |
| CXRefactoringCandidateSet CandidateSet = {nullptr, 0}; |
| bool HasCandidateSet = false; |
| |
| public: |
| CXRefactoringActionType Type; |
| unsigned SelectedCandidate = 0; |
| CXTranslationUnit InitiationTU; |
| // TODO: Remove (no longer needed due to continuations). |
| CXTranslationUnit ImplementationTU; |
| |
| RefactoringAction(std::unique_ptr<RefactoringOperation> Operation, |
| CXRefactoringActionType Type, |
| CXTranslationUnit InitiationTU) |
| : Operation(std::move(Operation)), Type(Type), InitiationTU(InitiationTU), |
| ImplementationTU(nullptr) {} |
| |
| RefactoringAction(std::unique_ptr<RenamingAction> Rename, |
| CXTranslationUnit InitiationTU) |
| : Rename(std::move(Rename)), |
| Type(this->Rename->Operation.isLocal() ? CXRefactor_Rename_Local |
| : CXRefactor_Rename), |
| InitiationTU(InitiationTU), ImplementationTU(nullptr) {} |
| |
| ~RefactoringAction() { |
| for (const auto &Candidate : RefactoringCandidates) |
| clang_disposeString(Candidate.Description); |
| } |
| |
| RefactoringOperation *getOperation() const { return Operation.get(); } |
| |
| RenamingAction *getRenamingAction() const { return Rename.get(); } |
| |
| CXRefactoringCandidateSet getRefactoringCandidates() { |
| if (HasCandidateSet) |
| return CandidateSet; |
| HasCandidateSet = true; |
| RefactoringOperation *Operation = getOperation(); |
| if (!Operation) |
| return CandidateSet; |
| auto Candidates = Operation->getRefactoringCandidates(); |
| if (Candidates.empty()) |
| return CandidateSet; |
| for (const auto &Candidate : Candidates) |
| RefactoringCandidates.push_back({cxstring::createDup(Candidate)}); |
| CandidateSet = {RefactoringCandidates.data(), |
| (unsigned)RefactoringCandidates.size()}; |
| return CandidateSet; |
| } |
| |
| CXErrorCode selectCandidate(unsigned Index) { |
| RefactoringOperation *Operation = getOperation(); |
| if (!Operation) |
| return CXError_InvalidArguments; |
| if (Index != 0 && Index >= getRefactoringCandidates().NumCandidates) |
| return CXError_InvalidArguments; |
| SelectedCandidate = Index; |
| return CXError_Success; |
| } |
| }; |
| |
| static bool operator==(const CXFileLocation &LHS, const CXFileLocation &RHS) { |
| return LHS.Line == RHS.Line && LHS.Column == RHS.Column; |
| } |
| |
| static CXFileRange translateOffsetToRelativeRange(unsigned Offset, |
| unsigned Size, |
| StringRef Source) { |
| assert(Source.drop_front(Offset).take_front(Size).count('\n') == 0 && |
| "Newlines in translated range?"); |
| StringRef Prefix = Source.take_front(Offset); |
| unsigned StartLines = Prefix.count('\n') + 1; |
| if (StartLines > 1) |
| Offset -= Prefix.rfind('\n') + 1; |
| return CXFileRange{{StartLines, Offset + 1}, {StartLines, Offset + 1 + Size}}; |
| } |
| |
| class RefactoringResultWrapper { |
| public: |
| CXRefactoringReplacements_Old Replacements; // TODO: Remove. |
| CXRefactoringReplacements SourceReplacements; |
| std::unique_ptr<RefactoringContinuation> Continuation; |
| llvm::BumpPtrAllocator Allocator; |
| CXTranslationUnit TU; |
| |
| struct AssociatedReplacementInfo { |
| CXSymbolOccurrence *AssociatedSymbolOccurrences; |
| unsigned NumAssociatedSymbolOccurrences; |
| }; |
| |
| ~RefactoringResultWrapper() { |
| // TODO: Remove. |
| for (unsigned I = 0; I < Replacements.NumFileReplacementSets; ++I) { |
| const CXRefactoringFileReplacementSet_Old &FileSet = |
| Replacements.FileReplacementSets[I]; |
| clang_disposeString(FileSet.Filename); |
| for (unsigned J = 0; J < FileSet.NumReplacements; ++J) |
| clang_disposeString(FileSet.Replacements[J].ReplacementString); |
| delete[] FileSet.Replacements; |
| } |
| delete[] Replacements.FileReplacementSets; |
| |
| for (unsigned I = 0; I < SourceReplacements.NumFileReplacementSets; ++I) { |
| const CXRefactoringFileReplacementSet &FileSet = |
| SourceReplacements.FileReplacementSets[I]; |
| clang_disposeString(FileSet.Filename); |
| for (unsigned J = 0; J < FileSet.NumReplacements; ++J) |
| clang_disposeString(FileSet.Replacements[J].ReplacementString); |
| } |
| } |
| |
| RefactoringResultWrapper( |
| ArrayRef<RefactoringReplacement> Replacements, |
| ArrayRef<std::unique_ptr<RefactoringResultAssociatedSymbol>> |
| AssociatedSymbols, |
| std::unique_ptr<RefactoringContinuation> Continuation, |
| ASTContext &Context, CXTranslationUnit TU) |
| : Continuation(std::move(Continuation)), TU(TU) { |
| SourceManager &SM = Context.getSourceManager(); |
| |
| if (Replacements.empty()) { |
| assert(AssociatedSymbols.empty() && "Symbols without replacements??"); |
| // TODO: Remove begin |
| this->Replacements.NumFileReplacementSets = 0; |
| this->Replacements.FileReplacementSets = nullptr; |
| // Remove end |
| this->SourceReplacements.NumFileReplacementSets = 0; |
| this->SourceReplacements.FileReplacementSets = nullptr; |
| return; |
| } |
| llvm::SmallDenseMap<const RefactoringResultAssociatedSymbol *, unsigned> |
| AssociatedSymbolToIndex; |
| for (const auto &Symbol : llvm::enumerate(AssociatedSymbols)) |
| AssociatedSymbolToIndex[Symbol.value().get()] = Symbol.index(); |
| |
| // Find the set of files that have to be modified and gather the indices of |
| // the occurrences for each file. |
| llvm::DenseMap<const FileEntry *, std::vector<unsigned>> |
| FilesToReplacements; |
| for (const auto &Replacement : llvm::enumerate(Replacements)) { |
| SourceLocation Loc = Replacement.value().Range.getBegin(); |
| const std::pair<FileID, unsigned> DecomposedLocation = |
| SM.getDecomposedLoc(Loc); |
| assert(DecomposedLocation.first.isValid() && "Invalid file!"); |
| const FileEntry *Entry = SM.getFileEntryForID(DecomposedLocation.first); |
| FilesToReplacements.try_emplace(Entry, std::vector<unsigned>()) |
| .first->second.push_back(Replacement.index()); |
| } |
| |
| // TODO: Remove |
| unsigned NumFiles = FilesToReplacements.size(); |
| auto *FileReplacementSets = |
| new CXRefactoringFileReplacementSet_Old[NumFiles]; |
| |
| unsigned FileIndex = 0; |
| for (const auto &Entry : FilesToReplacements) { |
| CXRefactoringFileReplacementSet_Old &FileSet = |
| FileReplacementSets[FileIndex]; |
| ++FileIndex; |
| ArrayRef<unsigned> ReplacementIndices = Entry.second; |
| FileSet.Filename = cxstring::createDup(Entry.first->getName()); |
| FileSet.NumReplacements = ReplacementIndices.size(); |
| auto *FileReplacements = |
| new CXRefactoringReplacement_Old[ReplacementIndices.size()]; |
| FileSet.Replacements = FileReplacements; |
| |
| unsigned NumRemoved = 0; |
| for (unsigned I = 0; I < FileSet.NumReplacements; ++I) { |
| const RefactoringReplacement &RefReplacement = |
| Replacements[ReplacementIndices[I]]; |
| CXSourceRange Range = cxloc::translateSourceRange( |
| SM, Context.getLangOpts(), |
| CharSourceRange::getCharRange(RefReplacement.Range.getBegin(), |
| RefReplacement.Range.getEnd())); |
| CXFileLocation Begin, End; |
| clang_getFileLocation(clang_getRangeStart(Range), nullptr, &Begin.Line, |
| &Begin.Column, nullptr); |
| clang_getFileLocation(clang_getRangeEnd(Range), nullptr, &End.Line, |
| &End.Column, nullptr); |
| |
| if (I && FileReplacements[I - NumRemoved - 1].Range.End == Begin) { |
| // Merge the previous and the current replacement. |
| FileReplacements[I - NumRemoved - 1].Range.End = End; |
| std::string Replacement = |
| std::string(clang_getCString( |
| FileReplacements[I - NumRemoved - 1].ReplacementString)) + |
| RefReplacement.ReplacementString; |
| FileReplacements[I - NumRemoved - 1].ReplacementString = |
| cxstring::createDup(Replacement); |
| NumRemoved++; |
| continue; |
| } |
| |
| CXRefactoringReplacement_Old &Replacement = |
| FileReplacements[I - NumRemoved]; |
| Replacement.ReplacementString = |
| cxstring::createDup(RefReplacement.ReplacementString); |
| Replacement.Range.Begin = Begin; |
| Replacement.Range.End = End; |
| } |
| FileSet.NumReplacements -= NumRemoved; |
| } |
| |
| this->Replacements.FileReplacementSets = FileReplacementSets; |
| this->Replacements.NumFileReplacementSets = NumFiles; |
| |
| // TODO: Outdent. |
| { |
| unsigned NumFiles = FilesToReplacements.size(); |
| auto *FileReplacementSets = |
| Allocator.Allocate<CXRefactoringFileReplacementSet>(NumFiles); |
| SourceReplacements.FileReplacementSets = FileReplacementSets; |
| SourceReplacements.NumFileReplacementSets = NumFiles; |
| unsigned FileIndex = 0; |
| for (const auto &Entry : FilesToReplacements) { |
| CXRefactoringFileReplacementSet &FileSet = |
| FileReplacementSets[FileIndex]; |
| ++FileIndex; |
| ArrayRef<unsigned> ReplacementIndices = Entry.second; |
| FileSet.Filename = cxstring::createDup(Entry.first->getName()); |
| FileSet.NumReplacements = ReplacementIndices.size(); |
| auto *FileReplacements = Allocator.Allocate<CXRefactoringReplacement>( |
| ReplacementIndices.size()); |
| FileSet.Replacements = FileReplacements; |
| |
| unsigned NumRemoved = 0; |
| for (unsigned I = 0; I < FileSet.NumReplacements; ++I) { |
| const RefactoringReplacement &RefReplacement = |
| Replacements[ReplacementIndices[I]]; |
| CXSourceRange Range = cxloc::translateSourceRange( |
| SM, Context.getLangOpts(), |
| CharSourceRange::getCharRange(RefReplacement.Range.getBegin(), |
| RefReplacement.Range.getEnd())); |
| CXFileLocation Begin, End; |
| clang_getFileLocation(clang_getRangeStart(Range), nullptr, |
| &Begin.Line, &Begin.Column, nullptr); |
| clang_getFileLocation(clang_getRangeEnd(Range), nullptr, &End.Line, |
| &End.Column, nullptr); |
| |
| if (I && FileReplacements[I - NumRemoved - 1].Range.End == Begin) { |
| // Merge the previous and the current replacement. |
| FileReplacements[I - NumRemoved - 1].Range.End = End; |
| std::string Replacement = |
| std::string(clang_getCString( |
| FileReplacements[I - NumRemoved - 1].ReplacementString)) + |
| RefReplacement.ReplacementString; |
| FileReplacements[I - NumRemoved - 1].ReplacementString = |
| cxstring::createDup(Replacement); |
| NumRemoved++; |
| continue; |
| } |
| |
| CXRefactoringReplacement &Replacement = |
| FileReplacements[I - NumRemoved]; |
| Replacement.ReplacementString = |
| cxstring::createDup(RefReplacement.ReplacementString); |
| Replacement.Range.Begin = Begin; |
| Replacement.Range.End = End; |
| unsigned NumAssociatedSymbols = RefReplacement.SymbolLocations.size(); |
| if (!NumAssociatedSymbols) { |
| Replacement.AssociatedData = nullptr; |
| continue; |
| } |
| AssociatedReplacementInfo *AssociatedData = |
| Allocator.Allocate<AssociatedReplacementInfo>(); |
| Replacement.AssociatedData = AssociatedData; |
| AssociatedData->AssociatedSymbolOccurrences = |
| Allocator.Allocate<CXSymbolOccurrence>(NumAssociatedSymbols); |
| AssociatedData->NumAssociatedSymbolOccurrences = NumAssociatedSymbols; |
| unsigned SymbolIndex = 0; |
| for (const auto &AssociatedSymbol : RefReplacement.SymbolLocations) { |
| unsigned Index = AssociatedSymbolToIndex[AssociatedSymbol.first]; |
| const RefactoringReplacement::AssociatedSymbolLocation &Loc = |
| AssociatedSymbol.second; |
| CXFileRange *NamePieces = |
| Allocator.Allocate<CXFileRange>(Loc.Offsets.size()); |
| assert(AssociatedSymbol.first->getName().size() == |
| Loc.Offsets.size() && |
| "mismatching symbol name and offsets"); |
| for (const auto &Offset : llvm::enumerate(Loc.Offsets)) { |
| StringRef NamePiece = |
| AssociatedSymbol.first->getName()[Offset.index()]; |
| NamePieces[Offset.index()] = translateOffsetToRelativeRange( |
| Offset.value(), NamePiece.size(), |
| RefReplacement.ReplacementString); |
| } |
| AssociatedData->AssociatedSymbolOccurrences[SymbolIndex] = |
| CXSymbolOccurrence{ |
| NamePieces, (unsigned)Loc.Offsets.size(), |
| Loc.IsDeclaration |
| ? CXSymbolOccurrence_ExtractedDeclaration |
| : CXSymbolOccurrence_ExtractedDeclaration_Reference, |
| /*IsMacroExpansion=*/0, Index}; |
| ++SymbolIndex; |
| } |
| } |
| FileSet.NumReplacements -= NumRemoved; |
| } |
| } |
| } |
| }; |
| |
| class RefactoringContinuationWrapper { |
| public: |
| std::unique_ptr<RefactoringContinuation> Continuation; |
| struct QueryWrapper { |
| indexer::IndexerQuery *Query; |
| CXTranslationUnit TU; |
| std::vector<indexer::Indexed<PersistentDeclRef<Decl>>> DeclResults; |
| unsigned ConsumedResults = 0; |
| |
| QueryWrapper(indexer::IndexerQuery *Query, CXTranslationUnit TU) |
| : Query(Query), TU(TU) {} |
| }; |
| SmallVector<QueryWrapper, 4> Queries; |
| bool IsInitiationTUAbandoned = false; |
| |
| RefactoringContinuationWrapper( |
| std::unique_ptr<RefactoringContinuation> Continuation, |
| CXTranslationUnit TU) |
| : Continuation(std::move(Continuation)) { |
| Queries.emplace_back(this->Continuation->getASTUnitIndexerQuery(), TU); |
| assert(Queries.back().Query && "Invalid ast query"); |
| std::vector<indexer::IndexerQuery *> AdditionalQueries = |
| this->Continuation->getAdditionalIndexerQueries(); |
| for (indexer::IndexerQuery *IQ : AdditionalQueries) |
| Queries.emplace_back(IQ, TU); |
| } |
| }; |
| |
| class RefactoringDiagnosticConsumer : public DiagnosticConsumer { |
| const ASTContext &Context; |
| DiagnosticConsumer *PreviousClient; |
| std::unique_ptr<DiagnosticConsumer> PreviousClientPtr; |
| llvm::SmallVector<StoredDiagnostic, 2> RenameDiagnostics; |
| llvm::SmallVector<StoredDiagnostic, 1> ContinuationDiagnostics; |
| |
| public: |
| RefactoringDiagnosticConsumer(ASTContext &Context) : Context(Context) { |
| PreviousClient = Context.getDiagnostics().getClient(); |
| PreviousClientPtr = Context.getDiagnostics().takeClient(); |
| Context.getDiagnostics().setClient(this, /*ShouldOwnClient=*/false); |
| } |
| |
| ~RefactoringDiagnosticConsumer() { |
| if (PreviousClientPtr) |
| Context.getDiagnostics().setClient(PreviousClientPtr.release()); |
| else |
| Context.getDiagnostics().setClient(PreviousClient, |
| /*ShouldOwnClient=*/false); |
| } |
| |
| void HandleDiagnostic(DiagnosticsEngine::Level Level, |
| const Diagnostic &Info) override { |
| unsigned Cat = DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); |
| if (Cat == diag::DiagCat_Rename_Issue) |
| RenameDiagnostics.push_back(StoredDiagnostic(Level, Info)); |
| else if (Cat == diag::DiagCat_Refactoring_Continuation_Issue) |
| ContinuationDiagnostics.push_back(StoredDiagnostic(Level, Info)); |
| else |
| assert(false && "Unhandled refactoring category"); |
| } |
| |
| CXDiagnosticSetImpl *createDiags() const { |
| if (RenameDiagnostics.empty() && ContinuationDiagnostics.empty()) |
| return nullptr; |
| llvm::SmallVector<StoredDiagnostic, 2> AllDiagnostics; |
| for (const auto &D : RenameDiagnostics) |
| AllDiagnostics.push_back(D); |
| for (const auto &D : ContinuationDiagnostics) |
| AllDiagnostics.push_back(D); |
| return cxdiag::createStoredDiags(AllDiagnostics, Context.getLangOpts()); |
| } |
| |
| CXRefactoringActionSetWithDiagnostics createActionSet() const { |
| if (RenameDiagnostics.empty()) |
| return {nullptr, 0}; |
| CXRefactoringActionWithDiagnostics *Actions = |
| new CXRefactoringActionWithDiagnostics[1]; |
| Actions[0].Action = CXRefactor_Rename; |
| Actions[0].Diagnostics = |
| cxdiag::createStoredDiags(RenameDiagnostics, Context.getLangOpts()); |
| return {Actions, 1}; |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| template <typename T> |
| static T withRenamingAction(CXRefactoringAction Action, T DefaultValue, |
| llvm::function_ref<T(RenamingAction &)> Callback) { |
| if (!Action) |
| return DefaultValue; |
| RenamingAction *Rename = |
| static_cast<RefactoringAction *>(Action)->getRenamingAction(); |
| if (!Rename) |
| return DefaultValue; |
| return Callback(*Rename); |
| } |
| |
| static enum CXIndexerQueryKind |
| translateDeclPredicate(const indexer::DeclPredicate &Predicate) { |
| indexer::DeclEntity Entity; |
| if (Predicate == Entity.isDefined().Predicate) |
| return CXIndexerQuery_Decl_IsDefined; |
| return CXIndexerQuery_Unknown; |
| } |
| |
| extern "C" { |
| |
| CXString |
| clang_RefactoringActionType_getName(enum CXRefactoringActionType Action) { |
| return cxstring::createRef( |
| getRefactoringActionTypeName(translateRefactoringActionType(Action))); |
| } |
| |
| void clang_RefactoringActionSet_dispose(CXRefactoringActionSet *Set) { |
| if (Set && Set->Actions) |
| delete[] Set->Actions; |
| } |
| |
| void clang_RefactoringActionSetWithDiagnostics_dispose( |
| CXRefactoringActionSetWithDiagnostics *Set) { |
| if (Set && Set->Actions) { |
| for (auto &S : llvm::makeArrayRef(Set->Actions, Set->NumActions)) |
| clang_disposeDiagnosticSet(S.Diagnostics); |
| delete[] Set->Actions; |
| } |
| } |
| |
| CXRefactoringOptionSet clang_RefactoringOptionSet_create() { |
| return new RefactoringOptionSet; |
| } |
| |
| CXRefactoringOptionSet |
| clang_RefactoringOptionSet_createFromString(const char *String) { |
| RefactoringOptionSet *Result = new RefactoringOptionSet; |
| auto Options = RefactoringOptionSet::parse(String); |
| if (Options) { |
| *Result = std::move(*Options); |
| return Result; |
| } |
| llvm::handleAllErrors(Options.takeError(), |
| [](const llvm::StringError &Error) {}); |
| return clang_RefactoringOptionSet_create(); |
| } |
| |
| void clang_RefactoringOptionSet_add(CXRefactoringOptionSet Set, |
| enum CXRefactoringOption Option) { |
| if (!Set) |
| return; |
| switch (Option) { |
| case CXRefactorOption_AvoidTextualMatches: |
| static_cast<RefactoringOptionSet *>(Set)->add( |
| option::AvoidTextualMatches::getTrue()); |
| break; |
| } |
| } |
| |
| CXString clang_RefactoringOptionSet_toString(CXRefactoringOptionSet Set) { |
| if (!Set) |
| return cxstring::createNull(); |
| std::string Result; |
| llvm::raw_string_ostream OS(Result); |
| static_cast<RefactoringOptionSet *>(Set)->print(OS); |
| return cxstring::createDup(OS.str()); |
| } |
| |
| void clang_RefactoringOptionSet_dispose(CXRefactoringOptionSet Set) { |
| if (Set) |
| delete static_cast<RefactoringOptionSet *>(Set); |
| } |
| |
| enum CXErrorCode |
| clang_Refactoring_findActionsAt(CXTranslationUnit TU, CXSourceLocation Location, |
| CXSourceRange SelectionRange, |
| CXRefactoringOptionSet Options, |
| CXRefactoringActionSet *OutSet) { |
| return clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt( |
| TU, Location, SelectionRange, Options, OutSet, /*OutFailureSet=*/nullptr); |
| } |
| |
| enum CXErrorCode clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt( |
| CXTranslationUnit TU, CXSourceLocation Location, |
| CXSourceRange SelectionRange, CXRefactoringOptionSet Options, |
| CXRefactoringActionSet *OutSet, |
| CXRefactoringActionSetWithDiagnostics *OutFailureSet) { |
| LOG_FUNC_SECTION { *Log << TU << ' '; } |
| if (OutFailureSet) { |
| OutFailureSet->Actions = nullptr; |
| OutFailureSet->NumActions = 0; |
| } |
| |
| if (!OutSet) |
| return CXError_InvalidArguments; |
| |
| OutSet->Actions = nullptr; |
| OutSet->NumActions = 0; |
| |
| if (cxtu::isNotUsableTU(TU)) { |
| LOG_BAD_TU(TU); |
| return CXError_InvalidArguments; |
| } |
| |
| ASTUnit *CXXUnit = cxtu::getASTUnit(TU); |
| if (!CXXUnit) |
| return CXError_InvalidArguments; |
| |
| SourceLocation Loc = cxloc::translateSourceLocation(Location); |
| if (Loc.isInvalid()) |
| return CXError_InvalidArguments; |
| |
| ASTUnit::ConcurrencyCheck Check(*CXXUnit); |
| (void)Options; // FIXME: handle options |
| ASTContext &Context = CXXUnit->getASTContext(); |
| RefactoringDiagnosticConsumer DiagConsumer(Context); |
| RefactoringActionSet ActionSet = findActionSetAt( |
| Loc, cxloc::translateCXSourceRange(SelectionRange), Context); |
| if (OutFailureSet) |
| *OutFailureSet = DiagConsumer.createActionSet(); |
| if (ActionSet.Actions.empty()) |
| return CXError_RefactoringActionUnavailable; |
| |
| CXRefactoringActionType *Actions = |
| new CXRefactoringActionType[ActionSet.Actions.size()]; |
| OutSet->Actions = Actions; |
| OutSet->NumActions = ActionSet.Actions.size(); |
| for (const auto &Action : llvm::enumerate(ActionSet.Actions)) |
| Actions[Action.index()] = translateRefactoringActionType(Action.value()); |
| return CXError_Success; |
| } |
| |
| void clang_RefactoringAction_dispose(CXRefactoringAction Action) { |
| if (Action) |
| delete static_cast<RefactoringAction *>(Action); |
| } |
| |
| CXSourceRange |
| clang_RefactoringAction_getSourceRangeOfInterest(CXRefactoringAction Action) { |
| if (Action) { |
| RefactoringOperation *Operation = |
| static_cast<RefactoringAction *>(Action)->getOperation(); |
| if (Operation) { |
| ASTUnit *CXXUnit = cxtu::getASTUnit( |
| static_cast<RefactoringAction *>(Action)->InitiationTU); |
| if (const Stmt *S = Operation->getTransformedStmt()) { |
| SourceRange Range = S->getSourceRange(); |
| if (const Stmt *Last = Operation->getLastTransformedStmt()) |
| Range.setEnd(Last->getLocEnd()); |
| return cxloc::translateSourceRange(CXXUnit->getASTContext(), Range); |
| } else if (const Decl *D = Operation->getTransformedDecl()) { |
| SourceRange Range = D->getSourceRange(); |
| if (const Decl *Last = Operation->getLastTransformedDecl()) |
| Range.setEnd(Last->getLocEnd()); |
| return cxloc::translateSourceRange(CXXUnit->getASTContext(), Range); |
| } |
| } |
| } |
| return clang_getNullRange(); |
| } |
| |
| int clang_RefactoringAction_requiresImplementationTU( |
| CXRefactoringAction Action) { |
| return withRenamingAction<int>(Action, 0, [](RenamingAction &Action) { |
| return Action.Operation.requiresImplementationTU(); |
| }); |
| } |
| |
| CXString clang_RefactoringAction_getUSRThatRequiresImplementationTU( |
| CXRefactoringAction Action) { |
| return withRenamingAction<CXString>( |
| Action, cxstring::createNull(), [](RenamingAction &Action) { |
| return Action.getUSRThatRequiresImplementationTU(); |
| }); |
| } |
| |
| enum CXErrorCode |
| clang_RefactoringAction_addImplementationTU(CXRefactoringAction Action, |
| CXTranslationUnit TU) { |
| if (!Action || !TU) |
| return CXError_InvalidArguments; |
| // Prohibit multiple additions of implementation TU. |
| if (static_cast<RefactoringAction *>(Action)->ImplementationTU) |
| return CXError_Failure; |
| static_cast<RefactoringAction *>(Action)->ImplementationTU = TU; |
| return CXError_Success; |
| } |
| |
| enum CXErrorCode clang_RefactoringAction_getRefactoringCandidates( |
| CXRefactoringAction Action, |
| CXRefactoringCandidateSet *OutRefactoringCandidateSet) { |
| if (!Action || !OutRefactoringCandidateSet) |
| return CXError_InvalidArguments; |
| *OutRefactoringCandidateSet = |
| static_cast<RefactoringAction *>(Action)->getRefactoringCandidates(); |
| return CXError_Success; |
| } |
| |
| enum CXErrorCode |
| clang_RefactoringAction_selectRefactoringCandidate(CXRefactoringAction Action, |
| unsigned Index) { |
| if (!Action) |
| return CXError_InvalidArguments; |
| return static_cast<RefactoringAction *>(Action)->selectCandidate(Index); |
| } |
| |
| // TODO: Remove. |
| enum CXErrorCode clang_Refactoring_initiateActionAt( |
| CXTranslationUnit TU, CXSourceLocation Location, |
| CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType, |
| CXRefactoringOptionSet Options, CXRefactoringAction *OutAction, |
| CXString *OutFailureReason) { |
| CXDiagnosticSet Diags; |
| CXErrorCode Result = clang_Refactoring_initiateAction( |
| TU, Location, SelectionRange, ActionType, Options, OutAction, &Diags); |
| if (OutFailureReason && Diags && clang_getNumDiagnosticsInSet(Diags) == 1) { |
| CXString Spelling = |
| clang_getDiagnosticSpelling(clang_getDiagnosticInSet(Diags, 0)); |
| *OutFailureReason = cxstring::createDup(clang_getCString(Spelling)); |
| clang_disposeString(Spelling); |
| } else if (OutFailureReason) |
| *OutFailureReason = cxstring::createEmpty(); |
| clang_disposeDiagnosticSet(Diags); |
| return Result; |
| } |
| |
| enum CXErrorCode clang_Refactoring_initiateAction( |
| CXTranslationUnit TU, CXSourceLocation Location, |
| CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType, |
| CXRefactoringOptionSet Options, CXRefactoringAction *OutAction, |
| CXDiagnosticSet *OutDiagnostics) { |
| if (!OutAction) |
| return CXError_InvalidArguments; |
| *OutAction = nullptr; |
| if (OutDiagnostics) |
| *OutDiagnostics = nullptr; |
| |
| if (cxtu::isNotUsableTU(TU)) { |
| LOG_BAD_TU(TU); |
| return CXError_InvalidArguments; |
| } |
| |
| ASTUnit *CXXUnit = cxtu::getASTUnit(TU); |
| if (!CXXUnit) |
| return CXError_InvalidArguments; |
| |
| SourceLocation Loc = cxloc::translateSourceLocation(Location); |
| if (Loc.isInvalid()) |
| return CXError_InvalidArguments; |
| |
| ASTUnit::ConcurrencyCheck Check(*CXXUnit); |
| |
| (void)Options; // FIXME: handle options |
| ASTContext &Context = CXXUnit->getASTContext(); |
| RefactoringDiagnosticConsumer DiagConsumer(Context); |
| auto Operation = initiateRefactoringOperationAt( |
| Loc, cxloc::translateCXSourceRange(SelectionRange), Context, |
| translateRefactoringActionType(ActionType)); |
| if (!Operation.Initiated) { |
| if (OutDiagnostics) { |
| if (!Operation.FailureReason.empty()) { |
| // TODO: Remove when other actions migrate to diagnostics. |
| StoredDiagnostic Diag(DiagnosticsEngine::Error, /*ID=*/0, |
| Operation.FailureReason); |
| *OutDiagnostics = |
| cxdiag::createStoredDiags(Diag, Context.getLangOpts()); |
| } else |
| *OutDiagnostics = DiagConsumer.createDiags(); |
| } |
| return CXError_RefactoringActionUnavailable; |
| } |
| if (Operation.RefactoringOp) |
| *OutAction = new RefactoringAction(std::move(Operation.RefactoringOp), |
| ActionType, TU); |
| else |
| *OutAction = new RefactoringAction( |
| llvm::make_unique<RenamingAction>(CXXUnit->getLangOpts(), |
| std::move(*Operation.SymbolOp)), |
| TU); |
| return CXError_Success; |
| } |
| |
| enum CXErrorCode clang_Refactoring_initiateActionOnDecl( |
| CXTranslationUnit TU, const char *DeclUSR, |
| enum CXRefactoringActionType ActionType, CXRefactoringOptionSet Options, |
| CXRefactoringAction *OutAction, CXString *OutFailureReason) { |
| if (!OutAction) |
| return CXError_InvalidArguments; |
| *OutAction = nullptr; |
| if (OutFailureReason) |
| *OutFailureReason = cxstring::createNull(); |
| |
| if (cxtu::isNotUsableTU(TU)) { |
| LOG_BAD_TU(TU); |
| return CXError_InvalidArguments; |
| } |
| |
| ASTUnit *CXXUnit = cxtu::getASTUnit(TU); |
| if (!CXXUnit) |
| return CXError_InvalidArguments; |
| |
| ASTUnit::ConcurrencyCheck Check(*CXXUnit); |
| |
| (void)Options; // FIXME: handle options |
| auto Operation = initiateRefactoringOperationOnDecl( |
| DeclUSR, CXXUnit->getASTContext(), |
| translateRefactoringActionType(ActionType)); |
| if (!Operation.Initiated) |
| return CXError_RefactoringActionUnavailable; |
| // FIXME: Don't dupe with above |
| if (Operation.RefactoringOp) |
| *OutAction = new RefactoringAction(std::move(Operation.RefactoringOp), |
| ActionType, TU); |
| else |
| *OutAction = new RefactoringAction( |
| llvm::make_unique<RenamingAction>(CXXUnit->getLangOpts(), |
| std::move(*Operation.SymbolOp)), |
| TU); |
| return CXError_Success; |
| } |
| |
| enum CXErrorCode |
| clang_Refactoring_initiateRenamingOperation(CXRefactoringAction Action) { |
| if (!Action) |
| return CXError_InvalidArguments; |
| RefactoringAction *RefAction = static_cast<RefactoringAction *>(Action); |
| RenamingAction *Rename = RefAction->getRenamingAction(); |
| if (!Rename) |
| return CXError_InvalidArguments; |
| // TODO |
| return CXError_Success; |
| } |
| |
| CINDEX_LINKAGE |
| enum CXErrorCode clang_Refactoring_findRenamedCursor( |
| CXTranslationUnit TU, CXSourceLocation Location, |
| CXSourceRange SelectionRange, CXCursor *OutCursor) { |
| if (!OutCursor) |
| return CXError_InvalidArguments; |
| |
| if (cxtu::isNotUsableTU(TU)) { |
| LOG_BAD_TU(TU); |
| return CXError_InvalidArguments; |
| } |
| |
| ASTUnit *CXXUnit = cxtu::getASTUnit(TU); |
| if (!CXXUnit) |
| return CXError_InvalidArguments; |
| SourceLocation Loc = cxloc::translateSourceLocation(Location); |
| if (Loc.isInvalid()) |
| return CXError_InvalidArguments; |
| |
| const NamedDecl *ND = rename::getNamedDeclAt(CXXUnit->getASTContext(), Loc); |
| if (!ND) { |
| *OutCursor = cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound, TU); |
| return CXError_RefactoringActionUnavailable; |
| } |
| |
| *OutCursor = cxcursor::MakeCXCursor(ND, TU); |
| return CXError_Success; |
| } |
| |
| enum CXErrorCode clang_RenamingOperation_setNewName(CXRefactoringAction Action, |
| const char *NewName) { |
| return withRenamingAction<CXErrorCode>( |
| Action, CXError_InvalidArguments, |
| [=](RenamingAction &Action) -> CXErrorCode { |
| if (!NewName) |
| return CXError_InvalidArguments; |
| StringRef Name = NewName; |
| if (Name.empty()) |
| return CXError_InvalidArguments; |
| return Action.setNewName(Name); |
| }); |
| } |
| |
| enum CXRefactoringActionType |
| clang_RefactoringAction_getInitiatedActionType(CXRefactoringAction Action) { |
| return static_cast<RefactoringAction *>(Action)->Type; |
| } |
| |
| unsigned clang_RenamingOperation_getNumSymbols(CXRefactoringAction Action) { |
| return withRenamingAction<unsigned>(Action, 0, [](RenamingAction &Action) { |
| return Action.Operation.symbols().size(); |
| }); |
| } |
| |
| CXString clang_RenamingOperation_getUSRForSymbol(CXRefactoringAction Action, |
| unsigned Index) { |
| return withRenamingAction<CXString>( |
| Action, cxstring::createNull(), |
| [=](RenamingAction &Action) { return Action.usrForSymbolAt(Index); }); |
| } |
| |
| CXRenamingResult clang_Refactoring_findRenamedOccurrencesInPrimaryTUs( |
| CXRefactoringAction Action, const char *const *CommandLineArgs, |
| int NumCommandLineArgs, CXUnsavedFile *UnsavedFiles, |
| unsigned NumUnsavedFiles) { |
| if (!Action) |
| return nullptr; |
| RefactoringAction *RefAction = static_cast<RefactoringAction *>(Action); |
| RenamingAction *Rename = RefAction->getRenamingAction(); |
| if (!Rename) |
| return nullptr; |
| |
| // TODO: Handle implementation TU |
| if (cxtu::isNotUsableTU(RefAction->InitiationTU)) { |
| LOG_BAD_TU(RefAction->InitiationTU); |
| return nullptr; |
| } |
| |
| ASTUnit *CXXUnit = cxtu::getASTUnit(RefAction->InitiationTU); |
| if (!CXXUnit) |
| return nullptr; |
| |
| ASTUnit::ConcurrencyCheck Check(*CXXUnit); |
| |
| return Rename->handlePrimaryTU(RefAction->InitiationTU, *CXXUnit); |
| } |
| |
| CXSymbolOccurrencesResult clang_Refactoring_findSymbolOccurrencesInInitiationTU( |
| CXRefactoringAction Action, const char *const *CommandLineArgs, |
| int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, |
| unsigned NumUnsavedFiles) { |
| if (!Action) |
| return nullptr; |
| RefactoringAction *RefAction = static_cast<RefactoringAction *>(Action); |
| RenamingAction *Rename = RefAction->getRenamingAction(); |
| if (!Rename) |
| return nullptr; |
| |
| if (cxtu::isNotUsableTU(RefAction->InitiationTU)) { |
| LOG_BAD_TU(RefAction->InitiationTU); |
| return nullptr; |
| } |
| |
| ASTUnit *CXXUnit = cxtu::getASTUnit(RefAction->InitiationTU); |
| if (!CXXUnit) |
| return nullptr; |
| |
| ASTUnit::ConcurrencyCheck Check(*CXXUnit); |
| |
| return Rename->findSymbolsInInitiationTU(RefAction->InitiationTU, *CXXUnit); |
| } |
| |
| CXErrorCode clang_Refactoring_findRenamedOccurrencesInIndexedFile( |
| const CXRenamedIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx, |
| const char *Filename, const char *const *CommandLineArgs, |
| int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, |
| unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, |
| CXRenamingResult *OutResult) { |
| if (!OutResult) |
| return CXError_InvalidArguments; |
| if (!Symbols || !NumSymbols || !Filename) |
| return CXError_InvalidArguments; |
| return performIndexedFileRename( |
| llvm::makeArrayRef(Symbols, NumSymbols), StringRef(Filename), |
| llvm::makeArrayRef(CommandLineArgs, NumCommandLineArgs), CIdx, |
| MutableArrayRef<CXUnsavedFile>(UnsavedFiles, NumUnsavedFiles), |
| Options ? static_cast<RefactoringOptionSet *>(Options) : nullptr, |
| *OutResult); |
| } |
| |
| CXErrorCode clang_Refactoring_findSymbolOccurrencesInIndexedFile( |
| const CXIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx, |
| const char *Filename, const char *const *CommandLineArgs, |
| int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, |
| unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, |
| CXSymbolOccurrencesResult *OutResult) { |
| if (!OutResult) |
| return CXError_InvalidArguments; |
| if (!Symbols || !NumSymbols || !Filename) |
| return CXError_InvalidArguments; |
| return performIndexedSymbolSearch( |
| llvm::makeArrayRef(Symbols, NumSymbols), StringRef(Filename), |
| llvm::makeArrayRef(CommandLineArgs, NumCommandLineArgs), CIdx, |
| MutableArrayRef<CXUnsavedFile>(UnsavedFiles, NumUnsavedFiles), |
| Options ? static_cast<RefactoringOptionSet *>(Options) : nullptr, |
| *OutResult); |
| } |
| |
| unsigned clang_RenamingResult_getNumModifiedFiles(CXRenamingResult Result) { |
| if (Result) |
| return static_cast<RenamingResult *>(Result)->getFilenames().size(); |
| return 0; |
| } |
| |
| void clang_RenamingResult_getResultForFile(CXRenamingResult Result, |
| unsigned FileIndex, |
| CXFileRenamingResult *OutResult) { |
| if (!Result || |
| FileIndex >= |
| static_cast<RenamingResult *>(Result)->getFilenames().size()) { |
| OutResult->Filename = cxstring::createNull(); |
| OutResult->NumOccurrences = 0; |
| OutResult->Occurrences = nullptr; |
| return; |
| } |
| auto &RenameResult = *static_cast<RenamingResult *>(Result); |
| OutResult->Filename = RenameResult.getFilenames()[FileIndex]; |
| OutResult->NumOccurrences = RenameResult.getOccurrences(FileIndex).size(); |
| OutResult->Occurrences = RenameResult.getOccurrences(FileIndex).data(); |
| } |
| |
| void clang_RenamingResult_dispose(CXRenamingResult Result) { |
| if (Result) |
| delete static_cast<RenamingResult *>(Result); |
| } |
| |
| unsigned clang_SymbolOccurrences_getNumFiles(CXSymbolOccurrencesResult Result) { |
| if (Result) |
| return static_cast<SymbolOccurrencesResult *>(Result) |
| ->getFilenames() |
| .size(); |
| return 0; |
| } |
| |
| void clang_SymbolOccurrences_getOccurrencesForFile( |
| CXSymbolOccurrencesResult Result, unsigned FileIndex, |
| CXSymbolOccurrencesInFile *OutResult) { |
| if (!Result || |
| FileIndex >= static_cast<SymbolOccurrencesResult *>(Result) |
| ->getFilenames() |
| .size()) { |
| OutResult->Filename = cxstring::createNull(); |
| OutResult->NumOccurrences = 0; |
| OutResult->Occurrences = nullptr; |
| return; |
| } |
| auto &RenameResult = *static_cast<SymbolOccurrencesResult *>(Result); |
| OutResult->Filename = RenameResult.getFilenames()[FileIndex]; |
| OutResult->NumOccurrences = RenameResult.getOccurrences(FileIndex).size(); |
| OutResult->Occurrences = RenameResult.getOccurrences(FileIndex).data(); |
| } |
| |
| void clang_SymbolOccurrences_dispose(CXSymbolOccurrencesResult Result) { |
| if (Result) |
| delete static_cast<SymbolOccurrencesResult *>(Result); |
| } |
| |
| CXRefactoringResult clang_Refactoring_performOperation( |
| CXRefactoringAction Action, const char *const *CommandLineArgs, |
| int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles, |
| unsigned NumUnsavedFiles, CXRefactoringOptionSet Options, |
| CXString *OutFailureReason) { |
| if (OutFailureReason) |
| *OutFailureReason = cxstring::createNull(); |
| if (!Action) |
| return nullptr; |
| RefactoringAction *RefAction = static_cast<RefactoringAction *>(Action); |
| if (!RefAction->getOperation()) |
| return nullptr; |
| |
| ASTUnit *CXXUnit = cxtu::getASTUnit(RefAction->InitiationTU); |
| if (!CXXUnit) |
| return nullptr; |
| |
| ASTUnit::ConcurrencyCheck Check(*CXXUnit); |
| |
| RefactoringOptionSet EmptyOptionSet; |
| const RefactoringOptionSet &OptionSet = |
| Options ? *static_cast<RefactoringOptionSet *>(Options) : EmptyOptionSet; |
| llvm::Expected<RefactoringResult> Result = RefAction->getOperation()->perform( |
| CXXUnit->getASTContext(), CXXUnit->getPreprocessor(), OptionSet, |
| RefAction->SelectedCandidate); |
| if (!Result) { |
| if (OutFailureReason) { |
| (void)!llvm::handleErrors( |
| Result.takeError(), [&](const RefactoringOperationError &Error) { |
| *OutFailureReason = cxstring::createDup(Error.FailureReason); |
| }); |
| } |
| return nullptr; |
| } |
| return new RefactoringResultWrapper( |
| Result.get().Replacements, Result.get().AssociatedSymbols, |
| std::move(Result.get().Continuation), CXXUnit->getASTContext(), |
| RefAction->InitiationTU); |
| } |
| |
| void clang_RefactoringResult_getReplacements( |
| CXRefactoringResult Result, |
| CXRefactoringReplacements_Old *OutReplacements) { |
| if (!OutReplacements) |
| return; |
| if (!Result) { |
| OutReplacements->FileReplacementSets = nullptr; |
| OutReplacements->NumFileReplacementSets = 0; |
| return; |
| } |
| *OutReplacements = static_cast<RefactoringResultWrapper *>(Result)->Replacements; |
| } |
| |
| CXRefactoringReplacements |
| clang_RefactoringResult_getSourceReplacements(CXRefactoringResult Result) { |
| if (!Result) |
| return CXRefactoringReplacements{nullptr, 0}; |
| return static_cast<RefactoringResultWrapper *>(Result)->SourceReplacements; |
| } |
| |
| CXRefactoringReplacementAssociatedSymbolOccurrences |
| clang_RefactoringReplacement_getAssociatedSymbolOccurrences( |
| CXRefactoringReplacement Replacement) { |
| if (!Replacement.AssociatedData) |
| return CXRefactoringReplacementAssociatedSymbolOccurrences{nullptr, 0}; |
| auto *Data = |
| static_cast<RefactoringResultWrapper::AssociatedReplacementInfo *>( |
| Replacement.AssociatedData); |
| return CXRefactoringReplacementAssociatedSymbolOccurrences{ |
| Data->AssociatedSymbolOccurrences, Data->NumAssociatedSymbolOccurrences}; |
| } |
| |
| void clang_RefactoringResult_dispose(CXRefactoringResult Result) { |
| if (Result) |
| delete static_cast<RefactoringResultWrapper *>(Result); |
| } |
| |
| CXRefactoringContinuation |
| clang_RefactoringResult_getContinuation(CXRefactoringResult Result) { |
| if (!Result) |
| return nullptr; |
| auto *Wrapper = static_cast<RefactoringResultWrapper *>(Result); |
| if (!Wrapper->Continuation) |
| return nullptr; |
| return new RefactoringContinuationWrapper(std::move(Wrapper->Continuation), |
| Wrapper->TU); |
| } |
| |
| enum CXErrorCode |
| clang_RefactoringContinuation_loadSerializedIndexerQueryResults( |
| CXRefactoringContinuation Continuation, const char *Source) { |
| if (!Continuation) |
| return CXError_InvalidArguments; |
| auto *Wrapper = static_cast<RefactoringContinuationWrapper *>(Continuation); |
| llvm::SmallVector<indexer::IndexerQuery *, 4> Queries; |
| for (const auto &Query : Wrapper->Queries) |
| Queries.push_back(Query.Query); |
| auto Err = indexer::IndexerQuery::loadResultsFromYAML(Source, Queries); |
| if (Err) { |
| consumeError(std::move(Err)); |
| return CXError_Failure; |
| } |
| return CXError_Success; |
| } |
| |
| unsigned clang_RefactoringContinuation_getNumIndexerQueries( |
| CXRefactoringContinuation Continuation) { |
| if (Continuation) |
| return static_cast<RefactoringContinuationWrapper *>(Continuation) |
| ->Queries.size(); |
| return 0; |
| } |
| |
| CXIndexerQuery clang_RefactoringContinuation_getIndexerQuery( |
| CXRefactoringContinuation Continuation, unsigned Index) { |
| if (!Continuation) |
| return nullptr; |
| auto *Wrapper = static_cast<RefactoringContinuationWrapper *>(Continuation); |
| if (Index >= Wrapper->Queries.size()) |
| return nullptr; |
| return &Wrapper->Queries[Index]; |
| } |
| |
| CXDiagnosticSet clang_RefactoringContinuation_verifyBeforeFinalizing( |
| CXRefactoringContinuation Continuation) { |
| if (!Continuation) |
| return nullptr; |
| auto *Wrapper = static_cast<RefactoringContinuationWrapper *>(Continuation); |
| CXTranslationUnit TU = Wrapper->Queries[0].TU; |
| ASTUnit *CXXUnit = cxtu::getASTUnit(TU); |
| if (!CXXUnit) |
| return nullptr; |
| ASTContext &Context = CXXUnit->getASTContext(); |
| RefactoringDiagnosticConsumer DiagConsumer(Context); |
| for (const auto &Query : Wrapper->Queries) { |
| if (Query.Query->verify(Context)) |
| break; |
| } |
| return DiagConsumer.createDiags(); |
| } |
| |
| void clang_RefactoringContinuation_finalizeEvaluationInInitationTU( |
| CXRefactoringContinuation Continuation) { |
| if (!Continuation) |
| return; |
| auto *Wrapper = static_cast<RefactoringContinuationWrapper *>(Continuation); |
| Wrapper->Queries.clear(); |
| Wrapper->Continuation->persistTUSpecificState(); |
| Wrapper->IsInitiationTUAbandoned = true; |
| } |
| |
| CXRefactoringResult clang_RefactoringContinuation_continueOperationInTU( |
| CXRefactoringContinuation Continuation, CXTranslationUnit TU, |
| CXString *OutFailureReason) { |
| if (!Continuation || !TU) |
| return nullptr; |
| ASTUnit *CXXUnit = cxtu::getASTUnit(TU); |
| if (!CXXUnit) |
| return nullptr; |
| ASTUnit::ConcurrencyCheck Check(*CXXUnit); |
| const auto *Wrapper = |
| static_cast<RefactoringContinuationWrapper *>(Continuation); |
| if (!Wrapper->IsInitiationTUAbandoned) { |
| // FIXME: We can avoid conversions of TU-specific state if the given TU is |
| // the same as the initiation TU. |
| clang_RefactoringContinuation_finalizeEvaluationInInitationTU(Continuation); |
| } |
| auto Result = |
| Wrapper->Continuation->runInExternalASTUnit(CXXUnit->getASTContext()); |
| if (!Result) { |
| if (OutFailureReason) { |
| (void)!llvm::handleErrors( |
| Result.takeError(), [&](const RefactoringOperationError &Error) { |
| *OutFailureReason = cxstring::createDup(Error.FailureReason); |
| }); |
| } |
| return nullptr; |
| } |
| return new RefactoringResultWrapper( |
| Result.get().Replacements, Result.get().AssociatedSymbols, |
| std::move(Result.get().Continuation), CXXUnit->getASTContext(), TU); |
| } |
| |
| void clang_RefactoringContinuation_dispose( |
| CXRefactoringContinuation Continuation) { |
| if (Continuation) |
| delete static_cast<RefactoringContinuationWrapper *>(Continuation); |
| } |
| |
| enum CXIndexerQueryKind clang_IndexerQuery_getKind(CXIndexerQuery Query) { |
| if (!Query) |
| return CXIndexerQuery_Unknown; |
| const auto *IQ = |
| static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query)->Query; |
| if (const auto *DQ = dyn_cast<indexer::DeclarationsQuery>(IQ)) { |
| const indexer::detail::DeclPredicateNode &Node = DQ->getPredicateNode(); |
| if (const auto *NP = |
| dyn_cast<indexer::detail::DeclPredicateNotPredicate>(&Node)) |
| return translateDeclPredicate( |
| cast<indexer::detail::DeclPredicateNodePredicate>(NP->getChild()) |
| .getPredicate()); |
| return translateDeclPredicate( |
| cast<indexer::detail::DeclPredicateNodePredicate>(Node).getPredicate()); |
| } else if (isa<indexer::ASTUnitForImplementationOfDeclarationQuery>(IQ)) |
| return CXIndexerQuery_Decl_FileThatShouldImplement; |
| return CXIndexerQuery_Unknown; |
| } |
| |
| unsigned clang_IndexerQuery_getNumCursors(CXIndexerQuery Query) { |
| if (!Query) |
| return 0; |
| const auto *IQ = |
| static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query)->Query; |
| if (const auto *DQ = dyn_cast<indexer::DeclarationsQuery>(IQ)) |
| return DQ->getInputs().size(); |
| else if (isa<indexer::ASTUnitForImplementationOfDeclarationQuery>(IQ)) |
| return 1; |
| return 0; |
| } |
| |
| CXCursor clang_IndexerQuery_getCursor(CXIndexerQuery Query, |
| unsigned CursorIndex) { |
| if (Query) { |
| const auto *Wrapper = |
| static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query); |
| const indexer::IndexerQuery *IQ = Wrapper->Query; |
| CXTranslationUnit TU = Wrapper->TU; |
| if (const auto *DQ = dyn_cast<indexer::DeclarationsQuery>(IQ)) { |
| if (CursorIndex < DQ->getInputs().size()) |
| return cxcursor::MakeCXCursor(DQ->getInputs()[CursorIndex], TU); |
| } else if (const auto *ASTQuery = dyn_cast< |
| indexer::ASTUnitForImplementationOfDeclarationQuery>(IQ)) { |
| if (CursorIndex == 0) |
| return cxcursor::MakeCXCursor(ASTQuery->getDecl(), TU); |
| } |
| } |
| return cxcursor::MakeCXCursorInvalid(CXCursor_InvalidCode); |
| } |
| |
| enum CXIndexerQueryAction |
| clang_IndexerQuery_consumeIntResult(CXIndexerQuery Query, unsigned CursorIndex, |
| int Value) { |
| if (!Query) |
| return CXIndexerQueryAction_None; |
| auto *Wrapper = |
| static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query); |
| auto *DQ = dyn_cast<indexer::DeclarationsQuery>(Wrapper->Query); |
| if (!DQ) |
| return CXIndexerQueryAction_None; |
| if (CursorIndex >= DQ->getInputs().size() || |
| Wrapper->ConsumedResults == DQ->getInputs().size()) |
| return CXIndexerQueryAction_None; |
| if (Wrapper->DeclResults.empty()) |
| Wrapper->DeclResults.resize(DQ->getInputs().size(), |
| indexer::Indexed<PersistentDeclRef<Decl>>( |
| PersistentDeclRef<Decl>::create(nullptr))); |
| // Filter the declarations! |
| bool IsNot = false; |
| if (isa<indexer::detail::DeclPredicateNotPredicate>(DQ->getPredicateNode())) |
| IsNot = true; |
| bool Result = IsNot ? !Value : !!Value; |
| Wrapper->DeclResults[CursorIndex] = indexer::Indexed<PersistentDeclRef<Decl>>( |
| PersistentDeclRef<Decl>::create(Result ? DQ->getInputs()[CursorIndex] |
| : nullptr), |
| Result ? indexer::QueryBoolResult::Yes : indexer::QueryBoolResult::No); |
| Wrapper->ConsumedResults++; |
| if (Wrapper->ConsumedResults == Wrapper->DeclResults.size()) { |
| // We've received all the results, pass them back to the query. |
| DQ->setOutput(std::move(Wrapper->DeclResults)); |
| } |
| return CXIndexerQueryAction_None; |
| } |
| |
| enum CXIndexerQueryAction |
| clang_IndexerQuery_consumeFileResult(CXIndexerQuery Query, unsigned CursorIndex, |
| const char *Filename) { |
| if (!Query || !Filename) |
| return CXIndexerQueryAction_None; |
| auto *IQ = |
| static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query)->Query; |
| if (auto *ASTQuery = |
| dyn_cast<indexer::ASTUnitForImplementationOfDeclarationQuery>(IQ)) { |
| if (CursorIndex != 0) |
| return CXIndexerQueryAction_None; |
| ASTQuery->setResult(PersistentFileID(Filename)); |
| return CXIndexerQueryAction_RunContinuationInTUThatHasThisFile; |
| } |
| return CXIndexerQueryAction_None; |
| } |
| } |