diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index a9e0d1698a9178..5ec4a66879c020 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -98,6 +98,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, QualType T1, QualType T2); static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, Decl *D1, Decl *D2); +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + const Stmt *S1, const Stmt *S2); static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, const TemplateArgument &Arg1, const TemplateArgument &Arg2); @@ -437,12 +439,67 @@ class StmtComparer { }; } // namespace +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + const UnaryOperator *E1, + const CXXOperatorCallExpr *E2) { + return UnaryOperator::getOverloadedOperator(E1->getOpcode()) == + E2->getOperator() && + IsStructurallyEquivalent(Context, E1->getSubExpr(), E2->getArg(0)); +} + +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + const CXXOperatorCallExpr *E1, + const UnaryOperator *E2) { + return E1->getOperator() == + UnaryOperator::getOverloadedOperator(E2->getOpcode()) && + IsStructurallyEquivalent(Context, E1->getArg(0), E2->getSubExpr()); +} + +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + const BinaryOperator *E1, + const CXXOperatorCallExpr *E2) { + return BinaryOperator::getOverloadedOperator(E1->getOpcode()) == + E2->getOperator() && + IsStructurallyEquivalent(Context, E1->getLHS(), E2->getArg(0)) && + IsStructurallyEquivalent(Context, E1->getRHS(), E2->getArg(1)); +} + +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + const CXXOperatorCallExpr *E1, + const BinaryOperator *E2) { + return E1->getOperator() == + BinaryOperator::getOverloadedOperator(E2->getOpcode()) && + IsStructurallyEquivalent(Context, E1->getArg(0), E2->getLHS()) && + IsStructurallyEquivalent(Context, E1->getArg(1), E2->getRHS()); +} + /// Determine structural equivalence of two statements. static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, const Stmt *S1, const Stmt *S2) { if (!S1 || !S2) return S1 == S2; + // Check for statements with similar syntax but different AST. + // A UnaryOperator node is more lightweight than a CXXOperatorCallExpr node. + // The more heavyweight node is only created if the definition-time name + // lookup had any results. The lookup results are stored CXXOperatorCallExpr + // only. The lookup results can be different in a "From" and "To" AST even if + // the compared structure is otherwise equivalent. For this reason we must + // treat a similar unary/binary operator node and CXXOperatorCall node as + // equivalent. + if (const auto *E2CXXOperatorCall = dyn_cast(S2)) { + if (const auto *E1Unary = dyn_cast(S1)) + return IsStructurallyEquivalent(Context, E1Unary, E2CXXOperatorCall); + if (const auto *E1Binary = dyn_cast(S1)) + return IsStructurallyEquivalent(Context, E1Binary, E2CXXOperatorCall); + } + if (const auto *E1CXXOperatorCall = dyn_cast(S1)) { + if (const auto *E2Unary = dyn_cast(S2)) + return IsStructurallyEquivalent(Context, E1CXXOperatorCall, E2Unary); + if (const auto *E2Binary = dyn_cast(S2)) + return IsStructurallyEquivalent(Context, E1CXXOperatorCall, E2Binary); + } + // Compare the statements itself. StmtComparer Comparer(Context); if (!Comparer.IsEquivalent(S1, S2)) diff --git a/clang/unittests/AST/StructuralEquivalenceTest.cpp b/clang/unittests/AST/StructuralEquivalenceTest.cpp index 22c7b82460f0a0..91dd717d7b25e5 100644 --- a/clang/unittests/AST/StructuralEquivalenceTest.cpp +++ b/clang/unittests/AST/StructuralEquivalenceTest.cpp @@ -2275,6 +2275,176 @@ TEST_F(StructuralEquivalenceStmtTest, UnaryOperatorDifferentOps) { EXPECT_FALSE(testStructuralMatch(t)); } +TEST_F(StructuralEquivalenceStmtTest, + CXXOperatorCallExprVsUnaryBinaryOperator) { + auto t = makeNamedDecls( + R"( + template + class A; + template + void foo( + A, + A, + A, + A, + A, + A, + A, + A, + A, + A, + A, + A, + A, + A, + A y)>, + A, + A> y)>, + A, + A, + A, + A= y>, + A y>, + A, + A, + A, + A, + A, + A* y>, + A y> + ); + )", + R"( + struct Bar { + Bar& operator=(Bar&); + Bar& operator->(); + }; + + Bar& operator+(Bar&, Bar&); + Bar& operator+(Bar&); + Bar& operator-(Bar&, Bar&); + Bar& operator-(Bar&); + Bar& operator*(Bar&, Bar&); + Bar& operator*(Bar&); + Bar& operator/(Bar&, Bar&); + Bar& operator%(Bar&, Bar&); + Bar& operator^(Bar&, Bar&); + Bar& operator&(Bar&, Bar&); + Bar& operator&(Bar&); + Bar& operator|(Bar&, Bar&); + Bar& operator~(Bar&); + Bar& operator!(Bar&); + Bar& operator<(Bar&, Bar&); + Bar& operator>(Bar&, Bar&); + Bar& operator+=(Bar&, Bar&); + Bar& operator-=(Bar&, Bar&); + Bar& operator*=(Bar&, Bar&); + Bar& operator/=(Bar&, Bar&); + Bar& operator%=(Bar&, Bar&); + Bar& operator^=(Bar&, Bar&); + Bar& operator&=(Bar&, Bar&); + Bar& operator|=(Bar&, Bar&); + Bar& operator<<(Bar&, Bar&); + Bar& operator>>(Bar&, Bar&); + Bar& operator<<=(Bar&, Bar&); + Bar& operator>>=(Bar&, Bar&); + Bar& operator==(Bar&, Bar&); + Bar& operator!=(Bar&, Bar&); + Bar& operator<=(Bar&, Bar&); + Bar& operator>=(Bar&, Bar&); + Bar& operator<=>(Bar&, Bar&); + Bar& operator&&(Bar&, Bar&); + Bar& operator||(Bar&, Bar&); + Bar& operator++(Bar&); + Bar& operator--(Bar&); + Bar& operator,(Bar&, Bar&); + Bar& operator->*(Bar&, Bar&); + + template + class A; + template + void foo( + A, + A, + A, + A, + A, + A, + A, + A, + A, + A, + A, + A, + A, + A, + A y)>, + A, + A> y)>, + A, + A, + A, + A= y>, + A y>, + A, + A, + A, + A, + A, + A* y>, + A y> + ); + )", + Lang_CXX20); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceStmtTest, + CXXOperatorCallExprVsUnaryBinaryOperatorNe) { + auto t = makeNamedDecls( + R"( + template + class A; + template + void foo( + A + ); + )", + R"( + struct Bar; + + Bar& operator-(Bar&, Bar&); + + template + class A; + template + void foo( + A + ); + )", + Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceStmtTest, NonTypeTemplateParm) { + auto t = makeNamedDecls( + R"( + template + class A; + template + void foo(A); + )", + R"( + template + class A; + template + void foo(A); + )", + Lang_CXX11); + // FIXME: These should not match, + EXPECT_TRUE(testStructuralMatch(t)); +} + TEST_F(StructuralEquivalenceStmtTest, UnresolvedLookupDifferentName) { auto t = makeStmts( R"(