blob: 2f6f364b9e368fb8fd07b45bf8ead6578b76fe8c [file] [log] [blame]
Eric Liu495b2112016-09-19 17:40:321//===-- ChangeNamespace.cpp - Change namespace implementation -------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include "ChangeNamespace.h"
10#include "clang/Format/Format.h"
11#include "clang/Lex/Lexer.h"
12
13using namespace clang::ast_matchers;
14
15namespace clang {
16namespace change_namespace {
17
18namespace {
19
20inline std::string
21joinNamespaces(const llvm::SmallVectorImpl<StringRef> &Namespaces) {
22 if (Namespaces.empty())
23 return "";
24 std::string Result = Namespaces.front();
25 for (auto I = Namespaces.begin() + 1, E = Namespaces.end(); I != E; ++I)
26 Result += ("::" + *I).str();
27 return Result;
28}
29
30SourceLocation startLocationForType(TypeLoc TLoc) {
31 // For elaborated types (e.g. `struct a::A`) we want the portion after the
32 // `struct` but including the namespace qualifier, `a::`.
33 if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) {
34 NestedNameSpecifierLoc NestedNameSpecifier =
35 TLoc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
36 if (NestedNameSpecifier.getNestedNameSpecifier())
37 return NestedNameSpecifier.getBeginLoc();
38 TLoc = TLoc.getNextTypeLoc();
39 }
40 return TLoc.getLocStart();
41}
42
43SourceLocation EndLocationForType(TypeLoc TLoc) {
44 // Dig past any namespace or keyword qualifications.
45 while (TLoc.getTypeLocClass() == TypeLoc::Elaborated ||
46 TLoc.getTypeLocClass() == TypeLoc::Qualified)
47 TLoc = TLoc.getNextTypeLoc();
48
49 // The location for template specializations (e.g. Foo<int>) includes the
50 // templated types in its location range. We want to restrict this to just
51 // before the `<` character.
52 if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization)
53 return TLoc.castAs<TemplateSpecializationTypeLoc>()
54 .getLAngleLoc()
55 .getLocWithOffset(-1);
56 return TLoc.getEndLoc();
57}
58
59// Returns the containing namespace of `InnerNs` by skipping `PartialNsName`.
60// If the `InnerNs` does not have `PartialNsName` as suffix, nullptr is
61// returned.
62// For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then
63// the NamespaceDecl of namespace "a" will be returned.
64const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs,
65 llvm::StringRef PartialNsName) {
66 const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs);
67 const auto *CurrentNs = InnerNs;
68 llvm::SmallVector<llvm::StringRef, 4> PartialNsNameSplitted;
69 PartialNsName.split(PartialNsNameSplitted, "::");
70 while (!PartialNsNameSplitted.empty()) {
71 // Get the inner-most namespace in CurrentContext.
72 while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext))
73 CurrentContext = CurrentContext->getParent();
74 if (!CurrentContext)
75 return nullptr;
76 CurrentNs = llvm::cast<NamespaceDecl>(CurrentContext);
77 if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString())
78 return nullptr;
79 PartialNsNameSplitted.pop_back();
80 CurrentContext = CurrentContext->getParent();
81 }
82 return CurrentNs;
83}
84
85// FIXME: get rid of this helper function if this is supported in clang-refactor
86// library.
87SourceLocation getStartOfNextLine(SourceLocation Loc, const SourceManager &SM,
88 const LangOptions &LangOpts) {
89 if (Loc.isMacroID() &&
90 !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))
91 return SourceLocation();
92 // Break down the source location.
93 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
94 // Try to load the file buffer.
95 bool InvalidTemp = false;
96 llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
97 if (InvalidTemp)
98 return SourceLocation();
99
100 const char *TokBegin = File.data() + LocInfo.second;
101 // Lex from the start of the given location.
102 Lexer Lex(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(),
103 TokBegin, File.end());
104
105 llvm::SmallVector<char, 16> Line;
106 // FIXME: this is a bit hacky to get ReadToEndOfLine work.
107 Lex.setParsingPreprocessorDirective(true);
108 Lex.ReadToEndOfLine(&Line);
109 // FIXME: should not +1 at EOF.
110 return Loc.getLocWithOffset(Line.size() + 1);
111}
112
113// Returns `R` with new range that refers to code after `Replaces` being
114// applied.
115tooling::Replacement
116getReplacementInChangedCode(const tooling::Replacements &Replaces,
117 const tooling::Replacement &R) {
118 unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset());
119 unsigned NewEnd =
120 Replaces.getShiftedCodePosition(R.getOffset() + R.getLength());
121 return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
122 R.getReplacementText());
123}
124
125// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by
126// applying all existing Replaces first if there is conflict.
127void addOrMergeReplacement(const tooling::Replacement &R,
128 tooling::Replacements *Replaces) {
129 auto Err = Replaces->add(R);
130 if (Err) {
131 llvm::consumeError(std::move(Err));
132 auto Replace = getReplacementInChangedCode(*Replaces, R);
133 *Replaces = Replaces->merge(tooling::Replacements(Replace));
134 }
135}
136
137tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End,
138 llvm::StringRef ReplacementText,
139 const SourceManager &SM) {
140 if (!Start.isValid() || !End.isValid()) {
141 llvm::errs() << "start or end location were invalid\n";
142 return tooling::Replacement();
143 }
144 if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) {
145 llvm::errs()
146 << "start or end location were in different macro expansions\n";
147 return tooling::Replacement();
148 }
149 Start = SM.getSpellingLoc(Start);
150 End = SM.getSpellingLoc(End);
151 if (SM.getFileID(Start) != SM.getFileID(End)) {
152 llvm::errs() << "start or end location were in different files\n";
153 return tooling::Replacement();
154 }
155 return tooling::Replacement(
156 SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
157 SM.getSpellingLoc(End)),
158 ReplacementText);
159}
160
161tooling::Replacement createInsertion(SourceLocation Loc,
162 llvm::StringRef InsertText,
163 const SourceManager &SM) {
164 if (Loc.isInvalid()) {
165 llvm::errs() << "insert Location is invalid.\n";
166 return tooling::Replacement();
167 }
168 Loc = SM.getSpellingLoc(Loc);
169 return tooling::Replacement(SM, Loc, 0, InsertText);
170}
171
172// Returns the shortest qualified name for declaration `DeclName` in the
173// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName`
174// is "a::c::d", then "b::X" will be returned.
175std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,
176 llvm::StringRef NsName) {
177 llvm::SmallVector<llvm::StringRef, 4> DeclNameSplitted;
178 DeclName.split(DeclNameSplitted, "::");
179 if (DeclNameSplitted.size() == 1)
180 return DeclName;
181 const auto UnqualifiedName = DeclNameSplitted.back();
182 while (true) {
183 const auto Pos = NsName.find_last_of(':');
184 if (Pos == llvm::StringRef::npos)
185 return DeclName;
186 const auto Prefix = NsName.substr(0, Pos - 1);
187 if (DeclName.startswith(Prefix))
188 return (Prefix + "::" + UnqualifiedName).str();
189 NsName = Prefix;
190 }
191 return DeclName;
192}
193
194std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) {
195 if (Code.back() != '\n')
196 Code += "\n";
197 llvm::SmallVector<StringRef, 4> NsSplitted;
198 NestedNs.split(NsSplitted, "::");
199 while (!NsSplitted.empty()) {
200 // FIXME: consider code style for comments.
201 Code = ("namespace " + NsSplitted.back() + " {\n" + Code +
202 "} // namespace " + NsSplitted.back() + "\n")
203 .str();
204 NsSplitted.pop_back();
205 }
206 return Code;
207}
208
209} // anonymous namespace
210
211ChangeNamespaceTool::ChangeNamespaceTool(
212 llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
213 std::map<std::string, tooling::Replacements> *FileToReplacements,
214 llvm::StringRef FallbackStyle)
215 : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements),
216 OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')),
217 FilePattern(FilePattern) {
218 FileToReplacements->clear();
219 llvm::SmallVector<llvm::StringRef, 4> OldNsSplitted;
220 llvm::SmallVector<llvm::StringRef, 4> NewNsSplitted;
221 llvm::StringRef(OldNamespace).split(OldNsSplitted, "::");
222 llvm::StringRef(NewNamespace).split(NewNsSplitted, "::");
223 // Calculates `DiffOldNamespace` and `DiffNewNamespace`.
224 while (!OldNsSplitted.empty() && !NewNsSplitted.empty() &&
225 OldNsSplitted.front() == NewNsSplitted.front()) {
226 OldNsSplitted.erase(OldNsSplitted.begin());
227 NewNsSplitted.erase(NewNsSplitted.begin());
228 }
229 DiffOldNamespace = joinNamespaces(OldNsSplitted);
230 DiffNewNamespace = joinNamespaces(NewNsSplitted);
231}
232
233// FIXME: handle the following symbols:
234// - Types in `UsingShadowDecl` (e.g. `using a::b::c;`) which are not matched
235// by `typeLoc`.
236// - Types in nested name specifier, e.g. "na::X" in "na::X::Nested".
237// - Function references.
238// - Variable references.
239void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
240 // Match old namespace blocks.
241 std::string FullOldNs = "::" + OldNamespace;
242 Finder->addMatcher(
243 namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern))
244 .bind("old_ns"),
245 this);
246
247 auto IsInMovedNs =
248 allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")),
249 isExpansionInFileMatching(FilePattern));
250
251 // Match forward-declarations in the old namespace.
252 Finder->addMatcher(
253 cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())), IsInMovedNs)
254 .bind("fwd_decl"),
255 this);
256
257 // Match references to types that are not defined in the old namespace.
258 // Forward-declarations in the old namespace are also matched since they will
259 // be moved back to the old namespace.
260 auto DeclMatcher = namedDecl(
261 hasAncestor(namespaceDecl()),
262 unless(anyOf(
263 hasAncestor(namespaceDecl(isAnonymous())),
264 hasAncestor(cxxRecordDecl()),
265 allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition())))))));
266 // Match TypeLocs on the declaration. Carefully match only the outermost
267 // TypeLoc that's directly linked to the old class and don't handle nested
268 // name specifier locs.
269 // FIXME: match and handle nested name specifier locs.
270 Finder->addMatcher(
271 typeLoc(IsInMovedNs,
272 loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))),
273 unless(anyOf(hasParent(typeLoc(
274 loc(qualType(hasDeclaration(DeclMatcher))))),
275 hasParent(nestedNameSpecifierLoc()))),
276 hasAncestor(decl().bind("dc")))
277 .bind("type"),
278 this);
279}
280
281void ChangeNamespaceTool::run(
282 const ast_matchers::MatchFinder::MatchResult &Result) {
283 if (const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {
284 moveOldNamespace(Result, NsDecl);
285 } else if (const auto *FwdDecl =
286 Result.Nodes.getNodeAs<CXXRecordDecl>("fwd_decl")) {
287 moveClassForwardDeclaration(Result, FwdDecl);
288 } else {
289 const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type");
290 assert(TLoc != nullptr && "Expecting callback for TypeLoc");
291 fixTypeLoc(Result, startLocationForType(*TLoc), EndLocationForType(*TLoc),
292 *TLoc);
293 }
294}
295
296// Stores information about a moved namespace in `MoveNamespaces` and leaves
297// the actual movement to `onEndOfTranslationUnit()`.
298void ChangeNamespaceTool::moveOldNamespace(
299 const ast_matchers::MatchFinder::MatchResult &Result,
300 const NamespaceDecl *NsDecl) {
301 // If the namespace is empty, do nothing.
302 if (Decl::castToDeclContext(NsDecl)->decls_empty())
303 return;
304
305 // Get the range of the code in the old namespace.
306 SourceLocation Start = NsDecl->decls_begin()->getLocStart();
307 SourceLocation End = NsDecl->getRBraceLoc().getLocWithOffset(-1);
308 // Create a replacement that deletes the code in the old namespace merely for
309 // retrieving offset and length from it.
310 const auto R = createReplacement(Start, End, "", *Result.SourceManager);
311 MoveNamespace MoveNs;
312 MoveNs.Offset = R.getOffset();
313 MoveNs.Length = R.getLength();
314
315 // Insert the new namespace after `DiffOldNamespace`. For example, if
316 // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then
317 // "x::y" will be inserted inside the existing namespace "a" and after "a::b".
318 // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b"
319 // in the above example.
320 // FIXME: consider the case where DiffOldNamespace is empty.
321 const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace);
322 SourceLocation LocAfterNs =
323 getStartOfNextLine(OuterNs->getRBraceLoc(), *Result.SourceManager,
324 Result.Context->getLangOpts());
325 assert(LocAfterNs.isValid() &&
326 "Failed to get location after DiffOldNamespace");
327 MoveNs.InsertionOffset = Result.SourceManager->getFileOffset(
328 Result.SourceManager->getSpellingLoc(LocAfterNs));
329
Eric Liucc83c662016-09-19 17:58:59330 MoveNs.FID = Result.SourceManager->getFileID(Start);
331 MoveNs.SourceMgr = Result.SourceManager;
Eric Liu495b2112016-09-19 17:40:32332 MoveNamespaces[R.getFilePath()].push_back(MoveNs);
333}
334
335// Removes a class forward declaration from the code in the moved namespace and
336// creates an `InsertForwardDeclaration` to insert the forward declaration back
337// into the old namespace after moving code from the old namespace to the new
338// namespace.
339// For example, changing "a" to "x":
340// Old code:
341// namespace a {
342// class FWD;
343// class A { FWD *fwd; }
344// } // a
345// New code:
346// namespace a {
347// class FWD;
348// } // a
349// namespace x {
350// class A { a::FWD *fwd; }
351// } // x
352void ChangeNamespaceTool::moveClassForwardDeclaration(
353 const ast_matchers::MatchFinder::MatchResult &Result,
354 const CXXRecordDecl *FwdDecl) {
355 SourceLocation Start = FwdDecl->getLocStart();
356 SourceLocation End = FwdDecl->getLocEnd();
357 SourceLocation AfterSemi = Lexer::findLocationAfterToken(
358 End, tok::semi, *Result.SourceManager, Result.Context->getLangOpts(),
359 /*SkipTrailingWhitespaceAndNewLine=*/true);
360 if (AfterSemi.isValid())
361 End = AfterSemi.getLocWithOffset(-1);
362 // Delete the forward declaration from the code to be moved.
363 const auto Deletion =
364 createReplacement(Start, End, "", *Result.SourceManager);
365 addOrMergeReplacement(Deletion, &FileToReplacements[Deletion.getFilePath()]);
366 llvm::StringRef Code = Lexer::getSourceText(
367 CharSourceRange::getTokenRange(
368 Result.SourceManager->getSpellingLoc(Start),
369 Result.SourceManager->getSpellingLoc(End)),
370 *Result.SourceManager, Result.Context->getLangOpts());
371 // Insert the forward declaration back into the old namespace after moving the
372 // code from old namespace to new namespace.
373 // Insertion information is stored in `InsertFwdDecls` and actual
374 // insertion will be performed in `onEndOfTranslationUnit`.
375 // Get the (old) namespace that contains the forward declaration.
376 const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");
377 // The namespace contains the forward declaration, so it must not be empty.
378 assert(!NsDecl->decls_empty());
379 const auto Insertion = createInsertion(NsDecl->decls_begin()->getLocStart(),
380 Code, *Result.SourceManager);
381 InsertForwardDeclaration InsertFwd;
382 InsertFwd.InsertionOffset = Insertion.getOffset();
383 InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();
384 InsertFwdDecls[Insertion.getFilePath()].push_back(InsertFwd);
385}
386
387// Replaces a qualified symbol that refers to a declaration `DeclName` with the
388// shortest qualified name possible when the reference is in `NewNamespace`.
389void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
390 const ast_matchers::MatchFinder::MatchResult &Result, const Decl *DeclCtx,
391 SourceLocation Start, SourceLocation End, llvm::StringRef DeclName) {
392 const auto *NsDeclContext =
393 DeclCtx->getDeclContext()->getEnclosingNamespaceContext();
394 const auto *NsDecl = llvm::dyn_cast<NamespaceDecl>(NsDeclContext);
395 // Calculate the name of the `NsDecl` after it is moved to new namespace.
396 std::string OldNs = NsDecl->getQualifiedNameAsString();
397 llvm::StringRef Postfix = OldNs;
398 bool Consumed = Postfix.consume_front(OldNamespace);
399 assert(Consumed && "Expect OldNS to start with OldNamespace.");
400 (void)Consumed;
401 const std::string NewNs = (NewNamespace + Postfix).str();
402
403 llvm::StringRef NestedName = Lexer::getSourceText(
404 CharSourceRange::getTokenRange(
405 Result.SourceManager->getSpellingLoc(Start),
406 Result.SourceManager->getSpellingLoc(End)),
407 *Result.SourceManager, Result.Context->getLangOpts());
408 // If the symbol is already fully qualified, no change needs to be make.
409 if (NestedName.startswith("::"))
410 return;
411 std::string ReplaceName =
412 getShortestQualifiedNameInNamespace(DeclName, NewNs);
413 // If the new nested name in the new namespace is the same as it was in the
414 // old namespace, we don't create replacement.
415 if (NestedName == ReplaceName)
416 return;
417 auto R = createReplacement(Start, End, ReplaceName, *Result.SourceManager);
418 addOrMergeReplacement(R, &FileToReplacements[R.getFilePath()]);
419}
420
421// Replace the [Start, End] of `Type` with the shortest qualified name when the
422// `Type` is in `NewNamespace`.
423void ChangeNamespaceTool::fixTypeLoc(
424 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start,
425 SourceLocation End, TypeLoc Type) {
426 // FIXME: do not rename template parameter.
427 if (Start.isInvalid() || End.isInvalid())
428 return;
429 // The declaration which this TypeLoc refers to.
430 const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");
431 // `hasDeclaration` gives underlying declaration, but if the type is
432 // a typedef type, we need to use the typedef type instead.
433 if (auto *Typedef = Type.getType()->getAs<TypedefType>())
434 FromDecl = Typedef->getDecl();
435
436 const Decl *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc");
437 assert(DeclCtx && "Empty decl context.");
438 replaceQualifiedSymbolInDeclContext(Result, DeclCtx, Start, End,
439 FromDecl->getQualifiedNameAsString());
440}
441
442void ChangeNamespaceTool::onEndOfTranslationUnit() {
443 // Move namespace blocks and insert forward declaration to old namespace.
444 for (const auto &FileAndNsMoves : MoveNamespaces) {
445 auto &NsMoves = FileAndNsMoves.second;
446 if (NsMoves.empty())
447 continue;
448 const std::string &FilePath = FileAndNsMoves.first;
449 auto &Replaces = FileToReplacements[FilePath];
Eric Liucc83c662016-09-19 17:58:59450 auto &SM = *NsMoves.begin()->SourceMgr;
451 llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID);
Eric Liu495b2112016-09-19 17:40:32452 auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
453 if (!ChangedCode) {
454 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
455 continue;
456 }
457 // Replacements on the changed code for moving namespaces and inserting
458 // forward declarations to old namespaces.
459 tooling::Replacements NewReplacements;
460 // Cut the changed code from the old namespace and paste the code in the new
461 // namespace.
462 for (const auto &NsMove : NsMoves) {
463 // Calculate the range of the old namespace block in the changed
464 // code.
465 const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset);
466 const unsigned NewLength =
467 Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) -
468 NewOffset;
469 tooling::Replacement Deletion(FilePath, NewOffset, NewLength, "");
470 std::string MovedCode = ChangedCode->substr(NewOffset, NewLength);
471 std::string MovedCodeWrappedInNewNs =
472 wrapCodeInNamespace(DiffNewNamespace, MovedCode);
473 // Calculate the new offset at which the code will be inserted in the
474 // changed code.
475 unsigned NewInsertionOffset =
476 Replaces.getShiftedCodePosition(NsMove.InsertionOffset);
477 tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
478 MovedCodeWrappedInNewNs);
479 addOrMergeReplacement(Deletion, &NewReplacements);
480 addOrMergeReplacement(Insertion, &NewReplacements);
481 }
482 // After moving namespaces, insert forward declarations back to old
483 // namespaces.
484 const auto &FwdDeclInsertions = InsertFwdDecls[FilePath];
485 for (const auto &FwdDeclInsertion : FwdDeclInsertions) {
486 unsigned NewInsertionOffset =
487 Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset);
488 tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
489 FwdDeclInsertion.ForwardDeclText);
490 addOrMergeReplacement(Insertion, &NewReplacements);
491 }
492 // Add replacements referring to the changed code to existing replacements,
493 // which refers to the original code.
494 Replaces = Replaces.merge(NewReplacements);
495 format::FormatStyle Style =
496 format::getStyle("file", FilePath, FallbackStyle);
497 // Clean up old namespaces if there is nothing in it after moving.
498 auto CleanReplacements =
499 format::cleanupAroundReplacements(Code, Replaces, Style);
500 if (!CleanReplacements) {
501 llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
502 continue;
503 }
504 FileToReplacements[FilePath] = *CleanReplacements;
505 }
506}
507
508} // namespace change_namespace
509} // namespace clang