Author: Stephen Kelly Date: 2021-01-16T12:53:11Z New Revision: dbe056c2e37f00b9f33ab63bba73dbb004e13562
URL: https://github.com/llvm/llvm-project/commit/dbe056c2e37f00b9f33ab63bba73dbb004e13562 DIFF: https://github.com/llvm/llvm-project/commit/dbe056c2e37f00b9f33ab63bba73dbb004e13562.diff LOG: [ASTMatchers] Make cxxOperatorCallExpr matchers API-compatible with n-ary operators This makes them composable with mapAnyOf(). Differential Revision: https://reviews.llvm.org/D94128 Added: Modified: clang/docs/LibASTMatchersReference.html clang/include/clang/ASTMatchers/ASTMatchers.h clang/include/clang/ASTMatchers/ASTMatchersInternal.h clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp Removed: ################################################################################ diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html index e5de645266b7..497e2d4584a2 100644 --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -3232,6 +3232,16 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2> </pre></td></tr> +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasAnyOperatorName1')"><a name="hasAnyOperatorName1Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr> +<tr><td colspan="4" class="doc" id="hasAnyOperatorName1"><pre>Matches operator expressions (binary or unary) that have any of the +specified names. + + hasAnyOperatorName("+", "-") + Is equivalent to + anyOf(hasOperatorName("+"), hasOperatorName("-")) +</pre></td></tr> + + <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasAnyOverloadedOperatorName0')"><a name="hasAnyOverloadedOperatorName0Anchor">hasAnyOverloadedOperatorName</a></td><td>StringRef, ..., StringRef</td></tr> <tr><td colspan="4" class="doc" id="hasAnyOverloadedOperatorName0"><pre>Matches overloaded operator names. @@ -3244,6 +3254,15 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2> </pre></td></tr> +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasOperatorName1')"><a name="hasOperatorName1Anchor">hasOperatorName</a></td><td>std::string Name</td></tr> +<tr><td colspan="4" class="doc" id="hasOperatorName1"><pre>Matches the operator Name of operator expressions (binary or +unary). + +Example matches a || b (matcher = binaryOperator(hasOperatorName("||"))) + !(a || b) +</pre></td></tr> + + <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasOverloadedOperatorName1')"><a name="hasOverloadedOperatorName1Anchor">hasOverloadedOperatorName</a></td><td>StringRef Name</td></tr> <tr><td colspan="4" class="doc" id="hasOverloadedOperatorName1"><pre>Matches overloaded operator names. @@ -5152,8 +5171,8 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2> </pre></td></tr> -<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>></td><td class="name" onclick="toggle('hasAnyOperatorName1')"><a name="hasAnyOperatorName1Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr> -<tr><td colspan="4" class="doc" id="hasAnyOperatorName1"><pre>Matches operator expressions (binary or unary) that have any of the +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>></td><td class="name" onclick="toggle('hasAnyOperatorName2')"><a name="hasAnyOperatorName2Anchor">hasAnyOperatorName</a></td><td>StringRef, ..., StringRef</td></tr> +<tr><td colspan="4" class="doc" id="hasAnyOperatorName2"><pre>Matches operator expressions (binary or unary) that have any of the specified names. hasAnyOperatorName("+", "-") @@ -5162,8 +5181,8 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2> </pre></td></tr> -<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>></td><td class="name" onclick="toggle('hasOperatorName1')"><a name="hasOperatorName1Anchor">hasOperatorName</a></td><td>std::string Name</td></tr> -<tr><td colspan="4" class="doc" id="hasOperatorName1"><pre>Matches the operator Name of operator expressions (binary or +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1UnaryOperator.html">UnaryOperator</a>></td><td class="name" onclick="toggle('hasOperatorName2')"><a name="hasOperatorName2Anchor">hasOperatorName</a></td><td>std::string Name</td></tr> +<tr><td colspan="4" class="doc" id="hasOperatorName2"><pre>Matches the operator Name of operator expressions (binary or unary). Example matches a || b (matcher = binaryOperator(hasOperatorName("||"))) @@ -5689,16 +5708,16 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2> </pre></td></tr> -<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasLHS1')"><a name="hasLHS1Anchor">hasLHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> -<tr><td colspan="4" class="doc" id="hasLHS1"><pre>Matches the left hand side of binary operator expressions. +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasLHS2')"><a name="hasLHS2Anchor">hasLHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="hasLHS2"><pre>Matches the left hand side of binary operator expressions. Example matches a (matcher = binaryOperator(hasLHS())) a || b </pre></td></tr> -<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasRHS1')"><a name="hasRHS1Anchor">hasRHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> -<tr><td colspan="4" class="doc" id="hasRHS1"><pre>Matches the right hand side of binary operator expressions. +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ArraySubscriptExpr.html">ArraySubscriptExpr</a>></td><td class="name" onclick="toggle('hasRHS2')"><a name="hasRHS2Anchor">hasRHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="hasRHS2"><pre>Matches the right hand side of binary operator expressions. Example matches b (matcher = binaryOperator(hasRHS())) a || b @@ -5749,7 +5768,7 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2> </pre></td></tr> -<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BinaryOperator.html">BinaryOperator</a>></td><td class="name" onclick="toggle('hasEitherOperand0')"><a name="hasEitherOperand0Anchor">hasEitherOperand</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BinaryOperator.html">BinaryOperator</a>></td><td class="name" onclick="toggle('hasEitherOperand0')"><a name="hasEitherOperand0Anchor">hasEitherOperand</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> <tr><td colspan="4" class="doc" id="hasEitherOperand0"><pre>Matches if either the left hand side or the right hand side of a binary operator matches. </pre></td></tr> @@ -5763,7 +5782,7 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2> </pre></td></tr> -<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BinaryOperator.html">BinaryOperator</a>></td><td class="name" onclick="toggle('hasOperands0')"><a name="hasOperands0Anchor">hasOperands</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> Matcher1, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> Matcher2</td></tr> +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1BinaryOperator.html">BinaryOperator</a>></td><td class="name" onclick="toggle('hasOperands0')"><a name="hasOperands0Anchor">hasOperands</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> Matcher1, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> Matcher2</td></tr> <tr><td colspan="4" class="doc" id="hasOperands0"><pre>Matches if both matchers match with opposite sides of the binary operator. Example matcher = binaryOperator(hasOperands(integerLiteral(equals(1), @@ -6276,6 +6295,49 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2> </pre></td></tr> +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasEitherOperand1')"><a name="hasEitherOperand1Anchor">hasEitherOperand</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="hasEitherOperand1"><pre>Matches if either the left hand side or the right hand side of a +binary operator matches. +</pre></td></tr> + + +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasLHS1')"><a name="hasLHS1Anchor">hasLHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="hasLHS1"><pre>Matches the left hand side of binary operator expressions. + +Example matches a (matcher = binaryOperator(hasLHS())) + a || b +</pre></td></tr> + + +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasOperands1')"><a name="hasOperands1Anchor">hasOperands</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> Matcher1, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> Matcher2</td></tr> +<tr><td colspan="4" class="doc" id="hasOperands1"><pre>Matches if both matchers match with opposite sides of the binary operator. + +Example matcher = binaryOperator(hasOperands(integerLiteral(equals(1), + integerLiteral(equals(2))) + 1 + 2 // Match + 2 + 1 // Match + 1 + 1 // No match + 2 + 2 // No match +</pre></td></tr> + + +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasRHS1')"><a name="hasRHS1Anchor">hasRHS</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="hasRHS1"><pre>Matches the right hand side of binary operator expressions. + +Example matches b (matcher = binaryOperator(hasRHS())) + a || b +</pre></td></tr> + + +<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasUnaryOperand1')"><a name="hasUnaryOperand1Anchor">hasUnaryOperand</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="hasUnaryOperand1"><pre>Matches if the operand of a unary operator matches. + +Example matches true (matcher = hasUnaryOperand( + cxxBoolLiteral(equals(true)))) + !true +</pre></td></tr> + + <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>></td><td class="name" onclick="toggle('hasAnyBase0')"><a name="hasAnyBase0Anchor">hasAnyBase</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXBaseSpecifier.html">CXXBaseSpecifier</a>> BaseSpecMatcher</td></tr> <tr><td colspan="4" class="doc" id="hasAnyBase0"><pre>Matches C++ classes that have a direct or indirect base matching BaseSpecMatcher. diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index dd99e6a420af..e81ac4cb7bf8 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -5197,9 +5197,12 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD(equals, /// \endcode AST_POLYMORPHIC_MATCHER_P(hasOperatorName, AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, + CXXOperatorCallExpr, UnaryOperator), std::string, Name) { - return Name == Node.getOpcodeStr(Node.getOpcode()); + if (Optional<StringRef> OpName = internal::getOpName(Node)) + return *OpName == Name; + return false; } /// Matches operator expressions (binary or unary) that have any of the @@ -5211,7 +5214,8 @@ AST_POLYMORPHIC_MATCHER_P(hasOperatorName, extern const internal::VariadicFunction< internal::PolymorphicMatcherWithParam1< internal::HasAnyOperatorNameMatcher, std::vector<std::string>, - AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, UnaryOperator)>, + AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr, + UnaryOperator)>, StringRef, internal::hasAnyOperatorNameFunc> hasAnyOperatorName; @@ -5263,9 +5267,10 @@ AST_POLYMORPHIC_MATCHER(isComparisonOperator, /// \endcode AST_POLYMORPHIC_MATCHER_P(hasLHS, AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, + CXXOperatorCallExpr, ArraySubscriptExpr), internal::Matcher<Expr>, InnerMatcher) { - const Expr *LeftHandSide = Node.getLHS(); + const Expr *LeftHandSide = internal::getLHS(Node); return (LeftHandSide != nullptr && InnerMatcher.matches(*LeftHandSide, Finder, Builder)); } @@ -5278,18 +5283,23 @@ AST_POLYMORPHIC_MATCHER_P(hasLHS, /// \endcode AST_POLYMORPHIC_MATCHER_P(hasRHS, AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, + CXXOperatorCallExpr, ArraySubscriptExpr), internal::Matcher<Expr>, InnerMatcher) { - const Expr *RightHandSide = Node.getRHS(); + const Expr *RightHandSide = internal::getRHS(Node); return (RightHandSide != nullptr && InnerMatcher.matches(*RightHandSide, Finder, Builder)); } /// Matches if either the left hand side or the right hand side of a /// binary operator matches. -inline internal::Matcher<BinaryOperator> hasEitherOperand( - const internal::Matcher<Expr> &InnerMatcher) { - return anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher)); +AST_POLYMORPHIC_MATCHER_P(hasEitherOperand, + AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, + CXXOperatorCallExpr), + internal::Matcher<Expr>, InnerMatcher) { + return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()( + anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher))) + .matches(Node, Finder, Builder); } /// Matches if both matchers match with opposite sides of the binary operator. @@ -5302,11 +5312,15 @@ inline internal::Matcher<BinaryOperator> hasEitherOperand( /// 1 + 1 // No match /// 2 + 2 // No match /// \endcode -inline internal::Matcher<BinaryOperator> -hasOperands(const internal::Matcher<Expr> &Matcher1, - const internal::Matcher<Expr> &Matcher2) { - return anyOf(allOf(hasLHS(Matcher1), hasRHS(Matcher2)), - allOf(hasLHS(Matcher2), hasRHS(Matcher1))); +AST_POLYMORPHIC_MATCHER_P2(hasOperands, + AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, + CXXOperatorCallExpr), + internal::Matcher<Expr>, Matcher1, + internal::Matcher<Expr>, Matcher2) { + return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()( + anyOf(allOf(hasLHS(Matcher1), hasRHS(Matcher2)), + allOf(hasLHS(Matcher2), hasRHS(Matcher1)))) + .matches(Node, Finder, Builder); } /// Matches if the operand of a unary operator matches. @@ -5316,9 +5330,11 @@ hasOperands(const internal::Matcher<Expr> &Matcher1, /// \code /// !true /// \endcode -AST_MATCHER_P(UnaryOperator, hasUnaryOperand, - internal::Matcher<Expr>, InnerMatcher) { - const Expr * const Operand = Node.getSubExpr(); +AST_POLYMORPHIC_MATCHER_P(hasUnaryOperand, + AST_POLYMORPHIC_SUPPORTED_TYPES(UnaryOperator, + CXXOperatorCallExpr), + internal::Matcher<Expr>, InnerMatcher) { + const Expr *const Operand = internal::getSubExpr(Node); return (Operand != nullptr && InnerMatcher.matches(*Operand, Finder, Builder)); } diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h index 2c2b99ba918d..7f97eb3cd438 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1882,6 +1882,160 @@ struct GetBodyMatcher<Ty, typename std::enable_if< } }; +template <typename NodeType> +inline Optional<BinaryOperatorKind> +equivalentBinaryOperator(const NodeType &Node) { + return Node.getOpcode(); +} + +template <> +inline Optional<BinaryOperatorKind> +equivalentBinaryOperator<CXXOperatorCallExpr>(const CXXOperatorCallExpr &Node) { + if (Node.getNumArgs() != 2) + return None; + switch (Node.getOperator()) { + default: + return None; + case OO_ArrowStar: + return BO_PtrMemI; + case OO_Star: + return BO_Mul; + case OO_Slash: + return BO_Div; + case OO_Percent: + return BO_Rem; + case OO_Plus: + return BO_Add; + case OO_Minus: + return BO_Sub; + case OO_LessLess: + return BO_Shl; + case OO_GreaterGreater: + return BO_Shr; + case OO_Spaceship: + return BO_Cmp; + case OO_Less: + return BO_LT; + case OO_Greater: + return BO_GT; + case OO_LessEqual: + return BO_LE; + case OO_GreaterEqual: + return BO_GE; + case OO_EqualEqual: + return BO_EQ; + case OO_ExclaimEqual: + return BO_NE; + case OO_Amp: + return BO_And; + case OO_Caret: + return BO_Xor; + case OO_Pipe: + return BO_Or; + case OO_AmpAmp: + return BO_LAnd; + case OO_PipePipe: + return BO_LOr; + case OO_Equal: + return BO_Assign; + case OO_StarEqual: + return BO_MulAssign; + case OO_SlashEqual: + return BO_DivAssign; + case OO_PercentEqual: + return BO_RemAssign; + case OO_PlusEqual: + return BO_AddAssign; + case OO_MinusEqual: + return BO_SubAssign; + case OO_LessLessEqual: + return BO_ShlAssign; + case OO_GreaterGreaterEqual: + return BO_ShrAssign; + case OO_AmpEqual: + return BO_AndAssign; + case OO_CaretEqual: + return BO_XorAssign; + case OO_PipeEqual: + return BO_OrAssign; + case OO_Comma: + return BO_Comma; + } +} + +template <typename NodeType> +inline Optional<UnaryOperatorKind> +equivalentUnaryOperator(const NodeType &Node) { + return Node.getOpcode(); +} + +template <> +inline Optional<UnaryOperatorKind> +equivalentUnaryOperator<CXXOperatorCallExpr>(const CXXOperatorCallExpr &Node) { + if (Node.getNumArgs() != 1) + return None; + switch (Node.getOperator()) { + default: + return None; + case OO_Plus: + return UO_Plus; + case OO_Minus: + return UO_Minus; + case OO_Amp: + return UO_AddrOf; + case OO_Tilde: + return UO_Not; + case OO_Exclaim: + return UO_LNot; + case OO_PlusPlus: { + const auto *FD = Node.getDirectCallee(); + if (!FD) + return None; + return FD->getNumParams() > 0 ? UO_PostInc : UO_PreInc; + } + case OO_MinusMinus: { + const auto *FD = Node.getDirectCallee(); + if (!FD) + return None; + return FD->getNumParams() > 0 ? UO_PostDec : UO_PreDec; + } + case OO_Coawait: + return UO_Coawait; + } +} + +template <typename NodeType> inline const Expr *getLHS(const NodeType &Node) { + return Node.getLHS(); +} +template <> +inline const Expr * +getLHS<CXXOperatorCallExpr>(const CXXOperatorCallExpr &Node) { + if (!internal::equivalentBinaryOperator(Node)) + return nullptr; + return Node.getArg(0); +} +template <typename NodeType> inline const Expr *getRHS(const NodeType &Node) { + return Node.getRHS(); +} +template <> +inline const Expr * +getRHS<CXXOperatorCallExpr>(const CXXOperatorCallExpr &Node) { + if (!internal::equivalentBinaryOperator(Node)) + return nullptr; + return Node.getArg(1); +} +template <typename NodeType> +inline const Expr *getSubExpr(const NodeType &Node) { + return Node.getSubExpr(); +} +template <> +inline const Expr * +getSubExpr<CXXOperatorCallExpr>(const CXXOperatorCallExpr &Node) { + if (!internal::equivalentUnaryOperator(Node)) + return nullptr; + return Node.getArg(0); +} + template <typename Ty> struct HasSizeMatcher { static bool hasSize(const Ty &Node, unsigned int N) { @@ -1929,6 +2083,23 @@ llvm::Optional<SourceLocation> getExpansionLocOfMacro(StringRef MacroName, SourceLocation Loc, const ASTContext &Context); +inline Optional<StringRef> getOpName(const UnaryOperator &Node) { + return Node.getOpcodeStr(Node.getOpcode()); +} +inline Optional<StringRef> getOpName(const BinaryOperator &Node) { + return Node.getOpcodeStr(); +} +inline Optional<StringRef> getOpName(const CXXOperatorCallExpr &Node) { + auto optBinaryOpcode = equivalentBinaryOperator(Node); + if (!optBinaryOpcode) { + auto optUnaryOpcode = equivalentUnaryOperator(Node); + if (!optUnaryOpcode) + return None; + return UnaryOperator::getOpcodeStr(*optUnaryOpcode); + } + return BinaryOperator::getOpcodeStr(*optBinaryOpcode); +} + /// Matches overloaded operators with a specific name. /// /// The type argument ArgT is not used by this matcher but is used by @@ -1936,8 +2107,10 @@ getExpansionLocOfMacro(StringRef MacroName, SourceLocation Loc, template <typename T, typename ArgT = std::vector<std::string>> class HasAnyOperatorNameMatcher : public SingleNodeMatcherInterface<T> { static_assert(std::is_same<T, BinaryOperator>::value || + std::is_same<T, CXXOperatorCallExpr>::value || std::is_same<T, UnaryOperator>::value, - "Matcher only supports `BinaryOperator` and `UnaryOperator`"); + "Matcher only supports `BinaryOperator`, `UnaryOperator` and " + "`CXXOperatorCallExpr`"); static_assert(std::is_same<ArgT, std::vector<std::string>>::value, "Matcher ArgT must be std::vector<std::string>"); @@ -1946,26 +2119,21 @@ class HasAnyOperatorNameMatcher : public SingleNodeMatcherInterface<T> { : SingleNodeMatcherInterface<T>(), Names(std::move(Names)) {} bool matchesNode(const T &Node) const override { - StringRef OpName = getOpName(Node); - return llvm::any_of( - Names, [&](const std::string &Name) { return Name == OpName; }); + Optional<StringRef> OptOpName = getOpName(Node); + if (!OptOpName) + return false; + return llvm::any_of(Names, [OpName = *OptOpName](const std::string &Name) { + return Name == OpName; + }); } private: - static StringRef getOpName(const UnaryOperator &Node) { - return Node.getOpcodeStr(Node.getOpcode()); - } - static StringRef getOpName(const BinaryOperator &Node) { - return Node.getOpcodeStr(); - } - const std::vector<std::string> Names; }; -using HasOpNameMatcher = - PolymorphicMatcherWithParam1<HasAnyOperatorNameMatcher, - std::vector<std::string>, - void(TypeList<BinaryOperator, UnaryOperator>)>; +using HasOpNameMatcher = PolymorphicMatcherWithParam1< + HasAnyOperatorNameMatcher, std::vector<std::string>, + void(TypeList<BinaryOperator, CXXOperatorCallExpr, UnaryOperator>)>; HasOpNameMatcher hasAnyOperatorNameFunc(ArrayRef<const StringRef *> NameRefs); diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index 49b010feeb73..9e011892f6e5 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -516,6 +516,238 @@ void F() { EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(callExpr, cxxConstructExpr).bind("call")))); + + Code = R"cpp( +struct HasOpNeqMem +{ + bool operator!=(const HasOpNeqMem& other) const + { + return true; + } +}; +struct HasOpFree +{ +}; +bool operator!=(const HasOpFree& lhs, const HasOpFree& rhs) +{ + return true; +} + +void binop() +{ + int s1; + int s2; + if (s1 != s2) + return; +} + +void opMem() +{ + HasOpNeqMem s1; + HasOpNeqMem s2; + if (s1 != s2) + return; +} + +void opFree() +{ + HasOpFree s1; + HasOpFree s2; + if (s1 != s2) + return; +} +)cpp"; + + EXPECT_TRUE(matches( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(binaryOperator, cxxOperatorCallExpr) + .with(hasOperatorName("!="), + forFunction(functionDecl(hasName("binop"))), + hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); + + EXPECT_TRUE(matches( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(binaryOperator, cxxOperatorCallExpr) + .with(hasOperatorName("!="), + forFunction(functionDecl(hasName("opMem"))), + hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); + + EXPECT_TRUE(matches( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(binaryOperator, cxxOperatorCallExpr) + .with(hasOperatorName("!="), + forFunction(functionDecl(hasName("opFree"))), + hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(binaryOperator, cxxOperatorCallExpr) + .with(hasOperatorName("!="), + forFunction(functionDecl(hasName("binop"))), + hasEitherOperand( + declRefExpr(to(varDecl(hasName("s1"))))), + hasEitherOperand( + declRefExpr(to(varDecl(hasName("s2"))))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(binaryOperator, cxxOperatorCallExpr) + .with(hasOperatorName("!="), + forFunction(functionDecl(hasName("opMem"))), + hasEitherOperand( + declRefExpr(to(varDecl(hasName("s1"))))), + hasEitherOperand( + declRefExpr(to(varDecl(hasName("s2"))))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(binaryOperator, cxxOperatorCallExpr) + .with(hasOperatorName("!="), + forFunction(functionDecl(hasName("opFree"))), + hasEitherOperand( + declRefExpr(to(varDecl(hasName("s1"))))), + hasEitherOperand( + declRefExpr(to(varDecl(hasName("s2"))))))))); + + EXPECT_TRUE(matches( + Code, + traverse( + TK_IgnoreUnlessSpelledInSource, + mapAnyOf(binaryOperator, cxxOperatorCallExpr) + .with(hasOperatorName("!="), + forFunction(functionDecl(hasName("binop"))), + hasOperands(declRefExpr(to(varDecl(hasName("s1")))), + declRefExpr(to(varDecl(hasName("s2"))))), + hasOperands(declRefExpr(to(varDecl(hasName("s2")))), + declRefExpr(to(varDecl(hasName("s1"))))))))); + + EXPECT_TRUE(matches( + Code, + traverse( + TK_IgnoreUnlessSpelledInSource, + mapAnyOf(binaryOperator, cxxOperatorCallExpr) + .with(hasOperatorName("!="), + forFunction(functionDecl(hasName("opMem"))), + hasOperands(declRefExpr(to(varDecl(hasName("s1")))), + declRefExpr(to(varDecl(hasName("s2"))))), + hasOperands(declRefExpr(to(varDecl(hasName("s2")))), + declRefExpr(to(varDecl(hasName("s1"))))))))); + + EXPECT_TRUE(matches( + Code, + traverse( + TK_IgnoreUnlessSpelledInSource, + mapAnyOf(binaryOperator, cxxOperatorCallExpr) + .with(hasOperatorName("!="), + forFunction(functionDecl(hasName("opFree"))), + hasOperands(declRefExpr(to(varDecl(hasName("s1")))), + declRefExpr(to(varDecl(hasName("s2"))))), + hasOperands(declRefExpr(to(varDecl(hasName("s2")))), + declRefExpr(to(varDecl(hasName("s1"))))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(binaryOperator, cxxOperatorCallExpr) + .with(hasAnyOperatorName("==", "!="), + forFunction(functionDecl(hasName("binop"))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(binaryOperator, cxxOperatorCallExpr) + .with(hasAnyOperatorName("==", "!="), + forFunction(functionDecl(hasName("opMem"))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(binaryOperator, cxxOperatorCallExpr) + .with(hasAnyOperatorName("==", "!="), + forFunction(functionDecl(hasName("opFree"))))))); + + Code = R"cpp( +struct HasOpBangMem +{ + bool operator!() const + { + return false; + } +}; +struct HasOpBangFree +{ +}; +bool operator!(HasOpBangFree const&) +{ + return false; +} + +void unop() +{ + int s1; + if (!s1) + return; +} + +void opMem() +{ + HasOpBangMem s1; + if (!s1) + return; +} + +void opFree() +{ + HasOpBangFree s1; + if (!s1) + return; +} +)cpp"; + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(unaryOperator, cxxOperatorCallExpr) + .with(hasOperatorName("!"), + forFunction(functionDecl(hasName("unop"))), + hasUnaryOperand( + declRefExpr(to(varDecl(hasName("s1"))))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(unaryOperator, cxxOperatorCallExpr) + .with(hasOperatorName("!"), + forFunction(functionDecl(hasName("opMem"))), + hasUnaryOperand( + declRefExpr(to(varDecl(hasName("s1"))))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(unaryOperator, cxxOperatorCallExpr) + .with(hasOperatorName("!"), + forFunction(functionDecl(hasName("opFree"))), + hasUnaryOperand( + declRefExpr(to(varDecl(hasName("s1"))))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(unaryOperator, cxxOperatorCallExpr) + .with(hasAnyOperatorName("+", "!"), + forFunction(functionDecl(hasName("unop"))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(unaryOperator, cxxOperatorCallExpr) + .with(hasAnyOperatorName("+", "!"), + forFunction(functionDecl(hasName("opMem"))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(unaryOperator, cxxOperatorCallExpr) + .with(hasAnyOperatorName("+", "!"), + forFunction(functionDecl(hasName("opFree"))))))); } TEST_P(ASTMatchersTest, IsDerivedFrom) { diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp index 19ab6187d960..06bcd65d8911 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -1313,6 +1313,85 @@ TEST(MatchBinaryOperator, HasLHSAndHasRHS) { traverse(TK_AsIs, hasRHS(hasType(pointsTo(qualType()))))); EXPECT_TRUE(matches("void x() { 1[\"abc\"]; }", OperatorIntPointer)); EXPECT_TRUE(notMatches("void x() { \"abc\"[1]; }", OperatorIntPointer)); + + StringRef Code = R"cpp( +struct HasOpEqMem +{ + bool operator==(const HasOpEqMem& other) const + { + return true; + } +}; + +struct HasOpFree +{ +}; +bool operator==(const HasOpFree& lhs, const HasOpFree& rhs) +{ + return true; +} + +void opMem() +{ + HasOpEqMem s1; + HasOpEqMem s2; + if (s1 == s2) + return; +} + +void opFree() +{ + HasOpFree s1; + HasOpFree s2; + if (s1 == s2) + return; +} +)cpp"; + auto s1Expr = declRefExpr(to(varDecl(hasName("s1")))); + auto s2Expr = declRefExpr(to(varDecl(hasName("s2")))); + EXPECT_TRUE(matches( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + cxxOperatorCallExpr(forFunction(functionDecl(hasName("opMem"))), + hasOperatorName("=="), hasLHS(s1Expr), + hasRHS(s2Expr))))); + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + cxxOperatorCallExpr( + forFunction(functionDecl(hasName("opMem"))), + hasAnyOperatorName("!=", "=="), hasLHS(s1Expr))))); + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + cxxOperatorCallExpr( + forFunction(functionDecl(hasName("opMem"))), + hasOperatorName("=="), hasOperands(s1Expr, s2Expr))))); + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + cxxOperatorCallExpr( + forFunction(functionDecl(hasName("opMem"))), + hasOperatorName("=="), hasEitherOperand(s2Expr))))); + + EXPECT_TRUE(matches( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + cxxOperatorCallExpr(forFunction(functionDecl(hasName("opFree"))), + hasOperatorName("=="), hasLHS(s1Expr), + hasRHS(s2Expr))))); + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + cxxOperatorCallExpr( + forFunction(functionDecl(hasName("opFree"))), + hasAnyOperatorName("!=", "=="), hasLHS(s1Expr))))); + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + cxxOperatorCallExpr( + forFunction(functionDecl(hasName("opFree"))), + hasOperatorName("=="), hasOperands(s1Expr, s2Expr))))); + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + cxxOperatorCallExpr( + forFunction(functionDecl(hasName("opFree"))), + hasOperatorName("=="), hasEitherOperand(s2Expr))))); } TEST(MatchBinaryOperator, HasEitherOperand) { @@ -1461,6 +1540,60 @@ TEST(MatchUnaryOperator, HasUnaryOperand) { EXPECT_TRUE(matches("void x() { !false; }", OperatorOnFalse)); EXPECT_TRUE(notMatches("void x() { !true; }", OperatorOnFalse)); + + StringRef Code = R"cpp( +struct HasOpBangMem +{ + bool operator!() const + { + return false; + } +}; +struct HasOpBangFree +{ +}; +bool operator!(HasOpBangFree const&) +{ + return false; +} + +void opMem() +{ + HasOpBangMem s1; + if (!s1) + return; +} +void opFree() +{ + HasOpBangFree s1; + if (!s1) + return; +} +)cpp"; + auto s1Expr = declRefExpr(to(varDecl(hasName("s1")))); + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + cxxOperatorCallExpr( + forFunction(functionDecl(hasName("opMem"))), + hasOperatorName("!"), hasUnaryOperand(s1Expr))))); + EXPECT_TRUE(matches( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + cxxOperatorCallExpr(forFunction(functionDecl(hasName("opMem"))), + hasAnyOperatorName("+", "!"), + hasUnaryOperand(s1Expr))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + cxxOperatorCallExpr( + forFunction(functionDecl(hasName("opFree"))), + hasOperatorName("!"), hasUnaryOperand(s1Expr))))); + EXPECT_TRUE(matches( + Code, + traverse(TK_IgnoreUnlessSpelledInSource, + cxxOperatorCallExpr(forFunction(functionDecl(hasName("opFree"))), + hasAnyOperatorName("+", "!"), + hasUnaryOperand(s1Expr))))); } TEST(Matcher, UnaryOperatorTypes) { _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits