forked from qt-creator/qt-creator
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:
225
src/tools/clangrefactoringbackend/source/clangquery.cpp
Normal file
225
src/tools/clangrefactoringbackend/source/clangquery.cpp
Normal 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
|
||||
73
src/tools/clangrefactoringbackend/source/clangquery.h
Normal file
73
src/tools/clangrefactoringbackend/source/clangquery.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
72
src/tools/clangrefactoringbackend/source/clangtool.cpp
Normal file
72
src/tools/clangrefactoringbackend/source/clangtool.cpp
Normal 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
|
||||
86
src/tools/clangrefactoringbackend/source/clangtool.h
Normal file
86
src/tools/clangrefactoringbackend/source/clangtool.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user