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:
Samuel Benzaquen 2014-08-12 21:11:37 +00:00
parent 4834ad2609
commit 646f23b809
9 changed files with 470 additions and 248 deletions

View File

@ -63,17 +63,6 @@ public:
public: public:
virtual ~Sema(); 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. /// \brief Process a matcher expression.
/// ///
/// All the arguments passed here have already been processed. /// All the arguments passed here have already been processed.
@ -105,6 +94,29 @@ public:
/// found. /// found.
virtual llvm::Optional<MatcherCtor> virtual llvm::Optional<MatcherCtor>
lookupMatcherCtor(StringRef MatcherName) = 0; 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 /// \brief Sema implementation that uses the matcher registry to process the
@ -121,58 +133,91 @@ public:
StringRef BindID, StringRef BindID,
ArrayRef<ParserValue> Args, ArrayRef<ParserValue> Args,
Diagnostics *Error) override; 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. typedef llvm::StringMap<VariantValue> NamedValueMap;
///
/// 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);
/// \brief Parse a matcher expression. /// \brief Parse a matcher expression.
/// ///
/// \param MatcherCode The matcher expression to parse. /// \param MatcherCode The matcher expression to parse.
/// ///
/// \param S The Sema instance that will help the parser /// \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 /// \return The matcher object constructed by the processor, or an empty
/// Optional if an error occurred. In that case, \c Error will contain a /// Optional if an error occurred. In that case, \c Error will contain a
/// description of the error. /// description of the error.
/// The caller takes ownership of the DynTypedMatcher object returned. /// The caller takes ownership of the DynTypedMatcher object returned.
static llvm::Optional<DynTypedMatcher> static llvm::Optional<DynTypedMatcher>
parseMatcherExpression(StringRef MatcherCode, Sema *S, Diagnostics *Error); parseMatcherExpression(StringRef MatcherCode, Sema *S,
const NamedValueMap *NamedValues,
/// \brief Parse an expression, creating matchers from the registry. Diagnostics *Error);
/// static llvm::Optional<DynTypedMatcher>
/// Parses any expression supported by this parser. In general, the parseMatcherExpression(StringRef MatcherCode, Sema *S,
/// \c parseMatcherExpression function is a better approach to get a matcher Diagnostics *Error) {
/// object. return parseMatcherExpression(MatcherCode, S, nullptr, Error);
static bool parseExpression(StringRef Code, VariantValue *Value, }
Diagnostics *Error); static llvm::Optional<DynTypedMatcher>
parseMatcherExpression(StringRef MatcherCode, Diagnostics *Error) {
return parseMatcherExpression(MatcherCode, nullptr, Error);
}
/// \brief Parse an expression. /// \brief Parse an expression.
/// ///
/// Parses any expression supported by this parser. In general, the /// Parses any expression supported by this parser. In general, the
/// \c parseMatcherExpression function is a better approach to get a matcher /// \c parseMatcherExpression function is a better approach to get a matcher
/// object. /// 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, static bool parseExpression(StringRef Code, Sema *S,
const NamedValueMap *NamedValues,
VariantValue *Value, Diagnostics *Error); 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. /// \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 /// \return The list of completions, which may be empty if there are no
/// available completions or if an error occurred. /// available completions or if an error occurred.
static std::vector<MatcherCompletion> 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: private:
class CodeTokenizer; class CodeTokenizer;
@ -180,6 +225,7 @@ private:
struct TokenInfo; struct TokenInfo;
Parser(CodeTokenizer *Tokenizer, Sema *S, Parser(CodeTokenizer *Tokenizer, Sema *S,
const NamedValueMap *NamedValues,
Diagnostics *Error); Diagnostics *Error);
bool parseExpressionImpl(VariantValue *Value); bool parseExpressionImpl(VariantValue *Value);
@ -187,12 +233,16 @@ private:
VariantValue *Value); VariantValue *Value);
bool parseIdentifierPrefixImpl(VariantValue *Value); bool parseIdentifierPrefixImpl(VariantValue *Value);
void addCompletion(const TokenInfo &CompToken, StringRef TypedText, void addCompletion(const TokenInfo &CompToken,
StringRef Decl); const MatcherCompletion &Completion);
void addExpressionCompletions(); void addExpressionCompletions();
std::vector<MatcherCompletion>
getNamedValueCompletions(ArrayRef<ArgKind> AcceptedTypes);
CodeTokenizer *const Tokenizer; CodeTokenizer *const Tokenizer;
Sema *const S; Sema *const S;
const NamedValueMap *const NamedValues;
Diagnostics *const Error; Diagnostics *const Error;
typedef std::vector<std::pair<MatcherCtor, unsigned> > ContextStackTy; typedef std::vector<std::pair<MatcherCtor, unsigned> > ContextStackTy;

View File

