Skip to content

Commit 16a6981

Browse files
authored
Merge pull request #12430 from ethereum/lsp-goto-definition
LSP goto definition
2 parents db30f4d + 2b2f8ac commit 16a6981

16 files changed

+898
-96
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Compiler Features:
88
* JSON-AST: Added selector field for errors and events.
99
* Peephole Optimizer: Optimize comparisons in front of conditional jumps and conditional jumps across a single unconditional jump.
1010
* Yul Optimizer: Remove ``sstore`` and ``mstore`` operations that are never read from.
11+
* LSP: Implements goto-definition.
1112

1213
Bugfixes:
1314
* Yul IR Code Generation: Optimize embedded creation code with correct settings. This fixes potential mismatches between the constructor code of a contract compiled in isolation and the bytecode in ``type(C).creationCode``, resp. the bytecode used for ``new C(...)``.

libsolidity/CMakeLists.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,18 @@ set(sources
155155
interface/StorageLayout.h
156156
interface/Version.cpp
157157
interface/Version.h
158-
lsp/LanguageServer.cpp
159-
lsp/LanguageServer.h
160158
lsp/FileRepository.cpp
161159
lsp/FileRepository.h
160+
lsp/GotoDefinition.cpp
161+
lsp/GotoDefinition.h
162+
lsp/HandlerBase.cpp
163+
lsp/HandlerBase.h
164+
lsp/LanguageServer.cpp
165+
lsp/LanguageServer.h
162166
lsp/Transport.cpp
163167
lsp/Transport.h
168+
lsp/Utils.cpp
169+
lsp/Utils.h
164170
parsing/DocStringParser.cpp
165171
parsing/DocStringParser.h
166172
parsing/Parser.cpp

libsolidity/ast/ASTUtils.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,35 @@
1818

1919
#include <libsolidity/ast/AST.h>
2020
#include <libsolidity/ast/ASTUtils.h>
21+
#include <libsolidity/ast/ASTVisitor.h>
2122

2223
#include <libsolutil/Algorithms.h>
2324

2425
namespace solidity::frontend
2526
{
2627

28+
ASTNode const* locateInnermostASTNode(int _offsetInFile, SourceUnit const& _sourceUnit)
29+
{
30+
ASTNode const* innermostMatch = nullptr;
31+
auto locator = SimpleASTVisitor(
32+
[&](ASTNode const& _node) -> bool
33+
{
34+
// In the AST parent location always covers the whole child location.
35+
// The parent is visited first so to get the innermost node we simply
36+
// take the last one that still contains the offset.
37+
38+
if (!_node.location().containsOffset(_offsetInFile))
39+
return false;
40+
41+
innermostMatch = &_node;
42+
return true;
43+
},
44+
[](ASTNode const&) {}
45+
);
46+
_sourceUnit.accept(locator);
47+
return innermostMatch;
48+
}
49+
2750
bool isConstantVariableRecursive(VariableDeclaration const& _varDecl)
2851
{
2952
solAssert(_varDecl.isConstant(), "Constant variable expected");

libsolidity/ast/ASTUtils.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
namespace solidity::frontend
2222
{
2323

24-
class VariableDeclaration;
24+
class ASTNode;
2525
class Declaration;
2626
class Expression;
27+
class SourceUnit;
28+
class VariableDeclaration;
2729

2830
/// Find the topmost referenced constant variable declaration when the given variable
2931
/// declaration value is an identifier. Works only for constant variable declarations.
@@ -33,4 +35,7 @@ VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration cons
3335
/// Returns true if the constant variable declaration is recursive.
3436
bool isConstantVariableRecursive(VariableDeclaration const& _varDecl);
3537

38+
/// Returns the innermost AST node that covers the given location or nullptr if not found.
39+
ASTNode const* locateInnermostASTNode(int _offsetInFile, SourceUnit const& _sourceUnit);
40+
3641
}

libsolidity/lsp/GotoDefinition.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
// SPDX-License-Identifier: GPL-3.0
18+
#include <libsolidity/lsp/GotoDefinition.h>
19+
#include <libsolidity/lsp/Transport.h> // for RequestError
20+
#include <libsolidity/lsp/Utils.h>
21+
#include <libsolidity/ast/AST.h>
22+
#include <libsolidity/ast/ASTUtils.h>
23+
24+
#include <fmt/format.h>
25+
26+
#include <memory>
27+
#include <string>
28+
#include <vector>
29+
30+
using namespace solidity::frontend;
31+
using namespace solidity::langutil;
32+
using namespace solidity::lsp;
33+
using namespace std;
34+
35+
void GotoDefinition::operator()(MessageID _id, Json::Value const& _args)
36+
{
37+
auto const [sourceUnitName, lineColumn] = extractSourceUnitNameAndLineColumn(_args);
38+
39+
ASTNode const* sourceNode = m_server.astNodeAtSourceLocation(sourceUnitName, lineColumn);
40+
41+
vector<SourceLocation> locations;
42+
if (auto const* expression = dynamic_cast<Expression const*>(sourceNode))
43+
{
44+
// Handles all expressions that can have one or more declaration annotation.
45+
if (auto const* declaration = referencedDeclaration(expression))
46+
if (auto location = declarationLocation(declaration))
47+
locations.emplace_back(move(location.value()));
48+
}
49+
else if (auto const* identifierPath = dynamic_cast<IdentifierPath const*>(sourceNode))
50+
{
51+
if (auto const* declaration = identifierPath->annotation().referencedDeclaration)
52+
if (auto location = declarationLocation(declaration))
53+
locations.emplace_back(move(location.value()));
54+
}
55+
else if (auto const* importDirective = dynamic_cast<ImportDirective const*>(sourceNode))
56+
{
57+
auto const& path = *importDirective->annotation().absolutePath;
58+
if (fileRepository().sourceUnits().count(path))
59+
locations.emplace_back(SourceLocation{0, 0, make_shared<string const>(path)});
60+
}
61+
62+
Json::Value reply = Json::arrayValue;
63+
for (SourceLocation const& location: locations)
64+
reply.append(toJson(location));
65+
client().reply(_id, reply);
66+
}

libsolidity/lsp/GotoDefinition.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
// SPDX-License-Identifier: GPL-3.0
18+
#include <libsolidity/lsp/HandlerBase.h>
19+
20+
namespace solidity::lsp
21+
{
22+
23+
class GotoDefinition: public HandlerBase
24+
{
25+
public:
26+
explicit GotoDefinition(LanguageServer& _server): HandlerBase(_server) {}
27+
28+
void operator()(MessageID, Json::Value const&);
29+
};
30+
31+
}

libsolidity/lsp/HandlerBase.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
// SPDX-License-Identifier: GPL-3.0
18+
#include <libsolutil/Exceptions.h>
19+
20+
#include <libsolidity/lsp/HandlerBase.h>
21+
#include <libsolidity/lsp/LanguageServer.h>
22+
#include <libsolidity/lsp/Utils.h>
23+
#include <libsolidity/ast/AST.h>
24+
25+
#include <liblangutil/Exceptions.h>
26+
27+
#include <fmt/format.h>
28+
29+
using namespace solidity::langutil;
30+
using namespace solidity::lsp;
31+
using namespace solidity::util;
32+
using namespace std;
33+
34+
Json::Value HandlerBase::toRange(SourceLocation const& _location) const
35+
{
36+
if (!_location.hasText())
37+
return toJsonRange({}, {});
38+
39+
solAssert(_location.sourceName, "");
40+
langutil::CharStream const& stream = charStreamProvider().charStream(*_location.sourceName);
41+
LineColumn start = stream.translatePositionToLineColumn(_location.start);
42+
LineColumn end = stream.translatePositionToLineColumn(_location.end);
43+
return toJsonRange(start, end);
44+
}
45+
46+
Json::Value HandlerBase::toJson(SourceLocation const& _location) const
47+
{
48+
solAssert(_location.sourceName);
49+
Json::Value item = Json::objectValue;
50+
item["uri"] = fileRepository().sourceUnitNameToClientPath(*_location.sourceName);
51+
item["range"] = toRange(_location);
52+
return item;
53+
}
54+
55+
pair<string, LineColumn> HandlerBase::extractSourceUnitNameAndLineColumn(Json::Value const& _args) const
56+
{
57+
string const uri = _args["textDocument"]["uri"].asString();
58+
string const sourceUnitName = fileRepository().clientPathToSourceUnitName(uri);
59+
if (!fileRepository().sourceUnits().count(sourceUnitName))
60+
BOOST_THROW_EXCEPTION(
61+
RequestError(ErrorCode::RequestFailed) <<
62+
errinfo_comment("Unknown file: " + uri)
63+
);
64+
65+
auto const lineColumn = parseLineColumn(_args["position"]);
66+
if (!lineColumn)
67+
BOOST_THROW_EXCEPTION(
68+
RequestError(ErrorCode::RequestFailed) <<
69+
errinfo_comment(fmt::format(
70+
"Unknown position {line}:{column} in file: {file}",
71+
fmt::arg("line", lineColumn.value().line),
72+
fmt::arg("column", lineColumn.value().column),
73+
fmt::arg("file", sourceUnitName)
74+
))
75+
);
76+
77+
return {sourceUnitName, *lineColumn};
78+
}

libsolidity/lsp/HandlerBase.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
This file is part of solidity.
3+
4+
solidity is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
solidity is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with solidity. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
// SPDX-License-Identifier: GPL-3.0
18+
#pragma once
19+
20+
#include <libsolidity/lsp/FileRepository.h>
21+
#include <libsolidity/lsp/LanguageServer.h>
22+
23+
#include <liblangutil/SourceLocation.h>
24+
#include <liblangutil/CharStreamProvider.h>
25+
26+
#include <optional>
27+
28+
namespace solidity::lsp
29+
{
30+
31+
class Transport;
32+
33+
/**
34+
* Helper base class for implementing handlers.
35+
*/
36+
class HandlerBase
37+
{
38+
public:
39+
explicit HandlerBase(LanguageServer& _server): m_server{_server} {}
40+
41+
Json::Value toRange(langutil::SourceLocation const& _location) const;
42+
Json::Value toJson(langutil::SourceLocation const& _location) const;
43+
44+
/// @returns source unit name and the line column position as extracted
45+
/// from the JSON-RPC parameters.
46+
std::pair<std::string, langutil::LineColumn> extractSourceUnitNameAndLineColumn(Json::Value const& _params) const;
47+
48+
langutil::CharStreamProvider const& charStreamProvider() const noexcept { return m_server.charStreamProvider(); }
49+
FileRepository const& fileRepository() const noexcept { return m_server.fileRepository(); }
50+
Transport& client() const noexcept { return m_server.client(); }
51+
52+
protected:
53+
LanguageServer& m_server;
54+
};
55+
56+
}

0 commit comments

Comments
 (0)