diff --git a/clang/include/clang/ASTMatchers/Dynamic/Parser.h b/clang/include/clang/ASTMatchers/Dynamic/Parser.h index 4045f57d1b36..6f6b4130f00f 100644 --- a/clang/include/clang/ASTMatchers/Dynamic/Parser.h +++ b/clang/include/clang/ASTMatchers/Dynamic/Parser.h @@ -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 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 getAcceptedCompletionTypes( + llvm::ArrayRef> 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 + getMatcherCompletions(llvm::ArrayRef AcceptedTypes); }; /// \brief Sema implementation that uses the matcher registry to process the @@ -121,58 +133,91 @@ public: StringRef BindID, ArrayRef Args, Diagnostics *Error) override; + + std::vector getAcceptedCompletionTypes( + llvm::ArrayRef> Context) override; + + std::vector + getMatcherCompletions(llvm::ArrayRef 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 - parseMatcherExpression(StringRef MatcherCode, Diagnostics *Error); + typedef llvm::StringMap 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 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 - 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 + parseMatcherExpression(StringRef MatcherCode, Sema *S, + Diagnostics *Error) { + return parseMatcherExpression(MatcherCode, S, nullptr, Error); + } + static llvm::Optional + 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 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 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 - completeExpression(StringRef Code, unsigned CompletionOffset); + completeExpression(StringRef Code, unsigned CompletionOffset, Sema *S, + const NamedValueMap *NamedValues); + static std::vector + completeExpression(StringRef Code, unsigned CompletionOffset, Sema *S) { + return completeExpression(Code, CompletionOffset, S, nullptr); + } + static std::vector + 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 + getNamedValueCompletions(ArrayRef AcceptedTypes); + CodeTokenizer *const Tokenizer; Sema *const S; + const NamedValueMap *const NamedValues; Diagnostics *const Error; typedef std::vector > ContextStackTy; diff --git a/clang/include/clang/ASTMatchers/Dynamic/Registry.h b/clang/include/clang/ASTMatchers/Dynamic/Registry.h index faa9254bc2e7..3551622f66d7 100644 --- a/clang/include/clang/ASTMatchers/Dynamic/Registry.h +++ b/clang/include/clang/ASTMatchers/Dynamic/Registry.h @@ -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() if not found. static llvm::Optional 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 getAcceptedCompletionTypes( + llvm::ArrayRef> 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 - getCompletions(ArrayRef > Context); + getMatcherCompletions(ArrayRef AcceptedTypes); /// \brief Construct a matcher from the registry. /// diff --git a/clang/include/clang/ASTMatchers/Dynamic/VariantValue.h b/clang/include/clang/ASTMatchers/Dynamic/VariantValue.h index b25267b1c54b..baee72ccf4b7 100644 --- a/clang/include/clang/ASTMatchers/Dynamic/VariantValue.h +++ b/clang/include/clang/ASTMatchers/Dynamic/VariantValue.h @@ -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 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. /// /// 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 Kinds, unsigned *Specificity) const; + /// \brief String representation of the type of the value. std::string getTypeAsString() const; diff --git a/clang/lib/ASTMatchers/Dynamic/Marshallers.h b/clang/lib/ASTMatchers/Dynamic/Marshallers.h index 6e144cd26ed9..b047cf478b6c 100644 --- a/clang/lib/ASTMatchers/Dynamic/Marshallers.h +++ b/clang/lib/ASTMatchers/Dynamic/Marshallers.h @@ -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 RetKinds, ast_type_traits::ASTNodeKind Kind, unsigned *Specificity, ast_type_traits::ASTNodeKind *LeastDerivedKind) { - for (ArrayRef::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; } } diff --git a/clang/lib/ASTMatchers/Dynamic/Parser.cpp b/clang/lib/ASTMatchers/Dynamic/Parser.cpp index 25629d99a7cc..9930c530c081 100644 --- a/clang/lib/ASTMatchers/Dynamic/Parser.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Parser.cpp @@ -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 #include @@ -258,8 +259,14 @@ private: Parser::Sema::~Sema() {} -VariantValue Parser::Sema::getNamedValue(StringRef Name) { - return VariantValue(); +std::vector Parser::Sema::getAcceptedCompletionTypes( + llvm::ArrayRef> Context) { + return std::vector(); +} + +std::vector +Parser::Sema::getMatcherCompletions(llvm::ArrayRef AcceptedTypes) { + return std::vector(); } 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 Parser::getNamedValueCompletions( + ArrayRef AcceptedTypes) { + if (!NamedValues) return std::vector(); + std::vector 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 RegCompletions = - Registry::getCompletions(ContextStack); - for (std::vector::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 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 Parser::RegistrySema::getAcceptedCompletionTypes( + ArrayRef> Context) { + return Registry::getAcceptedCompletionTypes(Context); +} + +std::vector Parser::RegistrySema::getMatcherCompletions( + ArrayRef 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 -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 -Parser::parseMatcherExpression(StringRef Code, Diagnostics *Error) { - RegistrySema S; - return parseMatcherExpression(Code, &S, Error); -} - -llvm::Optional -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(); if (!Value.isMatcher()) { Error->addError(SourceRange(), Error->ET_ParserNotAMatcher); diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp index 4bc50a0f2a96..7e323a5198ea 100644 --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -353,77 +353,63 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, return OS; } -struct ReverseSpecificityThenName { - bool operator()(const std::pair &A, - const std::pair &B) const { - return A.first > B.first || (A.first == B.first && A.second < B.second); - } -}; +} // namespace -} - -std::vector Registry::getCompletions( - ArrayRef > Context) { +std::vector Registry::getAcceptedCompletionTypes( + ArrayRef> Context) { ASTNodeKind InitialTypes[] = { - ASTNodeKind::getFromNodeKind(), - ASTNodeKind::getFromNodeKind(), - ASTNodeKind::getFromNodeKind(), - ASTNodeKind::getFromNodeKind(), - ASTNodeKind::getFromNodeKind(), - ASTNodeKind::getFromNodeKind(), - ASTNodeKind::getFromNodeKind() - }; - ArrayRef InitialTypesRef(InitialTypes); + ASTNodeKind::getFromNodeKind(), + ASTNodeKind::getFromNodeKind(), + ASTNodeKind::getFromNodeKind(), + ASTNodeKind::getFromNodeKind(), + ASTNodeKind::getFromNodeKind(), + ASTNodeKind::getFromNodeKind(), + ASTNodeKind::getFromNodeKind()}; // 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 TypeSet(InitialTypesRef.begin(), InitialTypesRef.end()); - for (ArrayRef >::iterator - CtxI = Context.begin(), - CtxE = Context.end(); - CtxI != CtxE; ++CtxI) { - std::vector NextTypeSet; - for (std::set::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 TypeSet(std::begin(InitialTypes), std::end(InitialTypes)); + for (const auto &CtxEntry : Context) { + MatcherCtor Ctor = CtxEntry.first; + unsigned ArgNumber = CtxEntry.second; + std::vector 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::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(TypeSet.begin(), TypeSet.end()); +} - typedef std::map, MatcherCompletion, - ReverseSpecificityThenName> CompletionsTy; - CompletionsTy Completions; +std::vector +Registry::getMatcherCompletions(ArrayRef AcceptedTypes) { + std::vector 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 RetKinds; unsigned NumArgs = I->second->isVariadic() ? 1 : I->second->getNumArgs(); bool IsPolymorphic = I->second->isPolymorphic(); - std::vector > ArgsKinds(NumArgs); + std::vector> ArgsKinds(NumArgs); unsigned MaxSpecificity = 0; - for (std::set::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 Registry::getCompletions( OS << "Matcher " << I->first() << "(Matcher"; } else { OS << "Matcher<" << RetKinds << "> " << I->first() << "("; - for (std::vector >::iterator - KI = ArgsKinds.begin(), - KE = ArgsKinds.end(); - KI != KE; ++KI) { - if (KI != ArgsKinds.begin()) + for (const std::vector &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 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 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 RetVal; - for (CompletionsTy::iterator I = Completions.begin(), E = Completions.end(); - I != E; ++I) - RetVal.push_back(I->second); - return RetVal; + return Completions; } // static diff --git a/clang/lib/ASTMatchers/Dynamic/VariantValue.cpp b/clang/lib/ASTMatchers/Dynamic/VariantValue.cpp index 18c989432f46..57c7b80d3619 100644 --- a/clang/lib/ASTMatchers/Dynamic/VariantValue.cpp +++ b/clang/lib/ASTMatchers/Dynamic/VariantValue.cpp @@ -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 getSingleMatcher() const { + llvm::Optional 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 MatchersIn) : Matchers(std::move(MatchersIn)) {} - virtual ~PolymorphicPayload() {} + ~PolymorphicPayload() override {} - virtual llvm::Optional getSingleMatcher() const { + llvm::Optional getSingleMatcher() const override { if (Matchers.size() != 1) return llvm::Optional(); 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 Matchers; }; @@ -101,11 +151,11 @@ public: std::vector Args) : Func(Func), Args(std::move(Args)) {} - virtual llvm::Optional getSingleMatcher() const { + llvm::Optional getSingleMatcher() const override { return llvm::Optional(); } - 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 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 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"; diff --git a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp index 4e3239ff5a50..860db7ef46ae 100644 --- a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp +++ b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -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 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 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(); @@ -270,7 +268,7 @@ TEST(ParserTest, OverloadErrors) { ParseWithError("callee(\"A\")")); } -TEST(ParserTest, Completion) { +TEST(ParserTest, CompletionRegistry) { std::vector 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 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 hasParamA", Comps[0].MatcherDecl); + + EXPECT_EQ("arameter(", Comps[1].TypedText); + EXPECT_EQ( + "Matcher hasParameter(unsigned, Matcher)", + Comps[1].MatcherDecl); + + EXPECT_EQ("arent(", Comps[2].TypedText); + EXPECT_EQ("Matcher hasParent(Matcher)", + Comps[2].MatcherDecl); +} + } // end anonymous namespace } // end namespace dynamic } // end namespace ast_matchers diff --git a/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp b/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp index e659b3a733d6..5552790bcacb 100644 --- a/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp +++ b/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp @@ -82,8 +82,9 @@ public: typedef std::vector CompVector; CompVector getCompletions() { - return Registry::getCompletions( - ArrayRef >()); + std::vector > 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 hasBody(Matcher)", - &HasBodyIndex)); + "Matcher hasBody(Matcher)")); EXPECT_TRUE(hasCompletion(WhileComps, "hasParent(", - "Matcher hasParent(Matcher)", - &HasParentIndex)); - EXPECT_TRUE(hasCompletion(WhileComps, "allOf(", - "Matcher allOf(Matcher...)", &AllOfIndex)); - EXPECT_GT(HasParentIndex, HasBodyIndex); - EXPECT_GT(AllOfIndex, HasParentIndex); + "Matcher hasParent(Matcher)")); + EXPECT_TRUE( + hasCompletion(WhileComps, "allOf(", "Matcher allOf(Matcher...)")); EXPECT_FALSE(hasCompletion(WhileComps, "whileStmt(")); EXPECT_FALSE(hasCompletion(WhileComps, "ifStmt("));