mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
LSP: Implements goto-definition.
This commit is contained in:
@@ -155,10 +155,10 @@ set(sources
|
||||
interface/StorageLayout.h
|
||||
interface/Version.cpp
|
||||
interface/Version.h
|
||||
lsp/LanguageServer.cpp
|
||||
lsp/LanguageServer.h
|
||||
lsp/FileRepository.cpp
|
||||
lsp/FileRepository.h
|
||||
lsp/GotoDefinition.cpp
|
||||
lsp/GotoDefinition.h
|
||||
lsp/HandlerBase.cpp
|
||||
lsp/HandlerBase.h
|
||||
lsp/LanguageServer.cpp
|
||||
|
||||
@@ -18,12 +18,35 @@
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/ASTUtils.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
|
||||
#include <libsolutil/Algorithms.h>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
ASTNode const* locateInnermostASTNode(int _offsetInFile, SourceUnit const& _sourceUnit)
|
||||
{
|
||||
ASTNode const* innermostMatch = nullptr;
|
||||
auto locator = SimpleASTVisitor(
|
||||
[&](ASTNode const& _node) -> bool
|
||||
{
|
||||
// In the AST parent location always covers the whole child location.
|
||||
// The parent is visited first so to get the innermost node we simply
|
||||
// take the last one that still contains the offset.
|
||||
|
||||
if (!_node.location().containsOffset(_offsetInFile))
|
||||
return false;
|
||||
|
||||
innermostMatch = &_node;
|
||||
return true;
|
||||
},
|
||||
[](ASTNode const&) {}
|
||||
);
|
||||
_sourceUnit.accept(locator);
|
||||
return innermostMatch;
|
||||
}
|
||||
|
||||
bool isConstantVariableRecursive(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
solAssert(_varDecl.isConstant(), "Constant variable expected");
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
class VariableDeclaration;
|
||||
class ASTNode;
|
||||
class Declaration;
|
||||
class Expression;
|
||||
class SourceUnit;
|
||||
class VariableDeclaration;
|
||||
|
||||
/// Find the topmost referenced constant variable declaration when the given variable
|
||||
/// declaration value is an identifier. Works only for constant variable declarations.
|
||||
@@ -33,4 +35,7 @@ VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration cons
|
||||
/// Returns true if the constant variable declaration is recursive.
|
||||
bool isConstantVariableRecursive(VariableDeclaration const& _varDecl);
|
||||
|
||||
/// Returns the innermost AST node that covers the given location or nullptr if not found.
|
||||
ASTNode const* locateInnermostASTNode(int _offsetInFile, SourceUnit const& _sourceUnit);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
#include <libsolidity/lsp/GotoDefinition.h>
|
||||
#include <libsolidity/lsp/Transport.h> // for RequestError
|
||||
#include <libsolidity/lsp/Utils.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/ASTUtils.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::lsp;
|
||||
using namespace std;
|
||||
|
||||
void GotoDefinition::operator()(MessageID _id, Json::Value const& _args)
|
||||
{
|
||||
auto const [sourceUnitName, lineColumn] = extractSourceUnitNameAndLineColumn(_args);
|
||||
|
||||
ASTNode const* sourceNode = m_server.astNodeAtSourceLocation(sourceUnitName, lineColumn);
|
||||
|
||||
vector<SourceLocation> locations;
|
||||
if (auto const* expression = dynamic_cast<Expression const*>(sourceNode))
|
||||
{
|
||||
// Handles all expressions that can have one or more declaration annotation.
|
||||
if (auto const* declaration = referencedDeclaration(expression))
|
||||
if (auto location = declarationLocation(declaration))
|
||||
locations.emplace_back(move(location.value()));
|
||||
}
|
||||
else if (auto const* identifierPath = dynamic_cast<IdentifierPath const*>(sourceNode))
|
||||
{
|
||||
if (auto const* declaration = identifierPath->annotation().referencedDeclaration)
|
||||
if (auto location = declarationLocation(declaration))
|
||||
locations.emplace_back(move(location.value()));
|
||||
}
|
||||
else if (auto const* importDirective = dynamic_cast<ImportDirective const*>(sourceNode))
|
||||
{
|
||||
auto const& path = *importDirective->annotation().absolutePath;
|
||||
if (fileRepository().sourceUnits().count(path))
|
||||
locations.emplace_back(SourceLocation{0, 0, make_shared<string const>(path)});
|
||||
}
|
||||
|
||||
Json::Value reply = Json::arrayValue;
|
||||
for (SourceLocation const& location: locations)
|
||||
reply.append(toJson(location));
|
||||
client().reply(_id, reply);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
#include <libsolidity/lsp/HandlerBase.h>
|
||||
|
||||
namespace solidity::lsp
|
||||
{
|
||||
|
||||
class GotoDefinition: public HandlerBase
|
||||
{
|
||||
public:
|
||||
explicit GotoDefinition(LanguageServer& _server): HandlerBase(_server) {}
|
||||
|
||||
void operator()(MessageID, Json::Value const&);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
#include <libsolutil/Exceptions.h>
|
||||
|
||||
#include <libsolidity/lsp/HandlerBase.h>
|
||||
#include <libsolidity/lsp/LanguageServer.h>
|
||||
#include <libsolidity/lsp/Utils.h>
|
||||
@@ -5,13 +24,13 @@
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::lsp;
|
||||
using namespace solidity::util;
|
||||
using namespace std;
|
||||
|
||||
namespace solidity::lsp
|
||||
{
|
||||
|
||||
using namespace langutil;
|
||||
|
||||
Json::Value HandlerBase::toRange(SourceLocation const& _location) const
|
||||
{
|
||||
if (!_location.hasText())
|
||||
@@ -33,31 +52,27 @@ Json::Value HandlerBase::toJson(SourceLocation const& _location) const
|
||||
return item;
|
||||
}
|
||||
|
||||
optional<SourceLocation> HandlerBase::parsePosition(string const& _sourceUnitName, Json::Value const& _position) const
|
||||
pair<string, LineColumn> HandlerBase::extractSourceUnitNameAndLineColumn(Json::Value const& _args) const
|
||||
{
|
||||
if (!fileRepository().sourceUnits().count(_sourceUnitName))
|
||||
return nullopt;
|
||||
string const uri = _args["textDocument"]["uri"].asString();
|
||||
string const sourceUnitName = fileRepository().clientPathToSourceUnitName(uri);
|
||||
if (!fileRepository().sourceUnits().count(sourceUnitName))
|
||||
BOOST_THROW_EXCEPTION(
|
||||
RequestError(ErrorCode::RequestFailed) <<
|
||||
errinfo_comment("Unknown file: " + uri)
|
||||
);
|
||||
|
||||
if (optional<LineColumn> lineColumn = parseLineColumn(_position))
|
||||
if (optional<int> const offset = CharStream::translateLineColumnToPosition(
|
||||
fileRepository().sourceUnits().at(_sourceUnitName),
|
||||
*lineColumn
|
||||
))
|
||||
return SourceLocation{*offset, *offset, make_shared<string>(_sourceUnitName)};
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
optional<SourceLocation> HandlerBase::parseRange(string const& _sourceUnitName, Json::Value const& _range) const
|
||||
{
|
||||
if (!_range.isObject())
|
||||
return nullopt;
|
||||
optional<SourceLocation> start = parsePosition(_sourceUnitName, _range["start"]);
|
||||
optional<SourceLocation> end = parsePosition(_sourceUnitName, _range["end"]);
|
||||
if (!start || !end)
|
||||
return nullopt;
|
||||
solAssert(*start->sourceName == *end->sourceName);
|
||||
start->end = end->end;
|
||||
return start;
|
||||
}
|
||||
auto const lineColumn = parseLineColumn(_args["position"]);
|
||||
if (!lineColumn)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
RequestError(ErrorCode::RequestFailed) <<
|
||||
errinfo_comment(fmt::format(
|
||||
"Unknown position {line}:{column} in file: {file}",
|
||||
fmt::arg("line", lineColumn.value().line),
|
||||
fmt::arg("column", lineColumn.value().column),
|
||||
fmt::arg("file", sourceUnitName)
|
||||
))
|
||||
);
|
||||
|
||||
return {sourceUnitName, *lineColumn};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/lsp/FileRepository.h>
|
||||
@@ -24,22 +41,15 @@ public:
|
||||
Json::Value toRange(langutil::SourceLocation const& _location) const;
|
||||
Json::Value toJson(langutil::SourceLocation const& _location) const;
|
||||
|
||||
std::optional<langutil::SourceLocation> parsePosition(
|
||||
std::string const& _sourceUnitName,
|
||||
Json::Value const& _position
|
||||
) const;
|
||||
/// @returns source unit name and the line column position as extracted
|
||||
/// from the JSON-RPC parameters.
|
||||
std::pair<std::string, langutil::LineColumn> extractSourceUnitNameAndLineColumn(Json::Value const& _params) const;
|
||||
|
||||
/// @returns the source location given a source unit name and an LSP Range object,
|
||||
/// or nullopt on failure.
|
||||
std::optional<langutil::SourceLocation> parseRange(
|
||||
std::string const& _sourceUnitName,
|
||||
Json::Value const& _range
|
||||
) const;
|
||||
|
||||
langutil::CharStreamProvider const& charStreamProvider() const noexcept { return m_server.charStreamProvider(); };
|
||||
FileRepository const& fileRepository() const noexcept { return m_server.fileRepository(); };
|
||||
Transport& client() const noexcept { return m_server.client(); };
|
||||
langutil::CharStreamProvider const& charStreamProvider() const noexcept { return m_server.charStreamProvider(); }
|
||||
FileRepository const& fileRepository() const noexcept { return m_server.fileRepository(); }
|
||||
Transport& client() const noexcept { return m_server.client(); }
|
||||
|
||||
protected:
|
||||
LanguageServer& m_server;
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <libsolidity/lsp/HandlerBase.h>
|
||||
#include <libsolidity/lsp/Utils.h>
|
||||
|
||||
// LSP feature implementations
|
||||
#include <libsolidity/lsp/GotoDefinition.h>
|
||||
|
||||
#include <liblangutil/SourceReferenceExtractor.h>
|
||||
#include <liblangutil/CharStream.h>
|
||||
@@ -75,9 +77,11 @@ LanguageServer::LanguageServer(Transport& _transport):
|
||||
{"initialize", bind(&LanguageServer::handleInitialize, this, _1, _2)},
|
||||
{"initialized", [](auto, auto) {}},
|
||||
{"shutdown", [this](auto, auto) { m_state = State::ShutdownRequested; }},
|
||||
{"textDocument/definition", GotoDefinition(*this) },
|
||||
{"textDocument/didOpen", bind(&LanguageServer::handleTextDocumentDidOpen, this, _2)},
|
||||
{"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _2)},
|
||||
{"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _2)},
|
||||
{"textDocument/implementation", GotoDefinition(*this) },
|
||||
{"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _2)},
|
||||
},
|
||||
m_fileRepository("/" /* basePath */),
|
||||
@@ -85,11 +89,6 @@ LanguageServer::LanguageServer(Transport& _transport):
|
||||
{
|
||||
}
|
||||
|
||||
optional<SourceLocation> LanguageServer::parseRange(string const& _sourceUnitName, Json::Value const& _range)
|
||||
{
|
||||
return HandlerBase{*this}.parseRange(_sourceUnitName, _range);
|
||||
}
|
||||
|
||||
Json::Value LanguageServer::toRange(SourceLocation const& _location)
|
||||
{
|
||||
return HandlerBase(*this).toRange(_location);
|
||||
@@ -258,8 +257,10 @@ void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args)
|
||||
Json::Value replyArgs;
|
||||
replyArgs["serverInfo"]["name"] = "solc";
|
||||
replyArgs["serverInfo"]["version"] = string(VersionNumber);
|
||||
replyArgs["capabilities"]["textDocumentSync"]["openClose"] = true;
|
||||
replyArgs["capabilities"]["definitionProvider"] = true;
|
||||
replyArgs["capabilities"]["implementationProvider"] = true;
|
||||
replyArgs["capabilities"]["textDocumentSync"]["change"] = 2; // 0=none, 1=full, 2=incremental
|
||||
replyArgs["capabilities"]["textDocumentSync"]["openClose"] = true;
|
||||
|
||||
m_client.reply(_id, move(replyArgs));
|
||||
}
|
||||
@@ -313,7 +314,7 @@ void LanguageServer::handleTextDocumentDidChange(Json::Value const& _args)
|
||||
string text = jsonContentChange["text"].asString();
|
||||
if (jsonContentChange["range"].isObject()) // otherwise full content update
|
||||
{
|
||||
optional<SourceLocation> change = parseRange(sourceUnitName, jsonContentChange["range"]);
|
||||
optional<SourceLocation> change = parseRange(m_fileRepository, sourceUnitName, jsonContentChange["range"]);
|
||||
lspAssert(
|
||||
change && change->hasText(),
|
||||
ErrorCode::RequestFailed,
|
||||
@@ -346,7 +347,7 @@ void LanguageServer::handleTextDocumentDidClose(Json::Value const& _args)
|
||||
compileAndUpdateDiagnostics();
|
||||
}
|
||||
|
||||
ASTNode const* LanguageServer::requestASTNode(std::string const& _sourceUnitName, LineColumn const& _filePos)
|
||||
ASTNode const* LanguageServer::astNodeAtSourceLocation(std::string const& _sourceUnitName, LineColumn const& _filePos)
|
||||
{
|
||||
if (m_compilerStack.state() < CompilerStack::AnalysisPerformed)
|
||||
return nullptr;
|
||||
@@ -354,11 +355,10 @@ ASTNode const* LanguageServer::requestASTNode(std::string const& _sourceUnitName
|
||||
if (!m_fileRepository.sourceUnits().count(_sourceUnitName))
|
||||
return nullptr;
|
||||
|
||||
optional<int> sourcePos = m_compilerStack.charStream(_sourceUnitName)
|
||||
.translateLineColumnToPosition(_filePos);
|
||||
if (!sourcePos.has_value())
|
||||
if (optional<int> sourcePos =
|
||||
m_compilerStack.charStream(_sourceUnitName).translateLineColumnToPosition(_filePos))
|
||||
return locateInnermostASTNode(*sourcePos, m_compilerStack.ast(_sourceUnitName));
|
||||
else
|
||||
return nullptr;
|
||||
|
||||
return locateInnermostASTNode(*sourcePos, m_compilerStack.ast(_sourceUnitName));
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
|
||||
FileRepository& fileRepository() noexcept { return m_fileRepository; }
|
||||
Transport& client() noexcept { return m_client; }
|
||||
frontend::ASTNode const* requestASTNode(std::string const& _sourceUnitName, langutil::LineColumn const& _filePos);
|
||||
frontend::ASTNode const* astNodeAtSourceLocation(std::string const& _sourceUnitName, langutil::LineColumn const& _filePos);
|
||||
langutil::CharStreamProvider const& charStreamProvider() const noexcept { return m_compilerStack; }
|
||||
|
||||
private:
|
||||
@@ -71,6 +71,7 @@ private:
|
||||
void handleTextDocumentDidOpen(Json::Value const& _args);
|
||||
void handleTextDocumentDidChange(Json::Value const& _args);
|
||||
void handleTextDocumentDidClose(Json::Value const& _args);
|
||||
void handleGotoDefinition(MessageID _id, Json::Value const& _args);
|
||||
|
||||
/// Invoked when the server user-supplied configuration changes (initiated by the client).
|
||||
void changeConfiguration(Json::Value const&);
|
||||
@@ -79,12 +80,6 @@ private:
|
||||
void compile();
|
||||
using MessageHandler = std::function<void(MessageID, Json::Value const&)>;
|
||||
|
||||
/// @returns the source location given a source unit name and an LSP Range object,
|
||||
/// or nullopt on failure.
|
||||
std::optional<langutil::SourceLocation> parseRange(
|
||||
std::string const& _sourceUnitName,
|
||||
Json::Value const& _range
|
||||
);
|
||||
Json::Value toRange(langutil::SourceLocation const& _location);
|
||||
Json::Value toJson(langutil::SourceLocation const& _location);
|
||||
|
||||
|
||||
+62
-14
@@ -1,9 +1,30 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#include <liblangutil/CharStreamProvider.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/lsp/FileRepository.h>
|
||||
#include <libsolidity/lsp/Utils.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
|
||||
namespace solidity::lsp
|
||||
{
|
||||
|
||||
@@ -19,7 +40,7 @@ optional<LineColumn> parseLineColumn(Json::Value const& _lineColumn)
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
Json::Value toJson(LineColumn _pos)
|
||||
Json::Value toJson(LineColumn const& _pos)
|
||||
{
|
||||
Json::Value json = Json::objectValue;
|
||||
json["line"] = max(_pos.line, 0);
|
||||
@@ -36,24 +57,20 @@ Json::Value toJsonRange(LineColumn const& _start, LineColumn const& _end)
|
||||
return json;
|
||||
}
|
||||
|
||||
vector<Declaration const*> allAnnotatedDeclarations(Expression const* _expression)
|
||||
Declaration const* referencedDeclaration(Expression const* _expression)
|
||||
{
|
||||
vector<Declaration const*> output;
|
||||
|
||||
if (auto const* identifier = dynamic_cast<Identifier const*>(_expression))
|
||||
{
|
||||
output.push_back(identifier->annotation().referencedDeclaration);
|
||||
output += identifier->annotation().candidateDeclarations;
|
||||
}
|
||||
else if (auto const* memberAccess = dynamic_cast<MemberAccess const*>(_expression))
|
||||
{
|
||||
output.push_back(memberAccess->annotation().referencedDeclaration);
|
||||
}
|
||||
if (Declaration const* referencedDeclaration = identifier->annotation().referencedDeclaration)
|
||||
return referencedDeclaration;
|
||||
|
||||
return output;
|
||||
if (auto const* memberAccess = dynamic_cast<MemberAccess const*>(_expression))
|
||||
if (memberAccess->annotation().referencedDeclaration)
|
||||
return memberAccess->annotation().referencedDeclaration;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
optional<SourceLocation> declarationPosition(Declaration const* _declaration)
|
||||
optional<SourceLocation> declarationLocation(Declaration const* _declaration)
|
||||
{
|
||||
if (!_declaration)
|
||||
return nullopt;
|
||||
@@ -67,4 +84,35 @@ optional<SourceLocation> declarationPosition(Declaration const* _declaration)
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
optional<SourceLocation> parsePosition(
|
||||
FileRepository const& _fileRepository,
|
||||
string const& _sourceUnitName,
|
||||
Json::Value const& _position
|
||||
)
|
||||
{
|
||||
if (!_fileRepository.sourceUnits().count(_sourceUnitName))
|
||||
return nullopt;
|
||||
|
||||
if (optional<LineColumn> lineColumn = parseLineColumn(_position))
|
||||
if (optional<int> const offset = CharStream::translateLineColumnToPosition(
|
||||
_fileRepository.sourceUnits().at(_sourceUnitName),
|
||||
*lineColumn
|
||||
))
|
||||
return SourceLocation{*offset, *offset, make_shared<string>(_sourceUnitName)};
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
optional<SourceLocation> parseRange(FileRepository const& _fileRepository, string const& _sourceUnitName, Json::Value const& _range)
|
||||
{
|
||||
if (!_range.isObject())
|
||||
return nullopt;
|
||||
optional<SourceLocation> start = parsePosition(_fileRepository, _sourceUnitName, _range["start"]);
|
||||
optional<SourceLocation> end = parsePosition(_fileRepository, _sourceUnitName, _range["end"]);
|
||||
if (!start || !end)
|
||||
return nullopt;
|
||||
solAssert(*start->sourceName == *end->sourceName);
|
||||
start->end = end->end;
|
||||
return start;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+53
-3
@@ -1,3 +1,21 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
@@ -9,6 +27,13 @@
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#include <fstream>
|
||||
#define lspDebug(message) (std::ofstream("/tmp/solc.log", std::ios::app) << (message) << std::endl)
|
||||
#else
|
||||
#define lspDebug(message) do {} while (0)
|
||||
#endif
|
||||
|
||||
namespace solidity::langutil
|
||||
{
|
||||
class CharStreamProvider;
|
||||
@@ -20,10 +45,35 @@ namespace solidity::lsp
|
||||
class FileRepository;
|
||||
|
||||
std::optional<langutil::LineColumn> parseLineColumn(Json::Value const& _lineColumn);
|
||||
Json::Value toJson(langutil::LineColumn _pos);
|
||||
Json::Value toJson(langutil::LineColumn const& _pos);
|
||||
Json::Value toJsonRange(langutil::LineColumn const& _start, langutil::LineColumn const& _end);
|
||||
|
||||
std::vector<frontend::Declaration const*> allAnnotatedDeclarations(frontend::Expression const* _expression);
|
||||
std::optional<langutil::SourceLocation> declarationPosition(frontend::Declaration const* _declaration);
|
||||
/// @returns the source location given a source unit name and an LSP Range object,
|
||||
/// or nullopt on failure.
|
||||
std::optional<langutil::SourceLocation> parsePosition(
|
||||
FileRepository const& _fileRepository,
|
||||
std::string const& _sourceUnitName,
|
||||
Json::Value const& _position
|
||||
);
|
||||
|
||||
/// @returns the source location given a source unit name and an LSP Range object,
|
||||
/// or nullopt on failure.
|
||||
std::optional<langutil::SourceLocation> parseRange(
|
||||
FileRepository const& _fileRepository,
|
||||
std::string const& _sourceUnitName,
|
||||
Json::Value const& _range
|
||||
);
|
||||
|
||||
/// Extracts the resolved declaration of the given expression AST node.
|
||||
///
|
||||
/// This may for example be the type declaration of an identifier,
|
||||
/// or the type declaration of a structured member identifier.
|
||||
///
|
||||
/// @returns the resolved type declaration if found, or nullptr otherwise.
|
||||
frontend::Declaration const* referencedDeclaration(frontend::Expression const* _expression);
|
||||
|
||||
/// @returns the location of the declaration's name, if present, or the location of the complete
|
||||
/// declaration otherwise. If the input declaration is nullptr, std::nullopt is returned instead.
|
||||
std::optional<langutil::SourceLocation> declarationLocation(frontend::Declaration const* _declaration);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user