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
joinNamespaces(ArrayRef<StringRef> Namespaces)22 inline std::string joinNamespaces(ArrayRef<StringRef> Namespaces) {
23 return llvm::join(Namespaces, "::");
24 }
25
26 // Given "a::b::c", returns {"a", "b", "c"}.
splitSymbolName(llvm::StringRef Name)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
startLocationForType(TypeLoc TLoc)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
endLocationForType(TypeLoc TLoc)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.
getOuterNamespace(const NamespaceDecl * InnerNs,llvm::StringRef PartialNsName)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>
getLexerStartingFromLoc(SourceLocation Loc,const SourceManager & SM,const LangOptions & LangOpts)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.
getStartOfNextLine(SourceLocation Loc,const SourceManager & SM,const LangOptions & LangOpts)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
getReplacementInChangedCode(const tooling::Replacements & Replaces,const tooling::Replacement & R)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.
addOrMergeReplacement(const tooling::Replacement & R,tooling::Replacements * Replaces)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
createReplacement(SourceLocation Start,SourceLocation End,llvm::StringRef ReplacementText,const SourceManager & SM)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
addReplacementOrDie(SourceLocation Start,SourceLocation End,llvm::StringRef ReplacementText,const SourceManager & SM,std::map<std::string,tooling::Replacements> * FileToReplacements)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
createInsertion(SourceLocation Loc,llvm::StringRef InsertText,const SourceManager & SM)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.
getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,llvm::StringRef NsName)205 std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,
206 llvm::StringRef NsName) {
207 DeclName = DeclName.ltrim(':');
208 NsName = NsName.ltrim(':');
209 if (DeclName.find(':') == llvm::StringRef::npos)
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
wrapCodeInNamespace(StringRef NestedNs,std::string Code)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
isNestedDeclContext(const DeclContext * D,const DeclContext * 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.
isDeclVisibleAtLocation(const SourceManager & SM,const Decl * D,const DeclContext * DeclCtx,SourceLocation Loc)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).
conflictInNamespace(const ASTContext & AST,llvm::StringRef QualifiedSymbol,llvm::StringRef Namespace)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 (auto I = NsSplitted.begin() + 1, E = NsSplitted.end(); I != E; ++I) {
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
isTemplateParameter(TypeLoc Type)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
ChangeNamespaceTool(llvm::StringRef OldNs,llvm::StringRef NewNs,llvm::StringRef FilePattern,llvm::ArrayRef<std::string> AllowedSymbolPatterns,std::map<std::string,tooling::Replacements> * FileToReplacements,llvm::StringRef FallbackStyle)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
registerMatchers(ast_matchers::MatchFinder * Finder)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(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(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(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
run(const ast_matchers::MatchFinder::MatchResult & Result)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 // This happens for friend declaration of a base class with injected class
571 // name.
572 if (!NestedNameSpecifier.getNestedNameSpecifier())
573 return;
574 const Type *SpecifierType =
575 NestedNameSpecifier.getNestedNameSpecifier()->getAsType();
576 if (SpecifierType && SpecifierType->isRecordType())
577 return;
578 }
579 fixTypeLoc(Result, startLocationForType(Loc), endLocationForType(Loc), Loc);
580 } else if (const auto *VarRef =
581 Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) {
582 const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl");
583 assert(Var);
584 if (Var->getCanonicalDecl()->isStaticDataMember())
585 return;
586 const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
587 assert(Context && "Empty decl context.");
588 fixDeclRefExpr(Result, Context->getDeclContext(),
589 llvm::cast<NamedDecl>(Var), VarRef);
590 } else if (const auto *EnumConstRef =
591 Result.Nodes.getNodeAs<DeclRefExpr>("enum_const_ref")) {
592 // Do not rename the reference if it is already scoped by the EnumDecl name.
593 if (EnumConstRef->hasQualifier() &&
594 EnumConstRef->getQualifier()->getKind() ==
595 NestedNameSpecifier::SpecifierKind::TypeSpec &&
596 EnumConstRef->getQualifier()->getAsType()->isEnumeralType())
597 return;
598 const auto *EnumConstDecl =
599 Result.Nodes.getNodeAs<EnumConstantDecl>("enum_const_decl");
600 assert(EnumConstDecl);
601 const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
602 assert(Context && "Empty decl context.");
603 // FIXME: this would qualify "ns::VALUE" as "ns::EnumValue::VALUE". Fix it
604 // if it turns out to be an issue.
605 fixDeclRefExpr(Result, Context->getDeclContext(),
606 llvm::cast<NamedDecl>(EnumConstDecl), EnumConstRef);
607 } else if (const auto *FuncRef =
608 Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
609 // If this reference has been processed as a function call, we do not
610 // process it again.
611 if (ProcessedFuncRefs.count(FuncRef))
612 return;
613 ProcessedFuncRefs.insert(FuncRef);
614 const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
615 assert(Func);
616 const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
617 assert(Context && "Empty decl context.");
618 fixDeclRefExpr(Result, Context->getDeclContext(),
619 llvm::cast<NamedDecl>(Func), FuncRef);
620 } else {
621 const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
622 assert(Call != nullptr && "Expecting callback for CallExpr.");
623 const auto *CalleeFuncRef =
624 llvm::cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit());
625 ProcessedFuncRefs.insert(CalleeFuncRef);
626 const FunctionDecl *Func = Call->getDirectCallee();
627 assert(Func != nullptr);
628 // FIXME: ignore overloaded operators. This would miss cases where operators
629 // are called by qualified names (i.e. "ns::operator <"). Ignore such
630 // cases for now.
631 if (Func->isOverloadedOperator())
632 return;
633 // Ignore out-of-line static methods since they will be handled by nested
634 // name specifiers.
635 if (Func->getCanonicalDecl()->getStorageClass() ==
636 StorageClass::SC_Static &&
637 Func->isOutOfLine())
638 return;
639 const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
640 assert(Context && "Empty decl context.");
641 SourceRange CalleeRange = Call->getCallee()->getSourceRange();
642 replaceQualifiedSymbolInDeclContext(
643 Result, Context->getDeclContext(), CalleeRange.getBegin(),
644 CalleeRange.getEnd(), llvm::cast<NamedDecl>(Func));
645 }
646 }
647
getLocAfterNamespaceLBrace(const NamespaceDecl * NsDecl,const SourceManager & SM,const LangOptions & LangOpts)648 static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl,
649 const SourceManager &SM,
650 const LangOptions &LangOpts) {
651 std::unique_ptr<Lexer> Lex =
652 getLexerStartingFromLoc(NsDecl->getBeginLoc(), SM, LangOpts);
653 assert(Lex.get() &&
654 "Failed to create lexer from the beginning of namespace.");
655 if (!Lex.get())
656 return SourceLocation();
657 Token Tok;
658 while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) {
659 }
660 return Tok.isNot(tok::TokenKind::l_brace)
661 ? SourceLocation()
662 : Tok.getEndLoc().getLocWithOffset(1);
663 }
664
665 // Stores information about a moved namespace in `MoveNamespaces` and leaves
666 // the actual movement to `onEndOfTranslationUnit()`.
moveOldNamespace(const ast_matchers::MatchFinder::MatchResult & Result,const NamespaceDecl * NsDecl)667 void ChangeNamespaceTool::moveOldNamespace(
668 const ast_matchers::MatchFinder::MatchResult &Result,
669 const NamespaceDecl *NsDecl) {
670 // If the namespace is empty, do nothing.
671 if (Decl::castToDeclContext(NsDecl)->decls_empty())
672 return;
673
674 const SourceManager &SM = *Result.SourceManager;
675 // Get the range of the code in the old namespace.
676 SourceLocation Start =
677 getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts());
678 assert(Start.isValid() && "Can't find l_brace for namespace.");
679 MoveNamespace MoveNs;
680 MoveNs.Offset = SM.getFileOffset(Start);
681 // The range of the moved namespace is from the location just past the left
682 // brace to the location right before the right brace.
683 MoveNs.Length = SM.getFileOffset(NsDecl->getRBraceLoc()) - MoveNs.Offset;
684
685 // Insert the new namespace after `DiffOldNamespace`. For example, if
686 // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then
687 // "x::y" will be inserted inside the existing namespace "a" and after "a::b".
688 // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b"
689 // in the above example.
690 // If there is no outer namespace (i.e. DiffOldNamespace is empty), the new
691 // namespace will be a nested namespace in the old namespace.
692 const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace);
693 SourceLocation InsertionLoc = Start;
694 if (OuterNs) {
695 SourceLocation LocAfterNs = getStartOfNextLine(
696 OuterNs->getRBraceLoc(), SM, Result.Context->getLangOpts());
697 assert(LocAfterNs.isValid() &&
698 "Failed to get location after DiffOldNamespace");
699 InsertionLoc = LocAfterNs;
700 }
701 MoveNs.InsertionOffset = SM.getFileOffset(SM.getSpellingLoc(InsertionLoc));
702 MoveNs.FID = SM.getFileID(Start);
703 MoveNs.SourceMgr = Result.SourceManager;
704 MoveNamespaces[std::string(SM.getFilename(Start))].push_back(MoveNs);
705 }
706
707 // Removes a class forward declaration from the code in the moved namespace and
708 // creates an `InsertForwardDeclaration` to insert the forward declaration back
709 // into the old namespace after moving code from the old namespace to the new
710 // namespace.
711 // For example, changing "a" to "x":
712 // Old code:
713 // namespace a {
714 // class FWD;
715 // class A { FWD *fwd; }
716 // } // a
717 // New code:
718 // namespace a {
719 // class FWD;
720 // } // a
721 // namespace x {
722 // class A { a::FWD *fwd; }
723 // } // x
moveClassForwardDeclaration(const ast_matchers::MatchFinder::MatchResult & Result,const NamedDecl * FwdDecl)724 void ChangeNamespaceTool::moveClassForwardDeclaration(
725 const ast_matchers::MatchFinder::MatchResult &Result,
726 const NamedDecl *FwdDecl) {
727 SourceLocation Start = FwdDecl->getBeginLoc();
728 SourceLocation End = FwdDecl->getEndLoc();
729 const SourceManager &SM = *Result.SourceManager;
730 SourceLocation AfterSemi = Lexer::findLocationAfterToken(
731 End, tok::semi, SM, Result.Context->getLangOpts(),
732 /*SkipTrailingWhitespaceAndNewLine=*/true);
733 if (AfterSemi.isValid())
734 End = AfterSemi.getLocWithOffset(-1);
735 // Delete the forward declaration from the code to be moved.
736 addReplacementOrDie(Start, End, "", SM, &FileToReplacements);
737 llvm::StringRef Code = Lexer::getSourceText(
738 CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
739 SM.getSpellingLoc(End)),
740 SM, Result.Context->getLangOpts());
741 // Insert the forward declaration back into the old namespace after moving the
742 // code from old namespace to new namespace.
743 // Insertion information is stored in `InsertFwdDecls` and actual
744 // insertion will be performed in `onEndOfTranslationUnit`.
745 // Get the (old) namespace that contains the forward declaration.
746 const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");
747 // The namespace contains the forward declaration, so it must not be empty.
748 assert(!NsDecl->decls_empty());
749 const auto Insertion = createInsertion(
750 getLocAfterNamespaceLBrace(NsDecl, SM, Result.Context->getLangOpts()),
751 Code, SM);
752 InsertForwardDeclaration InsertFwd;
753 InsertFwd.InsertionOffset = Insertion.getOffset();
754 InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();
755 InsertFwdDecls[std::string(Insertion.getFilePath())].push_back(InsertFwd);
756 }
757
758 // Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p
759 // FromDecl with the shortest qualified name possible when the reference is in
760 // `NewNamespace`.
replaceQualifiedSymbolInDeclContext(const ast_matchers::MatchFinder::MatchResult & Result,const DeclContext * DeclCtx,SourceLocation Start,SourceLocation End,const NamedDecl * FromDecl)761 void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
762 const ast_matchers::MatchFinder::MatchResult &Result,
763 const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End,
764 const NamedDecl *FromDecl) {
765 const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext();
766 if (llvm::isa<TranslationUnitDecl>(NsDeclContext)) {
767 // This should not happen in usual unless the TypeLoc is in function type
768 // parameters, e.g `std::function<void(T)>`. In this case, DeclContext of
769 // `T` will be the translation unit. We simply use fully-qualified name
770 // here.
771 // Note that `FromDecl` must not be defined in the old namespace (according
772 // to `DeclMatcher`), so its fully-qualified name will not change after
773 // changing the namespace.
774 addReplacementOrDie(Start, End, FromDecl->getQualifiedNameAsString(),
775 *Result.SourceManager, &FileToReplacements);
776 return;
777 }
778 const auto *NsDecl = llvm::cast<NamespaceDecl>(NsDeclContext);
779 // Calculate the name of the `NsDecl` after it is moved to new namespace.
780 std::string OldNs = NsDecl->getQualifiedNameAsString();
781 llvm::StringRef Postfix = OldNs;
782 bool Consumed = Postfix.consume_front(OldNamespace);
783 assert(Consumed && "Expect OldNS to start with OldNamespace.");
784 (void)Consumed;
785 const std::string NewNs = (NewNamespace + Postfix).str();
786
787 llvm::StringRef NestedName = Lexer::getSourceText(
788 CharSourceRange::getTokenRange(
789 Result.SourceManager->getSpellingLoc(Start),
790 Result.SourceManager->getSpellingLoc(End)),
791 *Result.SourceManager, Result.Context->getLangOpts());
792 std::string FromDeclName = FromDecl->getQualifiedNameAsString();
793 for (llvm::Regex &RE : AllowedSymbolRegexes)
794 if (RE.match(FromDeclName))
795 return;
796 std::string ReplaceName =
797 getShortestQualifiedNameInNamespace(FromDeclName, NewNs);
798 // Checks if there is any using namespace declarations that can shorten the
799 // qualified name.
800 for (const auto *UsingNamespace : UsingNamespaceDecls) {
801 if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx,
802 Start))
803 continue;
804 StringRef FromDeclNameRef = FromDeclName;
805 if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace()
806 ->getQualifiedNameAsString())) {
807 FromDeclNameRef = FromDeclNameRef.drop_front(2);
808 if (FromDeclNameRef.size() < ReplaceName.size())
809 ReplaceName = std::string(FromDeclNameRef);
810 }
811 }
812 // Checks if there is any namespace alias declarations that can shorten the
813 // qualified name.
814 for (const auto *NamespaceAlias : NamespaceAliasDecls) {
815 if (!isDeclVisibleAtLocation(*Result.SourceManager, NamespaceAlias, DeclCtx,
816 Start))
817 continue;
818 StringRef FromDeclNameRef = FromDeclName;
819 if (FromDeclNameRef.consume_front(
820 NamespaceAlias->getNamespace()->getQualifiedNameAsString() +
821 "::")) {
822 std::string AliasName = NamespaceAlias->getNameAsString();
823 std::string AliasQualifiedName =
824 NamespaceAlias->getQualifiedNameAsString();
825 // We only consider namespace aliases define in the global namespace or
826 // in namespaces that are directly visible from the reference, i.e.
827 // ancestor of the `OldNs`. Note that declarations in ancestor namespaces
828 // but not visible in the new namespace is filtered out by
829 // "IsVisibleInNewNs" matcher.
830 if (AliasQualifiedName != AliasName) {
831 // The alias is defined in some namespace.
832 assert(StringRef(AliasQualifiedName).endswith("::" + AliasName));
833 llvm::StringRef AliasNs =
834 StringRef(AliasQualifiedName).drop_back(AliasName.size() + 2);
835 if (!llvm::StringRef(OldNs).startswith(AliasNs))
836 continue;
837 }
838 std::string NameWithAliasNamespace =
839 (AliasName + "::" + FromDeclNameRef).str();
840 if (NameWithAliasNamespace.size() < ReplaceName.size())
841 ReplaceName = NameWithAliasNamespace;
842 }
843 }
844 // Checks if there is any using shadow declarations that can shorten the
845 // qualified name.
846 bool Matched = false;
847 for (const UsingDecl *Using : UsingDecls) {
848 if (Matched)
849 break;
850 if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) {
851 for (const auto *UsingShadow : Using->shadows()) {
852 const auto *TargetDecl = UsingShadow->getTargetDecl();
853 if (TargetDecl->getQualifiedNameAsString() ==
854 FromDecl->getQualifiedNameAsString()) {
855 ReplaceName = FromDecl->getNameAsString();
856 Matched = true;
857 break;
858 }
859 }
860 }
861 }
862 bool Conflict = conflictInNamespace(DeclCtx->getParentASTContext(),
863 ReplaceName, NewNamespace);
864 // If the new nested name in the new namespace is the same as it was in the
865 // old namespace, we don't create replacement unless there can be ambiguity.
866 if ((NestedName == ReplaceName && !Conflict) ||
867 (NestedName.startswith("::") && NestedName.drop_front(2) == ReplaceName))
868 return;
869 // If the reference need to be fully-qualified, add a leading "::" unless
870 // NewNamespace is the global namespace.
871 if (ReplaceName == FromDeclName && !NewNamespace.empty() && Conflict)
872 ReplaceName = "::" + ReplaceName;
873 addReplacementOrDie(Start, End, ReplaceName, *Result.SourceManager,
874 &FileToReplacements);
875 }
876
877 // Replace the [Start, End] of `Type` with the shortest qualified name when the
878 // `Type` is in `NewNamespace`.
fixTypeLoc(const ast_matchers::MatchFinder::MatchResult & Result,SourceLocation Start,SourceLocation End,TypeLoc Type)879 void ChangeNamespaceTool::fixTypeLoc(
880 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start,
881 SourceLocation End, TypeLoc Type) {
882 // FIXME: do not rename template parameter.
883 if (Start.isInvalid() || End.isInvalid())
884 return;
885 // Types of CXXCtorInitializers do not need to be fixed.
886 if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type))
887 return;
888 if (isTemplateParameter(Type))
889 return;
890 // The declaration which this TypeLoc refers to.
891 const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");
892 // `hasDeclaration` gives underlying declaration, but if the type is
893 // a typedef type, we need to use the typedef type instead.
894 auto IsInMovedNs = [&](const NamedDecl *D) {
895 if (!llvm::StringRef(D->getQualifiedNameAsString())
896 .startswith(OldNamespace + "::"))
897 return false;
898 auto ExpansionLoc = Result.SourceManager->getExpansionLoc(D->getBeginLoc());
899 if (ExpansionLoc.isInvalid())
900 return false;
901 llvm::StringRef Filename = Result.SourceManager->getFilename(ExpansionLoc);
902 return FilePatternRE.match(Filename);
903 };
904 // Make `FromDecl` the immediate declaration that `Type` refers to, i.e. if
905 // `Type` is an alias type, we make `FromDecl` the type alias declaration.
906 // Also, don't fix the \p Type if it refers to a type alias decl in the moved
907 // namespace since the alias decl will be moved along with the type reference.
908 if (auto *Typedef = Type.getType()->getAs<TypedefType>()) {
909 FromDecl = Typedef->getDecl();
910 if (IsInMovedNs(FromDecl))
911 return;
912 } else if (auto *TemplateType =
913 Type.getType()->getAs<TemplateSpecializationType>()) {
914 if (TemplateType->isTypeAlias()) {
915 FromDecl = TemplateType->getTemplateName().getAsTemplateDecl();
916 if (IsInMovedNs(FromDecl))
917 return;
918 }
919 }
920 const auto *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc");
921 assert(DeclCtx && "Empty decl context.");
922 replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start,
923 End, FromDecl);
924 }
925
fixUsingShadowDecl(const ast_matchers::MatchFinder::MatchResult & Result,const UsingDecl * UsingDeclaration)926 void ChangeNamespaceTool::fixUsingShadowDecl(
927 const ast_matchers::MatchFinder::MatchResult &Result,
928 const UsingDecl *UsingDeclaration) {
929 SourceLocation Start = UsingDeclaration->getBeginLoc();
930 SourceLocation End = UsingDeclaration->getEndLoc();
931 if (Start.isInvalid() || End.isInvalid())
932 return;
933
934 assert(UsingDeclaration->shadow_size() > 0);
935 // FIXME: it might not be always accurate to use the first using-decl.
936 const NamedDecl *TargetDecl =
937 UsingDeclaration->shadow_begin()->getTargetDecl();
938 std::string TargetDeclName = TargetDecl->getQualifiedNameAsString();
939 // FIXME: check if target_decl_name is in moved ns, which doesn't make much
940 // sense. If this happens, we need to use name with the new namespace.
941 // Use fully qualified name in UsingDecl for now.
942 addReplacementOrDie(Start, End, "using ::" + TargetDeclName,
943 *Result.SourceManager, &FileToReplacements);
944 }
945
fixDeclRefExpr(const ast_matchers::MatchFinder::MatchResult & Result,const DeclContext * UseContext,const NamedDecl * From,const DeclRefExpr * Ref)946 void ChangeNamespaceTool::fixDeclRefExpr(
947 const ast_matchers::MatchFinder::MatchResult &Result,
948 const DeclContext *UseContext, const NamedDecl *From,
949 const DeclRefExpr *Ref) {
950 SourceRange RefRange = Ref->getSourceRange();
951 replaceQualifiedSymbolInDeclContext(Result, UseContext, RefRange.getBegin(),
952 RefRange.getEnd(), From);
953 }
954
onEndOfTranslationUnit()955 void ChangeNamespaceTool::onEndOfTranslationUnit() {
956 // Move namespace blocks and insert forward declaration to old namespace.
957 for (const auto &FileAndNsMoves : MoveNamespaces) {
958 auto &NsMoves = FileAndNsMoves.second;
959 if (NsMoves.empty())
960 continue;
961 const std::string &FilePath = FileAndNsMoves.first;
962 auto &Replaces = FileToReplacements[FilePath];
963 auto &SM = *NsMoves.begin()->SourceMgr;
964 llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID);
965 auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
966 if (!ChangedCode) {
967 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
968 continue;
969 }
970 // Replacements on the changed code for moving namespaces and inserting
971 // forward declarations to old namespaces.
972 tooling::Replacements NewReplacements;
973 // Cut the changed code from the old namespace and paste the code in the new
974 // namespace.
975 for (const auto &NsMove : NsMoves) {
976 // Calculate the range of the old namespace block in the changed
977 // code.
978 const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset);
979 const unsigned NewLength =
980 Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) -
981 NewOffset;
982 tooling::Replacement Deletion(FilePath, NewOffset, NewLength, "");
983 std::string MovedCode = ChangedCode->substr(NewOffset, NewLength);
984 std::string MovedCodeWrappedInNewNs =
985 wrapCodeInNamespace(DiffNewNamespace, MovedCode);
986 // Calculate the new offset at which the code will be inserted in the
987 // changed code.
988 unsigned NewInsertionOffset =
989 Replaces.getShiftedCodePosition(NsMove.InsertionOffset);
990 tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
991 MovedCodeWrappedInNewNs);
992 addOrMergeReplacement(Deletion, &NewReplacements);
993 addOrMergeReplacement(Insertion, &NewReplacements);
994 }
995 // After moving namespaces, insert forward declarations back to old
996 // namespaces.
997 const auto &FwdDeclInsertions = InsertFwdDecls[FilePath];
998 for (const auto &FwdDeclInsertion : FwdDeclInsertions) {
999 unsigned NewInsertionOffset =
1000 Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset);
1001 tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
1002 FwdDeclInsertion.ForwardDeclText);
1003 addOrMergeReplacement(Insertion, &NewReplacements);
1004 }
1005 // Add replacements referring to the changed code to existing replacements,
1006 // which refers to the original code.
1007 Replaces = Replaces.merge(NewReplacements);
1008 auto Style =
1009 format::getStyle(format::DefaultFormatStyle, FilePath, FallbackStyle);
1010 if (!Style) {
1011 llvm::errs() << llvm::toString(Style.takeError()) << "\n";
1012 continue;
1013 }
1014 // Clean up old namespaces if there is nothing in it after moving.
1015 auto CleanReplacements =
1016 format::cleanupAroundReplacements(Code, Replaces, *Style);
1017 if (!CleanReplacements) {
1018 llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
1019 continue;
1020 }
1021 FileToReplacements[FilePath] = *CleanReplacements;
1022 }
1023
1024 // Make sure we don't generate replacements for files that do not match
1025 // FilePattern.
1026 for (auto &Entry : FileToReplacements)
1027 if (!FilePatternRE.match(Entry.first))
1028 Entry.second.clear();
1029 }
1030
1031 } // namespace change_namespace
1032 } // namespace clang
1033