diff --git a/clang-tools-extra/change-namespace/ChangeNamespace.cpp b/clang-tools-extra/change-namespace/ChangeNamespace.cpp index 6c04a40880fd..0877519478cb 100644 --- a/clang-tools-extra/change-namespace/ChangeNamespace.cpp +++ b/clang-tools-extra/change-namespace/ChangeNamespace.cpp @@ -481,6 +481,11 @@ void ChangeNamespaceTool::run( llvm::cast(Var), VarRef); } else if (const auto *FuncRef = Result.Nodes.getNodeAs("func_ref")) { + // If this reference has been processed as a function call, we do not + // process it again. + if (ProcessedFuncRefs.count(FuncRef)) + return; + ProcessedFuncRefs.insert(FuncRef); const auto *Func = Result.Nodes.getNodeAs("func_decl"); assert(Func); const auto *Context = Result.Nodes.getNodeAs("dc"); @@ -490,8 +495,16 @@ void ChangeNamespaceTool::run( } else { const auto *Call = Result.Nodes.getNodeAs("call"); assert(Call != nullptr && "Expecting callback for CallExpr."); + const auto *CalleeFuncRef = + llvm::cast(Call->getCallee()->IgnoreImplicit()); + ProcessedFuncRefs.insert(CalleeFuncRef); const FunctionDecl *Func = Call->getDirectCallee(); assert(Func != nullptr); + // FIXME: ignore overloaded operators. This would miss cases where operators + // are called by qualified names (i.e. "ns::operator <"). Ignore such + // cases for now. + if (Func->isOverloadedOperator()) + return; // Ignore out-of-line static methods since they will be handled by nested // name specifiers. if (Func->getCanonicalDecl()->getStorageClass() == diff --git a/clang-tools-extra/change-namespace/ChangeNamespace.h b/clang-tools-extra/change-namespace/ChangeNamespace.h index 342457adf4cc..c8fb7a22355f 100644 --- a/clang-tools-extra/change-namespace/ChangeNamespace.h +++ b/clang-tools-extra/change-namespace/ChangeNamespace.h @@ -157,6 +157,10 @@ private: // TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to // be fixed. llvm::SmallVector BaseCtorInitializerTypeLocs; + // Since a DeclRefExpr for a function call can be matched twice (one as + // CallExpr and one as DeclRefExpr), we record all DeclRefExpr's that have + // been processed so that we don't handle them twice. + llvm::SmallPtrSet ProcessedFuncRefs; }; } // namespace change_namespace diff --git a/clang-tools-extra/unittests/change-namespace/ChangeNamespaceTests.cpp b/clang-tools-extra/unittests/change-namespace/ChangeNamespaceTests.cpp index 8deed04d2f18..71ba1aa5125e 100644 --- a/clang-tools-extra/unittests/change-namespace/ChangeNamespaceTests.cpp +++ b/clang-tools-extra/unittests/change-namespace/ChangeNamespaceTests.cpp @@ -575,6 +575,47 @@ TEST_F(ChangeNamespaceTest, FixFunctionNameSpecifiers) { EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); } +TEST_F(ChangeNamespaceTest, FixOverloadedOperatorFunctionNameSpecifiers) { + std::string Code = + "namespace na {\n" + "class A {\n" + "public:\n" + " int x;\n" + " bool operator==(const A &RHS) const { return x == RHS.x; }\n" + "};\n" + "bool operator<(const A &LHS, const A &RHS) { return LHS.x == RHS.x; }\n" + "namespace nb {\n" + "bool f() {\n" + " A x, y;\n" + " auto f = operator<;\n" + " return (x == y) && (x < y) && (operator<(x, y));\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = + "namespace na {\n" + "class A {\n" + "public:\n" + " int x;\n" + " bool operator==(const A &RHS) const { return x == RHS.x; }\n" + "};\n" + "bool operator<(const A &LHS, const A &RHS) { return LHS.x == RHS.x; }\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "bool f() {\n" + " ::na::A x, y;\n" + " auto f = ::na::operator<;\n" + // FIXME: function calls to overloaded operators are not fixed now even if + // they are referenced by qualified names. + " return (x == y) && (x < y) && (operator<(x,y));\n" + "}\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + TEST_F(ChangeNamespaceTest, FixNonCallingFunctionReferences) { std::string Code = "namespace na {\n" "class A {\n"