blob: af29c6d294743ed1e9cb9f93157778070d2b27a2 [file] [log] [blame]
Eric Liu495b2112016-09-19 17:40:32 +00001//===-- 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`.
Eric Liu6aa94162016-11-10 18:29:01 +000060// If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName`
61// is empty, nullptr is returned.
Eric Liu495b2112016-09-19 17:40:32 +000062// 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) {
Eric Liu6aa94162016-11-10 18:29:01 +000066 if (!InnerNs || PartialNsName.empty())
67 return nullptr;
Eric Liu495b2112016-09-19 17:40:32 +000068 const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs);
69 const auto *CurrentNs = InnerNs;
70 llvm::SmallVector<llvm::StringRef, 4> PartialNsNameSplitted;
Eric Liu6aa94162016-11-10 18:29:01 +000071 PartialNsName.split(PartialNsNameSplitted, "::", /*MaxSplit=*/-1,
72 /*KeepEmpty=*/false);
Eric Liu495b2112016-09-19 17:40:32 +000073 while (!PartialNsNameSplitted.empty()) {
74 // Get the inner-most namespace in CurrentContext.
75 while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext))
76 CurrentContext = CurrentContext->getParent();
77 if (!CurrentContext)
78 return nullptr;
79 CurrentNs = llvm::cast<NamespaceDecl>(CurrentContext);
80 if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString())
81 return nullptr;
82 PartialNsNameSplitted.pop_back();
83 CurrentContext = CurrentContext->getParent();
84 }
85 return CurrentNs;
86}
87
Eric Liu73f49fd2016-10-12 12:34:18 +000088static std::unique_ptr<Lexer>
89getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM,
90 const LangOptions &LangOpts) {
Eric Liu495b2112016-09-19 17:40:32 +000091 if (Loc.isMacroID() &&
92 !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))
Eric Liu73f49fd2016-10-12 12:34:18 +000093 return nullptr;
Eric Liu495b2112016-09-19 17:40:32 +000094 // Break down the source location.
95 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
96 // Try to load the file buffer.
97 bool InvalidTemp = false;
98 llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
99 if (InvalidTemp)
Eric Liu73f49fd2016-10-12 12:34:18 +0000100 return nullptr;
Eric Liu495b2112016-09-19 17:40:32 +0000101
102 const char *TokBegin = File.data() + LocInfo.second;
103 // Lex from the start of the given location.
Eric Liu73f49fd2016-10-12 12:34:18 +0000104 return llvm::make_unique<Lexer>(SM.getLocForStartOfFile(LocInfo.first),
105 LangOpts, File.begin(), TokBegin, File.end());
106}
Eric Liu495b2112016-09-19 17:40:32 +0000107
Eric Liu73f49fd2016-10-12 12:34:18 +0000108// FIXME: get rid of this helper function if this is supported in clang-refactor
109// library.
110static SourceLocation getStartOfNextLine(SourceLocation Loc,
111 const SourceManager &SM,
112 const LangOptions &LangOpts) {
113 std::unique_ptr<Lexer> Lex = getLexerStartingFromLoc(Loc, SM, LangOpts);
114 if (!Lex.get())
115 return SourceLocation();
Eric Liu495b2112016-09-19 17:40:32 +0000116 llvm::SmallVector<char, 16> Line;
117 // FIXME: this is a bit hacky to get ReadToEndOfLine work.
Eric Liu73f49fd2016-10-12 12:34:18 +0000118 Lex->setParsingPreprocessorDirective(true);
119 Lex->ReadToEndOfLine(&Line);
Haojian Wuef8a6dc2016-10-04 10:35:53 +0000120 auto End = Loc.getLocWithOffset(Line.size());
Eric Liu73f49fd2016-10-12 12:34:18 +0000121 return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End
122 ? End
123 : End.getLocWithOffset(1);
Eric Liu495b2112016-09-19 17:40:32 +0000124}
125
126// Returns `R` with new range that refers to code after `Replaces` being
127// applied.
128tooling::Replacement
129getReplacementInChangedCode(const tooling::Replacements &Replaces,
130 const tooling::Replacement &R) {
131 unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset());
132 unsigned NewEnd =
133 Replaces.getShiftedCodePosition(R.getOffset() + R.getLength());
134 return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
135 R.getReplacementText());
136}
137
138// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by
139// applying all existing Replaces first if there is conflict.
140void addOrMergeReplacement(const tooling::Replacement &R,
141 tooling::Replacements *Replaces) {
142 auto Err = Replaces->add(R);
143 if (Err) {
144 llvm::consumeError(std::move(Err));
145 auto Replace = getReplacementInChangedCode(*Replaces, R);
146 *Replaces = Replaces->merge(tooling::Replacements(Replace));
147 }
148}
149
150tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End,
151 llvm::StringRef ReplacementText,
152 const SourceManager &SM) {
153 if (!Start.isValid() || !End.isValid()) {
154 llvm::errs() << "start or end location were invalid\n";
155 return tooling::Replacement();
156 }
157 if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) {
158 llvm::errs()
159 << "start or end location were in different macro expansions\n";
160 return tooling::Replacement();
161 }
162 Start = SM.getSpellingLoc(Start);
163 End = SM.getSpellingLoc(End);
164 if (SM.getFileID(Start) != SM.getFileID(End)) {
165 llvm::errs() << "start or end location were in different files\n";
166 return tooling::Replacement();
167 }
168 return tooling::Replacement(
169 SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
170 SM.getSpellingLoc(End)),
171 ReplacementText);
172}
173
174tooling::Replacement createInsertion(SourceLocation Loc,
175 llvm::StringRef InsertText,
176 const SourceManager &SM) {
177 if (Loc.isInvalid()) {
178 llvm::errs() << "insert Location is invalid.\n";
179 return tooling::Replacement();
180 }
181 Loc = SM.getSpellingLoc(Loc);
182 return tooling::Replacement(SM, Loc, 0, InsertText);
183}
184
185// Returns the shortest qualified name for declaration `DeclName` in the
186// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName`
187// is "a::c::d", then "b::X" will be returned.
Eric Liu447164d2016-10-05 15:52:39 +0000188// \param DeclName A fully qualified name, "::a::b::X" or "a::b::X".
189// \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace
190// will have empty name.
Eric Liu495b2112016-09-19 17:40:32 +0000191std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,
192 llvm::StringRef NsName) {
Eric Liu447164d2016-10-05 15:52:39 +0000193 DeclName = DeclName.ltrim(':');
194 NsName = NsName.ltrim(':');
Eric Liu447164d2016-10-05 15:52:39 +0000195 if (DeclName.find(':') == llvm::StringRef::npos)
Eric Liub9bf1b52016-11-08 22:44:17 +0000196 return DeclName;
Eric Liu447164d2016-10-05 15:52:39 +0000197
198 while (!DeclName.consume_front((NsName + "::").str())) {
Eric Liu495b2112016-09-19 17:40:32 +0000199 const auto Pos = NsName.find_last_of(':');
200 if (Pos == llvm::StringRef::npos)
201 return DeclName;
Eric Liu447164d2016-10-05 15:52:39 +0000202 assert(Pos > 0);
203 NsName = NsName.substr(0, Pos - 1);
Eric Liu495b2112016-09-19 17:40:32 +0000204 }
205 return DeclName;
206}
207
208std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) {
209 if (Code.back() != '\n')
210 Code += "\n";
211 llvm::SmallVector<StringRef, 4> NsSplitted;
212 NestedNs.split(NsSplitted, "::");
213 while (!NsSplitted.empty()) {
214 // FIXME: consider code style for comments.
215 Code = ("namespace " + NsSplitted.back() + " {\n" + Code +
216 "} // namespace " + NsSplitted.back() + "\n")
217 .str();
218 NsSplitted.pop_back();
219 }
220 return Code;
221}
222
Eric Liub9bf1b52016-11-08 22:44:17 +0000223// Returns true if \p D is a nested DeclContext in \p Context
224bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) {
225 while (D) {
226 if (D == Context)
227 return true;
228 D = D->getParent();
229 }
230 return false;
231}
232
233// Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx.
234bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D,
235 const DeclContext *DeclCtx, SourceLocation Loc) {
236 SourceLocation DeclLoc = SM.getSpellingLoc(D->getLocation());
237 Loc = SM.getSpellingLoc(Loc);
238 return SM.isBeforeInTranslationUnit(DeclLoc, Loc) &&
239 (SM.getFileID(DeclLoc) == SM.getFileID(Loc) &&
240 isNestedDeclContext(DeclCtx, D->getDeclContext()));
241}
242
Eric Liu495b2112016-09-19 17:40:32 +0000243} // anonymous namespace
244
245ChangeNamespaceTool::ChangeNamespaceTool(
246 llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
247 std::map<std::string, tooling::Replacements> *FileToReplacements,
248 llvm::StringRef FallbackStyle)
249 : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements),
250 OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')),
251 FilePattern(FilePattern) {
252 FileToReplacements->clear();
253 llvm::SmallVector<llvm::StringRef, 4> OldNsSplitted;
254 llvm::SmallVector<llvm::StringRef, 4> NewNsSplitted;
255 llvm::StringRef(OldNamespace).split(OldNsSplitted, "::");
256 llvm::StringRef(NewNamespace).split(NewNsSplitted, "::");
257 // Calculates `DiffOldNamespace` and `DiffNewNamespace`.
258 while (!OldNsSplitted.empty() && !NewNsSplitted.empty() &&
259 OldNsSplitted.front() == NewNsSplitted.front()) {
260 OldNsSplitted.erase(OldNsSplitted.begin());
261 NewNsSplitted.erase(NewNsSplitted.begin());
262 }
263 DiffOldNamespace = joinNamespaces(OldNsSplitted);
264 DiffNewNamespace = joinNamespaces(NewNsSplitted);
265}
266
Eric Liu495b2112016-09-19 17:40:32 +0000267void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
Eric Liu495b2112016-09-19 17:40:32 +0000268 std::string FullOldNs = "::" + OldNamespace;
Eric Liub9bf1b52016-11-08 22:44:17 +0000269 // Prefix is the outer-most namespace in DiffOldNamespace. For example, if the
270 // OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will
271 // be "a::b". Declarations in this namespace will not be visible in the new
272 // namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-".
273 llvm::SmallVector<llvm::StringRef, 4> DiffOldNsSplitted;
274 llvm::StringRef(DiffOldNamespace).split(DiffOldNsSplitted, "::");
275 std::string Prefix = "-";
276 if (!DiffOldNsSplitted.empty())
277 Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) +
278 DiffOldNsSplitted.front())
279 .str();
280 auto IsInMovedNs =
281 allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")),
282 isExpansionInFileMatching(FilePattern));
283 auto IsVisibleInNewNs = anyOf(
284 IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix)))));
285 // Match using declarations.
286 Finder->addMatcher(
287 usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs)
288 .bind("using"),
289 this);
290 // Match using namespace declarations.
291 Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern),
292 IsVisibleInNewNs)
293 .bind("using_namespace"),
294 this);
295
296 // Match old namespace blocks.
Eric Liu495b2112016-09-19 17:40:32 +0000297 Finder->addMatcher(
298 namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern))
299 .bind("old_ns"),
300 this);
301
Eric Liu495b2112016-09-19 17:40:32 +0000302 // Match forward-declarations in the old namespace.
303 Finder->addMatcher(
304 cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())), IsInMovedNs)
305 .bind("fwd_decl"),
306 this);
307
308 // Match references to types that are not defined in the old namespace.
309 // Forward-declarations in the old namespace are also matched since they will
310 // be moved back to the old namespace.
311 auto DeclMatcher = namedDecl(
312 hasAncestor(namespaceDecl()),
313 unless(anyOf(
Eric Liu912d0392016-09-27 12:54:48 +0000314 isImplicit(), hasAncestor(namespaceDecl(isAnonymous())),
Eric Liu495b2112016-09-19 17:40:32 +0000315 hasAncestor(cxxRecordDecl()),
316 allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition())))))));
Eric Liu912d0392016-09-27 12:54:48 +0000317
Eric Liu495b2112016-09-19 17:40:32 +0000318 // Match TypeLocs on the declaration. Carefully match only the outermost
Eric Liu8393cb02016-10-31 08:28:29 +0000319 // TypeLoc and template specialization arguments (which are not outermost)
320 // that are directly linked to types matching `DeclMatcher`. Nested name
321 // specifier locs are handled separately below.
Eric Liu495b2112016-09-19 17:40:32 +0000322 Finder->addMatcher(
323 typeLoc(IsInMovedNs,
324 loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))),
Eric Liu8393cb02016-10-31 08:28:29 +0000325 unless(anyOf(hasParent(typeLoc(loc(qualType(
326 allOf(hasDeclaration(DeclMatcher),
327 unless(templateSpecializationType())))))),
Eric Liu495b2112016-09-19 17:40:32 +0000328 hasParent(nestedNameSpecifierLoc()))),
329 hasAncestor(decl().bind("dc")))
330 .bind("type"),
331 this);
Eric Liu912d0392016-09-27 12:54:48 +0000332
Eric Liu68765a82016-09-21 15:06:12 +0000333 // Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to
334 // special case it.
Eric Liub9bf1b52016-11-08 22:44:17 +0000335 Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()))
336 .bind("using_with_shadow"),
337 this);
Eric Liu912d0392016-09-27 12:54:48 +0000338
Eric Liu68765a82016-09-21 15:06:12 +0000339 // Handle types in nested name specifier.
340 Finder->addMatcher(nestedNameSpecifierLoc(
341 hasAncestor(decl(IsInMovedNs).bind("dc")),
342 loc(nestedNameSpecifier(specifiesType(
343 hasDeclaration(DeclMatcher.bind("from_decl"))))))
344 .bind("nested_specifier_loc"),
345 this);
Eric Liu12068d82016-09-22 11:54:00 +0000346
347 // Handle function.
Eric Liu912d0392016-09-27 12:54:48 +0000348 // Only handle functions that are defined in a namespace excluding member
349 // function, static methods (qualified by nested specifier), and functions
350 // defined in the global namespace.
Eric Liu12068d82016-09-22 11:54:00 +0000351 // Note that the matcher does not exclude calls to out-of-line static method
352 // definitions, so we need to exclude them in the callback handler.
Eric Liu912d0392016-09-27 12:54:48 +0000353 auto FuncMatcher =
354 functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs,
355 hasAncestor(namespaceDecl(isAnonymous())),
356 hasAncestor(cxxRecordDecl()))),
357 hasParent(namespaceDecl()));
Eric Liu12068d82016-09-22 11:54:00 +0000358 Finder->addMatcher(
359 decl(forEachDescendant(callExpr(callee(FuncMatcher)).bind("call")),
Eric Liu912d0392016-09-27 12:54:48 +0000360 IsInMovedNs, unless(isImplicit()))
Eric Liu12068d82016-09-22 11:54:00 +0000361 .bind("dc"),
362 this);
Eric Liu159f0132016-09-30 04:32:39 +0000363
364 auto GlobalVarMatcher = varDecl(
365 hasGlobalStorage(), hasParent(namespaceDecl()),
366 unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous())))));
367 Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")),
368 to(GlobalVarMatcher.bind("var_decl")))
369 .bind("var_ref"),
370 this);
Eric Liu495b2112016-09-19 17:40:32 +0000371}
372
373void ChangeNamespaceTool::run(
374 const ast_matchers::MatchFinder::MatchResult &Result) {
Eric Liub9bf1b52016-11-08 22:44:17 +0000375 if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {
376 UsingDecls.insert(Using);
377 } else if (const auto *UsingNamespace =
378 Result.Nodes.getNodeAs<UsingDirectiveDecl>(
379 "using_namespace")) {
380 UsingNamespaceDecls.insert(UsingNamespace);
381 } else if (const auto *NsDecl =
382 Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {
Eric Liu495b2112016-09-19 17:40:32 +0000383 moveOldNamespace(Result, NsDecl);
384 } else if (const auto *FwdDecl =
385 Result.Nodes.getNodeAs<CXXRecordDecl>("fwd_decl")) {
386 moveClassForwardDeclaration(Result, FwdDecl);
Eric Liub9bf1b52016-11-08 22:44:17 +0000387 } else if (const auto *UsingWithShadow =
388 Result.Nodes.getNodeAs<UsingDecl>("using_with_shadow")) {
389 fixUsingShadowDecl(Result, UsingWithShadow);
Eric Liu68765a82016-09-21 15:06:12 +0000390 } else if (const auto *Specifier =
391 Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
392 "nested_specifier_loc")) {
393 SourceLocation Start = Specifier->getBeginLoc();
394 SourceLocation End = EndLocationForType(Specifier->getTypeLoc());
395 fixTypeLoc(Result, Start, End, Specifier->getTypeLoc());
Eric Liu12068d82016-09-22 11:54:00 +0000396 } else if (const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type")) {
Eric Liu495b2112016-09-19 17:40:32 +0000397 fixTypeLoc(Result, startLocationForType(*TLoc), EndLocationForType(*TLoc),
398 *TLoc);
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000399 } else if (const auto *VarRef =
400 Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) {
Eric Liu159f0132016-09-30 04:32:39 +0000401 const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl");
402 assert(Var);
403 if (Var->getCanonicalDecl()->isStaticDataMember())
404 return;
Eric Liu159f0132016-09-30 04:32:39 +0000405 const clang::Decl *Context = Result.Nodes.getNodeAs<clang::Decl>("dc");
406 assert(Context && "Empty decl context.");
407 clang::SourceRange VarRefRange = VarRef->getSourceRange();
Eric Liub9bf1b52016-11-08 22:44:17 +0000408 replaceQualifiedSymbolInDeclContext(
409 Result, Context->getDeclContext(), VarRefRange.getBegin(),
Eric Liu231c6552016-11-10 18:15:34 +0000410 VarRefRange.getEnd(), llvm::cast<NamedDecl>(Var));
Eric Liu12068d82016-09-22 11:54:00 +0000411 } else {
Eric Liu159f0132016-09-30 04:32:39 +0000412 const auto *Call = Result.Nodes.getNodeAs<clang::CallExpr>("call");
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000413 assert(Call != nullptr && "Expecting callback for CallExpr.");
414 const clang::FunctionDecl *Func = Call->getDirectCallee();
Eric Liu12068d82016-09-22 11:54:00 +0000415 assert(Func != nullptr);
416 // Ignore out-of-line static methods since they will be handled by nested
417 // name specifiers.
418 if (Func->getCanonicalDecl()->getStorageClass() ==
419 clang::StorageClass::SC_Static &&
420 Func->isOutOfLine())
421 return;
Eric Liu12068d82016-09-22 11:54:00 +0000422 const clang::Decl *Context = Result.Nodes.getNodeAs<clang::Decl>("dc");
423 assert(Context && "Empty decl context.");
424 clang::SourceRange CalleeRange = Call->getCallee()->getSourceRange();
Eric Liub9bf1b52016-11-08 22:44:17 +0000425 replaceQualifiedSymbolInDeclContext(
426 Result, Context->getDeclContext(), CalleeRange.getBegin(),
Eric Liu231c6552016-11-10 18:15:34 +0000427 CalleeRange.getEnd(), llvm::cast<NamedDecl>(Func));
Eric Liu495b2112016-09-19 17:40:32 +0000428 }
429}
430
Eric Liu73f49fd2016-10-12 12:34:18 +0000431static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl,
432 const SourceManager &SM,
433 const LangOptions &LangOpts) {
434 std::unique_ptr<Lexer> Lex =
435 getLexerStartingFromLoc(NsDecl->getLocStart(), SM, LangOpts);
436 assert(Lex.get() &&
437 "Failed to create lexer from the beginning of namespace.");
438 if (!Lex.get())
439 return SourceLocation();
440 Token Tok;
441 while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) {
442 }
443 return Tok.isNot(tok::TokenKind::l_brace)
444 ? SourceLocation()
445 : Tok.getEndLoc().getLocWithOffset(1);
446}
447
Eric Liu495b2112016-09-19 17:40:32 +0000448// Stores information about a moved namespace in `MoveNamespaces` and leaves
449// the actual movement to `onEndOfTranslationUnit()`.
450void ChangeNamespaceTool::moveOldNamespace(
451 const ast_matchers::MatchFinder::MatchResult &Result,
452 const NamespaceDecl *NsDecl) {
453 // If the namespace is empty, do nothing.
454 if (Decl::castToDeclContext(NsDecl)->decls_empty())
455 return;
456
457 // Get the range of the code in the old namespace.
Eric Liu73f49fd2016-10-12 12:34:18 +0000458 SourceLocation Start = getLocAfterNamespaceLBrace(
459 NsDecl, *Result.SourceManager, Result.Context->getLangOpts());
460 assert(Start.isValid() && "Can't find l_brace for namespace.");
Eric Liu495b2112016-09-19 17:40:32 +0000461 SourceLocation End = NsDecl->getRBraceLoc().getLocWithOffset(-1);
462 // Create a replacement that deletes the code in the old namespace merely for
463 // retrieving offset and length from it.
464 const auto R = createReplacement(Start, End, "", *Result.SourceManager);
465 MoveNamespace MoveNs;
466 MoveNs.Offset = R.getOffset();
467 MoveNs.Length = R.getLength();
468
469 // Insert the new namespace after `DiffOldNamespace`. For example, if
470 // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then
471 // "x::y" will be inserted inside the existing namespace "a" and after "a::b".
472 // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b"
473 // in the above example.
Eric Liu6aa94162016-11-10 18:29:01 +0000474 // If there is no outer namespace (i.e. DiffOldNamespace is empty), the new
475 // namespace will be a nested namespace in the old namespace.
Eric Liu495b2112016-09-19 17:40:32 +0000476 const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace);
Eric Liu6aa94162016-11-10 18:29:01 +0000477 SourceLocation InsertionLoc = Start;
478 if (OuterNs) {
479 SourceLocation LocAfterNs =
480 getStartOfNextLine(OuterNs->getRBraceLoc(), *Result.SourceManager,
481 Result.Context->getLangOpts());
482 assert(LocAfterNs.isValid() &&
483 "Failed to get location after DiffOldNamespace");
484 InsertionLoc = LocAfterNs;
485 }
Eric Liu495b2112016-09-19 17:40:32 +0000486 MoveNs.InsertionOffset = Result.SourceManager->getFileOffset(
Eric Liu6aa94162016-11-10 18:29:01 +0000487 Result.SourceManager->getSpellingLoc(InsertionLoc));
Eric Liucc83c662016-09-19 17:58:59 +0000488 MoveNs.FID = Result.SourceManager->getFileID(Start);
489 MoveNs.SourceMgr = Result.SourceManager;
Eric Liu495b2112016-09-19 17:40:32 +0000490 MoveNamespaces[R.getFilePath()].push_back(MoveNs);
491}
492
493// Removes a class forward declaration from the code in the moved namespace and
494// creates an `InsertForwardDeclaration` to insert the forward declaration back
495// into the old namespace after moving code from the old namespace to the new
496// namespace.
497// For example, changing "a" to "x":
498// Old code:
499// namespace a {
500// class FWD;
501// class A { FWD *fwd; }
502// } // a
503// New code:
504// namespace a {
505// class FWD;
506// } // a
507// namespace x {
508// class A { a::FWD *fwd; }
509// } // x
510void ChangeNamespaceTool::moveClassForwardDeclaration(
511 const ast_matchers::MatchFinder::MatchResult &Result,
512 const CXXRecordDecl *FwdDecl) {
513 SourceLocation Start = FwdDecl->getLocStart();
514 SourceLocation End = FwdDecl->getLocEnd();
515 SourceLocation AfterSemi = Lexer::findLocationAfterToken(
516 End, tok::semi, *Result.SourceManager, Result.Context->getLangOpts(),
517 /*SkipTrailingWhitespaceAndNewLine=*/true);
518 if (AfterSemi.isValid())
519 End = AfterSemi.getLocWithOffset(-1);
520 // Delete the forward declaration from the code to be moved.
521 const auto Deletion =
522 createReplacement(Start, End, "", *Result.SourceManager);
523 addOrMergeReplacement(Deletion, &FileToReplacements[Deletion.getFilePath()]);
524 llvm::StringRef Code = Lexer::getSourceText(
525 CharSourceRange::getTokenRange(
526 Result.SourceManager->getSpellingLoc(Start),
527 Result.SourceManager->getSpellingLoc(End)),
528 *Result.SourceManager, Result.Context->getLangOpts());
529 // Insert the forward declaration back into the old namespace after moving the
530 // code from old namespace to new namespace.
531 // Insertion information is stored in `InsertFwdDecls` and actual
532 // insertion will be performed in `onEndOfTranslationUnit`.
533 // Get the (old) namespace that contains the forward declaration.
534 const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");
535 // The namespace contains the forward declaration, so it must not be empty.
536 assert(!NsDecl->decls_empty());
537 const auto Insertion = createInsertion(NsDecl->decls_begin()->getLocStart(),
538 Code, *Result.SourceManager);
539 InsertForwardDeclaration InsertFwd;
540 InsertFwd.InsertionOffset = Insertion.getOffset();
541 InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();
542 InsertFwdDecls[Insertion.getFilePath()].push_back(InsertFwd);
543}
544
Eric Liub9bf1b52016-11-08 22:44:17 +0000545// Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p
546// FromDecl with the shortest qualified name possible when the reference is in
547// `NewNamespace`.
Eric Liu495b2112016-09-19 17:40:32 +0000548void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
Eric Liub9bf1b52016-11-08 22:44:17 +0000549 const ast_matchers::MatchFinder::MatchResult &Result,
550 const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End,
551 const NamedDecl *FromDecl) {
552 const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext();
Eric Liu231c6552016-11-10 18:15:34 +0000553 const auto *NsDecl = llvm::cast<NamespaceDecl>(NsDeclContext);
Eric Liu495b2112016-09-19 17:40:32 +0000554 // Calculate the name of the `NsDecl` after it is moved to new namespace.
555 std::string OldNs = NsDecl->getQualifiedNameAsString();
556 llvm::StringRef Postfix = OldNs;
557 bool Consumed = Postfix.consume_front(OldNamespace);
558 assert(Consumed && "Expect OldNS to start with OldNamespace.");
559 (void)Consumed;
560 const std::string NewNs = (NewNamespace + Postfix).str();
561
562 llvm::StringRef NestedName = Lexer::getSourceText(
563 CharSourceRange::getTokenRange(
564 Result.SourceManager->getSpellingLoc(Start),
565 Result.SourceManager->getSpellingLoc(End)),
566 *Result.SourceManager, Result.Context->getLangOpts());
567 // If the symbol is already fully qualified, no change needs to be make.
568 if (NestedName.startswith("::"))
569 return;
Eric Liub9bf1b52016-11-08 22:44:17 +0000570 std::string FromDeclName = FromDecl->getQualifiedNameAsString();
Eric Liu495b2112016-09-19 17:40:32 +0000571 std::string ReplaceName =
Eric Liub9bf1b52016-11-08 22:44:17 +0000572 getShortestQualifiedNameInNamespace(FromDeclName, NewNs);
573 // Checks if there is any using namespace declarations that can shorten the
574 // qualified name.
575 for (const auto *UsingNamespace : UsingNamespaceDecls) {
576 if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx,
577 Start))
578 continue;
579 StringRef FromDeclNameRef = FromDeclName;
580 if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace()
581 ->getQualifiedNameAsString())) {
582 FromDeclNameRef = FromDeclNameRef.drop_front(2);
583 if (FromDeclNameRef.size() < ReplaceName.size())
584 ReplaceName = FromDeclNameRef;
585 }
586 }
587 // Checks if there is any using shadow declarations that can shorten the
588 // qualified name.
589 bool Matched = false;
590 for (const UsingDecl *Using : UsingDecls) {
591 if (Matched)
592 break;
593 if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) {
594 for (const auto *UsingShadow : Using->shadows()) {
595 const auto *TargetDecl = UsingShadow->getTargetDecl();
596 if (TargetDecl == FromDecl) {
597 ReplaceName = FromDecl->getNameAsString();
598 Matched = true;
599 break;
600 }
601 }
602 }
603 }
Eric Liu495b2112016-09-19 17:40:32 +0000604 // If the new nested name in the new namespace is the same as it was in the
605 // old namespace, we don't create replacement.
606 if (NestedName == ReplaceName)
607 return;
608 auto R = createReplacement(Start, End, ReplaceName, *Result.SourceManager);
609 addOrMergeReplacement(R, &FileToReplacements[R.getFilePath()]);
610}
611
612// Replace the [Start, End] of `Type` with the shortest qualified name when the
613// `Type` is in `NewNamespace`.
614void ChangeNamespaceTool::fixTypeLoc(
615 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start,
616 SourceLocation End, TypeLoc Type) {
617 // FIXME: do not rename template parameter.
618 if (Start.isInvalid() || End.isInvalid())
619 return;
620 // The declaration which this TypeLoc refers to.
621 const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");
622 // `hasDeclaration` gives underlying declaration, but if the type is
623 // a typedef type, we need to use the typedef type instead.
Eric Liu32158862016-11-14 19:37:55 +0000624 if (auto *Typedef = Type.getType()->getAs<TypedefType>()) {
Eric Liu495b2112016-09-19 17:40:32 +0000625 FromDecl = Typedef->getDecl();
Eric Liu32158862016-11-14 19:37:55 +0000626 auto IsInMovedNs = [&](const NamedDecl *D) {
627 if (!llvm::StringRef(D->getQualifiedNameAsString())
628 .startswith(OldNamespace + "::"))
629 return false;
630 auto ExpansionLoc =
631 Result.SourceManager->getExpansionLoc(D->getLocStart());
632 if (ExpansionLoc.isInvalid())
633 return false;
634 llvm::StringRef Filename =
635 Result.SourceManager->getFilename(ExpansionLoc);
636 llvm::Regex RE(FilePattern);
637 return RE.match(Filename);
638 };
639 // Don't fix the \p Type if it refers to a type alias decl in the moved
640 // namespace since the alias decl will be moved along with the type
641 // reference.
642 if (IsInMovedNs(FromDecl))
643 return;
644 }
Eric Liu495b2112016-09-19 17:40:32 +0000645
646 const Decl *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc");
647 assert(DeclCtx && "Empty decl context.");
Eric Liub9bf1b52016-11-08 22:44:17 +0000648 replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start,
649 End, FromDecl);
Eric Liu495b2112016-09-19 17:40:32 +0000650}
651
Eric Liu68765a82016-09-21 15:06:12 +0000652void ChangeNamespaceTool::fixUsingShadowDecl(
653 const ast_matchers::MatchFinder::MatchResult &Result,
654 const UsingDecl *UsingDeclaration) {
655 SourceLocation Start = UsingDeclaration->getLocStart();
656 SourceLocation End = UsingDeclaration->getLocEnd();
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000657 if (Start.isInvalid() || End.isInvalid())
658 return;
Eric Liu68765a82016-09-21 15:06:12 +0000659
660 assert(UsingDeclaration->shadow_size() > 0);
661 // FIXME: it might not be always accurate to use the first using-decl.
662 const NamedDecl *TargetDecl =
663 UsingDeclaration->shadow_begin()->getTargetDecl();
664 std::string TargetDeclName = TargetDecl->getQualifiedNameAsString();
665 // FIXME: check if target_decl_name is in moved ns, which doesn't make much
666 // sense. If this happens, we need to use name with the new namespace.
667 // Use fully qualified name in UsingDecl for now.
668 auto R = createReplacement(Start, End, "using ::" + TargetDeclName,
669 *Result.SourceManager);
670 addOrMergeReplacement(R, &FileToReplacements[R.getFilePath()]);
671}
672
Eric Liu495b2112016-09-19 17:40:32 +0000673void ChangeNamespaceTool::onEndOfTranslationUnit() {
674 // Move namespace blocks and insert forward declaration to old namespace.
675 for (const auto &FileAndNsMoves : MoveNamespaces) {
676 auto &NsMoves = FileAndNsMoves.second;
677 if (NsMoves.empty())
678 continue;
679 const std::string &FilePath = FileAndNsMoves.first;
680 auto &Replaces = FileToReplacements[FilePath];
Eric Liucc83c662016-09-19 17:58:59 +0000681 auto &SM = *NsMoves.begin()->SourceMgr;
682 llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID);
Eric Liu495b2112016-09-19 17:40:32 +0000683 auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
684 if (!ChangedCode) {
685 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
686 continue;
687 }
688 // Replacements on the changed code for moving namespaces and inserting
689 // forward declarations to old namespaces.
690 tooling::Replacements NewReplacements;
691 // Cut the changed code from the old namespace and paste the code in the new
692 // namespace.
693 for (const auto &NsMove : NsMoves) {
694 // Calculate the range of the old namespace block in the changed
695 // code.
696 const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset);
697 const unsigned NewLength =
698 Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) -
699 NewOffset;
700 tooling::Replacement Deletion(FilePath, NewOffset, NewLength, "");
701 std::string MovedCode = ChangedCode->substr(NewOffset, NewLength);
702 std::string MovedCodeWrappedInNewNs =
703 wrapCodeInNamespace(DiffNewNamespace, MovedCode);
704 // Calculate the new offset at which the code will be inserted in the
705 // changed code.
706 unsigned NewInsertionOffset =
707 Replaces.getShiftedCodePosition(NsMove.InsertionOffset);
708 tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
709 MovedCodeWrappedInNewNs);
710 addOrMergeReplacement(Deletion, &NewReplacements);
711 addOrMergeReplacement(Insertion, &NewReplacements);
712 }
713 // After moving namespaces, insert forward declarations back to old
714 // namespaces.
715 const auto &FwdDeclInsertions = InsertFwdDecls[FilePath];
716 for (const auto &FwdDeclInsertion : FwdDeclInsertions) {
717 unsigned NewInsertionOffset =
718 Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset);
719 tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
720 FwdDeclInsertion.ForwardDeclText);
721 addOrMergeReplacement(Insertion, &NewReplacements);
722 }
723 // Add replacements referring to the changed code to existing replacements,
724 // which refers to the original code.
725 Replaces = Replaces.merge(NewReplacements);
726 format::FormatStyle Style =
727 format::getStyle("file", FilePath, FallbackStyle);
728 // Clean up old namespaces if there is nothing in it after moving.
729 auto CleanReplacements =
730 format::cleanupAroundReplacements(Code, Replaces, Style);
731 if (!CleanReplacements) {
732 llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
733 continue;
734 }
735 FileToReplacements[FilePath] = *CleanReplacements;
736 }
737}
738
739} // namespace change_namespace
740} // namespace clang