forked from OSchip/llvm-project
Support named values in the autocomplete feature.
Summary: This includes: - Passing a Sema to completeExpression to allow for named values in the expression. - Passing a map of names to values to the parser. - Update the Sema interface to include completion for matchers. - Change the parser to use the Sema for completion, instead of going directly to Registry. Reviewers: pcc Subscribers: klimek, cfe-commits Differential Revision: http://reviews.llvm.org/D3509 llvm-svn: 215472
This commit is contained in:
parent
4834ad2609
commit
646f23b809
|
@ -63,17 +63,6 @@ public:
|
|||
public:
|
||||
virtual ~Sema();
|
||||
|
||||
/// \brief Lookup a value by name.
|
||||
///
|
||||
/// This can be used in the Sema layer to declare known constants or to
|
||||
/// allow to split an expression in pieces.
|
||||
///
|
||||
/// \param Name The name of the value to lookup.
|
||||
///
|
||||
/// \return The named value. It could be any type that VariantValue
|
||||
/// supports. An empty value means that the name is not recognized.
|
||||
virtual VariantValue getNamedValue(StringRef Name);
|
||||
|
||||
/// \brief Process a matcher expression.
|
||||
///
|
||||
/// All the arguments passed here have already been processed.
|
||||
|
@ -105,6 +94,29 @@ public:
|
|||
/// found.
|
||||
virtual llvm::Optional<MatcherCtor>
|
||||
lookupMatcherCtor(StringRef MatcherName) = 0;
|
||||
|
||||
/// \brief Compute the list of completion types for \p Context.
|
||||
///
|
||||
/// Each element of \p Context represents a matcher invocation, going from
|
||||
/// outermost to innermost. Elements are pairs consisting of a reference to
|
||||
/// the matcher constructor and the index of the next element in the
|
||||
/// argument list of that matcher (or for the last element, the index of
|
||||
/// the completion point in the argument list). An empty list requests
|
||||
/// completion for the root matcher.
|
||||
virtual std::vector<ArgKind> getAcceptedCompletionTypes(
|
||||
llvm::ArrayRef<std::pair<MatcherCtor, unsigned>> Context);
|
||||
|
||||
/// \brief Compute the list of completions that match any of
|
||||
/// \p AcceptedTypes.
|
||||
///
|
||||
/// \param All types accepted for this completion.
|
||||
///
|
||||
/// \return All completions for the specified types.
|
||||
/// Completions should be valid when used in \c lookupMatcherCtor().
|
||||
/// The matcher constructed from the return of \c lookupMatcherCtor()
|
||||
/// should be convertible to some type in \p AcceptedTypes.
|
||||
virtual std::vector<MatcherCompletion>
|
||||
getMatcherCompletions(llvm::ArrayRef<ArgKind> AcceptedTypes);
|
||||
};
|
||||
|
||||
/// \brief Sema implementation that uses the matcher registry to process the
|
||||
|
@ -121,58 +133,91 @@ public:
|
|||
StringRef BindID,
|
||||
ArrayRef<ParserValue> Args,
|
||||
Diagnostics *Error) override;
|
||||
|
||||
std::vector<ArgKind> getAcceptedCompletionTypes(
|
||||
llvm::ArrayRef<std::pair<MatcherCtor, unsigned>> Context) override;
|
||||
|
||||
std::vector<MatcherCompletion>
|
||||
getMatcherCompletions(llvm::ArrayRef<ArgKind> AcceptedTypes) override;
|
||||
};
|
||||
|
||||
/// \brief Parse a matcher expression, creating matchers from the registry.
|
||||
///
|
||||
/// This overload creates matchers calling directly into the registry. If the
|
||||
/// caller needs more control over how the matchers are created, then it can
|
||||
/// use the overload below that takes a Sema.
|
||||
///
|
||||
/// \param MatcherCode The matcher expression to parse.
|
||||
///
|
||||
/// \return The matcher object constructed, or an empty Optional if an error
|
||||
/// occurred.
|
||||
/// In that case, \c Error will contain a description of the error.
|
||||
/// The caller takes ownership of the DynTypedMatcher object returned.
|
||||
static llvm::Optional<DynTypedMatcher>
|
||||
parseMatcherExpression(StringRef MatcherCode, Diagnostics *Error);
|
||||
typedef llvm::StringMap<VariantValue> NamedValueMap;
|
||||
|
||||
/// \brief Parse a matcher expression.
|
||||
///
|
||||
/// \param MatcherCode The matcher expression to parse.
|
||||
///
|
||||
/// \param S The Sema instance that will help the parser
|
||||
/// construct the matchers.
|
||||
/// construct the matchers. If null, it uses the default registry.
|
||||
///
|
||||
/// \param NamedValues A map of precomputed named values. This provides
|
||||
/// the dictionary for the <NamedValue> rule of the grammar.
|
||||
/// If null, it is ignored.
|
||||
///
|
||||
/// \return The matcher object constructed by the processor, or an empty
|
||||
/// Optional if an error occurred. In that case, \c Error will contain a
|
||||
/// description of the error.
|
||||
/// The caller takes ownership of the DynTypedMatcher object returned.
|
||||
static llvm::Optional<DynTypedMatcher>
|
||||
parseMatcherExpression(StringRef MatcherCode, Sema *S, Diagnostics *Error);
|
||||
|
||||
/// \brief Parse an expression, creating matchers from the registry.
|
||||
///
|
||||
/// Parses any expression supported by this parser. In general, the
|
||||
/// \c parseMatcherExpression function is a better approach to get a matcher
|
||||
/// object.
|
||||
static bool parseExpression(StringRef Code, VariantValue *Value,
|
||||
Diagnostics *Error);
|
||||
parseMatcherExpression(StringRef MatcherCode, Sema *S,
|
||||
const NamedValueMap *NamedValues,
|
||||
Diagnostics *Error);
|
||||
static llvm::Optional<DynTypedMatcher>
|
||||
parseMatcherExpression(StringRef MatcherCode, Sema *S,
|
||||
Diagnostics *Error) {
|
||||
return parseMatcherExpression(MatcherCode, S, nullptr, Error);
|
||||
}
|
||||
static llvm::Optional<DynTypedMatcher>
|
||||
parseMatcherExpression(StringRef MatcherCode, Diagnostics *Error) {
|
||||
return parseMatcherExpression(MatcherCode, nullptr, Error);
|
||||
}
|
||||
|
||||
/// \brief Parse an expression.
|
||||
///
|
||||
/// Parses any expression supported by this parser. In general, the
|
||||
/// \c parseMatcherExpression function is a better approach to get a matcher
|
||||
/// object.
|
||||
///
|
||||
/// \param S The Sema instance that will help the parser
|
||||
/// construct the matchers. If null, it uses the default registry.
|
||||
///
|
||||
/// \param NamedValues A map of precomputed named values. This provides
|
||||
/// the dictionary for the <NamedValue> rule of the grammar.
|
||||
/// If null, it is ignored.
|
||||
static bool parseExpression(StringRef Code, Sema *S,
|
||||
const NamedValueMap *NamedValues,
|
||||
VariantValue *Value, Diagnostics *Error);
|
||||
static bool parseExpression(StringRef Code, Sema *S,
|
||||
VariantValue *Value, Diagnostics *Error) {
|
||||
return parseExpression(Code, S, nullptr, Value, Error);
|
||||
}
|
||||
static bool parseExpression(StringRef Code, VariantValue *Value,
|
||||
Diagnostics *Error) {
|
||||
return parseExpression(Code, nullptr, Value, Error);
|
||||
}
|
||||
|
||||
/// \brief Complete an expression at the given offset.
|
||||
///
|
||||
/// \param S The Sema instance that will help the parser
|
||||
/// construct the matchers. If null, it uses the default registry.
|
||||
///
|
||||
/// \param NamedValues A map of precomputed named values. This provides
|
||||
/// the dictionary for the <NamedValue> rule of the grammar.
|
||||
/// If null, it is ignored.
|
||||
///
|
||||
/// \return The list of completions, which may be empty if there are no
|
||||
/// available completions or if an error occurred.
|
||||
static std::vector<MatcherCompletion>
|
||||
completeExpression(StringRef Code, unsigned CompletionOffset);
|
||||
completeExpression(StringRef Code, unsigned CompletionOffset, Sema *S,
|
||||
const NamedValueMap *NamedValues);
|
||||
static std::vector<MatcherCompletion>
|
||||
completeExpression(StringRef Code, unsigned CompletionOffset, Sema *S) {
|
||||
return completeExpression(Code, CompletionOffset, S, nullptr);
|
||||
}
|
||||
static std::vector<MatcherCompletion>
|
||||
completeExpression(StringRef Code, unsigned CompletionOffset) {
|
||||
return completeExpression(Code, CompletionOffset, nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
class CodeTokenizer;
|
||||
|
@ -180,6 +225,7 @@ private:
|
|||
struct TokenInfo;
|
||||
|
||||
Parser(CodeTokenizer *Tokenizer, Sema *S,
|
||||
const NamedValueMap *NamedValues,
|
||||
Diagnostics *Error);
|
||||
|
||||
bool parseExpressionImpl(VariantValue *Value);
|
||||
|
@ -187,12 +233,16 @@ private:
|
|||
VariantValue *Value);
|
||||
bool parseIdentifierPrefixImpl(VariantValue *Value);
|
||||
|
||||
void addCompletion(const TokenInfo &CompToken, StringRef TypedText,
|
||||
StringRef Decl);
|
||||
void addCompletion(const TokenInfo &CompToken,
|
||||
const MatcherCompletion &Completion);
|
||||
void addExpressionCompletions();
|
||||
|
||||
std::vector<MatcherCompletion>
|
||||
getNamedValueCompletions(ArrayRef<ArgKind> AcceptedTypes);
|
||||
|
||||
CodeTokenizer *const Tokenizer;
|
||||
Sema *const S;
|
||||
const NamedValueMap *const NamedValues;
|
||||
Diagnostics *const Error;
|
||||
|
||||
typedef std::vector<std::pair<MatcherCtor, unsigned> > ContextStackTy;
|
||||
|
|
|
@ -36,8 +36,10 @@ typedef const internal::MatcherDescriptor *MatcherCtor;
|
|||
|
||||
struct MatcherCompletion {
|
||||
MatcherCompletion() {}
|
||||
MatcherCompletion(StringRef TypedText, StringRef MatcherDecl)
|
||||
: TypedText(TypedText), MatcherDecl(MatcherDecl) {}
|
||||
MatcherCompletion(StringRef TypedText, StringRef MatcherDecl,
|
||||
unsigned Specificity)
|
||||
: TypedText(TypedText), MatcherDecl(MatcherDecl),
|
||||
Specificity(Specificity) {}
|
||||
|
||||
/// \brief The text to type to select this matcher.
|
||||
std::string TypedText;
|
||||
|
@ -45,6 +47,13 @@ struct MatcherCompletion {
|
|||
/// \brief The "declaration" of the matcher, with type information.
|
||||
std::string MatcherDecl;
|
||||
|
||||
/// \brief Value corresponding to the "specificity" of the converted matcher.
|
||||
///
|
||||
/// Zero specificity indicates that this conversion would produce a trivial
|
||||
/// matcher that will either always or never match.
|
||||
/// Such matchers are excluded from code completion results.
|
||||
unsigned Specificity;
|
||||
|
||||
bool operator==(const MatcherCompletion &Other) const {
|
||||
return TypedText == Other.TypedText && MatcherDecl == Other.MatcherDecl;
|
||||
}
|
||||
|
@ -58,28 +67,28 @@ public:
|
|||
/// constructor, or Optional<MatcherCtor>() if not found.
|
||||
static llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName);
|
||||
|
||||
/// \brief Compute the list of completions for \p Context.
|
||||
/// \brief Compute the list of completion types for \p Context.
|
||||
///
|
||||
/// Each element of \p Context represents a matcher invocation, going from
|
||||
/// outermost to innermost. Elements are pairs consisting of a reference to the
|
||||
/// matcher constructor and the index of the next element in the argument list
|
||||
/// of that matcher (or for the last element, the index of the completion
|
||||
/// point in the argument list). An empty list requests completion for the
|
||||
/// root matcher.
|
||||
/// outermost to innermost. Elements are pairs consisting of a reference to
|
||||
/// the matcher constructor and the index of the next element in the
|
||||
/// argument list of that matcher (or for the last element, the index of
|
||||
/// the completion point in the argument list). An empty list requests
|
||||
/// completion for the root matcher.
|
||||
static std::vector<ArgKind> getAcceptedCompletionTypes(
|
||||
llvm::ArrayRef<std::pair<MatcherCtor, unsigned>> Context);
|
||||
|
||||
/// \brief Compute the list of completions that match any of
|
||||
/// \p AcceptedTypes.
|
||||
///
|
||||
/// The completions are ordered first by decreasing relevance, then
|
||||
/// alphabetically. Relevance is determined by how closely the matcher's
|
||||
/// type matches that of the context. For example, if the innermost matcher
|
||||
/// takes a FunctionDecl matcher, the FunctionDecl matchers are returned
|
||||
/// first, followed by the ValueDecl matchers, then NamedDecl, then Decl, then
|
||||
/// polymorphic matchers.
|
||||
/// \param All types accepted for this completion.
|
||||
///
|
||||
/// Matchers which are technically convertible to the innermost context but
|
||||
/// which would match either all or no nodes are excluded. For example,
|
||||
/// namedDecl and varDecl are excluded in a FunctionDecl context, because
|
||||
/// those matchers would match respectively all or no nodes in such a context.
|
||||
/// \return All completions for the specified types.
|
||||
/// Completions should be valid when used in \c lookupMatcherCtor().
|
||||
/// The matcher constructed from the return of \c lookupMatcherCtor()
|
||||
/// should be convertible to some type in \p AcceptedTypes.
|
||||
static std::vector<MatcherCompletion>
|
||||
getCompletions(ArrayRef<std::pair<MatcherCtor, unsigned> > Context);
|
||||
getMatcherCompletions(ArrayRef<ArgKind> AcceptedTypes);
|
||||
|
||||
/// \brief Construct a matcher from the registry.
|
||||
///
|
||||
|
|
|
@ -29,6 +29,50 @@ namespace clang {
|
|||
namespace ast_matchers {
|
||||
namespace dynamic {
|
||||
|
||||
/// \brief Kind identifier.
|
||||
///
|
||||
/// It supports all types that VariantValue can contain.
|
||||
class ArgKind {
|
||||
public:
|
||||
enum Kind {
|
||||
AK_Matcher,
|
||||
AK_Unsigned,
|
||||
AK_String
|
||||
};
|
||||
/// \brief Constructor for non-matcher types.
|
||||
ArgKind(Kind K) : K(K) { assert(K != AK_Matcher); }
|
||||
|
||||
/// \brief Constructor for matcher types.
|
||||
ArgKind(ast_type_traits::ASTNodeKind MatcherKind)
|
||||
: K(AK_Matcher), MatcherKind(MatcherKind) {}
|
||||
|
||||
Kind getArgKind() const { return K; }
|
||||
ast_type_traits::ASTNodeKind getMatcherKind() const {
|
||||
assert(K == AK_Matcher);
|
||||
return MatcherKind;
|
||||
}
|
||||
|
||||
/// \brief Determines if this type can be converted to \p To.
|
||||
///
|
||||
/// \param To the requested destination type.
|
||||
///
|
||||
/// \param Value corresponding to the "specificity" of the convertion.
|
||||
bool isConvertibleTo(ArgKind To, unsigned *Specificity) const;
|
||||
|
||||
bool operator<(const ArgKind &Other) const {
|
||||
if (K == AK_Matcher && Other.K == AK_Matcher)
|
||||
return MatcherKind < Other.MatcherKind;
|
||||
return K < Other.K;
|
||||
}
|
||||
|
||||
/// \brief String representation of the type.
|
||||
std::string asString() const;
|
||||
|
||||
private:
|
||||
Kind K;
|
||||
ast_type_traits::ASTNodeKind MatcherKind;
|
||||
};
|
||||
|
||||
using ast_matchers::internal::DynTypedMatcher;
|
||||
|
||||
/// \brief A variant matcher object.
|
||||
|
@ -66,6 +110,8 @@ class VariantMatcher {
|
|||
virtual llvm::Optional<DynTypedMatcher> getSingleMatcher() const = 0;
|
||||
virtual std::string getTypeAsString() const = 0;
|
||||
virtual void makeTypedMatcher(MatcherOps &Ops) const = 0;
|
||||
virtual bool isConvertibleTo(ast_type_traits::ASTNodeKind Kind,
|
||||
unsigned *Specificity) const = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
|
@ -116,6 +162,18 @@ public:
|
|||
return Ops.hasMatcher();
|
||||
}
|
||||
|
||||
/// \brief Determines if the contained matcher can be converted to \p Kind.
|
||||
///
|
||||
/// \param Kind the requested destination type.
|
||||
///
|
||||
/// \param Value corresponding to the "specificity" of the convertion.
|
||||
bool isConvertibleTo(ast_type_traits::ASTNodeKind Kind,
|
||||
unsigned *Specificity) const {
|
||||
if (Value)
|
||||
return Value->isConvertibleTo(Kind, Specificity);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// \brief Return this matcher as a \c Matcher<T>.
|
||||
///
|
||||
/// Handles the different types (Single, Polymorphic) accordingly.
|
||||
|
@ -228,6 +286,22 @@ public:
|
|||
const VariantMatcher &getMatcher() const;
|
||||
void setMatcher(const VariantMatcher &Matcher);
|
||||
|
||||
/// \brief Determines if the contained value can be converted to \p Kind.
|
||||
///
|
||||
/// \param Kind the requested destination type.
|
||||
///
|
||||
/// \param Value corresponding to the "specificity" of the convertion.
|
||||
bool isConvertibleTo(ArgKind Kind, unsigned* Specificity) const;
|
||||
|
||||
/// \brief Determines if the contained value can be converted to any kind
|
||||
/// in \p Kinds.
|
||||
///
|
||||
/// \param Kinds the requested destination types.
|
||||
///
|
||||
/// \param Value corresponding to the "specificity" of the convertion. It is
|
||||
/// the maximum specificity of all the possible conversions.
|
||||
bool isConvertibleTo(ArrayRef<ArgKind> Kinds, unsigned *Specificity) const;
|
||||
|
||||
/// \brief String representation of the type of the value.
|
||||
std::string getTypeAsString() const;
|
||||
|
||||
|
|
|
@ -30,48 +30,8 @@
|
|||
namespace clang {
|
||||
namespace ast_matchers {
|
||||
namespace dynamic {
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct ArgKind {
|
||||
enum Kind {
|
||||
AK_Matcher,
|
||||
AK_Unsigned,
|
||||
AK_String
|
||||
};
|
||||
ArgKind(Kind K)
|
||||
: K(K) {}
|
||||
ArgKind(ast_type_traits::ASTNodeKind MatcherKind)
|
||||
: K(AK_Matcher), MatcherKind(MatcherKind) {}
|
||||
|
||||
std::string asString() const {
|
||||
switch (getArgKind()) {
|
||||
case AK_Matcher:
|
||||
return (Twine("Matcher<") + MatcherKind.asStringRef() + ">").str();
|
||||
case AK_Unsigned:
|
||||
return "unsigned";
|
||||
case AK_String:
|
||||
return "string";
|
||||
}
|
||||
llvm_unreachable("unhandled ArgKind");
|
||||
}
|
||||
|
||||
Kind getArgKind() const { return K; }
|
||||
ast_type_traits::ASTNodeKind getMatcherKind() const {
|
||||
assert(K == AK_Matcher);
|
||||
return MatcherKind;
|
||||
}
|
||||
|
||||
bool operator<(const ArgKind &Other) const {
|
||||
if (K == AK_Matcher && Other.K == AK_Matcher)
|
||||
return MatcherKind < Other.MatcherKind;
|
||||
return K < Other.K;
|
||||
}
|
||||
|
||||
private:
|
||||
Kind K;
|
||||
ast_type_traits::ASTNodeKind MatcherKind;
|
||||
};
|
||||
|
||||
/// \brief Helper template class to just from argument type to the right is/get
|
||||
/// functions in VariantValue.
|
||||
|
@ -161,16 +121,10 @@ inline bool isRetKindConvertibleTo(
|
|||
ArrayRef<ast_type_traits::ASTNodeKind> RetKinds,
|
||||
ast_type_traits::ASTNodeKind Kind, unsigned *Specificity,
|
||||
ast_type_traits::ASTNodeKind *LeastDerivedKind) {
|
||||
for (ArrayRef<ast_type_traits::ASTNodeKind>::const_iterator
|
||||
i = RetKinds.begin(),
|
||||
e = RetKinds.end();
|
||||
i != e; ++i) {
|
||||
unsigned Distance;
|
||||
if (i->isBaseOf(Kind, &Distance)) {
|
||||
if (Specificity)
|
||||
*Specificity = 100 - Distance;
|
||||
for (const ast_type_traits::ASTNodeKind &NodeKind : RetKinds) {
|
||||
if (ArgKind(NodeKind).isConvertibleTo(Kind, Specificity)) {
|
||||
if (LeastDerivedKind)
|
||||
*LeastDerivedKind = *i;
|
||||
*LeastDerivedKind = NodeKind;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "clang/Basic/CharInfo.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -258,8 +259,14 @@ private:
|
|||
|
||||
Parser::Sema::~Sema() {}
|
||||
|
||||
VariantValue Parser::Sema::getNamedValue(StringRef Name) {
|
||||
return VariantValue();
|
||||
std::vector<ArgKind> Parser::Sema::getAcceptedCompletionTypes(
|
||||
llvm::ArrayRef<std::pair<MatcherCtor, unsigned>> Context) {
|
||||
return std::vector<ArgKind>();
|
||||
}
|
||||
|
||||
std::vector<MatcherCompletion>
|
||||
Parser::Sema::getMatcherCompletions(llvm::ArrayRef<ArgKind> AcceptedTypes) {
|
||||
return std::vector<MatcherCompletion>();
|
||||
}
|
||||
|
||||
struct Parser::ScopedContextEntry {
|
||||
|
@ -288,7 +295,9 @@ bool Parser::parseIdentifierPrefixImpl(VariantValue *Value) {
|
|||
|
||||
if (Tokenizer->nextTokenKind() != TokenInfo::TK_OpenParen) {
|
||||
// Parse as a named value.
|
||||
if (const VariantValue NamedValue = S->getNamedValue(NameToken.Text)) {
|
||||
if (const VariantValue NamedValue =
|
||||
NamedValues ? NamedValues->lookup(NameToken.Text)
|
||||
: VariantValue()) {
|
||||
*Value = NamedValue;
|
||||
return true;
|
||||
}
|
||||
|
@ -379,7 +388,7 @@ bool Parser::parseMatcherExpressionImpl(const TokenInfo &NameToken,
|
|||
Tokenizer->consumeNextToken(); // consume the period.
|
||||
const TokenInfo BindToken = Tokenizer->consumeNextToken();
|
||||
if (BindToken.Kind == TokenInfo::TK_CodeCompletion) {
|
||||
addCompletion(BindToken, "bind(\"", "bind");
|
||||
addCompletion(BindToken, MatcherCompletion("bind(\"", "bind", 1));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -427,15 +436,30 @@ bool Parser::parseMatcherExpressionImpl(const TokenInfo &NameToken,
|
|||
|
||||
// If the prefix of this completion matches the completion token, add it to
|
||||
// Completions minus the prefix.
|
||||
void Parser::addCompletion(const TokenInfo &CompToken, StringRef TypedText,
|
||||
StringRef Decl) {
|
||||
if (TypedText.size() >= CompToken.Text.size() &&
|
||||
TypedText.substr(0, CompToken.Text.size()) == CompToken.Text) {
|
||||
Completions.push_back(
|
||||
MatcherCompletion(TypedText.substr(CompToken.Text.size()), Decl));
|
||||
void Parser::addCompletion(const TokenInfo &CompToken,
|
||||
const MatcherCompletion& Completion) {
|
||||
if (StringRef(Completion.TypedText).startswith(CompToken.Text) &&
|
||||
Completion.Specificity > 0) {
|
||||
Completions.emplace_back(Completion.TypedText.substr(CompToken.Text.size()),
|
||||
Completion.MatcherDecl, Completion.Specificity);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<MatcherCompletion> Parser::getNamedValueCompletions(
|
||||
ArrayRef<ArgKind> AcceptedTypes) {
|
||||
if (!NamedValues) return std::vector<MatcherCompletion>();
|
||||
std::vector<MatcherCompletion> Result;
|
||||
for (const auto &Entry : *NamedValues) {
|
||||
unsigned Specificity;
|
||||
if (Entry.getValue().isConvertibleTo(AcceptedTypes, &Specificity)) {
|
||||
std::string Decl =
|
||||
(Entry.getValue().getTypeAsString() + " " + Entry.getKey()).str();
|
||||
Result.emplace_back(Entry.getKey(), Decl, Specificity);
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void Parser::addExpressionCompletions() {
|
||||
const TokenInfo CompToken = Tokenizer->consumeNextToken();
|
||||
assert(CompToken.Kind == TokenInfo::TK_CodeCompletion);
|
||||
|
@ -449,12 +473,13 @@ void Parser::addExpressionCompletions() {
|
|||
return;
|
||||
}
|
||||
|
||||
std::vector<MatcherCompletion> RegCompletions =
|
||||
Registry::getCompletions(ContextStack);
|
||||
for (std::vector<MatcherCompletion>::iterator I = RegCompletions.begin(),
|
||||
E = RegCompletions.end();
|
||||
I != E; ++I) {
|
||||
addCompletion(CompToken, I->TypedText, I->MatcherDecl);
|
||||
auto AcceptedTypes = S->getAcceptedCompletionTypes(ContextStack);
|
||||
for (const auto &Completion : S->getMatcherCompletions(AcceptedTypes)) {
|
||||
addCompletion(CompToken, Completion);
|
||||
}
|
||||
|
||||
for (const auto &Completion : getNamedValueCompletions(AcceptedTypes)) {
|
||||
addCompletion(CompToken, Completion);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -494,9 +519,12 @@ bool Parser::parseExpressionImpl(VariantValue *Value) {
|
|||
llvm_unreachable("Unknown token kind.");
|
||||
}
|
||||
|
||||
static llvm::ManagedStatic<Parser::RegistrySema> DefaultRegistrySema;
|
||||
|
||||
Parser::Parser(CodeTokenizer *Tokenizer, Sema *S,
|
||||
Diagnostics *Error)
|
||||
: Tokenizer(Tokenizer), S(S), Error(Error) {}
|
||||
const NamedValueMap *NamedValues, Diagnostics *Error)
|
||||
: Tokenizer(Tokenizer), S(S ? S : &*DefaultRegistrySema),
|
||||
NamedValues(NamedValues), Error(Error) {}
|
||||
|
||||
Parser::RegistrySema::~RegistrySema() {}
|
||||
|
||||
|
@ -516,16 +544,22 @@ VariantMatcher Parser::RegistrySema::actOnMatcherExpression(
|
|||
}
|
||||
}
|
||||
|
||||
bool Parser::parseExpression(StringRef Code, VariantValue *Value,
|
||||
Diagnostics *Error) {
|
||||
RegistrySema S;
|
||||
return parseExpression(Code, &S, Value, Error);
|
||||
std::vector<ArgKind> Parser::RegistrySema::getAcceptedCompletionTypes(
|
||||
ArrayRef<std::pair<MatcherCtor, unsigned>> Context) {
|
||||
return Registry::getAcceptedCompletionTypes(Context);
|
||||
}
|
||||
|
||||
std::vector<MatcherCompletion> Parser::RegistrySema::getMatcherCompletions(
|
||||
ArrayRef<ArgKind> AcceptedTypes) {
|
||||
return Registry::getMatcherCompletions(AcceptedTypes);
|
||||
}
|
||||
|
||||
bool Parser::parseExpression(StringRef Code, Sema *S,
|
||||
const NamedValueMap *NamedValues,
|
||||
VariantValue *Value, Diagnostics *Error) {
|
||||
CodeTokenizer Tokenizer(Code, Error);
|
||||
if (!Parser(&Tokenizer, S, Error).parseExpressionImpl(Value)) return false;
|
||||
if (!Parser(&Tokenizer, S, NamedValues, Error).parseExpressionImpl(Value))
|
||||
return false;
|
||||
if (Tokenizer.peekNextToken().Kind != TokenInfo::TK_Eof) {
|
||||
Error->addError(Tokenizer.peekNextToken().Range,
|
||||
Error->ET_ParserTrailingCode);
|
||||
|
@ -535,28 +569,31 @@ bool Parser::parseExpression(StringRef Code, Sema *S,
|
|||
}
|
||||
|
||||
std::vector<MatcherCompletion>
|
||||
Parser::completeExpression(StringRef Code, unsigned CompletionOffset) {
|
||||
Parser::completeExpression(StringRef Code, unsigned CompletionOffset, Sema *S,
|
||||
const NamedValueMap *NamedValues) {
|
||||
Diagnostics Error;
|
||||
CodeTokenizer Tokenizer(Code, &Error, CompletionOffset);
|
||||
RegistrySema S;
|
||||
Parser P(&Tokenizer, &S, &Error);
|
||||
Parser P(&Tokenizer, S, NamedValues, &Error);
|
||||
VariantValue Dummy;
|
||||
P.parseExpressionImpl(&Dummy);
|
||||
|
||||
// Sort by specificity, then by name.
|
||||
std::sort(P.Completions.begin(), P.Completions.end(),
|
||||
[](const MatcherCompletion &A, const MatcherCompletion &B) {
|
||||
if (A.Specificity != B.Specificity)
|
||||
return A.Specificity > B.Specificity;
|
||||
return A.TypedText < B.TypedText;
|
||||
});
|
||||
|
||||
return P.Completions;
|
||||
}
|
||||
|
||||
llvm::Optional<DynTypedMatcher>
|
||||
Parser::parseMatcherExpression(StringRef Code, Diagnostics *Error) {
|
||||
RegistrySema S;
|
||||
return parseMatcherExpression(Code, &S, Error);
|
||||
}
|
||||
|
||||
llvm::Optional<DynTypedMatcher>
|
||||
Parser::parseMatcherExpression(StringRef Code, Parser::Sema *S,
|
||||
Parser::parseMatcherExpression(StringRef Code, Sema *S,
|
||||
const NamedValueMap *NamedValues,
|
||||
Diagnostics *Error) {
|
||||
VariantValue Value;
|
||||
if (!parseExpression(Code, S, &Value, Error))
|
||||
if (!parseExpression(Code, S, NamedValues, &Value, Error))
|
||||
return llvm::Optional<DynTypedMatcher>();
|
||||
if (!Value.isMatcher()) {
|
||||
Error->addError(SourceRange(), Error->ET_ParserNotAMatcher);
|
||||
|
|
|
@ -353,77 +353,63 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
|
|||
return OS;
|
||||
}
|
||||
|
||||
struct ReverseSpecificityThenName {
|
||||
bool operator()(const std::pair<unsigned, std::string> &A,
|
||||
const std::pair<unsigned, std::string> &B) const {
|
||||
return A.first > B.first || (A.first == B.first && A.second < B.second);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
}
|
||||
|
||||
std::vector<MatcherCompletion> Registry::getCompletions(
|
||||
ArrayRef<std::pair<MatcherCtor, unsigned> > Context) {
|
||||
std::vector<ArgKind> Registry::getAcceptedCompletionTypes(
|
||||
ArrayRef<std::pair<MatcherCtor, unsigned>> Context) {
|
||||
ASTNodeKind InitialTypes[] = {
|
||||
ASTNodeKind::getFromNodeKind<Decl>(),
|
||||
ASTNodeKind::getFromNodeKind<QualType>(),
|
||||
ASTNodeKind::getFromNodeKind<Type>(),
|
||||
ASTNodeKind::getFromNodeKind<Stmt>(),
|
||||
ASTNodeKind::getFromNodeKind<NestedNameSpecifier>(),
|
||||
ASTNodeKind::getFromNodeKind<NestedNameSpecifierLoc>(),
|
||||
ASTNodeKind::getFromNodeKind<TypeLoc>()
|
||||
};
|
||||
ArrayRef<ASTNodeKind> InitialTypesRef(InitialTypes);
|
||||
ASTNodeKind::getFromNodeKind<Decl>(),
|
||||
ASTNodeKind::getFromNodeKind<QualType>(),
|
||||
ASTNodeKind::getFromNodeKind<Type>(),
|
||||
ASTNodeKind::getFromNodeKind<Stmt>(),
|
||||
ASTNodeKind::getFromNodeKind<NestedNameSpecifier>(),
|
||||
ASTNodeKind::getFromNodeKind<NestedNameSpecifierLoc>(),
|
||||
ASTNodeKind::getFromNodeKind<TypeLoc>()};
|
||||
|
||||
// Starting with the above seed of acceptable top-level matcher types, compute
|
||||
// the acceptable type set for the argument indicated by each context element.
|
||||
std::set<ASTNodeKind> TypeSet(InitialTypesRef.begin(), InitialTypesRef.end());
|
||||
for (ArrayRef<std::pair<MatcherCtor, unsigned> >::iterator
|
||||
CtxI = Context.begin(),
|
||||
CtxE = Context.end();
|
||||
CtxI != CtxE; ++CtxI) {
|
||||
std::vector<internal::ArgKind> NextTypeSet;
|
||||
for (std::set<ASTNodeKind>::iterator I = TypeSet.begin(), E = TypeSet.end();
|
||||
I != E; ++I) {
|
||||
if (CtxI->first->isConvertibleTo(*I) &&
|
||||
(CtxI->first->isVariadic() ||
|
||||
CtxI->second < CtxI->first->getNumArgs()))
|
||||
CtxI->first->getArgKinds(*I, CtxI->second, NextTypeSet);
|
||||
std::set<ArgKind> TypeSet(std::begin(InitialTypes), std::end(InitialTypes));
|
||||
for (const auto &CtxEntry : Context) {
|
||||
MatcherCtor Ctor = CtxEntry.first;
|
||||
unsigned ArgNumber = CtxEntry.second;
|
||||
std::vector<ArgKind> NextTypeSet;
|
||||
for (const ArgKind &Kind : TypeSet) {
|
||||
if (Kind.getArgKind() == Kind.AK_Matcher &&
|
||||
Ctor->isConvertibleTo(Kind.getMatcherKind()) &&
|
||||
(Ctor->isVariadic() || ArgNumber < Ctor->getNumArgs()))
|
||||
Ctor->getArgKinds(Kind.getMatcherKind(), ArgNumber, NextTypeSet);
|
||||
}
|
||||
TypeSet.clear();
|
||||
for (std::vector<internal::ArgKind>::iterator I = NextTypeSet.begin(),
|
||||
E = NextTypeSet.end();
|
||||
I != E; ++I) {
|
||||
if (I->getArgKind() == internal::ArgKind::AK_Matcher)
|
||||
TypeSet.insert(I->getMatcherKind());
|
||||
}
|
||||
TypeSet.insert(NextTypeSet.begin(), NextTypeSet.end());
|
||||
}
|
||||
return std::vector<ArgKind>(TypeSet.begin(), TypeSet.end());
|
||||
}
|
||||
|
||||
typedef std::map<std::pair<unsigned, std::string>, MatcherCompletion,
|
||||
ReverseSpecificityThenName> CompletionsTy;
|
||||
CompletionsTy Completions;
|
||||
std::vector<MatcherCompletion>
|
||||
Registry::getMatcherCompletions(ArrayRef<ArgKind> AcceptedTypes) {
|
||||
std::vector<MatcherCompletion> Completions;
|
||||
|
||||
// TypeSet now contains the list of acceptable types for the argument we are
|
||||
// completing. Search the registry for acceptable matchers.
|
||||
// Search the registry for acceptable matchers.
|
||||
for (ConstructorMap::const_iterator I = RegistryData->constructors().begin(),
|
||||
E = RegistryData->constructors().end();
|
||||
I != E; ++I) {
|
||||
std::set<ASTNodeKind> RetKinds;
|
||||
unsigned NumArgs = I->second->isVariadic() ? 1 : I->second->getNumArgs();
|
||||
bool IsPolymorphic = I->second->isPolymorphic();
|
||||
std::vector<std::vector<internal::ArgKind> > ArgsKinds(NumArgs);
|
||||
std::vector<std::vector<ArgKind>> ArgsKinds(NumArgs);
|
||||
unsigned MaxSpecificity = 0;
|
||||
for (std::set<ASTNodeKind>::iterator TI = TypeSet.begin(),
|
||||
TE = TypeSet.end();
|
||||
TI != TE; ++TI) {
|
||||
for (const ArgKind& Kind : AcceptedTypes) {
|
||||
if (Kind.getArgKind() != Kind.AK_Matcher)
|
||||
continue;
|
||||
unsigned Specificity;
|
||||
ASTNodeKind LeastDerivedKind;
|
||||
if (I->second->isConvertibleTo(*TI, &Specificity, &LeastDerivedKind)) {
|
||||
if (I->second->isConvertibleTo(Kind.getMatcherKind(), &Specificity,
|
||||
&LeastDerivedKind)) {
|
||||
if (MaxSpecificity < Specificity)
|
||||
MaxSpecificity = Specificity;
|
||||
RetKinds.insert(LeastDerivedKind);
|
||||
for (unsigned Arg = 0; Arg != NumArgs; ++Arg)
|
||||
I->second->getArgKinds(*TI, Arg, ArgsKinds[Arg]);
|
||||
I->second->getArgKinds(Kind.getMatcherKind(), Arg, ArgsKinds[Arg]);
|
||||
if (IsPolymorphic)
|
||||
break;
|
||||
}
|
||||
|
@ -437,24 +423,20 @@ std::vector<MatcherCompletion> Registry::getCompletions(
|
|||
OS << "Matcher<T> " << I->first() << "(Matcher<T>";
|
||||
} else {
|
||||
OS << "Matcher<" << RetKinds << "> " << I->first() << "(";
|
||||
for (std::vector<std::vector<internal::ArgKind> >::iterator
|
||||
KI = ArgsKinds.begin(),
|
||||
KE = ArgsKinds.end();
|
||||
KI != KE; ++KI) {
|
||||
if (KI != ArgsKinds.begin())
|
||||
for (const std::vector<ArgKind> &Arg : ArgsKinds) {
|
||||
if (&Arg != &ArgsKinds[0])
|
||||
OS << ", ";
|
||||
// This currently assumes that a matcher may not overload a
|
||||
// non-matcher, and all non-matcher overloads have identical
|
||||
// arguments.
|
||||
if ((*KI)[0].getArgKind() == internal::ArgKind::AK_Matcher) {
|
||||
if (Arg[0].getArgKind() == ArgKind::AK_Matcher) {
|
||||
std::set<ASTNodeKind> MatcherKinds;
|
||||
std::transform(
|
||||
KI->begin(), KI->end(),
|
||||
std::inserter(MatcherKinds, MatcherKinds.end()),
|
||||
std::mem_fun_ref(&internal::ArgKind::getMatcherKind));
|
||||
std::transform(Arg.begin(), Arg.end(),
|
||||
std::inserter(MatcherKinds, MatcherKinds.end()),
|
||||
std::mem_fun_ref(&ArgKind::getMatcherKind));
|
||||
OS << "Matcher<" << MatcherKinds << ">";
|
||||
} else {
|
||||
OS << (*KI)[0].asString();
|
||||
OS << Arg[0].asString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -466,19 +448,14 @@ std::vector<MatcherCompletion> Registry::getCompletions(
|
|||
TypedText += "(";
|
||||
if (ArgsKinds.empty())
|
||||
TypedText += ")";
|
||||
else if (ArgsKinds[0][0].getArgKind() == internal::ArgKind::AK_String)
|
||||
else if (ArgsKinds[0][0].getArgKind() == ArgKind::AK_String)
|
||||
TypedText += "\"";
|
||||
|
||||
Completions[std::make_pair(MaxSpecificity, I->first())] =
|
||||
MatcherCompletion(TypedText, OS.str());
|
||||
Completions.emplace_back(TypedText, OS.str(), MaxSpecificity);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<MatcherCompletion> RetVal;
|
||||
for (CompletionsTy::iterator I = Completions.begin(), E = Completions.end();
|
||||
I != E; ++I)
|
||||
RetVal.push_back(I->second);
|
||||
return RetVal;
|
||||
return Completions;
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
@ -20,6 +20,35 @@ namespace clang {
|
|||
namespace ast_matchers {
|
||||
namespace dynamic {
|
||||
|
||||
std::string ArgKind::asString() const {
|
||||
switch (getArgKind()) {
|
||||
case AK_Matcher:
|
||||
return (Twine("Matcher<") + MatcherKind.asStringRef() + ">").str();
|
||||
case AK_Unsigned:
|
||||
return "unsigned";
|
||||
case AK_String:
|
||||
return "string";
|
||||
}
|
||||
llvm_unreachable("unhandled ArgKind");
|
||||
}
|
||||
|
||||
bool ArgKind::isConvertibleTo(ArgKind To, unsigned *Specificity) const {
|
||||
if (K != To.K)
|
||||
return false;
|
||||
if (K != AK_Matcher) {
|
||||
if (Specificity)
|
||||
*Specificity = 1;
|
||||
return true;
|
||||
}
|
||||
unsigned Distance;
|
||||
if (!MatcherKind.isBaseOf(To.MatcherKind, &Distance))
|
||||
return false;
|
||||
|
||||
if (Specificity)
|
||||
*Specificity = 100 - Distance;
|
||||
return true;
|
||||
}
|
||||
|
||||
VariantMatcher::MatcherOps::~MatcherOps() {}
|
||||
VariantMatcher::Payload::~Payload() {}
|
||||
|
||||
|
@ -27,21 +56,27 @@ class VariantMatcher::SinglePayload : public VariantMatcher::Payload {
|
|||
public:
|
||||
SinglePayload(const DynTypedMatcher &Matcher) : Matcher(Matcher) {}
|
||||
|
||||
virtual llvm::Optional<DynTypedMatcher> getSingleMatcher() const {
|
||||
llvm::Optional<DynTypedMatcher> getSingleMatcher() const override {
|
||||
return Matcher;
|
||||
}
|
||||
|
||||
virtual std::string getTypeAsString() const {
|
||||
std::string getTypeAsString() const override {
|
||||
return (Twine("Matcher<") + Matcher.getSupportedKind().asStringRef() + ">")
|
||||
.str();
|
||||
}
|
||||
|
||||
virtual void makeTypedMatcher(MatcherOps &Ops) const {
|
||||
void makeTypedMatcher(MatcherOps &Ops) const override {
|
||||
bool Ignore;
|
||||
if (Ops.canConstructFrom(Matcher, Ignore))
|
||||
Ops.constructFrom(Matcher);
|
||||
}
|
||||
|
||||
bool isConvertibleTo(ast_type_traits::ASTNodeKind Kind,
|
||||
unsigned *Specificity) const override {
|
||||
return ArgKind(Matcher.getSupportedKind())
|
||||
.isConvertibleTo(Kind, Specificity);
|
||||
}
|
||||
|
||||
private:
|
||||
const DynTypedMatcher Matcher;
|
||||
};
|
||||
|
@ -51,15 +86,15 @@ public:
|
|||
PolymorphicPayload(std::vector<DynTypedMatcher> MatchersIn)
|
||||
: Matchers(std::move(MatchersIn)) {}
|
||||
|
||||
virtual ~PolymorphicPayload() {}
|
||||
~PolymorphicPayload() override {}
|
||||
|
||||
virtual llvm::Optional<DynTypedMatcher> getSingleMatcher() const {
|
||||
llvm::Optional<DynTypedMatcher> getSingleMatcher() const override {
|
||||
if (Matchers.size() != 1)
|
||||
return llvm::Optional<DynTypedMatcher>();
|
||||
return Matchers[0];
|
||||
}
|
||||
|
||||
virtual std::string getTypeAsString() const {
|
||||
std::string getTypeAsString() const override {
|
||||
std::string Inner;
|
||||
for (size_t i = 0, e = Matchers.size(); i != e; ++i) {
|
||||
if (i != 0)
|
||||
|
@ -69,7 +104,7 @@ public:
|
|||
return (Twine("Matcher<") + Inner + ">").str();
|
||||
}
|
||||
|
||||
virtual void makeTypedMatcher(MatcherOps &Ops) const {
|
||||
void makeTypedMatcher(MatcherOps &Ops) const override {
|
||||
bool FoundIsExact = false;
|
||||
const DynTypedMatcher *Found = nullptr;
|
||||
int NumFound = 0;
|
||||
|
@ -92,6 +127,21 @@ public:
|
|||
Ops.constructFrom(*Found);
|
||||
}
|
||||
|
||||
bool isConvertibleTo(ast_type_traits::ASTNodeKind Kind,
|
||||
unsigned *Specificity) const override {
|
||||
unsigned MaxSpecificity = 0;
|
||||
for (const DynTypedMatcher &Matcher : Matchers) {
|
||||
unsigned ThisSpecificity;
|
||||
if (ArgKind(Matcher.getSupportedKind())
|
||||
.isConvertibleTo(Kind, &ThisSpecificity)) {
|
||||
MaxSpecificity = std::max(MaxSpecificity, ThisSpecificity);
|
||||
}
|
||||
}
|
||||
if (Specificity)
|
||||
*Specificity = MaxSpecificity;
|
||||
return MaxSpecificity > 0;
|
||||
}
|
||||
|
||||
const std::vector<DynTypedMatcher> Matchers;
|
||||
};
|
||||
|
||||
|
@ -101,11 +151,11 @@ public:
|
|||
std::vector<VariantMatcher> Args)
|
||||
: Func(Func), Args(std::move(Args)) {}
|
||||
|
||||
virtual llvm::Optional<DynTypedMatcher> getSingleMatcher() const {
|
||||
llvm::Optional<DynTypedMatcher> getSingleMatcher() const override {
|
||||
return llvm::Optional<DynTypedMatcher>();
|
||||
}
|
||||
|
||||
virtual std::string getTypeAsString() const {
|
||||
std::string getTypeAsString() const override {
|
||||
std::string Inner;
|
||||
for (size_t i = 0, e = Args.size(); i != e; ++i) {
|
||||
if (i != 0)
|
||||
|
@ -115,10 +165,19 @@ public:
|
|||
return Inner;
|
||||
}
|
||||
|
||||
virtual void makeTypedMatcher(MatcherOps &Ops) const {
|
||||
void makeTypedMatcher(MatcherOps &Ops) const override {
|
||||
Ops.constructVariadicOperator(Func, Args);
|
||||
}
|
||||
|
||||
bool isConvertibleTo(ast_type_traits::ASTNodeKind Kind,
|
||||
unsigned *Specificity) const override {
|
||||
for (const VariantMatcher &Matcher : Args) {
|
||||
if (!Matcher.isConvertibleTo(Kind, Specificity))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const ast_matchers::internal::VariadicOperatorFunction Func;
|
||||
const std::vector<VariantMatcher> Args;
|
||||
|
@ -251,6 +310,43 @@ void VariantValue::setMatcher(const VariantMatcher &NewValue) {
|
|||
Value.Matcher = new VariantMatcher(NewValue);
|
||||
}
|
||||
|
||||
bool VariantValue::isConvertibleTo(ArgKind Kind, unsigned *Specificity) const {
|
||||
switch (Kind.getArgKind()) {
|
||||
case ArgKind::AK_Unsigned:
|
||||
if (!isUnsigned())
|
||||
return false;
|
||||
*Specificity = 1;
|
||||
return true;
|
||||
|
||||
case ArgKind::AK_String:
|
||||
if (!isString())
|
||||
return false;
|
||||
*Specificity = 1;
|
||||
return true;
|
||||
|
||||
case ArgKind::AK_Matcher:
|
||||
if (!isMatcher())
|
||||
return false;
|
||||
return getMatcher().isConvertibleTo(Kind.getMatcherKind(), Specificity);
|
||||
}
|
||||
llvm_unreachable("Invalid Type");
|
||||
}
|
||||
|
||||
bool VariantValue::isConvertibleTo(ArrayRef<ArgKind> Kinds,
|
||||
unsigned *Specificity) const {
|
||||
unsigned MaxSpecificity = 0;
|
||||
for (const ArgKind& Kind : Kinds) {
|
||||
unsigned ThisSpecificity;
|
||||
if (!isConvertibleTo(Kind, &ThisSpecificity))
|
||||
continue;
|
||||
MaxSpecificity = std::max(MaxSpecificity, ThisSpecificity);
|
||||
}
|
||||
if (Specificity && MaxSpecificity > 0) {
|
||||
*Specificity = MaxSpecificity;
|
||||
}
|
||||
return MaxSpecificity > 0;
|
||||
}
|
||||
|
||||
std::string VariantValue::getTypeAsString() const {
|
||||
switch (Type) {
|
||||
case VT_String: return "String";
|
||||
|
|
|
@ -152,6 +152,14 @@ TEST(ParserTest, ParseMatcher) {
|
|||
|
||||
using ast_matchers::internal::Matcher;
|
||||
|
||||
Parser::NamedValueMap getTestNamedValues() {
|
||||
Parser::NamedValueMap Values;
|
||||
Values["nameX"] = std::string("x");
|
||||
Values["hasParamA"] =
|
||||
VariantMatcher::SingleMatcher(hasParameter(0, hasName("a")));
|
||||
return Values;
|
||||
}
|
||||
|
||||
TEST(ParserTest, FullParserTest) {
|
||||
Diagnostics Error;
|
||||
llvm::Optional<DynTypedMatcher> VarDecl(Parser::parseMatcherExpression(
|
||||
|
@ -174,21 +182,11 @@ TEST(ParserTest, FullParserTest) {
|
|||
EXPECT_FALSE(matches("void f(int x, int a);", M));
|
||||
|
||||
// Test named values.
|
||||
struct NamedSema : public Parser::RegistrySema {
|
||||
public:
|
||||
virtual VariantValue getNamedValue(StringRef Name) {
|
||||
if (Name == "nameX")
|
||||
return std::string("x");
|
||||
if (Name == "param0")
|
||||
return VariantMatcher::SingleMatcher(hasParameter(0, hasName("a")));
|
||||
return VariantValue();
|
||||
}
|
||||
};
|
||||
NamedSema Sema;
|
||||
auto NamedValues = getTestNamedValues();
|
||||
llvm::Optional<DynTypedMatcher> HasParameterWithNamedValues(
|
||||
Parser::parseMatcherExpression(
|
||||
"functionDecl(param0, hasParameter(1, hasName(nameX)))", &Sema,
|
||||
&Error));
|
||||
"functionDecl(hasParamA, hasParameter(1, hasName(nameX)))",
|
||||
nullptr, &NamedValues, &Error));
|
||||
EXPECT_EQ("", Error.toStringFull());
|
||||
M = HasParameterWithNamedValues->unconditionalConvertTo<Decl>();
|
||||
|
||||
|
@ -270,7 +268,7 @@ TEST(ParserTest, OverloadErrors) {
|
|||
ParseWithError("callee(\"A\")"));
|
||||
}
|
||||
|
||||
TEST(ParserTest, Completion) {
|
||||
TEST(ParserTest, CompletionRegistry) {
|
||||
std::vector<MatcherCompletion> Comps =
|
||||
Parser::completeExpression("while", 5);
|
||||
ASSERT_EQ(1u, Comps.size());
|
||||
|
@ -284,6 +282,38 @@ TEST(ParserTest, Completion) {
|
|||
EXPECT_EQ("bind", Comps[0].MatcherDecl);
|
||||
}
|
||||
|
||||
TEST(ParserTest, CompletionNamedValues) {
|
||||
// Can complete non-matcher types.
|
||||
auto NamedValues = getTestNamedValues();
|
||||
StringRef Code = "functionDecl(hasName(";
|
||||
std::vector<MatcherCompletion> Comps =
|
||||
Parser::completeExpression(Code, Code.size(), nullptr, &NamedValues);
|
||||
ASSERT_EQ(1u, Comps.size());
|
||||
EXPECT_EQ("nameX", Comps[0].TypedText);
|
||||
EXPECT_EQ("String nameX", Comps[0].MatcherDecl);
|
||||
|
||||
// Can complete if there are names in the expression.
|
||||
Code = "methodDecl(hasName(nameX), ";
|
||||
Comps = Parser::completeExpression(Code, Code.size(), nullptr, &NamedValues);
|
||||
EXPECT_LT(0u, Comps.size());
|
||||
|
||||
// Can complete names and registry together.
|
||||
Code = "methodDecl(hasP";
|
||||
Comps = Parser::completeExpression(Code, Code.size(), nullptr, &NamedValues);
|
||||
ASSERT_EQ(3u, Comps.size());
|
||||
EXPECT_EQ("aramA", Comps[0].TypedText);
|
||||
EXPECT_EQ("Matcher<FunctionDecl> hasParamA", Comps[0].MatcherDecl);
|
||||
|
||||
EXPECT_EQ("arameter(", Comps[1].TypedText);
|
||||
EXPECT_EQ(
|
||||
"Matcher<FunctionDecl> hasParameter(unsigned, Matcher<ParmVarDecl>)",
|
||||
Comps[1].MatcherDecl);
|
||||
|
||||
EXPECT_EQ("arent(", Comps[2].TypedText);
|
||||
EXPECT_EQ("Matcher<Decl> hasParent(Matcher<Decl|Stmt>)",
|
||||
Comps[2].MatcherDecl);
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
} // end namespace dynamic
|
||||
} // end namespace ast_matchers
|
||||
|
|
|
@ -82,8 +82,9 @@ public:
|
|||
typedef std::vector<MatcherCompletion> CompVector;
|
||||
|
||||
CompVector getCompletions() {
|
||||
return Registry::getCompletions(
|
||||
ArrayRef<std::pair<MatcherCtor, unsigned> >());
|
||||
std::vector<std::pair<MatcherCtor, unsigned> > Context;
|
||||
return Registry::getMatcherCompletions(
|
||||
Registry::getAcceptedCompletionTypes(Context));
|
||||
}
|
||||
|
||||
CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1) {
|
||||
|
@ -92,7 +93,8 @@ public:
|
|||
if (!Ctor)
|
||||
return CompVector();
|
||||
Context.push_back(std::make_pair(*Ctor, ArgNo1));
|
||||
return Registry::getCompletions(Context);
|
||||
return Registry::getMatcherCompletions(
|
||||
Registry::getAcceptedCompletionTypes(Context));
|
||||
}
|
||||
|
||||
CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1,
|
||||
|
@ -106,18 +108,16 @@ public:
|
|||
if (!Ctor)
|
||||
return CompVector();
|
||||
Context.push_back(std::make_pair(*Ctor, ArgNo2));
|
||||
return Registry::getCompletions(Context);
|
||||
return Registry::getMatcherCompletions(
|
||||
Registry::getAcceptedCompletionTypes(Context));
|
||||
}
|
||||
|
||||
bool hasCompletion(const CompVector &Comps, StringRef TypedText,
|
||||
StringRef MatcherDecl = StringRef(),
|
||||
unsigned *Index = nullptr) {
|
||||
StringRef MatcherDecl = StringRef()) {
|
||||
for (CompVector::const_iterator I = Comps.begin(), E = Comps.end(); I != E;
|
||||
++I) {
|
||||
if (I->TypedText == TypedText &&
|
||||
(MatcherDecl.empty() || I->MatcherDecl == MatcherDecl)) {
|
||||
if (Index)
|
||||
*Index = I - Comps.begin();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -445,17 +445,12 @@ TEST_F(RegistryTest, Completion) {
|
|||
|
||||
CompVector WhileComps = getCompletions("whileStmt", 0);
|
||||
|
||||
unsigned HasBodyIndex, HasParentIndex, AllOfIndex;
|
||||
EXPECT_TRUE(hasCompletion(WhileComps, "hasBody(",
|
||||
"Matcher<WhileStmt> hasBody(Matcher<Stmt>)",
|
||||
&HasBodyIndex));
|
||||
"Matcher<WhileStmt> hasBody(Matcher<Stmt>)"));
|
||||
EXPECT_TRUE(hasCompletion(WhileComps, "hasParent(",
|
||||
"Matcher<Stmt> hasParent(Matcher<Decl|Stmt>)",
|
||||
&HasParentIndex));
|
||||
EXPECT_TRUE(hasCompletion(WhileComps, "allOf(",
|
||||
"Matcher<T> allOf(Matcher<T>...)", &AllOfIndex));
|
||||
EXPECT_GT(HasParentIndex, HasBodyIndex);
|
||||
EXPECT_GT(AllOfIndex, HasParentIndex);
|
||||
"Matcher<Stmt> hasParent(Matcher<Decl|Stmt>)"));
|
||||
EXPECT_TRUE(
|
||||
hasCompletion(WhileComps, "allOf(", "Matcher<T> allOf(Matcher<T>...)"));
|
||||
|
||||
EXPECT_FALSE(hasCompletion(WhileComps, "whileStmt("));
|
||||
EXPECT_FALSE(hasCompletion(WhileComps, "ifStmt("));
|
||||
|
|
Loading…
Reference in New Issue