Clang: Add clang query

Clang query is mechanism to use AST matcher to search for code. Think
about regular expression but in the context of AST. So you get a semantic
search tool for C++.

Change-Id: I72e882c5b53a0c52f352a3664847c4c3e4f6fc2e
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Tim Jenssen
2016-11-15 15:38:12 +01:00
parent 96187594b5
commit 9c7ff5199f
99 changed files with 4603 additions and 246 deletions

View File

@@ -0,0 +1,225 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangquery.h"
#include "sourcelocationsutils.h"
#include <sourcerangescontainer.h>
#include <QTime>
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#include <clang/ASTMatchers/ASTMatchers.h>
#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <clang/ASTMatchers/Dynamic/Diagnostics.h>
#include <clang/ASTMatchers/Dynamic/Parser.h>
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
using clang::ast_matchers::dynamic::Diagnostics;
using clang::ast_matchers::dynamic::Parser;
using clang::ast_matchers::BoundNodes;
using clang::ast_matchers::MatchFinder;
namespace ClangBackEnd {
struct CollectBoundNodes : MatchFinder::MatchCallback {
std::vector<BoundNodes> &Bindings;
CollectBoundNodes(std::vector<BoundNodes> &Bindings) : Bindings(Bindings) {}
void run(const MatchFinder::MatchResult &Result) override {
Bindings.push_back(Result.Nodes);
}
};
ClangQuery::ClangQuery(Utils::SmallString &&query)
: query(std::move(query))
{
}
void ClangQuery::setQuery(Utils::SmallString &&query)
{
this->query = std::move(query);
}
void ClangQuery::findLocations()
{
auto tool = createTool();
std::vector<std::unique_ptr<clang::ASTUnit>> asts;
{
QTime timer;
timer.start();
tool.buildASTs(asts);
qWarning() << "ASTs are built: " << timer.elapsed();
}
std::vector<clang::SourceRange> sourceRanges;
sourceRanges.reserve(100);
std::for_each (std::make_move_iterator(asts.begin()),
std::make_move_iterator(asts.end()),
[&] (std::unique_ptr<clang::ASTUnit> &&ast) {
Diagnostics diagnostics;
auto optionalMatcher = Parser::parseMatcherExpression({query.data(), query.size()},
nullptr,
&diagnostics);
parseDiagnostics(diagnostics);
matchLocation(optionalMatcher, std::move(ast), sourceRanges);
});
}
SourceRangesContainer ClangQuery::takeSourceRanges()
{
return std::move(sourceRangesContainer);
}
std::vector<DynamicASTMatcherDiagnosticContainer> ClangQuery::takeDiagnosticContainers()
{
return std::move(diagnosticContainers_);
}
namespace {
V2::SourceRangeContainer convertToContainer(const clang::ast_matchers::dynamic::SourceRange sourceRange)
{
return V2::SourceRangeContainer(0,
sourceRange.Start.Line,
sourceRange.Start.Column,
0,
sourceRange.End.Line,
sourceRange.End.Column,
0);
}
#define ERROR_RETURN_CASE(name) \
case Diagnostics::ET_##name: return ClangQueryDiagnosticErrorType::name;
ClangQueryDiagnosticErrorType convertToErrorType(Diagnostics::ErrorType clangErrorType)
{
switch (clangErrorType) {
ERROR_RETURN_CASE(None)
ERROR_RETURN_CASE(RegistryMatcherNotFound)
ERROR_RETURN_CASE(RegistryWrongArgCount)
ERROR_RETURN_CASE(RegistryWrongArgType)
ERROR_RETURN_CASE(RegistryNotBindable)
ERROR_RETURN_CASE(RegistryAmbiguousOverload)
ERROR_RETURN_CASE(RegistryValueNotFound)
ERROR_RETURN_CASE(ParserStringError)
ERROR_RETURN_CASE(ParserNoOpenParen)
ERROR_RETURN_CASE(ParserNoCloseParen)
ERROR_RETURN_CASE(ParserNoComma)
ERROR_RETURN_CASE(ParserNoCode)
ERROR_RETURN_CASE(ParserNotAMatcher)
ERROR_RETURN_CASE(ParserInvalidToken)
ERROR_RETURN_CASE(ParserMalformedBindExpr)
ERROR_RETURN_CASE(ParserTrailingCode)
ERROR_RETURN_CASE(ParserUnsignedError)
ERROR_RETURN_CASE(ParserOverloadedType)
}
Q_UNREACHABLE();
}
#define CONTEXT_RETURN_CASE(name) \
case Diagnostics::CT_##name: return ClangQueryDiagnosticContextType::name;
ClangQueryDiagnosticContextType convertToContextType(Diagnostics::ContextType clangContextType)
{
switch (clangContextType) {
CONTEXT_RETURN_CASE(MatcherArg)
CONTEXT_RETURN_CASE(MatcherConstruct)
}
Q_UNREACHABLE();
}
}
void ClangQuery::parseDiagnostics(const clang::ast_matchers::dynamic::Diagnostics &diagnostics)
{
auto errors = diagnostics.errors();
for (const auto &errorContent : errors) {
diagnosticContainers_.emplace_back();
DynamicASTMatcherDiagnosticContainer &diagnosticContainer = diagnosticContainers_.back();
for (const auto &message : errorContent.Messages) {
diagnosticContainer.insertMessage(convertToContainer(message.Range),
convertToErrorType(message.Type),
Utils::SmallStringVector(message.Args));
}
for (const auto &message : errorContent.ContextStack) {
diagnosticContainer.insertContext(convertToContainer(message.Range),
convertToContextType(message.Type),
Utils::SmallStringVector(message.Args));
}
}
}
void ClangQuery::matchLocation(
const llvm::Optional< clang::ast_matchers::internal::DynTypedMatcher> &optionalStartMatcher,
std::unique_ptr<clang::ASTUnit> ast,
std::vector<clang::SourceRange> &sourceRanges)
{
if (optionalStartMatcher) {
auto matcher = *optionalStartMatcher;
auto optionalMatcher = matcher.tryBind("root");
matcher = *optionalMatcher;
MatchFinder finder;
std::vector<BoundNodes> matches;
CollectBoundNodes collectBoundNodes(matches);
finder.addDynamicMatcher(matcher, &collectBoundNodes);
finder.matchAST(ast->getASTContext());
for (const auto &boundNodes : matches) {
for (const auto &mapEntry : boundNodes.getMap()) {
const auto sourceRange = mapEntry.second.getSourceRange();
if (sourceRange.isValid())
sourceRanges.push_back(sourceRange);
}
}
appendSourceRangesToSourceRangesContainer(sourceRangesContainer,
sourceRanges,
ast->getSourceManager());
}
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,73 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "clangtool.h"
#include <sourcerangescontainer.h>
#include <dynamicastmatcherdiagnosticcontainer.h>
namespace clang {
namespace ast_matchers {
namespace dynamic {
class Diagnostics;
}
namespace internal {
class DynTypedMatcher;
}
}
class SourceManager;
}
namespace ClangBackEnd {
class ClangQuery : public ClangTool
{
public:
ClangQuery(Utils::SmallString &&query={});
void setQuery(Utils::SmallString &&query);
void findLocations();
SourceRangesContainer takeSourceRanges();
std::vector<DynamicASTMatcherDiagnosticContainer> takeDiagnosticContainers();
private:
void parseDiagnostics(const clang::ast_matchers::dynamic::Diagnostics &diagnostics);
void matchLocation(const llvm::Optional< clang::ast_matchers::internal::DynTypedMatcher> &optionalStartMatcher,
std::unique_ptr<clang::ASTUnit> ast,
std::vector<clang::SourceRange> &sourceRanges);
private:
SourceRangesContainer sourceRangesContainer;
Utils::SmallString query;
std::vector<DynamicASTMatcherDiagnosticContainer> diagnosticContainers_;
};
} // namespace ClangBackEnd

View File

@@ -7,7 +7,9 @@ SOURCES += \
$$PWD/refactoringserver.cpp \
$$PWD/sourcefilecallbacks.cpp \
$$PWD/macropreprocessorcallbacks.cpp \
$$PWD/findusrforcursoraction.cpp
$$PWD/findusrforcursoraction.cpp \
$$PWD/clangquery.cpp \
$$PWD/clangtool.cpp
HEADERS += \
$$PWD/refactoringcompilationdatabase.h \
@@ -20,4 +22,6 @@ HEADERS += \
$$PWD/sourcelocationsutils.h \
$$PWD/findcursorusr.h \
$$PWD/findusrforcursoraction.h \
$$PWD/findlocationsofusrs.h
$$PWD/findlocationsofusrs.h \
$$PWD/clangquery.h \
$$PWD/clangtool.h

View File

@@ -0,0 +1,72 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangtool.h"
namespace ClangBackEnd {
namespace {
// use std::filesystem::path if it is supported by all compilers
std::string toNativePath(std::string &&path)
{
#ifdef WIN32
std::replace(path.begin(), path.end(), '/', '\\');
#endif
return std::move(path);
}
}
void ClangTool::addFile(std::string &&directory,
std::string &&fileName,
std::string &&content,
std::vector<std::string> &&commandLine)
{
fileContents.emplace_back(toNativePath(std::move(directory)),
std::move(fileName),
std::move(content),
std::move(commandLine));
const auto &fileContent = fileContents.back();
compilationDatabase.addFile(fileContent.directory, fileContent.fileName, fileContent.commandLine);
sourceFilePaths.push_back(fileContent.filePath);
}
clang::tooling::ClangTool ClangTool::createTool() const
{
clang::tooling::ClangTool tool(compilationDatabase, sourceFilePaths);
for (auto &&fileContent : fileContents) {
if (!fileContent.content.empty())
tool.mapVirtualFile(fileContent.filePath, fileContent.content);
}
return tool;
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,86 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "refactoringcompilationdatabase.h"
#include <clangrefactoringbackend_global.h>
#include <sourcelocationscontainer.h>
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#include "clang/Tooling/Refactoring.h"
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#include <string>
#include <vector>
namespace ClangBackEnd {
struct FileContent
{
FileContent(const std::string &directory,
const std::string &fileName,
const std::string &content,
const std::vector<std::string> &commandLine)
: directory(directory),
fileName(fileName),
filePath(directory + nativeSeperator + fileName),
content(content),
commandLine(commandLine)
{}
std::string directory;
std::string fileName;
std::string filePath;
std::string content;
std::vector<std::string> commandLine;
};
class ClangTool
{
public:
void addFile(std::string &&directory,
std::string &&fileName,
std::string &&content,
std::vector<std::string> &&commandLine);
clang::tooling::ClangTool createTool() const;
private:
RefactoringCompilationDatabase compilationDatabase;
std::vector<FileContent> fileContents;
std::vector<std::string> sourceFilePaths;
};
} // namespace ClangBackEnd

View File

@@ -26,18 +26,24 @@
#include "refactoringserver.h"
#include "symbolfinder.h"
#include "clangquery.h"
#include <refactoringclientinterface.h>
#include <requestsourcelocationforrenamingmessage.h>
#include <requestsourcerangesanddiagnosticsforquerymessage.h>
#include <sourcelocationsforrenamingmessage.h>
#include <sourcerangesanddiagnosticsforquerymessage.h>
#include <QCoreApplication>
#include <algorithm>
#include <chrono>
#include <future>
namespace ClangBackEnd {
RefactoringServer::RefactoringServer()
{
}
void RefactoringServer::end()
@@ -56,10 +62,86 @@ void RefactoringServer::requestSourceLocationsForRenamingMessage(RequestSourceLo
symbolFinder.findSymbol();
client()->sourceLocationsForRenamingMessage(SourceLocationsForRenamingMessage(
symbolFinder.takeSymbolName(),
symbolFinder.takeSourceLocations(),
message.textDocumentRevision()));
client()->sourceLocationsForRenamingMessage({symbolFinder.takeSymbolName(),
symbolFinder.takeSourceLocations(),
message.textDocumentRevision()});
}
namespace {
SourceRangesAndDiagnosticsForQueryMessage createSourceRangesAndDiagnosticsForQueryMessage(
V2::FileContainer &&fileContainer,
Utils::SmallString &&query) {
ClangQuery clangQuery(std::move(query));
clangQuery.addFile(fileContainer.filePath().directory(),
fileContainer.filePath().name(),
fileContainer.takeUnsavedFileContent(),
fileContainer.takeCommandLineArguments());
clangQuery.findLocations();
return {clangQuery.takeSourceRanges(), clangQuery.takeDiagnosticContainers()};
}
}
void RefactoringServer::requestSourceRangesAndDiagnosticsForQueryMessage(
RequestSourceRangesAndDiagnosticsForQueryMessage &&message)
{
gatherSourceRangesAndDiagnosticsForQueryMessage(message.takeFileContainers(), message.takeQuery());
}
void RefactoringServer::gatherSourceRangesAndDiagnosticsForQueryMessage(
std::vector<V2::FileContainer> &&fileContainers,
Utils::SmallString &&query)
{
std::vector<Future> futures;
std::size_t freeProcessors = std::thread::hardware_concurrency();
while (!fileContainers.empty() || !futures.empty()) {
--freeProcessors;
if (!fileContainers.empty()) {
Future &&future = std::async(std::launch::async,
createSourceRangesAndDiagnosticsForQueryMessage,
std::move(fileContainers.back()),
query.clone());
fileContainers.pop_back();
futures.emplace_back(std::move(future));
}
if (freeProcessors == 0 || fileContainers.empty())
freeProcessors += waitForNewSourceRangesAndDiagnosticsForQueryMessage(futures);
}
}
std::size_t RefactoringServer::waitForNewSourceRangesAndDiagnosticsForQueryMessage(std::vector<Future> &futures)
{
while (true) {
std::vector<Future> readyFutures;
readyFutures.reserve(futures.size());
auto beginReady = std::partition(futures.begin(),
futures.end(),
[] (const Future &future) {
return future.wait_for(std::chrono::duration<int>::zero()) != std::future_status::ready;
});
std::move(beginReady, futures.end(), std::back_inserter(readyFutures));
futures.erase(beginReady, futures.end());
for (Future &readyFuture : readyFutures)
client()->sourceRangesAndDiagnosticsForQueryMessage(std::move(readyFuture.get()));
if (readyFutures.empty())
std::this_thread::sleep_for(std::chrono::milliseconds(20));
else
return readyFutures.size();
}
}
} // namespace ClangBackEnd

View File

@@ -23,22 +23,35 @@
**
****************************************************************************/
#ifndef CLANGBACKEND_REFACTORINGSERVER_H
#define CLANGBACKEND_REFACTORINGSERVER_H
#pragma once
#include <refactoringserverinterface.h>
#include <future>
#include <vector>
namespace ClangBackEnd {
class SourceRangesAndDiagnosticsForQueryMessage;
namespace V2 {
class FileContainer;
}
class RefactoringServer : public RefactoringServerInterface
{
using Future = std::future<SourceRangesAndDiagnosticsForQueryMessage>;
public:
RefactoringServer();
void end() override;
void requestSourceLocationsForRenamingMessage(RequestSourceLocationsForRenamingMessage &&message) override;
void requestSourceRangesAndDiagnosticsForQueryMessage(RequestSourceRangesAndDiagnosticsForQueryMessage &&message) override;
private:
void gatherSourceRangesAndDiagnosticsForQueryMessage(std::vector<V2::FileContainer> &&fileContainers,
Utils::SmallString &&query);
std::size_t waitForNewSourceRangesAndDiagnosticsForQueryMessage(std::vector<Future> &futures);
};
} // namespace ClangBackEnd
#endif // CLANGBACKEND_REFACTORINGSERVER_H

View File

@@ -26,6 +26,7 @@
#pragma once
#include <sourcelocationscontainer.h>
#include <sourcerangescontainer.h>
#if defined(__GNUC__)
#pragma GCC diagnostic push
@@ -40,6 +41,8 @@
#pragma GCC diagnostic pop
#endif
#include <cctype>
namespace ClangBackEnd {
inline
@@ -65,6 +68,75 @@ Utils::SmallString fromNativePath(Container container)
return path;
}
inline Utils::SmallString getSourceText(const clang::FullSourceLoc &startFullSourceLocation,
uint startOffset,
uint endOffset)
{
auto startBuffer = startFullSourceLocation.getBufferData();
const auto bufferSize = endOffset - startOffset;
return Utils::SmallString(startBuffer.data() + startOffset, bufferSize + 1);
}
inline void makePrintable(Utils::SmallString &text)
{
text.replace("\n", " ");
text.replace("\t", " ");
auto end = std::unique(text.begin(), text.end(), [](char l, char r){
return std::isspace(l) && std::isspace(r) && l == r;
});
text.resize(std::distance(text.begin(), end));
}
inline void appendSourceRangeToSourceRangesContainer(
const clang::SourceRange &sourceRange,
ClangBackEnd::SourceRangesContainer &sourceRangesContainer,
const clang::SourceManager &sourceManager)
{
clang::FullSourceLoc startFullSourceLocation(sourceRange.getBegin(), sourceManager);
clang::FullSourceLoc endFullSourceLocation(sourceRange.getEnd(), sourceManager);
if (startFullSourceLocation.isFileID() && endFullSourceLocation.isFileID()) {
const auto startDecomposedLoction = startFullSourceLocation.getDecomposedLoc();
const auto endDecomposedLoction = endFullSourceLocation.getDecomposedLoc();
const auto fileId = startDecomposedLoction.first;
const auto startOffset = startDecomposedLoction.second;
const auto endOffset = endDecomposedLoction.second;
const auto fileEntry = sourceManager.getFileEntryForID(fileId);
auto filePath = absolutePath(fileEntry->getName());
const auto fileName = llvm::sys::path::filename(filePath);
llvm::sys::path::remove_filename(filePath);
Utils::SmallString content = getSourceText(startFullSourceLocation,
startOffset,
endOffset);
makePrintable(content);
sourceRangesContainer.insertFilePath(fileId.getHashValue(),
fromNativePath(filePath),
fromNativePath(fileName));
sourceRangesContainer.insertSourceRange(fileId.getHashValue(),
startFullSourceLocation.getSpellingLineNumber(),
startFullSourceLocation.getSpellingColumnNumber(),
startOffset,
endFullSourceLocation.getSpellingLineNumber(),
endFullSourceLocation.getSpellingColumnNumber(),
endOffset,
std::move(content));
}
}
inline
void appendSourceRangesToSourceRangesContainer(
ClangBackEnd::SourceRangesContainer &sourceRangesContainer,
const std::vector<clang::SourceRange> &sourceRanges,
const clang::SourceManager &sourceManager)
{
sourceRangesContainer.reserve(sourceRanges.size());
for (const auto &sourceRange : sourceRanges)
appendSourceRangeToSourceRangesContainer(sourceRange, sourceRangesContainer, sourceManager);
}
inline
void appendSourceLocationsToSourceLocationsContainer(
ClangBackEnd::SourceLocationsContainer &sourceLocationsContainer,
@@ -75,10 +147,12 @@ void appendSourceLocationsToSourceLocationsContainer(
for (const auto &sourceLocation : sourceLocations) {
clang::FullSourceLoc fullSourceLocation(sourceLocation, sourceManager);
auto fileId = fullSourceLocation.getFileID();
auto fileEntry = sourceManager.getFileEntryForID(fileId);
const auto decomposedLoction = fullSourceLocation.getDecomposedLoc();
const auto fileId = decomposedLoction.first;
const auto offset = decomposedLoction.second;
const auto fileEntry = sourceManager.getFileEntryForID(fileId);
auto filePath = absolutePath(fileEntry->getName());
auto fileName = llvm::sys::path::filename(filePath);
const auto fileName = llvm::sys::path::filename(filePath);
llvm::sys::path::remove_filename(filePath);
sourceLocationsContainer.insertFilePath(fileId.getHashValue(),
@@ -86,8 +160,11 @@ void appendSourceLocationsToSourceLocationsContainer(
fromNativePath(fileName));
sourceLocationsContainer.insertSourceLocation(fileId.getHashValue(),
fullSourceLocation.getSpellingLineNumber(),
fullSourceLocation.getSpellingColumnNumber());
fullSourceLocation.getSpellingColumnNumber(),
offset);
}
}
} // namespace ClangBackEnd

View File

@@ -25,7 +25,6 @@
#include "symbolfinder.h"
#include "refactoringcompilationdatabase.h"
#include "sourcefilecallbacks.h"
#include "symbollocationfinderaction.h"
@@ -37,49 +36,9 @@ SymbolFinder::SymbolFinder(uint line, uint column)
{
}
namespace {
// use std::filesystem::path if it is supported by all compilers
std::string toNativePath(std::string &&path)
{
#ifdef WIN32
std::replace(path.begin(), path.end(), '/', '\\');
#endif
return std::move(path);
}
}
void SymbolFinder::addFile(std::string &&directory,
std::string &&fileName,
std::string &&content,
std::vector<std::string> &&commandLine)
{
fileContents.emplace_back(toNativePath(std::move(directory)),
std::move(fileName),
std::move(content),
std::move(commandLine));
}
void SymbolFinder::findSymbol()
{
RefactoringCompilationDatabase compilationDatabase;
for (auto &&fileContent : fileContents)
compilationDatabase.addFile(fileContent.directory, fileContent.fileName, fileContent.commandLine);
std::vector<std::string> files;
for (auto &&fileContent : fileContents)
files.push_back(fileContent.filePath);
clang::tooling::ClangTool tool(compilationDatabase, files);
for (auto &&fileContent : fileContents) {
if (!fileContent.content.empty())
tool.mapVirtualFile(fileContent.filePath, fileContent.content);
}
clang::tooling::ClangTool tool = createTool();
tool.run(clang::tooling::newFrontendActionFactory(&usrFindingAction, &sourceFileCallbacks).get());

View File

@@ -25,6 +25,7 @@
#pragma once
#include "clangtool.h"
#include "findusrforcursoraction.h"
#include "symbollocationfinderaction.h"
#include "sourcefilecallbacks.h"
@@ -42,42 +43,13 @@
#pragma GCC diagnostic pop
#endif
#include <string>
#include <vector>
namespace ClangBackEnd {
struct FileContent
{
FileContent(const std::string &directory,
const std::string &fileName,
const std::string &content,
const std::vector<std::string> &commandLine)
: directory(directory),
fileName(fileName),
filePath(directory + nativeSeperator + fileName),
content(content),
commandLine(commandLine)
{}
std::string directory;
std::string fileName;
std::string filePath;
std::string content;
std::vector<std::string> commandLine;
};
class SymbolFinder
class SymbolFinder : public ClangTool
{
public:
SymbolFinder(uint line, uint column);
void addFile(std::string &&directory,
std::string &&fileName,
std::string &&content,
std::vector<std::string> &&commandLine);
void findSymbol();
Utils::SmallString takeSymbolName();
@@ -90,7 +62,7 @@ private:
USRFindingAction usrFindingAction;
SymbolLocationFinderAction symbolLocationFinderAction;
SourceFileCallbacks sourceFileCallbacks;
std::vector<FileContent> fileContents;
ClangBackEnd::SourceLocationsContainer sourceLocations_;
};