diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 88e552d5c4611..dcc0c2f2cc40c 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -447,6 +447,8 @@ AST Matchers - ``isInStdNamespace`` now supports Decl declared with ``extern "C++"``. - Add ``isExplicitObjectMemberFunction``. +- Fixed ``forEachArgumentWithParam`` and ``forEachArgumentWithParamType`` to + not skip the explicit object parameter for operator calls. clang-format ------------ diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index 96dbcdc344e13..2f71053d030f6 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -5032,6 +5032,25 @@ AST_POLYMORPHIC_MATCHER_P2(hasParameter, && InnerMatcher.matches(*Node.parameters()[N], Finder, Builder)); } +/// Matches if the given method declaration declares a member function with an +/// explicit object parameter. +/// +/// Given +/// \code +/// struct A { +/// int operator-(this A, int); +/// void fun(this A &&self); +/// static int operator()(int); +/// int operator+(int); +/// }; +/// \endcode +/// +/// cxxMethodDecl(isExplicitObjectMemberFunction()) matches the first two +/// methods but not the last two. +AST_MATCHER(CXXMethodDecl, isExplicitObjectMemberFunction) { + return Node.isExplicitObjectMemberFunction(); +} + /// Matches all arguments and their respective ParmVarDecl. /// /// Given @@ -5060,10 +5079,12 @@ AST_POLYMORPHIC_MATCHER_P2(forEachArgumentWithParam, // argument of the method which should not be matched against a parameter, so // we skip over it here. BoundNodesTreeBuilder Matches; - unsigned ArgIndex = cxxOperatorCallExpr(callee(cxxMethodDecl())) - .matches(Node, Finder, &Matches) - ? 1 - : 0; + unsigned ArgIndex = + cxxOperatorCallExpr( + callee(cxxMethodDecl(unless(isExplicitObjectMemberFunction())))) + .matches(Node, Finder, &Matches) + ? 1 + : 0; int ParamIndex = 0; bool Matched = false; for (; ArgIndex < Node.getNumArgs(); ++ArgIndex) { @@ -5121,11 +5142,12 @@ AST_POLYMORPHIC_MATCHER_P2(forEachArgumentWithParamType, // argument of the method which should not be matched against a parameter, so // we skip over it here. BoundNodesTreeBuilder Matches; - unsigned ArgIndex = cxxOperatorCallExpr(callee(cxxMethodDecl())) - .matches(Node, Finder, &Matches) - ? 1 - : 0; - + unsigned ArgIndex = + cxxOperatorCallExpr( + callee(cxxMethodDecl(unless(isExplicitObjectMemberFunction())))) + .matches(Node, Finder, &Matches) + ? 1 + : 0; const FunctionProtoType *FProto = nullptr; if (const auto *Call = dyn_cast(&Node)) { @@ -6366,25 +6388,6 @@ AST_MATCHER(CXXMethodDecl, isConst) { return Node.isConst(); } -/// Matches if the given method declaration declares a member function with an -/// explicit object parameter. -/// -/// Given -/// \code -/// struct A { -/// int operator-(this A, int); -/// void fun(this A &&self); -/// static int operator()(int); -/// int operator+(int); -/// }; -/// \endcode -/// -/// cxxMethodDecl(isExplicitObjectMemberFunction()) matches the first two -/// methods but not the last two. -AST_MATCHER(CXXMethodDecl, isExplicitObjectMemberFunction) { - return Node.isExplicitObjectMemberFunction(); -} - /// Matches if the given method declaration declares a copy assignment /// operator. /// diff --git a/clang/unittests/ASTMatchers/ASTMatchersTest.h b/clang/unittests/ASTMatchers/ASTMatchersTest.h index 1ed1b5958a8b3..e981299531574 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersTest.h +++ b/clang/unittests/ASTMatchers/ASTMatchersTest.h @@ -293,7 +293,8 @@ testing::AssertionResult notMatchesWithOpenMP51(const Twine &Code, template testing::AssertionResult matchAndVerifyResultConditionally( const Twine &Code, const T &AMatcher, - std::unique_ptr FindResultVerifier, bool ExpectResult) { + std::unique_ptr FindResultVerifier, bool ExpectResult, + ArrayRef Args = {}, StringRef Filename = "input.cc") { bool VerifiedResult = false; MatchFinder Finder; VerifyMatch VerifyVerifiedResult(std::move(FindResultVerifier), @@ -304,9 +305,13 @@ testing::AssertionResult matchAndVerifyResultConditionally( // Some tests use typeof, which is a gnu extension. Using an explicit // unknown-unknown triple is good for a large speedup, because it lets us // avoid constructing a full system triple. - std::vector Args = {"-std=gnu++11", "-target", - "i386-unknown-unknown"}; - if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) { + std::vector CompileArgs = {"-std=gnu++11", "-target", + "i386-unknown-unknown"}; + // Append additional arguments at the end to allow overriding the default + // choices that we made above. + llvm::copy(Args, std::back_inserter(CompileArgs)); + + if (!runToolOnCodeWithArgs(Factory->create(), Code, CompileArgs, Filename)) { return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; } if (!VerifiedResult && ExpectResult) { @@ -319,8 +324,8 @@ testing::AssertionResult matchAndVerifyResultConditionally( VerifiedResult = false; SmallString<256> Buffer; - std::unique_ptr AST( - buildASTFromCodeWithArgs(Code.toStringRef(Buffer), Args)); + std::unique_ptr AST(buildASTFromCodeWithArgs( + Code.toStringRef(Buffer), CompileArgs, Filename)); if (!AST.get()) return testing::AssertionFailure() << "Parsing error in \"" << Code << "\" while building AST"; @@ -339,19 +344,24 @@ testing::AssertionResult matchAndVerifyResultConditionally( // FIXME: Find better names for these functions (or document what they // do more precisely). template -testing::AssertionResult matchAndVerifyResultTrue( - const Twine &Code, const T &AMatcher, - std::unique_ptr FindResultVerifier) { - return matchAndVerifyResultConditionally(Code, AMatcher, - std::move(FindResultVerifier), true); +testing::AssertionResult +matchAndVerifyResultTrue(const Twine &Code, const T &AMatcher, + std::unique_ptr FindResultVerifier, + ArrayRef Args = {}, + StringRef Filename = "input.cc") { + return matchAndVerifyResultConditionally( + Code, AMatcher, std::move(FindResultVerifier), + /*ExpectResult=*/true, Args, Filename); } template testing::AssertionResult matchAndVerifyResultFalse( const Twine &Code, const T &AMatcher, - std::unique_ptr FindResultVerifier) { + std::unique_ptr FindResultVerifier, + ArrayRef Args = {}, StringRef Filename = "input.cc") { return matchAndVerifyResultConditionally( - Code, AMatcher, std::move(FindResultVerifier), false); + Code, AMatcher, std::move(FindResultVerifier), + /*ExpectResult=*/false, Args, Filename); } // Implements a run method that returns whether BoundNodes contains a diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp index 6911d7600a718..f198dc71eb833 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -985,6 +985,38 @@ TEST(ForEachArgumentWithParam, HandlesBoundNodesForNonMatches) { std::make_unique>("v", 4))); } +TEST_P(ASTMatchersTest, + ForEachArgumentWithParamMatchesExplicitObjectParamOnOperatorCalls) { + if (!GetParam().isCXX23OrLater()) { + return; + } + + auto DeclRef = declRefExpr(to(varDecl().bind("declOfArg"))).bind("arg"); + auto SelfParam = parmVarDecl().bind("param"); + StatementMatcher CallExpr = + callExpr(forEachArgumentWithParam(DeclRef, SelfParam)); + + StringRef S = R"cpp( + struct A { + int operator()(this const A &self); + }; + A obj; + int global = obj(); + )cpp"; + + auto Args = GetParam().getCommandLineArgs(); + auto Filename = getFilenameForTesting(GetParam().Language); + + EXPECT_TRUE(matchAndVerifyResultTrue( + S, CallExpr, + std::make_unique>("param", "self"), Args, + Filename)); + EXPECT_TRUE(matchAndVerifyResultTrue( + S, CallExpr, + std::make_unique>("declOfArg", "obj"), Args, + Filename)); +} + TEST(ForEachArgumentWithParamType, ReportsNoFalsePositives) { StatementMatcher ArgumentY = declRefExpr(to(varDecl(hasName("y")))).bind("arg"); @@ -1168,6 +1200,37 @@ TEST(ForEachArgumentWithParamType, MatchesVariadicFunctionPtrCalls) { S, CallExpr, std::make_unique>("arg"))); } +TEST_P(ASTMatchersTest, + ForEachArgumentWithParamTypeMatchesExplicitObjectParamOnOperatorCalls) { + if (!GetParam().isCXX23OrLater()) { + return; + } + + auto DeclRef = declRefExpr(to(varDecl().bind("declOfArg"))).bind("arg"); + auto SelfTy = qualType(asString("const A &")).bind("selfType"); + StatementMatcher CallExpr = + callExpr(forEachArgumentWithParamType(DeclRef, SelfTy)); + + StringRef S = R"cpp( + struct A { + int operator()(this const A &self); + }; + A obj; + int global = obj(); + )cpp"; + + auto Args = GetParam().getCommandLineArgs(); + auto Filename = getFilenameForTesting(GetParam().Language); + + EXPECT_TRUE(matchAndVerifyResultTrue( + S, CallExpr, std::make_unique>("selfType"), + Args, Filename)); + EXPECT_TRUE(matchAndVerifyResultTrue( + S, CallExpr, + std::make_unique>("declOfArg", "obj"), Args, + Filename)); +} + TEST(QualType, hasCanonicalType) { EXPECT_TRUE(notMatches("typedef int &int_ref;" "int a;"