blob: 7de4690ac11950c3cdba64ad8dae6432ef02e7f2 [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"
Eric Liuff51f012016-11-16 16:54:53 +000012#include "llvm/Support/ErrorHandling.h"
Eric Liu495b2112016-09-19 17:40:32 +000013
14using namespace clang::ast_matchers;
15
16namespace clang {
17namespace change_namespace {
18
19namespace {
20
21inline std::string
22joinNamespaces(const llvm::SmallVectorImpl<StringRef> &Namespaces) {
23 if (Namespaces.empty())
24 return "";
25 std::string Result = Namespaces.front();
26 for (auto I = Namespaces.begin() + 1, E = Namespaces.end(); I != E; ++I)
27 Result += ("::" + *I).str();
28 return Result;
29}
30
31SourceLocation startLocationForType(TypeLoc TLoc) {
32 // For elaborated types (e.g. `struct a::A`) we want the portion after the
33 // `struct` but including the namespace qualifier, `a::`.
34 if (TLoc.getTypeLocClass() == TypeLoc::Elaborated) {
35 NestedNameSpecifierLoc NestedNameSpecifier =
36 TLoc.castAs<ElaboratedTypeLoc>().getQualifierLoc();
37 if (NestedNameSpecifier.getNestedNameSpecifier())
38 return NestedNameSpecifier.getBeginLoc();
39 TLoc = TLoc.getNextTypeLoc();
40 }
41 return TLoc.getLocStart();
42}
43
Eric Liuc265b022016-12-01 17:25:55 +000044SourceLocation endLocationForType(TypeLoc TLoc) {
Eric Liu495b2112016-09-19 17:40:32 +000045 // Dig past any namespace or keyword qualifications.
46 while (TLoc.getTypeLocClass() == TypeLoc::Elaborated ||
47 TLoc.getTypeLocClass() == TypeLoc::Qualified)
48 TLoc = TLoc.getNextTypeLoc();
49
50 // The location for template specializations (e.g. Foo<int>) includes the
51 // templated types in its location range. We want to restrict this to just
52 // before the `<` character.
53 if (TLoc.getTypeLocClass() == TypeLoc::TemplateSpecialization)
54 return TLoc.castAs<TemplateSpecializationTypeLoc>()
55 .getLAngleLoc()
56 .getLocWithOffset(-1);
57 return TLoc.getEndLoc();
58}
59
60// Returns the containing namespace of `InnerNs` by skipping `PartialNsName`.
Eric Liu6aa94162016-11-10 18:29:01 +000061// If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName`
62// is empty, nullptr is returned.
Eric Liu495b2112016-09-19 17:40:32 +000063// For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then
64// the NamespaceDecl of namespace "a" will be returned.
65const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs,
66 llvm::StringRef PartialNsName) {
Eric Liu6aa94162016-11-10 18:29:01 +000067 if (!InnerNs || PartialNsName.empty())
68 return nullptr;
Eric Liu495b2112016-09-19 17:40:32 +000069 const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs);
70 const auto *CurrentNs = InnerNs;
71 llvm::SmallVector<llvm::StringRef, 4> PartialNsNameSplitted;
Eric Liu6aa94162016-11-10 18:29:01 +000072 PartialNsName.split(PartialNsNameSplitted, "::", /*MaxSplit=*/-1,
73 /*KeepEmpty=*/false);
Eric Liu495b2112016-09-19 17:40:32 +000074 while (!PartialNsNameSplitted.empty()) {
75 // Get the inner-most namespace in CurrentContext.
76 while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext))
77 CurrentContext = CurrentContext->getParent();
78 if (!CurrentContext)
79 return nullptr;
80 CurrentNs = llvm::cast<NamespaceDecl>(CurrentContext);
81 if (PartialNsNameSplitted.back() != CurrentNs->getNameAsString())
82 return nullptr;
83 PartialNsNameSplitted.pop_back();
84 CurrentContext = CurrentContext->getParent();
85 }
86 return CurrentNs;
87}
88
Eric Liu73f49fd2016-10-12 12:34:18 +000089static std::unique_ptr<Lexer>
90getLexerStartingFromLoc(SourceLocation Loc, const SourceManager &SM,
91 const LangOptions &LangOpts) {
Eric Liu495b2112016-09-19 17:40:32 +000092 if (Loc.isMacroID() &&
93 !Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))
Eric Liu73f49fd2016-10-12 12:34:18 +000094 return nullptr;
Eric Liu495b2112016-09-19 17:40:32 +000095 // Break down the source location.
96 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
97 // Try to load the file buffer.
98 bool InvalidTemp = false;
99 llvm::StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
100 if (InvalidTemp)
Eric Liu73f49fd2016-10-12 12:34:18 +0000101 return nullptr;
Eric Liu495b2112016-09-19 17:40:32 +0000102
103 const char *TokBegin = File.data() + LocInfo.second;
104 // Lex from the start of the given location.
Eric Liu73f49fd2016-10-12 12:34:18 +0000105 return llvm::make_unique<Lexer>(SM.getLocForStartOfFile(LocInfo.first),
106 LangOpts, File.begin(), TokBegin, File.end());
107}
Eric Liu495b2112016-09-19 17:40:32 +0000108
Eric Liu73f49fd2016-10-12 12:34:18 +0000109// FIXME: get rid of this helper function if this is supported in clang-refactor
110// library.
111static SourceLocation getStartOfNextLine(SourceLocation Loc,
112 const SourceManager &SM,
113 const LangOptions &LangOpts) {
114 std::unique_ptr<Lexer> Lex = getLexerStartingFromLoc(Loc, SM, LangOpts);
115 if (!Lex.get())
116 return SourceLocation();
Eric Liu495b2112016-09-19 17:40:32 +0000117 llvm::SmallVector<char, 16> Line;
118 // FIXME: this is a bit hacky to get ReadToEndOfLine work.
Eric Liu73f49fd2016-10-12 12:34:18 +0000119 Lex->setParsingPreprocessorDirective(true);
120 Lex->ReadToEndOfLine(&Line);
Haojian Wuef8a6dc2016-10-04 10:35:53 +0000121 auto End = Loc.getLocWithOffset(Line.size());
Eric Liu73f49fd2016-10-12 12:34:18 +0000122 return SM.getLocForEndOfFile(SM.getDecomposedLoc(Loc).first) == End
123 ? End
124 : End.getLocWithOffset(1);
Eric Liu495b2112016-09-19 17:40:32 +0000125}
126
127// Returns `R` with new range that refers to code after `Replaces` being
128// applied.
129tooling::Replacement
130getReplacementInChangedCode(const tooling::Replacements &Replaces,
131 const tooling::Replacement &R) {
132 unsigned NewStart = Replaces.getShiftedCodePosition(R.getOffset());
133 unsigned NewEnd =
134 Replaces.getShiftedCodePosition(R.getOffset() + R.getLength());
135 return tooling::Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
136 R.getReplacementText());
137}
138
139// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by
140// applying all existing Replaces first if there is conflict.
141void addOrMergeReplacement(const tooling::Replacement &R,
142 tooling::Replacements *Replaces) {
143 auto Err = Replaces->add(R);
144 if (Err) {
145 llvm::consumeError(std::move(Err));
146 auto Replace = getReplacementInChangedCode(*Replaces, R);
147 *Replaces = Replaces->merge(tooling::Replacements(Replace));
148 }
149}
150
151tooling::Replacement createReplacement(SourceLocation Start, SourceLocation End,
152 llvm::StringRef ReplacementText,
153 const SourceManager &SM) {
154 if (!Start.isValid() || !End.isValid()) {
155 llvm::errs() << "start or end location were invalid\n";
156 return tooling::Replacement();
157 }
158 if (SM.getDecomposedLoc(Start).first != SM.getDecomposedLoc(End).first) {
159 llvm::errs()
160 << "start or end location were in different macro expansions\n";
161 return tooling::Replacement();
162 }
163 Start = SM.getSpellingLoc(Start);
164 End = SM.getSpellingLoc(End);
165 if (SM.getFileID(Start) != SM.getFileID(End)) {
166 llvm::errs() << "start or end location were in different files\n";
167 return tooling::Replacement();
168 }
169 return tooling::Replacement(
170 SM, CharSourceRange::getTokenRange(SM.getSpellingLoc(Start),
171 SM.getSpellingLoc(End)),
172 ReplacementText);
173}
174
175tooling::Replacement createInsertion(SourceLocation Loc,
176 llvm::StringRef InsertText,
177 const SourceManager &SM) {
178 if (Loc.isInvalid()) {
179 llvm::errs() << "insert Location is invalid.\n";
180 return tooling::Replacement();
181 }
182 Loc = SM.getSpellingLoc(Loc);
183 return tooling::Replacement(SM, Loc, 0, InsertText);
184}
185
186// Returns the shortest qualified name for declaration `DeclName` in the
187// namespace `NsName`. For example, if `DeclName` is "a::b::X" and `NsName`
188// is "a::c::d", then "b::X" will be returned.
Eric Liu447164d2016-10-05 15:52:39 +0000189// \param DeclName A fully qualified name, "::a::b::X" or "a::b::X".
190// \param NsName A fully qualified name, "::a::b" or "a::b". Global namespace
191// will have empty name.
Eric Liu495b2112016-09-19 17:40:32 +0000192std::string getShortestQualifiedNameInNamespace(llvm::StringRef DeclName,
193 llvm::StringRef NsName) {
Eric Liu447164d2016-10-05 15:52:39 +0000194 DeclName = DeclName.ltrim(':');
195 NsName = NsName.ltrim(':');
Eric Liu447164d2016-10-05 15:52:39 +0000196 if (DeclName.find(':') == llvm::StringRef::npos)
Eric Liub9bf1b52016-11-08 22:44:17 +0000197 return DeclName;
Eric Liu447164d2016-10-05 15:52:39 +0000198
199 while (!DeclName.consume_front((NsName + "::").str())) {
Eric Liu495b2112016-09-19 17:40:32 +0000200 const auto Pos = NsName.find_last_of(':');
201 if (Pos == llvm::StringRef::npos)
202 return DeclName;
Eric Liu447164d2016-10-05 15:52:39 +0000203 assert(Pos > 0);
204 NsName = NsName.substr(0, Pos - 1);
Eric Liu495b2112016-09-19 17:40:32 +0000205 }
206 return DeclName;
207}
208
209std::string wrapCodeInNamespace(StringRef NestedNs, std::string Code) {
210 if (Code.back() != '\n')
211 Code += "\n";
212 llvm::SmallVector<StringRef, 4> NsSplitted;
213 NestedNs.split(NsSplitted, "::");
214 while (!NsSplitted.empty()) {
215 // FIXME: consider code style for comments.
216 Code = ("namespace " + NsSplitted.back() + " {\n" + Code +
217 "} // namespace " + NsSplitted.back() + "\n")
218 .str();
219 NsSplitted.pop_back();
220 }
221 return Code;
222}
223
Eric Liub9bf1b52016-11-08 22:44:17 +0000224// Returns true if \p D is a nested DeclContext in \p Context
225bool isNestedDeclContext(const DeclContext *D, const DeclContext *Context) {
226 while (D) {
227 if (D == Context)
228 return true;
229 D = D->getParent();
230 }
231 return false;
232}
233
234// Returns true if \p D is visible at \p Loc with DeclContext \p DeclCtx.
235bool isDeclVisibleAtLocation(const SourceManager &SM, const Decl *D,
236 const DeclContext *DeclCtx, SourceLocation Loc) {
237 SourceLocation DeclLoc = SM.getSpellingLoc(D->getLocation());
238 Loc = SM.getSpellingLoc(Loc);
239 return SM.isBeforeInTranslationUnit(DeclLoc, Loc) &&
240 (SM.getFileID(DeclLoc) == SM.getFileID(Loc) &&
241 isNestedDeclContext(DeclCtx, D->getDeclContext()));
242}
243
Eric Liu495b2112016-09-19 17:40:32 +0000244} // anonymous namespace
245
246ChangeNamespaceTool::ChangeNamespaceTool(
247 llvm::StringRef OldNs, llvm::StringRef NewNs, llvm::StringRef FilePattern,
248 std::map<std::string, tooling::Replacements> *FileToReplacements,
249 llvm::StringRef FallbackStyle)
250 : FallbackStyle(FallbackStyle), FileToReplacements(*FileToReplacements),
251 OldNamespace(OldNs.ltrim(':')), NewNamespace(NewNs.ltrim(':')),
Eric Liuc265b022016-12-01 17:25:55 +0000252 FilePattern(FilePattern), FilePatternRE(FilePattern) {
Eric Liu495b2112016-09-19 17:40:32 +0000253 FileToReplacements->clear();
254 llvm::SmallVector<llvm::StringRef, 4> OldNsSplitted;
255 llvm::SmallVector<llvm::StringRef, 4> NewNsSplitted;
256 llvm::StringRef(OldNamespace).split(OldNsSplitted, "::");
257 llvm::StringRef(NewNamespace).split(NewNsSplitted, "::");
258 // Calculates `DiffOldNamespace` and `DiffNewNamespace`.
259 while (!OldNsSplitted.empty() && !NewNsSplitted.empty() &&
260 OldNsSplitted.front() == NewNsSplitted.front()) {
261 OldNsSplitted.erase(OldNsSplitted.begin());
262 NewNsSplitted.erase(NewNsSplitted.begin());
263 }
264 DiffOldNamespace = joinNamespaces(OldNsSplitted);
265 DiffNewNamespace = joinNamespaces(NewNsSplitted);
266}
267
Eric Liu495b2112016-09-19 17:40:32 +0000268void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
Eric Liu495b2112016-09-19 17:40:32 +0000269 std::string FullOldNs = "::" + OldNamespace;
Eric Liub9bf1b52016-11-08 22:44:17 +0000270 // Prefix is the outer-most namespace in DiffOldNamespace. For example, if the
271 // OldNamespace is "a::b::c" and DiffOldNamespace is "b::c", then Prefix will
272 // be "a::b". Declarations in this namespace will not be visible in the new
273 // namespace. If DiffOldNamespace is empty, Prefix will be a invalid name "-".
274 llvm::SmallVector<llvm::StringRef, 4> DiffOldNsSplitted;
275 llvm::StringRef(DiffOldNamespace).split(DiffOldNsSplitted, "::");
276 std::string Prefix = "-";
277 if (!DiffOldNsSplitted.empty())
278 Prefix = (StringRef(FullOldNs).drop_back(DiffOldNamespace.size()) +
279 DiffOldNsSplitted.front())
280 .str();
281 auto IsInMovedNs =
282 allOf(hasAncestor(namespaceDecl(hasName(FullOldNs)).bind("ns_decl")),
283 isExpansionInFileMatching(FilePattern));
284 auto IsVisibleInNewNs = anyOf(
285 IsInMovedNs, unless(hasAncestor(namespaceDecl(hasName(Prefix)))));
286 // Match using declarations.
287 Finder->addMatcher(
288 usingDecl(isExpansionInFileMatching(FilePattern), IsVisibleInNewNs)
289 .bind("using"),
290 this);
291 // Match using namespace declarations.
292 Finder->addMatcher(usingDirectiveDecl(isExpansionInFileMatching(FilePattern),
293 IsVisibleInNewNs)
294 .bind("using_namespace"),
295 this);
296
297 // Match old namespace blocks.
Eric Liu495b2112016-09-19 17:40:32 +0000298 Finder->addMatcher(
299 namespaceDecl(hasName(FullOldNs), isExpansionInFileMatching(FilePattern))
300 .bind("old_ns"),
301 this);
302
Eric Liu495b2112016-09-19 17:40:32 +0000303 // Match forward-declarations in the old namespace.
304 Finder->addMatcher(
305 cxxRecordDecl(unless(anyOf(isImplicit(), isDefinition())), IsInMovedNs)
306 .bind("fwd_decl"),
307 this);
308
309 // Match references to types that are not defined in the old namespace.
310 // Forward-declarations in the old namespace are also matched since they will
311 // be moved back to the old namespace.
312 auto DeclMatcher = namedDecl(
313 hasAncestor(namespaceDecl()),
314 unless(anyOf(
Eric Liu912d0392016-09-27 12:54:48 +0000315 isImplicit(), hasAncestor(namespaceDecl(isAnonymous())),
Eric Liu495b2112016-09-19 17:40:32 +0000316 hasAncestor(cxxRecordDecl()),
317 allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition())))))));
Eric Liu912d0392016-09-27 12:54:48 +0000318
Eric Liu495b2112016-09-19 17:40:32 +0000319 // Match TypeLocs on the declaration. Carefully match only the outermost
Eric Liu8393cb02016-10-31 08:28:29 +0000320 // TypeLoc and template specialization arguments (which are not outermost)
321 // that are directly linked to types matching `DeclMatcher`. Nested name
322 // specifier locs are handled separately below.
Eric Liu495b2112016-09-19 17:40:32 +0000323 Finder->addMatcher(
324 typeLoc(IsInMovedNs,
325 loc(qualType(hasDeclaration(DeclMatcher.bind("from_decl")))),
Eric Liu8393cb02016-10-31 08:28:29 +0000326 unless(anyOf(hasParent(typeLoc(loc(qualType(
327 allOf(hasDeclaration(DeclMatcher),
328 unless(templateSpecializationType())))))),
Eric Liu495b2112016-09-19 17:40:32 +0000329 hasParent(nestedNameSpecifierLoc()))),
330 hasAncestor(decl().bind("dc")))
331 .bind("type"),
332 this);
Eric Liu912d0392016-09-27 12:54:48 +0000333
Eric Liu68765a82016-09-21 15:06:12 +0000334 // Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to
335 // special case it.
Eric Liub9bf1b52016-11-08 22:44:17 +0000336 Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()))
337 .bind("using_with_shadow"),
338 this);
Eric Liu912d0392016-09-27 12:54:48 +0000339
Eric Liuff51f012016-11-16 16:54:53 +0000340 // Handle types in nested name specifier. Specifiers that are in a TypeLoc
341 // matched above are not matched, e.g. "A::" in "A::A" is not matched since
342 // "A::A" would have already been fixed.
Eric Liu68765a82016-09-21 15:06:12 +0000343 Finder->addMatcher(nestedNameSpecifierLoc(
344 hasAncestor(decl(IsInMovedNs).bind("dc")),
345 loc(nestedNameSpecifier(specifiesType(
Eric Liuff51f012016-11-16 16:54:53 +0000346 hasDeclaration(DeclMatcher.bind("from_decl"))))),
347 unless(hasAncestor(typeLoc(loc(qualType(hasDeclaration(
348 decl(equalsBoundNode("from_decl")))))))))
Eric Liu68765a82016-09-21 15:06:12 +0000349 .bind("nested_specifier_loc"),
350 this);
Eric Liu12068d82016-09-22 11:54:00 +0000351
Eric Liuff51f012016-11-16 16:54:53 +0000352 // Matches base class initializers in constructors. TypeLocs of base class
353 // initializers do not need to be fixed. For example,
354 // class X : public a::b::Y {
355 // public:
356 // X() : Y::Y() {} // Y::Y do not need namespace specifier.
357 // };
358 Finder->addMatcher(
359 cxxCtorInitializer(isBaseInitializer()).bind("base_initializer"), this);
360
Eric Liu12068d82016-09-22 11:54:00 +0000361 // Handle function.
Eric Liu912d0392016-09-27 12:54:48 +0000362 // Only handle functions that are defined in a namespace excluding member
363 // function, static methods (qualified by nested specifier), and functions
364 // defined in the global namespace.
Eric Liu12068d82016-09-22 11:54:00 +0000365 // Note that the matcher does not exclude calls to out-of-line static method
366 // definitions, so we need to exclude them in the callback handler.
Eric Liu912d0392016-09-27 12:54:48 +0000367 auto FuncMatcher =
368 functionDecl(unless(anyOf(cxxMethodDecl(), IsInMovedNs,
369 hasAncestor(namespaceDecl(isAnonymous())),
370 hasAncestor(cxxRecordDecl()))),
371 hasParent(namespaceDecl()));
Eric Liuda22b3c2016-11-29 14:15:14 +0000372 Finder->addMatcher(decl(forEachDescendant(expr(anyOf(
373 callExpr(callee(FuncMatcher)).bind("call"),
374 declRefExpr(to(FuncMatcher.bind("func_decl")))
375 .bind("func_ref")))),
376 IsInMovedNs, unless(isImplicit()))
377 .bind("dc"),
378 this);
Eric Liu159f0132016-09-30 04:32:39 +0000379
380 auto GlobalVarMatcher = varDecl(
381 hasGlobalStorage(), hasParent(namespaceDecl()),
382 unless(anyOf(IsInMovedNs, hasAncestor(namespaceDecl(isAnonymous())))));
383 Finder->addMatcher(declRefExpr(IsInMovedNs, hasAncestor(decl().bind("dc")),
384 to(GlobalVarMatcher.bind("var_decl")))
385 .bind("var_ref"),
386 this);
Eric Liu495b2112016-09-19 17:40:32 +0000387}
388
389void ChangeNamespaceTool::run(
390 const ast_matchers::MatchFinder::MatchResult &Result) {
Eric Liub9bf1b52016-11-08 22:44:17 +0000391 if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) {
392 UsingDecls.insert(Using);
393 } else if (const auto *UsingNamespace =
394 Result.Nodes.getNodeAs<UsingDirectiveDecl>(
395 "using_namespace")) {
396 UsingNamespaceDecls.insert(UsingNamespace);
397 } else if (const auto *NsDecl =
398 Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {
Eric Liu495b2112016-09-19 17:40:32 +0000399 moveOldNamespace(Result, NsDecl);
400 } else if (const auto *FwdDecl =
401 Result.Nodes.getNodeAs<CXXRecordDecl>("fwd_decl")) {
402 moveClassForwardDeclaration(Result, FwdDecl);
Eric Liub9bf1b52016-11-08 22:44:17 +0000403 } else if (const auto *UsingWithShadow =
404 Result.Nodes.getNodeAs<UsingDecl>("using_with_shadow")) {
405 fixUsingShadowDecl(Result, UsingWithShadow);
Eric Liu68765a82016-09-21 15:06:12 +0000406 } else if (const auto *Specifier =
407 Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
408 "nested_specifier_loc")) {
409 SourceLocation Start = Specifier->getBeginLoc();
Eric Liuc265b022016-12-01 17:25:55 +0000410 SourceLocation End = endLocationForType(Specifier->getTypeLoc());
Eric Liu68765a82016-09-21 15:06:12 +0000411 fixTypeLoc(Result, Start, End, Specifier->getTypeLoc());
Eric Liuff51f012016-11-16 16:54:53 +0000412 } else if (const auto *BaseInitializer =
413 Result.Nodes.getNodeAs<CXXCtorInitializer>(
414 "base_initializer")) {
415 BaseCtorInitializerTypeLocs.push_back(
416 BaseInitializer->getTypeSourceInfo()->getTypeLoc());
Eric Liu12068d82016-09-22 11:54:00 +0000417 } else if (const auto *TLoc = Result.Nodes.getNodeAs<TypeLoc>("type")) {
Eric Liuc265b022016-12-01 17:25:55 +0000418 fixTypeLoc(Result, startLocationForType(*TLoc), endLocationForType(*TLoc),
Eric Liu495b2112016-09-19 17:40:32 +0000419 *TLoc);
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000420 } else if (const auto *VarRef =
421 Result.Nodes.getNodeAs<DeclRefExpr>("var_ref")) {
Eric Liu159f0132016-09-30 04:32:39 +0000422 const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var_decl");
423 assert(Var);
424 if (Var->getCanonicalDecl()->isStaticDataMember())
425 return;
Eric Liuda22b3c2016-11-29 14:15:14 +0000426 const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
Eric Liu159f0132016-09-30 04:32:39 +0000427 assert(Context && "Empty decl context.");
Eric Liuda22b3c2016-11-29 14:15:14 +0000428 fixDeclRefExpr(Result, Context->getDeclContext(),
429 llvm::cast<NamedDecl>(Var), VarRef);
430 } else if (const auto *FuncRef =
431 Result.Nodes.getNodeAs<DeclRefExpr>("func_ref")) {
432 const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
433 assert(Func);
434 const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
435 assert(Context && "Empty decl context.");
436 fixDeclRefExpr(Result, Context->getDeclContext(),
437 llvm::cast<NamedDecl>(Func), FuncRef);
Eric Liu12068d82016-09-22 11:54:00 +0000438 } else {
Eric Liuda22b3c2016-11-29 14:15:14 +0000439 const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000440 assert(Call != nullptr && "Expecting callback for CallExpr.");
Eric Liuda22b3c2016-11-29 14:15:14 +0000441 const FunctionDecl *Func = Call->getDirectCallee();
Eric Liu12068d82016-09-22 11:54:00 +0000442 assert(Func != nullptr);
443 // Ignore out-of-line static methods since they will be handled by nested
444 // name specifiers.
445 if (Func->getCanonicalDecl()->getStorageClass() ==
Eric Liuda22b3c2016-11-29 14:15:14 +0000446 StorageClass::SC_Static &&
Eric Liu12068d82016-09-22 11:54:00 +0000447 Func->isOutOfLine())
448 return;
Eric Liuda22b3c2016-11-29 14:15:14 +0000449 const auto *Context = Result.Nodes.getNodeAs<Decl>("dc");
Eric Liu12068d82016-09-22 11:54:00 +0000450 assert(Context && "Empty decl context.");
Eric Liuda22b3c2016-11-29 14:15:14 +0000451 SourceRange CalleeRange = Call->getCallee()->getSourceRange();
Eric Liub9bf1b52016-11-08 22:44:17 +0000452 replaceQualifiedSymbolInDeclContext(
453 Result, Context->getDeclContext(), CalleeRange.getBegin(),
Eric Liu231c6552016-11-10 18:15:34 +0000454 CalleeRange.getEnd(), llvm::cast<NamedDecl>(Func));
Eric Liu495b2112016-09-19 17:40:32 +0000455 }
456}
457
Eric Liu73f49fd2016-10-12 12:34:18 +0000458static SourceLocation getLocAfterNamespaceLBrace(const NamespaceDecl *NsDecl,
459 const SourceManager &SM,
460 const LangOptions &LangOpts) {
461 std::unique_ptr<Lexer> Lex =
462 getLexerStartingFromLoc(NsDecl->getLocStart(), SM, LangOpts);
463 assert(Lex.get() &&
464 "Failed to create lexer from the beginning of namespace.");
465 if (!Lex.get())
466 return SourceLocation();
467 Token Tok;
468 while (!Lex->LexFromRawLexer(Tok) && Tok.isNot(tok::TokenKind::l_brace)) {
469 }
470 return Tok.isNot(tok::TokenKind::l_brace)
471 ? SourceLocation()
472 : Tok.getEndLoc().getLocWithOffset(1);
473}
474
Eric Liu495b2112016-09-19 17:40:32 +0000475// Stores information about a moved namespace in `MoveNamespaces` and leaves
476// the actual movement to `onEndOfTranslationUnit()`.
477void ChangeNamespaceTool::moveOldNamespace(
478 const ast_matchers::MatchFinder::MatchResult &Result,
479 const NamespaceDecl *NsDecl) {
480 // If the namespace is empty, do nothing.
481 if (Decl::castToDeclContext(NsDecl)->decls_empty())
482 return;
483
484 // Get the range of the code in the old namespace.
Eric Liu73f49fd2016-10-12 12:34:18 +0000485 SourceLocation Start = getLocAfterNamespaceLBrace(
486 NsDecl, *Result.SourceManager, Result.Context->getLangOpts());
487 assert(Start.isValid() && "Can't find l_brace for namespace.");
Eric Liu495b2112016-09-19 17:40:32 +0000488 SourceLocation End = NsDecl->getRBraceLoc().getLocWithOffset(-1);
489 // Create a replacement that deletes the code in the old namespace merely for
490 // retrieving offset and length from it.
491 const auto R = createReplacement(Start, End, "", *Result.SourceManager);
492 MoveNamespace MoveNs;
493 MoveNs.Offset = R.getOffset();
494 MoveNs.Length = R.getLength();
495
496 // Insert the new namespace after `DiffOldNamespace`. For example, if
497 // `OldNamespace` is "a::b::c" and `NewNamespace` is `a::x::y`, then
498 // "x::y" will be inserted inside the existing namespace "a" and after "a::b".
499 // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b"
500 // in the above example.
Eric Liu6aa94162016-11-10 18:29:01 +0000501 // If there is no outer namespace (i.e. DiffOldNamespace is empty), the new
502 // namespace will be a nested namespace in the old namespace.
Eric Liu495b2112016-09-19 17:40:32 +0000503 const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace);
Eric Liu6aa94162016-11-10 18:29:01 +0000504 SourceLocation InsertionLoc = Start;
505 if (OuterNs) {
506 SourceLocation LocAfterNs =
507 getStartOfNextLine(OuterNs->getRBraceLoc(), *Result.SourceManager,
508 Result.Context->getLangOpts());
509 assert(LocAfterNs.isValid() &&
510 "Failed to get location after DiffOldNamespace");
511 InsertionLoc = LocAfterNs;
512 }
Eric Liu495b2112016-09-19 17:40:32 +0000513 MoveNs.InsertionOffset = Result.SourceManager->getFileOffset(
Eric Liu6aa94162016-11-10 18:29:01 +0000514 Result.SourceManager->getSpellingLoc(InsertionLoc));
Eric Liucc83c662016-09-19 17:58:59 +0000515 MoveNs.FID = Result.SourceManager->getFileID(Start);
516 MoveNs.SourceMgr = Result.SourceManager;
Eric Liu495b2112016-09-19 17:40:32 +0000517 MoveNamespaces[R.getFilePath()].push_back(MoveNs);
518}
519
520// Removes a class forward declaration from the code in the moved namespace and
521// creates an `InsertForwardDeclaration` to insert the forward declaration back
522// into the old namespace after moving code from the old namespace to the new
523// namespace.
524// For example, changing "a" to "x":
525// Old code:
526// namespace a {
527// class FWD;
528// class A { FWD *fwd; }
529// } // a
530// New code:
531// namespace a {
532// class FWD;
533// } // a
534// namespace x {
535// class A { a::FWD *fwd; }
536// } // x
537void ChangeNamespaceTool::moveClassForwardDeclaration(
538 const ast_matchers::MatchFinder::MatchResult &Result,
539 const CXXRecordDecl *FwdDecl) {
540 SourceLocation Start = FwdDecl->getLocStart();
541 SourceLocation End = FwdDecl->getLocEnd();
542 SourceLocation AfterSemi = Lexer::findLocationAfterToken(
543 End, tok::semi, *Result.SourceManager, Result.Context->getLangOpts(),
544 /*SkipTrailingWhitespaceAndNewLine=*/true);
545 if (AfterSemi.isValid())
546 End = AfterSemi.getLocWithOffset(-1);
547 // Delete the forward declaration from the code to be moved.
548 const auto Deletion =
549 createReplacement(Start, End, "", *Result.SourceManager);
Eric Liuff51f012016-11-16 16:54:53 +0000550 auto Err = FileToReplacements[Deletion.getFilePath()].add(Deletion);
551 if (Err)
552 llvm_unreachable(llvm::toString(std::move(Err)).c_str());
Eric Liu495b2112016-09-19 17:40:32 +0000553 llvm::StringRef Code = Lexer::getSourceText(
554 CharSourceRange::getTokenRange(
555 Result.SourceManager->getSpellingLoc(Start),
556 Result.SourceManager->getSpellingLoc(End)),
557 *Result.SourceManager, Result.Context->getLangOpts());
558 // Insert the forward declaration back into the old namespace after moving the
559 // code from old namespace to new namespace.
560 // Insertion information is stored in `InsertFwdDecls` and actual
561 // insertion will be performed in `onEndOfTranslationUnit`.
562 // Get the (old) namespace that contains the forward declaration.
563 const auto *NsDecl = Result.Nodes.getNodeAs<NamespaceDecl>("ns_decl");
564 // The namespace contains the forward declaration, so it must not be empty.
565 assert(!NsDecl->decls_empty());
566 const auto Insertion = createInsertion(NsDecl->decls_begin()->getLocStart(),
567 Code, *Result.SourceManager);
568 InsertForwardDeclaration InsertFwd;
569 InsertFwd.InsertionOffset = Insertion.getOffset();
570 InsertFwd.ForwardDeclText = Insertion.getReplacementText().str();
571 InsertFwdDecls[Insertion.getFilePath()].push_back(InsertFwd);
572}
573
Eric Liub9bf1b52016-11-08 22:44:17 +0000574// Replaces a qualified symbol (in \p DeclCtx) that refers to a declaration \p
575// FromDecl with the shortest qualified name possible when the reference is in
576// `NewNamespace`.
Eric Liu495b2112016-09-19 17:40:32 +0000577void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
Eric Liub9bf1b52016-11-08 22:44:17 +0000578 const ast_matchers::MatchFinder::MatchResult &Result,
579 const DeclContext *DeclCtx, SourceLocation Start, SourceLocation End,
580 const NamedDecl *FromDecl) {
581 const auto *NsDeclContext = DeclCtx->getEnclosingNamespaceContext();
Eric Liu231c6552016-11-10 18:15:34 +0000582 const auto *NsDecl = llvm::cast<NamespaceDecl>(NsDeclContext);
Eric Liu495b2112016-09-19 17:40:32 +0000583 // Calculate the name of the `NsDecl` after it is moved to new namespace.
584 std::string OldNs = NsDecl->getQualifiedNameAsString();
585 llvm::StringRef Postfix = OldNs;
586 bool Consumed = Postfix.consume_front(OldNamespace);
587 assert(Consumed && "Expect OldNS to start with OldNamespace.");
588 (void)Consumed;
589 const std::string NewNs = (NewNamespace + Postfix).str();
590
591 llvm::StringRef NestedName = Lexer::getSourceText(
592 CharSourceRange::getTokenRange(
593 Result.SourceManager->getSpellingLoc(Start),
594 Result.SourceManager->getSpellingLoc(End)),
595 *Result.SourceManager, Result.Context->getLangOpts());
596 // If the symbol is already fully qualified, no change needs to be make.
597 if (NestedName.startswith("::"))
598 return;
Eric Liub9bf1b52016-11-08 22:44:17 +0000599 std::string FromDeclName = FromDecl->getQualifiedNameAsString();
Eric Liu495b2112016-09-19 17:40:32 +0000600 std::string ReplaceName =
Eric Liub9bf1b52016-11-08 22:44:17 +0000601 getShortestQualifiedNameInNamespace(FromDeclName, NewNs);
602 // Checks if there is any using namespace declarations that can shorten the
603 // qualified name.
604 for (const auto *UsingNamespace : UsingNamespaceDecls) {
605 if (!isDeclVisibleAtLocation(*Result.SourceManager, UsingNamespace, DeclCtx,
606 Start))
607 continue;
608 StringRef FromDeclNameRef = FromDeclName;
609 if (FromDeclNameRef.consume_front(UsingNamespace->getNominatedNamespace()
610 ->getQualifiedNameAsString())) {
611 FromDeclNameRef = FromDeclNameRef.drop_front(2);
612 if (FromDeclNameRef.size() < ReplaceName.size())
613 ReplaceName = FromDeclNameRef;
614 }
615 }
616 // Checks if there is any using shadow declarations that can shorten the
617 // qualified name.
618 bool Matched = false;
619 for (const UsingDecl *Using : UsingDecls) {
620 if (Matched)
621 break;
622 if (isDeclVisibleAtLocation(*Result.SourceManager, Using, DeclCtx, Start)) {
623 for (const auto *UsingShadow : Using->shadows()) {
624 const auto *TargetDecl = UsingShadow->getTargetDecl();
625 if (TargetDecl == FromDecl) {
626 ReplaceName = FromDecl->getNameAsString();
627 Matched = true;
628 break;
629 }
630 }
631 }
632 }
Eric Liu495b2112016-09-19 17:40:32 +0000633 // If the new nested name in the new namespace is the same as it was in the
634 // old namespace, we don't create replacement.
635 if (NestedName == ReplaceName)
636 return;
637 auto R = createReplacement(Start, End, ReplaceName, *Result.SourceManager);
Eric Liuff51f012016-11-16 16:54:53 +0000638 auto Err = FileToReplacements[R.getFilePath()].add(R);
639 if (Err)
640 llvm_unreachable(llvm::toString(std::move(Err)).c_str());
Eric Liu495b2112016-09-19 17:40:32 +0000641}
642
643// Replace the [Start, End] of `Type` with the shortest qualified name when the
644// `Type` is in `NewNamespace`.
645void ChangeNamespaceTool::fixTypeLoc(
646 const ast_matchers::MatchFinder::MatchResult &Result, SourceLocation Start,
647 SourceLocation End, TypeLoc Type) {
648 // FIXME: do not rename template parameter.
649 if (Start.isInvalid() || End.isInvalid())
650 return;
Eric Liuff51f012016-11-16 16:54:53 +0000651 // Types of CXXCtorInitializers do not need to be fixed.
652 if (llvm::is_contained(BaseCtorInitializerTypeLocs, Type))
653 return;
Eric Liu495b2112016-09-19 17:40:32 +0000654 // The declaration which this TypeLoc refers to.
655 const auto *FromDecl = Result.Nodes.getNodeAs<NamedDecl>("from_decl");
656 // `hasDeclaration` gives underlying declaration, but if the type is
657 // a typedef type, we need to use the typedef type instead.
Eric Liu32158862016-11-14 19:37:55 +0000658 if (auto *Typedef = Type.getType()->getAs<TypedefType>()) {
Eric Liu495b2112016-09-19 17:40:32 +0000659 FromDecl = Typedef->getDecl();
Eric Liu32158862016-11-14 19:37:55 +0000660 auto IsInMovedNs = [&](const NamedDecl *D) {
661 if (!llvm::StringRef(D->getQualifiedNameAsString())
662 .startswith(OldNamespace + "::"))
663 return false;
664 auto ExpansionLoc =
665 Result.SourceManager->getExpansionLoc(D->getLocStart());
666 if (ExpansionLoc.isInvalid())
667 return false;
668 llvm::StringRef Filename =
669 Result.SourceManager->getFilename(ExpansionLoc);
Eric Liuc265b022016-12-01 17:25:55 +0000670 return FilePatternRE.match(Filename);
Eric Liu32158862016-11-14 19:37:55 +0000671 };
672 // Don't fix the \p Type if it refers to a type alias decl in the moved
673 // namespace since the alias decl will be moved along with the type
674 // reference.
675 if (IsInMovedNs(FromDecl))
676 return;
677 }
Eric Liu495b2112016-09-19 17:40:32 +0000678
679 const Decl *DeclCtx = Result.Nodes.getNodeAs<Decl>("dc");
680 assert(DeclCtx && "Empty decl context.");
Eric Liub9bf1b52016-11-08 22:44:17 +0000681 replaceQualifiedSymbolInDeclContext(Result, DeclCtx->getDeclContext(), Start,
682 End, FromDecl);
Eric Liu495b2112016-09-19 17:40:32 +0000683}
684
Eric Liu68765a82016-09-21 15:06:12 +0000685void ChangeNamespaceTool::fixUsingShadowDecl(
686 const ast_matchers::MatchFinder::MatchResult &Result,
687 const UsingDecl *UsingDeclaration) {
688 SourceLocation Start = UsingDeclaration->getLocStart();
689 SourceLocation End = UsingDeclaration->getLocEnd();
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000690 if (Start.isInvalid() || End.isInvalid())
691 return;
Eric Liu68765a82016-09-21 15:06:12 +0000692
693 assert(UsingDeclaration->shadow_size() > 0);
694 // FIXME: it might not be always accurate to use the first using-decl.
695 const NamedDecl *TargetDecl =
696 UsingDeclaration->shadow_begin()->getTargetDecl();
697 std::string TargetDeclName = TargetDecl->getQualifiedNameAsString();
698 // FIXME: check if target_decl_name is in moved ns, which doesn't make much
699 // sense. If this happens, we need to use name with the new namespace.
700 // Use fully qualified name in UsingDecl for now.
701 auto R = createReplacement(Start, End, "using ::" + TargetDeclName,
702 *Result.SourceManager);
Eric Liuff51f012016-11-16 16:54:53 +0000703 auto Err = FileToReplacements[R.getFilePath()].add(R);
704 if (Err)
705 llvm_unreachable(llvm::toString(std::move(Err)).c_str());
Eric Liu68765a82016-09-21 15:06:12 +0000706}
707
Eric Liuda22b3c2016-11-29 14:15:14 +0000708void ChangeNamespaceTool::fixDeclRefExpr(
709 const ast_matchers::MatchFinder::MatchResult &Result,
710 const DeclContext *UseContext, const NamedDecl *From,
711 const DeclRefExpr *Ref) {
712 SourceRange RefRange = Ref->getSourceRange();
713 replaceQualifiedSymbolInDeclContext(Result, UseContext, RefRange.getBegin(),
714 RefRange.getEnd(), From);
715}
716
Eric Liu495b2112016-09-19 17:40:32 +0000717void ChangeNamespaceTool::onEndOfTranslationUnit() {
718 // Move namespace blocks and insert forward declaration to old namespace.
719 for (const auto &FileAndNsMoves : MoveNamespaces) {
720 auto &NsMoves = FileAndNsMoves.second;
721 if (NsMoves.empty())
722 continue;
723 const std::string &FilePath = FileAndNsMoves.first;
724 auto &Replaces = FileToReplacements[FilePath];
Eric Liucc83c662016-09-19 17:58:59 +0000725 auto &SM = *NsMoves.begin()->SourceMgr;
726 llvm::StringRef Code = SM.getBufferData(NsMoves.begin()->FID);
Eric Liu495b2112016-09-19 17:40:32 +0000727 auto ChangedCode = tooling::applyAllReplacements(Code, Replaces);
728 if (!ChangedCode) {
729 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
730 continue;
731 }
732 // Replacements on the changed code for moving namespaces and inserting
733 // forward declarations to old namespaces.
734 tooling::Replacements NewReplacements;
735 // Cut the changed code from the old namespace and paste the code in the new
736 // namespace.
737 for (const auto &NsMove : NsMoves) {
738 // Calculate the range of the old namespace block in the changed
739 // code.
740 const unsigned NewOffset = Replaces.getShiftedCodePosition(NsMove.Offset);
741 const unsigned NewLength =
742 Replaces.getShiftedCodePosition(NsMove.Offset + NsMove.Length) -
743 NewOffset;
744 tooling::Replacement Deletion(FilePath, NewOffset, NewLength, "");
745 std::string MovedCode = ChangedCode->substr(NewOffset, NewLength);
746 std::string MovedCodeWrappedInNewNs =
747 wrapCodeInNamespace(DiffNewNamespace, MovedCode);
748 // Calculate the new offset at which the code will be inserted in the
749 // changed code.
750 unsigned NewInsertionOffset =
751 Replaces.getShiftedCodePosition(NsMove.InsertionOffset);
752 tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
753 MovedCodeWrappedInNewNs);
754 addOrMergeReplacement(Deletion, &NewReplacements);
755 addOrMergeReplacement(Insertion, &NewReplacements);
756 }
757 // After moving namespaces, insert forward declarations back to old
758 // namespaces.
759 const auto &FwdDeclInsertions = InsertFwdDecls[FilePath];
760 for (const auto &FwdDeclInsertion : FwdDeclInsertions) {
761 unsigned NewInsertionOffset =
762 Replaces.getShiftedCodePosition(FwdDeclInsertion.InsertionOffset);
763 tooling::Replacement Insertion(FilePath, NewInsertionOffset, 0,
764 FwdDeclInsertion.ForwardDeclText);
765 addOrMergeReplacement(Insertion, &NewReplacements);
766 }
767 // Add replacements referring to the changed code to existing replacements,
768 // which refers to the original code.
769 Replaces = Replaces.merge(NewReplacements);
770 format::FormatStyle Style =
771 format::getStyle("file", FilePath, FallbackStyle);
772 // Clean up old namespaces if there is nothing in it after moving.
773 auto CleanReplacements =
774 format::cleanupAroundReplacements(Code, Replaces, Style);
775 if (!CleanReplacements) {
776 llvm::errs() << llvm::toString(CleanReplacements.takeError()) << "\n";
777 continue;
778 }
779 FileToReplacements[FilePath] = *CleanReplacements;
780 }
Eric Liuc265b022016-12-01 17:25:55 +0000781
782 // Make sure we don't generate replacements for files that do not match
783 // FilePattern.
784 for (auto &Entry : FileToReplacements)
785 if (!FilePatternRE.match(Entry.first))
786 Entry.second.clear();
Eric Liu495b2112016-09-19 17:40:32 +0000787}
788
789} // namespace change_namespace
790} // namespace clang