blob: 45f21ba134206b81bf74ed0aff71eeb5d9b68d54 [file] [log] [blame]
//===- 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;
}
}