|
|
@@ -12,6 +12,7 @@
|
|
|
|
|
|
#include <ostream>
|
|
|
|
|
|
+#include "executable_semantics/ast/ast.h"
|
|
|
#include "executable_semantics/ast/ast_node.h"
|
|
|
#include "executable_semantics/ast/declaration.h"
|
|
|
#include "executable_semantics/ast/expression.h"
|
|
|
@@ -21,76 +22,91 @@
|
|
|
namespace Carbon {
|
|
|
namespace TestingInternal {
|
|
|
|
|
|
-// Matches a Block based on its contents.
|
|
|
-class BlockContentsMatcher {
|
|
|
+// Abstract GoogleMock matcher which matches AstNodes, and is agnostic to
|
|
|
+// whether they are passed by pointer or reference. Derived classes specify what
|
|
|
+// kinds of AstNodes they match by overriding DescribeToImpl and
|
|
|
+// MatchAndExplainImpl.
|
|
|
+class AstNodeMatcherBase {
|
|
|
public:
|
|
|
using is_gtest_matcher = void;
|
|
|
|
|
|
- // Constructs a matcher which matches a Block node whose .statements() matches
|
|
|
- // `matcher`.
|
|
|
- explicit BlockContentsMatcher(
|
|
|
- ::testing::Matcher<llvm::ArrayRef<Nonnull<const Statement*>>> matcher)
|
|
|
- : matcher_(std::move(matcher)) {}
|
|
|
+ virtual ~AstNodeMatcherBase();
|
|
|
|
|
|
void DescribeTo(std::ostream* out) const {
|
|
|
- *out << "is a Block whose statements collection ";
|
|
|
- matcher_.DescribeTo(out);
|
|
|
+ DescribeToImpl(out, /*negated=*/false);
|
|
|
}
|
|
|
|
|
|
void DescribeNegationTo(std::ostream* out) const {
|
|
|
- *out << "is not a Block whose statements collection ";
|
|
|
- matcher_.DescribeTo(out);
|
|
|
+ DescribeToImpl(out, /*negated=*/true);
|
|
|
}
|
|
|
|
|
|
auto MatchAndExplain(const AstNode& node,
|
|
|
::testing::MatchResultListener* out) const -> bool {
|
|
|
- return MatchAndExplain(&node, out);
|
|
|
+ return MatchAndExplainImpl(&node, out);
|
|
|
}
|
|
|
|
|
|
auto MatchAndExplain(Nonnull<const AstNode*> node,
|
|
|
- ::testing::MatchResultListener* out) const -> bool;
|
|
|
+ ::testing::MatchResultListener* out) const -> bool {
|
|
|
+ return MatchAndExplainImpl(node, out);
|
|
|
+ }
|
|
|
|
|
|
private:
|
|
|
- testing::Matcher<llvm::ArrayRef<Nonnull<const Statement*>>> matcher_;
|
|
|
+ // The implementation of this method must satisfy the contract of
|
|
|
+ // `DescribeTo(out)` (as specified by GoogleMock) if `negated` is false,
|
|
|
+ // or the contract of `DescribeNegationTo(out)` if `negated` is true.
|
|
|
+ virtual void DescribeToImpl(std::ostream* out, bool negated) const = 0;
|
|
|
+
|
|
|
+ // The implementation of this method must satisfy the contract of
|
|
|
+ // `MatchAndExplain(node, out)`, as specified by GoogleMock.
|
|
|
+ virtual auto MatchAndExplainImpl(Nonnull<const AstNode*> node,
|
|
|
+ ::testing::MatchResultListener* out) const
|
|
|
+ -> bool = 0;
|
|
|
};
|
|
|
|
|
|
-// Matches an IntLiteral.
|
|
|
-class MatchesIntLiteralMatcher {
|
|
|
+// Matches a Block based on its contents.
|
|
|
+class BlockContentsMatcher : public AstNodeMatcherBase {
|
|
|
public:
|
|
|
- using is_gtest_matcher = void;
|
|
|
-
|
|
|
- // Constructs a matcher which matches an IntLiteral whose value() is `value`.
|
|
|
- explicit MatchesIntLiteralMatcher(int value) : value_(value) {}
|
|
|
+ // Constructs a matcher which matches a Block node whose .statements() matches
|
|
|
+ // `matcher`.
|
|
|
+ explicit BlockContentsMatcher(
|
|
|
+ ::testing::Matcher<llvm::ArrayRef<Nonnull<const Statement*>>> matcher)
|
|
|
+ : matcher_(std::move(matcher)) {}
|
|
|
|
|
|
- void DescribeTo(std::ostream* out) const {
|
|
|
- DescribeToImpl(out, /*negated=*/false);
|
|
|
+ private:
|
|
|
+ void DescribeToImpl(std::ostream* out, bool negated) const override {
|
|
|
+ *out << "is " << (negated ? "not " : "")
|
|
|
+ << "a Block whose statements collection ";
|
|
|
+ matcher_.DescribeTo(out);
|
|
|
}
|
|
|
|
|
|
- void DescribeNegationTo(std::ostream* out) const {
|
|
|
- DescribeToImpl(out, /*negated=*/true);
|
|
|
- }
|
|
|
+ auto MatchAndExplainImpl(Nonnull<const AstNode*> node,
|
|
|
+ ::testing::MatchResultListener* out) const
|
|
|
+ -> bool override;
|
|
|
|
|
|
- auto MatchAndExplain(const AstNode& node,
|
|
|
- ::testing::MatchResultListener* listener) const -> bool {
|
|
|
- return MatchAndExplain(&node, listener);
|
|
|
- }
|
|
|
+ testing::Matcher<llvm::ArrayRef<Nonnull<const Statement*>>> matcher_;
|
|
|
+};
|
|
|
|
|
|
- auto MatchAndExplain(const AstNode* node,
|
|
|
- ::testing::MatchResultListener* listener) const -> bool;
|
|
|
+// Matches an IntLiteral.
|
|
|
+class MatchesIntLiteralMatcher : public AstNodeMatcherBase {
|
|
|
+ public:
|
|
|
+ // Constructs a matcher which matches an IntLiteral whose value() is `value`.
|
|
|
+ explicit MatchesIntLiteralMatcher(int value) : value_(value) {}
|
|
|
|
|
|
private:
|
|
|
- void DescribeToImpl(std::ostream* out, bool negated) const {
|
|
|
+ void DescribeToImpl(std::ostream* out, bool negated) const override {
|
|
|
*out << "is " << (negated ? "not " : "") << "a literal " << value_;
|
|
|
}
|
|
|
|
|
|
+ auto MatchAndExplainImpl(const AstNode* node,
|
|
|
+ ::testing::MatchResultListener* listener) const
|
|
|
+ -> bool override;
|
|
|
+
|
|
|
int value_;
|
|
|
};
|
|
|
|
|
|
// Matches a PrimitiveOperatorExpression that has two operands.
|
|
|
-class BinaryOperatorExpressionMatcher {
|
|
|
+class BinaryOperatorExpressionMatcher : public AstNodeMatcherBase {
|
|
|
public:
|
|
|
- using is_gtest_matcher = void;
|
|
|
-
|
|
|
// Constructs a matcher which matches a PrimitiveOperatorExpression whose
|
|
|
// operator is `op`, and which has two operands that match `lhs` and `rhs`
|
|
|
// respectively.
|
|
|
@@ -99,24 +115,12 @@ class BinaryOperatorExpressionMatcher {
|
|
|
::testing::Matcher<AstNode> rhs)
|
|
|
: op_(op), lhs_(std::move(lhs)), rhs_(std::move(rhs)) {}
|
|
|
|
|
|
- void DescribeTo(std::ostream* out) const {
|
|
|
- DescribeToImpl(out, /*negated=*/false);
|
|
|
- }
|
|
|
-
|
|
|
- void DescribeNegationTo(std::ostream* out) const {
|
|
|
- DescribeToImpl(out, /*negated=*/true);
|
|
|
- }
|
|
|
-
|
|
|
- auto MatchAndExplain(const AstNode& node,
|
|
|
- ::testing::MatchResultListener* out) const -> bool {
|
|
|
- return MatchAndExplain(&node, out);
|
|
|
- }
|
|
|
-
|
|
|
- auto MatchAndExplain(Nonnull<const AstNode*> node,
|
|
|
- ::testing::MatchResultListener* out) const -> bool;
|
|
|
-
|
|
|
private:
|
|
|
- void DescribeToImpl(std::ostream* out, bool negated) const;
|
|
|
+ void DescribeToImpl(std::ostream* out, bool negated) const override;
|
|
|
+
|
|
|
+ auto MatchAndExplainImpl(Nonnull<const AstNode*> node,
|
|
|
+ ::testing::MatchResultListener* out) const
|
|
|
+ -> bool override;
|
|
|
|
|
|
Operator op_;
|
|
|
::testing::Matcher<AstNode> lhs_;
|
|
|
@@ -124,10 +128,8 @@ class BinaryOperatorExpressionMatcher {
|
|
|
};
|
|
|
|
|
|
// Matches a Return node.
|
|
|
-class MatchesReturnMatcher {
|
|
|
+class MatchesReturnMatcher : public AstNodeMatcherBase {
|
|
|
public:
|
|
|
- using is_gtest_matcher = void;
|
|
|
-
|
|
|
// Constructs a matcher which matches a Return statement that has no operand.
|
|
|
explicit MatchesReturnMatcher() = default;
|
|
|
|
|
|
@@ -136,34 +138,20 @@ class MatchesReturnMatcher {
|
|
|
explicit MatchesReturnMatcher(::testing::Matcher<AstNode> matcher)
|
|
|
: matcher_(std::move(matcher)) {}
|
|
|
|
|
|
- void DescribeTo(std::ostream* out) const {
|
|
|
- DescribeToImpl(out, /*negated=*/false);
|
|
|
- }
|
|
|
-
|
|
|
- void DescribeNegationTo(std::ostream* out) const {
|
|
|
- DescribeToImpl(out, /*negated=*/true);
|
|
|
- }
|
|
|
-
|
|
|
- auto MatchAndExplain(const AstNode& node,
|
|
|
- ::testing::MatchResultListener* listener) const -> bool {
|
|
|
- return MatchAndExplain(&node, listener);
|
|
|
- }
|
|
|
-
|
|
|
- auto MatchAndExplain(const AstNode* node,
|
|
|
- ::testing::MatchResultListener* listener) const -> bool;
|
|
|
-
|
|
|
private:
|
|
|
- void DescribeToImpl(std::ostream* out, bool negated) const;
|
|
|
+ void DescribeToImpl(std::ostream* out, bool negated) const override;
|
|
|
+
|
|
|
+ auto MatchAndExplainImpl(const AstNode* node,
|
|
|
+ ::testing::MatchResultListener* listener) const
|
|
|
+ -> bool override;
|
|
|
|
|
|
std::optional<::testing::Matcher<AstNode>> matcher_;
|
|
|
};
|
|
|
|
|
|
// Matches a FunctionDeclaration. See documentation for
|
|
|
// MatchesFunctionDeclaration in ast_test_matchers.h.
|
|
|
-class MatchesFunctionDeclarationMatcher {
|
|
|
+class MatchesFunctionDeclarationMatcher : public AstNodeMatcherBase {
|
|
|
public:
|
|
|
- using is_gtest_matcher = void;
|
|
|
-
|
|
|
MatchesFunctionDeclarationMatcher() = default;
|
|
|
|
|
|
auto WithName(::testing::Matcher<std::string> name_matcher)
|
|
|
@@ -178,27 +166,69 @@ class MatchesFunctionDeclarationMatcher {
|
|
|
return *this;
|
|
|
}
|
|
|
|
|
|
+ private:
|
|
|
+ void DescribeToImpl(std::ostream* out, bool negated) const override;
|
|
|
+ auto MatchAndExplainImpl(const AstNode* node,
|
|
|
+ ::testing::MatchResultListener* listener) const
|
|
|
+ -> bool override;
|
|
|
+
|
|
|
+ std::optional<::testing::Matcher<std::string>> name_matcher_;
|
|
|
+ std::optional<::testing::Matcher<AstNode>> body_matcher_;
|
|
|
+};
|
|
|
+
|
|
|
+// Matches an UnimplementedExpression.
|
|
|
+class MatchesUnimplementedExpressionMatcher : public AstNodeMatcherBase {
|
|
|
+ public:
|
|
|
+ // Constructs a matcher which matches an UnimplementedExpression that has the
|
|
|
+ // given label, and whose children match children_matcher.
|
|
|
+ MatchesUnimplementedExpressionMatcher(
|
|
|
+ std::string label,
|
|
|
+ ::testing::Matcher<llvm::ArrayRef<Nonnull<const AstNode*>>>
|
|
|
+ children_matcher)
|
|
|
+ : label_(std::move(label)),
|
|
|
+ children_matcher_(std::move(children_matcher)) {}
|
|
|
+
|
|
|
+ private:
|
|
|
+ void DescribeToImpl(std::ostream* out, bool negated) const override;
|
|
|
+
|
|
|
+ auto MatchAndExplainImpl(Nonnull<const AstNode*> node,
|
|
|
+ ::testing::MatchResultListener* listener) const
|
|
|
+ -> bool override;
|
|
|
+
|
|
|
+ std::string label_;
|
|
|
+ ::testing::Matcher<llvm::ArrayRef<Nonnull<const AstNode*>>> children_matcher_;
|
|
|
+};
|
|
|
+
|
|
|
+// Matches an `AST`.
|
|
|
+class ASTDeclarationsMatcher {
|
|
|
+ public:
|
|
|
+ using is_gtest_matcher = void;
|
|
|
+
|
|
|
+ // Constructs a matcher which matches an `AST` whose `declarations` member
|
|
|
+ // matches `declarations_matcher`
|
|
|
+ explicit ASTDeclarationsMatcher(
|
|
|
+ ::testing::Matcher<std::vector<Nonnull<Declaration*>>>
|
|
|
+ declarations_matcher)
|
|
|
+ : declarations_matcher_(std::move(declarations_matcher)) {}
|
|
|
+
|
|
|
void DescribeTo(std::ostream* out) const {
|
|
|
- DescribeToImpl(out, /*negated=*/false);
|
|
|
+ *out << "AST declarations ";
|
|
|
+ declarations_matcher_.DescribeTo(out);
|
|
|
}
|
|
|
|
|
|
void DescribeNegationTo(std::ostream* out) const {
|
|
|
- DescribeToImpl(out, /*negated=*/true);
|
|
|
+ *out << "AST declarations ";
|
|
|
+ declarations_matcher_.DescribeNegationTo(out);
|
|
|
}
|
|
|
|
|
|
- auto MatchAndExplain(const AstNode& node,
|
|
|
+ auto MatchAndExplain(const AST& ast,
|
|
|
::testing::MatchResultListener* listener) const -> bool {
|
|
|
- return MatchAndExplain(&node, listener);
|
|
|
+ *listener << "whose declarations ";
|
|
|
+ return declarations_matcher_.MatchAndExplain(ast.declarations, listener);
|
|
|
}
|
|
|
|
|
|
- auto MatchAndExplain(const AstNode* node,
|
|
|
- ::testing::MatchResultListener* listener) const -> bool;
|
|
|
-
|
|
|
private:
|
|
|
- void DescribeToImpl(std::ostream* out, bool negated) const;
|
|
|
-
|
|
|
- std::optional<::testing::Matcher<std::string>> name_matcher_;
|
|
|
- std::optional<::testing::Matcher<AstNode>> body_matcher_;
|
|
|
+ ::testing::Matcher<std::vector<Nonnull<Declaration*>>> declarations_matcher_;
|
|
|
};
|
|
|
|
|
|
} // namespace TestingInternal
|