blob: 48728930b182e52cf92072d5c081c52fe0caa975 [file] [log] [blame]
//===--- IndexerQuery.h - A set of indexer query interfaces ---------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the base indexer queries that can be used with
// refactoring continuations.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLING_REFACTOR_INDEXER_QUERY_H
#define LLVM_CLANG_TOOLING_REFACTOR_INDEXER_QUERY_H
#include "clang/Tooling/Refactor/RefactoringOperationState.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Error.h"
#include <vector>
namespace clang {
namespace tooling {
namespace indexer {
/// Represents an abstract indexer query.
class IndexerQuery {
public:
const char *BaseUID;
const char *NameUID;
IndexerQuery(const char *BaseUID, const char *NameUID)
: BaseUID(BaseUID), NameUID(NameUID) {}
virtual ~IndexerQuery() {}
virtual void invalidateTUSpecificState() = 0;
/// Checks if this query was satisfied. Returns true if it wasn't and reports
/// appropriate errors.
virtual bool verify(ASTContext &) { return false; }
// Mainly used for testing.
static llvm::Error loadResultsFromYAML(StringRef Source,
ArrayRef<IndexerQuery *> Queries);
static bool classof(const IndexerQuery *) { return true; }
};
/// An abstract AST query that can produce an AST unit in which the refactoring
/// continuation will run.
class ASTProducerQuery : public IndexerQuery {
static const char *BaseUIDString;
public:
/// Deriving AST producer queries can redefine this type to generate custom
/// results that are then passed into the refactoring continuations.
using ResultTy = void;
ASTProducerQuery(const char *NameUID)
: IndexerQuery(BaseUIDString, NameUID) {}
static bool classof(const IndexerQuery *Q) {
return Q->BaseUID == BaseUIDString;
}
};
/// A query that finds a file that contains/should contain the implementation of
/// some declaration.
class ASTUnitForImplementationOfDeclarationQuery final
: public ASTProducerQuery {
static const char *NameUIDString;
const Decl *D;
PersistentFileID Result;
public:
ASTUnitForImplementationOfDeclarationQuery(const Decl *D)
: ASTProducerQuery(NameUIDString), D(D), Result("") {}
using ResultTy = FileID;
const Decl *getDecl() const { return D; }
void invalidateTUSpecificState() override { D = nullptr; }
void setResult(PersistentFileID File) { Result = std::move(File); }
bool verify(ASTContext &Context) override;
const PersistentFileID &getResult() const { return Result; }
static bool classof(const IndexerQuery *D) {
return D->NameUID == NameUIDString;
}
};
/// Returns an indexer query that will allow a refactoring continuation to run
/// in an AST unit that contains a file that should contain the implementation
/// of the given declaration \p D.
///
/// The continuation function will receive \c FileID that corresponds to the
/// implementation file. The indexer can decide which file should be used as an
/// implementation of a declaration based on a number of different heuristics.
/// It does not guarantee that the file will actually have any declarations that
/// correspond to the implementation of \p D yet, as the indexer may decide to
/// point to a file that it thinks will have the implementation declarations in
/// the future.
std::unique_ptr<ASTUnitForImplementationOfDeclarationQuery>
fileThatShouldContainImplementationOf(const Decl *D);
/// A declaration predicate operates.
struct DeclPredicate {
const char *Name;
DeclPredicate(const char *Name) : Name(Name) {}
bool operator==(const DeclPredicate &P) const {
return StringRef(Name) == P.Name;
}
bool operator!=(const DeclPredicate &P) const {
return StringRef(Name) != P.Name;
}
};
/// Represents a declaration predicate that will evaluate to either 'true' or
/// 'false' in an indexer query.
struct BoolDeclPredicate {
DeclPredicate Predicate;
bool IsInverted;
BoolDeclPredicate(DeclPredicate Predicate, bool IsInverted = false)
: Predicate(Predicate), IsInverted(IsInverted) {}
BoolDeclPredicate operator!() const {
return BoolDeclPredicate(Predicate, /*IsInverted=*/!IsInverted);
}
};
namespace detail {
/// AST-like representation for decl predicates.
class DeclPredicateNode {
public:
const char *NameUID;
DeclPredicateNode(const char *NameUID) : NameUID(NameUID) {}
static std::unique_ptr<DeclPredicateNode>
create(const DeclPredicate &Predicate);
static std::unique_ptr<DeclPredicateNode>
create(const BoolDeclPredicate &Predicate);
static bool classof(const DeclPredicateNode *) { return true; }
};
class DeclPredicateNodePredicate : public DeclPredicateNode {
static const char *NameUIDString;
DeclPredicate Predicate;
public:
DeclPredicateNodePredicate(const DeclPredicate &Predicate)
: DeclPredicateNode(NameUIDString), Predicate(Predicate) {}
const DeclPredicate &getPredicate() const { return Predicate; }
static bool classof(const DeclPredicateNode *P) {
return P->NameUID == NameUIDString;
}
};
class DeclPredicateNotPredicate : public DeclPredicateNode {
static const char *NameUIDString;
std::unique_ptr<DeclPredicateNode> Child;
public:
DeclPredicateNotPredicate(std::unique_ptr<DeclPredicateNode> Child)
: DeclPredicateNode(NameUIDString), Child(std::move(Child)) {}
const DeclPredicateNode &getChild() const { return *Child; }
static bool classof(const DeclPredicateNode *P) {
return P->NameUID == NameUIDString;
}
};
} // end namespace detail
enum class QueryBoolResult {
Unknown,
Yes,
No,
};
// FIXME: Check that 'T' is either a PersistentDeclRef<> or a Decl *.
template <typename T> struct Indexed {
T Decl;
// FIXME: Generalize better in the new refactoring engine.
QueryBoolResult IsNotDefined;
Indexed(T Decl, QueryBoolResult IsNotDefined = QueryBoolResult::Unknown)
: Decl(Decl), IsNotDefined(IsNotDefined) {}
Indexed(Indexed<T> &&Other) = default;
Indexed &operator=(Indexed<T> &&Other) = default;
Indexed(const Indexed<T> &Other) = default;
Indexed &operator=(const Indexed<T> &Other) = default;
/// True iff the declaration is not defined in the entire project.
bool isNotDefined() const {
// FIXME: This is hack. Need a better system in the new engine.
return IsNotDefined == QueryBoolResult::Yes;
}
};
/// Transforms one set of declarations into another using some predicate.
class DeclarationsQuery : public IndexerQuery {
static const char *BaseUIDString;
std::vector<const Decl *> Input;
std::unique_ptr<detail::DeclPredicateNode> Predicate;
protected:
std::vector<Indexed<PersistentDeclRef<Decl>>> Output;
public:
DeclarationsQuery(std::vector<const Decl *> Input,
std::unique_ptr<detail::DeclPredicateNode> Predicate)
: IndexerQuery(BaseUIDString, nullptr), Input(std::move(Input)),
Predicate(std::move(Predicate)) {
assert(!this->Input.empty() && "empty declarations list!");
}
ArrayRef<const Decl *> getInputs() const { return Input; }
void invalidateTUSpecificState() override { Input.clear(); }
bool verify(ASTContext &Context) override;
void setOutput(std::vector<Indexed<PersistentDeclRef<Decl>>> Output) {
this->Output = Output;
}
const detail::DeclPredicateNode &getPredicateNode() const {
return *Predicate;
}
static bool classof(const IndexerQuery *Q) {
return Q->BaseUID == BaseUIDString;
}
};
/// The \c DeclEntity class acts as a proxy for the entity that represents a
/// declaration in the indexer. It defines a set of declaration predicates that
/// can be used in indexer queries.
struct DeclEntity {
/// The indexer will evaluate this predicate to 'true' when a certain
/// declaration has a corresponding definition.
BoolDeclPredicate isDefined() const {
return BoolDeclPredicate("decl.isDefined");
}
};
template <typename T>
class ManyToManyDeclarationsQuery final
: public std::enable_if<std::is_base_of<Decl, T>::value,
DeclarationsQuery>::type {
public:
ManyToManyDeclarationsQuery(
ArrayRef<const T *> Input,
std::unique_ptr<detail::DeclPredicateNode> Predicate)
: DeclarationsQuery(std::vector<const Decl *>(Input.begin(), Input.end()),
std::move(Predicate)) {}
std::vector<Indexed<PersistentDeclRef<T>>> getOutput() const {
std::vector<Indexed<PersistentDeclRef<T>>> Results;
for (const auto &Ref : DeclarationsQuery::Output)
Results.push_back(Indexed<PersistentDeclRef<T>>(
PersistentDeclRef<T>(Ref.Decl.USR), Ref.IsNotDefined));
return Results;
}
};
/// Returns an indexer query that will pass a filtered list of declarations to
/// a refactoring continuation.
///
/// The filtering is done based on predicates that are available on the \c
/// DeclEntity types. For example, you can use the following invocation to
/// find a set of declarations that are defined in the entire project:
///
/// \code
/// filter({ MyDeclA, MyDeclB }, [] (const DeclEntity &D) { return D.isDefined()
/// })
/// \endcode
template <typename T>
std::unique_ptr<ManyToManyDeclarationsQuery<T>>
filter(ArrayRef<const T *> Declarations,
BoolDeclPredicate (*Fn)(const DeclEntity &),
typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
nullptr) {
return llvm::make_unique<ManyToManyDeclarationsQuery<T>>(
Declarations, detail::DeclPredicateNode::create(Fn(DeclEntity())));
}
} // end namespace indexer
} // end namespace tooling
} // end namespace clang
#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_INDEXER_QUERY_H