diff --git a/Changelog.md b/Changelog.md index 98b36aab8..987e734e4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,7 @@ Compiler Features: * Code Generator: More efficient overflow checks for multiplication. * Language Server: Analyze all files in a project by default (can be customized by setting ``'file-load-strategy'`` to ``'directly-opened-and-on-import'`` in LSP settings object). * Yul Optimizer: Simplify the starting offset of zero-length operations to zero. + * Language Server: Implements finding all references as well as semantic highlighting of symbols. Bugfixes: diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 68e58e1c9..432cbe0f1 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -165,8 +165,14 @@ set(sources lsp/HandlerBase.h lsp/LanguageServer.cpp lsp/LanguageServer.h + lsp/SemanticHighlight.cpp + lsp/SemanticHighlight.h lsp/SemanticTokensBuilder.cpp lsp/SemanticTokensBuilder.h + lsp/ReferenceCollector.cpp + lsp/ReferenceCollector.h + lsp/References.cpp + lsp/References.h lsp/Transport.cpp lsp/Transport.h lsp/Utils.cpp diff --git a/libsolidity/lsp/LanguageServer.cpp b/libsolidity/lsp/LanguageServer.cpp index f868a022a..d79befea6 100644 --- a/libsolidity/lsp/LanguageServer.cpp +++ b/libsolidity/lsp/LanguageServer.cpp @@ -26,7 +26,9 @@ // LSP feature implementations #include +#include #include +#include #include #include @@ -145,8 +147,10 @@ LanguageServer::LanguageServer(Transport& _transport): {"textDocument/didChange", bind(&LanguageServer::handleTextDocumentDidChange, this, _2)}, {"textDocument/didClose", bind(&LanguageServer::handleTextDocumentDidClose, this, _2)}, {"textDocument/rename", RenameSymbol(*this) }, + {"textDocument/documentHighlight", SemanticHighlight(*this) }, {"textDocument/implementation", GotoDefinition(*this) }, {"textDocument/semanticTokens/full", bind(&LanguageServer::semanticTokensFull, this, _1, _2)}, + {"textDocument/references", References(*this) }, {"workspace/didChangeConfiguration", bind(&LanguageServer::handleWorkspaceDidChangeConfiguration, this, _2)}, }, m_fileRepository("/" /* basePath */, {} /* no search paths */), @@ -411,6 +415,8 @@ void LanguageServer::handleInitialize(MessageID _id, Json::Value const& _args) replyArgs["serverInfo"]["version"] = string(VersionNumber); replyArgs["capabilities"]["definitionProvider"] = true; replyArgs["capabilities"]["implementationProvider"] = true; + replyArgs["capabilities"]["documentHighlightProvider"] = true; + replyArgs["capabilities"]["referencesProvider"] = true; replyArgs["capabilities"]["textDocumentSync"]["change"] = 2; // 0=none, 1=full, 2=incremental replyArgs["capabilities"]["textDocumentSync"]["openClose"] = true; replyArgs["capabilities"]["semanticTokensProvider"]["legend"] = semanticTokensLegend(); diff --git a/libsolidity/lsp/LanguageServer.h b/libsolidity/lsp/LanguageServer.h index a05bec497..74b4728bf 100644 --- a/libsolidity/lsp/LanguageServer.h +++ b/libsolidity/lsp/LanguageServer.h @@ -77,6 +77,7 @@ public: FileRepository& fileRepository() noexcept { return m_fileRepository; } Transport& client() noexcept { return m_client; } + frontend::SourceUnit const& ast(std::string const& _sourceUnitName) const { return m_compilerStack.ast(_sourceUnitName); } frontend::ASTNode const* astNodeAtSourceLocation(std::string const& _sourceUnitName, langutil::LineColumn const& _filePos); frontend::CompilerStack const& compilerStack() const noexcept { return m_compilerStack; } diff --git a/libsolidity/lsp/ReferenceCollector.cpp b/libsolidity/lsp/ReferenceCollector.cpp new file mode 100644 index 000000000..1fc16ac4e --- /dev/null +++ b/libsolidity/lsp/ReferenceCollector.cpp @@ -0,0 +1,192 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#include +#include + +#include +#include + +#include + +using namespace solidity::frontend; +using namespace solidity::langutil; +using namespace std::string_literals; +using namespace std; + +namespace solidity::lsp +{ + +namespace +{ + +vector allAnnotatedDeclarations(Identifier const* _identifier) +{ + vector output; + output.push_back(_identifier->annotation().referencedDeclaration); + output += _identifier->annotation().candidateDeclarations; + return output; +} + +} + +ReferenceCollector::ReferenceCollector( + Declaration const& _declaration, + std::string const& _sourceIdentifierName +): + m_declaration{_declaration}, + m_sourceIdentifierName{_sourceIdentifierName.empty() ? _declaration.name() : _sourceIdentifierName} +{ +} + +std::vector ReferenceCollector::collect( + Declaration const* _declaration, + ASTNode const& _ast, + std::string const& _sourceIdentifierName +) +{ + if (!_declaration) + return {}; + + ReferenceCollector collector(*_declaration, _sourceIdentifierName); + _ast.accept(collector); + return std::move(collector.m_resultingReferences); +} + +std::vector ReferenceCollector::collect( + ASTNode const* _sourceNode, + SourceUnit const& _sourceUnit +) +{ + if (!_sourceNode) + return {}; + + auto output = vector{}; + + if (auto const* identifier = dynamic_cast(_sourceNode)) + { + for (auto const* declaration: allAnnotatedDeclarations(identifier)) + output += collect(declaration, _sourceUnit, identifier->name()); + } + else if (auto const* identifierPath = dynamic_cast(_sourceNode)) + { + solAssert(identifierPath->path().size() >= 1, ""); + output += collect(identifierPath->annotation().referencedDeclaration, _sourceUnit, identifierPath->path().back()); + } + else if (auto const* memberAccess = dynamic_cast(_sourceNode)) + output += collect(memberAccess->annotation().referencedDeclaration, _sourceUnit, memberAccess->memberName()); + else if (auto const* declaration = dynamic_cast(_sourceNode)) + output += collect(declaration, _sourceUnit, declaration->name()); + else + lspAssert(false, ErrorCode::InternalError, "Unhandled AST node "s + typeid(*_sourceNode).name()); + + return output; +} + +bool ReferenceCollector::visit(ImportDirective const& _import) +{ + if (_import.name() == m_sourceIdentifierName) + return true; + return false; +} + +void ReferenceCollector::endVisit(ImportDirective const& _import) +{ + for (auto const& symbolAlias: _import.symbolAliases()) + { + if ( + m_sourceIdentifierName == *symbolAlias.alias && + symbolAlias.symbol && + symbolAlias.symbol->annotation().referencedDeclaration == &m_declaration + ) + { + m_resultingReferences.emplace_back(symbolAlias.location, DocumentHighlightKind::Read); + break; + } + else if (m_sourceIdentifierName == *symbolAlias.alias) + { + m_resultingReferences.emplace_back(symbolAlias.location, DocumentHighlightKind::Text); + break; + } + } +} + +bool ReferenceCollector::tryAddReference(Declaration const* _declaration, SourceLocation const& _location) +{ + if (&m_declaration != _declaration) + return false; + + m_resultingReferences.emplace_back(_location, m_kind); + return true; +} + +void ReferenceCollector::endVisit(Identifier const& _identifier) +{ + if (auto const* declaration = _identifier.annotation().referencedDeclaration) + tryAddReference(declaration, _identifier.location()); + + for (auto const* declaration: _identifier.annotation().candidateDeclarations + _identifier.annotation().overloadedDeclarations) + tryAddReference(declaration, _identifier.location()); +} + + +void ReferenceCollector::endVisit(IdentifierPath const& _identifierPath) +{ + tryAddReference(_identifierPath.annotation().referencedDeclaration, _identifierPath.location()); +} + +void ReferenceCollector::endVisit(MemberAccess const& _memberAccess) +{ + if (_memberAccess.annotation().referencedDeclaration == &m_declaration) + m_resultingReferences.emplace_back(_memberAccess.location(), m_kind); +} + +bool ReferenceCollector::visit(Assignment const& _node) +{ + auto const restoreKind = ScopeGuard{[this, savedKind=m_kind]() { m_kind = savedKind; }}; + + m_kind = DocumentHighlightKind::Write; + _node.leftHandSide().accept(*this); + + m_kind = DocumentHighlightKind::Read; + _node.rightHandSide().accept(*this); + + return false; +} + +bool ReferenceCollector::visit(VariableDeclaration const& _node) +{ + if (&_node == &m_declaration) + m_resultingReferences.emplace_back(_node.nameLocation(), DocumentHighlightKind::Write); + + return true; +} + +bool ReferenceCollector::visitNode(ASTNode const& _node) +{ + if (&_node == &m_declaration) + { + if (auto const* declaration = dynamic_cast(&_node)) + m_resultingReferences.emplace_back(declaration->nameLocation(), m_kind); + else + m_resultingReferences.emplace_back(_node.location(), DocumentHighlightKind::Read); + } + + return true; +} + +} diff --git a/libsolidity/lsp/ReferenceCollector.h b/libsolidity/lsp/ReferenceCollector.h new file mode 100644 index 000000000..4242f237a --- /dev/null +++ b/libsolidity/lsp/ReferenceCollector.h @@ -0,0 +1,69 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include + +namespace solidity::lsp +{ + +enum class DocumentHighlightKind +{ + Unspecified = 0, //!< could be for example a highlight found in a comment + Text = 1, //!< a textual occurrence + Read = 2, //!< read access to a variable + Write = 3, //!< write access to a variable +}; + +// Represents a symbol / AST node that is to be highlighted, with some context associated. +using Reference = std::tuple; + +class ReferenceCollector: public frontend::ASTConstVisitor +{ +public: + ReferenceCollector(frontend::Declaration const& _declaration, std::string const& _sourceIdentifierName); + + static std::vector collect( + frontend::Declaration const* _declaration, + frontend::ASTNode const& _ast, + std::string const& _sourceIdentifierName + ); + + static std::vector collect(frontend::ASTNode const* _sourceNode, frontend::SourceUnit const& _sourceUnit); + + bool visit(frontend::ImportDirective const& _import) override; + void endVisit(frontend::ImportDirective const& _import) override; + void endVisit(frontend::Identifier const& _identifier) override; + void endVisit(frontend::IdentifierPath const& _identifierPath) override; + void endVisit(frontend::MemberAccess const& _memberAccess) override; + bool visit(frontend::Assignment const& _node) override; + bool visit(frontend::VariableDeclaration const& _node) override; + bool visitNode(frontend::ASTNode const& _node) override; + +private: + bool tryAddReference(frontend::Declaration const* _declaration, solidity::langutil::SourceLocation const& _location); + +private: + frontend::Declaration const& m_declaration; + std::string const& m_sourceIdentifierName; + std::vector m_resultingReferences; + DocumentHighlightKind m_kind = DocumentHighlightKind::Read; +}; + +} // end namespace diff --git a/libsolidity/lsp/References.cpp b/libsolidity/lsp/References.cpp new file mode 100644 index 000000000..b97290187 --- /dev/null +++ b/libsolidity/lsp/References.cpp @@ -0,0 +1,46 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#include +#include +#include +#include + +#include + +using namespace std; +using namespace solidity::langutil; +using namespace solidity::frontend; + +namespace solidity::lsp +{ + +void References::operator()(MessageID _id, Json::Value const& _args) +{ + auto const [sourceUnitName, lineColumn] = extractSourceUnitNameAndLineColumn(_args); + + ASTNode const* sourceNode = m_server.astNodeAtSourceLocation(sourceUnitName, lineColumn); + SourceUnit const& sourceUnit = m_server.ast(sourceUnitName); + + Json::Value jsonReply = Json::arrayValue; + for (auto const& reference: ReferenceCollector::collect(sourceNode, sourceUnit)) + jsonReply.append(toJson(get(reference))); + + client().reply(_id, jsonReply); +} + +} diff --git a/libsolidity/lsp/References.h b/libsolidity/lsp/References.h new file mode 100644 index 000000000..f61a75c1b --- /dev/null +++ b/libsolidity/lsp/References.h @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#include + +namespace solidity::lsp +{ + +class References: public HandlerBase +{ +public: + explicit References(LanguageServer& _server): HandlerBase(_server) {} + + void operator()(MessageID _id, Json::Value const& _args); +}; + +} diff --git a/libsolidity/lsp/SemanticHighlight.cpp b/libsolidity/lsp/SemanticHighlight.cpp new file mode 100644 index 000000000..efd9dc1a2 --- /dev/null +++ b/libsolidity/lsp/SemanticHighlight.cpp @@ -0,0 +1,52 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::lsp; +using namespace solidity::frontend; + +void SemanticHighlight::operator()(MessageID _id, Json::Value const& _args) +{ + auto const [sourceUnitName, lineColumn] = extractSourceUnitNameAndLineColumn(_args); + ASTNode const* sourceNode = m_server.astNodeAtSourceLocation(sourceUnitName, lineColumn); + + Json::Value jsonReply = Json::arrayValue; + for (auto const& [location, kind]: semanticHighlight(sourceNode, sourceUnitName)) + { + Json::Value item = Json::objectValue; + item["range"] = toRange(location); + if (kind != DocumentHighlightKind::Unspecified) + item["kind"] = static_cast(kind); + jsonReply.append(item); + } + client().reply(_id, jsonReply); +} + +vector SemanticHighlight::semanticHighlight(ASTNode const* _sourceNode, string const& _sourceUnitName) +{ + if (!_sourceNode) + return {}; + + SourceUnit const& sourceUnit = m_server.ast(_sourceUnitName); + + return ReferenceCollector::collect(_sourceNode, sourceUnit); +} + diff --git a/libsolidity/lsp/SemanticHighlight.h b/libsolidity/lsp/SemanticHighlight.h new file mode 100644 index 000000000..eb6fde2d9 --- /dev/null +++ b/libsolidity/lsp/SemanticHighlight.h @@ -0,0 +1,37 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#include +#include + +#include + +namespace solidity::lsp +{ + +class SemanticHighlight: public HandlerBase +{ +public: + SemanticHighlight(LanguageServer& _server): HandlerBase(_server) {} + + void operator()(MessageID _id, Json::Value const& _args); + +private: + std::vector semanticHighlight(frontend::ASTNode const* _sourceNode, std::string const& _sourceUnitName); +}; + +} diff --git a/test/libsolidity/lsp/references/enum.sol b/test/libsolidity/lsp/references/enum.sol new file mode 100644 index 000000000..38cc1e5c5 --- /dev/null +++ b/test/libsolidity/lsp/references/enum.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +enum Color { +// ^ @EnumDef +// ^^^^^ @ColorType0 + Red, +// ^ @RedDef +// ^^^ @Red1 + Green +} + +contract MyContract +{ + Color lastColor = Color.Red; +// ^ @lastCursorDef +// ^^^^^^^^^ @lastCursor1 +// ^^^^^^^^^ @Red2 +// ^^^^^ @ColorType1 +// ^^^^^ @ColorType2 + + function sum(Color a) public view returns (Color) +// ^^^^^ @ColorType3 +// ^^^^^ @ColorType4 + { + Color result = Color(a); +// ^^^^^ @ColorType5 +// ^^^^^ @ColorType6 + if (a != lastColor) +// ^^^^^^^^^ @lastCursor2 + result = Color.Green; +// ^^^^^ @ColorType7 + return result; + } + + function f() public pure returns (uint) + { + uint result = 42; + return result; + } +} + +// ---- +// -> textDocument/documentHighlight { +// "position": @EnumDef +// } +// <- [ +// { "kind": 2, "range": @ColorType0 }, +// { "kind": 2, "range": @ColorType1 }, +// { "kind": 2, "range": @ColorType2 }, +// { "kind": 2, "range": @ColorType3 }, +// { "kind": 2, "range": @ColorType4 }, +// { "kind": 2, "range": @ColorType5 }, +// { "kind": 2, "range": @ColorType6 }, +// { "kind": 2, "range": @ColorType7 } +// ] +// -> textDocument/documentHighlight { +// "position": @RedDef +// } +// <- [ +// { "kind": 2, "range": @Red1 }, +// { "kind": 2, "range": @Red2 } +// ] +// -> textDocument/documentHighlight { +// "position": @lastCursorDef +// } +// <- [ +// { "kind": 3, "range": @lastCursor1 }, +// { "kind": 2, "range": @lastCursor2 } +// ] diff --git a/test/libsolidity/lsp/references/import_aliases.sol b/test/libsolidity/lsp/references/import_aliases.sol new file mode 100644 index 000000000..b94cd55ae --- /dev/null +++ b/test/libsolidity/lsp/references/import_aliases.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +import "../goto/lib.sol" as Imported; +// ^ @ImportedDef + +contract C +{ + function one(uint lhs, uint rhs) public pure returns (uint result) + { + result = Imported.Lib.add(lhs, rhs + 123); + // ^ @ImportedUse + // ^^^^^^^^ @Imported1 + // ^^^^^^^^^^^^ @ImportedLib1 + } + function two(uint lhs, uint rhs) public pure returns (uint result) + { + result = one(Imported.Lib.add(lhs, rhs), rhs); + // ^ @LibRef + // ^^^^^^^^ @Imported2 + // ^^^^^^^^^^^^ @ImportedLib2 + } +} +// ---- +// lib: @diagnostics 2072 +// -> textDocument/documentHighlight { +// "position": @ImportedDef +// } +// <- [ +// { "kind": 2, "range": @Imported1 }, +// { "kind": 2, "range": @Imported2 } +// ] +// -> textDocument/documentHighlight { +// "position": @ImportedUse +// } +// <- [ +// { "kind": 2, "range": @Imported1 }, +// { "kind": 2, "range": @Imported2 } +// ] +// -> textDocument/documentHighlight { +// "position": @LibRef +// } +// <- [ +// { "kind": 2, "range": @ImportedLib1 }, +// { "kind": 2, "range": @ImportedLib2 } +// ] diff --git a/test/libsolidity/lsp/references/read_write_access_kinds.sol b/test/libsolidity/lsp/references/read_write_access_kinds.sol new file mode 100644 index 000000000..beda93181 --- /dev/null +++ b/test/libsolidity/lsp/references/read_write_access_kinds.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +import {RGBColor as ThatColor} from "../goto/lib.sol"; + +contract C +{ + function other() public pure returns (ThatColor memory output) + // ^^^^^^ @OutputDef + { + output.red = 50; + // ^^^^^^ @OutputWrite1 + // ^ @OutputWrite + output.green = output.red; + // ^^^^^^^^^^^^ @OutputGreenWrite1 + // ^^^^^^ @OutputWrite2 + // ^^^^^^ @OutputRead1 + // ^ @GreenWrite + output.blue = output.green; + // ^^^^^^^^^^^^ @OutputGreenRead1 + // ^^^^^^ @OutputWrite3 + // ^^^^^^ @OutputRead2 + // ^ @GreenUse + } +} + +// ---- +// lib: @diagnostics 2072 +// -> textDocument/documentHighlight { +// "position": @OutputWrite +// } +// <- [ +// { +// "kind": 3, +// "range": @OutputDef +// }, +// { +// "kind": 3, +// "range": @OutputWrite1 +// }, +// { +// "kind": 3, +// "range": @OutputWrite2 +// }, +// { +// "kind": 2, +// "range": @OutputRead1 +// }, +// { +// "kind": 3, +// "range": @OutputWrite3 +// }, +// { +// "kind": 2, +// "range": @OutputRead2 +// } +// ] +// -> textDocument/documentHighlight { +// "position": @GreenWrite +// } +// <- [ +// { +// "kind": 3, +// "range": @OutputGreenWrite1 +// }, +// { +// "kind": 2, +// "range": @OutputGreenRead1 +// } +// ] +// -> textDocument/documentHighlight { +// "position": @GreenUse +// } +// <- [ +// { +// "kind": 3, +// "range": @OutputGreenWrite1 +// }, +// { +// "kind": 2, +// "range": @OutputGreenRead1 +// } +// ] diff --git a/test/libsolidity/lsp/references/struct.sol b/test/libsolidity/lsp/references/struct.sol new file mode 100644 index 000000000..ac1f35c0c --- /dev/null +++ b/test/libsolidity/lsp/references/struct.sol @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +struct MyStruct +// ^ @MyStructDef +{ + uint value; +} + +contract MyContract +{ + MyStruct thatStruct; + // ^ @thatStructDef + + function f(MyStruct memory _someStruct) public view returns (uint result) + { + MyStruct memory local = _someStruct; + local.value = local.value + thatStruct.value; + // ^ @thatStructUse (TODO) + result = local.value; + // ^ @localUse + } + + function g(uint lhs, uint rhs) public pure returns (uint result) + { + uint output = lhs + rhs; + result = output; + } +} + +// ---- +// -> textDocument/documentHighlight { +// "position": @MyStructDef +// } +// <- [ +// { +// "kind": 2, +// "range": { +// "end": { +// "character": 15, +// "line": 3 +// }, +// "start": { +// "character": 7, +// "line": 3 +// } +// } +// }, +// { +// "kind": 2, +// "range": { +// "end": { +// "character": 12, +// "line": 11 +// }, +// "start": { +// "character": 4, +// "line": 11 +// } +// } +// }, +// { +// "kind": 2, +// "range": { +// "end": { +// "character": 23, +// "line": 14 +// }, +// "start": { +// "character": 15, +// "line": 14 +// } +// } +// }, +// { +// "kind": 2, +// "range": { +// "end": { +// "character": 16, +// "line": 16 +// }, +// "start": { +// "character": 8, +// "line": 16 +// } +// } +// } +// ] +// -> textDocument/documentHighlight { +// "position": @thatStructDef +// } +// <- [ +// { +// "kind": 3, +// "range": { +// "end": { +// "character": 23, +// "line": 11 +// }, +// "start": { +// "character": 13, +// "line": 11 +// } +// } +// }, +// { +// "kind": 2, +// "range": { +// "end": { +// "character": 46, +// "line": 17 +// }, +// "start": { +// "character": 36, +// "line": 17 +// } +// } +// } +// ] +// -> textDocument/documentHighlight { +// "position": @localUse +// } +// <- [ +// { +// "kind": 3, +// "range": { +// "end": { +// "character": 29, +// "line": 16 +// }, +// "start": { +// "character": 24, +// "line": 16 +// } +// } +// }, +// { +// "kind": 3, +// "range": { +// "end": { +// "character": 13, +// "line": 17 +// }, +// "start": { +// "character": 8, +// "line": 17 +// } +// } +// }, +// { +// "kind": 2, +// "range": { +// "end": { +// "character": 27, +// "line": 17 +// }, +// "start": { +// "character": 22, +// "line": 17 +// } +// } +// }, +// { +// "kind": 2, +// "range": { +// "end": { +// "character": 22, +// "line": 19 +// }, +// "start": { +// "character": 17, +// "line": 19 +// } +// } +// } +// ] diff --git a/test/libsolidity/lsp/references/symbol_aliases.sol b/test/libsolidity/lsp/references/symbol_aliases.sol new file mode 100644 index 000000000..c7e551efe --- /dev/null +++ b/test/libsolidity/lsp/references/symbol_aliases.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +import { + Lib as TheLib, + RGBColor as ThatColor +} from "../goto/lib.sol"; + +contract C +{ + function one(uint lhs, uint rhs) public pure returns (uint result) + { + result = TheLib.add(lhs, rhs + 123); + // ^ @TheLibUse + } + + function other() public pure returns (ThatColor memory output) + // ^ @ThatColorRet + { + output.red = 50; + output.green = output.red; + output.blue = output.green; + } +} +// ---- +// lib: @diagnostics 2072 +// -> textDocument/documentHighlight { +// "position": @TheLibUse +// } +// <- [ +// { +// "kind": 2, +// "range": { +// "end": { +// "character": 17, +// "line": 4 +// }, +// "start": { +// "character": 11, +// "line": 4 +// } +// } +// }, +// { +// "kind": 2, +// "range": { +// "end": { +// "character": 23, +// "line": 12 +// }, +// "start": { +// "character": 17, +// "line": 12 +// } +// } +// } +// ] +// -> textDocument/documentHighlight { +// "position": @ThatColorRet +// } +// <- [ +// { +// "kind": 2, +// "range": { +// "end": { +// "character": 25, +// "line": 5 +// }, +// "start": { +// "character": 16, +// "line": 5 +// } +// } +// }, +// { +// "kind": 2, +// "range": { +// "end": { +// "character": 51, +// "line": 16 +// }, +// "start": { +// "character": 42, +// "line": 16 +// } +// } +// } +// ]