Index: include/clang/ASTMatchers/Dynamic/Diagnostics.h =================================================================== --- include/clang/ASTMatchers/Dynamic/Diagnostics.h +++ include/clang/ASTMatchers/Dynamic/Diagnostics.h @@ -60,11 +60,12 @@ enum ErrorType { ET_None = 0, - ET_RegistryNotFound = 1, + ET_RegistryMatcherNotFound = 1, ET_RegistryWrongArgCount = 2, ET_RegistryWrongArgType = 3, ET_RegistryNotBindable = 4, ET_RegistryAmbiguousOverload = 5, + ET_RegistryValueNotFound = 6, ET_ParserStringError = 100, ET_ParserNoOpenParen = 101, Index: include/clang/ASTMatchers/Dynamic/Parser.h =================================================================== --- include/clang/ASTMatchers/Dynamic/Parser.h +++ include/clang/ASTMatchers/Dynamic/Parser.h @@ -18,13 +18,14 @@ /// /// \code /// Grammar for the expressions supported: -/// := | +/// := | | /// := | /// := "quoted string" /// := [0-9]+ -/// := () | -/// ().bind() -/// := [a-zA-Z]+ +/// := +/// := () | +/// ().bind() +/// := [a-zA-Z]+ /// := | , /// \endcode /// @@ -62,6 +63,17 @@ 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. @@ -89,15 +101,26 @@ /// /// \param MatcherName The matcher name found by the parser. /// - /// \param NameRange The location of the name in the matcher source. - /// Useful for error reporting. - /// - /// \return The matcher constructor, or Optional() if an error - /// occurred. In that case, \c Error will contain a description of the - /// error. + /// \return The matcher constructor, or Optional() if not + /// found. virtual llvm::Optional - lookupMatcherCtor(StringRef MatcherName, const SourceRange &NameRange, - Diagnostics *Error) = 0; + lookupMatcherCtor(StringRef MatcherName) = 0; + }; + + /// \brief Sema implementation that uses the matcher registry to process the + /// tokens. + class RegistrySema : public Parser::Sema { + public: + virtual ~RegistrySema(); + + llvm::Optional + lookupMatcherCtor(StringRef MatcherName) override; + + VariantMatcher actOnMatcherExpression(MatcherCtor Ctor, + const SourceRange &NameRange, + StringRef BindID, + ArrayRef Args, + Diagnostics *Error) override; }; /// \brief Parse a matcher expression, creating matchers from the registry. @@ -160,7 +183,9 @@ Diagnostics *Error); bool parseExpressionImpl(VariantValue *Value); - bool parseMatcherExpressionImpl(VariantValue *Value); + bool parseMatcherExpressionImpl(const TokenInfo &NameToken, + VariantValue *Value); + bool parseIdentifierPrefixImpl(VariantValue *Value); void addCompletion(const TokenInfo &CompToken, StringRef TypedText, StringRef Decl); Index: include/clang/ASTMatchers/Dynamic/Registry.h =================================================================== --- include/clang/ASTMatchers/Dynamic/Registry.h +++ include/clang/ASTMatchers/Dynamic/Registry.h @@ -55,11 +55,8 @@ /// \brief Look up a matcher in the registry by name, /// /// \return An opaque value which may be used to refer to the matcher - /// constructor, or Optional() if not found. In that case - /// \c Error will contain the description of the error. - static llvm::Optional - lookupMatcherCtor(StringRef MatcherName, const SourceRange &NameRange, - Diagnostics *Error); + /// constructor, or Optional() if not found. + static llvm::Optional lookupMatcherCtor(StringRef MatcherName); /// \brief Compute the list of completions for \p Context. /// Index: include/clang/ASTMatchers/Dynamic/VariantValue.h =================================================================== --- include/clang/ASTMatchers/Dynamic/VariantValue.h +++ include/clang/ASTMatchers/Dynamic/VariantValue.h @@ -78,7 +78,8 @@ /// \brief Clones the provided matchers. /// /// They should be the result of a polymorphic matcher. - static VariantMatcher PolymorphicMatcher(std::vector Matchers); + static VariantMatcher + PolymorphicMatcher(std::vector Matchers); /// \brief Creates a 'variadic' operator matcher. /// @@ -208,6 +209,10 @@ VariantValue(const std::string &String); VariantValue(const VariantMatcher &Matchers); + /// \brief Returns true iff this is not an empty value. + LLVM_EXPLICIT operator bool() const { return hasValue(); } + bool hasValue() const { return Type != VT_Nothing; } + /// \brief Unsigned value functions. bool isUnsigned() const; unsigned getUnsigned() const; Index: lib/ASTMatchers/Dynamic/Diagnostics.cpp =================================================================== --- lib/ASTMatchers/Dynamic/Diagnostics.cpp +++ lib/ASTMatchers/Dynamic/Diagnostics.cpp @@ -87,7 +87,7 @@ StringRef errorTypeToFormatString(Diagnostics::ErrorType Type) { switch (Type) { - case Diagnostics::ET_RegistryNotFound: + case Diagnostics::ET_RegistryMatcherNotFound: return "Matcher not found: $0"; case Diagnostics::ET_RegistryWrongArgCount: return "Incorrect argument count. (Expected = $0) != (Actual = $1)"; @@ -98,6 +98,8 @@ case Diagnostics::ET_RegistryAmbiguousOverload: // TODO: Add type info about the overload error. return "Ambiguous matcher overload."; + case Diagnostics::ET_RegistryValueNotFound: + return "Value not found: $0"; case Diagnostics::ET_ParserStringError: return "Error parsing string token: <$0>"; Index: lib/ASTMatchers/Dynamic/Parser.cpp =================================================================== --- lib/ASTMatchers/Dynamic/Parser.cpp +++ lib/ASTMatchers/Dynamic/Parser.cpp @@ -258,6 +258,10 @@ Parser::Sema::~Sema() {} +VariantValue Parser::Sema::getNamedValue(StringRef Name) { + return VariantValue(); +} + struct Parser::ScopedContextEntry { Parser *P; @@ -274,12 +278,43 @@ } }; +/// \brief Parse expressions that start with an identifier. +/// +/// This function can parse named values and matchers. +/// In case of failure it will try to determine the user's intent to give +/// an appropriate error message. +bool Parser::parseIdentifierPrefixImpl(VariantValue *Value) { + const TokenInfo NameToken = Tokenizer->consumeNextToken(); + + if (Tokenizer->nextTokenKind() != TokenInfo::TK_OpenParen) { + // Parse as a named value. + if (const VariantValue NamedValue = S->getNamedValue(NameToken.Text)) { + *Value = NamedValue; + return true; + } + // If the syntax is correct and the name is not a matcher either, report + // unknown named value. + if ((Tokenizer->nextTokenKind() == TokenInfo::TK_Comma || + Tokenizer->nextTokenKind() == TokenInfo::TK_CloseParen || + Tokenizer->nextTokenKind() == TokenInfo::TK_Eof) && + !S->lookupMatcherCtor(NameToken.Text)) { + Error->addError(NameToken.Range, Error->ET_RegistryValueNotFound) + << NameToken.Text; + return false; + } + // Otherwise, fallback to the matcher parser. + } + + // Parse as a matcher expression. + return parseMatcherExpressionImpl(NameToken, Value); +} + /// \brief Parse and validate a matcher expression. /// \return \c true on success, in which case \c Value has the matcher parsed. /// If the input is malformed, or some argument has an error, it /// returns \c false. -bool Parser::parseMatcherExpressionImpl(VariantValue *Value) { - const TokenInfo NameToken = Tokenizer->consumeNextToken(); +bool Parser::parseMatcherExpressionImpl(const TokenInfo &NameToken, + VariantValue *Value) { assert(NameToken.Kind == TokenInfo::TK_Ident); const TokenInfo OpenToken = Tokenizer->consumeNextToken(); if (OpenToken.Kind != TokenInfo::TK_OpenParen) { @@ -288,8 +323,14 @@ return false; } - llvm::Optional Ctor = - S->lookupMatcherCtor(NameToken.Text, NameToken.Range, Error); + llvm::Optional Ctor = S->lookupMatcherCtor(NameToken.Text); + + if (!Ctor) { + Error->addError(NameToken.Range, Error->ET_RegistryMatcherNotFound) + << NameToken.Text; + // Do not return here. We need to continue to give completion suggestions. + } + std::vector Args; TokenInfo EndToken; @@ -425,7 +466,7 @@ return true; case TokenInfo::TK_Ident: - return parseMatcherExpressionImpl(Value); + return parseIdentifierPrefixImpl(Value); case TokenInfo::TK_CodeCompletion: addExpressionCompletions(); @@ -457,27 +498,23 @@ Diagnostics *Error) : Tokenizer(Tokenizer), S(S), Error(Error) {} -class RegistrySema : public Parser::Sema { -public: - virtual ~RegistrySema() {} - llvm::Optional lookupMatcherCtor(StringRef MatcherName, - const SourceRange &NameRange, - Diagnostics *Error) { - return Registry::lookupMatcherCtor(MatcherName, NameRange, Error); - } - VariantMatcher actOnMatcherExpression(MatcherCtor Ctor, - const SourceRange &NameRange, - StringRef BindID, - ArrayRef Args, - Diagnostics *Error) { - if (BindID.empty()) { - return Registry::constructMatcher(Ctor, NameRange, Args, Error); - } else { - return Registry::constructBoundMatcher(Ctor, NameRange, BindID, Args, - Error); - } +Parser::RegistrySema::~RegistrySema() {} + +llvm::Optional +Parser::RegistrySema::lookupMatcherCtor(StringRef MatcherName) { + return Registry::lookupMatcherCtor(MatcherName); +} + +VariantMatcher Parser::RegistrySema::actOnMatcherExpression( + MatcherCtor Ctor, const SourceRange &NameRange, StringRef BindID, + ArrayRef Args, Diagnostics *Error) { + if (BindID.empty()) { + return Registry::constructMatcher(Ctor, NameRange, Args, Error); + } else { + return Registry::constructBoundMatcher(Ctor, NameRange, BindID, Args, + Error); } -}; +} bool Parser::parseExpression(StringRef Code, VariantValue *Value, Diagnostics *Error) { Index: lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- lib/ASTMatchers/Dynamic/Registry.cpp +++ lib/ASTMatchers/Dynamic/Registry.cpp @@ -325,17 +325,12 @@ } // anonymous namespace // static -llvm::Optional -Registry::lookupMatcherCtor(StringRef MatcherName, const SourceRange &NameRange, - Diagnostics *Error) { +llvm::Optional Registry::lookupMatcherCtor(StringRef MatcherName) { ConstructorMap::const_iterator it = RegistryData->constructors().find(MatcherName); - if (it == RegistryData->constructors().end()) { - Error->addError(NameRange, Error->ET_RegistryNotFound) << MatcherName; - return llvm::Optional(); - } - - return it->second; + return it == RegistryData->constructors().end() + ? llvm::Optional() + : it->second; } namespace { Index: unittests/ASTMatchers/Dynamic/ParserTest.cpp =================================================================== --- unittests/ASTMatchers/Dynamic/ParserTest.cpp +++ unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -39,9 +39,7 @@ Errors.push_back(Error.toStringFull()); } - llvm::Optional lookupMatcherCtor(StringRef MatcherName, - const SourceRange &NameRange, - Diagnostics *Error) { + llvm::Optional lookupMatcherCtor(StringRef MatcherName) { const ExpectedMatchersTy::value_type *Matcher = &*ExpectedMatchers.find(MatcherName); return reinterpret_cast(Matcher); @@ -175,6 +173,29 @@ EXPECT_TRUE(matches("void f(int a, int x);", M)); 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; + llvm::Optional HasParameterWithNamedValues( + Parser::parseMatcherExpression( + "functionDecl(param0, hasParameter(1, hasName(nameX)))", &Sema, + &Error)); + EXPECT_EQ("", Error.toStringFull()); + M = HasParameterWithNamedValues->unconditionalConvertTo(); + + EXPECT_TRUE(matches("void f(int a, int x);", M)); + EXPECT_FALSE(matches("void f(int x, int a);", M)); + + EXPECT_TRUE(!Parser::parseMatcherExpression( "hasInitializer(\n binaryOperator(hasLHS(\"A\")))", &Error).hasValue()); @@ -208,6 +229,10 @@ "1:9: Error parsing matcher. Found token <123> while looking for ','.", ParseWithError("Foo(\"A\" 123)")); EXPECT_EQ( + "1:1: Error parsing argument 1 for matcher stmt.\n" + "1:6: Value not found: someValue", + ParseWithError("stmt(someValue)")); + EXPECT_EQ( "1:1: Matcher not found: Foo\n" "1:4: Error parsing matcher. Found end-of-code while looking for ')'.", ParseWithError("Foo(")); Index: unittests/ASTMatchers/Dynamic/RegistryTest.cpp =================================================================== --- unittests/ASTMatchers/Dynamic/RegistryTest.cpp +++ unittests/ASTMatchers/Dynamic/RegistryTest.cpp @@ -35,21 +35,15 @@ return Out; } - llvm::Optional lookupMatcherCtor(StringRef MatcherName, - Diagnostics *Error = 0) { - Diagnostics DummyError; - if (!Error) Error = &DummyError; - llvm::Optional Ctor = - Registry::lookupMatcherCtor(MatcherName, SourceRange(), Error); - EXPECT_EQ("", DummyError.toStringFull()); - return Ctor; + llvm::Optional lookupMatcherCtor(StringRef MatcherName) { + return Registry::lookupMatcherCtor(MatcherName); } VariantMatcher constructMatcher(StringRef MatcherName, Diagnostics *Error = NULL) { Diagnostics DummyError; if (!Error) Error = &DummyError; - llvm::Optional Ctor = lookupMatcherCtor(MatcherName, Error); + llvm::Optional Ctor = lookupMatcherCtor(MatcherName); VariantMatcher Out; if (Ctor) Out = Registry::constructMatcher(*Ctor, SourceRange(), Args(), Error); @@ -62,7 +56,7 @@ Diagnostics *Error = NULL) { Diagnostics DummyError; if (!Error) Error = &DummyError; - llvm::Optional Ctor = lookupMatcherCtor(MatcherName, Error); + llvm::Optional Ctor = lookupMatcherCtor(MatcherName); VariantMatcher Out; if (Ctor) Out = Registry::constructMatcher(*Ctor, SourceRange(), Args(Arg1), Error); @@ -76,7 +70,7 @@ Diagnostics *Error = NULL) { Diagnostics DummyError; if (!Error) Error = &DummyError; - llvm::Optional Ctor = lookupMatcherCtor(MatcherName, Error); + llvm::Optional Ctor = lookupMatcherCtor(MatcherName); VariantMatcher Out; if (Ctor) Out = Registry::constructMatcher(*Ctor, SourceRange(), Args(Arg1, Arg2), Index: unittests/ASTMatchers/Dynamic/VariantValueTest.cpp =================================================================== --- unittests/ASTMatchers/Dynamic/VariantValueTest.cpp +++ unittests/ASTMatchers/Dynamic/VariantValueTest.cpp @@ -26,6 +26,7 @@ EXPECT_TRUE(Value.isUnsigned()); EXPECT_EQ(kUnsigned, Value.getUnsigned()); + EXPECT_TRUE(Value.hasValue()); EXPECT_FALSE(Value.isString()); EXPECT_FALSE(Value.isMatcher()); } @@ -38,6 +39,7 @@ EXPECT_EQ(kString, Value.getString()); EXPECT_EQ("String", Value.getTypeAsString()); + EXPECT_TRUE(Value.hasValue()); EXPECT_FALSE(Value.isUnsigned()); EXPECT_FALSE(Value.isMatcher()); } @@ -45,6 +47,7 @@ TEST(VariantValueTest, DynTypedMatcher) { VariantValue Value = VariantMatcher::SingleMatcher(stmt()); + EXPECT_TRUE(Value.hasValue()); EXPECT_FALSE(Value.isUnsigned()); EXPECT_FALSE(Value.isString()); @@ -74,11 +77,13 @@ VariantValue Value = std::string("A"); EXPECT_TRUE(Value.isString()); EXPECT_EQ("A", Value.getString()); + EXPECT_TRUE(Value.hasValue()); EXPECT_FALSE(Value.isUnsigned()); EXPECT_FALSE(Value.isMatcher()); EXPECT_EQ("String", Value.getTypeAsString()); Value = VariantMatcher::SingleMatcher(recordDecl()); + EXPECT_TRUE(Value.hasValue()); EXPECT_FALSE(Value.isUnsigned()); EXPECT_FALSE(Value.isString()); EXPECT_TRUE(Value.isMatcher()); @@ -89,16 +94,36 @@ Value = 17; EXPECT_TRUE(Value.isUnsigned()); EXPECT_EQ(17U, Value.getUnsigned()); + EXPECT_TRUE(Value.hasValue()); EXPECT_FALSE(Value.isMatcher()); EXPECT_FALSE(Value.isString()); Value = VariantValue(); + EXPECT_FALSE(Value.hasValue()); EXPECT_FALSE(Value.isUnsigned()); EXPECT_FALSE(Value.isString()); EXPECT_FALSE(Value.isMatcher()); EXPECT_EQ("Nothing", Value.getTypeAsString()); } +TEST(VariantValueTest, ImplicitBool) { + VariantValue Value; + bool IfTrue = false; + if (Value) { + IfTrue = true; + } + EXPECT_FALSE(IfTrue); + EXPECT_TRUE(!Value); + + Value = std::string(); + IfTrue = false; + if (Value) { + IfTrue = true; + } + EXPECT_TRUE(IfTrue); + EXPECT_FALSE(!Value); +} + TEST(VariantValueTest, Matcher) { EXPECT_TRUE(matches("class X {};", VariantValue(VariantMatcher::SingleMatcher( recordDecl(hasName("X"))))