diff options
-rw-r--r-- | clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h | 3 | ||||
-rw-r--r-- | clang/include/clang/ASTMatchers/Dynamic/Parser.h | 51 | ||||
-rw-r--r-- | clang/include/clang/ASTMatchers/Dynamic/Registry.h | 7 | ||||
-rw-r--r-- | clang/include/clang/ASTMatchers/Dynamic/VariantValue.h | 7 | ||||
-rw-r--r-- | clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp | 4 | ||||
-rw-r--r-- | clang/lib/ASTMatchers/Dynamic/Parser.cpp | 87 | ||||
-rw-r--r-- | clang/lib/ASTMatchers/Dynamic/Registry.cpp | 13 | ||||
-rw-r--r-- | clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp | 31 | ||||
-rw-r--r-- | clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp | 16 | ||||
-rw-r--r-- | clang/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp | 25 |
10 files changed, 175 insertions, 69 deletions
diff --git a/clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h b/clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h index 53629bf567d..82a14f1929b 100644 --- a/clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h +++ b/clang/include/clang/ASTMatchers/Dynamic/Diagnostics.h @@ -60,11 +60,12 @@ public: 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, diff --git a/clang/include/clang/ASTMatchers/Dynamic/Parser.h b/clang/include/clang/ASTMatchers/Dynamic/Parser.h index 5901495bddb..4045f57d1b3 100644 --- a/clang/include/clang/ASTMatchers/Dynamic/Parser.h +++ b/clang/include/clang/ASTMatchers/Dynamic/Parser.h @@ -18,13 +18,14 @@ /// /// \code /// Grammar for the expressions supported: -/// <Expression> := <Literal> | <MatcherExpression> +/// <Expression> := <Literal> | <NamedValue> | <MatcherExpression> /// <Literal> := <StringLiteral> | <Unsigned> /// <StringLiteral> := "quoted string" /// <Unsigned> := [0-9]+ -/// <MatcherExpression> := <MatcherName>(<ArgumentList>) | -/// <MatcherName>(<ArgumentList>).bind(<StringLiteral>) -/// <MatcherName> := [a-zA-Z]+ +/// <NamedValue> := <Identifier> +/// <MatcherExpression> := <Identifier>(<ArgumentList>) | +/// <Identifier>(<ArgumentList>).bind(<StringLiteral>) +/// <Identifier> := [a-zA-Z]+ /// <ArgumentList> := <Expression> | <Expression>,<ArgumentList> /// \endcode /// @@ -62,6 +63,17 @@ 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. @@ -89,15 +101,26 @@ public: /// /// \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<MatcherCtor>() if an error - /// occurred. In that case, \c Error will contain a description of the - /// error. + /// \return The matcher constructor, or Optional<MatcherCtor>() if not + /// found. virtual llvm::Optional<MatcherCtor> - 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<MatcherCtor> + lookupMatcherCtor(StringRef MatcherName) override; + + VariantMatcher actOnMatcherExpression(MatcherCtor Ctor, + const SourceRange &NameRange, + StringRef BindID, + ArrayRef<ParserValue> Args, + Diagnostics *Error) override; }; /// \brief Parse a matcher expression, creating matchers from the registry. @@ -160,7 +183,9 @@ private: 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); diff --git a/clang/include/clang/ASTMatchers/Dynamic/Registry.h b/clang/include/clang/ASTMatchers/Dynamic/Registry.h index d7df698b65d..72ae6126f7c 100644 --- a/clang/include/clang/ASTMatchers/Dynamic/Registry.h +++ b/clang/include/clang/ASTMatchers/Dynamic/Registry.h @@ -55,11 +55,8 @@ public: /// \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<MatcherCtor>() if not found. In that case - /// \c Error will contain the description of the error. - static llvm::Optional<MatcherCtor> - lookupMatcherCtor(StringRef MatcherName, const SourceRange &NameRange, - Diagnostics *Error); + /// constructor, or Optional<MatcherCtor>() if not found. + static llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName); /// \brief Compute the list of completions for \p Context. /// diff --git a/clang/include/clang/ASTMatchers/Dynamic/VariantValue.h b/clang/include/clang/ASTMatchers/Dynamic/VariantValue.h index c6853572ec8..336da3e5dd3 100644 --- a/clang/include/clang/ASTMatchers/Dynamic/VariantValue.h +++ b/clang/include/clang/ASTMatchers/Dynamic/VariantValue.h @@ -78,7 +78,8 @@ public: /// \brief Clones the provided matchers. /// /// They should be the result of a polymorphic matcher. - static VariantMatcher PolymorphicMatcher(std::vector<DynTypedMatcher> Matchers); + static VariantMatcher + PolymorphicMatcher(std::vector<DynTypedMatcher> Matchers); /// \brief Creates a 'variadic' operator matcher. /// @@ -208,6 +209,10 @@ public: 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; diff --git a/clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp b/clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp index da2ed9a1d27..b0abdc7cf81 100644 --- a/clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Diagnostics.cpp @@ -87,7 +87,7 @@ StringRef contextTypeToFormatString(Diagnostics::ContextType Type) { 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 @@ StringRef errorTypeToFormatString(Diagnostics::ErrorType Type) { 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>"; diff --git a/clang/lib/ASTMatchers/Dynamic/Parser.cpp b/clang/lib/ASTMatchers/Dynamic/Parser.cpp index 24ffdcda51b..70343811898 100644 --- a/clang/lib/ASTMatchers/Dynamic/Parser.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Parser.cpp @@ -258,6 +258,10 @@ private: Parser::Sema::~Sema() {} +VariantValue Parser::Sema::getNamedValue(StringRef Name) { + return VariantValue(); +} + struct Parser::ScopedContextEntry { Parser *P; @@ -274,12 +278,43 @@ struct Parser::ScopedContextEntry { } }; +/// \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 @@ bool Parser::parseMatcherExpressionImpl(VariantValue *Value) { return false; } - llvm::Optional<MatcherCtor> Ctor = - S->lookupMatcherCtor(NameToken.Text, NameToken.Range, Error); + llvm::Optional<MatcherCtor> 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<ParserValue> Args; TokenInfo EndToken; @@ -425,7 +466,7 @@ bool Parser::parseExpressionImpl(VariantValue *Value) { return true; case TokenInfo::TK_Ident: - return parseMatcherExpressionImpl(Value); + return parseIdentifierPrefixImpl(Value); case TokenInfo::TK_CodeCompletion: addExpressionCompletions(); @@ -457,27 +498,23 @@ Parser::Parser(CodeTokenizer *Tokenizer, Sema *S, Diagnostics *Error) : Tokenizer(Tokenizer), S(S), Error(Error) {} -class RegistrySema : public Parser::Sema { -public: - virtual ~RegistrySema() {} - llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName, - const SourceRange &NameRange, - Diagnostics *Error) { - return Registry::lookupMatcherCtor(MatcherName, NameRange, Error); - } - VariantMatcher actOnMatcherExpression(MatcherCtor Ctor, - const SourceRange &NameRange, - StringRef BindID, - ArrayRef<ParserValue> 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<MatcherCtor> +Parser::RegistrySema::lookupMatcherCtor(StringRef MatcherName) { + return Registry::lookupMatcherCtor(MatcherName); +} + +VariantMatcher Parser::RegistrySema::actOnMatcherExpression( + MatcherCtor Ctor, const SourceRange &NameRange, StringRef BindID, + ArrayRef<ParserValue> 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) { diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp index 6089b45e42e..87fe043406b 100644 --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -325,17 +325,12 @@ static llvm::ManagedStatic<RegistryMaps> RegistryData; } // anonymous namespace // static -llvm::Optional<MatcherCtor> -Registry::lookupMatcherCtor(StringRef MatcherName, const SourceRange &NameRange, - Diagnostics *Error) { +llvm::Optional<MatcherCtor> 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<MatcherCtor>(); - } - - return it->second; + return it == RegistryData->constructors().end() + ? llvm::Optional<MatcherCtor>() + : it->second; } namespace { diff --git a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp index cdf4f92db65..a4b493295ee 100644 --- a/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp +++ b/clang/unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -39,9 +39,7 @@ public: Errors.push_back(Error.toStringFull()); } - llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName, - const SourceRange &NameRange, - Diagnostics *Error) { + llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName) { const ExpectedMatchersTy::value_type *Matcher = &*ExpectedMatchers.find(MatcherName); return reinterpret_cast<MatcherCtor>(Matcher); @@ -175,6 +173,29 @@ TEST(ParserTest, FullParserTest) { 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<DynTypedMatcher> HasParameterWithNamedValues( + Parser::parseMatcherExpression( + "functionDecl(param0, hasParameter(1, hasName(nameX)))", &Sema, + &Error)); + EXPECT_EQ("", Error.toStringFull()); + M = HasParameterWithNamedValues->unconditionalConvertTo<Decl>(); + + 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 @@ TEST(ParserTest, Errors) { "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(")); diff --git a/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp b/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp index 150f8c929c9..abacb6fe7d4 100644 --- a/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp +++ b/clang/unittests/ASTMatchers/Dynamic/RegistryTest.cpp @@ -35,21 +35,15 @@ public: return Out; } - llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName, - Diagnostics *Error = 0) { - Diagnostics DummyError; - if (!Error) Error = &DummyError; - llvm::Optional<MatcherCtor> Ctor = - Registry::lookupMatcherCtor(MatcherName, SourceRange(), Error); - EXPECT_EQ("", DummyError.toStringFull()); - return Ctor; + llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName) { + return Registry::lookupMatcherCtor(MatcherName); } VariantMatcher constructMatcher(StringRef MatcherName, Diagnostics *Error = NULL) { Diagnostics DummyError; if (!Error) Error = &DummyError; - llvm::Optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName, Error); + llvm::Optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName); VariantMatcher Out; if (Ctor) Out = Registry::constructMatcher(*Ctor, SourceRange(), Args(), Error); @@ -62,7 +56,7 @@ public: Diagnostics *Error = NULL) { Diagnostics DummyError; if (!Error) Error = &DummyError; - llvm::Optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName, Error); + llvm::Optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName); VariantMatcher Out; if (Ctor) Out = Registry::constructMatcher(*Ctor, SourceRange(), Args(Arg1), Error); @@ -76,7 +70,7 @@ public: Diagnostics *Error = NULL) { Diagnostics DummyError; if (!Error) Error = &DummyError; - llvm::Optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName, Error); + llvm::Optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName); VariantMatcher Out; if (Ctor) Out = Registry::constructMatcher(*Ctor, SourceRange(), Args(Arg1, Arg2), diff --git a/clang/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp b/clang/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp index e62a4645f75..524b73dc524 100644 --- a/clang/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp +++ b/clang/unittests/ASTMatchers/Dynamic/VariantValueTest.cpp @@ -26,6 +26,7 @@ TEST(VariantValueTest, Unsigned) { 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 @@ TEST(VariantValueTest, String) { 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, String) { TEST(VariantValueTest, DynTypedMatcher) { VariantValue Value = VariantMatcher::SingleMatcher(stmt()); + EXPECT_TRUE(Value.hasValue()); EXPECT_FALSE(Value.isUnsigned()); EXPECT_FALSE(Value.isString()); @@ -74,11 +77,13 @@ TEST(VariantValueTest, Assignment) { 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 @@ TEST(VariantValueTest, Assignment) { 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")))) |