Index: clang-tools-extra/trunk/change-namespace/ChangeNamespace.h =================================================================== --- clang-tools-extra/trunk/change-namespace/ChangeNamespace.h +++ clang-tools-extra/trunk/change-namespace/ChangeNamespace.h @@ -157,6 +157,10 @@ // 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 Index: clang-tools-extra/trunk/change-namespace/ChangeNamespace.cpp =================================================================== --- clang-tools-extra/trunk/change-namespace/ChangeNamespace.cpp +++ clang-tools-extra/trunk/change-namespace/ChangeNamespace.cpp @@ -481,6 +481,11 @@ 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 @@ } 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() == Index: clang-tools-extra/trunk/unittests/change-namespace/ChangeNamespaceTests.cpp =================================================================== --- clang-tools-extra/trunk/unittests/change-namespace/ChangeNamespaceTests.cpp +++ clang-tools-extra/trunk/unittests/change-namespace/ChangeNamespaceTests.cpp @@ -575,6 +575,47 @@ 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"