1 //===-- ClangChangeNamespace.cpp - Standalone change namespace ------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
8 // This tool can be used to change the surrounding namespaces of class/function
18 // To move the definition of class Y from namespace "na::nb" to "x::y", run:
19 // clang-change-namespace --old_namespace "na::nb" \
20 // --new_namespace "x::y" --file_pattern "test.cc" test.cc --
27 // class Y { na::X x; };
31 #include "ChangeNamespace.h"
32 #include "clang/ASTMatchers/ASTMatchFinder.h"
33 #include "clang/Frontend/FrontendActions.h"
34 #include "clang/Frontend/TextDiagnosticPrinter.h"
35 #include "clang/Rewrite/Core/Rewriter.h"
36 #include "clang/Tooling/CommonOptionsParser.h"
37 #include "clang/Tooling/Refactoring.h"
38 #include "clang/Tooling/Tooling.h"
39 #include "llvm/Support/CommandLine.h"
40 #include "llvm/Support/Signals.h"
41 #include "llvm/Support/YAMLTraits.h"
43 using namespace clang
;
48 cl::OptionCategory
ChangeNamespaceCategory("Change namespace.");
50 cl::opt
<std::string
> OldNamespace("old_namespace", cl::Required
,
51 cl::desc("Old namespace."),
52 cl::cat(ChangeNamespaceCategory
));
54 cl::opt
<std::string
> NewNamespace("new_namespace", cl::Required
,
55 cl::desc("New namespace."),
56 cl::cat(ChangeNamespaceCategory
));
58 cl::opt
<std::string
> FilePattern(
59 "file_pattern", cl::Required
,
60 cl::desc("Only rename namespaces in files that match the given pattern."),
61 cl::cat(ChangeNamespaceCategory
));
63 cl::opt
<bool> Inplace("i", cl::desc("Inplace edit <file>s, if specified."),
64 cl::cat(ChangeNamespaceCategory
));
67 DumpYAML("dump_result",
68 cl::desc("Dump new file contents in YAML, if specified."),
69 cl::cat(ChangeNamespaceCategory
));
71 cl::opt
<std::string
> Style("style",
72 cl::desc("The style name used for reformatting."),
73 cl::init("LLVM"), cl::cat(ChangeNamespaceCategory
));
75 cl::opt
<std::string
> AllowedFile(
77 cl::desc("A file containing regexes of symbol names that are not expected "
78 "to be updated when changing namespaces around them."),
79 cl::init(""), cl::cat(ChangeNamespaceCategory
));
81 llvm::ErrorOr
<std::vector
<std::string
>> GetAllowedSymbolPatterns() {
82 std::vector
<std::string
> Patterns
;
83 if (AllowedFile
.empty())
86 llvm::SmallVector
<StringRef
, 8> Lines
;
87 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>> File
=
88 llvm::MemoryBuffer::getFile(AllowedFile
);
90 return File
.getError();
91 llvm::StringRef Content
= File
.get()->getBuffer();
92 Content
.split(Lines
, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
93 for (auto Line
: Lines
)
94 Patterns
.push_back(std::string(Line
.trim()));
98 } // anonymous namespace
100 int main(int argc
, const char **argv
) {
101 llvm::sys::PrintStackTraceOnErrorSignal(argv
[0]);
102 auto ExpectedParser
=
103 tooling::CommonOptionsParser::create(argc
, argv
, ChangeNamespaceCategory
);
104 if (!ExpectedParser
) {
105 llvm::errs() << ExpectedParser
.takeError();
108 tooling::CommonOptionsParser
&OptionsParser
= ExpectedParser
.get();
109 const auto &Files
= OptionsParser
.getSourcePathList();
110 tooling::RefactoringTool
Tool(OptionsParser
.getCompilations(), Files
);
111 llvm::ErrorOr
<std::vector
<std::string
>> AllowedPatterns
=
112 GetAllowedSymbolPatterns();
113 if (!AllowedPatterns
) {
114 llvm::errs() << "Failed to open allow file " << AllowedFile
<< ". "
115 << AllowedPatterns
.getError().message() << "\n";
118 change_namespace::ChangeNamespaceTool
NamespaceTool(
119 OldNamespace
, NewNamespace
, FilePattern
, *AllowedPatterns
,
120 &Tool
.getReplacements(), Style
);
121 ast_matchers::MatchFinder Finder
;
122 NamespaceTool
.registerMatchers(&Finder
);
123 std::unique_ptr
<tooling::FrontendActionFactory
> Factory
=
124 tooling::newFrontendActionFactory(&Finder
);
126 if (int Result
= Tool
.run(Factory
.get()))
128 LangOptions DefaultLangOptions
;
129 IntrusiveRefCntPtr
<DiagnosticOptions
> DiagOpts
= new DiagnosticOptions();
130 clang::TextDiagnosticPrinter
DiagnosticPrinter(errs(), &*DiagOpts
);
131 DiagnosticsEngine
Diagnostics(
132 IntrusiveRefCntPtr
<DiagnosticIDs
>(new DiagnosticIDs()), &*DiagOpts
,
133 &DiagnosticPrinter
, false);
134 auto &FileMgr
= Tool
.getFiles();
135 SourceManager
Sources(Diagnostics
, FileMgr
);
136 Rewriter
Rewrite(Sources
, DefaultLangOptions
);
138 if (!formatAndApplyAllReplacements(Tool
.getReplacements(), Rewrite
, Style
)) {
139 llvm::errs() << "Failed applying all replacements.\n";
143 return Rewrite
.overwriteChangedFiles();
145 std::set
<llvm::StringRef
> ChangedFiles
;
146 for (const auto &it
: Tool
.getReplacements())
147 ChangedFiles
.insert(it
.first
);
150 auto WriteToYAML
= [&](llvm::raw_ostream
&OS
) {
152 for (auto I
= ChangedFiles
.begin(), E
= ChangedFiles
.end(); I
!= E
; ++I
) {
154 OS
<< " \"FilePath\": \"" << *I
<< "\",\n";
155 const auto Entry
= FileMgr
.getFile(*I
);
156 auto ID
= Sources
.getOrCreateFileID(*Entry
, SrcMgr::C_User
);
158 llvm::raw_string_ostream
ContentStream(Content
);
159 Rewrite
.getEditBuffer(ID
).write(ContentStream
);
160 OS
<< " \"SourceText\": \""
161 << llvm::yaml::escape(ContentStream
.str()) << "\"\n";
163 if (I
!= std::prev(E
))
168 WriteToYAML(llvm::outs());
172 for (const auto &File
: ChangedFiles
) {
173 const auto Entry
= FileMgr
.getFile(File
);
175 auto ID
= Sources
.getOrCreateFileID(*Entry
, SrcMgr::C_User
);
176 outs() << "============== " << File
<< " ==============\n";
177 Rewrite
.getEditBuffer(ID
).write(llvm::outs());
178 outs() << "\n============================================\n";