@ -36,8 +36,10 @@ typedef const internal::MatcherDescriptor *MatcherCtor;
struct MatcherCompletion { struct MatcherCompletion {
MatcherCompletion() {} MatcherCompletion() {}
MatcherCompletion(StringRef TypedText, StringRef MatcherDecl) MatcherCompletion(StringRef TypedText, StringRef MatcherDecl,
: TypedText(TypedText), MatcherDecl(MatcherDecl) {} unsigned Specificity)
: TypedText(TypedText), MatcherDecl(MatcherDecl),
Specificity(Specificity) {}
/// \brief The text to type to select this matcher. /// \brief The text to type to select this matcher.
std::string TypedText; std::string TypedText;
@ -45,6 +47,13 @@ struct MatcherCompletion {
/// \brief The "declaration" of the matcher, with type information. /// \brief The "declaration" of the matcher, with type information.
std::string MatcherDecl; 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 { bool operator==(const MatcherCompletion &Other) const {
return TypedText == Other.TypedText && MatcherDecl == Other.MatcherDecl; return TypedText == Other.TypedText && MatcherDecl == Other.MatcherDecl;
} }
@ -58,28 +67,28 @@ public:
/// constructor, or Optional<MatcherCtor>() if not found. /// constructor, or Optional<MatcherCtor>() if not found.
static llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName); 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 /// Each element of \p Context represents a matcher invocation, going from
/// outermost to innermost. Elements are pairs consisting of a reference to the /// outermost to innermost. Elements are pairs consisting of a reference to
/// matcher constructor and the index of the next element in the argument list /// the matcher constructor and the index of the next element in the
/// of that matcher (or for the last element, the index of the completion /// argument list of that matcher (or for the last element, the index of
/// point in the argument list). An empty list requests completion for the /// the completion point in the argument list). An empty list requests
/// root matcher. /// 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 /// \param All types accepted for this completion.
/// 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.
/// ///
/// Matchers which are technically convertible to the innermost context but /// \return All completions for the specified types.
/// which would match either all or no nodes are excluded. For example, /// Completions should be valid when used in \c lookupMatcherCtor().
/// namedDecl and varDecl are excluded in a FunctionDecl context, because /// The matcher constructed from the return of \c lookupMatcherCtor()
/// those matchers would match respectively all or no nodes in such a context. /// should be convertible to some type in \p AcceptedTypes.
static std::vector<MatcherCompletion> static std::vector<MatcherCompletion>
getCompletions(ArrayRef<std::pair<MatcherCtor, unsigned> > Context); getMatcherCompletions(ArrayRef<ArgKind> AcceptedTypes);
/// \brief Construct a matcher from the registry. /// \brief Construct a matcher from the registry.
/// ///

View File

@ -29,6 +29,50 @@ namespace clang {
namespace ast_matchers { namespace ast_matchers {
namespace dynamic { 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; using ast_matchers::internal::DynTypedMatcher;
/// \brief A variant matcher object. /// \brief A variant matcher object.
@ -66,6 +110,8 @@ class VariantMatcher {
virtual llvm::Optional<DynTypedMatcher> getSingleMatcher() const = 0; virtual llvm::Optional<DynTypedMatcher> getSingleMatcher() const = 0;
virtual std::string getTypeAsString() const = 0; virtual std::string getTypeAsString() const = 0;
virtual void makeTypedMatcher(MatcherOps &Ops) const = 0; virtual void makeTypedMatcher(MatcherOps &Ops) const = 0;
virtual bool isConvertibleTo(ast_type_traits::ASTNodeKind Kind,
unsigned *Specificity) const = 0;
}; };
public: public:
@ -116,6 +162,18 @@ public:
return Ops.hasMatcher(); 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>. /// \brief Return this matcher as a \c Matcher<T>.
/// ///
/// Handles the different types (Single, Polymorphic) accordingly. /// Handles the different types (Single, Polymorphic) accordingly.
@ -228,6 +286,22 @@ public:
const VariantMatcher &getMatcher() const; const VariantMatcher &getMatcher() const;
void setMatcher(const VariantMatcher &Matcher); 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. /// \brief String representation of the type of the value.
std::string getTypeAsString() const; std::string getTypeAsString() const;

View File

@ -30,48 +30,8 @@
namespace clang { namespace clang {
namespace ast_matchers { namespace ast_matchers {
namespace dynamic { namespace dynamic {
namespace internal { 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 /// \brief Helper template class to just from argument type to the right is/get
/// functions in VariantValue. /// functions in VariantValue.
@ -161,16 +121,10 @@ inline bool isRetKindConvertibleTo(
ArrayRef<ast_type_traits::ASTNodeKind> RetKinds, ArrayRef<ast_type_traits::ASTNodeKind> RetKinds,
ast_type_traits::ASTNodeKind Kind, unsigned *Specificity, ast_type_traits::ASTNodeKind Kind, unsigned *Specificity,
ast_type_traits::ASTNodeKind *LeastDerivedKind) { ast_type_traits::ASTNodeKind *LeastDerivedKind) {
for (ArrayRef<ast_type_traits::ASTNodeKind>::const_iterator for (const ast_type_traits::ASTNodeKind &NodeKind : RetKinds) {
i = RetKinds.begin(), if (ArgKind(NodeKind).isConvertibleTo(Kind, Specificity)) {
e = RetKinds.end();
i != e; ++i) {
unsigned Distance;
if (i->isBaseOf(Kind, &Distance)) {
if (Specificity)
*Specificity = 100 - Distance;
if (LeastDerivedKind) if (LeastDerivedKind)
*LeastDerivedKind = *i; *LeastDerivedKind = NodeKind;
return true; return true;
} }
} }

View File

@ -17,6 +17,7 @@
#include "clang/Basic/CharInfo.h" #include "clang/Basic/CharInfo.h"
#include "llvm/ADT/Optional.h" #include "llvm/ADT/Optional.h"
#include "llvm/ADT/Twine.h" #include "llvm/ADT/Twine.h"
#include "llvm/Support/ManagedStatic.h"
#include <string> #include <string>
#include <vector> #include <vector>
@ -258,8 +259,14 @@ private:
Parser::Sema::~Sema() {} Parser::Sema::~Sema() {}
VariantValue Parser::Sema::getNamedValue(StringRef Name) { std::vector<ArgKind> Parser::Sema::getAcceptedCompletionTypes(
return VariantValue(); 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 { struct Parser::ScopedContextEntry {
@ -288,7 +295,9 @@ bool Parser::parseIdentifierPrefixImpl(VariantValue *Value) {
if (Tokenizer->nextTokenKind() != TokenInfo::TK_OpenParen) { if (Tokenizer->nextTokenKind() != TokenInfo::TK_OpenParen) {
// Parse as a named value. // 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; *Value = NamedValue;
return true; return true;
} }
@ -379,7 +388,7 @@ bool Parser::parseMatcherExpressionImpl(const TokenInfo &NameToken,
Tokenizer->consumeNextToken(); // consume the period. Tokenizer->consumeNextToken(); // consume the period.
const TokenInfo BindToken = Tokenizer->consumeNextToken(); const TokenInfo BindToken = Tokenizer->consumeNextToken();
if (BindToken.Kind == TokenInfo::TK_CodeCompletion) { if (BindToken.Kind == TokenInfo::TK_CodeCompletion) {
addCompletion(BindToken, "bind(\"", "bind"); addCompletion(BindToken, MatcherCompletion("bind(\"", "bind", 1));
return false; 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 // If the prefix of this completion matches the completion token, add it to
// Completions minus the prefix. // Completions minus the prefix.
void Parser::addCompletion(const TokenInfo &CompToken, StringRef TypedText, void Parser::addCompletion(const TokenInfo &CompToken,
StringRef Decl) { const MatcherCompletion& Completion) {
if (TypedText.size() >= CompToken.Text.size() && if (StringRef(Completion.TypedText).startswith(CompToken.Text) &&
TypedText.substr(0, CompToken.Text.size()) == CompToken.Text) { Completion.Specificity > 0) {
Completions.push_back( Completions.emplace_back(Completion.TypedText.substr(CompToken.Text.size()),
MatcherCompletion(TypedText.substr(CompToken.Text.size()), Decl)); 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() { void Parser::addExpressionCompletions() {
const TokenInfo CompToken = Tokenizer->consumeNextToken(); const TokenInfo CompToken = Tokenizer->consumeNextToken();
assert(CompToken.Kind == TokenInfo::TK_CodeCompletion); assert(CompToken.Kind == TokenInfo::TK_CodeCompletion);
@ -449,12 +473,13 @@ void Parser::addExpressionCompletions() {
return; return;
} }
std::vector<MatcherCompletion> RegCompletions = auto AcceptedTypes = S->getAcceptedCompletionTypes(ContextStack);
Registry::getCompletions(ContextStack); for (const auto &Completion : S->getMatcherCompletions(AcceptedTypes)) {
for (std::vector<MatcherCompletion>::iterator I = RegCompletions.begin(), addCompletion(CompToken, Completion);
E = RegCompletions.end(); }
I != E; ++I) {
addCompletion(CompToken, I->TypedText, I->MatcherDecl); for (const auto &Completion : getNamedValueCompletions(AcceptedTypes)) {
addCompletion(CompToken, Completion);
} }
} }
@ -494,9 +519,12 @@ bool Parser::parseExpressionImpl(VariantValue *Value) {
llvm_unreachable("Unknown token kind."); llvm_unreachable("Unknown token kind.");
} }
static llvm::ManagedStatic<Parser::RegistrySema> DefaultRegistrySema;
Parser::Parser(CodeTokenizer *Tokenizer, Sema *S, Parser::Parser(CodeTokenizer *Tokenizer, Sema *S,
Diagnostics *Error) const NamedValueMap *NamedValues, Diagnostics *Error)
: Tokenizer(Tokenizer), S(S), Error(Error) {} : Tokenizer(Tokenizer), S(S ? S : &*DefaultRegistrySema),
NamedValues(NamedValues), Error(Error) {}
Parser::RegistrySema::~RegistrySema() {} Parser::RegistrySema::~RegistrySema() {}
@ -516,16 +544,22 @@ VariantMatcher Parser::RegistrySema::actOnMatcherExpression(
} }
} }
bool Parser::parseExpression(StringRef Code, VariantValue *Value, std::vector<ArgKind> Parser::RegistrySema::getAcceptedCompletionTypes(
Diagnostics *Error) { ArrayRef<std::pair<MatcherCtor, unsigned>> Context) {
RegistrySema S; return Registry::getAcceptedCompletionTypes(Context);
return parseExpression(Code, &S, Value, Error); }
std::vector<MatcherCompletion> Parser::RegistrySema::getMatcherCompletions(
ArrayRef<ArgKind> AcceptedTypes) {
return Registry::getMatcherCompletions(AcceptedTypes);
} }
bool Parser::parseExpression(StringRef Code, Sema *S, bool Parser::parseExpression(StringRef Code, Sema *S,
const NamedValueMap *NamedValues,
VariantValue *Value, Diagnostics *Error) { VariantValue *Value, Diagnostics *Error) {
CodeTokenizer Tokenizer(Code, 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) { if (Tokenizer.peekNextToken().Kind != TokenInfo::TK_Eof) {
Error->addError(Tokenizer.peekNextToken().Range, Error->addError(Tokenizer.peekNextToken().Range,
Error->ET_ParserTrailingCode); Error->ET_ParserTrailingCode);
@ -535,28 +569,31 @@ bool Parser::parseExpression(StringRef Code, Sema *S,
} }
std::vector<MatcherCompletion> std::vector<MatcherCompletion>
Parser::completeExpression(StringRef Code, unsigned CompletionOffset) { Parser::completeExpression(StringRef Code, unsigned CompletionOffset, Sema *S,
const NamedValueMap *NamedValues) {
Diagnostics Error; Diagnostics Error;
CodeTokenizer Tokenizer(Code, &Error, CompletionOffset); CodeTokenizer Tokenizer(Code, &Error, CompletionOffset);
RegistrySema S; Parser P(&Tokenizer, S, NamedValues, &Error);
Parser P(&Tokenizer, &S, &Error);
VariantValue Dummy; VariantValue Dummy;
P.parseExpressionImpl(&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; return P.Completions;
} }
llvm::Optional<DynTypedMatcher> llvm::Optional<DynTypedMatcher>
Parser::parseMatcherExpression(StringRef Code, Diagnostics *Error) { Parser::parseMatcherExpression(StringRef Code, Sema *S,
RegistrySema S; const NamedValueMap *NamedValues,
return parseMatcherExpression(Code, &S, Error);
}
llvm::Optional<DynTypedMatcher>
Parser::parseMatcherExpression(StringRef Code, Parser::Sema *S,
Diagnostics *Error) { Diagnostics *Error) {
VariantValue Value; VariantValue Value;
if (!parseExpression(Code, S, &Value, Error)) if (!parseExpression(Code, S, NamedValues, &Value, Error))
return llvm::Optional<DynTypedMatcher>(); return llvm::Optional<DynTypedMatcher>();
if (!Value.isMatcher()) { if (!Value.isMatcher()) {
Error->addError(SourceRange(), Error->ET_ParserNotAMatcher); Error->addError(SourceRange(), Error->ET_ParserNotAMatcher);

View File

@ -353,77 +353,63 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
return OS; return OS;
} }
struct ReverseSpecificityThenName { } // namespace
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);
}
};
} std::vector<ArgKind> Registry::getAcceptedCompletionTypes(
ArrayRef<std::pair<MatcherCtor, unsigned>> Context) {
std::vector<MatcherCompletion> Registry::getCompletions(
ArrayRef<std::pair<MatcherCtor, unsigned> > Context) {
ASTNodeKind InitialTypes[] = { ASTNodeKind InitialTypes[] = {
ASTNodeKind::getFromNodeKind<Decl>(), ASTNodeKind::getFromNodeKind<Decl>(),
ASTNodeKind::getFromNodeKind<QualType>(), ASTNodeKind::getFromNodeKind<QualType>(),
ASTNodeKind::getFromNodeKind<Type>(), ASTNodeKind::getFromNodeKind<Type>(),
ASTNodeKind::getFromNodeKind<Stmt>(), ASTNodeKind::getFromNodeKind<Stmt>(),
ASTNodeKind::getFromNodeKind<NestedNameSpecifier>(), ASTNodeKind::getFromNodeKind<NestedNameSpecifier>(),
ASTNodeKind::getFromNodeKind<NestedNameSpecifierLoc>(), ASTNodeKind::getFromNodeKind<NestedNameSpecifierLoc>(),
ASTNodeKind::getFromNodeKind<TypeLoc>() ASTNodeKind::getFromNodeKind<TypeLoc>()};
};
ArrayRef<ASTNodeKind> InitialTypesRef(InitialTypes);
// Starting with the above seed of acceptable top-level matcher types, compute // Starting with the above seed of acceptable top-level matcher types, compute
// the acceptable type set for the argument indicated by each context element. // the acceptable type set for the argument indicated by each context element.
std::set<ASTNodeKind> TypeSet(InitialTypesRef.begin(), InitialTypesRef.end()); std::set<ArgKind> TypeSet(std::begin(InitialTypes), std::end(InitialTypes));
for (ArrayRef<std::pair<MatcherCtor, unsigned> >::iterator for (const auto &CtxEntry : Context) {
CtxI = Context.begin(), MatcherCtor Ctor = CtxEntry.first;
CtxE = Context.end(); unsigned ArgNumber = CtxEntry.second;
CtxI != CtxE; ++CtxI) { std::vector<ArgKind> NextTypeSet;
std::vector<internal::ArgKind> NextTypeSet; for (const ArgKind &Kind : TypeSet) {
for (std::set<ASTNodeKind>::iterator I = TypeSet.begin(), E = TypeSet.end(); if (Kind.getArgKind() == Kind.AK_Matcher &&
I != E; ++I) { Ctor->isConvertibleTo(Kind.getMatcherKind()) &&
if (CtxI->first->isConvertibleTo(*I) && (Ctor->isVariadic() || ArgNumber < Ctor->getNumArgs()))
(CtxI->first->isVariadic() || Ctor->getArgKinds(Kind.getMatcherKind(), ArgNumber, NextTypeSet);
CtxI->second < CtxI->first->getNumArgs()))
CtxI->first->getArgKinds(*I, CtxI->second, NextTypeSet);
} }
TypeSet.clear(); TypeSet.clear();
for (std::vector<internal::ArgKind>::iterator I = NextTypeSet.begin(), TypeSet.insert(NextTypeSet.begin(), NextTypeSet.end());
E = NextTypeSet.end();
I != E; ++I) {
if (I->getArgKind() == internal::ArgKind::AK_Matcher)
TypeSet.insert(I->getMatcherKind());
}
} }
return std::vector<ArgKind>(TypeSet.begin(), TypeSet.end());
}
typedef std::map<std::pair<unsigned, std::string>, MatcherCompletion, std::vector<MatcherCompletion>
ReverseSpecificityThenName> CompletionsTy; Registry::getMatcherCompletions(ArrayRef<ArgKind> AcceptedTypes) {
CompletionsTy Completions; std::vector<MatcherCompletion> Completions;
// TypeSet now contains the list of acceptable types for the argument we are // Search the registry for acceptable matchers.
// completing. Search the registry for acceptable matchers.
for (ConstructorMap::const_iterator I = RegistryData->constructors().begin(), for (ConstructorMap::const_iterator I = RegistryData->constructors().begin(),
E = RegistryData->constructors().end(); E = RegistryData->constructors().end();
I != E; ++I) { I != E; ++I) {
std::set<ASTNodeKind> RetKinds; std::set<ASTNodeKind> RetKinds;
unsigned NumArgs = I->second->isVariadic() ? 1 : I->second->getNumArgs(); unsigned NumArgs = I->second->isVariadic() ? 1 : I->second->getNumArgs();
bool IsPolymorphic = I->second->isPolymorphic(); bool IsPolymorphic = I->second->isPolymorphic();
std::vector<std::vector<internal::ArgKind> > ArgsKinds(NumArgs); std::vector<std::vector<ArgKind>> ArgsKinds(NumArgs);
unsigned MaxSpecificity = 0; unsigned MaxSpecificity = 0;
for (std::set<ASTNodeKind>::iterator TI = TypeSet.begin(), for (const ArgKind& Kind : AcceptedTypes) {
TE = TypeSet.end(); if (Kind.getArgKind() != Kind.AK_Matcher)
TI != TE; ++TI) { continue;
unsigned Specificity; unsigned Specificity;
ASTNodeKind LeastDerivedKind; ASTNodeKind LeastDerivedKind;
if (I->second->isConvertibleTo(*TI, &Specificity, &LeastDerivedKind)) { if (I->second->isConvertibleTo(Kind.getMatcherKind(), &Specificity,
&LeastDerivedKind)) {
if (MaxSpecificity < Specificity) if (MaxSpecificity < Specificity)
MaxSpecificity = Specificity; MaxSpecificity = Specificity;
RetKinds.insert(LeastDerivedKind); RetKinds.insert(LeastDerivedKind);
for (unsigned Arg = 0; Arg != NumArgs; ++Arg) for (unsigned Arg = 0; Arg != NumArgs; ++Arg)
I->second->getArgKinds(*TI, Arg, ArgsKinds[Arg]); I->second->getArgKinds(Kind.getMatcherKind(), Arg, ArgsKinds[Arg]);
if (IsPolymorphic) if (IsPolymorphic)
break; break;
} }
@ -437,24 +423,20 @@ std::vector<MatcherCompletion> Registry::getCompletions(
OS << "Matcher<T> " << I->first() << "(Matcher<T>"; OS << "Matcher<T> " << I->first() << "(Matcher<T>";
} else { } else {
OS << "Matcher<" << RetKinds << "> " << I->first() << "("; OS << "Matcher<" << RetKinds << "> " << I->first() << "(";
for (std::vector<std::vector<internal::ArgKind> >::iterator for (const std::vector<ArgKind> &Arg : ArgsKinds) {
KI = ArgsKinds.begin(), if (&Arg != &ArgsKinds[0])
KE = ArgsKinds.end();
KI != KE; ++KI) {
if (KI != ArgsKinds.begin())
OS << ", "; OS << ", ";
// This currently assumes that a matcher may not overload a // This currently assumes that a matcher may not overload a
// non-matcher, and all non-matcher overloads have identical // non-matcher, and all non-matcher overloads have identical
// arguments. // arguments.
if ((*KI)[0].getArgKind() == internal::ArgKind::AK_Matcher) { if (Arg[0].getArgKind() == ArgKind::AK_Matcher) {
std::set<ASTNodeKind> MatcherKinds; std::set<ASTNodeKind> MatcherKinds;
std::transform( std::transform(Arg.begin(), Arg.end(),
KI->begin(), KI->end(), std::inserter(MatcherKinds, MatcherKinds.end()),
std::inserter(MatcherKinds, MatcherKinds.end()), std::mem_fun_ref(&ArgKind::getMatcherKind));
std::mem_fun_ref(&internal::ArgKind::getMatcherKind));
OS << "Matcher<" << MatcherKinds << ">"; OS << "Matcher<" << MatcherKinds << ">";
} else { } else {
OS << (*KI)[0].asString(); OS << Arg[0].asString();
} }
} }
} }
@ -466,19 +448,14 @@ std::vector<MatcherCompletion> Registry::getCompletions(
TypedText += "("; TypedText += "(";
if (ArgsKinds.empty()) if (ArgsKinds.empty())
TypedText += ")"; TypedText += ")";
else if (ArgsKinds[0][0].getArgKind() == internal::ArgKind::AK_String) else if (ArgsKinds[0][0].getArgKind() == ArgKind::AK_String)
TypedText += "\""; TypedText += "\"";
Completions[std::make_pair(MaxSpecificity, I->first())] = Completions.emplace_back(TypedText, OS.str(), MaxSpecificity);
MatcherCompletion(TypedText, OS.str());
} }
} }
std::vector<MatcherCompletion> RetVal; return Completions;
for (CompletionsTy::iterator I = Completions.begin(), E = Completions.end();
I != E; ++I)
RetVal.push_back(I->second);
return RetVal;
} }
// static // static

View File

@ -20,6 +20,35 @@ namespace clang {
namespace ast_matchers { namespace ast_matchers {
namespace dynamic { 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::MatcherOps::~MatcherOps() {}
VariantMatcher::Payload::~Payload() {} VariantMatcher::Payload::~Payload() {}
@ -27,21 +56,27 @@ class VariantMatcher::SinglePayload : public VariantMatcher::Payload {
public: public:
SinglePayload(const DynTypedMatcher &Matcher) : Matcher(Matcher) {} SinglePayload(const DynTypedMatcher &Matcher) : Matcher(Matcher) {}
virtual llvm::Optional<DynTypedMatcher> getSingleMatcher() const { llvm::Optional<DynTypedMatcher> getSingleMatcher() const override {
return Matcher; return Matcher;
} }
virtual std::string getTypeAsString() const { std::string getTypeAsString() const override {
return (Twine("Matcher<") + Matcher.getSupportedKind().asStringRef() + ">") return (Twine("Matcher<") + Matcher.getSupportedKind().asStringRef() + ">")
.str(); .str();
} }
virtual void makeTypedMatcher(MatcherOps &Ops) const { void makeTypedMatcher(MatcherOps &Ops) const override {
bool Ignore; bool Ignore;
if (Ops.canConstructFrom(Matcher, Ignore)) if (Ops.canConstructFrom(Matcher, Ignore))
Ops.constructFrom(Matcher); Ops.constructFrom(Matcher);
} }
bool isConvertibleTo(ast_type_traits::ASTNodeKind Kind,
unsigned *Specificity) const override {
return ArgKind(Matcher.getSupportedKind())
.isConvertibleTo(Kind, Specificity);
}
private: private:
const DynTypedMatcher Matcher; const DynTypedMatcher Matcher;
}; };
@ -51,15 +86,15 @@ public:
PolymorphicPayload(std::vector<DynTypedMatcher> MatchersIn) PolymorphicPayload(std::vector<DynTypedMatcher> MatchersIn)
: Matchers(std::move(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) if (Matchers.size() != 1)
return llvm::Optional<DynTypedMatcher>(); return llvm::Optional<DynTypedMatcher>();
return Matchers[0]; return Matchers[0];
} }
virtual std::string getTypeAsString() const { std::string getTypeAsString() const override {
std::string Inner; std::string Inner;
for (size_t i = 0, e = Matchers.size(); i != e; ++i) { for (size_t i = 0, e = Matchers.size(); i != e; ++i) {
if (i != 0) if (i != 0)
@ -69,7 +104,7 @@ public:
return (Twine("Matcher<") + Inner + ">").str(); return (Twine("Matcher<") + Inner + ">").str();
} }
virtual void makeTypedMatcher(MatcherOps &Ops) const { void makeTypedMatcher(MatcherOps &Ops) const override {
bool FoundIsExact = false; bool FoundIsExact = false;
const DynTypedMatcher *Found = nullptr; const DynTypedMatcher *Found = nullptr;
int NumFound = 0; int NumFound = 0;
@ -92,6 +127,21 @@ public:
Ops.constructFrom(*Found); 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; const std::vector<DynTypedMatcher> Matchers;
}; };
@ -101,11 +151,11 @@ public:
std::vector<VariantMatcher> Args) std::vector<VariantMatcher> Args)
: Func(Func), Args(std::move(Args)) {} : Func(Func), Args(std::move(Args)) {}
virtual llvm::Optional<DynTypedMatcher> getSingleMatcher() const { llvm::Optional<DynTypedMatcher> getSingleMatcher() const override {
return llvm::Optional<DynTypedMatcher>(); return llvm::Optional<DynTypedMatcher>();
} }
virtual std::string getTypeAsString() const { std::string getTypeAsString() const override {
std::string Inner; std::string Inner;
for (size_t i = 0, e = Args.size(); i != e; ++i) { for (size_t i = 0, e = Args.size(); i != e; ++i) {
if (i != 0) if (i != 0)
@ -115,10 +165,19 @@ public:
return Inner; return Inner;
} }
virtual void makeTypedMatcher(MatcherOps &Ops) const { void makeTypedMatcher(MatcherOps &Ops) const override {
Ops.constructVariadicOperator(Func, Args); 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: private:
const ast_matchers::internal::VariadicOperatorFunction Func; const ast_matchers::internal::VariadicOperatorFunction Func;
const std::vector<VariantMatcher> Args; const std::vector<VariantMatcher> Args;
@ -251,6 +310,43 @@ void VariantValue::setMatcher(const VariantMatcher &NewValue) {
Value.Matcher = new 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 { std::string VariantValue::getTypeAsString() const {
switch (Type) { switch (Type) {
case VT_String: return "String"; case VT_String: return "String";

View File

@ -152,6 +152,14 @@ TEST(ParserTest, ParseMatcher) {
using ast_matchers::internal::Matcher; 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) { TEST(ParserTest, FullParserTest) {
Diagnostics Error; Diagnostics Error;
llvm::Optional<DynTypedMatcher> VarDecl(Parser::parseMatcherExpression( llvm::Optional<DynTypedMatcher> VarDecl(Parser::parseMatcherExpression(
@ -174,21 +182,11 @@ TEST(ParserTest, FullParserTest) {
EXPECT_FALSE(matches("void f(int x, int a);", M)); EXPECT_FALSE(matches("void f(int x, int a);", M));
// Test named values. // Test named values.
struct NamedSema : public Parser::RegistrySema { auto NamedValues = getTestNamedValues();
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;
llvm::Optional<DynTypedMatcher> HasParameterWithNamedValues( llvm::Optional<DynTypedMatcher> HasParameterWithNamedValues(
Parser::parseMatcherExpression( Parser::parseMatcherExpression(
"functionDecl(param0, hasParameter(1, hasName(nameX)))", &Sema, "functionDecl(hasParamA, hasParameter(1, hasName(nameX)))",
&Error)); nullptr, &NamedValues, &Error));
EXPECT_EQ("", Error.toStringFull()); EXPECT_EQ("", Error.toStringFull());
M = HasParameterWithNamedValues->unconditionalConvertTo<Decl>(); M = HasParameterWithNamedValues->unconditionalConvertTo<Decl>();
@ -270,7 +268,7 @@ TEST(ParserTest, OverloadErrors) {
ParseWithError("callee(\"A\")")); ParseWithError("callee(\"A\")"));
} }
TEST(ParserTest, Completion) { TEST(ParserTest, CompletionRegistry) {
std::vector<MatcherCompletion> Comps = std::vector<MatcherCompletion> Comps =
Parser::completeExpression("while", 5); Parser::completeExpression("while", 5);
ASSERT_EQ(1u, Comps.size()); ASSERT_EQ(1u, Comps.size());
@ -284,6 +282,38 @@ TEST(ParserTest, Completion) {
EXPECT_EQ("bind", Comps[0].MatcherDecl); 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 anonymous namespace
} // end namespace dynamic } // end namespace dynamic
} // end namespace ast_matchers } // end namespace ast_matchers

View File

@ -82,8 +82,9 @@ public:
typedef std::vector<MatcherCompletion> CompVector; typedef std::vector<MatcherCompletion> CompVector;
CompVector getCompletions() { CompVector getCompletions() {
return Registry::getCompletions( std::vector<std::pair<MatcherCtor, unsigned> > Context;
ArrayRef<std::pair<MatcherCtor, unsigned> >()); return Registry::getMatcherCompletions(
Registry::getAcceptedCompletionTypes(Context));
} }
CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1) { CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1) {
@ -92,7 +93,8 @@ public:
if (!Ctor) if (!Ctor)
return CompVector(); return CompVector();
Context.push_back(std::make_pair(*Ctor, ArgNo1)); Context.push_back(std::make_pair(*Ctor, ArgNo1));
return Registry::getCompletions(Context); return Registry::getMatcherCompletions(
Registry::getAcceptedCompletionTypes(Context));
} }
CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1, CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1,
@ -106,18 +108,16 @@ public:
if (!Ctor) if (!Ctor)
return CompVector(); return CompVector();
Context.push_back(std::make_pair(*Ctor, ArgNo2)); 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, bool hasCompletion(const CompVector &Comps, StringRef TypedText,
StringRef MatcherDecl = StringRef(), StringRef MatcherDecl = StringRef()) {
unsigned *Index = nullptr) {
for (CompVector::const_iterator I = Comps.begin(), E = Comps.end(); I != E; for (CompVector::const_iterator I = Comps.begin(), E = Comps.end(); I != E;
++I) { ++I) {
if (I->TypedText == TypedText && if (I->TypedText == TypedText &&
(MatcherDecl.empty() || I->MatcherDecl == MatcherDecl)) { (MatcherDecl.empty() || I->MatcherDecl == MatcherDecl)) {
if (Index)
*Index = I - Comps.begin();
return true; return true;
} }
} }
@ -445,17 +445,12 @@ TEST_F(RegistryTest, Completion) {
CompVector WhileComps = getCompletions("whileStmt", 0); CompVector WhileComps = getCompletions("whileStmt", 0);
unsigned HasBodyIndex, HasParentIndex, AllOfIndex;
EXPECT_TRUE(hasCompletion(WhileComps, "hasBody(", EXPECT_TRUE(hasCompletion(WhileComps, "hasBody(",
"Matcher<WhileStmt> hasBody(Matcher<Stmt>)", "Matcher<WhileStmt> hasBody(Matcher<Stmt>)"));
&HasBodyIndex));
EXPECT_TRUE(hasCompletion(WhileComps, "hasParent(", EXPECT_TRUE(hasCompletion(WhileComps, "hasParent(",
"Matcher<Stmt> hasParent(Matcher<Decl|Stmt>)", "Matcher<Stmt> hasParent(Matcher<Decl|Stmt>)"));
&HasParentIndex)); EXPECT_TRUE(
EXPECT_TRUE(hasCompletion(WhileComps, "allOf(", hasCompletion(WhileComps, "allOf(", "Matcher<T> allOf(Matcher<T>...)"));
"Matcher<T> allOf(Matcher<T>...)", &AllOfIndex));
EXPECT_GT(HasParentIndex, HasBodyIndex);
EXPECT_GT(AllOfIndex, HasParentIndex);
EXPECT_FALSE(hasCompletion(WhileComps, "whileStmt(")); EXPECT_FALSE(hasCompletion(WhileComps, "whileStmt("));
EXPECT_FALSE(hasCompletion(WhileComps, "ifStmt(")); EXPECT_FALSE(hasCompletion(WhileComps, "ifStmt("));