16 #include "clang/Format/Format.h" 17 #include "clang/Frontend/TextDiagnosticPrinter.h" 18 #include "clang/Rewrite/Core/Rewriter.h" 19 #include "clang/Tooling/CommonOptionsParser.h" 20 #include "clang/Tooling/Core/Replacement.h" 21 #include "clang/Tooling/Tooling.h" 22 #include "llvm/Support/CommandLine.h" 23 #include "llvm/Support/Path.h" 24 #include "llvm/Support/YAMLTraits.h" 26 using namespace clang;
31 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(IncludeFixerContext::HeaderInfo)
32 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(IncludeFixerContext::QuerySymbolInfo)
37 template <>
struct MappingTraits<tooling::
Range> {
38 struct NormalizedRange {
42 : Offset(R.getOffset()), Length(R.getLength()) {}
52 MappingNormalization<NormalizedRange, tooling::Range> Keys(IO, Info);
53 IO.mapRequired(
"Offset", Keys->Offset);
54 IO.mapRequired(
"Length", Keys->Length);
59 static void mapping(IO &io, IncludeFixerContext::HeaderInfo &
Info) {
60 io.mapRequired(
"Header", Info.Header);
61 io.mapRequired(
"QualifiedName", Info.QualifiedName);
66 static void mapping(IO &io, IncludeFixerContext::QuerySymbolInfo &
Info) {
67 io.mapRequired(
"RawIdentifier", Info.RawIdentifier);
68 io.mapRequired(
"Range", Info.Range);
74 IO.mapRequired(
"QuerySymbolInfos", Context.QuerySymbolInfos);
75 IO.mapRequired(
"HeaderInfos", Context.HeaderInfos);
76 IO.mapRequired(
"FilePath", Context.FilePath);
83 cl::OptionCategory IncludeFixerCategory(
"Tool options");
91 cl::opt<DatabaseFormatTy> DatabaseFormat(
92 "db", cl::desc(
"Specify input format"),
93 cl::values(clEnumVal(fixed,
"Hard-coded mapping"),
94 clEnumVal(
yaml,
"Yaml database created by find-all-symbols"),
95 clEnumVal(fuzzyYaml,
"Yaml database, with fuzzy-matched names")),
96 cl::init(
yaml), cl::cat(IncludeFixerCategory));
98 cl::opt<std::string> Input(
"input",
99 cl::desc(
"String to initialize the database"),
100 cl::cat(IncludeFixerCategory));
103 QuerySymbol(
"query-symbol",
104 cl::desc(
"Query a given symbol (e.g. \"a::b::foo\") in\n" 105 "database directly without parsing the file."),
106 cl::cat(IncludeFixerCategory));
109 MinimizeIncludePaths(
"minimize-paths",
110 cl::desc(
"Whether to minimize added include paths"),
111 cl::init(
true), cl::cat(IncludeFixerCategory));
113 cl::opt<bool>
Quiet(
"q", cl::desc(
"Reduce terminal output"), cl::init(
false),
114 cl::cat(IncludeFixerCategory));
118 cl::desc(
"Override source file's content (in the overlaying\n" 119 "virtual file system) with input from <stdin> and run\n" 120 "the tool on the new content with the compilation\n" 121 "options of the source file. This mode is currently\n" 122 "used for editor integration."),
123 cl::init(
false), cl::cat(IncludeFixerCategory));
125 cl::opt<bool> OutputHeaders(
127 cl::desc(
"Print the symbol being queried and all its relevant headers in\n" 128 "JSON format to stdout:\n" 130 " \"FilePath\": \"/path/to/foo.cc\",\n" 131 " \"QuerySymbolInfos\": [\n" 132 " {\"RawIdentifier\": \"foo\",\n" 133 " \"Range\": {\"Offset\": 0, \"Length\": 3}}\n" 135 " \"HeaderInfos\": [ {\"Header\": \"\\\"foo_a.h\\\"\",\n" 136 " \"QualifiedName\": \"a::foo\"} ]\n" 138 cl::init(
false), cl::cat(IncludeFixerCategory));
140 cl::opt<std::string> InsertHeader(
142 cl::desc(
"Insert a specific header. This should run with STDIN mode.\n" 143 "The result is written to stdout. It is currently used for\n" 144 "editor integration. Support YAML/JSON format:\n" 145 " -insert-header=\"{\n" 146 " FilePath: \"/path/to/foo.cc\",\n" 147 " QuerySymbolInfos: [\n" 148 " {RawIdentifier: foo,\n" 149 " Range: {Offset: 0, Length: 3}}\n" 151 " HeaderInfos: [ {Headers: \"\\\"foo_a.h\\\"\",\n" 152 " QualifiedName: \"a::foo\"} ]}\""),
153 cl::init(
""), cl::cat(IncludeFixerCategory));
157 cl::desc(
"Fallback style for reformatting after inserting new\n" 158 "headers if there is no clang-format config file found."),
159 cl::init(
"llvm"), cl::cat(IncludeFixerCategory));
161 std::unique_ptr<include_fixer::SymbolIndexManager>
162 createSymbolIndexManager(StringRef FilePath) {
165 auto SymbolIndexMgr = llvm::make_unique<include_fixer::SymbolIndexManager>();
166 switch (DatabaseFormat) {
171 std::map<std::string, std::vector<std::string>> SymbolsMap;
172 SmallVector<StringRef, 4> SemicolonSplits;
173 StringRef(Input).split(SemicolonSplits,
";");
174 std::vector<find_all_symbols::SymbolAndSignals> Symbols;
175 for (StringRef Pair : SemicolonSplits) {
176 auto Split = Pair.split(
'=');
177 std::vector<std::string> Headers;
178 SmallVector<StringRef, 4> CommaSplits;
179 Split.second.split(CommaSplits,
",");
180 for (
size_t I = 0, E = CommaSplits.size(); I != E; ++I)
182 {SymbolInfo(Split.first.trim(), SymbolInfo::SymbolKind::Unknown,
183 CommaSplits[I].trim(), {}),
188 SymbolIndexMgr->addSymbolIndex([=]() {
189 return llvm::make_unique<include_fixer::InMemorySymbolIndex>(Symbols);
194 auto CreateYamlIdx = [=]() -> std::unique_ptr<include_fixer::SymbolIndex> {
195 llvm::ErrorOr<std::unique_ptr<include_fixer::YamlSymbolIndex>> DB(
197 if (!Input.empty()) {
203 SmallString<128> AbsolutePath(tooling::getAbsolutePath(FilePath));
204 StringRef
Directory = llvm::sys::path::parent_path(AbsolutePath);
206 Directory,
"find_all_symbols_db.yaml");
210 llvm::errs() <<
"Couldn't find YAML db: " << DB.getError().message()
214 return std::move(*DB);
217 SymbolIndexMgr->addSymbolIndex(std::move(CreateYamlIdx));
223 SymbolIndexMgr->addSymbolIndex(
224 []() -> std::unique_ptr<include_fixer::SymbolIndex> {
227 llvm::errs() <<
"Couldn't load fuzzy YAML db: " 231 return std::move(*DB);
236 return SymbolIndexMgr;
241 <<
" \"FilePath\": \"" 242 << llvm::yaml::escape(Context.
getFilePath()) <<
"\",\n" 243 <<
" \"QuerySymbolInfos\": [\n";
245 OS <<
" {\"RawIdentifier\": \"" <<
Info.RawIdentifier <<
"\",\n";
246 OS <<
" \"Range\":{";
247 OS <<
"\"Offset\":" <<
Info.Range.getOffset() <<
",";
248 OS <<
"\"Length\":" <<
Info.Range.getLength() <<
"}}";
253 OS <<
" \"HeaderInfos\": [\n";
255 for (
const auto &
Info : HeaderInfos) {
256 OS <<
" {\"Header\": \"" << llvm::yaml::escape(
Info.Header) <<
"\",\n" 257 <<
" \"QualifiedName\": \"" <<
Info.QualifiedName <<
"\"}";
258 if (&
Info != &HeaderInfos.back())
266 int includeFixerMain(
int argc,
const char **argv) {
267 tooling::CommonOptionsParser options(argc, argv, IncludeFixerCategory);
268 tooling::ClangTool tool(options.getCompilations(),
269 options.getSourcePathList());
271 llvm::StringRef SourceFilePath = options.getSourcePathList().front();
275 std::unique_ptr<llvm::MemoryBuffer> Code;
277 assert(options.getSourcePathList().size() == 1 &&
278 "Expect exactly one file path in STDINMode.");
279 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> CodeOrErr =
280 MemoryBuffer::getSTDIN();
281 if (std::error_code EC = CodeOrErr.getError()) {
282 errs() << EC.message() <<
"\n";
285 Code = std::move(CodeOrErr.get());
286 if (Code->getBufferSize() == 0)
289 tool.mapVirtualFile(SourceFilePath, Code->getBuffer());
292 if (!InsertHeader.empty()) {
294 errs() <<
"Should be running in STDIN mode\n";
298 llvm::yaml::Input yin(InsertHeader);
303 assert(!HeaderInfos.empty());
306 bool IsUniqueHeader = std::equal(
307 HeaderInfos.begin()+1, HeaderInfos.end(), HeaderInfos.begin(),
308 [](
const IncludeFixerContext::HeaderInfo &LHS,
309 const IncludeFixerContext::HeaderInfo &RHS) {
310 return LHS.Header == RHS.Header;
312 if (!IsUniqueHeader) {
313 errs() <<
"Expect exactly one unique header.\n";
321 bool IsUniqueQualifiedName = std::equal(
322 HeaderInfos.begin() + 1, HeaderInfos.end(), HeaderInfos.begin(),
323 [](
const IncludeFixerContext::HeaderInfo &LHS,
324 const IncludeFixerContext::HeaderInfo &RHS) {
325 return LHS.QualifiedName == RHS.QualifiedName;
327 auto InsertStyle = format::getStyle(format::DefaultFormatStyle,
328 Context.getFilePath(), Style);
334 Code->getBuffer(), Context, *InsertStyle,
335 IsUniqueQualifiedName);
337 errs() <<
"Failed to create replacements: " 343 tooling::applyAllReplacements(Code->getBuffer(), *Replacements);
348 llvm::outs() << *ChangedCode;
353 std::unique_ptr<include_fixer::SymbolIndexManager> SymbolIndexMgr =
354 createSymbolIndexManager(SourceFilePath);
359 if (!QuerySymbol.empty()) {
360 auto MatchedSymbols = SymbolIndexMgr->search(
361 QuerySymbol,
true, SourceFilePath);
362 for (
auto &Symbol : MatchedSymbols) {
363 std::string HeaderPath = Symbol.getFilePath().str();
364 Symbol.SetFilePath(((HeaderPath[0] ==
'"' || HeaderPath[0] ==
'<')
366 :
"\"" + HeaderPath +
"\""));
372 IncludeFixerContext::QuerySymbolInfo Symbol;
373 Symbol.RawIdentifier = QuerySymbol;
376 writeToJson(llvm::outs(), Context);
381 std::vector<include_fixer::IncludeFixerContext> Contexts;
383 Style, MinimizeIncludePaths);
385 if (tool.run(&Factory) != 0) {
390 llvm::errs() <<
"Fatal compiler error occurred while parsing file!" 391 " (incorrect include paths?)\n";
395 assert(!Contexts.empty());
399 writeToJson(llvm::outs(), Contexts.front());
403 std::vector<tooling::Replacements> FixerReplacements;
404 for (
const auto &Context : Contexts) {
407 format::getStyle(format::DefaultFormatStyle, FilePath, Style);
412 auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
414 errs() <<
"Couldn't open file: " + FilePath.str() +
": " 415 << Buffer.getError().message() +
"\n";
420 Buffer.get()->getBuffer(), Context, *InsertStyle);
422 errs() <<
"Failed to create replacement: " 426 FixerReplacements.push_back(*Replacements);
430 for (
const auto &Context : Contexts) {
432 llvm::errs() <<
"Added #include " 440 assert(FixerReplacements.size() == 1);
441 auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(),
442 FixerReplacements.front());
447 llvm::outs() << *ChangedCode;
452 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(
new DiagnosticOptions);
453 DiagnosticsEngine Diagnostics(
new DiagnosticIDs, &*DiagOpts);
454 TextDiagnosticPrinter DiagnosticPrinter(outs(), &*DiagOpts);
455 SourceManager SM(Diagnostics, tool.getFiles());
456 Diagnostics.setClient(&DiagnosticPrinter,
false);
459 Rewriter Rewrites(SM, LangOptions());
460 for (
const auto &Replacement : FixerReplacements) {
461 if (!tooling::applyAllReplacements(Replacement, Rewrites)) {
462 llvm::errs() <<
"Failed to apply replacements.\n";
466 return Rewrites.overwriteChangedFiles();
471 int main(
int argc,
const char **argv) {
472 return includeFixerMain(argc, argv);
Some operations such as code completion produce a set of candidates.
static void mapping(IO &IO, IncludeFixerContext &Context)
llvm::Expected< tooling::Replacements > createIncludeFixerReplacements(StringRef Code, const IncludeFixerContext &Context, const clang::format::FormatStyle &Style, bool AddQualifiers)
const std::vector< QuerySymbolInfo > & getQuerySymbolInfos() const
Get information of symbols being querid.
StringRef getFilePath() const
Get the file path to the file being processed.
A context for a file being processed.
const std::vector< HeaderInfo > & getHeaderInfos() const
Get header information.
int main(int argc, const char **argv)
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
static llvm::Expected< std::unique_ptr< FuzzySymbolIndex > > createFromYAML(llvm::StringRef File)
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
clang::find_all_symbols::SymbolInfo SymbolInfo
static llvm::ErrorOr< std::unique_ptr< YamlSymbolIndex > > createFromDirectory(llvm::StringRef Directory, llvm::StringRef Name)
Look for a file called Name in Directory and all parent directories.
static void mapping(IO &io, IncludeFixerContext::QuerySymbolInfo &Info)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
static llvm::ErrorOr< std::unique_ptr< YamlSymbolIndex > > createFromFile(llvm::StringRef FilePath)
Create a new Yaml db from a file.
static cl::opt< bool > Quiet("quiet", cl::desc(R"(
Run clang-tidy in quiet mode. This suppresses
printing statistics about ignored warnings and
warnings treated as errors if the respective
options are specified.
)"), cl::init(false), cl::cat(ClangTidyCategory))