blob: ec2ee1bcf50ca95584dcb9ac6f6a3f3a02b883b1 [file] [log] [blame]
//===--- RefactoringContinuations.h - Defines refactoring continuations ---===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORING_CONTINUATIONS_H
#define LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORING_CONTINUATIONS_H
#include "clang/AST/Decl.h"
#include "clang/Tooling/Refactor/IndexerQuery.h"
#include "clang/Tooling/Refactor/RefactoringOperation.h"
#include "llvm/ADT/StringMap.h"
#include <tuple>
namespace clang {
namespace tooling {
namespace detail {
struct ValidBase {};
/// The ContinuationPassType determine which type is passed into the refactoring
/// continuation.
template <typename T> struct ContinuationPassType { using Type = T; };
template <typename T> struct ContinuationPassType<std::vector<T>> {
using Type = ArrayRef<T>;
};
/// Refactoring operations can pass state to the continuations. Valid state
/// values should have a corresponding \c StateTraits specialization.
template <typename T> struct StateTraits {
/// Specializations should define the following types:
///
/// StoredResultType: The TU-specific type which is then passed into the
/// continuation function. The continuation receives the result whose type is
/// \c ContinuationPassType<StoredResultType>::Type.
///
/// PersistentType: The TU-independent type that's persisted even after the
/// TU in which the continuation was created is disposed.
};
template <typename T>
struct StateTraits<const T *>
: std::enable_if<std::is_base_of<Decl, T>::value, ValidBase>::type {
using StoredResultType = const T *;
using PersistentType = PersistentDeclRef<T>;
};
template <typename T>
struct StateTraits<ArrayRef<const T *>>
: std::enable_if<std::is_base_of<Decl, T>::value, ValidBase>::type {
using StoredResultType = std::vector<const T *>;
using PersistentType = std::vector<PersistentDeclRef<T>>;
};
template <typename T>
struct StateTraits<std::unique_ptr<indexer::ManyToManyDeclarationsQuery<T>>>
: std::enable_if<std::is_base_of<Decl, T>::value, ValidBase>::type {
using StoredResultType = std::vector<indexer::Indexed<const T *>>;
using PersistentType = std::vector<indexer::Indexed<PersistentDeclRef<T>>>;
};
template <> struct StateTraits<std::vector<std::string>> {
using StoredResultType = std::vector<std::string>;
using PersistentType = std::vector<std::string>;
};
/// Conversion functions convert the TU-specific state to a TU independent
/// state and vice-versa.
template <typename T>
PersistentDeclRef<T> convertToPersistentRepresentation(
const T *Declaration,
typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
nullptr) {
return PersistentDeclRef<T>::create(Declaration);
}
template <typename T>
std::vector<PersistentDeclRef<T>> convertToPersistentRepresentation(
ArrayRef<const T *> Declarations,
typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
nullptr) {
std::vector<PersistentDeclRef<T>> Result;
Result.reserve(Declarations.size());
for (const T *D : Declarations)
Result.push_back(PersistentDeclRef<T>::create(D));
return Result;
}
template <typename T>
std::vector<indexer::Indexed<PersistentDeclRef<T>>>
convertToPersistentRepresentation(
std::unique_ptr<indexer::ManyToManyDeclarationsQuery<T>> &Query,
typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
nullptr) {
Query->invalidateTUSpecificState();
return Query->getOutput();
}
inline std::vector<std::string>
convertToPersistentRepresentation(const std::vector<std::string> &Values) {
return Values;
}
/// Converts the TU-independent state to the TU-specific state.
class PersistentToASTSpecificStateConverter {
ASTContext &Context;
llvm::StringMap<const Decl *> ConvertedDeclRefs;
const Decl *lookupDecl(StringRef USR);
public:
// FIXME: We can hide the addConvertible/convert interface so that
// the continuation will just invoke one conversion function for the entire
// tuple.
PersistentToASTSpecificStateConverter(ASTContext &Context)
: Context(Context) {}
template <typename T>
bool addConvertible(
const PersistentDeclRef<T> &Ref,
typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
nullptr) {
if (!Ref.USR.empty())
ConvertedDeclRefs[Ref.USR] = nullptr;
return true;
}
template <typename T>
const T *
convert(const PersistentDeclRef<T> &Ref,
typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
nullptr) {
return dyn_cast_or_null<T>(lookupDecl(Ref.USR));
}
template <typename T>
bool addConvertible(
const std::vector<PersistentDeclRef<T>> &Refs,
typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
nullptr) {
for (const auto &Ref : Refs) {
if (!Ref.USR.empty())
ConvertedDeclRefs[Ref.USR] = nullptr;
}
return true;
}
template <typename T>
std::vector<const T *>
convert(const std::vector<PersistentDeclRef<T>> &Refs,
typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
nullptr) {
std::vector<const T *> Results;
Results.reserve(Refs.size());
// Allow nulls in the produced array, the continuation will have to deal
// with them by itself.
for (const auto &Ref : Refs)
Results.push_back(dyn_cast_or_null<T>(lookupDecl(Ref.USR)));
return Results;
}
template <typename T>
bool addConvertible(
const std::vector<indexer::Indexed<PersistentDeclRef<T>>> &Refs,
typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
nullptr) {
for (const auto &Ref : Refs) {
if (!Ref.Decl.USR.empty())
ConvertedDeclRefs[Ref.Decl.USR] = nullptr;
}
return true;
}
template <typename T>
std::vector<indexer::Indexed<const T *>>
convert(const std::vector<indexer::Indexed<PersistentDeclRef<T>>> &Refs,
typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
nullptr) {
std::vector<indexer::Indexed<const T *>> Results;
Results.reserve(Refs.size());
// Allow nulls in the produced array, the continuation will have to deal
// with them by itself.
for (const auto &Ref : Refs)
Results.push_back(indexer::Indexed<const T *>(
dyn_cast_or_null<T>(lookupDecl(Ref.Decl.USR)), Ref.IsNotDefined));
return Results;
}
bool addConvertible(const PersistentFileID &) {
// Do nothing since FileIDs are converted one-by-one.
return true;
}
FileID convert(const PersistentFileID &Ref);
bool addConvertible(const std::vector<std::string> &) { return true; }
std::vector<std::string> convert(const std::vector<std::string> &Values) {
return Values;
}
/// Converts the added persistent state into TU-specific state using one
/// efficient operation.
void runCoalescedConversions();
};
template <typename T, typename ASTQueryType, typename... QueryOrState>
struct ContinuationFunction {
using Type = llvm::Expected<RefactoringResult> (*)(
ASTContext &, const T &,
typename ContinuationPassType<
typename StateTraits<QueryOrState>::StoredResultType>::Type...);
template <size_t... Is>
static llvm::Expected<RefactoringResult> dispatch(
Type Fn, detail::PersistentToASTSpecificStateConverter &Converter,
ASTContext &Context, const ASTQueryType &Query,
const std::tuple<typename StateTraits<QueryOrState>::StoredResultType...>
&Arguments,
llvm::index_sequence<Is...>) {
auto ASTQueryResult = Converter.convert(Query.getResult());
return Fn(Context, ASTQueryResult, std::get<Is>(Arguments)...);
}
};
template <typename ASTQueryType, typename... QueryOrState>
struct ContinuationFunction<void, ASTQueryType, QueryOrState...> {
using Type = llvm::Expected<RefactoringResult> (*)(
ASTContext &,
typename ContinuationPassType<
typename StateTraits<QueryOrState>::StoredResultType>::Type...);
template <size_t... Is>
static llvm::Expected<RefactoringResult> dispatch(
Type Fn, detail::PersistentToASTSpecificStateConverter &,
ASTContext &Context, const ASTQueryType &,
const std::tuple<typename StateTraits<QueryOrState>::StoredResultType...>
&Arguments,
llvm::index_sequence<Is...>) {
return Fn(Context, std::get<Is>(Arguments)...);
}
};
/// The refactoring contination contains a set of structures that implement
/// the refactoring operation continuation mechanism.
template <typename ASTQueryType, typename... QueryOrState>
class SpecificRefactoringContinuation final : public RefactoringContinuation {
public:
static_assert(std::is_base_of<indexer::ASTProducerQuery, ASTQueryType>::value,
"Invalid AST Query");
// TODO: Validate the QueryOrState types.
/// The consumer function is the actual continuation. It receives the state
/// that was passed-in in the request or the results of the indexing queries
/// that were passed-in in the request.
using ConsumerFn =
typename ContinuationFunction<typename ASTQueryType::ResultTy,
ASTQueryType, QueryOrState...>::Type;
private:
ConsumerFn Consumer;
std::unique_ptr<ASTQueryType> ASTQuery;
/// Inputs store state that's dependent on the original TU.
llvm::Optional<std::tuple<QueryOrState...>> Inputs;
/// State contains TU-independent values.
llvm::Optional<
std::tuple<typename StateTraits<QueryOrState>::PersistentType...>>
State;
/// Converts a tuple that contains the TU dependent state to a tuple with
/// TU independent state.
template <size_t... Is>
std::tuple<typename StateTraits<QueryOrState>::PersistentType...>
convertToPersistentImpl(llvm::index_sequence<Is...>) {
assert(Inputs && "TU-dependent state is already converted");
return std::make_tuple(
detail::convertToPersistentRepresentation(std::get<Is>(*Inputs))...);
}
template <typename T>
bool gatherQueries(
std::vector<indexer::IndexerQuery *> &Queries,
const std::unique_ptr<T> &Query,
typename std::enable_if<
std::is_base_of<indexer::IndexerQuery, T>::value>::type * = nullptr) {
Queries.push_back(Query.get());
return true;
}
template <typename T>
bool gatherQueries(std::vector<indexer::IndexerQuery *> &, const T &) {
// This input element is not a query.
return true;
}
template <size_t... Is>
std::vector<indexer::IndexerQuery *>
gatherQueries(llvm::index_sequence<Is...>) {
assert(Inputs && "TU-dependent state is already converted");
std::vector<indexer::IndexerQuery *> Queries;
std::make_tuple(gatherQueries(Queries, std::get<Is>(*Inputs))...);
return Queries;
}
/// Calls the consumer function with the given \p Context and the state
/// whose values are converted from the TU-independent to TU-specific values.
template <size_t... Is>
llvm::Expected<RefactoringResult> dispatch(ASTContext &Context,
llvm::index_sequence<Is...> Seq) {
assert(State && "TU-independent state is not yet produced");
detail::PersistentToASTSpecificStateConverter Converter(Context);
(void)std::make_tuple(Converter.addConvertible(std::get<Is>(*State))...);
Converter.runCoalescedConversions();
auto Results = std::make_tuple(Converter.convert(std::get<Is>(*State))...);
// TODO: Check for errors?
return detail::ContinuationFunction<
typename ASTQueryType::ResultTy, ASTQueryType,
QueryOrState...>::dispatch(Consumer, Converter, Context, *ASTQuery,
Results, Seq);
}
public:
SpecificRefactoringContinuation(ConsumerFn Consumer,
std::unique_ptr<ASTQueryType> ASTQuery,
QueryOrState... Inputs)
: Consumer(Consumer), ASTQuery(std::move(ASTQuery)),
Inputs(std::make_tuple(std::move(Inputs)...)) {}
SpecificRefactoringContinuation(SpecificRefactoringContinuation &&) = default;
SpecificRefactoringContinuation &
operator=(SpecificRefactoringContinuation &&) = default;
indexer::ASTProducerQuery *getASTUnitIndexerQuery() override {
return ASTQuery.get();
}
std::vector<indexer::IndexerQuery *> getAdditionalIndexerQueries() override {
return gatherQueries(llvm::index_sequence_for<QueryOrState...>());
}
/// Query results are fetched. State is converted to a persistent
/// representation.
void persistTUSpecificState() override {
ASTQuery->invalidateTUSpecificState();
State =
convertToPersistentImpl(llvm::index_sequence_for<QueryOrState...>());
Inputs = None;
}
/// The state is converted to the AST representation in the given ASTContext
/// and the continuation is dispatched.
llvm::Expected<RefactoringResult>
runInExternalASTUnit(ASTContext &Context) override {
return dispatch(Context, llvm::index_sequence_for<QueryOrState...>());
}
};
} // end namespace detail
/// Returns a refactoring continuation that will run within the context of a
/// single external AST unit.
///
/// The indexer determines which AST unit should receive the continuation by
/// evaluation the AST query operation \p ASTQuery.
///
/// \param ASTQuery The query that will determine which AST unit should the
/// continuation run in.
///
/// \param Consumer The continuation function that will be called once the
/// external AST unit is loaded.
///
/// \param Inputs Each individiual input element can contain either some
/// state value that will be passed into the \p Consumer function or an
/// indexer query whose results will be passed into the \p Consumer function.
template <typename ASTQueryType, typename... QueryOrState>
typename std::enable_if<
std::is_base_of<indexer::ASTProducerQuery, ASTQueryType>::value,
std::unique_ptr<RefactoringContinuation>>::type
continueInExternalASTUnit(
std::unique_ptr<ASTQueryType> ASTQuery,
typename detail::SpecificRefactoringContinuation<
ASTQueryType, QueryOrState...>::ConsumerFn Consumer,
QueryOrState... Inputs) {
return llvm::make_unique<
detail::SpecificRefactoringContinuation<ASTQueryType, QueryOrState...>>(
Consumer, std::move(ASTQuery), std::move(Inputs)...);
}
} // end namespace tooling
} // end namespace clang
#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORING_CONTINUATIONS_H