1 //===-- ChangeNamespace.cpp - Change namespace implementation -------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 #include "ChangeNamespace.h" 9 #include "clang/AST/ASTContext.h" 10 #include "clang/Format/Format.h" 11 #include "clang/Lex/Lexer.h" 12 #include "llvm/Support/Casting.h" 13 #include "llvm/Support/ErrorHandling.h" 14 15 using namespace clang::ast_matchers; 16 17 namespace clang { 18 namespace change_namespace { 19 20 namespace { 21 22 inline std::string joinNamespaces(ArrayRef<StringRef> Namespaces) { 23 return llvm::join(Namespaces, "::"); 24 } 25 26 // Given "a::b::c", returns {"a", "b", "c"}. 27 llvm::SmallVector<llvm::StringRef, 4> splitSymbolName(llvm::StringRef Name) { 28 llvm::SmallVector<llvm::StringRef, 4> Splitted; 29 Name.split(Splitted, "::", /*MaxSplit=*/-1, 30 /*KeepEmpty=*/false); 31 return Splitted; 32 } 33 34 SourceLocation startLocationForType(TypeLoc TLoc) { 35 // For elaborated types (e.g. `struct a::A`) we want the portion after the 36 // `struct` but including the namespace qualifier, `a::`. 37 if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) { 38 NestedNameSpecifierLoc NestedNameSpecifier = 39 TLoc.castAs<ElaboratedTypeLoc>().getQualifierLoc(); 40 if (NestedNameSpecifier.getNestedNameSpecifier()) 41 return NestedNameSpecifier.getBeginLoc(); 42 TLoc = TLoc.getNextTypeLoc(); 43 } 44 return TLoc.getBeginLoc(); 45 } 46 47 SourceLocation endLocationForType(TypeLoc TLoc) { 48 // Dig past any namespace or keyword qualifications. 49 while (TLoc.getTypeLocClass() == TypeLoc::Elaborated || 50 TLoc.getTypeLocClass() == TypeLoc::Qualified) 51 TLoc = TLoc.getNextTypeLoc(); 52 53 // The location for template specializations (e.g. Foo<int>) includes the 54 // templated types in its location range. We want to restrict this to just 55 // before the `<` character. 56 if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization) 57 return TLoc.castAs<TemplateSpecializationTypeLoc>() 58 .getLAngleLoc() 59 .getLocWithOffset(-1); 60 return TLoc.getEndLoc(); 61 } 62 63 // Returns the containing namespace of `InnerNs` by skipping `PartialNsName`. 64 // If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName` 65 // is empty, nullptr is returned. 66 // For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then 67 // the NamespaceDecl of namespace "a" will be returned. 68 const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs, 69 llvm::StringRef PartialNsName) { 70 if (!InnerNs || PartialNsName.empty()) 71 return nullptr; 72 const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs); 73 const auto *CurrentNs = InnerNs; 74 auto PartialNsNameSplitted = splitSymbolName(PartialNsName); 75 while (!PartialNsNameSplitted.empty()) { 76 // Get the inner-most namespace in CurrentContext. 77 while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext)) 78 CurrentContext = CurrentContext->getParent(); 79 if (!CurrentContext) 80 return nullptr; 81 CurrentNs = llvm::cast<NamespaceDecl>(CurrentContext); 82 if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString()) 83 return nullptr; 84 PartialNsNameSplitted.pop_back(); 85 CurrentContext = CurrentContext->getParent(); 86 } 87 return CurrentNs; 88 } 89 90 static std::unique_ptr<Lexer> 91 getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM, 92 const LangOptions &LangOpts) { 93 if (Loc.isMacroID() && 94 !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc)) 95 return nullptr; 96 // Break down the source location. 97 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); 98 // Try to load the file buffer. 99 bool InvalidTemp = false; 100 llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp); 101 if (InvalidTemp) 102 return nullptr; 103 104 const char *TokBegin = File.data() + LocInfo.second; 105 // Lex from the start of the given location. 106 return std::make_unique<Lexer>(SM.getLocForStartOfFile(LocInfo.first), 107 LangOpts, File.begin(), TokBegin, File.end()); 108 } 109 110 // FIXME: get rid of this helper function if this is supported in clang-refactor 111 // library. 112 static SourceLocation getStartOfNextLine(SourceLocation Loc, 113 const SourceManager &SM, 114 const LangOptions &LangOpts) { 115 std::unique_ptr<Lexer> Lex = getLexerStartingFromLoc(Loc, SM, LangOpts); 116 if (!Lex.get()) 117 return SourceLocation(); 118 llvm::SmallVector<char, 16> Line; 119 // FIXME: this is a bit hacky to get ReadToEndOfLine work. 120 Lex->setParsingPreprocessorDirective(true); 121 Lex->ReadToEndOfLine(&Line); 122 auto End = Loc.getLocWithOffset(Line.size()); 123 return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End 124 ? End 125 : End.getLocWithOffset(1); 126 } 127 128 // Returns `R` with new range that refers to code after `Replaces` being 129 // applied. 130 tooling::Replacement 131 getReplacementInChangedCode(const tooling::Replacements &Replaces, 132 const tooling::Replacement &R) { 133 unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset()); 134 unsigned NewEnd = 135 Replaces.getShiftedCodePosition(R.getOffset() + R.getLength()); 136 return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart, 137 R.getReplacementText()); 138 } 139 140 // Adds a replacement `R` into `Replaces` or merges it into `Replaces` by 141 // applying all existing Replaces first if there is conflict. 142 void addOrMergeReplacement(const tooling::Replacement &R, 143 tooling::Replacements *Replaces) { 144 auto Err = Replaces->add(R); 145 if (Err) { 146 llvm::consumeError(std::move(Err)); 147 auto Replace = getReplacementInChangedCode(*Replaces, R); 148 *Replaces = Replaces->merge(tooling::Replacements(Replace)); 149 } 150 } 151 152 tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End, 153 llvm::StringRef ReplacementText, 154 const SourceManager &SM) { 155 if (!Start.isValid() || !End.isValid()) { 156 llvm::errs() << "start or end location were invalid\n"; 157 return tooling::Replacement(); 158 } 159 if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) { 160 llvm::errs() 161 << "start or end location were in different macro expansions\n"; 162 return tooling::Replacement(); 163 } 164 Start = SM.getSpellingLoc(Start); 165 End = SM.getSpellingLoc(End); 166 if (SM.getFileID(Start) != SM.getFileID(End)) { 167 llvm::errs() << "start or end location were in different files\n"; 168 return tooling::Replacement(); 169 } 170 return tooling::Replacement( 171 SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start), 172 SM.getSpellingLoc(End)), 173 ReplacementText); 174 } 175 176 void addReplacementOrDie( 177 SourceLocation Start, SourceLocation End, llvm::StringRef ReplacementText, 178 const SourceManager &SM, 179 std::map<std::string, tooling::Replacements> *FileToReplacements) { 180 const auto R = createReplacement(Start, End, ReplacementText, SM); 181 auto Err = (*FileToReplacements)[std::string(R.getFilePath())].add(R); 182 if (Err) 183 llvm_unreachable(llvm::toString(std::move(Err)).c_str()); 184 } 185 186 tooling::Replacement createInsertion(SourceLocation Loc, 187 llvm::StringRef InsertText, 188 const SourceManager &SM) { 189 if (Loc.isInvalid()) { 190 llvm::errs() << "insert Location is invalid.\n"; 191 return tooling::Replacement(); 192 } 193 Loc = SM.getSpellingLoc(Loc); 194 return tooling::Replacement(SM, Loc, 0, InsertText); 195 } 196 197 // Returns the shortest qualified name for declaration `DeclName` in the 198 // namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName` 199 // is "a::c::d", then "b::X" will be returned. 200 // Note that if `DeclName` is `::b::X` and `NsName` is `::a::b`, this returns 201 // "::b::X" instead of "b::X" since there will be a name conflict otherwise. 202 // \param DeclName A fully qualified name, "::a::b::X" or "a::b::X". 203 // \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace 204 // will have empty name. 205 std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName, 206 llvm::StringRef NsName) { 207 DeclName = DeclName.ltrim(':'); 208 NsName = NsName.ltrim(':'); 209 if (!DeclName.contains(':')) 210 return std::string(DeclName); 211 212 auto NsNameSplitted = splitSymbolName(NsName); 213 auto DeclNsSplitted = splitSymbolName(DeclName); 214 llvm::StringRef UnqualifiedDeclName = DeclNsSplitted.pop_back_val(); 215 // If the Decl is in global namespace, there is no need to shorten it. 216 if (DeclNsSplitted.empty()) 217 return std::string(UnqualifiedDeclName); 218 // If NsName is the global namespace, we can simply use the DeclName sans 219 // leading "::". 220 if (NsNameSplitted.empty()) 221 return std::string(DeclName); 222 223 if (NsNameSplitted.front() != DeclNsSplitted.front()) { 224 // The DeclName must be fully-qualified, but we still need to decide if a 225 // leading "::" is necessary. For example, if `NsName` is "a::b::c" and the 226 // `DeclName` is "b::X", then the reference must be qualified as "::b::X" 227 // to avoid conflict. 228 if (llvm::is_contained(NsNameSplitted, DeclNsSplitted.front())) 229 return ("::" + DeclName).str(); 230 return std::string(DeclName); 231 } 232 // Since there is already an overlap namespace, we know that `DeclName` can be 233 // shortened, so we reduce the longest common prefix. 234 auto DeclI = DeclNsSplitted.begin(); 235 auto DeclE = DeclNsSplitted.end(); 236 auto NsI = NsNameSplitted.begin(); 237 auto NsE = NsNameSplitted.end(); 238 for (; DeclI != DeclE && NsI != NsE && *DeclI == *NsI; ++DeclI, ++NsI) { 239 } 240 return (DeclI == DeclE) 241 ? UnqualifiedDeclName.str() 242 : (llvm::join(DeclI, DeclE, "::") + "::" + UnqualifiedDeclName) 243 .str(); 244 } 245 246 std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) { 247 if (Code.back() != '\n') 248 Code += "\n"; 249 auto NsSplitted = splitSymbolName(NestedNs); 250 while (!NsSplitted.empty()) { 251 // FIXME: consider code style for comments. 252 Code = ("namespace " + NsSplitted.back() + " {\n" + Code + 253 "} // namespace " + NsSplitted.back() + "\n") 254 .str(); 255 NsSplitted.pop_back(); 256 } 257 return Code; 258 } 259 260 // Returns true if \p D is a nested DeclContext in \p Context 261 bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) { 262 while (D) { 263 if (D == Context) 264 return true; 265 D = D->getParent(); 266 } 267 return false; 268 } 269 270 // Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx. 271 bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D, 272 const DeclContext *DeclCtx, SourceLocation Loc) { 273 SourceLocation DeclLoc = SM.getSpellingLoc(D->getBeginLoc()); 274 Loc = SM.getSpellingLoc(Loc); 275 return SM.isBeforeInTranslationUnit(DeclLoc, Loc) && 276 (SM.getFileID(DeclLoc) == SM.getFileID(Loc) && 277 isNestedDeclContext(DeclCtx, D->getDeclContext())); 278 } 279 280 // Given a qualified symbol name, returns true if the symbol will be 281 // incorrectly qualified without leading "::". For example, a symbol 282 // "nx::ny::Foo" in namespace "na::nx::ny" without leading "::"; a symbol 283 // "util::X" in namespace "na" can potentially conflict with "na::util" (if this 284 // exists). 285 bool conflictInNamespace(const ASTContext &AST, llvm::StringRef QualifiedSymbol, 286 llvm::StringRef Namespace) { 287 auto SymbolSplitted = splitSymbolName(QualifiedSymbol.trim(":")); 288 assert(!SymbolSplitted.empty()); 289 SymbolSplitted.pop_back(); // We are only interested in namespaces. 290 291 if (SymbolSplitted.size() >= 1 && !Namespace.empty()) { 292 auto SymbolTopNs = SymbolSplitted.front(); 293 auto NsSplitted = splitSymbolName(Namespace.trim(":")); 294 assert(!NsSplitted.empty()); 295 296 auto LookupDecl = [&AST](const Decl &Scope, 297 llvm::StringRef Name) -> const NamedDecl * { 298 const auto *DC = llvm::dyn_cast<DeclContext>(&Scope); 299 if (!DC) 300 return nullptr; 301 auto LookupRes = DC->lookup(DeclarationName(&AST.Idents.get(Name))); 302 if (LookupRes.empty()) 303 return nullptr; 304 return LookupRes.front(); 305 }; 306 // We do not check the outermost namespace since it would not be a 307 // conflict if it equals to the symbol's outermost namespace and the 308 // symbol name would have been shortened. 309 const NamedDecl *Scope = 310 LookupDecl(*AST.getTranslationUnitDecl(), NsSplitted.front()); 311 for (const auto &I : llvm::drop_begin(NsSplitted)) { 312 if (I == SymbolTopNs) // Handles "::ny" in "::nx::ny" case. 313 return true; 314 // Handles "::util" and "::nx::util" conflicts. 315 if (Scope) { 316 if (LookupDecl(*Scope, SymbolTopNs)) 317 return true; 318 Scope = LookupDecl(*Scope, I); 319 } 320 } 321 if (Scope && LookupDecl(*Scope, SymbolTopNs)) 322 return true; 323 } 324 return false; 325 } 326 327 bool isTemplateParameter(TypeLoc Type) { 328 while (!Type.isNull()) { 329 if (Type.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm) 330 return true; 331 Type = Type.getNextTypeLoc(); 332 } 333 return false; 334 } 335 336 } // anonymous namespace 337 338 ChangeNamespaceTool::ChangeNamespaceTool( 339 llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern, 340 llvm::ArrayRef<std::string> AllowedSymbolPatterns, 341 std::map<std::string, tooling::Replacements> *FileToReplacements, 342 llvm::StringRef FallbackStyle) 343 : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements), 344 OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')), 345 FilePattern(FilePattern), FilePatternRE(FilePattern) { 346 FileToReplacements->clear(); 347 auto OldNsSplitted = splitSymbolName(OldNamespace); 348 auto NewNsSplitted = splitSymbolName(NewNamespace); 349 // Calculates `DiffOldNamespace` and `DiffNewNamespace`. 350 while (!OldNsSplitted.empty() && !NewNsSplitted.empty() && 351 OldNsSplitted.front() == NewNsSplitted.front()) { 352 OldNsSplitted.erase(OldNsSplitted.begin()); 353 NewNsSplitted.erase(NewNsSplitted.begin()); 354 } 355 DiffOldNamespace = joinNamespaces(OldNsSplitted); 356 DiffNewNamespace = joinNamespaces(NewNsSplitted); 357 358 for (const auto &Pattern : AllowedSymbolPatterns) 359 AllowedSymbolRegexes.emplace_back(Pattern); 360 } 361 362 void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) { 363 std::string FullOldNs = "::" + OldNamespace; 364 // Prefix is the outer-most namespace in DiffOldNamespace. For example, if the 365 // OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will 366 // be "a::b". Declarations in this namespace will not be visible in the new 367 // namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-". 368 llvm::SmallVector<llvm::StringRef, 4> DiffOldNsSplitted; 369 llvm::StringRef(DiffOldNamespace) 370 .split(DiffOldNsSplitted, "::", /*MaxSplit=*/-1, 371 /*KeepEmpty=*/false); 372 std::string Prefix = "-"; 373 if (!DiffOldNsSplitted.empty()) 374 Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) + 375 DiffOldNsSplitted.front()) 376 .str(); 377 auto IsInMovedNs = 378 allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")), 379 isExpansionInFileMatching(FilePattern)); 380 auto IsVisibleInNewNs = anyOf( 381 IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix))))); 382 // Match using declarations. 383 Finder->addMatcher( 384 usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs) 385 .bind("using"), 386 this); 387 // Match using namespace declarations. 388 Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern), 389 IsVisibleInNewNs) 390 .bind("using_namespace"), 391 this); 392 // Match namespace alias declarations. 393 Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern), 394 IsVisibleInNewNs) 395 .bind("namespace_alias"), 396 this); 397 398 // Match old namespace blocks. 399 Finder->addMatcher( 400 namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern)) 401 .bind("old_ns"), 402 this); 403 404 // Match class forward-declarations in the old namespace. 405 // Note that forward-declarations in classes are not matched. 406 Finder->addMatcher(cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())), 407 IsInMovedNs, hasParent(namespaceDecl())) 408 .bind("class_fwd_decl"), 409 this); 410 411 // Match template class forward-declarations in the old namespace. 412 Finder->addMatcher( 413 classTemplateDecl(unless(hasDescendant(cxxRecordDecl(isDefinition()))), 414 IsInMovedNs, hasParent(namespaceDecl())) 415 .bind("template_class_fwd_decl"), 416 this); 417 418 // Match references to types that are not defined in the old namespace. 419 // Forward-declarations in the old namespace are also matched since they will 420 // be moved back to the old namespace. 421 auto DeclMatcher = namedDecl( 422 hasAncestor(namespaceDecl()), 423 unless(anyOf( 424 isImplicit(), hasAncestor(namespaceDecl(isAnonymous())), 425 hasAncestor(cxxRecordDecl()), 426 allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition()))))))); 427 428 // Using shadow declarations in classes always refers to base class, which 429 // does not need to be qualified since it can be inferred from inheritance. 430 // Note that this does not match using alias declarations. 431 auto UsingShadowDeclInClass = 432 usingDecl(hasAnyUsingShadowDecl(decl()), hasParent(cxxRecordDecl())); 433 434 // Match TypeLocs on the declaration. Carefully match only the outermost 435 // TypeLoc and template specialization arguments (which are not outermost) 436 // that are directly linked to types matching `DeclMatcher`. Nested name 437 // specifier locs are handled separately below. 438 Finder->addMatcher( 439 typeLoc(IsInMovedNs, 440 loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))), 441 unless(anyOf(hasParent(typeLoc(loc(qualType( 442 hasDeclaration(DeclMatcher), 443 unless(templateSpecializationType()))))), 444 hasParent(nestedNameSpecifierLoc()), 445 hasAncestor(decl(isImplicit())), 446 hasAncestor(UsingShadowDeclInClass), 447 hasAncestor(functionDecl(isDefaulted())))), 448 hasAncestor(decl().bind("dc"))) 449 .bind("type"), 450 this); 451 452 // Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to 453 // special case it. 454 // Since using declarations inside classes must have the base class in the 455 // nested name specifier, we leave it to the nested name specifier matcher. 456 Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()), 457 unless(UsingShadowDeclInClass)) 458 .bind("using_with_shadow"), 459 this); 460 461 // Handle types in nested name specifier. Specifiers that are in a TypeLoc 462 // matched above are not matched, e.g. "A::" in "A::A" is not matched since 463 // "A::A" would have already been fixed. 464 Finder->addMatcher( 465 nestedNameSpecifierLoc( 466 hasAncestor(decl(IsInMovedNs).bind("dc")), 467 loc(nestedNameSpecifier( 468 specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))), 469 unless(anyOf(hasAncestor(decl(isImplicit())), 470 hasAncestor(UsingShadowDeclInClass), 471 hasAncestor(functionDecl(isDefaulted())), 472 hasAncestor(typeLoc(loc(qualType(hasDeclaration( 473 decl(equalsBoundNode("from_decl")))))))))) 474 .bind("nested_specifier_loc"), 475 this); 476 477 // Matches base class initializers in constructors. TypeLocs of base class 478 // initializers do not need to be fixed. For example, 479 // class X : public a::b::Y { 480 // public: 481 // X() : Y::Y() {} // Y::Y do not need namespace specifier. 482 // }; 483 Finder->addMatcher( 484 cxxCtorInitializer(isBaseInitializer()).bind("base_initializer"), this); 485 486 // Handle function. 487 // Only handle functions that are defined in a namespace excluding member 488 // function, static methods (qualified by nested specifier), and functions 489 // defined in the global namespace. 490 // Note that the matcher does not exclude calls to out-of-line static method 491 // definitions, so we need to exclude them in the callback handler. 492 auto FuncMatcher = 493 functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs, 494 hasAncestor(namespaceDecl(isAnonymous())), 495 hasAncestor(cxxRecordDecl()))), 496 hasParent(namespaceDecl())); 497 Finder->addMatcher(expr(hasAncestor(decl().bind("dc")), IsInMovedNs, 498 unless(hasAncestor(decl(isImplicit()))), 499 anyOf(callExpr(callee(FuncMatcher)).bind("call"), 500 declRefExpr(to(FuncMatcher.bind("func_decl"))) 501 .bind("func_ref"))), 502 this); 503 504 auto GlobalVarMatcher = varDecl( 505 hasGlobalStorage(), hasParent(namespaceDecl()), 506 unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous()))))); 507 Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")), 508 to(GlobalVarMatcher.bind("var_decl"))) 509 .bind("var_ref"), 510 this); 511 512 // Handle unscoped enum constant. 513 auto UnscopedEnumMatcher = enumConstantDecl(hasParent(enumDecl( 514 hasParent(namespaceDecl()), 515 unless(anyOf(isScoped(), IsInMovedNs, hasAncestor(cxxRecordDecl()), 516 hasAncestor(namespaceDecl(isAnonymous()))))))); 517 Finder->addMatcher( 518 declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")), 519 to(UnscopedEnumMatcher.bind("enum_const_decl"))) 520 .bind("enum_const_ref"), 521 this); 522 } 523 524 void ChangeNamespaceTool::run( 525 const ast_matchers::MatchFinder::MatchResult &Result) { 526 if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) { 527 UsingDecls.insert(Using); 528 } else if (const auto *UsingNamespace = 529 Result.Nodes.getNodeAs<UsingDirectiveDecl>( 530 "using_namespace")) { 531 UsingNamespaceDecls.insert(UsingNamespace); 532 } else if (const auto *NamespaceAlias = 533 Result.Nodes.getNodeAs<NamespaceAliasDecl>( 534 "namespace_alias")) { 535 NamespaceAliasDecls.insert(NamespaceAlias); 536 } else if (const auto *NsDecl = 537 Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) { 538 moveOldNamespace(Result, NsDecl); 539 } else if (const auto *FwdDecl = 540 Result.Nodes.getNodeAs<CXXRecordDecl>("class_fwd_decl")) { 541 moveClassForwardDeclaration(Result, cast<NamedDecl>(FwdDecl)); 542 } else if (const auto *TemplateFwdDecl = 543 Result.Nodes.getNodeAs<ClassTemplateDecl>( 544 "template_class_fwd_decl")) { 545 moveClassForwardDeclaration(Result, cast<NamedDecl>(TemplateFwdDecl)); 546 } else if (const auto *UsingWithShadow = 547 Result.Nodes.getNodeAs<UsingDecl>("using_with_shadow")) { 548 fixUsingShadowDecl(Result, UsingWithShadow); 549 } else if (const auto *Specifier = 550 Result.Nodes.getNodeAs<NestedNameSpecifierLoc>( 551 "nested_specifier_loc")) { 552 SourceLocation Start = Specifier->getBeginLoc(); 553 SourceLocation End = endLocationForType(Specifier->getTypeLoc()); 554 fixTypeLoc(Result, Start, End, Specifier->getTypeLoc()); 555 } else if (const auto *BaseInitializer = 556 Result.Nodes.getNodeAs<CXXCtorInitializer>( 557 "base_initializer")) { 558 BaseCtorInitializerTypeLocs.push_back( 559 BaseInitializer->getTypeSourceInfo()->getTypeLoc()); 560 } else if (const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type")) { 561 // This avoids fixing types with record types as qualifier, which is not 562 // filtered by matchers in some cases, e.g. the type is templated. We should 563 // handle the record type qualifier instead. 564 TypeLoc Loc = *TLoc; 565 while (Loc.getTypeLocClass() == TypeLoc::Qualified) 566 Loc = Loc.getNextTypeLoc(); 567 if (Loc.getTypeLocClass() == TypeLoc::Elaborated) { 568 NestedNameSpecifierLoc NestedNameSpecifier = 569 Loc.castAs<ElaboratedTypeLoc>().getQualifierLoc(); 570 // FIXME: avoid changing injected class names. 571 if (auto *NNS = NestedNameSpecifier.getNestedNameSpecifier()) { 572 const Type *SpecifierType = NNS->getAsType(); 573 if (SpecifierType && SpecifierType->isRecordType()) 574 return; 575 } 576 } 577 fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc); 578 } else if (const auto *VarRef = 579 Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) { 580 const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl"); 581 assert(Var); 582 if (Var->getCanonicalDecl()->isStaticDataMember()) 583 return; 584 const auto *Context = Result.Nodes.getNodeAs<Decl>("dc"); 585 assert(Context && "Empty decl context."); 586 fixDeclRefExpr(Result, Context->getDeclContext(), 587 llvm::cast<NamedDecl>(Var), VarRef); 588 } else if (const auto *EnumConstRef = 589 Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) { 590 // Do not rename the reference if it is already scoped by the EnumDecl name. 591 if (EnumConstRef->hasQualifier() && 592 EnumConstRef->getQualifier()->getKind() == 593 NestedNameSpecifier::SpecifierKind::TypeSpec && 594 EnumConstRef->getQualifier()->getAsType()->isEnumeralType()) 595 return; 596 const auto *EnumConstDecl = 597 Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl"); 598 assert(EnumConstDecl); 599 const auto *Context = Result.Nodes.getNodeAs<Decl>("dc"); 600 assert(Context && "Empty decl context."); 601 // FIXME: this would qualify "ns::VALUE" as "ns::EnumValue::VALUE". Fix it 602 // if it turns out to be an issue. 603 fixDeclRefExpr(Result, Context->getDeclContext(), 604 llvm::cast<NamedDecl>(EnumConstDecl), EnumConstRef); 605 } else if (const auto *FuncRef = 606 Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) { 607 // If this reference has been processed as a function call, we do not 608 // process it again. 609 if (!ProcessedFuncRefs.insert(FuncRef).second) 610 return; 611 const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl"); 612 assert(Func); 613 const auto *Context = Result.Nodes.getNodeAs<Decl>("dc"); 614 assert(Context && "Empty decl context."); 615 fixDeclRefExpr(Result, Context->getDeclContext(), 616 llvm::cast<NamedDecl>(Func), FuncRef); 617 } else { 618 const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call"); 619 assert(Call != nullptr && "Expecting callback for CallExpr."); 620 const auto *CalleeFuncRef = 621 llvm::cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit()); 622 ProcessedFuncRefs.insert(CalleeFuncRef); 623 const FunctionDecl *Func = Call->getDirectCallee(); 624 assert(Func != nullptr); 625 // FIXME: ignore overloaded operators. This would miss cases where operators 626 // are called by qualified names (i.e. "ns::operator <"). Ignore such 627 // cases for now. 628 if (Func->isOverloadedOperator()) 629 return; 630 // Ignore out-of-line static methods since they will be handled by nested 631 // name specifiers. 632 if (Func->getCanonicalDecl()->getStorageClass() == 633 StorageClass::SC_Static && 634 Func->isOutOfLine()) 635 return; 636 const auto *Context = Result.Nodes.getNodeAs<Decl>("dc"); 637 assert(Context && "Empty decl context."); 638 SourceRange CalleeRange = Call->getCallee()->getSourceRange(); 639 replaceQualifiedSymbolInDeclContext( 640 Result, Context->getDeclContext(), CalleeRange.getBegin(), 641 CalleeRange.getEnd(), llvm::cast<NamedDecl>(Func)); 642 } 643 } 644 645 static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl, 646 const SourceManager &SM, 647 const LangOptions &LangOpts) { 648 std::unique_ptr<Lexer> Lex = 649 getLexerStartingFromLoc(NsDecl->getBeginLoc(), SM, LangOpts); 650 assert(Lex.get() && 651 "Failed to create lexer from the beginning of namespace."); 652 if (!Lex.get()) 653 return SourceLocation(); 654 Token Tok; 655 while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) { 656 } 657 return Tok.isNot(tok::TokenKind::l_brace) 658 ? SourceLocation() 659 : Tok.getEndLoc().getLocWithOffset(1); 660 } 661 662 // Stores information about a moved namespace in `MoveNamespaces` and leaves 663 // the actual movement to `onEndOfTranslationUnit()`. 664 void ChangeNamespaceTool::moveOldNamespace( 665 const ast_matchers::MatchFinder::MatchResult &Result, 666 const NamespaceDecl *NsDecl) { 667 // If the namespace is empty, do nothing. 668 if (Decl::castToDeclContext(NsDecl)->decls_empty()) 669 return; 670 671 const SourceManager &SM = *Result.SourceManager; 672 // Get the range of the code in the old namespace. 673 SourceLocation Start = 674 getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()); 675 assert(Start.isValid() && "Can't find l_brace for namespace."); 676 MoveNamespace MoveNs; 677 MoveNs.Offset = SM.getFileOffset(Start); 678 // The range of the moved namespace is from the location just past the left 679 // brace to the location right before the right brace. 680 MoveNs.Length = SM.getFileOffset(NsDecl->getRBraceLoc()) - MoveNs.Offset; 681 682 // Insert the new namespace after `DiffOldNamespace`. For example, if 683 // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then 684 // "x::y" will be inserted inside the existing namespace "a" and after "a::b". 685 // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b" 686 // in the above example. 687 // If there is no outer namespace (i.e. DiffOldNamespace is empty), the new 688 // namespace will be a nested namespace in the old namespace. 689 const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace); 690 SourceLocation InsertionLoc = Start; 691 if (OuterNs) { 692 SourceLocation LocAfterNs = getStartOfNextLine( 693 OuterNs->getRBraceLoc(), SM, Result.Context->getLangOpts()); 694 assert(LocAfterNs.isValid() && 695 "Failed to get location after DiffOldNamespace"); 696 InsertionLoc = LocAfterNs; 697 } 698 MoveNs.InsertionOffset = SM.getFileOffset(SM.getSpellingLoc(InsertionLoc)); 699 MoveNs.FID = SM.getFileID(Start); 700 MoveNs.SourceMgr = Result.SourceManager; 701 MoveNamespaces[std::string(SM.getFilename(Start))].push_back(MoveNs); 702 } 703 704 // Removes a class forward declaration from the code in the moved namespace and 705 // creates an `InsertForwardDeclaration` to insert the forward declaration back 706 // into the old namespace after moving code from the old namespace to the new 707 // namespace. 708 // For example, changing "a" to "x": 709 // Old code: 710 // namespace a { 711 // class FWD; 712 // class A { FWD *fwd; } 713 // } // a 714 // New code: 715 // namespace a { 716 // class FWD; 717 // } // a 718 // namespace x { 719 // class A { a::FWD *fwd; } 720 // } // x 721 void ChangeNamespaceTool::moveClassForwardDeclaration( 722 const ast_matchers::MatchFinder::MatchResult &Result, 723 const NamedDecl *FwdDecl) { 724 SourceLocation Start = FwdDecl->getBeginLoc(); 725 SourceLocation End = FwdDecl->getEndLoc(); 726 const SourceManager &SM = *Result.SourceManager; 727 SourceLocation AfterSemi = Lexer::findLocationAfterToken( 728 End, tok::semi, SM, Result.Context->getLangOpts(), 729 /*SkipTrailingWhitespaceAndNewLine=*/true); 730 if (AfterSemi.isValid()) 731 End = AfterSemi.getLocWithOffset(-1); 732 // Delete the forward declaration from the code to be moved. 733 addReplacementOrDie(Start, End, "", SM, &FileToReplacements); 734 llvm::StringRef Code = Lexer::getSourceText( 735 CharSourceRange::getTokenRange(SM.getSpellingLoc(Start), 736 SM.getSpellingLoc(End)), 737 SM, Result.Context->getLangOpts()); 738 // Insert the forward declaration back into the old namespace after moving the 739 // code from old namespace to new namespace. 740 // Insertion information is stored in `InsertFwdDecls` and actual 741 // insertion will be performed in `onEndOfTranslationUnit`. 742 // Get the (old) namespace that contains the forward declaration. 743 const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl"); 744 // The namespace contains the forward declaration, so it must not be empty. 745 assert(!NsDecl->decls_empty()); 746 const auto Insertion = createInsertion( 747 getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()), 748 Code, SM); 749 InsertForwardDeclaration InsertFwd; 750 InsertFwd.InsertionOffset = Insertion.getOffset(); 751 InsertFwd.ForwardDeclText = Insertion.getReplacementText().str(); 752 InsertFwdDecls[std::string(Insertion.getFilePath())].push_back(InsertFwd); 753 } 754 755 // Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p 756 // FromDecl with the shortest qualified name possible when the reference is in 757 // `NewNamespace`. 758 void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext( 759 const ast_matchers::MatchFinder::MatchResult &Result, 760 const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End, 761 const NamedDecl *FromDecl) { 762 const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext(); 763 if (llvm::isa<TranslationUnitDecl>(NsDeclContext)) { 764 // This should not happen in usual unless the TypeLoc is in function type 765 // parameters, e.g `std::function<void(T)>`. In this case, DeclContext of 766 // `T` will be the translation unit. We simply use fully-qualified name 767 // here. 768 // Note that `FromDecl` must not be defined in the old namespace (according 769 // to `DeclMatcher`), so its fully-qualified name will not change after 770 // changing the namespace. 771 addReplacementOrDie(Start, End, FromDecl->getQualifiedNameAsString(), 772 *Result.SourceManager, &FileToReplacements); 773 return; 774 } 775 const auto *NsDecl = llvm::cast<NamespaceDecl>(NsDeclContext); 776 // Calculate the name of the `NsDecl` after it is moved to new namespace. 777 std::string OldNs = NsDecl->getQualifiedNameAsString(); 778 llvm::StringRef Postfix = OldNs; 779 bool Consumed = Postfix.consume_front(OldNamespace); 780 assert(Consumed && "Expect OldNS to start with OldNamespace."); 781 (void)Consumed; 782 const std::string NewNs = (NewNamespace + Postfix).str(); 783 784 llvm::StringRef NestedName = Lexer::getSourceText( 785 CharSourceRange::getTokenRange( 786 Result.SourceManager->getSpellingLoc(Start), 787 Result.SourceManager->getSpellingLoc(End)), 788 *Result.SourceManager, Result.Context->getLangOpts()); 789 std::string FromDeclName = FromDecl->getQualifiedNameAsString(); 790 for (llvm::Regex &RE : AllowedSymbolRegexes) 791 if (RE.match(FromDeclName)) 792 return; 793 std::string ReplaceName = 794 getShortestQualifiedNameInNamespace(FromDeclName, NewNs); 795 // Checks if there is any using namespace declarations that can shorten the 796 // qualified name. 797 for (const auto *UsingNamespace : UsingNamespaceDecls) { 798 if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx, 799 Start)) 800 continue; 801 StringRef FromDeclNameRef = FromDeclName; 802 if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace() 803 ->getQualifiedNameAsString())) { 804 FromDeclNameRef = FromDeclNameRef.drop_front(2); 805 if (FromDeclNameRef.size() < ReplaceName.size()) 806 ReplaceName = std::string(FromDeclNameRef); 807 } 808 } 809 // Checks if there is any namespace alias declarations that can shorten the 810 // qualified name. 811 for (const auto *NamespaceAlias : NamespaceAliasDecls) { 812 if (!isDeclVisibleAtLocation(*Result.SourceManager, NamespaceAlias, DeclCtx, 813 Start)) 814 continue; 815 StringRef FromDeclNameRef = FromDeclName; 816 if (FromDeclNameRef.consume_front( 817 NamespaceAlias->getNamespace()->getQualifiedNameAsString() + 818 "::")) { 819 std::string AliasName = NamespaceAlias->getNameAsString(); 820 std::string AliasQualifiedName = 821 NamespaceAlias->getQualifiedNameAsString(); 822 // We only consider namespace aliases define in the global namespace or 823 // in namespaces that are directly visible from the reference, i.e. 824 // ancestor of the `OldNs`. Note that declarations in ancestor namespaces 825 // but not visible in the new namespace is filtered out by 826 // "IsVisibleInNewNs" matcher. 827 if (AliasQualifiedName != AliasName) { 828 // The alias is defined in some namespace. 829 assert(StringRef(AliasQualifiedName).ends_with("::" + AliasName)); 830 llvm::StringRef AliasNs = 831 StringRef(AliasQualifiedName).drop_back(AliasName.size() + 2); 832 if (!llvm::StringRef(OldNs).starts_with(AliasNs)) 833 continue; 834 } 835 std::string NameWithAliasNamespace = 836 (AliasName + "::" + FromDeclNameRef).str(); 837 if (NameWithAliasNamespace.size() < ReplaceName.size()) 838 ReplaceName = NameWithAliasNamespace; 839 } 840 } 841 // Checks if there is any using shadow declarations that can shorten the 842 // qualified name. 843 bool Matched = false; 844 for (const UsingDecl *Using : UsingDecls) { 845 if (Matched) 846 break; 847 if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) { 848 for (const auto *UsingShadow : Using->shadows()) { 849 const auto *TargetDecl = UsingShadow->getTargetDecl(); 850 if (TargetDecl->getQualifiedNameAsString() == 851 FromDecl->getQualifiedNameAsString()) { 852 ReplaceName = FromDecl->getNameAsString(); 853 Matched = true; 854 break; 855 } 856 } 857 } 858 } 859 bool Conflict = conflictInNamespace(DeclCtx->getParentASTContext(), 860 ReplaceName, NewNamespace); 861 // If the new nested name in the new namespace is the same as it was in the 862 // old namespace, we don't create replacement unless there can be ambiguity. 863 if ((NestedName == ReplaceName && !Conflict) || 864 (NestedName.starts_with("::") && NestedName.drop_front(2) == ReplaceName)) 865 return; 866 // If the reference need to be fully-qualified, add a leading "::" unless 867 // NewNamespace is the global namespace. 868 if (ReplaceName == FromDeclName && !NewNamespace.empty() && Conflict) 869 ReplaceName = "::" + ReplaceName; 870 addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager, 871 &FileToReplacements); 872 } 873 874 // Replace the [Start, End] of `Type` with the shortest qualified name when the 875 // `Type` is in `NewNamespace`. 876 void ChangeNamespaceTool::fixTypeLoc( 877 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start, 878 SourceLocation End, TypeLoc Type) { 879 // FIXME: do not rename template parameter. 880 if (Start.isInvalid() || End.isInvalid()) 881 return; 882 // Types of CXXCtorInitializers do not need to be fixed. 883 if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type)) 884 return; 885 if (isTemplateParameter(Type)) 886 return; 887 // The declaration which this TypeLoc refers to. 888 const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl"); 889 // `hasDeclaration` gives underlying declaration, but if the type is 890 // a typedef type, we need to use the typedef type instead. 891 auto IsInMovedNs = [&](const NamedDecl *D) { 892 if (!llvm::StringRef(D->getQualifiedNameAsString()) 893 .starts_with(OldNamespace + "::")) 894 return false; 895 auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getBeginLoc()); 896 if (ExpansionLoc.isInvalid()) 897 return false; 898 llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc); 899 return FilePatternRE.match(Filename); 900 }; 901 // Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if 902 // `Type` is an alias type, we make `FromDecl` the type alias declaration. 903 // Also, don't fix the \p Type if it refers to a type alias decl in the moved 904 // namespace since the alias decl will be moved along with the type reference. 905 if (auto *Typedef = Type.getType()->getAs<TypedefType>()) { 906 FromDecl = Typedef->getDecl(); 907 if (IsInMovedNs(FromDecl)) 908 return; 909 } else if (auto *TemplateType = 910 Type.getType()->getAs<TemplateSpecializationType>()) { 911 if (TemplateType->isTypeAlias()) { 912 FromDecl = TemplateType->getTemplateName().getAsTemplateDecl(); 913 if (IsInMovedNs(FromDecl)) 914 return; 915 } 916 } 917 const auto *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc"); 918 assert(DeclCtx && "Empty decl context."); 919 replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start, 920 End, FromDecl); 921 } 922 923 void ChangeNamespaceTool::fixUsingShadowDecl( 924 const ast_matchers::MatchFinder::MatchResult &Result, 925 const UsingDecl *UsingDeclaration) { 926 SourceLocation Start = UsingDeclaration->getBeginLoc(); 927 SourceLocation End = UsingDeclaration->getEndLoc(); 928 if (Start.isInvalid() || End.isInvalid()) 929 return; 930 931 assert(UsingDeclaration->shadow_size() > 0); 932 // FIXME: it might not be always accurate to use the first using-decl. 933 const NamedDecl *TargetDecl = 934 UsingDeclaration->shadow_begin()->getTargetDecl(); 935 std::string TargetDeclName = TargetDecl->getQualifiedNameAsString(); 936 // FIXME: check if target_decl_name is in moved ns, which doesn't make much 937 // sense. If this happens, we need to use name with the new namespace. 938 // Use fully qualified name in UsingDecl for now. 939 addReplacementOrDie(Start, End, "using ::" + TargetDeclName, 940 *Result.SourceManager, &FileToReplacements); 941 } 942 943 void ChangeNamespaceTool::fixDeclRefExpr( 944 const ast_matchers::MatchFinder::MatchResult &Result, 945 const DeclContext *UseContext, const NamedDecl *From, 946 const DeclRefExpr *Ref) { 947 SourceRange RefRange = Ref->getSourceRange(); 948 replaceQualifiedSymbolInDeclContext(Result, UseContext, RefRange.getBegin(), 949 RefRange.getEnd(), From); 950 } 951 952 void ChangeNamespaceTool::onEndOfTranslationUnit() { 953 // Move namespace blocks and insert forward declaration to old namespace. 954 for (const auto &FileAndNsMoves : MoveNamespaces) { 955 auto &NsMoves = FileAndNsMoves.second; 956 if (NsMoves.empty()) 957 continue; 958 const std::string &FilePath = FileAndNsMoves.first; 959 auto &Replaces = FileToReplacements[FilePath]; 960 auto &SM = *NsMoves.begin()->SourceMgr; 961 llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID); 962 auto ChangedCode = tooling::applyAllReplacements(Code, Replaces); 963 if (!ChangedCode) { 964 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n"; 965 continue; 966 } 967 // Replacements on the changed code for moving namespaces and inserting 968 // forward declarations to old namespaces. 969 tooling::Replacements NewReplacements; 970 // Cut the changed code from the old namespace and paste the code in the new 971 // namespace. 972 for (const auto &NsMove : NsMoves) { 973 // Calculate the range of the old namespace block in the changed 974 // code. 975 const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset); 976 const unsigned NewLength = 977 Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) - 978 NewOffset; 979 tooling::Replacement Deletion(FilePath, NewOffset, NewLength, ""); 980 std::string MovedCode = ChangedCode->substr(NewOffset, NewLength); 981 std::string MovedCodeWrappedInNewNs = 982 wrapCodeInNamespace(DiffNewNamespace, MovedCode); 983 // Calculate the new offset at which the code will be inserted in the 984 // changed code. 985 unsigned NewInsertionOffset = 986 Replaces.getShiftedCodePosition(NsMove.InsertionOffset); 987 tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0, 988 MovedCodeWrappedInNewNs); 989 addOrMergeReplacement(Deletion, &NewReplacements); 990 addOrMergeReplacement(Insertion, &NewReplacements); 991 } 992 // After moving namespaces, insert forward declarations back to old 993 // namespaces. 994 const auto &FwdDeclInsertions = InsertFwdDecls[FilePath]; 995 for (const auto &FwdDeclInsertion : FwdDeclInsertions) { 996 unsigned NewInsertionOffset = 997 Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset); 998 tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0, 999 FwdDeclInsertion.ForwardDeclText); 1000 addOrMergeReplacement(Insertion, &NewReplacements); 1001 } 1002 // Add replacements referring to the changed code to existing replacements, 1003 // which refers to the original code. 1004 Replaces = Replaces.merge(NewReplacements); 1005 auto Style = 1006 format::getStyle(format::DefaultFormatStyle, FilePath, FallbackStyle); 1007 if (!Style) { 1008 llvm::errs() << llvm::toString(Style.takeError()) << "\n"; 1009 continue; 1010 } 1011 // Clean up old namespaces if there is nothing in it after moving. 1012 auto CleanReplacements = 1013 format::cleanupAroundReplacements(Code, Replaces, *Style); 1014 if (!CleanReplacements) { 1015 llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n"; 1016 continue; 1017 } 1018 FileToReplacements[FilePath] = *CleanReplacements; 1019 } 1020 1021 // Make sure we don't generate replacements for files that do not match 1022 // FilePattern. 1023 for (auto &Entry : FileToReplacements) 1024 if (!FilePatternRE.match(Entry.first)) 1025 Entry.second.clear(); 1026 } 1027 1028 } // namespace change_namespace 1029 } // namespace clang 1030