blob: 680dfe58fffc8a132903a6230b147bef3b2b43c4 [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`.
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
Eric Liu73f49fd2016-10-12 12:34:18 +000085static std::unique_ptr<Lexer>
86getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM,
87 const LangOptions &LangOpts) {
Eric Liu495b2112016-09-19 17:40:32 +000088 if (Loc.isMacroID() &&
89 !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))
Eric Liu73f49fd2016-10-12 12:34:18 +000090 return nullptr;
Eric Liu495b2112016-09-19 17:40:32 +000091 // Break down the source location.
92 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
93 // Try to load the file buffer.
94 bool InvalidTemp = false;
95 llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
96 if (InvalidTemp)
Eric Liu73f49fd2016-10-12 12:34:18 +000097 return nullptr;
Eric Liu495b2112016-09-19 17:40:32 +000098
99 const char *TokBegin = File.data() + LocInfo.second;
100 // Lex from the start of the given location.
Eric Liu73f49fd2016-10-12 12:34:18 +0000101 return llvm::make_unique<Lexer>(SM.getLocForStartOfFile(LocInfo.first),
102 LangOpts, File.begin(), TokBegin, File.end());
103}
Eric Liu495b2112016-09-19 17:40:32 +0000104
Eric Liu73f49fd2016-10-12 12:34:18 +0000105// FIXME: get rid of this helper function if this is supported in clang-refactor
106// library.
107static SourceLocation getStartOfNextLine(SourceLocation Loc,
108 const SourceManager &SM,
109 const LangOptions &LangOpts) {
110 std::unique_ptr<Lexer> Lex = getLexerStartingFromLoc(Loc, SM, LangOpts);
111 if (!Lex.get())
112 return SourceLocation();
Eric Liu495b2112016-09-19 17:40:32 +0000113 llvm::SmallVector<char, 16> Line;
114 // FIXME: this is a bit hacky to get ReadToEndOfLine work.
Eric Liu73f49fd2016-10-12 12:34:18 +0000115 Lex->setParsingPreprocessorDirective(true);
116 Lex->ReadToEndOfLine(&Line);
Haojian Wuef8a6dc2016-10-04 10:35:53 +0000117 auto End = Loc.getLocWithOffset(Line.size());
Eric Liu73f49fd2016-10-12 12:34:18 +0000118 return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End
119 ? End
120 : End.getLocWithOffset(1);
Eric Liu495b2112016-09-19 17:40:32 +0000121}
122
123// Returns `R` with new range that refers to code after `Replaces` being
124// applied.
125tooling::Replacement
126getReplacementInChangedCode(const tooling::Replacements &Replaces,
127 const tooling::Replacement &R) {
128 unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset());
129 unsigned NewEnd =
130 Replaces.getShiftedCodePosition(R.getOffset() + R.getLength());
131 return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
132 R.getReplacementText());
133}
134
135// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by
136// applying all existing Replaces first if there is conflict.
137void addOrMergeReplacement(const tooling::Replacement &R,
138 tooling::Replacements *Replaces) {
139 auto Err = Replaces->add(R);
140 if (Err) {
141 llvm::consumeError(std::move(Err));
142 auto Replace = getReplacementInChangedCode(*Replaces, R);
143 *Replaces = Replaces->merge(tooling::Replacements(Replace));
144 }
145}
146
147tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End,
148 llvm::StringRef ReplacementText,
149 const SourceManager &SM) {
150 if (!Start.isValid() || !End.isValid()) {
151 llvm::errs() << "start or end location were invalid\n";
152 return tooling::Replacement();
153 }
154 if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) {
155 llvm::errs()
156 << "start or end location were in different macro expansions\n";
157 return tooling::Replacement();
158 }
159 Start = SM.getSpellingLoc(Start);
160 End = SM.getSpellingLoc(End);
161 if (SM.getFileID(Start) != SM.getFileID(End)) {
162 llvm::errs() << "start or end location were in different files\n";
163 return tooling::Replacement();
164 }
165 return tooling::Replacement(
166 SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
167 SM.getSpellingLoc(End)),
168 ReplacementText);
169}
170
171tooling::Replacement createInsertion(SourceLocation Loc,
172 llvm::StringRef InsertText,
173 const SourceManager &SM) {
174 if (Loc.isInvalid()) {
175 llvm::errs() << "insert Location is invalid.\n";
176 return tooling::Replacement();
177 }
178 Loc = SM.getSpellingLoc(Loc);
179 return tooling::Replacement(SM, Loc, 0, InsertText);
180}
181
182// Returns the shortest qualified name for declaration `DeclName` in the
183// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName`
184// is "a::c::d", then "b::X" will be returned.
Eric Liu447164d2016-10-05 15:52:39 +0000185// \param DeclName A fully qualified name, "::a::b::X" or "a::b::X".
186// \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace
187// will have empty name.
Eric Liu495b2112016-09-19 17:40:32 +0000188std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,
189 llvm::StringRef NsName) {
Eric Liu447164d2016-10-05 15:52:39 +0000190 DeclName = DeclName.ltrim(':');
191 NsName = NsName.ltrim(':');
Eric Liu447164d2016-10-05 15:52:39 +0000192 if (DeclName.find(':') == llvm::StringRef::npos)
Eric Liub9bf1b52016-11-08 22:44:17 +0000193 return DeclName;
Eric Liu447164d2016-10-05 15:52:39 +0000194
195 while (!DeclName.consume_front((NsName + "::").str())) {
Eric Liu495b2112016-09-19 17:40:32 +0000196 const auto Pos = NsName.find_last_of(':');
197 if (Pos == llvm::StringRef::npos)
198 return DeclName;
Eric Liu447164d2016-10-05 15:52:39 +0000199 assert(Pos > 0);
200 NsName = NsName.substr(0, Pos - 1);
Eric Liu495b2112016-09-19 17:40:32 +0000201 }
202 return DeclName;
203}
204
205std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) {
206 if (Code.back() != '\n')
207 Code += "\n";
208 llvm::SmallVector<StringRef, 4> NsSplitted;
209 NestedNs.split(NsSplitted, "::");
210 while (!NsSplitted.empty()) {
211 // FIXME: consider code style for comments.
212 Code = ("namespace " + NsSplitted.back() + " {\n" + Code +
213 "} // namespace " + NsSplitted.back() + "\n")
214 .str();
215 NsSplitted.pop_back();
216 }
217 return Code;
218}
219
Eric Liub9bf1b52016-11-08 22:44:17 +0000220// Returns true if \p D is a nested DeclContext in \p Context
221bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) {
222 while (D) {
223 if (D == Context)
224 return true;
225 D = D->getParent();
226 }
227 return false;
228}
229
230// Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx.
231bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D,
232 const DeclContext *DeclCtx, SourceLocation Loc) {
233 SourceLocation DeclLoc = SM.getSpellingLoc(D->getLocation());
234 Loc = SM.getSpellingLoc(Loc);
235 return SM.isBeforeInTranslationUnit(DeclLoc, Loc) &&
236 (SM.getFileID(DeclLoc) == SM.getFileID(Loc) &&
237 isNestedDeclContext(DeclCtx, D->getDeclContext()));
238}
239
Eric Liu495b2112016-09-19 17:40:32 +0000240} // anonymous namespace
241
242ChangeNamespaceTool::ChangeNamespaceTool(
243 llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
244 std::map<std::string, tooling::Replacements> *FileToReplacements,
245 llvm::StringRef FallbackStyle)
246 : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements),
247 OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')),
248 FilePattern(FilePattern) {
249 FileToReplacements->clear();
250 llvm::SmallVector<llvm::StringRef, 4> OldNsSplitted;
251 llvm::SmallVector<llvm::StringRef, 4> NewNsSplitted;
252 llvm::StringRef(OldNamespace).split(OldNsSplitted, "::");
253 llvm::StringRef(NewNamespace).split(NewNsSplitted, "::");
254 // Calculates `DiffOldNamespace` and `DiffNewNamespace`.
255 while (!OldNsSplitted.empty() && !NewNsSplitted.empty() &&
256 OldNsSplitted.front() == NewNsSplitted.front()) {
257 OldNsSplitted.erase(OldNsSplitted.begin());
258 NewNsSplitted.erase(NewNsSplitted.begin());
259 }
260 DiffOldNamespace = joinNamespaces(OldNsSplitted);
261 DiffNewNamespace = joinNamespaces(NewNsSplitted);
262}
263
Eric Liu495b2112016-09-19 17:40:32 +0000264void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
Eric Liu495b2112016-09-19 17:40:32 +0000265 std::string FullOldNs = "::" + OldNamespace;
Eric Liub9bf1b52016-11-08 22:44:17 +0000266 // Prefix is the outer-most namespace in DiffOldNamespace. For example, if the
267 // OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will
268 // be "a::b". Declarations in this namespace will not be visible in the new
269 // namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-".
270 llvm::SmallVector<llvm::StringRef, 4> DiffOldNsSplitted;
271 llvm::StringRef(DiffOldNamespace).split(DiffOldNsSplitted, "::");
272 std::string Prefix = "-";
273 if (!DiffOldNsSplitted.empty())
274 Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) +
275 DiffOldNsSplitted.front())
276 .str();
277 auto IsInMovedNs =
278 allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")),
279 isExpansionInFileMatching(FilePattern));
280 auto IsVisibleInNewNs = anyOf(
281 IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix)))));
282 // Match using declarations.
283 Finder->addMatcher(
284 usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs)
285 .bind("using"),
286 this);
287 // Match using namespace declarations.
288 Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern),
289 IsVisibleInNewNs)
290 .bind("using_namespace"),
291 this);
292
293 // Match old namespace blocks.
Eric Liu495b2112016-09-19 17:40:32 +0000294 Finder->addMatcher(
295 namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern))
296 .bind("old_ns"),
297 this);
298
Eric Liu495b2112016-09-19 17:40:32 +0000299 // Match forward-declarations in the old namespace.
300 Finder->addMatcher(
301 cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())), IsInMovedNs)
302 .bind("fwd_decl"),
303 this);
304
305 // Match references to types that are not defined in the old namespace.
306 // Forward-declarations in the old namespace are also matched since they will
307 // be moved back to the old namespace.
308 auto DeclMatcher = namedDecl(
309 hasAncestor(namespaceDecl()),
310 unless(anyOf(
Eric Liu912d0392016-09-27 12:54:48 +0000311 isImplicit(), hasAncestor(namespaceDecl(isAnonymous())),
Eric Liu495b2112016-09-19 17:40:32 +0000312 hasAncestor(cxxRecordDecl()),
313 allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition())))))));
Eric Liu912d0392016-09-27 12:54:48 +0000314
Eric Liu495b2112016-09-19 17:40:32 +0000315 // Match TypeLocs on the declaration. Carefully match only the outermost
Eric Liu8393cb02016-10-31 08:28:29 +0000316 // TypeLoc and template specialization arguments (which are not outermost)
317 // that are directly linked to types matching `DeclMatcher`. Nested name
318 // specifier locs are handled separately below.
Eric Liu495b2112016-09-19 17:40:32 +0000319 Finder->addMatcher(
320 typeLoc(IsInMovedNs,
321 loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))),
Eric Liu8393cb02016-10-31 08:28:29 +0000322 unless(anyOf(hasParent(typeLoc(loc(qualType(
323 allOf(hasDeclaration(DeclMatcher),
324 unless(templateSpecializationType())))))),
Eric Liu495b2112016-09-19 17:40:32 +0000325 hasParent(nestedNameSpecifierLoc()))),
326 hasAncestor(decl().bind("dc")))
327 .bind("type"),
328 this);
Eric Liu912d0392016-09-27 12:54:48 +0000329
Eric Liu68765a82016-09-21 15:06:12 +0000330 // Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to
331 // special case it.
Eric Liub9bf1b52016-11-08 22:44:17 +0000332 Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()))
333 .bind("using_with_shadow"),
334 this);
Eric Liu912d0392016-09-27 12:54:48 +0000335
Eric Liu68765a82016-09-21 15:06:12 +0000336 // Handle types in nested name specifier.
337 Finder->addMatcher(nestedNameSpecifierLoc(
338 hasAncestor(decl(IsInMovedNs).bind("dc")),
339 loc(nestedNameSpecifier(specifiesType(
340 hasDeclaration(DeclMatcher.bind("from_decl"))))))
341 .bind("nested_specifier_loc"),
342 this);
Eric Liu12068d82016-09-22 11:54:00 +0000343
344 // Handle function.
Eric Liu912d0392016-09-27 12:54:48 +0000345 // Only handle functions that are defined in a namespace excluding member
346 // function, static methods (qualified by nested specifier), and functions
347 // defined in the global namespace.
Eric Liu12068d82016-09-22 11:54:00 +0000348 // Note that the matcher does not exclude calls to out-of-line static method
349 // definitions, so we need to exclude them in the callback handler.
Eric Liu912d0392016-09-27 12:54:48 +0000350 auto FuncMatcher =
351 functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs,
352 hasAncestor(namespaceDecl(isAnonymous())),
353 hasAncestor(cxxRecordDecl()))),
354 hasParent(namespaceDecl()));
Eric Liu12068d82016-09-22 11:54:00 +0000355 Finder->addMatcher(
356 decl(forEachDescendant(callExpr(callee(FuncMatcher)).bind("call")),
Eric Liu912d0392016-09-27 12:54:48 +0000357 IsInMovedNs, unless(isImplicit()))
Eric Liu12068d82016-09-22 11:54:00 +0000358 .bind("dc"),
359 this);
Eric Liu159f0132016-09-30 04:32:39 +0000360
361 auto GlobalVarMatcher = varDecl(
362 hasGlobalStorage(), hasParent(namespaceDecl()),
363 unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous())))));
364 Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")),
365 to(GlobalVarMatcher.bind("var_decl")))
366 .bind("var_ref"),
367 this);
Eric Liu495b2112016-09-19 17:40:32 +0000368}
369
370void ChangeNamespaceTool::run(
371 const ast_matchers::MatchFinder::MatchResult &Result) {
Eric Liub9bf1b52016-11-08 22:44:17 +0000372 if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {
373 UsingDecls.insert(Using);
374 } else if (const auto *UsingNamespace =
375 Result.Nodes.getNodeAs<UsingDirectiveDecl>(
376 "using_namespace")) {
377 UsingNamespaceDecls.insert(UsingNamespace);
378 } else if (const auto *NsDecl =
379 Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {
Eric Liu495b2112016-09-19 17:40:32 +0000380 moveOldNamespace(Result, NsDecl);
381 } else if (const auto *FwdDecl =
382 Result.Nodes.getNodeAs<CXXRecordDecl>("fwd_decl")) {
383 moveClassForwardDeclaration(Result, FwdDecl);
Eric Liub9bf1b52016-11-08 22:44:17 +0000384 } else if (const auto *UsingWithShadow =
385 Result.Nodes.getNodeAs<UsingDecl>("using_with_shadow")) {
386 fixUsingShadowDecl(Result, UsingWithShadow);
Eric Liu68765a82016-09-21 15:06:12 +0000387 } else if (const auto *Specifier =
388 Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
389 "nested_specifier_loc")) {
390 SourceLocation Start = Specifier->getBeginLoc();
391 SourceLocation End = EndLocationForType(Specifier->getTypeLoc());
392 fixTypeLoc(Result, Start, End, Specifier->getTypeLoc());
Eric Liu12068d82016-09-22 11:54:00 +0000393 } else if (const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type")) {
Eric Liu495b2112016-09-19 17:40:32 +0000394 fixTypeLoc(Result, startLocationForType(*TLoc), EndLocationForType(*TLoc),
395 *TLoc);
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000396 } else if (const auto *VarRef =
397 Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) {
Eric Liu159f0132016-09-30 04:32:39 +0000398 const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl");
399 assert(Var);
400 if (Var->getCanonicalDecl()->isStaticDataMember())
401 return;
Eric Liu159f0132016-09-30 04:32:39 +0000402 const clang::Decl *Context = Result.Nodes.getNodeAs<clang::Decl>("dc");
403 assert(Context && "Empty decl context.");
404 clang::SourceRange VarRefRange = VarRef->getSourceRange();
Eric Liub9bf1b52016-11-08 22:44:17 +0000405 replaceQualifiedSymbolInDeclContext(
406 Result, Context->getDeclContext(), VarRefRange.getBegin(),
407 VarRefRange.getEnd(), llvm::dyn_cast<NamedDecl>(Var));
Eric Liu12068d82016-09-22 11:54:00 +0000408 } else {
Eric Liu159f0132016-09-30 04:32:39 +0000409 const auto *Call = Result.Nodes.getNodeAs<clang::CallExpr>("call");
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000410 assert(Call != nullptr && "Expecting callback for CallExpr.");
411 const clang::FunctionDecl *Func = Call->getDirectCallee();
Eric Liu12068d82016-09-22 11:54:00 +0000412 assert(Func != nullptr);
413 // Ignore out-of-line static methods since they will be handled by nested
414 // name specifiers.
415 if (Func->getCanonicalDecl()->getStorageClass() ==
416 clang::StorageClass::SC_Static &&
417 Func->isOutOfLine())
418 return;
Eric Liu12068d82016-09-22 11:54:00 +0000419 const clang::Decl *Context = Result.Nodes.getNodeAs<clang::Decl>("dc");
420 assert(Context && "Empty decl context.");
421 clang::SourceRange CalleeRange = Call->getCallee()->getSourceRange();
Eric Liub9bf1b52016-11-08 22:44:17 +0000422 replaceQualifiedSymbolInDeclContext(
423 Result, Context->getDeclContext(), CalleeRange.getBegin(),
424 CalleeRange.getEnd(), llvm::dyn_cast<NamedDecl>(Func));
Eric Liu495b2112016-09-19 17:40:32 +0000425 }
426}
427
Eric Liu73f49fd2016-10-12 12:34:18 +0000428static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl,
429 const SourceManager &SM,
430 const LangOptions &LangOpts) {
431 std::unique_ptr<Lexer> Lex =
432 getLexerStartingFromLoc(NsDecl->getLocStart(), SM, LangOpts);
433 assert(Lex.get() &&
434 "Failed to create lexer from the beginning of namespace.");
435 if (!Lex.get())
436 return SourceLocation();
437 Token Tok;
438 while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) {
439 }
440 return Tok.isNot(tok::TokenKind::l_brace)
441 ? SourceLocation()
442 : Tok.getEndLoc().getLocWithOffset(1);
443}
444
Eric Liu495b2112016-09-19 17:40:32 +0000445// Stores information about a moved namespace in `MoveNamespaces` and leaves
446// the actual movement to `onEndOfTranslationUnit()`.
447void ChangeNamespaceTool::moveOldNamespace(
448 const ast_matchers::MatchFinder::MatchResult &Result,
449 const NamespaceDecl *NsDecl) {
450 // If the namespace is empty, do nothing.
451 if (Decl::castToDeclContext(NsDecl)->decls_empty())
452 return;
453
454 // Get the range of the code in the old namespace.
Eric Liu73f49fd2016-10-12 12:34:18 +0000455 SourceLocation Start = getLocAfterNamespaceLBrace(
456 NsDecl, *Result.SourceManager, Result.Context->getLangOpts());
457 assert(Start.isValid() && "Can't find l_brace for namespace.");
Eric Liu495b2112016-09-19 17:40:32 +0000458 SourceLocation End = NsDecl->getRBraceLoc().getLocWithOffset(-1);
459 // Create a replacement that deletes the code in the old namespace merely for
460 // retrieving offset and length from it.
461 const auto R = createReplacement(Start, End, "", *Result.SourceManager);
462 MoveNamespace MoveNs;
463 MoveNs.Offset = R.getOffset();
464 MoveNs.Length = R.getLength();
465
466 // Insert the new namespace after `DiffOldNamespace`. For example, if
467 // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then
468 // "x::y" will be inserted inside the existing namespace "a" and after "a::b".
469 // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b"
470 // in the above example.
471 // FIXME: consider the case where DiffOldNamespace is empty.
472 const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace);
473 SourceLocation LocAfterNs =
474 getStartOfNextLine(OuterNs->getRBraceLoc(), *Result.SourceManager,
475 Result.Context->getLangOpts());
476 assert(LocAfterNs.isValid() &&
477 "Failed to get location after DiffOldNamespace");
478 MoveNs.InsertionOffset = Result.SourceManager->getFileOffset(
479 Result.SourceManager->getSpellingLoc(LocAfterNs));
480
Eric Liucc83c662016-09-19 17:58:59 +0000481 MoveNs.FID = Result.SourceManager->getFileID(Start);
482 MoveNs.SourceMgr = Result.SourceManager;
Eric Liu495b2112016-09-19 17:40:32 +0000483 MoveNamespaces[R.getFilePath()].push_back(MoveNs);
484}
485
486// Removes a class forward declaration from the code in the moved namespace and
487// creates an `InsertForwardDeclaration` to insert the forward declaration back
488// into the old namespace after moving code from the old namespace to the new
489// namespace.
490// For example, changing "a" to "x":
491// Old code:
492// namespace a {
493// class FWD;
494// class A { FWD *fwd; }
495// } // a
496// New code:
497// namespace a {
498// class FWD;
499// } // a
500// namespace x {
501// class A { a::FWD *fwd; }
502// } // x
503void ChangeNamespaceTool::moveClassForwardDeclaration(
504 const ast_matchers::MatchFinder::MatchResult &Result,
505 const CXXRecordDecl *FwdDecl) {
506 SourceLocation Start = FwdDecl->getLocStart();
507 SourceLocation End = FwdDecl->getLocEnd();
508 SourceLocation AfterSemi = Lexer::findLocationAfterToken(
509 End, tok::semi, *Result.SourceManager, Result.Context->getLangOpts(),
510 /*SkipTrailingWhitespaceAndNewLine=*/true);
511 if (AfterSemi.isValid())
512 End = AfterSemi.getLocWithOffset(-1);
513 // Delete the forward declaration from the code to be moved.
514 const auto Deletion =
515 createReplacement(Start, End, "", *Result.SourceManager);
516 addOrMergeReplacement(Deletion, &FileToReplacements[Deletion.getFilePath()]);
517 llvm::StringRef Code = Lexer::getSourceText(
518 CharSourceRange::getTokenRange(
519 Result.SourceManager->getSpellingLoc(Start),
520 Result.SourceManager->getSpellingLoc(End)),
521 *Result.SourceManager, Result.Context->getLangOpts());
522 // Insert the forward declaration back into the old namespace after moving the
523 // code from old namespace to new namespace.
524 // Insertion information is stored in `InsertFwdDecls` and actual
525 // insertion will be performed in `onEndOfTranslationUnit`.
526 // Get the (old) namespace that contains the forward declaration.
527 const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");
528 // The namespace contains the forward declaration, so it must not be empty.
529 assert(!NsDecl->decls_empty());
530 const auto Insertion = createInsertion(NsDecl->decls_begin()->getLocStart(),
531 Code, *Result.SourceManager);
532 InsertForwardDeclaration InsertFwd;
533 InsertFwd.InsertionOffset = Insertion.getOffset();
534 InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();
535 InsertFwdDecls[Insertion.getFilePath()].push_back(InsertFwd);
536}
537
Eric Liub9bf1b52016-11-08 22:44:17 +0000538// Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p
539// FromDecl with the shortest qualified name possible when the reference is in
540// `NewNamespace`.
Eric Liu495b2112016-09-19 17:40:32 +0000541void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
Eric Liub9bf1b52016-11-08 22:44:17 +0000542 const ast_matchers::MatchFinder::MatchResult &Result,
543 const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End,
544 const NamedDecl *FromDecl) {
545 const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext();
Eric Liu495b2112016-09-19 17:40:32 +0000546 const auto *NsDecl = llvm::dyn_cast<NamespaceDecl>(NsDeclContext);
547 // Calculate the name of the `NsDecl` after it is moved to new namespace.
548 std::string OldNs = NsDecl->getQualifiedNameAsString();
549 llvm::StringRef Postfix = OldNs;
550 bool Consumed = Postfix.consume_front(OldNamespace);
551 assert(Consumed && "Expect OldNS to start with OldNamespace.");
552 (void)Consumed;
553 const std::string NewNs = (NewNamespace + Postfix).str();
554
555 llvm::StringRef NestedName = Lexer::getSourceText(
556 CharSourceRange::getTokenRange(
557 Result.SourceManager->getSpellingLoc(Start),
558 Result.SourceManager->getSpellingLoc(End)),
559 *Result.SourceManager, Result.Context->getLangOpts());
560 // If the symbol is already fully qualified, no change needs to be make.
561 if (NestedName.startswith("::"))
562 return;
Eric Liub9bf1b52016-11-08 22:44:17 +0000563 std::string FromDeclName = FromDecl->getQualifiedNameAsString();
Eric Liu495b2112016-09-19 17:40:32 +0000564 std::string ReplaceName =
Eric Liub9bf1b52016-11-08 22:44:17 +0000565 getShortestQualifiedNameInNamespace(FromDeclName, NewNs);
566 // Checks if there is any using namespace declarations that can shorten the
567 // qualified name.
568 for (const auto *UsingNamespace : UsingNamespaceDecls) {
569 if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx,
570 Start))
571 continue;
572 StringRef FromDeclNameRef = FromDeclName;
573 if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace()
574 ->getQualifiedNameAsString())) {
575 FromDeclNameRef = FromDeclNameRef.drop_front(2);
576 if (FromDeclNameRef.size() < ReplaceName.size())
577 ReplaceName = FromDeclNameRef;
578 }
579 }
580 // Checks if there is any using shadow declarations that can shorten the
581 // qualified name.
582 bool Matched = false;
583 for (const UsingDecl *Using : UsingDecls) {
584 if (Matched)
585 break;
586 if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) {
587 for (const auto *UsingShadow : Using->shadows()) {
588 const auto *TargetDecl = UsingShadow->getTargetDecl();
589 if (TargetDecl == FromDecl) {
590 ReplaceName = FromDecl->getNameAsString();
591 Matched = true;
592 break;
593 }
594 }
595 }
596 }
Eric Liu495b2112016-09-19 17:40:32 +0000597 // If the new nested name in the new namespace is the same as it was in the
598 // old namespace, we don't create replacement.
599 if (NestedName == ReplaceName)
600 return;
601 auto R = createReplacement(Start, End, ReplaceName, *Result.SourceManager);
602 addOrMergeReplacement(R, &FileToReplacements[R.getFilePath()]);
603}
604
605// Replace the [Start, End] of `Type` with the shortest qualified name when the
606// `Type` is in `NewNamespace`.
607void ChangeNamespaceTool::fixTypeLoc(
608 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start,
609 SourceLocation End, TypeLoc Type) {
610 // FIXME: do not rename template parameter.
611 if (Start.isInvalid() || End.isInvalid())
612 return;
613 // The declaration which this TypeLoc refers to.
614 const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");
615 // `hasDeclaration` gives underlying declaration, but if the type is
616 // a typedef type, we need to use the typedef type instead.
617 if (auto *Typedef = Type.getType()->getAs<TypedefType>())
618 FromDecl = Typedef->getDecl();
619
620 const Decl *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc");
621 assert(DeclCtx && "Empty decl context.");
Eric Liub9bf1b52016-11-08 22:44:17 +0000622 replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start,
623 End, FromDecl);
Eric Liu495b2112016-09-19 17:40:32 +0000624}
625
Eric Liu68765a82016-09-21 15:06:12 +0000626void ChangeNamespaceTool::fixUsingShadowDecl(
627 const ast_matchers::MatchFinder::MatchResult &Result,
628 const UsingDecl *UsingDeclaration) {
629 SourceLocation Start = UsingDeclaration->getLocStart();
630 SourceLocation End = UsingDeclaration->getLocEnd();
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000631 if (Start.isInvalid() || End.isInvalid())
632 return;
Eric Liu68765a82016-09-21 15:06:12 +0000633
634 assert(UsingDeclaration->shadow_size() > 0);
635 // FIXME: it might not be always accurate to use the first using-decl.
636 const NamedDecl *TargetDecl =
637 UsingDeclaration->shadow_begin()->getTargetDecl();
638 std::string TargetDeclName = TargetDecl->getQualifiedNameAsString();
639 // FIXME: check if target_decl_name is in moved ns, which doesn't make much
640 // sense. If this happens, we need to use name with the new namespace.
641 // Use fully qualified name in UsingDecl for now.
642 auto R = createReplacement(Start, End, "using ::" + TargetDeclName,
643 *Result.SourceManager);
644 addOrMergeReplacement(R, &FileToReplacements[R.getFilePath()]);
645}
646
Eric Liu495b2112016-09-19 17:40:32 +0000647void ChangeNamespaceTool::onEndOfTranslationUnit() {
648 // Move namespace blocks and insert forward declaration to old namespace.
649 for (const auto &FileAndNsMoves : MoveNamespaces) {
650 auto &NsMoves = FileAndNsMoves.second;
651 if (NsMoves.empty())
652 continue;
653 const std::string &FilePath = FileAndNsMoves.first;
654 auto &Replaces = FileToReplacements[FilePath];
Eric Liucc83c662016-09-19 17:58:59 +0000655 auto &SM = *NsMoves.begin()->SourceMgr;
656 llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID);
Eric Liu495b2112016-09-19 17:40:32 +0000657 auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
658 if (!ChangedCode) {
659 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
660 continue;
661 }
662 // Replacements on the changed code for moving namespaces and inserting
663 // forward declarations to old namespaces.
664 tooling::Replacements NewReplacements;
665 // Cut the changed code from the old namespace and paste the code in the new
666 // namespace.
667 for (const auto &NsMove : NsMoves) {
668 // Calculate the range of the old namespace block in the changed
669 // code.
670 const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset);
671 const unsigned NewLength =
672 Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) -
673 NewOffset;
674 tooling::Replacement Deletion(FilePath, NewOffset, NewLength, "");
675 std::string MovedCode = ChangedCode->substr(NewOffset, NewLength);
676 std::string MovedCodeWrappedInNewNs =
677 wrapCodeInNamespace(DiffNewNamespace, MovedCode);
678 // Calculate the new offset at which the code will be inserted in the
679 // changed code.
680 unsigned NewInsertionOffset =
681 Replaces.getShiftedCodePosition(NsMove.InsertionOffset);
682 tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
683 MovedCodeWrappedInNewNs);
684 addOrMergeReplacement(Deletion, &NewReplacements);
685 addOrMergeReplacement(Insertion, &NewReplacements);
686 }
687 // After moving namespaces, insert forward declarations back to old
688 // namespaces.
689 const auto &FwdDeclInsertions = InsertFwdDecls[FilePath];
690 for (const auto &FwdDeclInsertion : FwdDeclInsertions) {
691 unsigned NewInsertionOffset =
692 Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset);
693 tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
694 FwdDeclInsertion.ForwardDeclText);
695 addOrMergeReplacement(Insertion, &NewReplacements);
696 }
697 // Add replacements referring to the changed code to existing replacements,
698 // which refers to the original code.
699 Replaces = Replaces.merge(NewReplacements);
700 format::FormatStyle Style =
701 format::getStyle("file", FilePath, FallbackStyle);
702 // Clean up old namespaces if there is nothing in it after moving.
703 auto CleanReplacements =
704 format::cleanupAroundReplacements(Code, Replaces, Style);
705 if (!CleanReplacements) {
706 llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
707 continue;
708 }
709 FileToReplacements[FilePath] = *CleanReplacements;
710 }
711}
712
713} // namespace change_namespace
714} // namespace clang