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 @@ -454,6 +454,17 @@ BaseCtorInitializerTypeLocs.push_back( BaseInitializer->getTypeSourceInfo()->getTypeLoc()); } else if (const auto *TLoc = Result.Nodes.getNodeAs("type")) { + // This avoids fixing types with record types as qualifier, which is not + // filtered by matchers in some cases, e.g. the type is templated. We should + // handle the record type qualifier instead. + if (TLoc->getTypeLocClass() == TypeLoc::Elaborated) { + NestedNameSpecifierLoc NestedNameSpecifier = + TLoc->castAs().getQualifierLoc(); + const Type *SpecifierType = + NestedNameSpecifier.getNestedNameSpecifier()->getAsType(); + if (SpecifierType && SpecifierType->isRecordType()) + return; + } fixTypeLoc(Result, startLocationForType(*TLoc), endLocationForType(*TLoc), *TLoc); } else if (const auto *VarRef = @@ -705,27 +716,32 @@ const auto *FromDecl = Result.Nodes.getNodeAs("from_decl"); // `hasDeclaration` gives underlying declaration, but if the type is // a typedef type, we need to use the typedef type instead. + auto IsInMovedNs = [&](const NamedDecl *D) { + if (!llvm::StringRef(D->getQualifiedNameAsString()) + .startswith(OldNamespace + "::")) + return false; + auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getLocStart()); + if (ExpansionLoc.isInvalid()) + return false; + llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc); + return FilePatternRE.match(Filename); + }; + // Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if + // `Type` is an alias type, we make `FromDecl` the type alias declaration. + // Also, don't fix the \p Type if it refers to a type alias decl in the moved + // namespace since the alias decl will be moved along with the type reference. if (auto *Typedef = Type.getType()->getAs()) { FromDecl = Typedef->getDecl(); - auto IsInMovedNs = [&](const NamedDecl *D) { - if (!llvm::StringRef(D->getQualifiedNameAsString()) - .startswith(OldNamespace + "::")) - return false; - auto ExpansionLoc = - Result.SourceManager->getExpansionLoc(D->getLocStart()); - if (ExpansionLoc.isInvalid()) - return false; - llvm::StringRef Filename = - Result.SourceManager->getFilename(ExpansionLoc); - return FilePatternRE.match(Filename); - }; - // Don't fix the \p Type if it refers to a type alias decl in the moved - // namespace since the alias decl will be moved along with the type - // reference. if (IsInMovedNs(FromDecl)) return; + } else if (auto *TemplateType = + Type.getType()->getAs()) { + if (TemplateType->isTypeAlias()) { + FromDecl = TemplateType->getTemplateName().getAsTemplateDecl(); + if (IsInMovedNs(FromDecl)) + return; + } } - const auto *DeclCtx = Result.Nodes.getNodeAs("dc"); assert(DeclCtx && "Empty decl context."); replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start, 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 @@ -1317,6 +1317,88 @@ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); } +TEST_F(ChangeNamespaceTest, UsingAliasInTemplate) { + NewNamespace = "na::nb::nc"; + std::string Code = "namespace some_ns {\n" + "template \n" + "class G {};\n" + "} // namespace some_ns\n" + "namespace na {\n" + "template\n" + "using GG = some_ns::G;\n" + "} // namespace na\n" + "namespace na {\n" + "namespace nb {\n" + "void f() {\n" + " GG g;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace some_ns {\n" + "template \n" + "class G {};\n" + "} // namespace some_ns\n" + "namespace na {\n" + "template\n" + "using GG = some_ns::G;\n" + "} // namespace na\n" + "namespace na {\n" + "namespace nb {\n" + "namespace nc {\n" + "void f() {\n" + " GG g;\n" + "}\n" + "} // namespace nc\n\n" + "} // namespace nb\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, TemplateUsingAliasInBaseClass) { + NewNamespace = "na::nb::nc"; + std::string Code = "namespace some_ns {\n" + "template \n" + "class G {};\n" + "} // namespace some_ns\n" + "namespace na {\n" + "class Base {\n" + "public:\n" + " template\n" + " using GG = some_ns::G;\n" + "};\n" + "class Derived : public Base {};\n" + "} // namespace na\n" + "namespace na {\n" + "namespace nb {\n" + "void f() {\n" + " Derived::GG g;\n" + "}\n" + "} // namespace nb\n" + "} // namespace na\n"; + std::string Expected = "namespace some_ns {\n" + "template \n" + "class G {};\n" + "} // namespace some_ns\n" + "namespace na {\n" + "class Base {\n" + "public:\n" + " template\n" + " using GG = some_ns::G;\n" + "};\n" + "class Derived : public Base {};\n" + "} // namespace na\n" + "namespace na {\n" + "namespace nb {\n" + "namespace nc {\n" + "void f() {\n" + " Derived::GG g;\n" + "}\n" + "} // namespace nc\n\n" + "} // namespace nb\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + } // anonymous namespace } // namespace change_namespace } // namespace clang