Index: include-fixer/find-all-symbols/SymbolInfo.h =================================================================== --- include-fixer/find-all-symbols/SymbolInfo.h +++ include-fixer/find-all-symbols/SymbolInfo.h @@ -54,6 +54,8 @@ int LineNumber, const std::vector &Contexts, unsigned NumOccurrences = 0); + void SetFilePath(llvm::StringRef Path) { FilePath = Path; } + /// \brief Get symbol name. llvm::StringRef getName() const { return Name; } Index: include-fixer/tool/ClangIncludeFixer.cpp =================================================================== --- include-fixer/tool/ClangIncludeFixer.cpp +++ include-fixer/tool/ClangIncludeFixer.cpp @@ -98,6 +98,12 @@ cl::desc("String to initialize the database"), cl::cat(IncludeFixerCategory)); +cl::opt + QuerySymbol("query-symbol", + cl::desc("Query a given symbol (e.g. \"a::b::foo\") in\n" + "database directly without parsing the file."), + cl::cat(IncludeFixerCategory)); + cl::opt MinimizeIncludePaths("minimize-paths", cl::desc("Whether to minimize added include paths"), @@ -236,6 +242,7 @@ tooling::ClangTool tool(options.getCompilations(), options.getSourcePathList()); + llvm::StringRef SourceFilePath = options.getSourcePathList().front(); // In STDINMode, we override the file content with the input. // Since `tool.mapVirtualFile` takes `StringRef`, we define `Code` outside of // the if-block so that `Code` is not released after the if-block. @@ -253,7 +260,7 @@ if (Code->getBufferSize() == 0) return 0; // Skip empty files. - tool.mapVirtualFile(options.getSourcePathList().front(), Code->getBuffer()); + tool.mapVirtualFile(SourceFilePath, Code->getBuffer()); } if (!InsertHeader.empty()) { @@ -314,10 +321,31 @@ // Set up data source. std::unique_ptr SymbolIndexMgr = - createSymbolIndexManager(options.getSourcePathList().front()); + createSymbolIndexManager(SourceFilePath); if (!SymbolIndexMgr) return 1; + // Query symbol mode. + if (!QuerySymbol.empty()) { + auto MatchedSymbols = SymbolIndexMgr->search(QuerySymbol); + for (auto &Symbol : MatchedSymbols) { + std::string HeaderPath = Symbol.getFilePath().str(); + Symbol.SetFilePath(((HeaderPath[0] == '"' || HeaderPath[0] == '<') + ? HeaderPath + : "\"" + HeaderPath + "\"")); + } + + // We leave an empty symbol range as we don't know the range of the symbol + // being queried in this mode. include-fixer won't add namespace qualifiers + // if the symbol range is empty, which also fits this case. + IncludeFixerContext::QuerySymbolInfo Symbol; + Symbol.RawIdentifier = QuerySymbol; + auto Context = + IncludeFixerContext(SourceFilePath, {Symbol}, MatchedSymbols); + writeToJson(llvm::outs(), Context); + return 0; + } + // Now run our tool. std::vector Contexts; include_fixer::IncludeFixerActionFactory Factory(*SymbolIndexMgr, Contexts, Index: test/include-fixer/query_symbol.cpp =================================================================== --- /dev/null +++ test/include-fixer/query_symbol.cpp @@ -0,0 +1,13 @@ +// RUN: clang-include-fixer -db=fixed -input='foo= "foo.h","bar.h"' -query-symbol="foo" test.cpp -- | FileCheck %s + +// CHECK: "FilePath": "test.cpp", +// CHECK-NEXT:"QuerySymbolInfos": [ +// CHECK-NEXT: {"RawIdentifier": "foo", +// CHECK-NEXT: "Range":{"Offset":0,"Length":0}} +// CHECK-NEXT:], +// CHECK-NEXT:"HeaderInfos": [ +// CHECK-NEXT: {"Header": "\"foo.h\"", +// CHECK-NEXT: "QualifiedName": "foo"}, +// CHECK-NEXT: {"Header": "\"bar.h\"", +// CHECK-NEXT: "QualifiedName": "foo"} +// CHECK-NEXT:]